Files
thijooree/docs/mibapi/02-login.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

347 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Login Flow
MIB uses a two-phase authentication model:
| Phase | Trigger | Key used |
|---|---|---|
| **Device Registration** | First time this device+account pair is seen | `DEFAULT_KEY` → DH session key |
| **Regular Login** | Every subsequent login (stored `key1`/`key2`) | `key1` → DH session key |
---
## Password Hashing (`pgf03`)
The password is never sent in plaintext. Required by both `C41` (registration) and `A41` (login).
```
pgf03 = SHA256( clientSalt + SHA256( userSalt + SHA256( password ) ) )
```
All SHA-256 values are uppercase hex strings. `clientSalt` is a fresh random 32-character alphanumeric string each time.
```python
import hashlib
def compute_pgf03(password: str, user_salt: str, client_salt: str) -> str:
h1 = hashlib.sha256(password.encode()).hexdigest().upper()
h2 = hashlib.sha256((user_salt + h1).encode()).hexdigest().upper()
return hashlib.sha256((client_salt + h2).encode()).hexdigest().upper()
```
---
## Device Registration Flow (first time only)
```
[0] sfunc=r DEFAULT_KEY → DH exchange → derive session_key_1, get xxid + nonceGenerator
[1] sfunc=n A44 → get userSalt
[2] sfunc=n C41 → submit credentials → returns key1, key2 (persist!)
[3] sfunc=n C42 → verify OTP
[48] regular login (below) using the key1/key2 just received
```
---
## Regular Login Flow
```
[0] sfunc=i key1 → DH exchange → derive session_key_2, get xxid + nonceGenerator
[1] sfunc=n A44 → get userSalt
[2] sfunc=n A41 → submit credentials → returns otpTypes, email, uuid
[3] sfunc=n P41 → fetch profile image (optional)
[4] sfunc=n A42 → verify OTP → session established
[5] sfunc=n P47 → select profile → returns accountBalance array
```
---
## Step-by-Step Reference
### [0] Initial Key Exchange — `sfunc=r`
**Key**: `DEFAULT_KEY = 8M3L9SBF1AC4FRE56788M3L9SBF1AC4FRE5678`
**Request** (outer + inner are encrypted together):
```json
{
"sfunc": "r",
"data": {
"cmod": "<G^A mod P as decimal string>",
"appId": "IOS17.2-<15 random alphanumeric chars>",
"routePath": "S40",
"sodium": "<random 20-bit int as string>",
"xxid": "<random 40-bit int as string>"
}
}
```
**Response** (decrypted with `DEFAULT_KEY`):
```json
{
"success": true,
"reasonCode": "201",
"reasonText": "Key generated successfully.",
"smod": "<server DH public key as decimal string>",
"nonceGenerator": "<instruction string>",
"xxid": "<session token — use for all subsequent calls>",
"sodium": "<server random>",
"encMethod": 2
}
```
After: `session_key = derive_session_key(int(smod))`. Save `xxid` and `nonceGenerator`.
---
### [1] Get Auth Type — `sfunc=n`, `routePath: A44`
**Key**: session key
**Request**:
```json
{
"sfunc": "n",
"xxid": "<session xxid>",
"data": {
"uname": "<username>",
"nonce": "<computed nonce>",
"appId": "<appId>",
"sodium": "<random>",
"routePath": "A44",
"xxid": "<session xxid>"
}
}
```
**Response**:
```json
{
"success": true,
"data": [{ "loginType": "1", "userSalt": "<server salt>" }]
}
```
Use `userSalt` in `pgf03` computation.
---
### [2a] Device Registration Init — `sfunc=n`, `routePath: C41`
_First-time only._
**Request**:
```json
{
"sfunc": "n",
"xxid": "<session xxid>",
"data": {
"uname": "<username>",
"pgf03": "<computed>",
"clientSalt": "<random 32-char string>",
"nonce": "<nonce>",
"appId": "<appId>",
"sodium": "<random>",
"routePath": "C41",
"xxid": "<session xxid>"
}
}
```
**Response**:
```json
{
"success": true,
"reasonCode": "201",
"primaryOTPType": "3",
"otpTypes": [2, 3],
"fullName": "<user full name>",
"customerImgHash": "<hash>"
}
```
---
### [3a] OTP Verification (Registration) — `sfunc=n`, `routePath: C42`
_First-time only. Receive and persist `key1`/`key2`._
**Request**:
```json
{
"sfunc": "n",
"xxid": "<session xxid>",
"data": {
"otp": "<6-digit OTP>",
"uname": "<username>",
"otpType": "3",
"nonce": "<nonce>",
"appId": "<appId>",
"sodium": "<random>",
"routePath": "C42",
"xxid": "<session xxid>"
}
}
```
**Response**:
```json
{
"success": true,
"reasonCode": "101",
"data": [{
"key1": "<store securely — Blowfish key for next sfunc=i>",
"key2": "<store securely — sent plaintext in sfunc=i wrapper>",
"appId": "<appId>",
"encryptionMethod": "2"
}]
}
```
`key1` and `key2` are long-lived device credentials. Store them securely on the device.
---
### [0b] Authenticated Key Exchange — `sfunc=i`
_Regular login. `key2` is a separate unencrypted outer field._
**Key**: `key1`
Form body: `key2=<key2>&sfunc=i&data=<encrypted payload>`
**Encrypted payload**:
```json
{
"sfunc": "i",
"key2": "<key2>",
"data": {
"cmod": "<G^A mod P>",
"appId": "<appId>",
"routePath": "S40",
"sodium": "<random 20-bit int>",
"xxid": "<random 40-bit int>"
}
}
```
**Response** (decrypted with `key1`):
```json
{
"success": true,
"smod": "<new server DH public key>",
"nonceGenerator": "<new instruction string>",
"xxid": "<new session token>",
"encMethod": 2
}
```
After: derive new session key, replace `xxid` and `nonceGenerator`.
---
### [2b] Regular Login Init — `sfunc=n`, `routePath: A41`
**Key**: session key
**Request**:
```json
{
"sfunc": "n",
"xxid": "<session xxid>",
"data": {
"uname": "<username>",
"pgf03": "<computed>",
"clientSalt": "<random 32-char>",
"pmodTime": 0,
"requireBankData": 1,
"nonce": "<nonce>",
"appId": "<appId>",
"sodium": "<random>",
"routePath": "A41",
"xxid": "<session xxid>"
}
}
```
**Response**:
```json
{
"success": true,
"reasonCode": "104",
"primaryOTPType": "3",
"otpTypes": [2, 3],
"email": "<masked email>",
"uuid": "<uuid1>",
"uuid2": "<uuid2>"
}
```
---
### [3b] Get Profile Image — `sfunc=n`, `routePath: P41`
Optional. Fetch the user's avatar to display on the OTP screen.
**Request**:
```json
{
"sfunc": "n",
"xxid": "<session xxid>",
"data": {
"imageHash": "<customerImgHash from C41/A41>",
"nonce": "<nonce>",
"appId": "<appId>",
"sodium": "<random>",
"routePath": "P41",
"xxid": "<session xxid>"
}
}
```
**Response**:
```json
{
"success": true,
"reasonCode": "201",
"profileImage": "<base64-encoded JPEG>"
}
```
---
### [4b] OTP Verification (Login) — `sfunc=n`, `routePath: A42`
**Request**:
```json
{
"sfunc": "n",
"xxid": "<session xxid>",
"data": {
"otp": "<6-digit OTP>",
"uname": "<username>",
"otpType": "3",
"nonce": "<nonce>",
"appId": "<appId>",
"sodium": "<random>",
"routePath": "A42",
"xxid": "<session xxid>"
}
}
```
After successful `A42`, the `xxid` and `nonceGenerator` from the `sfunc=i` response become the WebView session cookies. See [README](README.md) for the cookie format.
---
### [5] Select Profile — `sfunc=n`, `routePath: P47`
See [03-accounts.md](03-accounts.md) for the full P47 reference.
---
&nbsp;
---
[← Encryption](01-encryption.md) &nbsp;&nbsp;&nbsp; **Next →** [Accounts](03-accounts.md)