Skip to content

Commit 01377a4

Browse files
Added OAuth 2.0 support
1 parent 4b7f971 commit 01377a4

File tree

9 files changed

+1055
-6
lines changed

9 files changed

+1055
-6
lines changed

.talismanrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,10 @@ fileignoreconfig:
393393
checksum: 344aa9e4b3ec399c581a507eceff63ff6faf56ad938475e5f4865f6cb590df68
394394
- filename: tests/unit/contentstack/test_contentstack.py
395395
checksum: 98503cbd96cb546a19aed037a6ca28ef54fcea312efcd9bac1171e43760f6e86
396+
- filename: contentstack_management/contentstack.py
397+
checksum: 520f6fa236569a05579011fa67cb29381f187616d96526ecdfad5ec8255231a5
398+
- filename: tests/unit/test_oauth_handler.py
399+
checksum: 8b6853ba64c3de4f9097ca506719c5e33c7468ae5985b8adcda3eb6461d76be5
400+
- filename: contentstack_management/oauth/oauth_handler.py
401+
checksum: e33cfd32d90c0553c4959c0d266fef1247cd0e0fe7bbe85cae98bb205e62c70e
396402
version: "1.0"

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# CHANGELOG
22

33
## Content Management SDK For Python
4+
5+
---
6+
## v1.7.0
7+
8+
#### Date: 15 September 2025
9+
10+
- OAuth 2.0 support.
411
---
512
## v1.6.0
613

contentstack_management/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
from .extensions.extension import Extension
3535
from .variant_group.variant_group import VariantGroup
3636
from .variants.variants import Variants
37+
from .oauth.oauth_handler import OAuthHandler
38+
from .oauth.oauth_interceptor import OAuthInterceptor
3739

3840

3941
__all__ = (
@@ -71,14 +73,16 @@
7173
"PublishQueue",
7274
"Extension",
7375
"VariantGroup",
74-
"Variants"
76+
"Variants",
77+
"OAuthHandler",
78+
"OAuthInterceptor"
7579
)
7680

7781
__title__ = 'contentstack-management-python'
7882
__author__ = 'dev-ex'
7983
__status__ = 'debug'
8084
__region__ = 'na'
81-
__version__ = '1.6.0'
85+
__version__ = '1.7.0'
8286
__host__ = 'api.contentstack.io'
8387
__protocol__ = 'https://'
8488
__api_version__ = 'v3'

contentstack_management/_api_client.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44
class _APIClient:
5-
def __init__(self, endpoint, headers, timeout=30, max_retries: int = 5):
5+
def __init__(self, endpoint, headers, timeout=30, max_retries: int = 5, oauth_interceptor=None):
66
"""
77
The function is a constructor that initializes the endpoint, headers, timeout, and max_retries
88
attributes of an object.
@@ -25,6 +25,8 @@ def __init__(self, endpoint, headers, timeout=30, max_retries: int = 5):
2525
self.headers = headers
2626
self.timeout = timeout
2727
self.max_retries = max_retries
28+
self.oauth_interceptor = oauth_interceptor
29+
self.oauth = {} # OAuth token storage
2830
pass
2931

3032
def _call_request(self, method, url, headers: dict = None, params=None, data=None, json_data=None, files=None):
@@ -52,9 +54,19 @@ def _call_request(self, method, url, headers: dict = None, params=None, data=Non
5254
:return: the JSON response from the HTTP request.
5355
"""
5456

55-
# headers.update(self.headers)
57+
# Use OAuth interceptor if available (matching Java implementation)
58+
if self.oauth_interceptor and self.oauth_interceptor.is_oauth_configured():
59+
return self.oauth_interceptor.execute_request(
60+
method, url, headers=headers, params=params, data=data,
61+
json=json_data, files=files, timeout=self.timeout
62+
)
63+
64+
# Fallback to standard requests (matching Java implementation)
65+
if headers is None:
66+
headers = {}
67+
headers.update(self.headers) # Merge client headers (including authtoken) with request headers
5668
response = requests.request(
57-
method, url, headers=headers, params=params, data=data, json=json_data, files=files)
69+
method, url, headers=headers, params=params, data=data, json=json_data, files=files, timeout=self.timeout)
5870
# response.raise_for_status()
5971
return response
6072

