6.5 KiB
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 (and Business OTP if applicable)
blaze_identitycookie set in the session- PKCE
code_verifierandcode_challengegenerated at the start of the login session Device-IDgenerated 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 |
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+%28Xiaomi%3B+Android+14%3B+22101320I%29&x-app-version=2.1.44.348' \
--header 'User-Agent: Mozilla/5.0 (Android 14; 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 |
curl --request POST \
--url 'https://www.bankofmaldives.com.mv/internetbanking/oauth/token' \
--header 'User-Agent: Mozilla/5.0 (Android 14; 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+%28Xiaomi%3B+Android+14%3B+22101320I%29' \
--data 'x-app-version=2.1.44.348'
Response
{
"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 |
curl --request POST \
--url 'https://www.bankofmaldives.com.mv/internetbanking/oauth/token' \
--header 'User-Agent: bml-mobile-banking/348 (Xiaomi; Android 14; 22101320I)' \
--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+%28Xiaomi%3B+Android+14%3B+22101320I%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.
{
"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.
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 Next → Dashboard