Skip to content
This repository has been archived by the owner on May 9, 2023. It is now read-only.

Latest commit

 

History

History
129 lines (94 loc) · 5.06 KB

authorization_flow.md

File metadata and controls

129 lines (94 loc) · 5.06 KB

Authorization flow

The authorization flow is implemented as a set of HTTP redirects back and forth between the three systems. It only supports browser authentication.

State

During redirects between Auth API and other systems, you will notice a state object is being passed back and forth. It contains all data collected during the entire authorization flow, and exists so that Auth API can be stateless. It is a signed JWT containing all data, so only Auth API can edit its content.

Sequence diagram

Login flow

alt text

Logout flow

alt text

Tokens

Upon successful authorization, the Auth API creates two tokens representing the user; An internal token containing all necessary details about the user, and an opaque token which refers to the internal token, but does not contain any data in itself (it is a random generated UUID4).

The opaque token is set as an HTTP Cookie on the Auth API's domain, so that subsequent requests to the API includes the token automatically. The client never gets access to the internal token.

Once requests hit the API, an API Gateway (Træfik) translates the opaque token into the internal token using a ForwardAuth endpoint, and then forwards the requests to the appropriate service. Thus, from the perspective of a service, only the internal token exists (and is relevant).

TODO Internal token is JWT

Actor vs. Subject

An important distinction to make is the difference between an Actor and a Subject.

Internal tokens contains both actor and subject field:

  • The actor describes the user doing the action, and will contain the unique ID of the user who authenticated.
  • The subject describes who the action were done on behalf of.

This allows issuing tokens and invoking the API on behalf of someone else. For example, if John acts on behalf of Lisa, the actor would be John, and the subject would be Lisa. Thus John would have access to all of Lisa's data, however, any action made by John can be tracked back to him.

Scopes

Tokens are always issued with a list of scopes. They determine which privileges a client has when making requests to the API. When authenticating as one self (ie. not working on behalf of someone else), the token would contain all scopes necessary to access and mutate one's own data. When working on behalf of someone else, the token could contain a limited set of scopes.

Examples of scopes:

  • meteringpoints.read
  • measurements.read
  • etc.

TODO List all possible scopes?

Interpreting opaque tokens

The sequence diagram below illustrates the flow of incoming requests to the API, and how the opaque token is translated into an internal token before being routed to the service.

  • Client makes a request to the API's public URL, which is routed to the API Gateway
  • The API Gateway invokes a ForwardAuth endpoint on the Auth API (forwarding the client's original request)
  • The Auth API reads the opaque token from an HTTP cookie, and queries a simple key-value database for an internal token.
  • If the token exists and is valid, the ForwardAuth endpoint returns an HTTP 200 OK along with the internal token in the Authorization response header
    • The request is forwarded to the service containing the Authorization request header
  • Otherwise, an HTTP 401 Unauthorized is returned to the client, stopping further processing of the request

alt text

Internal tokens

As illustrated it the diagram above, services only have to concern itself with the internal token. It contains all necessary data for services to decide whether a request is properly authorized. The internal token is a JWT signed by Auth API.

An example of an internal token:

{
  "name": "John Doe",
  "company": "John's Fine Wine",
  "issued": "2021-12-22 13:50:20+00:00",
  "expires": "2022-01-22 13:50:20+00:00",
  "actor": "33785eeb-2527-4e43-b091-8496af1972b0",
  "subject": "1f71a97b-a207-47b5-a5bf-0bd1006245d4",
  "scope": ["meteringpoints.read", "measurements.read"]
}

Frontend security policies

To ensure strict and secure communication between our frontend applications and the Auth API, we use HTTPS with strict Content Security Policy and CORS policy. The authorization cookie set by the Auth API uses the HttpOnly, Secure, and SameSite=Strict flags in non-development environments.

To allow local frontend development against remote development environments, the authorization cookie is sent to the client (no HttpOnly flag) with SameSite=None. The Secure flag is support by requests from a localhost origin. HTTP clients must forward credentials with every request except for the OIDC login endpoint. This requires the Auth API to respond with an Access-Control-Allow-Credentials: true header. Requests from the http://localhost:4200 origin are allowed by responding with an Access-Control-Allow-Origin: http://localhost:4200 header.