Package: handshake
handshakeA Python library to create and validate authentication tokens.handshakeis used to generate and validate arbitrary authentication tokens
that contain arbitrary metadata and support expiration. It uses basic
cryptographic primitives (hashing, HMACs) and is based around the concept of a
shared private secret for security.Example usage would be to create namespaced authentication tokens for clients
of an API which another service can check is valid and hasn't expired. The
tokens are safe to be made public, put in headers etc. and can be used like
session tokens.The tokens are strings in the format of:arbitrary:data:here:timestamp:random:signatureAll fields other than timestamp, random and signature are optional. Signatures
are in the format of:HMAC(arbitrary:data:here:timestamp:random){shared_secret}The library is designed to allow whatever metadata is required into the token,
such as the first parameter could be a namespace and the second parameter an
object id. This allows tokens to be easily split between internal systems and
uses while containing metadata or IDs for other objects.For example, you could usehandshaketo allow an API to generate tokens which
a client stores for a variable amount of time and can verify their state with
other services. The arbitrary data prefix can be used to store an application
namespace and the UUID of the object being referenced (such asuser:uuidorservice:recordtype:uuid). This library is of most use if you have multiple
diverse systems, microservices or other distributed endpoints that require
ad-hoc authentication and something like JWT or OAuth is overkill.Installationhandshakeis pure Python and has no dependancies. You can installhandshakevia pip:$pipinstallhandshakeAny modern version of Python3 will be compatible.Usagehandshakehas one class providing two basic public functions. Examples:importosfromhandshakeimportAuthToken# The shared secret, keep this private, can be str or bytes but needs to be# from a cryptographically secure sourcesecret=os.urandom(128)# Create the instancetoken=AuthToken(secret)# Basic token with no additional parametersplain_token=token.create()token.verify(plain_token)# The token must be no more than 300 seconds oldplain_token=token.create()token.verify(plain_token,time_range=300)# Namespaced but no specific item, namespace is arbitrarynamespaced_token=token.create('namespace')token.verify(namespaced_token)# Namespaced and with an arbitrary item IDfromuuidimportuuid4client_token=token.create('user',uuid4())token.verify(client_token)# Lots of metadataclient_token=token.create('network','node','12345','67890')token.verify(client_token)# Use blake2s for hashes and signaturesfromhashlibimportblake2stoken=AuthToken(secret,hashfunc=blake2s)blake2s_token=token.create()token.verify(blake2s_token)If a token fails to validate it raise the relevent exception:# Create a token with one secrettoken=AuthToken('a fixed secret string')plain_token=token.create()# Attempt to verify it with a different token, this is invalidtoken_with_different_secret=AuthToken('not the same secret string')token_with_different_secret.verify(plain_token)# ... a child of handshake.errors.InvalidTokenError exception is raisedLimitationsThe secret must be at least 16 bytes or characters and no more than 1024 bytes
or characters. The total generated token length cannot be longer than 2048
characters.Full API synopsishandshake.AuthToken(secret=str_or_bool, hashfunc=function)Initiates an AuthToken object using the specified secret. The secret is
required. It must be either a string or a bytes and must be between 32 and 1024
characters or bytes in length. The secret should be sourced from a
cryptographically safe random source, such asos.urandom.hashfuncdefaults tohashlib.sha256but you can replace it with another
hash function if you need to.handshake.AuthToken.create(*arbitrary str)Creates an authentication token.handshake.AuthToken.verify(token=str, time_range=int)Verifies an authentication token created withhandshake.AuthToken.create().time_rangeis an optional integer which if set specifies the valid time
range the token must have been generated within. This is used to verify
expiring tokens. It defaults to0which disables time range validation.If the token is valid a tuple containing any arbitrary data in the token. For
example a token ofarbitrary:data:here:timestamp:random:signatureIf valid would return a tuple of:('arbitrary','data','here')If the token is invalid for any reason ahandshake.errors.InvalidTokenErrorexception is raised (or a child exception ofhandshake.errors.InvalidTokenError). You can handle different errors by
catching them specifically and the exception names describe the event:importosfromhandshakeimportAuthToken,errorssecret=os.urandom(128)token=AuthToken(secret)test_token=token.create()try:token.verify(test_token)excepterrors.TokenExpiredErrorase:print(e)excepterrors.TokenSignatureErrorase:print(e)excepterrors.InvalidTokenErrorase:print(e)TestsThere is a test suite that you can run by cloning this repository and
executing:$maketestContributingAll properly formatted and sensible pull requests, issues and comments are
welcome.