# Handling 2FA Push

Hyphen uses Firebase Messaging to send a 2-factor authorization request to the user's device. Without using SDK, your client needs to handle and implement the data sent along with the push message and display appropriate UI to the user.

## Common Data Format

In the push message, there are fields that indicate whether the message is from Hyphen and which type is it. The actual data is always encoded as JSON and wrapped in a `hyphen:data` key in the push message.

<table><thead><tr><th width="214">Key</th><th width="244.33333333333331">Description</th><th>Example</th></tr></thead><tbody><tr><td><strong><code>hyphen:type</code></strong></td><td>The message type</td><td><code>"2fa-request"</code></td></tr><tr><td><strong><code>hyphen:data</code></strong></td><td>JSON-encoded data object</td><td><code>"{\"twoFactorAuth\":...}"</code></td></tr></tbody></table>

For example, on iOS, the data can be decoded like this:

{% tabs %}
{% tab title="iOS (Swift) Example" %}

```swift
struct TwoFactorAuthStatusPayload: Codable { ...}

func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable : Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    guard let msgType = userInfo["hyphen:type"] as? String,
        let msgDataString = userInfo["hyphen:data"] as? String,
        let msgData = msgDataString.data(using: .utf8) else {
        // The payload was not a string or the string could not be converted to Data
        completionHandler(.failed)
        return
    }
    do {
        let decoder = JSONDecoder()
        switch msgType {
        case "2fa-status-change":
            let myData = try decoder.decode(TwoFactorAuthStatusPayload.self, from: msgData) 
            completionHandler(.myData)
        }
    } catch {
        print("Decoding error: \(error)")
        completionHandler(.failed)
    }
}

```

{% endtab %}

{% tab title="Android (Kotlin) Example" %}

<pre class="language-kotlin"><code class="lang-kotlin">import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import kotlinx.serialization.*
import kotlinx.serialization.json.*
<strong>
</strong><strong>class YourFCMService: FirebaseMessagingService {
</strong>    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        if (remoteMessage.data.isNotEmpty()) {
            val type = remoteMessage.data["hyphen:type"]
            val hyphenData = remoteMessage.data["hyphen:data"]
            
            val json = Json {}
            
            if (type == "2fa-status-change") {
                val myData = json.decodeToString&#x3C;TwoFactorAuthStatusPayload>(hyphenData)
                // write your logic
            }
        }
    }
}
</code></pre>

{% endtab %}
{% endtabs %}

## List of Push Message Data

### 2FA Request (On Destination Device)

This message is being sent to the destination device which approves/denies the 2FA request (e.g. the existing device having a key registered). It's sent as a notification with the highest priority.

#### Data Format

The **`hyphen:type`** is `"2fa-request"` .

<table><thead><tr><th width="191.33333333333331">Key</th><th>Description</th><th>Example</th></tr></thead><tbody><tr><td><code>twoFactorAuth</code></td><td><code>TwoFactorAuthStatus</code> object used same as REST API's.</td><td><code>{"id": "faceb00c-cafe-babe-badd-deadbeef1234", "app": ...}</code></td></tr></tbody></table>

#### Example

<pre class="language-json"><code class="lang-json">{
<strong>  "twoFactorAuth": {
</strong>    "id": "faceb00c-cafe-babe-badd-deadbeef1234",
    "status": "pending",
    "expiresAt": "2023-07-21T18:36:27.872Z",
    "request": {
      "id": "faceb00c-cafe-babe-badd-deadbeef1234",
      "app": {"appId": "swirl-dev", "appName": "Swirl"},
      "userOpInfo": {
        "type": "sign-in",
        "signIn": {
          "email": "john@acme.com",
          "ip": "127.0.0.1",
          "location": "Seoul, Korea"
        }
      },
      "message": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
      "srcDevice": {
        "publicKey": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
        "pushToken": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
        "name": "iPhone 14",
        "osName": "iOS",
        "osVersion": "16.2",
        "deviceManufacturer": "Apple",
        "deviceModel": "SM-265N",
        "lang": "en",
        "type": "mobile"
      },
      "destDevice": {
        "publicKey": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
        "pushToken": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
        "name": "iPhone 14",
        "osName": "iOS",
        "osVersion": "16.2",
        "deviceManufacturer": "Apple",
        "deviceModel": "SM-265N",
        "lang": "en",
        "type": "mobile"
      },
      "requestedAt": "2023-07-21T18:31:27.872Z"
    }
  }
}
</code></pre>

### 2FA Status Change (On Source Device)

This message is being sent to the source device which requested the 2FA (e.g. the new device tries to sign in). It's sent as a [Background Update](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app) push without any notification.&#x20;

#### Data Format

The **`hyphen:type`** is `"2fa-status-change"` .

<table><thead><tr><th width="191.33333333333331">Key</th><th>Description</th><th>Example</th></tr></thead><tbody><tr><td><code>twoFactorAuth</code></td><td><code>TwoFactorAuthStatus</code> object used same as REST API's.</td><td><code>{"id": "faceb00c-cafe-babe-badd-deadbeef1234", "app": ...}</code></td></tr></tbody></table>

#### Example

If the user approves, the `twoFactorAuth.result.txId` field will contain the new key registration transaction sent from the destination device.

```json
{
  "twoFactorAuth": {
    "id": "faceb00c-cafe-babe-badd-deadbeef1234",
    "status": "approved",
    "result": {
      "txId": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de"
    }
    "expiresAt": "2023-07-21T18:36:27.872Z",
    "request": {
      "id": "faceb00c-cafe-babe-badd-deadbeef1234",
      "app": {"appId": "swirl-dev", "appName": "Swirl"},
      "userOpInfo": {
        "type": "sign-in",
        "signIn": {
          "email": "john@acme.com",
          "ip": "127.0.0.1",
          "location": "Seoul, Korea"
        }
      },
      "message": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
      "srcDevice": {
        "publicKey": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
        "pushToken": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
        "name": "iPhone 14",
        "osName": "iOS",
        "osVersion": "16.2",
        "deviceManufacturer": "Apple",
        "deviceModel": "SM-265N",
        "lang": "en",
        "type": "mobile"
      },
      "destDevice": {
        "publicKey": "faceb00ccafebabedeadbeefbadf00defaceb00ccafebabedeadbeefbadf00de",
        "pushToken": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
        "name": "iPhone 14",
        "osName": "iOS",
        "osVersion": "16.2",
        "deviceManufacturer": "Apple",
        "deviceModel": "SM-265N",
        "lang": "en",
        "type": "mobile"
      },
      "requestedAt": "2023-07-21T18:31:27.872Z"
    }
  }
}
```
