Skip to content

Commit 8bc8132

Browse files
committed
Change session_token decode signature
1 parent f6a0b30 commit 8bc8132

File tree

3 files changed

+38
-19
lines changed

3 files changed

+38
-19
lines changed

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,23 +122,23 @@ _Note: Your application must be public to test the billing process. To test on a
122122

123123
### Session tokens
124124

125-
The Shopify Python API library provides helper methods to decode [session tokens](https://shopify.dev/concepts/apps/building-embedded-apps-using-session-tokens). You can use the `get_decoded_session_token` function to help extract and decode a session token from an HTTP Authorization header.
125+
The Shopify Python API library provides helper methods to decode [session tokens](https://shopify.dev/concepts/apps/building-embedded-apps-using-session-tokens). You can use the `decode_from_header` function to extract and decode a session token from an HTTP Authorization header.
126126

127127
#### Basic usage
128128

129129
```python
130130
from shopify import session_token
131131

132-
decoded_payload = session_token.get_decoded_session_token(
132+
decoded_payload = session_token.decode_from_header(
133133
authorization_header=your_auth_request_header,
134134
api_key=your_api_key,
135135
secret=your_api_secret,
136136
)
137137
```
138138

139-
#### Use session_token to create a decorator
139+
#### Create a decorator using `session_token`
140140

141-
Here's a sample use-case of building a Django decorator using `session_token`:
141+
Here's a sample decorator that protects your app views/routes by requiring the presence of valid session tokens as part of a request's headers.
142142

143143
```python
144144
from shopify import session_token
@@ -148,27 +148,28 @@ def session_token_required(func):
148148
def wrapper(*args, **kwargs):
149149
request = args[0] # Or flask.request if you use Flask
150150
try:
151-
session_token = session_token.get_decoded_session_token(
151+
decoded_session_token = session_token.decode_from_header(
152152
authorization_header = request.headers.get('Authorization'),
153153
api_key = SHOPIFY_API_KEY,
154154
secret = SHOPIFY_API_SECRET
155155
)
156-
with shopify_session(session_token):
156+
with shopify_session(decoded_session_token):
157157
return func(*args, **kwargs)
158-
except session_token.SessionTokenError:
159-
return generate_http_401_response()
158+
except session_token.SessionTokenError as e:
159+
# Log the error here
160+
return unauthorized_401_response()
160161

161162
return wrapper
162163

163164

164-
def shopify_session(session_token):
165-
shopify_domain = session_token.get("dest")
165+
def shopify_session(decoded_session_token):
166+
shopify_domain = decoded_session_token.get("dest")
166167
access_token = get_offline_access_token_by_shop_domain(shopify_domain)
167168

168169
return shopify.Session.temp(shopify_domain, SHOPIFY_API_VERSION, access_token)
169170

170171

171-
@session_token_required
172+
@session_token_required # Requests to /products require session tokens
172173
def products(request):
173174
products = shopify.Product.find()
174175
...

shopify/session_token.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class TokenAuthenticationError(SessionTokenError):
3333
pass
3434

3535

36-
def get_decoded_session_token(authorization_header, api_key, secret):
36+
def decode_from_header(authorization_header, api_key, secret):
3737
session_token = _extract_session_token(authorization_header)
3838
decoded_payload = _decode_session_token(session_token, api_key, secret)
3939
_validate_issuer(decoded_payload)

test/session_token_test.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,44 +43,62 @@ def test_raises_if_token_authentication_header_is_not_bearer(self):
4343
authorization_header = "Bad auth header"
4444

4545
with self.assertRaises(session_token.TokenAuthenticationError) as cm:
46-
session_token.get_decoded_session_token(authorization_header, api_key=self.api_key, secret=self.secret)
46+
session_token.decode_from_header(authorization_header, api_key=self.api_key, secret=self.secret)
4747

4848
self.assertEqual("The HTTP_AUTHORIZATION_HEADER provided does not contain a Bearer token", str(cm.exception))
4949

5050
def test_raises_jwt_error_if_session_token_is_expired(self):
5151
self.payload["exp"] = timestamp((datetime.now() + timedelta(0, -10)))
5252

53-
with self.assertRaises(session_token.SessionTokenError, msg="Expird") as cm:
54-
session_token.get_decoded_session_token(self.build_auth_header(), api_key=self.api_key, secret=self.secret)
53+
with self.assertRaises(session_token.SessionTokenError) as cm:
54+
session_token.decode_from_header(self.build_auth_header(), api_key=self.api_key, secret=self.secret)
5555

5656
self.assertEqual("Signature has expired", str(cm.exception))
5757

58+
def test_raises_jwt_error_if_invalid_alg(self):
59+
bad_session_token = jwt.encode(self.payload, None, algorithm="none")
60+
invalid_header = "Bearer {session_token}".format(session_token=bad_session_token)
61+
62+
with self.assertRaises(session_token.SessionTokenError) as cm:
63+
session_token.decode_from_header(invalid_header, api_key=self.api_key, secret=self.secret)
64+
65+
self.assertEqual("The specified alg value is not allowed", str(cm.exception))
66+
67+
def test_raises_jwt_error_if_invalid_signature(self):
68+
bad_session_token = jwt.encode(self.payload, "bad_secret", algorithm="HS256")
69+
invalid_header = "Bearer {session_token}".format(session_token=bad_session_token)
70+
71+
with self.assertRaises(session_token.SessionTokenError) as cm:
72+
session_token.decode_from_header(invalid_header, api_key=self.api_key, secret=self.secret)
73+
74+
self.assertEqual("Signature verification failed", str(cm.exception))
75+
5876
def test_raises_if_aud_doesnt_match_api_key(self):
5977
self.payload["aud"] = "bad audience"
6078

6179
with self.assertRaises(session_token.SessionTokenError) as cm:
62-
session_token.get_decoded_session_token(self.build_auth_header(), api_key=self.api_key, secret=self.secret)
80+
session_token.decode_from_header(self.build_auth_header(), api_key=self.api_key, secret=self.secret)
6381

6482
self.assertEqual("Invalid audience", str(cm.exception))
6583

6684
def test_raises_if_issuer_hostname_is_invalid(self):
6785
self.payload["iss"] = "bad_shop_hostname"
6886

6987
with self.assertRaises(session_token.InvalidIssuerError) as cm:
70-
session_token.get_decoded_session_token(self.build_auth_header(), api_key=self.api_key, secret=self.secret)
88+
session_token.decode_from_header(self.build_auth_header(), api_key=self.api_key, secret=self.secret)
7189

7290
self.assertEqual("Invalid issuer", str(cm.exception))
7391

7492
def test_raises_if_iss_and_dest_dont_match(self):
7593
self.payload["dest"] = "bad_shop.myshopify.com"
7694

7795
with self.assertRaises(session_token.MismatchedHostsError) as cm:
78-
session_token.get_decoded_session_token(self.build_auth_header(), api_key=self.api_key, secret=self.secret)
96+
session_token.decode_from_header(self.build_auth_header(), api_key=self.api_key, secret=self.secret)
7997

8098
self.assertEqual("The issuer and destination do not match", str(cm.exception))
8199

82100
def test_returns_decoded_payload(self):
83-
decoded_payload = session_token.get_decoded_session_token(
101+
decoded_payload = session_token.decode_from_header(
84102
self.build_auth_header(), api_key=self.api_key, secret=self.secret
85103
)
86104

0 commit comments

Comments
 (0)