JWT Authenticator (Python + Flask)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Alfred b7d265c1ee Add role field to returned data 1 anno fa
controllers Add role field to returned data 1 anno fa
imgs Finish README.md 1 anno fa
models Avoid repeated username in the same app 1 anno fa
requirements Add CORS 1 anno fa
resources Update README and jawth tool 1 anno fa
tests Avoid repeated username in the same app 1 anno fa
views Avoid repeated username in the same app 1 anno fa
.gitattributes Apply a boilerplate for project structure 1 anno fa
.gitignore First tests 1 anno fa
README.md Update README and jawth tool 1 anno fa
app.py Add CORS 1 anno fa
config.py Set port on config 1 anno fa
run.py Set port on config 1 anno fa
wsgi.py Finish README.md 1 anno fa

README.md

JaWTh

JWT Authenticator coded in python 3 using Flask. It publishes several end points to manage users and applications and the corresponding credentials.

How it works?

It stores applications and users. Applications have a name and a secret key for generating JWT. Users have a username, an encrypted password, and a timestamp of the last login between other fields. Each user corresponds to an application.

When a user makes a request of login in that application JaWTh compares the passwords, if the request is valid also signs a JWT with the username, the user uid, and the timestamp. JaWTh returns this info as token to the client. Then the client will send this JWT in each request for making any action into the application.

When the application receives the JWT, decodes the token and will apply the required actions for the user indicated into the token.

Security concerns

  • The timestamp allows to expire tokens at application level.
  • Passwords are stored encrypted (of course!).
  • The secret key for signing tokens should only be known by the encoder and the decoder (JaWth and the application).
  • To make changes into JaWTh you must know a password and its own secret key for JWT.
  • It forces the HS256 algorithm. So clients are not allowed to set the signature algorithm in the HTTP, with it they cannot set the signature to alg=none.

Possible improvements

  • For banning users it could make requests to webhooks implemented into the application target.
  • It could manage mailing for recovering passwords and confirm accounts. Also sign ups.
  • It could be an application key for letting them to add and remove users.
  • Specify CORS by app.
  • Improve error messages.

Using it

Clients comunicate with JaWTh using HTTP requests to the different end points. It is required to provide an auth request header with the value jwt <jwt>. Below you can find examples.

To generate the JWT there are multiple libraries for the most popular programming languages. jwt.io offers a tool to do it directly on their website.

The JSON to be encoded would be one that includes the JAWTH_KEY into the field password. If the request requires to senddata the JSON should also include another field with the field data.

Here is a generated JWT for a request that no requires to send data:

And here one that requires to send data:

Configuring and launching

To configure JaWTht there is a config file into the project root folder. It’s not recommended to change that but to create a new one. To specify the execution of JaWTh to use that one define a JAWTH_CONFIG environment variable with the path of the new config file.

Your config file should define the next variables:

  • DEBUG: Set to False
  • SECRET_KEY: The JaWTh secret key, the one to use to sign and decode those request sent to JaWTh.
  • JAWTH_KEY: The JaWTh password, the one to use on the JWT.
  • SQLALCHEMY_DATABASE_URI: The database uri.

Two examples of database uri are:

  • Sqlite: sqlite:////tmp/database.db
  • PostgreSQL: postgresql://postgres:mysecretpassword@127.0.0.1:5432/jawth

To use PostgreSQL remember to create a database before launching for first time JaWTh. For example: create database jawth;

To execute a JaWTh development version launch the run.py script.

To run it using Gunicorn use the wsgi.py script: gunicorn --bind 0.0.0.0:8000 wsgi:app

Examples

Here is a list of examples of how to use some of the end points.

List applications [Admin]

curl --request GET \
  --url http://127.0.0.1:5000/applications \
  --header 'auth: jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXNzd29yZCI6IkpBV1RIUEFTU1dPUkQifQ.w1w-sVSeIs4Z9pHtFuLx1dnnoK1MCg-zKW4JS9SKFYg' \
  --header 'content-type: application/json'

Create application [Admin]

curl --request POST \
  --url http://127.0.0.1:5000/applications \
  --header 'auth: jwt eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IkpBV1RIUEFTU1dPUkQiLCJkYXRhIjp7Im5hbWUiOiJ0ZXN0YXBwIiwic2VjcmV0IjoibmFuYW5hbmEifX0.Pb63sGNUFz5ebfzo-7pkic64MOPS-WgkKyaqncX1spQ' \
  --header 'content-type: application/json' \
  --data '{
	"name": "testapp",
	"secret": "nananana"
}'

Create user in application ‘testapp’ [Admin]

curl --request POST \
  --url http://127.0.0.1:5000/testapp/users \
  --header 'auth: jwt eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IkpBV1RIUEFTU1dPUkQiLCJkYXRhIjp7InVzZXJuYW1lIjoidXNlcjEiLCJwYXNzd29yZCI6InBhc3MxMjM0In19.y-gw7kpHdvuClAbfnxsAmloG3gOR_06_3x6SrNEjstg' \
  --header 'content-type: application/json' \
  --data '{
	"username": "user1",
	"password": "pass1234"
}'

List users in application ‘testapp’ [Admin]

curl --request GET \
  --url http://127.0.0.1:5000/testapp/users \
  --header 'auth: jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXNzd29yZCI6IkpBV1RIUEFTU1dPUkQifQ.w1w-sVSeIs4Z9pHtFuLx1dnnoK1MCg-zKW4JS9SKFYg' \
  --header 'content-type: application/json'

Change ‘user1’ password in application ‘testapp’ [Admin]

curl --request PATCH \
  --url http://127.0.0.1:5000/testapp/users/user1 \
  --header 'auth: jwt eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IkpBV1RIUEFTU1dPUkQiLCJkYXRhIjp7InBhc3N3b3JkIjoicGE1NXcwcmQifX0.p4Kj_isPF1jUszIxCkhS_5soFI7XWgFIW19Bnur8-ss' \
  --header 'content-type: application/json' \
  --data '{
	"password": "pa55w0rd"
}'

Login ‘user1’ with password ‘pa55w0rd’ in application ‘testapp’ [User]

curl --request POST \
  --url http://127.0.0.1:5000/testapp/login \
  --header 'content-type: application/json' \
  --data '{
	"username": "user1",
	"password": "pa55w0rd"
}'

Change ‘user1’ password [User]

Here we need a proper token. This one uses the time of last login: 2018-09-12 12:44:51.715047

curl --request POST \
  --url http://127.0.0.1:5000/testapp/user1/change_password \
  --header 'auth: jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwiaWQiOjEsInRpbWVzdGFtcCI6IjIwMTgtMDktMTIgMTI6NDQ6NTEuNzE1MDQ3In0.vjAWSCFUbGsvLBJa3-uCqEtDQ20KS5pc_bucXFOmw2A' \
  --header 'content-type: application/json' \
  --data '{
	"password": "pass1234again",
	"repeated_password": "pass1234again"
}'

Other end points

  • CRUD on all users: POST, GET, PATCH, DELETE ─ /users[/useruid]
  • CRUD on all applications: POST, GET, PATCH, DELETE ─ /application[/appname]