---
title: 2. OAuth 2.0 Implementation
tags: [Part 2]

---

# 2. OAuth 2.0 Implementation

> Connected Services Framework (CSF) — Part 2: Implementation — Version 2.0

## 2.1 Overview

The CSF uses **OAuth 2.0 Client Credentials flow** for machine-to-machine authentication between MAPs. This is the same approach used by TOTSCo for OTS, ensuring maximum compatibility across the industry.

Each MAP operates as both:
- An **OAuth 2.0 Authorisation Server** — issuing tokens to other MAPs that want to access its endpoints
- An **OAuth 2.0 Client** — requesting tokens from other MAPs to access their endpoints

During onboarding, MAPs exchange `client_id` and `client_secret` credentials with each other.

## 2.2 Access Token Request

To obtain an access token, the requesting MAP sends an HTTP POST to the target MAP's token endpoint.

**Request URL**: `https://{fqdn}/oauth2/token`

The FQDN is obtained from the `oauthTokenURL` field in the target MAP's registry (see [Directory API](05-directory-api.md)).

### Request Headers

| Header | Value | Notes |
|---|---|---|
| `Authorization` | `Basic <Base64(client_id:client_secret)>` | Base64-encoded credentials provided during onboarding |
| `Content-Type` | `application/x-www-form-urlencoded` | Standard OAuth 2.0 form encoding |

### Request Body Parameters

| Field | Type | Required | Description |
|---|---|---|---|
| `grant_type` | String| yes| always `client_credentials` |
| `client_id` | String | Yes | Client identifier provided by the target MAP during onboarding |
| `client_secret` | String | Yes | Client secret provided by the target MAP during onboarding |

Note : A MAP should support `client_id` + `client_secret` in both headers and body as per OAuth 2.0 Standard

## 2.3 Access Token Response

A successful request returns an HTTP 200 with the following JSON body:

```json
{
  "access_token": "{OAuth2 Access Token}",
  "token_type": "bearer",
  "scope": "default",
  "expires_in": 3600
}
```

| Field | Type | Mandatory | Default | Description |
|---|---|---|---|---|
| `access_token` | String | Y | — | The bearer token to include in subsequent API requests |
| `token_type` | String | Y | `bearer` | The type of token issued |
| `scope` | String | N | `default` | The scope of access granted |
| `expires_in` | Integer | Y | `3600` | Token validity in seconds (typically 1 hour) |

## 2.4 Using the Access Token

Include the access token in the `Authorization` header of all API requests:

```
Authorization: Bearer {access_token}
```

The token MUST be validated by the receiving MAP on every request, checking:
- Token signature validity
- Token has not expired
- Issuer matches the expected authorisation server
- Audience/scope is appropriate for the requested resource

## 2.5 Token Lifecycle

- Tokens are **short-lived** (typically 1 hour)
- MAPs SHOULD cache tokens and reuse them until close to expiry
- Client secrets MUST be stored securely (e.g., in environment variables or secret vaults, never in source code)
- Client secrets SHOULD be rotated periodically

## 2.6 Error Responses

The following errors may be returned during token generation:

| HTTP Code | Error | Root Cause |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or missing required fields |
| 401 | Unauthorised | Incorrect client credentials (`client_id` or `client_secret`) |
| 404 | Not Found | Incorrect token generation URL |
| 405 | Method Not Allowed | Incorrect HTTP method (must be POST) |
| 415 | Unsupported Media Type | Missing or incorrect `Content-Type` header |

## 2.7 Security Requirements

| Requirement | Mandatory |
|---|---|
| All token exchanges over TLS 1.3 | Yes |
| Validate token signature, issuer, and audience on every request | Yes |
| Limit token scope and lifetime | Yes |
| Never expose client secrets in frontend code or logs | Yes |
| Store client secrets in secure vaults or environment variables | Yes |
| Rotate client secrets periodically | Recommended |
| Use Proof Key for Code Exchange (PKCE) for public clients | If applicable |
