Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 9 Next »

Overview

The Ed-Fi API needs to be secured with the OAuth 2.0 Client Credentials flow. In many cases a third-party authentication provider will be most appropriate for managing authentication. However, some organizations may wish to continue using a built-in authentication provider, as with the ODS/API Suite 3. In addition to managing the authentication process itself, the built-in provider should handle provisioning of keys and secrets. In this way, we will be able to have a micro data store that is accessed by only a single application.

Requirements

Authentication / Token Generation

Support Client Credentials flow

  1. Example route signature: POST /oauth/token 
  2. with support for the following message body formats:
    1. grant_type , client_id , and client_secret  in a JSON payload
    2. grant_type , client_id , and client_secret  in a form-urlencoded payload
    3. grant_type in a json payload, with client_id , and client_secret  encoded into a basic authentication header
    4. grant_type in a form-urlencoded payload, with client_id , and client_secret  encoded into a basic authentication header.
  3. with responses:
    1. 200 when the request is valid, with a signed JSON Web Token (JWT) as an access code response (more detail on JWT below). Example:

      {
        "access_token": "eyJ0eXAiOiJKV1QiLCJibGciOiJIUzI1NiJ9.eyJpc3MiOiJlZC1maS1tZWfkb3dsYXJrIiwiYXVkIjoibWVhZG93bGFyayIsInJvbGVzIjpbInZlbmRvciJdLCJzdWIiOiJzdXBlci1ncmVhdC1TSVMiLCJqdGkiOiIyODQxNTY3Yi0wNzRiLTRiMDktYmQwMS1jZGYyODVlY2NjMDEiLCJpYXQiOjE2NTkzNzA2MjgsImV4cCI6MTY1OTM3NDIyOH0.GKwl3Uactabl6emQy9Ta2R5emGL6IF_v8w85LoR2wAs",
        "token_type": "bearer",
        "expires_in": 1659374228,
        "refresh_token": "not available"
      }


    2. 400 when the payload structure is invalid or the grant_type  is invalid. Example:

      {
        "message": "The request is invalid.",
        "modelState": {
          "grant_type": [
            "The grant_type '???' is not supported."
          ]
        }
      }

      This is an "ideal" example that provides some consistency with existing messaging. The actual solution can be different based on the package components used in the solution.

    3. 401 with no message body when the client_id  or client_secret  is invalid. (warning) Deliberately not revealing why the authentication attempt failed.

JSON Web Token Response

The access token provided by the /oauth/token endpoint should be in the format of a signed JSON Web Token (JWT). The expected format of the JWT is described in some detail in Meadowlark - Data Authorization. In summary, the token's payload is expected to match this structure:

{
  "iss": "ed-fi-meadowlark",
  "aud": "ed-fi-meadowlark",
  "sub": "client name",
  "jti": "3d59b75f-a762-4baa-9116-19c82fdf8de3",
  "iat": 1636562060,
  "exp": 3845548881,
  "client_id": "fbf739c4-fb86-4f03-a477-91af51cc46f2", 
  "roles": [ "vendor" ]
}

Token Introspection

An endpoint for verifying a token and accessing information about the token.

Can regular vendor/host tokens verify tokens? Or should we have a special client credential with a different role? For example, a role of "application" (Meadowlark itself) is allowed to verify tokens that are received by the application. Must decide on this when implementing.

  1. Example route signature: POST /oauth/verify with the token to verify in a form-urlencoded body, as well as a valid token authorizing the request itself. Example:

    POST /oauth/verify
    Authorization: bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI....
    
    token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI....
    1. (warning) The specification notes an optional token_type  hint. Meadowlark will only support bearer tokens, so this parameter is not necessary. However, the service should not reject a payload that includes this parameter. It can simply ignore the parameter, always validating as a bearer token.
  2. responses:
    1. 200 if the token is still valid with a message body containing the token information in in a JSON payload.

      1. Active token example:

        Active Token
        {
          "active": true,
          "client_id": "fbf739c4-fb86-4f03-a477-91af51cc46f2",
          "sub": "client name",
          "aud": "ed-fi-meadowlark",
          "iss": "ed-fi-meadowlark",
          "exp": 1659374285,
          "iat": 1659374285,
          "roles": [
            "vendor"
          ]
        }
      2. Inactive token example:

        Inactive Token
        {
          "active": false
        }
      3. active  will mean that the token is valid:
        1. issued by this application
        2. not revoked
        3. not expired
    2. 401 if the Authorization header is missing, invalid, or expired.

Client Credential Management

At this time there is no concept of vendors and applications - just keys and secrets. Therefore most of the Admin API Design is not relevant to this project. We simply need to have a route that supports creating keys and secrets.

  • Support standard HTTP verbs and status codes
  • Requires a token with role claim of "admin", any other valid token gets a 403 "forbidden" response, and invalid or no token gets a 401 response.
  • URL endpoint: "oauth/client" to start with, adjust as needed.
  • GET
    • GET by id
    • GET all
  • POST

    • body

      {
        "clientName": "Hometown SIS",
        "roles": [
          "vendor"
        ]
      }

      Three roles are available:

      • vendor
        • READ access on all descriptors
        • Full CRUD access on those resources created by this client credential
        • OAuth Token introspection for own token
      • host
        • Full CRUD access on all Ed-Fi API resources
        • OAuth Token introspection for any token
      • admin
        • Full CRUD on OAuth Client endpoint
        • OAuth Token introspection for any token
    • response

      location: /oauth/client/a-uuid-v4-value
      
      {
        "client_id": "a-uuid-v4-value",
        "client_secret": "a really good random secret",
        "clientName": "Hometown SIS",
        "roles": [
          "vendor"
        ] 
      }
  • PUT
    • body

      {
        "client_id": "a-uuid-v4-value",
        "clientName": "Hometown SIS",
        "roles": [
          "vendor"
        ]
      }
    • does not update client_id or client_secret
  • Generate a new secret for an existing client id:

    • Request: POST /oauth/client/a-uuid-v4-value/reset

    • Response

      {
        "client_id": "a-uuid-v4-value",
        "client_secret": "a new really good random secret"
      }
  • DELETE should be a soft delete / deactivation
    • When deactivated, token introspection endpoint should return "active": false for any token created for that client_id, and that client_id should now get a 401 response when used in a POST to oauth/token

Implementation Notes

  • Treat as a microservice
    • Separate API application, written in either TypeScript or Python
    • As a microservice, it will have its own datastore. 
      • Should support both PostgreSQL and MongoDB
      • By default, should use the same database as the Meadowlark API code, but with complete independence from the tables / collections used by the API code.
  • Can utilize open source* third-party identity provide packages (so as not GPL, LGPL, Affero GPL, or other restrictive / "viral" license)

Table of Contents

  • No labels