Files
thijooree/docs/bmlapi/03-oauth-token.md
Shihaam Abdul Rahman 256f216da4
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 4s
update docs
2026-05-23 23:46:00 +05:00

194 lines
6.5 KiB
Markdown

# OAuth Token Exchange and Refresh
After the web login flow has set the `blaze_identity` cookie (profile activated), the client exchanges a PKCE authorization code for an `access_token` and `refresh_token`. These tokens are used for all subsequent REST API calls.
---
## Prerequisites
- Completed [Login](01-login.md) (and [Business OTP](02-business-otp.md) if applicable)
- `blaze_identity` cookie set in the session
- PKCE `code_verifier` and `code_challenge` generated at the start of the login session
- `Device-ID` generated at the start of the login session
---
## PKCE Parameter Generation
Generate these once at the start of each login session:
| Parameter | Generation |
|---|---|
| `code_verifier` | 72 cryptographically random bytes, base64url-encoded (no padding) |
| `code_challenge` | SHA-256 hash of `code_verifier` (as ASCII bytes), base64url-encoded (no padding) |
| `Device-ID` | 8 cryptographically random bytes, hex-encoded (16 hex chars) |
| `state` | 16 random bytes, base64url-encoded |
| `nonce` | 12 random bytes, base64url-encoded |
---
## Step 1 — Authorize (Get Auth Code)
```
GET https://www.bankofmaldives.com.mv/internetbanking/oauth/authorize
```
### Query Parameters
| Parameter | Value |
|---|---|
| `redirect_uri` | `https://app.bankofmaldives.com.mv/oauth/mobile-callback` |
| `client_id` | `98C83590-513F-4716-B02B-EC68B7D9E7E7` |
| `response_type` | `code` |
| `state` | Random base64url string (16 bytes) |
| `nonce` | Random base64url string (12 bytes) |
| `code_challenge` | SHA-256 of `code_verifier`, base64url-encoded |
| `code_challenge_method` | `S256` |
| `Device-ID` | Random 16-char hex string |
| `User-Agent` | App user agent string |
| `x-app-version` | `2.1.44.348` |
```bash
curl --request GET \
--url 'https://www.bankofmaldives.com.mv/internetbanking/oauth/authorize?redirect_uri=https%3A%2F%2Fapp.bankofmaldives.com.mv%2Foauth%2Fmobile-callback&client_id=98C83590-513F-4716-B02B-EC68B7D9E7E7&response_type=code&state=<state>&nonce=<nonce>&code_challenge=<code_challenge>&code_challenge_method=S256&Device-ID=<device_id>&User-Agent=bml-mobile-banking%2F348+%28{manufacturer}%3B+Android+14%3B+{model}%29&x-app-version=2.1.44.348' \
--header 'User-Agent: Mozilla/5.0 (Android {version}; Mobile; rv:150.0) Gecko/150.0 Firefox/150.0' \
--cookie 'blaze_session=<session>; blaze_identity=<identity>'
```
### Response
`302` redirect to:
```
https://app.bankofmaldives.com.mv/oauth/mobile-callback?code=<auth_code>&state=<state>
```
Extract the `code` query parameter from the `Location` header. This is the one-time authorization code.
---
## Step 2 — Token Exchange
```
POST https://www.bankofmaldives.com.mv/internetbanking/oauth/token
```
### Request
**Content-Type:** `application/x-www-form-urlencoded`
| Field | Value |
|---|---|
| `grant_type` | `authorization_code` |
| `code` | Auth code from Step 1 |
| `code_verifier` | PKCE verifier generated at login start |
| `client_id` | `98C83590-513F-4716-B02B-EC68B7D9E7E7` |
| `redirect_uri` | `https://app.bankofmaldives.com.mv/oauth/mobile-callback` |
| `Device-ID` | Same device ID used in Step 1 |
| `User-Agent` | App user agent string |
| `x-app-version` | `2.1.44.348` |
```bash
curl --request POST \
--url 'https://www.bankofmaldives.com.mv/internetbanking/oauth/token' \
--header 'User-Agent: Mozilla/5.0 (Android {version}; Mobile; rv:150.0) Gecko/150.0 Firefox/150.0' \
--data 'grant_type=authorization_code' \
--data 'code=<auth_code>' \
--data 'code_verifier=<code_verifier>' \
--data 'client_id=98C83590-513F-4716-B02B-EC68B7D9E7E7' \
--data 'redirect_uri=https%3A%2F%2Fapp.bankofmaldives.com.mv%2Foauth%2Fmobile-callback' \
--data 'Device-ID=<device_id>' \
--data 'User-Agent=bml-mobile-banking%2F348+%28{manufacturer}%3B+Android+14%3B+{model}%29' \
--data 'x-app-version=2.1.44.348'
```
### Response
```json
{
"access_token": "eyJhbGciOiJSUzI1NiJ9...",
"refresh_token": "def50200aabbcc...",
"token_type": "Bearer",
"expires_in": 3600
}
```
| Field | Type | Description |
|---|---|---|
| `access_token` | `string` | JWT Bearer token — use in `Authorization: Bearer <token>` on all REST API calls |
| `refresh_token` | `string` | Long-lived token — use to refresh without re-login |
| `token_type` | `string` | Always `"Bearer"` |
| `expires_in` | `number` | Token lifetime in seconds (typically `3600`) |
Store `access_token`, `refresh_token`, `expires_in`, and `Device-ID` together as the session. The `access_token` expires at `now + expires_in * 1000` milliseconds.
---
## Token Refresh
When the access token expires (on `401` or `419` from any REST endpoint), obtain a new one using the refresh token. No web session or cookies are needed.
```
POST https://www.bankofmaldives.com.mv/internetbanking/oauth/token
```
### Request
**Content-Type:** `application/x-www-form-urlencoded`
| Field | Value |
|---|---|
| `grant_type` | `refresh_token` |
| `refresh_token` | Stored refresh token |
| `client_id` | `98C83590-513F-4716-B02B-EC68B7D9E7E7` |
| `Device-ID` | Same device ID from the original login |
| `User-Agent` | App user agent string |
| `x-app-version` | `2.1.44.348` |
```bash
curl --request POST \
--url 'https://www.bankofmaldives.com.mv/internetbanking/oauth/token' \
--header 'User-Agent: bml-mobile-banking/348 ({manufacturer}; Android {version}; {model})' \
--data 'grant_type=refresh_token' \
--data 'refresh_token=def50200aabbcc...' \
--data 'client_id=98C83590-513F-4716-B02B-EC68B7D9E7E7' \
--data 'Device-ID=a1b2c3d4e5f60718' \
--data 'User-Agent=bml-mobile-banking%2F348+%28{manufacturer}%3B+Android+14%3B+{model}%29' \
--data 'x-app-version=2.1.44.348'
```
### Response
Same structure as the token exchange response. The server may issue a new `refresh_token` (rotating tokens). If a new one is returned, replace the stored value; otherwise keep the original.
```json
{
"access_token": "eyJhbGciOiJSUzI1NiJ9...<new>",
"refresh_token": "def50200...<new or same>",
"token_type": "Bearer",
"expires_in": 3600
}
```
**Failure:** If `access_token` is absent or blank in the response, the refresh token has expired. Re-run the full [login flow](01-login.md).
---
## Session Storage
Persist the following to represent a saved BML session:
| Field | Description |
|---|---|
| `access_token` | Bearer token for REST API calls |
| `refresh_token` | Used to renew without re-login |
| `expires_at` | Unix timestamp (ms) when `access_token` expires |
| `device_id` | Must be sent with every refresh; ties tokens to the device |
---
&nbsp;
---
[← Business Profile OTP](02-business-otp.md) &nbsp;&nbsp;&nbsp; **Next →** [Dashboard](04-dashboard.md)