All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 4s
194 lines
6.5 KiB
Markdown
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 |
|
|
|
|
---
|
|
|
|
|
|
|
|
---
|
|
|
|
[← Business Profile OTP](02-business-otp.md) **Next →** [Dashboard](04-dashboard.md)
|