Package: jwthenticator
JWThenticatorA cloud first service for key to JWT authentication library and server written in Python 3.IntroJWThenticator was written for client authentication in micro-services architectures with usage of API gateways in mind.Although there are multiple open-source projects for authenticating users in exchange for JWT (json web token), we couldn't find any project that fit our need for a key based authentication for our clients. This is beneficial for any client authentication and more specifically for IoT.The service is stateless, Docker first service for cloud authentication, but can generally be used for any key to JWT authentication and in multiple different architectures (see examplebelow).How To UsePippipinstalljwthenticatorTo run as a server you can run:python3 -m jwthenticator.server.Make sure to configure the proper database to be used via the environment variables exposed injwthenticator/consts.pyfile.By default PostgreSQL is used and a basic local config setup is:exportDB_USER="my-postgres-user"exportDB_PASS="my-postgres-pass"Note - if RSA keys are not provided (via the environment variablesRSA_PUBLIC_KEY+RSA_PRIVATE_KEYorRSA_PUBLIC_KEY_PATH+RSA_PRIVATE_KEY_PATH), a new RSA pair will be generated every time the systems goes up.Dockerdockerpullclarotyltd/jwthenticator
dockerrun-p8080:8080clarotyltd/jwthenticatorA database is needed to be linked or configured to the image.Seeexamples/docker-compose.ymlfor a full example, run it using:cdexamples
docker-composeupFrom SourceThe project usespoetryfor dependency management and packaging.To run from source clone project and:pipinstallpoetry
poetryinstallDocumentationAPI documentation -openapi.yamlfile (ex Swagger)Configurable environment variables -jwthenticator/consts.pyCode usage examples -Code ExamplesExample architecture -Example ArchitectureDiagrams -docsfolder for some UMLsequence diagramsand Python diagrams usingdiagramsCode ExamplesFor full examples see theexamplesfolder.ClientTo make it easier to work agains a JWThenticator protected server (either directly or via API gateway), a client class is provided.TheClientclass handles auth state management against JWThenticator. It handles JWthenticator responses for you, performs authentication for you, and JWT refresh when needed.It exposes arequest_with_authfunction (and the simplerget_with_authandpost_with_auth) that manages all interactions against the secured service and the JWThenticator itself for you.Example usage:fromuuidimportuuid4fromjwthenticator.clientimportClientidentifier=uuid4()client=Client("https://my-jwthenticator-host/",identifier,key="my-awesome-key")response=awaitclient.get_with_auth("https://my-secure-server/")ServerAlthough JWThenticator was designed with an API gateway in mind, it can be used to authenticate server endpoints directly.For easy usage with anaiohttpPython server you can do the following:fromaiohttpimportwebfromjwthenticator.server_utilsimportauthenticateapp=web.Application()@authenticate("https://my-jwthenticator-host/")asyncdefsecure_index(request:web.Request)->web.Response:return"Secure hello world!"app.add_routes([web.get("/",secure_index)])web.run_app(app)Example ArchitectureA visual example on how JWThenticator is and can be used.Additional ones can be found indocsfolder.API Gateway ArchitectureGenerated fromdocs/api_gateway_architecture_diagram.pyAPI Gateway REST Sequence DiagramGenerated fromdocs/api_gateway_flow.diagHow it worksThere are 3 key components to JWThenticator:KeysKeys that are registered against the service and can then be used for authentication.All keys are registered to the database, have an expiration time (change default of 30 minutes using the env varKEY_EXPIRYin seconds), identifier of the registrant and some other metadata stored about them.The identifier is usefull if a key needs to be linked later to a specific server or route.Refresh tokensSince JWTs are short lived and keys should be kept safe, an intermediate method is needed so we don't have a long lived JWTs or use our secret key every 30 minutes (by default). This is where refresh token come into play.Refresh tokens are received from a successfull authentication and are used for receiving a new JWTs after they expire.They are recoreded in the database, have an expiration time (change default of 60 days using  the env varREFRESH_TOKEN_EXPIRYin seconds) and some other metadata stored about them.You can check outjwthenticator/models.pyto see what data is stored in the database.JWTsThe industry standard JWT (RFC 7519). The JWT is used for verification against an API gateway, JWThenticator itself, or any service / code you use for you auth verification.The JWTs are short lived (as they should be) with a configurable lease time viaJWT_LEASE_TIMEenv var.Additionaly, similarly to the keys we use a UUID identifier in the authentication process and store it in the JWT's payload. This is useful for better client identification or smarter k8s routing.Addtional FeaturesAll consts can be overriden via environment variables, seejwthenticator/consts.pyfor the full list.Service contains both internal and public routes, the admin / public API's can be disabled by setting theDISABLE_EXTERNAL_APIorDISABLE_INTERNAL_APIenv vars. This is very important when running the service in production environments, you don't want to expose the key registration to the general public :).The service can be used with any JWT verification service or API gateway using the industry standard JWKS (RFC 7517) via/jwksAPI call.JWThenticator can be used as anNginx authenticationbackend using the/validate_requestAPI call.Some requests require giving a UUID identifier. Even though the service doesn't enforce its verification, it can be used as a mean of identifiying incoming users, smart routing, and later for additional validations.All REST API schemas are defined using Pythondataclasses and validated usingmarshmallow_dataclass, seeschemas.py.