@@ -22,12 +22,12 @@ RSA encoding and decoding require the ``cryptography`` module. See :ref:`install
2222.. code-block :: pycon
2323
2424 >>> import jwt
25- >>> private_key = b"-----BEGIN PRIVATE KEY-----\nMIGEAgEAMBAGByqGSM49AgEGBS..."
26- >>> public_key = b"-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEAC... "
25+ >>> private_key = b"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwhvqCC+37A+UXgcvDl+7nbVjDI3QErdZBkI1VypVBMkKKWHM\nNLMdHk0bIKL+1aDYTRRsCKBy9ZmSSX1pwQlO/3+gRs/MWG27gdRNtf57uLk1+lQI\n6hBDozuyBR0YayQDIx6VsmpBn3Y8LS13p4pTBvirlsdX+jXrbOEaQphn0OdQo0WD\noOwwsPCNCKoIMbUOtUCowvjesFXlWkwG1zeMzlD1aDDS478PDZdckPjT96ICzqe4\nO1Ok6fRGnor2UTmuPy0f1tI0F7Ol5DHAD6pZbkhB70aTBuWDGLDR0iLenzyQecmD\n4aU19r1XC9AHsVbQzxHrP8FveZGlV/nJOBJwFwIDAQABAoIBAFCVFBA39yvJv/dV\nFiTqe1HahnckvFe4w/2EKO65xTfKWiyZzBOotBLrQbLH1/FJ5+H/82WVboQlMATQ\nSsH3olMRYbFj/NpNG8WnJGfEcQpb4Vu93UGGZP3z/1B+Jq/78E15Gf5KfFm91PeQ\nY5crJpLDU0CyGwTls4ms3aD98kNXuxhCGVbje5lCARizNKfm/+2qsnTYfKnAzN+n\nnm0WCjcHmvGYO8kGHWbFWMWvIlkoZ5YubSX2raNeg+YdMJUHz2ej1ocfW0A8/tmL\nwtFoBSuBe1Z2ykhX4t6mRHp0airhyc+MO0bIlW61vU/cPGPos16PoS7/V08S7ZED\nX64rkyECgYEA4iqeJZqny/PjOcYRuVOHBU9nEbsr2VJIf34/I9hta/mRq8hPxOdD\n/7ES/ZTZynTMnOdKht19Fi73Sf28NYE83y5WjGJV/JNj5uq2mLR7t2R0ZV8uK8tU\n4RR6b2bHBbhVLXZ9gqWtu9bWtsxWOkG1bs0iONgD3k5oZCXp+IWuklECgYEA27bA\n7UW+iBeB/2z4x1p/0wY+whBOtIUiZy6YCAOv/HtqppsUJM+W9GeaiMpPHlwDUWxr\n4xr6GbJSHrspkMtkX5bL9e7+9zBguqG5SiQVIzuues9Jio3ZHG1N2aNrr87+wMiB\nxX6Cyi0x1asmsmIBO7MdP/tSNB2ebr8qM6/6mecCgYBA82ZJfFm1+8uEuvo6E9/R\nyZTbBbq5BaVmX9Y4MB50hM6t26/050mi87J1err1Jofgg5fmlVMn/MLtz92uK/hU\nS9V1KYRyLc3h8gQQZLym1UWMG0KCNzmgDiZ/Oa/sV5y2mrG+xF/ZcwBkrNgSkO5O\n7MBoPLkXrcLTCARiZ9nTkQKBgQCsaBGnnkzOObQWnIny1L7s9j+UxHseCEJguR0v\nXMVh1+5uYc5CvGp1yj5nDGldJ1KrN+rIwMh0FYt+9dq99fwDTi8qAqoridi9Wl4t\nIXc8uH5HfBT3FivBtLucBjJgOIuK90ttj8JNp30tbynkXCcfk4NmS23L21oRCQyy\nlmqNDQKBgQDRvzEB26isJBr7/fwS0QbuIlgzEZ9T3ZkrGTFQNfUJZWcUllYI0ptv\ny7ShHOqyvjsC3LPrKGyEjeufaM5J8EFrqwtx6UB/tkGJ2bmd1YwOWFHvfHgHCZLP\n34ZNURCvxRV9ZojS1zmDRBJrSo7+/K0t28hXbiaTOjJA18XAyyWmGg==\n-----END RSA PRIVATE KEY-----\n"
26+ >>> public_key = b"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhvqCC+37A+UXgcvDl+7\nnbVjDI3QErdZBkI1VypVBMkKKWHMNLMdHk0bIKL+1aDYTRRsCKBy9ZmSSX1pwQlO\n/3+gRs/MWG27gdRNtf57uLk1+lQI6hBDozuyBR0YayQDIx6VsmpBn3Y8LS13p4pT\nBvirlsdX+jXrbOEaQphn0OdQo0WDoOwwsPCNCKoIMbUOtUCowvjesFXlWkwG1zeM\nzlD1aDDS478PDZdckPjT96ICzqe4O1Ok6fRGnor2UTmuPy0f1tI0F7Ol5DHAD6pZ\nbkhB70aTBuWDGLDR0iLenzyQecmD4aU19r1XC9AHsVbQzxHrP8FveZGlV/nJOBJw\nFwIDAQAB\n-----END PUBLIC KEY-----\n "
2727 >>> encoded = jwt.encode({"some": "payload"}, private_key, algorithm="RS256")
2828 >>> print(encoded)
29- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 .eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg
30- >>> decoded = jwt.decode(encoded, public_key, algorithms=["RS256"])
29+ eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9 .eyJzb21lIjoicGF5bG9hZCJ9.ACNvAmKejouaO7fOVAqiqTQ2nE7jtvA2hpUJk3XduO9qp1mhCSJGIMQRNokyzNZgJFibGLZhcfwf0_4amlYqwBQ3HTB58zDdV-iAoSIxnvOERMc9qxUKNupAs4B0aL0vzIvpIMoDxIMDAnNviDIDzWPnprnpwHNdza7Y40_O1h4trmJcstE0xjmcyV0CEemwOCSARcFnxVK8rE5dItdL05IWrzOx-twBPH5jIU9zwtDvyci9LXUpG010CiejDxI1Iu0ezbN8iX2wkNVbmsYPNnMvgmGCMJbSiatzXJNDdrGXM_YiJKxXvOHcL6aUDYM0vc5oymT4-aAHwKEUB8592A
30+ >>> jwt.decode(encoded, public_key, algorithms=["RS256"])
3131 {'some': 'payload'}
3232
3333 If your private key needs a passphrase, you need to pass in a ``PrivateKey `` object from ``cryptography ``.
@@ -37,8 +37,8 @@ If your private key needs a passphrase, you need to pass in a ``PrivateKey`` obj
3737 from cryptography.hazmat.primitives import serialization
3838 from cryptography.hazmat.backends import default_backend
3939
40- pem_bytes = b " -----BEGIN PRIVATE KEY-----\n MIGEAgEAMBAGByqGSM49AgEGBS..."
41- passphrase = b " your password "
40+ pem_bytes = b"-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,C9C8F89EC68D15F26EB9B9695216C6DC\nE3lvX0dYjDxC0DIDitwNj+mEvU48Cqlp9esIeVmfcFmM6KpuQEA4asg/19kldbRq\ntOAYwmMuzz6GNYtX6sQXcStUE3pKMiMaTuP9WXzTc0boSYsGpGoQLtGv3h+0lkPu\nTGaktEhIfplAYlmsS/twr9Jh9QZjEs3dEMwpuF8A/iDZFeIE2thZL0bo38VWorgZ\nTCoOlC7qGtaeDvXXYrMvAUw3lN9A+DvxuPvbGqfqiHVBhxRcQEcR5p65lKP/V0WQ\nDe0AqCx1ghYGnExT7I4GLfr7Ux3F1UcVldPPsNeCTR/5YMOYDw7o5CZZ2TM39T33\nDBwfRhDqKe4bMUQcvcD54S2tfW7tEekm6mx5JwzW11sd0Gprj2uggDTOj3ce2yzM\nzl/dfbyFgh6v4jFeblIgvQ4VPg9nfCaRhatw5KXnfHBvmvdxlQ1Qp5P43ThXjI2a\njaJdm2lu1DLhf1OYGeQ0ytDDPzvhrZrdEJ8jbB3VCn4O/hvCtdsp7jVw2Djxmw2A\niRz2zlZJUlaytbi/DMpEVFwIzpuiDkpJ+ekzAsBbm/rGR/tjCEtHzVuoQNUWI93k\n0FML+Zzb6AkBWYjBXDZtzwJpMdNr8Vvh3krZySbRzQstqL2PYuNoSZ8/1xnnVqTV\nA0pDX7OS856AXQzQ1FRjjk/Jd0k6jGj8d7LzVgMnb8VknKvshlLmZDz8Sqa1coN4\n0Z1VfiT0Hzlk0fkoGtRjhSc3MB6ZLg7vVlY5vb4bRrTX79s/p8Y/OecYnGC6qhTi\n+VyJiMfwXyjFjIWYH8Y3G0QLkvOrTxLAY/3B2TU5wVSD7lfnPKOatMK1W0DHu5jp\nG9PPTzK9ol3v6Pk0prYg1fiApb6CCBUeZBvCIbJCzYrL/yBV/xYlCwAekLNGz9Vj\nNQUoiJqi27fOQi+ZXCrF7gYj8afo/xrg0tf7YqoOty8qfsozXzqwHKn+PcZOcqa5\n5rIqjLOO2f6KO2dxBeZK6zmzg7K/8RjvsNkEuXffec/nwnC10OVoMbE4wyPmNUQi\ndSuZ6xWBqiREjodLL+Ez/N1Qa52kuLSigrrSBTM2e42PWDV1sNW5V2wwlnolXFF6\n2Xp74WaGdnwF4Afrm7AnaBxdmfjk/a+c2uzPkZkpVnxrW3l8afphhKpRoTLzqDPp\nZGc5Fx9UZsmX18B8D1OGbf4aVLUkoqPPHbccCI+wByoAgIoq+y2391fP/Db6fY9A\nR4t2uuP2sNqDfYtzPYikePBXhYlldE1UHJ378g8pTiRHOI9BhuKIOIbVngPUYk4I\nwhYct2K84HjvR3iRnobK0UmmNOqtK0AtUqne+xaj1f3OwMZSvTUe7/jESgw1e1tn\nulKiWnKnmTSZkeTIp6itui2T7ewfNyitPtvnhoH1fBnMyUVACip0SLXp1fwQ7iCc\namPFFKo7p+C7P3l0ItegaMHywOSTBvK39DQTIpF9ml8VCQ+UyPOv/LnSJk1mbJN/\nc2Hdoj5dMa6T7ysIwZGEissJ/MEP+dpRs7VmCjWrHCDHfeAIO0n32g4zbzlNc/OA\nIdCXTvi4xUEn2n3JPt5Ba9qDUevaHSERlLxI+9a4ZaZeg4t+AzY0ur6+RWx+PaXB\n-----END RSA PRIVATE KEY-----\n"
41+ passphrase = b " abc123 "
4242
4343 private_key = serialization.load_pem_private_key(
4444 pem_bytes, password = passphrase, backend = default_backend()
@@ -60,7 +60,7 @@ Specifying Additional Headers
6060 ... algorithm="HS256",
6161 ... headers={"kid": "230498151c214b788dd97f22b85410a5"},
6262 ... )
63- 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIzMDQ5ODE1MWMyMTRiNzg4ZGQ5N2YyMmI4NTQxMGE1In0 .eyJzb21lIjoicGF5bG9hZCJ9.DogbDGmMHgA_bU05TAB-R6geQ2nMU2BRM-LnYEtefwg '
63+ 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjIzMDQ5ODE1MWMyMTRiNzg4ZGQ5N2YyMmI4NTQxMGE1IiwidHlwIjoiSldUIn0 .eyJzb21lIjoicGF5bG9hZCJ9.0n16c-shKKnw6gervyk1Dge35tvzbzQ_KCV3H3bgoJ0 '
6464
6565
6666 Reading the Claimset without Validation
@@ -90,8 +90,14 @@ key in the header.
9090
9191.. code-block :: pycon
9292
93+ >>> encoded = jwt.encode(
94+ ... {"some": "payload"},
95+ ... "secret",
96+ ... algorithm="HS256",
97+ ... headers={"kid": "230498151c214b788dd97f22b85410a5"},
98+ ... )
9399 >>> jwt.get_unverified_header(encoded)
94- {'alg': 'RS256 ', 'typ ': 'JWT ', 'kid ': 'key-id-12345... '}
100+ {'alg': 'HS256 ', 'kid ': '230498151c214b788dd97f22b85410a5 ', 'typ ': 'JWT '}
95101
96102 Registered Claim Names
97103----------------------
@@ -119,21 +125,23 @@ Expiration Time Claim (exp)
119125You can pass the expiration time as a UTC UNIX timestamp (an int) or as a
120126datetime, which will be converted into an int. For example:
121127
122- .. code-block :: python
128+ .. code-block :: pycon
123129
124- jwt.encode({" exp" : 1371720939 }, " secret" )
125- jwt.encode({" exp" : datetime.now(tz = timezone.utc)}, " secret" )
130+ >>> from datetime import datetime, timezone
131+ >>> token = jwt.encode({"exp": 1371720939}, "secret")
132+ >>> token = jwt.encode({"exp": datetime.now(tz=timezone.utc)}, "secret")
126133
127134 Expiration time is automatically verified in `jwt.decode() ` and raises
128135`jwt.ExpiredSignatureError ` if the expiration time is in the past:
129136
130- .. code-block :: python
137+ .. code-block :: pycon
131138
132- try :
133- jwt.decode(" JWT_STRING" , " secret" , algorithms = [" HS256" ])
134- except jwt.ExpiredSignatureError:
135- # Signature has expired
136- ...
139+ >>> try:
140+ ... jwt.decode(token, "secret", algorithms=["HS256"])
141+ ... except jwt.ExpiredSignatureError:
142+ ... print("expired")
143+ ...
144+ expired
137145
138146 Expiration time will be compared to the current UTC time (as given by
139147`timegm(datetime.now(tz=timezone.utc).utctimetuple()) `), so be sure to use a UTC timestamp
@@ -147,27 +155,25 @@ For example, if you have a JWT payload with a expiration time set to 30 seconds
147155after creation but you know that sometimes you will process it after 30 seconds,
148156you can set a leeway of 10 seconds in order to have some margin:
149157
150- .. code-block :: python
151-
152- jwt_payload = jwt.encode(
153- {" exp" : datetime.datetime.now(tz = timezone.utc) + datetime.timedelta(seconds = 30 )},
154- " secret" ,
155- )
156-
157- time.sleep(32 )
158+ .. code-block :: pycon
158159
159- # JWT payload is now expired
160- # But with some leeway, it will still validate
161- jwt.decode(jwt_payload, " secret" , leeway = 10 , algorithms = [" HS256" ])
160+ >>> import time, datetime
161+ >>> from datetime import timezone
162+ >>> payload = {"exp": datetime.datetime.now(tz=timezone.utc) + datetime.timedelta(seconds=1)}
163+ >>> token = jwt.encode(payload, "secret")
164+ >>> time.sleep(2)
165+ >>> # JWT payload is now expired
166+ >>> # But with some leeway, it will still validate
167+ >>> decoded = jwt.decode(token, "secret", leeway=5, algorithms=["HS256"])
162168
163169 Instead of specifying the leeway as a number of seconds, a `datetime.timedelta `
164170instance can be used. The last line in the example above is equivalent to:
165171
166- .. code-block :: python
172+ .. code-block :: pycon
167173
168- jwt.decode(
169- jwt_payload , " secret" , leeway = datetime.timedelta(seconds = 10 ), algorithms = [" HS256" ]
170- )
174+ >>> decoded = jwt.decode(
175+ ... token , "secret", leeway=datetime.timedelta(seconds=10), algorithms=["HS256"]
176+ ... )
171177
172178 Not Before Time Claim (nbf)
173179~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -182,10 +188,10 @@ Not Before Time Claim (nbf)
182188
183189The `nbf ` claim works similarly to the `exp ` claim above.
184190
185- .. code-block :: python
191+ .. code-block :: pycon
186192
187- jwt.encode({" nbf" : 1371720939 }, " secret" )
188- jwt.encode({" nbf" : datetime.now(tz = timezone.utc)}, " secret" )
193+ >>> token = jwt.encode({"nbf": 1371720939}, "secret")
194+ >>> token = jwt.encode({"nbf": datetime. datetime.now(tz=timezone.utc)}, "secret")
189195
190196 Issuer Claim (iss)
191197~~~~~~~~~~~~~~~~~~
@@ -195,12 +201,16 @@ Issuer Claim (iss)
195201 The "iss" value is a case-sensitive string containing a StringOrURI
196202 value. Use of this claim is OPTIONAL.
197203
198- .. code-block :: python
199-
200- payload = {" some" : " payload" , " iss" : " urn:foo" }
204+ .. code-block :: pycon
201205
202- token = jwt.encode(payload, " secret" )
203- decoded = jwt.decode(token, " secret" , issuer = " urn:foo" , algorithms = [" HS256" ])
206+ >>> payload = {"some": "payload", "iss": "urn:foo"}
207+ >>> token = jwt.encode(payload, "secret")
208+ >>> try:
209+ ... jwt.decode(token, "secret", issuer="urn:invalid", algorithms=["HS256"])
210+ ... except jwt.InvalidIssuerError:
211+ ... print("invalid issuer")
212+ ...
213+ invalid issuer
204214
205215 If the issuer claim is incorrect, `jwt.InvalidIssuerError ` will be raised.
206216
@@ -217,34 +227,36 @@ Audience Claim (aud)
217227In the general case, the "aud" value is an array of case-
218228sensitive strings, each containing a StringOrURI value.
219229
220- .. code-block :: python
221-
222- payload = {" some" : " payload" , " aud" : [" urn:foo" , " urn:bar" ]}
230+ .. code-block :: pycon
223231
224- token = jwt.encode(payload, " secret" )
225- decoded = jwt.decode(token, " secret" , audience = " urn:foo" , algorithms = [" HS256" ])
232+ >>> payload = {"some": "payload", "aud": ["urn:foo", "urn:bar"]}
233+ >>> token = jwt.encode(payload, "secret")
234+ >>> decoded = jwt.decode(token, "secret", audience="urn:foo", algorithms=["HS256"])
235+ >>> decoded = jwt.decode(token, "secret", audience="urn:bar", algorithms=["HS256"])
226236
227237 In the special case when the JWT has one audience, the "aud" value MAY be
228238a single case-sensitive string containing a StringOrURI value.
229239
230- .. code-block :: python
231-
232- payload = {" some" : " payload" , " aud" : " urn:foo" }
240+ .. code-block :: pycon
233241
234- token = jwt.encode(payload, " secret" )
235- decoded = jwt.decode(token, " secret" , audience = " urn:foo" , algorithms = [" HS256" ])
242+ >>> payload = {"some": "payload", "aud": "urn:foo"}
243+ >>> token = jwt.encode(payload, "secret")
244+ >>> decoded = jwt.decode(token, "secret", audience="urn:foo", algorithms=["HS256"])
236245
237246 If multiple audiences are accepted, the ``audience `` parameter for
238247``jwt.decode `` can also be an iterable
239248
240- .. code-block :: python
249+ .. code-block :: pycon
241250
242- payload = {" some" : " payload" , " aud" : " urn:foo" }
243-
244- token = jwt.encode(payload, " secret" )
245- decoded = jwt.decode(
246- token, " secret" , audience = [" urn:foo" , " urn:bar" ], algorithms = [" HS256" ]
247- )
251+ >>> payload = {"some": "payload", "aud": "urn:foo"}
252+ >>> token = jwt.encode(payload, "secret")
253+ >>> decoded = jwt.decode(token, "secret", audience=["urn:foo", "urn:bar"], algorithms=["HS256"])
254+ >>> try:
255+ ... jwt.decode(token, "secret", audience=["urn:invalid"], algorithms=["HS256"])
256+ ... except jwt.InvalidAudienceError:
257+ ... print("invalid audience")
258+ ...
259+ invalid audience
248260
249261 The interpretation of audience values is generally application specific.
250262Use of this claim is OPTIONAL.
@@ -260,10 +272,10 @@ Issued At Claim (iat)
260272
261273 If the `iat ` claim is not a number, an `jwt.InvalidIssuedAtError ` exception will be raised.
262274
263- .. code-block :: python
275+ .. code-block :: pycon
264276
265- jwt.encode({" iat" : 1371720939 }, " secret" )
266- jwt.encode({" iat" : datetime.now(tz = timezone.utc)}, " secret" )
277+ >>> token = jwt.encode({"iat": 1371720939}, "secret")
278+ >>> token = jwt.encode({"iat": datetime. datetime.now(tz=timezone.utc)}, "secret")
267279
268280 Requiring Presence of Claims
269281----------------------------
@@ -272,8 +284,13 @@ If you wish to require one or more claims to be present in the claimset, you can
272284
273285.. code-block :: pycon
274286
275- >>> jwt.decode(encoded, options={"require": ["exp", "iss", "sub"]})
276- {'exp': 1371720939, 'iss': 'urn:foo', 'sub': '25c37522-f148-4cbf-8ee6-c4a9718dd0af'}
287+ >>> token = jwt.encode({"sub":"1234567890","iat":1371720939}, "secret")
288+ >>> try:
289+ ... jwt.decode(token, "secret", options={"require": ["exp", "iss", "sub"]}, algorithms=["HS256"])
290+ ... except jwt.MissingRequiredClaimError as e:
291+ ... print(e)
292+ ...
293+ Token is missing the "exp" claim
277294
278295 Retrieve RSA signing keys from a JWKS endpoint
279296----------------------------------------------
@@ -288,13 +305,13 @@ Retrieve RSA signing keys from a JWKS endpoint
288305 >>> optional_custom_headers = {"User-agent": "custom-user-agent"}
289306 >>> jwks_client = PyJWKClient(url, headers=optional_custom_headers)
290307 >>> signing_key = jwks_client.get_signing_key_from_jwt(token)
291- >>> data = jwt.decode(
308+ >>> jwt.decode(
292309 ... token,
293310 ... signing_key,
294311 ... audience="https://expenses-api",
295312 ... options={"verify_exp": False},
313+ ... algorithms=["RS256"])
296314 ... )
297- >>> print(data)
298315 {'iss': 'https://dev-87evx9ru.auth0.com/', 'sub': 'aW4Cca79xReLWUz0aE2H6kD0O3cXBVtC@clients', 'aud': 'https://expenses-api', 'iat': 1572006954, 'exp': 1572006964, 'azp': 'aW4Cca79xReLWUz0aE2H6kD0O3cXBVtC', 'gty': 'client-credentials'}
299316
300317 OIDC Login Flow
@@ -314,7 +331,6 @@ is not built into pyjwt.
314331 import jwt
315332 import requests
316333
317-
318334 # Part 1: setup
319335 # get the OIDC config and JWKs to use
320336
@@ -324,15 +340,12 @@ is not built into pyjwt.
324340 # example of fetching data from your OIDC server
325341 # see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
326342 oidc_server = ...
327- oidc_config = requests.get(
328- f " https:// { oidc_server} /.well-known/openid-configuration "
329- ).json()
343+ oidc_config = requests.get(f " https:// { oidc_server} /.well-known/openid-configuration " ).json()
330344 signing_algos = oidc_config[" id_token_signing_alg_values_supported" ]
331345
332346 # setup a PyJWKClient to get the appropriate signing key
333347 jwks_client = jwt.PyJWKClient(oidc_config[" jwks_uri" ])
334348
335-
336349 # Part 2: login / authorization
337350 # when a user completes an OIDC login flow, there will be a well-formed
338351 # response object to parse/handle
@@ -343,7 +356,6 @@ is not built into pyjwt.
343356 id_token = token_response[" id_token" ]
344357 access_token = token_response[" access_token" ]
345358
346-
347359 # Part 3: decode and validate at_hash
348360 # after the login is complete, the id_token needs to be decoded
349361 # this is the stage at which an OIDC client must verify the at_hash
@@ -352,7 +364,7 @@ is not built into pyjwt.
352364 signing_key = jwks_client.get_signing_key_from_jwt(id_token)
353365
354366 # now, decode_complete to get payload + header
355- data = jwt.api_jwt. decode_complete(
367+ data = jwt.decode_complete(
356368 id_token,
357369 key = signing_key,
358370 audience = client_id,
0 commit comments