Skip to content

Developer

Before Committing

Before you push your code to the remote repository, install commitizen.

Bash
pip install commitizen

Then, install the pre-commit hooks using:

Bash
poetry run pre-commit install

Now you can add your changes with git add <filename.py> to stage them for committing.

When ready to push the commit, run:

Bash
poetry run cz c

Respository Structure

The chime-frb-api has four major distinctive components:

  • core manages the authentication and the HTTP query to the backend.
  • modules are individual sets of API calls, generally mapping to a sanic blueprint.
  • backends are a collection of modules. They offer a singular object with all services availaible through a backend.
  • tests can be both for a backend or an individual module.

JWT Tokens

Access Tokens

Simply put, an ACCESS_TOKEN is a string with a bunch of characters that are used as credentials, to grant access to resources. More generally they are JSON Web Tokens.

ACCESS_TOKEN is

  • Just a JWT!
  • Disposable, i.e. they can be generated on-demand and in unlimited quantities.
  • Valid for only a short period, ~30 minutes.
  • Unrevokable, i.e. if you have an access token, and it is valid - the CHIME/FRB backend will process your request

Example

Text Only
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZGVidWciLCJleHAiOjE1NzQ0NjE2ODcsImlhdCI6MTU3NDQ1OTg4N30.wHVjUpZRINR0wLaxhLNOPMX3rJbVaicI4J-vNkJOGDM

On closer inspection, ACCESS_TOKEN consists of three parts separated by a period:

  • eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
  • eyJ1c2VyX2lkIjoiZGVidWciLCJleHAiOjE1NzQ0NjE2ODcsImlhdCI6MTU3NDQ1OTg4N30
  • wHVjUpZRINR0wLaxhLNOPMX3rJbVaicI4J-vNkJOGDM

In order of appearance, these parts are the Header, Payload, and the Signature.

Header

Contains information on how the JWT was encoded and is used to decode the payload.

Python
{"typ": "JWT", "alg": "HS256"}
Payload

contains key/value pairs of information. Each one of these is called a claim.

Python
{"user_id": "debug", "exp": 1574461687, "iat": 1574459887, "iss": "frb-master"}

In this example, there are four claims passed through the JWT:

  1. user_id - Name of the user who the token was issued to
  2. exp - ctime at which this token will expire
  3. iat - ctime at which the token was issued at
  4. iss - Issuer of the token, i.e. frb-master

In the future, backends will support more complex claims as well, e.g. scopes, rate or audiences for requests.

Signature

Contains some hot mess of characters that can only be decoded by the server which issued the token. The server uses a secret passphrase and the payload to create the signature, thus ensuring that the payload cannot be maliciously altered.

Refresh Tokens

Refresh tokens are simply an encrypted alphanumeric keys, generated by the CHIME/FRB backends. Every time an ACCESS_TOKEN expires, the client needs to re-authenticate with the username/password to create a new ACCESS_TOKEN. But requiring the user to insert username/password each time an ACCESS_TOKEN expires or asking them to save credentials in code or as environment variables, is neither safe or convenient. REFRESH_TOKEN solve this problem.

REFRESH_TOKEN is

  • NOT a JWT, rather its just a universally unique identifiers
  • Stored by the backend on a per user basis
  • Used to generate a new ACCESS_TOKEN when sent along with active or expired ACCESS_TOKEN.

Note

When you generate a new ACCESS_TOKEN using your credentials, it invalidates the currently issued tokens.

Generate Tokens

Generating a new set tokens requires CHIME credentials. When successfull, the backend responds with an ACCESS_TOKEN and REFRESH_TOKEN. Here is a code agnostic example,

Request
Bash
curl -X POST -H "Content-Type: application/json" -d '{"username": "<USERNAME>", "password": "<PASSWORD>"}' https://frb.chimenet.ca/frb-master/auth
Response
Bash
{"access_token" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZGVidWciLCJleHAiOjE1NzQ0NjQ1NzUsImlhdCI6MTU3NDQ2Mjc3NX0.I2RGLQo6smTT9GYkDXKsZ0Lz7q2aUXfrnm34-YedfB8",
"refresh_token" : "07cf9126e80f6fec1f1ea38a9d094ef28c6ea3eb3b270166"}

Validate Tokens

REFRESH_TOKEN and ACCESS_TOKEN work together to provide a friendly, yet secure and authentication environment. To check, whether an ACCESS_TOKEN is valid, you can use the verify endpoint,

Request
Bash
curl -X GET -H "Authorization: <ACCESS_TOKEN>" https://frb.chimenet.ca/frb-master/auth/verify
Response
Bash
200 Response
{"valid": true}

## or
401 Response
{"valid":false,
 "reasons":["Signature has expired."],
 "exception":"InvalidToken"}

## If no tokens are present
400 Response
{"reasons": ["Authorization header not present."],
 "exception": "Unauthorized"}

Refresh Tokens

In order acquire a new ACCESS_TOKEN given an existing REFRESH_TOKEN and ACCESS_TOKEN you can use the refresh endpoint,

Request
Bash
curl -X POST -H "Content-Type: application/json" -H "Authorization: <ACCESS_TOKEN>" -d '{"refresh_token": "<REFRESH_TOKEN>"}' https://frb.chimenet.ca/frb-master/auth/refresh
Response
Text Only
{"access_token": "<JWT>"}

Use Tokens

To authenticate, all you need to do is attach the authorization header to a HTTP Request

Request
Bash
curl -X GET -H "Content-Type: application/json" -H "Authorization: <ACCESS_TOKEN>" https://frb.chimenet.ca/frb-master/v1/events
Response
Python
import requests

authorization = {"Authorization": "ACCESS_TOKEN"}
response = requests.get(
    url="https://frb.chimenet.ca/frb-master/v1/events", headers=authorization
)

Example

Complete example in Python, using the requests package.

Code
Python
import requests

base_url = "https://frb.chimenet.ca/frb-master"
payload = {"username": "<USERNAME>", "password": "<PASSWORD>"}

# Acquire tokens from frb-master
response = requests.post(url=base_url + "/auth", json=payload)
response.raise_for_status()
tokens = response.json()
access_token = response.json().get("access_token")
refresh_token = response.json().get("refresh_token")

# Verify access token
authorization = {"Authorization": access_token}
response = requests.get(url=base_url + "/auth/verify", headers=authorization)
response.raise_for_status()
print(response.json())

# Refresh access_token
refresh_payload = {"refresh_token": refresh_token}
response = requests.post(
    url=base_url + "/auth/refresh", headers=authorization, json=refresh_payload
)
response.raise_for_status
access_token = response.json().get("access_token")