wellcomecollection / identity

Identity services for Wellcome Collection users
MIT License
0 stars 2 forks source link

identity

Build status

This repo contains identity-related services for Wellcome Collection users. This allows users to sign in on wellcomecollection.org and keeps their personal information safe.

Architecture

flowchart LR
    W[identity web app] --> IA[API authorizer]
    IA --> I_API[items API]
    IA --> R_API[requests API]
    IA --> API[identity API]
    API --> A0[(Auth0)]
    API --> SI[(Sierra)]
    I_API --> SI
    R_API --> SI
    A0 --> SI

    classDef externalNode fill:#e8e8e8,stroke:#8f8f8f
    class W,I_API,R_API externalNode

    classDef repoNode fill:#c8ecee,stroke:#298187,stroke-width:2px
    class IA,API,A0,SI repoNode

The canonical store of user information is Sierra, our library management system.

We put Auth0 in front of Sierra to act as a modern identity provider where we require authentication and authorization. Some of our code runs inside Auth0 in the form of Custom DB Scripts to interface with Sierra, or as Actions to augment its default behaviour.

We provide the Identity API for other services (like the identity webapp in the wellcomecollection.org repo) to be able to retrieve and mutate the data in Sierra. This is a TypesScript Lambda.

The API authorizer is another TypeScript Lambda within API Gateway that checks (a) the validity of access tokens (from Auth0) to authenticate the user and (b) that they are able to operate on the requested resource (usually themselves) to authorize them. It also protects the Items API and Requests API, which are used to manage items which users have requested from library stores. These APIs are defined in the catalogue-api repo.

Users experience these services/APIs through the identity web app, which is defined in the wellcomecollection.org repo.

In the terminology of the OAuth 2.0 specification, the primary resource owner is the identity web app, the 3 APIs combined with the authorizer constitute the resource server, and Auth0 functions as the authorization server. We use the Authorization Code Grant.

Login flow

The sequence of requests/actions between different entities for a login followed by an action is as follows:

sequenceDiagram
    participant User
    participant ID_APP as Identity Webapp
    participant A0_LOGIN as Auth0 Universal Login Screen
    participant A0_TENANT as Auth0 Tenant
    participant Sierra
    participant ID_AUTH as Identity Authorizer
    participant RESOURCE_APIS as Identity/Items/Requests APIs

    User->>ID_APP: Click login
    note right of ID_APP: Performed by Auth0 SDK
    ID_APP->>A0_LOGIN: Redirect
    A0_LOGIN->>A0_TENANT: Login request

    rect rgb(240, 240, 240)
        note over A0_TENANT, Sierra: Custom DB scripts
        A0_TENANT->>Sierra: Validate credentials
        Sierra->>Sierra: Credentials are correct
        Sierra->>A0_TENANT: User authenticated
    end

    rect rgb(240, 240, 240)
        note right of ID_APP: Webapp side performed by Auth0 SDK
        A0_TENANT->>ID_APP: Authorization code
        ID_APP->>A0_TENANT: Code and application credentials
        rect rgb(230, 230, 230)
            note right of A0_TENANT: Auth0 Actions
            A0_TENANT->>A0_TENANT: Add custom claims to tokens
        end
        A0_TENANT->>ID_APP: ID, access, and refresh tokens
        ID_APP->>ID_APP: Tokens stored in session cookie<br />(HttpOnly, Secure, symmetrically encrypted)
    end

    User->>ID_APP: Request to /account/api/*
    ID_APP->>ID_AUTH: Proxied request with access token added from session cookie
    ID_AUTH->>ID_AUTH: Verify token is valid and signed by our Auth0 Tenant
    ID_AUTH->>RESOURCE_APIS: Authorized request
    RESOURCE_APIS->>Sierra: Modify user or create hold request
    Sierra->>RESOURCE_APIS: Sierra response
    RESOURCE_APIS->>User: API response

Registration flow

See the Auth0 docs for details on the redirect flow used here.

sequenceDiagram
    participant User
    participant ID_APP as Identity Webapp
    participant A0_LOGIN as Auth0 Universal Login Screen
    participant A0_TENANT as Auth0 Tenant
    participant Sierra
    participant ID_API as Identity API

    User->>A0_LOGIN: Sign up with email/password
    A0_LOGIN->>A0_TENANT: Request to create user
    rect rgb(240, 240, 240)
        note over A0_TENANT, Sierra: Custom DB scripts
        A0_TENANT->>Sierra: Create user
        Sierra->>Sierra: User is created with <br />Self-registered patron type
        Sierra->>A0_TENANT: New user
    end

    A0_TENANT->>ID_APP: Authorization code
    ID_APP->>A0_TENANT: Code and application credentials
    rect rgb(230, 230, 230)
        note right of A0_TENANT: Auth0 Actions
        A0_TENANT->>A0_TENANT: Check whether user profile is filled
        A0_TENANT->>User: Redirect to registration screen
    end

    User->>ID_APP: Fill in name and other details
    ID_APP->>ID_API: Update user details
    ID_API->>Sierra: Modify user in Sierra
    Sierra->ID_API: Response
    ID_API->>ID_APP: Response
    ID_APP->>User: 302 Redirect back to tenant
    User->>A0_TENANT: Continue login flow
    A0_TENANT->>ID_APP: ID, access, and refresh tokens
    ID_APP->>ID_APP: Tokens stored in session cookie

Developing

Installing the project dependencies (ie yarn at the root) will also set up lint-staged to run prettier as a pre-commit hook. This formatting is also checked in CI. You might also want to set up your editor to run prettier on save!

Terraform

This project uses Terraform workspaces to accomodate multiple environments.

You will need to switch workspace to plan/apply in a particular environment:

> terraform workspace list
* default
  prod
  stage

> terraform workspace select stage
Switched to workspace "stage".

Continuous Integration

This project builds in Buildkite where there are pipelines to cover build and deployment through stage and production environments.

Build status

Merged pull requests will be automatically deployed to the staging environment, but a production deploy requires manual unblocking in Buildkite.

Build images

The build uses a docker image generated from the Dockerfile in the .buildkite folder.

When this image is updated it is necessary to push this image to ECR manually.

# Log in to ECR via docker (you may need to use a different profile)
aws ecr get-login-password --region eu-west-1 --profile identity | docker login --username AWS --password-stdin 770700576653.dkr.ecr.eu-west-1.amazonaws.com

# Build the latest image
docker build . -t 770700576653.dkr.ecr.eu-west-1.amazonaws.com/uk.ac.wellcome/identity-build:latest

# Push the latest image
docker push 770700576653.dkr.ecr.eu-west-1.amazonaws.com/uk.ac.wellcome/identity-build:latest