Using OAuth 2.0 and OpenID Connect to Secure Your API
OAuth 2.0 and OpenID Connect are both based on a token-based authorisation framework and are defined and implemented using Grant Flow type patterns. These define the different types of interaction a client application can perform to gain an access token and thus access to the protected API.
The grant types define the flow of the different token between the different end points exposed by the API Consumer and API Provider.
Token Typesβ
Both OAuth 2.0 and OpenID Connect utilise tokens however there are a number of mechanisms used to obtain them. These are sometimes referred to as grant flows. Different tokens can be used for different purposes.
| Token Type | Description | Grant Flow Used |
|---|---|---|
| Authorisation Code | Created by the API Provider and sent to the API Consumer after the resource owner has authenticated and provided consent for the required action (e.g. Read) against the data being accessed. Sent to the API Provider to obtain an Access Token. MUST be protected with TLS.π‘ MUST be encrypted when stored.π‘ MUST NOT be stored once it has been used.π‘ | Authorisation Code |
| Access Token | Returned to the API Consumer from the API Provider, and then sent to the API Providers resource server when requesting access to a protected resource. Also called a Bearer token MUST be protected with TLS.π‘ MUST be encrypted when stored.π‘ SHOULD have a lifetime less than 60 mins.π‘ | Client Credentials Implicit Authorisation Code |
| Refresh Token | Used to obtain a new Access token (and possibly a new Refresh Token) from the API Provider when the time limit on an issued Access Token has expired. MUST be protected with TLS.π‘ MUST be encrypted when stored.π‘ If used for Single Page Applications, MUST have a lifetime of 24 hours or less.π‘ | Implicit Authorisation Code |
| ID Token | Used in all OpenID Connect flows. It is a JWT that is signed and contains meta data that can be used to enhance the level of security during the token exchange(s) MUST be used as a detached signature.π‘ MUST be signed with an approved algorithm.π‘ | Implicit Authorisation Code |
| API Key | A string used in some scenarios to authenticate the client application to the API. SHOULD be a 40+ character random string.π‘ SHOULD have an associated rotation policy e.g. 6 - 12 month lifecycle.π‘ | N/A |
Token Formatsβ
There are three token formats that are used in OAuth 2.0 and OpenID Connect they are detailed in the table below.
| Token Format | Where used | Description | Recommendation / Classification |
|---|---|---|---|
| Opaque Tokens | Authorisation Code Access Token Refresh Token | They do not contain any user information, they are a random, unique string of characters that act as a reference for the OAuth 2.0 server to map it to stored information. | Opaque tokens MAY be used with UNCLASSIFIEDπ‘,MAY be used with MEDICAL IN-CONFIDENCE.π‘If the API provider supports opaque access tokens they MUST do this in conjunction with the token issuer's /tokeninfo endpoint.π‘ |
| JWT | Access Token Refresh Token ID Token | JSON Web Tokens are self contained token and store user identity and access information (claims) | MAY use with UNCLASSIFIEDπ‘ MAY be used with MEDICAL IN-CONFIDENCEπ‘ |
| JWE | Access Token Refresh Token ID Token | This is a JWT that has been encrypted using the JWE standard | MAY use with UNCLASSIFIEDπ‘ MAY be used with MEDICAL IN-CONFIDENCEπ‘ MUST be used where the token itself contains sensitive information or PHI/PIIπ‘ |
Opaque Tokenβ
Below is a JSON payload of a response from an OAuth 2.0 Server when they issue an [Opaque] Access Token. As you can see the Access Token itself is a string of characters.
{
"access_token": "sbQZuveFumUDV5R1vVBl6QAGNB8",
"scope": "Resource.r, Resource.w",
"token_type": "Bearer",
"expires_in": 3599
}
Note: The response also contains the lifetime of the access token and the scope(s) approved. Scopes will be covered later but are basically permission that can be performed on the information protected by the API.
JSON Web Token (JWT)β
Below is a JSON payload of a response from an API Provider when they issue a [JWT] Access Token. As you can see the Access Token itself is also a string of characters
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJteUNsaWVudCIsImlzcyI6Imh0β¦β.",
"scope":"Resource.r, Resource.w",
"token_type":"Bearer",
"expires_in":3599
}
The JWT token is made up of three sections (separated by a period (.):
- Header - token type, how it is signed and the key identifier
- Payload - including claims
- Signature - for validation of the JWT
JWT Decodedβ
An example of an encoded JWT is detailed below:
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJteUNsaWVudCIsImlzcyI6Imh0dHB
zOi8vaGVhbHRoLmV4YW1wbGUuY29tOjg0NDMvaGVhbHRoL29hdXRoMiIsInRva2VuTmFtZSI6ImFj
Y2Vzc190b2tlbiIsInRva2VuX3R5cGUiOiJCZWFyZXIiLCJhdWQiOiJteUNsaWVudCIsIm5iZiI6M
TUzOTA3NTk2NywiZ3JhbnRfdHlwZSI6ImNvZGUiLCJzY29wZSI6WyJ3cml0ZSJdLCJleHAiOjE1Mz
kwNzk1NjcsImlhdCI6MTUzOTA3NTk2NywiZXhwaXJlc19pbiI6MzYwMCwianRpIjoiRlRRVDZlWmt
EaG02UEhFYVN0aE9Sb1RMQjgwIn0.b2H_BoT988W28s6K-XAU7gsxXoJlBDNZFpeIxb-7a_yXmKnS
0YNw7nxGqYtBL9GMsh6QTXRkohTS\W9bU2cW0hvU1bJp-1XHywn1kNM5JqLNF2YakV3NX-_4WNdn_
Y1n9aRtbAExLIGea6Wlk23zFGWkJ19WQ7vtHvEqy1ho1gwg9-3STvtCp0YlA6wpA9RUlRHwIx_7_l
Aflrhezjm5cmR0BzuxeEoF5BVkxseFXs1l7nRWuXGetwPOYWP1OKm-gZLDhwpXFUhAsa61XAvfc9q
-xaf2deTXlTjUpJso7DiRBdpsdi6KEqvCILp2PBCMkAcOPQUCUOb6jSA_XMSOgzQ"
The JWT is decoded below
{
"typ": "JWT",
"alg": "RS256"
}
{
"sub": "myHealth",
"iss": "[https://health.example.com:8443/health/oauth]",
"tokenName": "access_token",
"token_type": "Bearer",
"aud": "myClient",
"nbf": 1539075967,
"grant_type": "code",
"scope": [
"write"
],
"exp": 1539079567,
"iat": 1539075967,
"expires_in": 3600,
"jti": "{Unique Identifier}"
}
[signature]
JWT Benefit Characteristicsβ
The following is a list of why a JWT SHOULD be used:
| Characteristic | Description |
|---|---|
| Client Introspection | The token contain self-contained information that can be introspected by the API Consumer and the API Providers Resource Server without having to call the API Provider's Authorisation server therefore improving client performance |
| Identity Claims | Identity claims, expiration time and issuer details are embedded in the JWT, which are used to provide Identity information to the API Consumer. Claims which can be used to provide granular access controls, enforce the lifetime of the token and validate the issuer and audience of the JWT. |
| Digital Signature | Ensures the integrity and authenticity of the token, preventing tampering of token content and providing a trust framework |
| Standard JWT format | It follows a format defined by an RFC so is compatible across many Vendor solutions |
| Claims Enhancement | A JWT can be enhanced with other claims or metadata and can include claims related to security tags |
| Encryption | The JWT can be encrypted if PII information is included |
OAuth 2.0 and OpenID Connect Endpoints (API Provider)β
There are multiple endpoints that are exposed and secured in the OAuth 2.0 / OpenID Connect architecture. Depending on the grant flow types (covered next) some or all of these end points will be required.
| Endpoint | Location | Tokens | Description |
|---|---|---|---|
Authorisation End Point/authorize | API Provider | Code_Token Access_Token ID_Token | Responsible for redirecting the Resource Owner/Participant to the API Provider Authentication Server so they can login to provide their consent for the client to access a protected resource. |
Token/token | API Provider | Access_Token Refresh_Token ID_Token | This authenticates the API Consumer and based on validation rules and the configuration of the client |
| Redirect endpoint | API Consumer | Code_Token Access_Token ID_Token | The response from the authorisation endpoint is sent here. This is via HTTP-redirect (302). The API Consumer is responsible for validating tokens from this endpoint. |
Revoke/revoke | API Provider | Access_Token Refresh_Token | This allows the API Consumer to revoke tokens if required |
Introspect/introspect | API Provider | Access_Token Refresh_Token | This allows the Resource Server or Client to find out if the token has expired and other details about the token. |
User Info/userinfo | API Provider | Access Token | Use an access token to get information about the authenticated health participant |
JSON Web Key URI/jwks | API Provider / API Consumer | API Consumers public key(s)π‘ | |
Pushed Authorisation Request (PAR)/par | API Provider | Push Authorisation Request (PAR) endpoint used when the authorisation request object is large and adds a a level of security as the request is signed | |
Backchannel Authorise/bc-authorize | Client-Initiated Backchannel Authentication (CIBA). This is a decoupled authentication process and uses a authentication device |
Discovery and Client Endpoints (API Provider)β
When exposing protected APIs to API consumers consideration as to how an API consumer application developer would create and manage the Application Client integration to the API Provider is required.
This is carried out by creating a Client on the OpenID Connect Server (API Provider). This can be achieved in a number of ways and the following tables detail Discovery and Registration endpoints.
This document does not go into detail on how these are achieved as the key focus is on how APIs are secured. It is important to note that API providers MUST clearly define and document API consumer onboarding processes and requirements.π‘
For completeness the following endpoints are used by the Discovery and registration process.
| Endpoint | Location | Description |
|---|---|---|
/register | API Provider | Relying parties can create (register) a client on the Authorisation Server using different security methods |
/.well-known/openid-configuration | API Provider | This is called by the API Consumer and returns the API providers OAuth 2.0/OIDC configuration and capabilities including endpoints, algorithms and grant types |
OpenID Connectβ
OpenIDConnect adds the following additional capabilities to provide access to a health participants claims / attributes:
-
An ID Token
-
A Userinfo endpoint
API Providers MUST use OpenID Connect architecture models with all MEDICAL IN-CONFIDENCE APIs
π‘OpenID Connect uses all the flows, grant types and endpoint exposed by OAuth 2.0. Once implemented it is enacted using a specific request scope openid in the initial authorisation call that the client makes to the OpenID Connect service.
ID Tokenβ
MUST be used with all MEDICAL IN-CONFIDENCE APIs
π‘The ID Token is a JSON Web Token (JWT) that contains authenticated user information (and attributes) that the authorisation API Provider (OpenID Connect Server) provides to the API Consumer.
| ID Token Capability | Recommendation |
|---|---|
| Provides authorisation credentials as claims (attributes) | MAY be used to enforce finer grained access controls by providing additional attributesπ‘ |
| The Token can be Signed | MUST be signed by an approved algorithmπ‘ |
| Claims that hash the code, state and access token can be added | SHOULD be applied to address Integrity of usersπ‘ |
| Non-Identity related information can also be added to the token | Additional information (e.g. Session details) MAY be appliedπ‘ |
| Integrity Validation | Validation of issuer, audience, nonce and expiry time MUST be applied by the API Consumerπ‘ |
| Encryption | The ID Token MAY be encryptedπ‘ |
| Customise API Consumer application | Depending on the flow selected the API Consumer MAY use the content of the ID Token to customise the API Consumer clientπ‘ |
Userinfo Endpointβ
The userinfo endpoint MAY be exposed by the API Provider.
π‘The Userinfo endpoint can be called with an access token to obtain the same claims (e.g. first name) provided in the ID Token or MAY be configured to provide additional claims such as the health participants National Health Index identifier.
Scopesβ
There are a number of additional scopes that OpenID Connect introduces (e.g. profile, name, email etc) that detail specific attributes that can be presented in an ID token.
See FHIR security for details on SMART on FHIR scopes.
OpenID Connect Implementation Patternsβ
Implementation patterns MAY contain one or more of the options below:
-
The Resource Server and Authorisation Server are owned by the API Providers
-
The Resource Server and Authorisation Server are owned by different API Providers
-
The Authentication Server is owned by an Identity Service Provider
-
All three components are managed by separate API Providers or Identity Service Providers
OAuth 2.0 and OpenID Connect Grant Typesβ
OAuth 2.0 and OpenID Connect supports two types of API Consumer, confidential and public, and eleven grant flows (how Client Applications can gain Access Tokens). Each is appropriate to different situations and solution requirements.
The initial authorisation call to the API Provider from the API Consumer as a parameter call resource_type this defines what Grant Type the API Consumer application would like to use.
OpenID Connect builds on the existing OAuth 2.0 grant flows.
Finally there is the concept of a Hybrid flow that uses the Authentication code flow as a base and (depending on what is required by the client and what is enabled by the Authorisation Server) it allows additional tokens (ID Tokens) to be issued during the flow.
A good example of where the Hybrid flow is being mandated is in the management of consent in the Open Banking Consumer Data Rights specifications.
Please see this excellent summary of the response type possible with OpenID Connect.
API provider and consumer developers MUST read the RFC OAuth 2.0 for Browser-Based Apps to gain an understanding of the differing architectures available.
Confidential clientsβ
These are:
- Websites and services that make secure connections to OAuth 2.0 server.
- Client secret or JSON Web Token (JWT) can be stored and protected
MUST be used to secure MEDICAL IN-CONFIDENCE APIs
Public clientsβ
These are:
- Single-page applications
- Applications running on devices
- Applications that cannot protect secrets.
MAY be used for UNCLASSIFIED APIs
Grant Typesβ
The table below details the eleven grant/response types.
| Grant Type Response type | Recommendations | Client Type |
|---|---|---|
| Authorisation Code (OAuth 2.0) | MAY be used for Confidential Clientsπ‘ MUST NOT be used for Public Clientsπ‘ | N/A |
| Authorisation Code (OpenID Connect) with PKCE | MAY be used for UNCLASSIFIED APIsπ‘ | MAY be used with Native or Single Page Applications (SPA)π‘ Where a SPA or mobile application does not have a secure backend for frontend (BFF) the use of PKCE prevents malicious interception of the authorisation code |
| Hybrid (OpenID Connect) code id_token token | SHOULD NOT useπ‘ | N/A |
| Hybrid (OpenID Connect) code id_token | MUST be used with MEDICAL IN-CONFIDENCE APIsπ‘ | MUST be used with a web application (confidential client)π‘ |
| Hybrid (OpenID Connect) code token | SHOULD NOT useπ‘ | N/A |
| Implicit (OAuth 2.0) | SHOULD NOT be usedπ‘ | N/A |
| Implicit (OpenID Connect) id_token token and PKCE | SHOULD NOT be usedπ‘ | N/A |
| Implicit (OpenID Connect) id_token | SHOULD NOT be usedπ‘ | N/A |
| Resource owner Password Credential | MUST NOT be usedπ‘ | N/A |
| Client Credentials | SHOULD only be used for system to system integrationπ‘ | N/A |
OIDC Authorisation Code Flow with PKCEβ
MUST be used when securing MEDICAL IN-CONFIDENCE APIsπ‘The Authorisation Code flow is the most frequently used model and as it is regarded as the most secure model for securing public facing APIs for consumer applications. It can also be used for internal APIs. The following security enhancements MUST be applied to the base code flow:
- OpenID Connect
code id_tokenflow - JWT Access and Refresh Tokens
- PKCE to secure the code returned from the API Provider
This MUST be used when supporting confidential clients
Flow Detailsβ
- Used by OAuth 2.0 and OpenID Connect
- Provides support for a confidential client (Where the client id and client secret can be securely stored)
- The resource Owner provides authorisation for the API Consumer to access the protected resource.
- Exchanges an authorisation code token for a access (and refresh) token over a secure back channel
- Two step process, the initial request is over TLS; and may utilise a mTLS backchannel for the access token exchange
- The resource owner authenticates to the API Provider and authorises the API Consumer to access the protected resource.
- The API Consumer receives a temporary authorisation code from the server as confirmation.
- The API Provider validates the authorisation code and exchanges it for an access token.
- The API Provider delivers the access token directly to the API Consumer
A detailed example of the Authorisation Code flow is covered here
PKCEβ
MUST be used when securing MEDICAL IN-CONFIDENCE APIsπ‘The PKCE-enhanced Authorisation Code Flow was introduced to help mitigate "man-in-the-middle" attacks.
The API consumer creates a secret that the authorisation server can verify before returning the access token to the client, i.e. the Authorisation Server can confirm that the code came from the from the Client Application and not a Malicious "in-the-middle" Application.
Implementationβ
Both the API Consumer and API Provider have to configured to support and enforce PKCE
PKCE Process Flowβ
-
The API Consumer:
- Creates code verifier (a Random Key)
- Applies a hash method to the code verifier to create code challenge (a Random Key)
-
The API Consumer sends the hash method and the code challenge to the API Provider (Authorisation server)
-
The API Provider (Authorisation Server) stores the code challenge
-
The API Consumer sends the "code token" to request the Access Token and includes the code verifier
-
The API Provider (Authorisation Server) validates this against the stores code challenge
-
The Access Token is returned to the API Consumer Application (it does not respond if it fails)
OAuth 2.0 and OpenID Connect flowβ
PKCE is applied in the same way for both OAuth 2.0 and OpenID Connect