Sign tokens with blake2b, then verify them.
Project description
bootleg-jwt
bootleg-jwt
aims to mimic JSON Web Tokens in a simple, pydantic
way.
New for v0.3.0
Version 0.3.0 extends v0.2.x with backwards compatible addition of the _salt
and _person
arguments. When these arguments are passed alongside a token, the token's hash is generated using them. Note: the _salt
and _person
arguments may not be longer than 16 bytes. This is a limitation of the blake2b hashing algorithm. If a string is supplied, it will be encoded to utf-8 and truncated automatically. Otherwise, any bytestring longer than 16 bytes will fail validation. This is by design.
_salt
and _person
allow the token to be namespaced and salted. This provides an extra layer of validation, allowing implementations of BootlegJWT
to perform more checks on tokens, other than what is baked in. This would allow, for example, invalidating all tokens with a particular salt or namespace.
Table of Contents
Generate a token
from bootleg_jwt import BootlegJWT, Payload, header, body
from pydantic import BaseModel
from os import environ # An environment variable is required.
SECRET = "some-secret-key"
DURATION = 60 * 60 # Token expires after this many seconds
TYPE = "Testing Token" # An arbitrary name
environ['SECRET'] = SECRET # This module depends upon an environment
# variable `SECRET`. You may also set this
# secret in a `.env` file in your project's root,
# or by using `export SECRET="secret"`
## These two pydantic models are simple examples. They may have arbitrary names and data. They must only map to Token.body.user and Token.body.data
class UserData(BaseModel):
id: int
username: str
class BodyData(BaseModel):
info: str
value: bool
payload_user = UserData(id=69,username="nice")
payload_body = BodyData(info="Some Information", value=True)
payload = Payload(
header=header(duration=DURATION,type=TYPE),
body=body(user=payload_user,data=payload_body)
)
generate = BootlegJWT(payload=payload)
token = generate.TOKEN
encoded = generate.ENCODED
json = generate.JSON
validate = generate.VALID
divider = "\n------------------------------\n"
print(token,divider,encoded,divider,json,divider,validate)
Output (click to expand):
header=Header(type='Testing Token', duration=Duration(unit=Unit(type='time', name='seconds', shorthand='s'), value=3600), created=Timestamp(unit=Unit(type='time', name='seconds since epoch', shorthand='s+epoch'), value=1677382369), expires=Timestamp(unit=Unit(type='time', name='seconds since epoch', shorthand='s+epoch'), value=1677385969)) body=Body(user=UserData(id=69, username='nice'), data=BodyData(info='Some Information', value=True)) signature=Hash(value=b'e0b99c4eca2811bd9d164185219a283a4fddc2129ec0d2b3e5ba7b22596e4c7b8ac6d7b7c81812b50ace0a5b6d0be3ce5f977f753069d951bf15d13f179014df', algorithm='blake2b', keyed=True, salt=b'', person=b'')
------------------------------
b'eyJoZWFkZXIiOiB7InR5cGUiOiAiVGVzdGluZyBUb2tlbiIsICJkdXJhdGlvbiI6IHsidW5pdCI6IHsidHlwZSI6ICJ0aW1lIiwgIm5hbWUiOiAic2Vjb25kcyIsICJzaG9ydGhhbmQiOiAicyJ9LCAidmFsdWUiOiAzNjAwfSwgImNyZWF0ZWQiOiB7InVuaXQiOiB7InR5cGUiOiAidGltZSIsICJuYW1lIjogInNlY29uZHMgc2luY2UgZXBvY2giLCAic2hvcnRoYW5kIjogInMrZXBvY2gifSwgInZhbHVlIjogMTY3NzM4MjM2OX0sICJleHBpcmVzIjogeyJ1bml0IjogeyJ0eXBlIjogInRpbWUiLCAibmFtZSI6ICJzZWNvbmRzIHNpbmNlIGVwb2NoIiwgInNob3J0aGFuZCI6ICJzK2Vwb2NoIn0sICJ2YWx1ZSI6IDE2NzczODU5Njl9fSwgImJvZHkiOiB7InVzZXIiOiB7ImlkIjogNjksICJ1c2VybmFtZSI6ICJuaWNlIn0sICJkYXRhIjogeyJpbmZvIjogIlNvbWUgSW5mb3JtYXRpb24iLCAidmFsdWUiOiB0cnVlfX0sICJzaWduYXR1cmUiOiB7InZhbHVlIjogImUwYjk5YzRlY2EyODExYmQ5ZDE2NDE4NTIxOWEyODNhNGZkZGMyMTI5ZWMwZDJiM2U1YmE3YjIyNTk2ZTRjN2I4YWM2ZDdiN2M4MTgxMmI1MGFjZTBhNWI2ZDBiZTNjZTVmOTc3Zjc1MzA2OWQ5NTFiZjE1ZDEzZjE3OTAxNGRmIiwgImFsZ29yaXRobSI6ICJibGFrZTJiIiwgImtleWVkIjogdHJ1ZSwgInNhbHQiOiAiIiwgInBlcnNvbiI6ICIifX0='
------------------------------
{
"header": {
"type": "Testing Token",
"duration": {
"unit": {
"type": "time",
"name": "seconds",
"shorthand": "s"
},
"value": 3600
},
"created": {
"unit": {
"type": "time",
"name": "seconds since epoch",
"shorthand": "s+epoch"
},
"value": 1677382369
},
"expires": {
"unit": {
"type": "time",
"name": "seconds since epoch",
"shorthand": "s+epoch"
},
"value": 1677385969
}
},
"body": {
"user": {
"id": 69,
"username": "nice"
},
"data": {
"info": "Some Information",
"value": true
}
},
"signature": {
"value": "e0b99c4eca2811bd9d164185219a283a4fddc2129ec0d2b3e5ba7b22596e4c7b8ac6d7b7c81812b50ace0a5b6d0be3ce5f977f753069d951bf15d13f179014df",
"algorithm": "blake2b",
"keyed": true,
"salt": "",
"person": ""
}
}
------------------------------
True
Validate a token
from bootleg_jwt import BootlegJWT, Payload, header, body
from pydantic import BaseModel
from os import environ # An environment variable is required.
SECRET = "some-secret-key"
DURATION = 60 * 60 # Token expires after this many seconds
TYPE = "Testing Token" # An arbitrary name
environ['SECRET'] = SECRET # This module depends upon an environment
# variable `SECRET`. You may also set this
# secret in a `.env` file in your project's root,
# or by using `export SECRET="secret"`
## These two pydantic models are simple examples. They may have arbitrary names and data. They must only map to Token.body.user and Token.body.data
class UserData(BaseModel):
id: int
username: str
class BodyData(BaseModel):
info: str
value: bool
payload_user = UserData(id=69,username="nice")
payload_body = BodyData(info="Some Information", value=True)
payload = Payload(
header=header(duration=DURATION,type=TYPE),
body=body(user=payload_user,data=payload_body)
)
divider = "\n------------------------------\n"
def generate(payload):
generate = BootlegJWT(payload=payload)
decoded = generate.DECODED
encoded = generate.ENCODED
json = generate.JSON
validate = generate.VALID
print(decoded,divider,encoded,divider,json,divider,validate)
return encoded
def validate_token(token):
validate_token = BootlegJWT(token=token)
v_decoded = validate_token.DECODED
v_json = validate_token.JSON
v_valid = validate_token.VALID
print(v_decoded,divider,v_json,divider,v_valid)
validate_token(generate(payload))
Output (click to expand):
header=Header(type='Testing Token', duration=Duration(unit=Unit(type='time', name='seconds', shorthand='s'), value=3600), created=Timestamp(unit=Unit(type='time', name='seconds since epoch', shorthand='s+epoch'), value=1677383225), expires=Timestamp(unit=Unit(type='time', name='seconds since epoch', shorthand='s+epoch'), value=1677386825)) body=Body(user=UserData(id=69, username='nice'), data=BodyData(info='Some Information', value=True)) signature=Hash(value=b'9a6a3fc5c866442ee886c1d20f44fe49da29be4e56fd6f40a1c3e23f672d801c0d787f9f239265477da1339fffc41754f16a0899f5955aa0ed7602693919071d', algorithm='blake2b', keyed=True, salt=b'', person=b'')
------------------------------
b'eyJoZWFkZXIiOiB7InR5cGUiOiAiVGVzdGluZyBUb2tlbiIsICJkdXJhdGlvbiI6IHsidW5pdCI6IHsidHlwZSI6ICJ0aW1lIiwgIm5hbWUiOiAic2Vjb25kcyIsICJzaG9ydGhhbmQiOiAicyJ9LCAidmFsdWUiOiAzNjAwfSwgImNyZWF0ZWQiOiB7InVuaXQiOiB7InR5cGUiOiAidGltZSIsICJuYW1lIjogInNlY29uZHMgc2luY2UgZXBvY2giLCAic2hvcnRoYW5kIjogInMrZXBvY2gifSwgInZhbHVlIjogMTY3NzM4MzIyNX0sICJleHBpcmVzIjogeyJ1bml0IjogeyJ0eXBlIjogInRpbWUiLCAibmFtZSI6ICJzZWNvbmRzIHNpbmNlIGVwb2NoIiwgInNob3J0aGFuZCI6ICJzK2Vwb2NoIn0sICJ2YWx1ZSI6IDE2NzczODY4MjV9fSwgImJvZHkiOiB7InVzZXIiOiB7ImlkIjogNjksICJ1c2VybmFtZSI6ICJuaWNlIn0sICJkYXRhIjogeyJpbmZvIjogIlNvbWUgSW5mb3JtYXRpb24iLCAidmFsdWUiOiB0cnVlfX0sICJzaWduYXR1cmUiOiB7InZhbHVlIjogIjlhNmEzZmM1Yzg2NjQ0MmVlODg2YzFkMjBmNDRmZTQ5ZGEyOWJlNGU1NmZkNmY0MGExYzNlMjNmNjcyZDgwMWMwZDc4N2Y5ZjIzOTI2NTQ3N2RhMTMzOWZmZmM0MTc1NGYxNmEwODk5ZjU5NTVhYTBlZDc2MDI2OTM5MTkwNzFkIiwgImFsZ29yaXRobSI6ICJibGFrZTJiIiwgImtleWVkIjogdHJ1ZSwgInNhbHQiOiAiIiwgInBlcnNvbiI6ICIifX0='
------------------------------
{
"header": {
"type": "Testing Token",
"duration": {
"unit": {
"type": "time",
"name": "seconds",
"shorthand": "s"
},
"value": 3600
},
"created": {
"unit": {
"type": "time",
"name": "seconds since epoch",
"shorthand": "s+epoch"
},
"value": 1677383225
},
"expires": {
"unit": {
"type": "time",
"name": "seconds since epoch",
"shorthand": "s+epoch"
},
"value": 1677386825
}
},
"body": {
"user": {
"id": 69,
"username": "nice"
},
"data": {
"info": "Some Information",
"value": true
}
},
"signature": {
"value": "9a6a3fc5c866442ee886c1d20f44fe49da29be4e56fd6f40a1c3e23f672d801c0d787f9f239265477da1339fffc41754f16a0899f5955aa0ed7602693919071d",
"algorithm": "blake2b",
"keyed": true,
"salt": "",
"person": ""
}
}
------------------------------
True
header=Header(type='Testing Token', duration=Duration(unit=Unit(type='time', name='seconds', shorthand='s'), value=3600), created=Timestamp(unit=Unit(type='time', name='seconds since epoch', shorthand='s+epoch'), value=1677383225), expires=Timestamp(unit=Unit(type='time', name='seconds since epoch', shorthand='s+epoch'), value=1677386825)) body=Body(user={'id': 69, 'username': 'nice'}, data={'info': 'Some Information', 'value': True}) signature=Hash(value=b'9a6a3fc5c866442ee886c1d20f44fe49da29be4e56fd6f40a1c3e23f672d801c0d787f9f239265477da1339fffc41754f16a0899f5955aa0ed7602693919071d', algorithm='blake2b', keyed=True, salt=b'', person=b'')
------------------------------
{
"header": {
"type": "Testing Token",
"duration": {
"unit": {
"type": "time",
"name": "seconds",
"shorthand": "s"
},
"value": 3600
},
"created": {
"unit": {
"type": "time",
"name": "seconds since epoch",
"shorthand": "s+epoch"
},
"value": 1677383225
},
"expires": {
"unit": {
"type": "time",
"name": "seconds since epoch",
"shorthand": "s+epoch"
},
"value": 1677386825
}
},
"body": {
"user": {
"id": 69,
"username": "nice"
},
"data": {
"info": "Some Information",
"value": true
}
},
"signature": {
"value": "9a6a3fc5c866442ee886c1d20f44fe49da29be4e56fd6f40a1c3e23f672d801c0d787f9f239265477da1339fffc41754f16a0899f5955aa0ed7602693919071d",
"algorithm": "blake2b",
"keyed": true,
"salt": "",
"person": ""
}
}
------------------------------
True
Renew a token
Using BootlegJWT.renew(token)
, you may pass a token to BootlegJWT in bytestring form. This function will return an instance of BootlegJWT
with a new token and expiration date, retaining all of the input token's properties.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for bootleg_jwt-0.3.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7ec08f258b64caccc7f410ea7edc69ab9af1e3ea048c2e4a53cf03e23ed8534e |
|
MD5 | f8a714ec784fb35f5a77d9e05b663fa3 |
|
BLAKE2b-256 | 6b434221428eddd2e61cff7401ef6dc44cd14307bd2ba607438e195ac051d685 |