Payloads¶
As discussed, JWTs have a payload that is essentially a key/value store of information.
With Sanic JWT, there are three main uses of the payload:
- passing claims (See What is a JWT? for more information)
- passing scope (See Scopes for more information)
- passing arbitrary information to the client
Built in Claims¶
Sanic JWT ships with the capability to add, and later verify, five standard claims: exp, nbf, iat, iss, and aud.
Expires - exp¶
Warning
It is possible to disable token expiration. Do NOT do this unless you know what you are doing and why you are doing it.
Initialize(app, verify_exp=False)
Okay, great. You know what you are doing. It is still revommended that you NOT do this. Are you sure you know what you are doing?
Audience - aud¶
claim_aud to a strInitialize(app, claim_aud='my_client_domain.com')
Issued at - iat¶
claim_iat to TrueInitialize(app, claim_iat=True)
Issuer - iss¶
claim_iss to a strInitialize(app, claim_iss='my_server_domain.com')
Not before - NBF¶
claim_nbf to True, and claim_nbf_delta to an offset in secondsInitialize(app, claim_nbf=True, claim_nbf_delta=(60 * 3))
Custom Claims¶
Sometimes you may find a need to add claims to a JWT beyond what is built into Sanic JWT.
To do so, simply subclass Claim and register them at initialization by providing the custom claim class in a list to custom_claims.
from sanic_jwt import Claim, Initialize
MyCustomClaim(Claim):
key = 'foo'
def setup(self, payload, user):
return 'bar'
def verify(self, value):
return value == 'bar'
Initialize(..., custom_claims=[MyCustomClaim])
There are three attributes that a Claim must have: key, setup, and verify.
key: The name of the claim and the key that will be inserted into the payload.setup: A method to be run at the time the payload is created. It should return the value of the claim.verify: A method to be run when a token is being verified. It should return a boolean whether or not the claim has been met.Extra Verifications¶
Besides registering custom claims, sometimes you may find the need to do additional verifications on a payload. For example, perhaps you want to run checks that span more than one claim on the payload.
To accomplish this, you can register a list of methods (that each return a boolean) at initialization by providing the list to extra_verifications.
def check_number_of_claims(payload):
return len(payload.keys()) == 5
extra_verifications = [check_number_of_claims]
Initialize(
...,
extra_verifications=extra_verifications
)
Payload Handlers¶
As discussed, there are a few handlers on the Initialize instance that can be used to modify the payload.
Adding Scopes¶
add_scopes_to_payload@scoped decorator, then you will need a way to inject the payload with the user’s scopes. It should return either a single scope, or a list of scopes. Read about scopes for more information.str or a list of strasync def my_scope_extender(user, *args, **kwargs):
return user.scopes
Initialize(app, add_scopes_to_payload=my_scope_extender)
Note
The return of the authenticate method will be injected into this handler as user for your convenience.
Extending the payload¶
extend_payloaddictdef my_foo_bar_payload_extender(payload, *args, **kwargs):
payload.update({
'foo': 'bar'
})
return payload
Initialize(app, extend_payload=my_foo_bar_payload_extender)
Token signing¶
JWTs need to be digitally signed to allow for cryptographically verifying that an access token was generated by your application.
secret = 'XXXXXXXXXXXXXXXXXXXXXXXX'
Initialize(
app,
secret=mysecret)
There are several hashing algorithms that can be used to accomplish this. Check out the Configuration page to see which algorithms are supported, and read this for more information.
If you decide to use an RSA or an EC algorithm, then you must provide Sanic JWT with both a public key and a private key to handle the encoding and decoding of the tokens.
from pathlib import Path
public_ec_key = Path('/path') / 'to' / 'my-ec-public-key.pem'
private_ec_key = Path('/path') / 'to' / 'my-ec-private-key.pem'
Initialize(
app,
public_key=public_ec_key,
private_key=private_ec_key,
algorithm='ES256')
User level secrets¶
Sometimes, you may find it useful to have a different secret for every user of your application. One advantage of this could be the ability to invalidate any existing NON-expired access token for a single user, without interrupting any of your other users. Essentially, this could be equivalent to “logging out” (albeit, JWTs are by definition stateless, so it is only mimicking session based login/logout).
To implement this:
async def retrieve_user_secret(user_id):
return f"user_id|{user_id}"
Initialize(
app,
user_secret_enabled=True,
retrieve_user_secret=retrieve_user_secret,
)