Authorization
The authorization system makes use of JSON Web Tokens. These tokens contain base64 encoded user profile information that can be decoded by client or server applications. The tokens are signed with a secret key available in the API that allows the API to ensure that the tokens are not altered after being created. Our token system bootstraps off of Google's, which reduced the implementation overhead. The user's profile data come from Google, then the API requests what Google Groups that user is a part of and packages that JSON data into a token.
To make a request to the API from an external server, a token must be first obtained and passed in
each request as a cookie called authHeaders
. If logging in using /auth/client
, this cookie is
set in the users browser automatically, and it will be removed when it expires. Subsequent requests
to the same top-level domain from when the cookie was obtained (e.g.https://hakai.org
will
automatically include the cookie in the request.
Alternatively, if it's not possible to store and save a cookie, the value of the JWT auth token can be saved somewhere and included in requests to hakai-api under the "Authorization" request header like so:
"Authorization": "Bearer eyu172319asdf..."
Auth endpoints
These endpoints should be used to obtain JWT tokens in web applications.
type | url | function |
---|---|---|
GET | /auth/client | Redirect users to the correct Google Sign in page url. After signing in, it will redirect the JWT token to be used in subsequent requests back to the requesting site in the form of querystring parameters. This can be overridden be setting ?state=http://url.to.redirect.to as a querystring parameter in the original request. |
GET | /auth/code2jwt | Used internally to issue a JWT token to the user that requested one form the /auth/client url. |
GET | /auth/validate_token | get the access-token from the header and verify it's valid. Return user profile. |
* | /auth/logout | Clears any auth cookies from the requestors browser. |
GET | /auth/login | Alias for /auth/client |
GET | /login | Alias for /auth/client |
* | /logout | Alias for /auth/logout |
Authorization flow
sequenceDiagram
actor User
participant Client
participant API
participant Google
User->>Client: Clicks "Login" button
Client->>API: GET /auth/client
API->>Google: Redirect to Google Sign In
Google->>User: Show Google Sign In page
User->>Google: Sign In
rect rgb(191, 223, 255)
Google->>API: Redirect to /auth/code2jwt
API->>Google: Exchange code for token
Google->>API: Responds with token
note over API: Add user info to JWT
end
API->>User: Redirect to client with JWT token
User->>Client: JWT token is stored in cookie
Client->>API: GET /api/endpoint
API->>Client: Response with data
JWT token structure
You can inspect the content of the JWT token in your browsers developer tools. The token is stored
in the authHeaders
cookie. The token is a base64 encoded JSON object that contains the following:
{
"authHeaders": {
"token-type": "Bearer",
"access-token": "eya.bc.123",
"expires-in": 43200,
"expires-at": 1677563877
}
}
You can copy the value of "access-token" and run it through a a JWT decoder like https://jwt.io/ to
decode the token and see the contents of what is stored there. Alternatively, use a base64 decoder
to decode each of the .
separated parts of the token and inspect the JSON data. It is often
useful to do this in client applications to get the name, profile picture, or group
permissions of the user that is signed in. Here are some example contents from the encoded data:
{
"id": "10423959575911111111",
"email": "bob.smith@hakai.org",
"verified_email": true,
"name": "Bob Smith",
"given_name": "Bob",
"family_name": "Smith",
"link": "https://plus.google.com/10423959575911111111",
"picture": "https://lh3.googleusercontent.com/a/AGBasdf-as1-",
"locale": "en",
"hd": "hakai.org",
"groups": [
"bob.smith@hakai.org",
"fakeapp.editors@hakai.org",
"fakeapp.readonly@hakai.org",
"muckraker.team@hakai.org"
],
"workareas": [
"QUADRA",
"LOWER MAINLAND"
],
"organizations": [
"HAKAI"
],
"iat": 1677520677,
"exp": 1677563877,
"iss": "hakai-api"
}
Postman
Note
This section needs improvement. The following instructions work, but require authenticating outside of Postman. It would be better to have a way to authenticate without having to open a browser window.
To test the API using Goose or Hecate using the Postman application do the following:
- Open a browser and navigate to https://hecate.hakai.org/api-client-login
- Login with your Google account
- On the next page, copy the access_token that is displayed in the text box (Not the entire JSON
object!).
- This text will look like
token_type=Bearer&access_token=ey...&expires_at=1677570587
- Copy the text starting from
ey
up to, but not including, the&expirs_at=...
part.
- This text will look like
- Open Postman and, under the URL, click "Auth"
- Select Type: "Bearer Token"
- Paste the copied access token into the "Token" field
- You can now test this worked by making a request in Postman
to https://hecate.hakai.org/api/whoami
- If it worked, you should see the content of your token with your name and google groups in the response.
Long-lived tokens
If you know what you're doing, you can generate a Hakai-API token that lasts for a year instead of a single day, side-stepping the need to log-in as a user every day. This is useful for allowing automatic scripts that cannot possibly run on the same server as the API and thus require an auth token. To do this:
- Log into the server you want a token for using ssh
- Navigate to the hakai-api directory
- Run
npm run generate-token
- Follow the prompts and record the token that you're given for later use
It's possible to invalidate these tokens by changing the JWT_SECRET in the server's .env file in the hakai-api directory, but it may already be too late to avoid damage to our database if an attacker obtained a long-lived token. Be careful.