contentstack_management/contentstack.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from contentstack_management.stack import stack
55
from contentstack_management.user_session import user_session
66
from contentstack_management.users import user
7+
from contentstack_management.oauth.oauth_handler import OAuthHandler
78

89
version = '0.0.1'
910

@@ -33,7 +34,7 @@ class Client:
3334
def __init__(self, host: str = 'api.contentstack.io', scheme: str = 'https://',
3435
authtoken: str = None , management_token=None, headers: dict = None,
3536
region: Region = Region.US.value, version='v3', timeout=2, max_retries: int = 18, early_access: list = None,
36-
**kwargs):
37+
oauth_config: dict = None, **kwargs):
3738
self.endpoint = 'https://api.contentstack.io/v3/'
3839
if region is not None and host is not None and region is not Region.US.value:
3940
self.endpoint = f'{scheme}{region}-{host}/{version}/'
@@ -55,6 +56,19 @@ def __init__(self, host: str = 'api.contentstack.io', scheme: str = 'https://',
5556
headers['authorization'] = management_token
5657
headers = user_agents(headers)
5758
self.client = _APIClient(endpoint=self.endpoint, headers=headers, timeout=timeout, max_retries=max_retries)
59+
60+
# Initialize OAuth if configuration is provided
61+
self.oauth_handler = None
62+
if oauth_config:
63+
self.oauth_handler = OAuthHandler(
64+
app_id=oauth_config.get('app_id'),
65+
client_id=oauth_config.get('client_id'),
66+
redirect_uri=oauth_config.get('redirect_uri'),
67+
response_type=oauth_config.get('response_type', 'code'),
68+
client_secret=oauth_config.get('client_secret'),
69+
scope=oauth_config.get('scope'),
70+
api_client=self.client
71+
)
5872

5973
"""
6074
:param host: Optional hostname for the API endpoint.
@@ -96,3 +110,41 @@ def organizations(self, organization_uid: str = None):
96110

97111
def stack(self, api_key: str = None):
98112
return stack.Stack(self.client, api_key)
113+
114+
def oauth(self, app_id: str, client_id: str, redirect_uri: str,
115+
response_type: str = "code", client_secret: str = None,
116+
scope: list = None):
117+
"""
118+
Create an OAuth handler for OAuth 2.0 authentication.
119+
120+
Args:
121+
app_id: Your registered App ID
122+
client_id: Your OAuth Client ID
123+
redirect_uri: The URL where the user is redirected after login and consent
124+
response_type: OAuth response type (default: "code")
125+
client_secret: Client secret for standard OAuth flows (optional for PKCE)
126+
scope: Permissions requested (optional)
127+
128+
Returns:
129+
OAuthHandler instance
130+
131+
Example:
132+
>>> import contentstack_management
133+
>>> client = contentstack_management.Client()
134+
>>> oauth_handler = client.oauth(
135+
... app_id='your-app-id',
136+
... client_id='your-client-id',
137+
... redirect_uri='http://localhost:3000/callback'
138+
... )
139+
>>> auth_url = oauth_handler.authorize()
140+
>>> print(f"Visit this URL to authorize: {auth_url}")
141+
"""
142+
return OAuthHandler(
143+
app_id=app_id,
144+
client_id=client_id,
145+
redirect_uri=redirect_uri,
146+
response_type=response_type,
147+
client_secret=client_secret,
148+
scope=scope,
149+
api_client=self.client
150+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""OAuth 2.0 authentication module for Contentstack Management SDK."""
2+
3+
from .oauth_handler import OAuthHandler
4+
5+
__all__ = ["OAuthHandler"]

0 commit comments

Comments
 (0)