-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
167 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ django >=3.2 | |
ShopifyAPI >=8.0.0 | ||
ua-parser | ||
python-jose | ||
requests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from django.core.checks import Error, register | ||
from django.conf import settings | ||
|
||
@register() | ||
def check_shopify_auth_bounce_page_url(app_configs, **kwargs): | ||
errors = [] | ||
if not hasattr(settings, 'SHOPIFY_AUTH_BOUNCE_PAGE_URL'): | ||
errors.append( | ||
Error( | ||
'SHOPIFY_AUTH_BOUNCE_PAGE_URL is not set in settings.', | ||
hint='Set SHOPIFY_AUTH_BOUNCE_PAGE_URL in your settings file or environment variables.', | ||
id='shopify_auth.E001', | ||
) | ||
) | ||
elif not settings.SHOPIFY_AUTH_BOUNCE_PAGE_URL: | ||
errors.append( | ||
Error( | ||
'SHOPIFY_AUTH_BOUNCE_PAGE_URL is empty.', | ||
hint='Provide a valid URL for SHOPIFY_AUTH_BOUNCE_PAGE_URL in your settings file or environment variables.', | ||
id='shopify_auth.E002', | ||
) | ||
) | ||
return errors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import logging | ||
from django.core.exceptions import ImproperlyConfigured | ||
|
||
import requests | ||
from django.conf import settings | ||
from django.http import HttpRequest, HttpResponse | ||
from django.shortcuts import redirect | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
from typing import TypedDict | ||
|
||
|
||
class ResponseData(TypedDict): | ||
access_token: str | ||
scope: list[str] | ||
|
||
|
||
# From https://shopify.dev/docs/apps/build/authentication-authorization/get-access-tokens/exchange-tokens#step-2-get-an-access-token | ||
def retrieve_api_token(shop: str, session_token: str) -> ResponseData: | ||
url = f"https://{shop}/admin/oauth/access_token" | ||
payload = { | ||
"client_id": settings.SHOPIFY_APP_API_KEY, | ||
"client_secret": settings.SHOPIFY_APP_API_SECRET, | ||
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", | ||
"subject_token": session_token, | ||
"subject_token_type": "urn:ietf:params:oauth:token-type:id_token", | ||
"requested_token_type": "urn:shopify:params:oauth:token-type:offline-access-token", | ||
} | ||
headers = {"Content-Type": "application/json", "Accept": "application/json"} | ||
response = requests.post(url, json=payload, headers=headers) | ||
response.raise_for_status() | ||
response_data_raw = response.json() | ||
response_data: ResponseData = { | ||
"access_token": response_data_raw["access_token"], | ||
"scope": [scope.strip() for scope in response_data_raw["scope"].split(",")], | ||
} | ||
return response_data | ||
|
||
|
||
def session_token_bounce_page_url(request: HttpRequest) -> str: | ||
search_params = request.GET.copy() | ||
search_params.pop("id_token", None) | ||
search_params["shopify-reload"] = f"{request.path}?{search_params.urlencode()}" | ||
|
||
bounce_page_url = settings.SHOPIFY_AUTH_BOUNCE_PAGE_URL | ||
return f"{bounce_page_url}?{search_params.urlencode()}" | ||
|
||
|
||
def redirect_to_session_token_bounce_page(request: HttpRequest) -> HttpResponse: | ||
return redirect(session_token_bounce_page_url(request)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from django.test import TestCase, RequestFactory | ||
from django.core.exceptions import ImproperlyConfigured | ||
from unittest.mock import patch | ||
|
||
from ..managed_install import ( | ||
retrieve_api_token, | ||
session_token_bounce_page_url, | ||
redirect_to_session_token_bounce_page, | ||
) | ||
|
||
class ManagedInstallTestCase(TestCase): | ||
def setUp(self): | ||
self.factory = RequestFactory() | ||
|
||
@patch('requests.post') | ||
def test_retrieve_api_token(self, mock_post): | ||
# Mock the response from Shopify | ||
mock_response = mock_post.return_value | ||
mock_response.json.return_value = { | ||
'access_token': 'test_token', | ||
'scope': 'read_products,write_orders' | ||
} | ||
|
||
result = retrieve_api_token('test-shop.myshopify.com', 'test_session_token') | ||
|
||
self.assertEqual(result['access_token'], 'test_token') | ||
self.assertEqual(result['scope'], ['read_products', 'write_orders']) | ||
|
||
# Check if the request was made with correct parameters | ||
mock_post.assert_called_once() | ||
args, kwargs = mock_post.call_args | ||
self.assertEqual(args[0], 'https://test-shop.myshopify.com/admin/oauth/access_token') | ||
|
||
def test_session_token_bounce_page_url(self): | ||
request = self.factory.get('/test-path/?param1=value1&id_token=test_token') | ||
|
||
with self.settings(SHOPIFY_AUTH_BOUNCE_PAGE_URL='/bounce/'): | ||
url = session_token_bounce_page_url(request) | ||
|
||
expected_url = '/bounce/?param1=value1&shopify-reload=%2Ftest-path%2F%3Fparam1%3Dvalue1' | ||
self.assertEqual(url, expected_url) | ||
|
||
def test_redirect_to_session_token_bounce_page(self): | ||
request = self.factory.get('/test-path/') | ||
|
||
with self.settings(SHOPIFY_AUTH_BOUNCE_PAGE_URL='/bounce/'): | ||
response = redirect_to_session_token_bounce_page(request) | ||
|
||
self.assertEqual(response.status_code, 302) | ||
self.assertTrue(response.url.startswith('/bounce/')) |
15 changes: 15 additions & 0 deletions
15
shopify_auth/session_tokens/tests/test_session_token_bounce_view.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from django.test import TestCase, RequestFactory | ||
from django.conf import settings | ||
from django.http import HttpResponse | ||
|
||
from ..views import session_token_bounce | ||
|
||
class SessionTokenBounceTestCase(TestCase): | ||
def test_session_token_bounce(self): | ||
request = RequestFactory().get('/bounce/') | ||
response = session_token_bounce(request) | ||
|
||
self.assertIsInstance(response, HttpResponse) | ||
self.assertEqual(response['Content-Type'], 'text/html') | ||
self.assertIn(settings.SHOPIFY_APP_API_KEY, response.content.decode()) | ||
self.assertIn('https://cdn.shopify.com/shopifycloud/app-bridge.js', response.content.decode()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters