---
# System prepended metadata

title: Forver Login

---

# Forver Login

1. Remove all sessionToken (CIAM) dependency and replace with ML sessionToken from current API (/user/otp/login/submit,logout,/request/jwtToken,/otp/validate/resetPassword)

2. SessionToken will be binded with deviceKey and deviceId, in order to keep the user sesson always active.

3. For change password, backend will login user to CIAM (same as biometric) to get sessionToken and then call change password API from CIAM to change password. For the logout backend will only use ML sessionToken to logout user.



## `/user/biometric/authenticate` && `/user/otp/login/submit`

Note: *only backend update*

Response:
```json=
{
    ... user details
  "token": "<token>",
  "sessionToken": "<session Token will be generated by ML[currently it is from CIAM]>",
  "refreshToken": "60844.0a7dac3b00513c37b46062caba2c7518bdf9d0c8fa46012975f3450693da3eaefa317a2b032bbcda",
  "sessionExpiresTime": 1652950840,
  "otpSessionExpiresTime": 1652950840
}
```

## `sessionToken` Valid and `token` expired `http.statusCode = 401`
### /user/request/jwtToken

Note: *Required both backend and mobile update*

Request:
```json=
{
  "email": "c25b7fd2c8f2c704:zgPO1T7cGAUTLu4nhIElnmDCOiqli1P/zq3QCGUSlAY=",
  "refreshToken": "9031.afbf3078dd8cff47f1dc8b38eb87a91ec5af433e3dae4e0b5949bc58178f4c0b3e66a1400edd2943",
  "sessionToken": "10001FFA9T-rCxnSDPWiWS8JPaZY1l8dMRhzMXX0_6TnjKWeJzMQkR8S_fX0WbJ5UO9pXC_bky3kxAb5hpVDH2MFddG3h4k7hYJOHHWABoGoUhCnANFkjMgjrEh1jLZqbhV8FamHPbTkQwZ2RbhuVF-d9jo6Dm4fU68fWJPQiwmGwWT9paL3SOwLcZMYSkecbm7OFRoH5g6mmIvFlcn9FnR68l3M3Z8EwJBgzbLNFtK2XPjzR3zSaD36Vyr6OwsSqAX9Z4pSGkTn6uveUw9AgEFLBky3tFi6GCqY8qRHqe3l4QHx_JraeuGa8LFIlg8bz0mRD9-MRcV411VJpaKw968CQWRYt2hZO9kaXqmSPWQJ18MDL7dyS",
  "version": 1
}
```
Response:
```json=
{
  "token": "<token>",
  "sessionToken": "<session Token will be generated by ML[currently it is from CIAM]>",
  "refreshToken": "60844.0a7dac3b00513c37b46062caba2c7518bdf9d0c8fa46012975f3450693da3eaefa317a2b032bbcda",
  "sessionExpiresTime": 1652950840
}
```

## `sessionToken` expired and `token` expired `http.statusCode = 440`

### POST /user/refreshSession

Note: *NEW API both backend and mobile*

Request:
 ```json
{
  "token": "a5199ed519e61f50:EsWn1EbnfAnFKV5j2uDpVVsla3Igb3OmOsZBpAIgGP4=",
  "signature": "EKf9rmiWRNKIsoVUMIQIY1BE8Z+cvyRGXftZ37DOZbqIPqRJSSJCtiNDEzrq+lFRdRlzPBIGYjtH1f4aXVtHvzjq0JW/2b+76SVkcus44tIU4K54aBN67fvQySOEqzXaTtIzyVPoSTxlUig7j2R3X7kqxkBR7xgJHwJqzM+aSQFdbfC/1yTVIvPazxwm74XyYE9MqLqAyYPOYbohzS1lnS5PPxnRd/smBEDwgBY8q0n9S49HB1EjtTskigQ6vl6oLj6sPcuw5oTlTXXQ9bFbUr8KvXsmAxvQVqbb41vSPkV8aOjlvmxg1blamqyK3mMzAx9O0sd8lBef9TcQifieuA==",
  "refreshToken": "9031.afbf3078dd8cff47f1dc8b38eb87a91ec5af433e3dae4e0b5949bc58178f4c0b3e66a1400edd2943",
  "sessionToken": "10001FFA9T-rCxnSDPWiWS8JPaZY1l8dMRhzMXX0_6TnjKWeJzMQkR8S_fX0WbJ5UO9pXC_bky3kxAb5hpVDH2MFddG3h4k7hYJOHHWABoGoUhCnANFkjMgjrEh1jLZqbhV8FamHPbTkQwZ2RbhuVF-d9jo6Dm4fU68fWJPQiwmGwWT9paL3SOwLcZMYSkecbm7OFRoH5g6mmIvFlcn9FnR68l3M3Z8EwJBgzbLNFtK2XPjzR3zSaD36Vyr6OwsSqAX9Z4pSGkTn6uveUw9AgEFLBky3tFi6GCqY8qRHqe3l4QHx_JraeuGa8LFIlg8bz0mRD9-MRcV411VJpaKw968CQWRYt2hZO9kaXqmSPWQJ18MDL7dyS"
}
 ```
 
Response:
```json=
{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaGFubmVsVXNlcklkIjoiODdlMDVmZmQ1NWQxMGMzNjpHSWRpNUhIMmJwb0xBTURPVkFFYytBPT0iLCJlbWFpbCI6ImI2NjUzMmQ3YzI1NGVlYTI6cnQ0SitGbkM1Z3NTbnFtb3RqUWZDQT09IiwiaXNCaW9tZXRyaWMiOnRydWUsImlhdCI6MTY1MzgzOTE2NSwiZXhwIjoxNjUzODQyNzY1fQ.LFlx9STzk8QWwhTQSLN4AQuTrnPICDh8oUfhL64c9nI",
    "sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NTM4MzkxNjUsImV4cCI6MTY1Mzg0MDk2NX0.PmHjlwqBbDNd0bpQ5HFeWFA9-T0KIGY4HjKj5xACbjE",
     "refreshToken": "9031.afbf3078dd8cff47f1dc8b38eb87a91ec5af433e3dae4e0b5949bc58178f4c0b3e66a1400edd2943",
    "sessionExpiresTime": 1653840965
}
```
 
## Session RSA key pair generation and signature 

Key Tag: `sg.com.snack.session.key`

BOTH RSA Key pair and signature generation same as existing implementation biometric approach.
 
### POST /user/register/device

Note: *Changes required in both backend and mobile `version & deviceKey` added for user session*


Request ANDROID:
```json=
{
  "deviceToken": "diX5TJ2cRrq7SMT7nJT1YI:APA91bFEuDr_AJWlWj1D5hS6uHdSqTp4zo6oUBtx9RQwP9BLthMFL2hITWWa2mPrzqE05hdCijKisWu2NvDVS0Ikku3cuTZ-7_bIXFHty8TZP4t_MWAh8BFaZMLTTxhbWdjRDb411CcP",
  "deviceType": 2,
  "primary": true,
  "version": 1,
  "deviceKey": "-----BEGIN PUBLIC KEY-----\r\nMIIBCgKCAQEAv7Z48S1GQh70Y+4xVn5R5+a0QSnM0nCIT4OIlhgo5NufduynQu+6\r\nIs9D2WGYzhyTIWPAQw6bLtc5WMP1WFSzZUKEZOKcr9864SCs9qVUmjrQXKkXWQnQ\r\nnKt6koBW1cbt86xHtTjcVO4I1R1TBhdcVq/83A86194MXXu9OlwmS9V637WT2jsd\r\ndL8DWmlce3OJhEZrvna1a+yujDNViTwW3njMGDMfwgonTREnXVFigaXdZZwTA4In\r\nQvD2wUY/n2kqhaLJQQglIXZzMSvIUCA8fyN0rfX86FvxS3CFxA30MKzg/YjzKMX4\r\ntwYgzIsKqmANKiUnFC1sKDZdEn8Fyu6l6wIDAQAB\r\n-----END PUBLIC KEY-----"
}
 ```
 
Request IOS:
 ```json=
 {
  "deviceToken": "42ae20e4708f887b621b0b9b522c7492b55aeedd9259173ce5182bdda83b1027",
  "deviceType": 1,
  "primary": true,
  "version": 1,
  "deviceKey": "-----BEGIN RSA PUBLIC KEY-----\r\nMIIBCgKCAQEAv7Z48S1GQh70Y+4xVn5R5+a0QSnM0nCIT4OIlhgo5NufduynQu+6\r\nIs9D2WGYzhyTIWPAQw6bLtc5WMP1WFSzZUKEZOKcr9864SCs9qVUmjrQXKkXWQnQ\r\nnKt6koBW1cbt86xHtTjcVO4I1R1TBhdcVq/83A86194MXXu9OlwmS9V637WT2jsd\r\ndL8DWmlce3OJhEZrvna1a+yujDNViTwW3njMGDMfwgonTREnXVFigaXdZZwTA4In\r\nQvD2wUY/n2kqhaLJQQglIXZzMSvIUCA8fyN0rfX86FvxS3CFxA30MKzg/YjzKMX4\r\ntwYgzIsKqmANKiUnFC1sKDZdEn8Fyu6l6wIDAQAB\r\n-----END RSA PUBLIC KEY-----"
}
 ```
 
Response:
```json=
{
    "deviceId": "228fd425-035d-4bfc-aec6-598a213cfc16"
}
```

Create new schema in user database and add record for the user session device.

```json=
user_session_devices
{
    id,
    deviceId, // varchar(255)
    deviceType, // tinyint(4) 
    deviceKey, // text
    status, // 0 -> inactive, 1 -> active
    createdAt,
    updatedAt
}
```

### POST /user/session/requestOTP [New]

Request:
 ```json
{}
 ```
 
Response:
```json=
{
    "data": {
        "tokenUUID": "MemoryTokenStore:LllzK2K1Gv",
        "challengeToken": "0001Fc6ba1ustLEkQZ_h2cPftVzult8u-go_I-wcE4ugll02zdNOfzDc3cF-PT2Ah18dT0FMN-CJUg",
        "mobileNo": "+XX XXXX2355(577405)",
        "otp": "577405"
    },
    "success": true
}
```

### POST /user/session/validateOTP [New]

Request:
 ```json
{
    "tokenUUID": "MemoryTokenStore:LllzK2K1Gv",
    "otp": "577405",
}
 ```
 
Response:
```json=
{
    "success": true,
    "data": {
        "otpSessionExpiresInSeconds": 1800 // in seconds, get it from OCP config
    }
}
```

## biometric/authenticate && /user/otp/login/submit
```json=
{
    "data": {
         ...
         "otpSessionExpiresInSeconds": 1800 // in seconds, get it from OCP config
    }
}
```

@Yahui - Rename `otpSessionExpiresTime` to `otpSessionExpiresInSeconds`, return the value in seconds

### Primary Device Cases
| Status      | Description |
| ----------- | ----------- |
| 1      | User doesn't have any primary device       |
| 2   | User have primary device, request device is not belongs to this user.        |
| 3 | User have primary device, request device is belongs to this user. |


For the user session RSA key pair, when the primiary device status is 1 or 2
- Check any keys available for key tag: `sg.com.snack.session.key`
- if it exists delete and generate new.
- Get the public key.

for `ueser/preauthenticate` and `/user/biometric/authenticate` 
- check in local if any keys generated for key tag: `sg.com.snack.session.key`
- if it exists get the public key
- else generate it and get the public key


### Use case to migrate primary device user for the current device:

### /user/preauthenticate [UPDATE]

current one:
```json=
{
  "email": "snackdevtest1@gmail.com",
  "password": "eyJwYXNzd29yZCI6InpXZm9PSXFacGlGQmdzMzM4N2YrOFE9PSIsImtleXMiOiJEUzVDVWhVODdkUmJkVFNnYUlKUmFrdWtXN3FpUFM4eHlXOUYxY2h5MmRhcXJVbEZEV2ZlUWhaYWNNQ3lYVlRhS0lCT2xVcVkzSVdiRGNMNHlIQUVuN3ZWYVl4cHhsZFdUVVJpSHYvdVhISHBITnNiSGRaZmJvVmUwOUUrakpBTkY5QXBkbFQwdW1SNTRaY21nbW1pNEo5NFdWMWZGMnliL2FueHM4dzh4NTBQbXJxc2txWWNaY1IrM3BJazByalFWaDJJVzBkUzlYdDhpbEFBWTJ2aFRiREZwT3MyL01wa2hhYitHYXdVNnpIakJrMEdSa3krWHFhODgwQ3JHY0lXSU53ZVdZTnp2NkY0Y3h6RXdNeXlXUzJnV2c0L1ZmQVhEZk0yNGErNGd3YUlteVVrcml3QzIxSXc5T0xqZnFPcGMrSWJzdFdXUXpnRFZtSzlDWktEVHc9PSJ9"
    "version":"2",
    "deviceId": "228fd425-035d-4bfc-aec6-598a213cfc16", //"<optional if the device already has deviceId it should be shared>"
}
```

Updated for v3:
```json=
{
  "email": "snackdevtest1@gmail.com",
  "password": "eyJwYXNzd29yZCI6InpXZm9PSXFacGlGQmdzMzM4N2YrOFE9PSIsImtleXMiOiJEUzVDVWhVODdkUmJkVFNnYUlKUmFrdWtXN3FpUFM4eHlXOUYxY2h5MmRhcXJVbEZEV2ZlUWhaYWNNQ3lYVlRhS0lCT2xVcVkzSVdiRGNMNHlIQUVuN3ZWYVl4cHhsZFdUVVJpSHYvdVhISHBITnNiSGRaZmJvVmUwOUUrakpBTkY5QXBkbFQwdW1SNTRaY21nbW1pNEo5NFdWMWZGMnliL2FueHM4dzh4NTBQbXJxc2txWWNaY1IrM3BJazByalFWaDJJVzBkUzlYdDhpbEFBWTJ2aFRiREZwT3MyL01wa2hhYitHYXdVNnpIakJrMEdSa3krWHFhODgwQ3JHY0lXSU53ZVdZTnp2NkY0Y3h6RXdNeXlXUzJnV2c0L1ZmQVhEZk0yNGErNGd3YUlteVVrcml3QzIxSXc5T0xqZnFPcGMrSWJzdFdXUXpnRFZtSzlDWktEVHc9PSJ9"
    "version":"3",
    "deviceId": "228fd425-035d-4bfc-aec6-598a213cfc16", //"<optional if the device already has deviceId it should be shared>"
    "deviceKey": "-----BEGIN PUBLIC KEY-----\r\nMIIBCgKCAQEAv7Z48S1GQh70Y+4xVn5R5+a0QSnM0nCIT4OIlhgo5NufduynQu+6\r\nIs9D2WGYzhyTIWPAQw6bLtc5WMP1WFSzZUKEZOKcr9864SCs9qVUmjrQXKkXWQnQ\r\nnKt6koBW1cbt86xHtTjcVO4I1R1TBhdcVq/83A86194MXXu9OlwmS9V637WT2jsd\r\ndL8DWmlce3OJhEZrvna1a+yujDNViTwW3njMGDMfwgonTREnXVFigaXdZZwTA4In\r\nQvD2wUY/n2kqhaLJQQglIXZzMSvIUCA8fyN0rfX86FvxS3CFxA30MKzg/YjzKMX4\r\ntwYgzIsKqmANKiUnFC1sKDZdEn8Fyu6l6wIDAQAB\r\n-----END PUBLIC KEY-----"
}
```

In the backend, check the primaryDeviceStatus == 3 (User have primary device, request device is belongs to this user) and there is no deviceKey, update it in the DB.

if the deviceKey is found in the DB, skip it.


### /user/biometric/authenticate [UPDATE]

current one:
```json=
{
  "token": "a5199ed519e61f50:EsWn1EbnfAnFKV5j2uDpVVsla3Igb3OmOsZBpAIgGP4=",
  "signature": "EKf9rmiWRNKIsoVUMIQIY1BE8Z+cvyRGXftZ37DOZbqIPqRJSSJCtiNDEzrq+lFRdRlzPBIGYjtH1f4aXVtHvzjq0JW/2b+76SVkcus44tIU4K54aBN67fvQySOEqzXaTtIzyVPoSTxlUig7j2R3X7kqxkBR7xgJHwJqzM+aSQFdbfC/1yTVIvPazxwm74XyYE9MqLqAyYPOYbohzS1lnS5PPxnRd/smBEDwgBY8q0n9S49HB1EjtTskigQ6vl6oLj6sPcuw5oTlTXXQ9bFbUr8KvXsmAxvQVqbb41vSPkV8aOjlvmxg1blamqyK3mMzAx9O0sd8lBef9TcQifieuA=="
}
```

Updated for v3:
```json=
{
  "token": "a5199ed519e61f50:EsWn1EbnfAnFKV5j2uDpVVsla3Igb3OmOsZBpAIgGP4=",
  "signature": "EKf9rmiWRNKIsoVUMIQIY1BE8Z+cvyRGXftZ37DOZbqIPqRJSSJCtiNDEzrq+lFRdRlzPBIGYjtH1f4aXVtHvzjq0JW/2b+76SVkcus44tIU4K54aBN67fvQySOEqzXaTtIzyVPoSTxlUig7j2R3X7kqxkBR7xgJHwJqzM+aSQFdbfC/1yTVIvPazxwm74XyYE9MqLqAyYPOYbohzS1lnS5PPxnRd/smBEDwgBY8q0n9S49HB1EjtTskigQ6vl6oLj6sPcuw5oTlTXXQ9bFbUr8KvXsmAxvQVqbb41vSPkV8aOjlvmxg1blamqyK3mMzAx9O0sd8lBef9TcQifieuA=="
    "version":"1",
    "deviceKey": "-----BEGIN PUBLIC KEY-----\r\nMIIBCgKCAQEAv7Z48S1GQh70Y+4xVn5R5+a0QSnM0nCIT4OIlhgo5NufduynQu+6\r\nIs9D2WGYzhyTIWPAQw6bLtc5WMP1WFSzZUKEZOKcr9864SCs9qVUmjrQXKkXWQnQ\r\nnKt6koBW1cbt86xHtTjcVO4I1R1TBhdcVq/83A86194MXXu9OlwmS9V637WT2jsd\r\ndL8DWmlce3OJhEZrvna1a+yujDNViTwW3njMGDMfwgonTREnXVFigaXdZZwTA4In\r\nQvD2wUY/n2kqhaLJQQglIXZzMSvIUCA8fyN0rfX86FvxS3CFxA30MKzg/YjzKMX4\r\ntwYgzIsKqmANKiUnFC1sKDZdEn8Fyu6l6wIDAQAB\r\n-----END PUBLIC KEY-----"
}
```

Once signature verified, check the deviceKey in the DB, if it not available update it, else skip it.


### SNACK v2.0 to v3.0 migration with active session.
- To user to force logout user with a trigger point from BE 
 [All Users]
 
pre-production:
BE - v3.0 - [Maintanance during the BE deployment - 1 hr]
APP: v2.0
 
After maintenace - App login - using the new token [which is encrypted claims]

after-production:
BE - v3.0
APP: v3.0 - [New installation it will be fine]
v2.0 -> v3.0 [if the user already logged in and update to v3.0]

migration {
    // force logout the user. // APP side.
}
 
[ALL users need to update]
v3.0 - forceupdate version


BE fixes for login: trigger force update from app_update.config from firebase.

v3.0 -> v3.0.1

migration { 
    // if needed
    // force logout the user. // APP side.
}
 



