# Hyphen

Hyphen aims for Firebase Auth for Web3: non-custodial mobile wallet SDK utilizing user's mobile devices as cold-wallet.

<figure><img src="/files/Rq6vJxPWaNmdZqiZOGIY" alt=""><figcaption></figcaption></figure>

### Explore Concepts

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Android SDK Quickstart</strong></td><td>Start building hassle-free Android Flow dapp using<br>Hyphen Android SDK. </td><td></td><td><a href="/pages/SrJzJNkLoz6j00zdWWpq">/pages/SrJzJNkLoz6j00zdWWpq</a></td></tr><tr><td><strong>iOS SDK Quickstart</strong></td><td>Start building hassle-free iOS Flow dapp using Hyphen iOS SDK. </td><td></td><td><a href="/pages/798iB1XmVlpjY3f6uIO9">/pages/798iB1XmVlpjY3f6uIO9</a></td></tr><tr><td><strong>Concepts</strong></td><td>Explore our concepts that enables building hassle-free UX with most security.</td><td></td><td></td></tr><tr><td><strong>Mechanism Paper</strong></td><td>Learn more about Hyphen's key management schemes.</td><td></td><td><a href="/pages/Qy8QSs1ioU5YP4mvXi2Y">/pages/Qy8QSs1ioU5YP4mvXi2Y</a></td></tr></tbody></table>


# Concepts

The concepts and features of the Hyphen

<figure><img src="/files/sciqwPi3q73jFQz89glP" alt=""><figcaption><p><strong>Diagram 1</strong>. The structure diagram of the Hyphen.</p></figcaption></figure>


# Auth Methods

Apps using Hyphen SDK can use Web2-ish login methods instead of connecting a wallet.

## Social Login

Social login is the basic login method using the basic OAuth 2.0. Various providers like Google, Facebook, Github, etc. are supported.

<figure><img src="/files/WkScXBjwjD2ix750XK2Q" alt="" width="249"><figcaption><p><strong>Figure 1.</strong> The example of Sign-In Screen.</p></figcaption></figure>

## PassKey *(Coming Soon)*

The app can allow the user to re-login with PassKey. Once the PassKey is created after the user signs up with the other log-in method, the user can log in with PassKey in one click. On iOS and Android, the PassKey is synchronized through their cloud account.

## Password Login *(Coming Soon)*

The app can also use ID/email and password to sign in. Hyphen provides essential features like finding the ID/password and changing the password.

## Custom Login Methods *(Coming Soon)*

The app can even implement the login method on their own. In this case, the app should enable the custom login method feature first on the dashboard and needs to authenticate the user using their own method (e.g. SAML), and call the Sign-In / Sign-Up REST API.


# Hyphen Multi-Sig Account

All user accounts in Hyphen are multi-sig. Normally, multi-sig means that keys are shared among multiple parties, but Hyphen's Multi-Sig only involves multiple devices owned by the same user.

This is referred to as the **User Key**. It operates similarly to the Login Session in Web2 services, so users can manage their logged-in devices and add/remove them as desired. In situations where multiple device keys are needed, using **2FA** is a characteristic of Hyphen.

However, doing this every time can be inconvenient in terms of UX and decrease recoverability (risk of losing control of the account due to device loss). To solve this problem, we set a Server Key and a Recovery Key respectively. The Server Key is stored on Hyphen's server, while the Recovery Key is stored in a user-owned space (e.g., cloud, personal wallet).

Thanks to this well-balanced multisig structure, Hyphen can be a **Non-Custodial Wallet while not sacrificing UX and Security**.


# Device Key

Storing private keys securely in HSM (Hardware Security Module)

The device key is stored safely in the mobile device’s special secure hardware chip: **HSM (Hardware Security Module)**. Examples of HSMs are Apple's Secure Enclave (Apple T2 chip) and Android Keystore.

The key is isolated from the mobile OS using a specialized hardware chip to protect it from any security threats, and an app can only request to sign/verify a transaction- meaning that the keys stored in HSM cannot be taken out of the hardware. Therefore, It can be seen as utilizing users’ mobile devices as a cold wallet.&#x20;


# Recovery Key

Recovering the account access when the user loses devices

If a user only has device keys, there's no way to access the account if these devices are all lost. To prevent such a situation, Hyphen provides a secondary recovery method called a **Recovery Key**. For more information and an explanation of why this was introduced, please refer to the ["Recovering an Account" section in the Hyphen Mechanism Paper](/mechanism-paper#recovering-an-account).

**Recovery Key** is a backup key used in the case when the user loses access to the account. It’s stored on the user's side (e.g. user's own cloud storage) so the user can access it anytime. Using the recovery key and the server key, a user can send the transaction to register a new device.

## Cloud Key

The key is stored in the user's cloud. This is a method commonly used by many Crypto Wallets. It is very convenient in terms of UX as the integration is complete with the press of a button. Although there is a downside that if the user's cloud is hacked, it is also compromised, the Cloud Key in Hyphen is just one of the multisig keys, so it is safe.

On iOS, we use iCloud Keychain, and on Android, we use the Google ID Platform (Blockstore).

## Email / SMS Key *(Coming Soon)*

The key is stored on the app server. Like the password recovery in conventional Web2, it can be recovered through email verification or SMS verification. If the app implements an Email / SMS verification API according to Hyphen standards, the user directly sends an Email / SMS recovery request to the app. The app then recovers the user's account with the stored recovery key.

{% hint style="info" %}
**Q. Why does the key is stored on the app's server, instead of the Hyphen Server?**\
For the checks and balances of the multi-sig account. If the Hyphen server stores the recovery key or handles the recovery process, we would end up having two keys, possessing the threat of taking over the control of the user's account.
{% endhint %}

## Custom Recovery Method *(Coming Soon)*

Apps can implement their own recovery methods. After creating a recovery key and directly storing it on the app server, when a user requests account recovery, Hyphen requests a recovery key signature from the app server through a Webhook.


# Server Key

Since Hyphen's multi-sig always requires at least two keys ***(2 of N)***, if a user account only has a device key, there is the inconvenience of having to do 2FA every time signing a transaction. To solve this UX issue, we store a key on the Hyphen server called the ***Server Key*** (a.k.a. Custodial Key).

The Server Key has an attribute called **Auto-Sign**. When signing a transaction with the Hyphen SDK, the Hyphen server always provides a Server Key signature. Therefore, from the user's perspective, they can secure two signatures— the current device key signature + server key signature— which means they can send transactions without 2FA under normal circumstances.


# Paymaster

Paying the transaction fee on behalf of users

Letting users pay the transaction fee is the biggest hurdle while building the consumer-centric app. Paymaster is the feature allowing the app can pay the user's transaction fees on their behalf. Not only just paying, the app can also apply any desired policy to prevent misusing.

## Pay-by-App Policy

This is the most basic paymaster policy. If the app deposits the Native Token of the respective chain ($FLOW) with Hyphen, the deposit is spent to pay for the gas of user transactions.

Apps can have user-specific limit policies (e.g., free up to 5 times per account per month).

## Pay-with-Token Policy *(Coming Soon)*

Users can pay for transaction costs by spending a specific token designated by the app. We swap that token for the chain's Native Token to cover the transaction gas fee.

## Custom Policy *(Coming Soon)*

Based on the Pay-by-App policy, apps can implement their own policy that allows them to filter the users/circumstances they want. Policies such as Pay-with-Token can also be implemented based on this Custom Policy.

The operation is done through a Webhook. When the app specifies an arbitrary Custom Policy Webhook URL, each time a user sends a transaction, Hyphen calls that Webhook to ask if this user can send such a transaction. Apps can implement this Webhook on their own server, allowing them to design a Paymaster that filters specific users or pays transaction costs with tokens/NFTs instead.


# Hybrid Custody

Hyphen is based on a [Hybrid Custody methodology](https://flow.com/hybrid-custody). Initially, users hand over some of their keys for the sake of UX, but later they can obtain 100% control over their account when desired. In other words, it can be seen as [structurally centralized, but politically decentralized](https://medium.com/@VitalikButerin/the-meaning-of-decentralization-a0c92b76a274).

## Method A. Account Linking

Users can link their wallets to the Hyphen Multi-Sig account. Here, 'linking' means the user's wallet becomes the Parent Account, and the Hyphen Multi-Sig becomes the Child Account, which falls under the control of the user's wallet.

In Flow, account linking can be possible because the `AuthAccount` capability is implemented natively. Note that after linking the account, the parent wallet must load the child wallet's information as well (e.g., when listing tokens/NFTs for the user, all tokens/NFTs information from all Child Accounts must also be listed).

## Method B. Revoke Custodial Key

You can gain 100% control over the account simply by removing the [Server Key](/concepts/hyphen-multi-sig-account/server-key) from the account. Simply, send a transaction to revoke the server key from the Hyphen Multi-Sig account. However, a change/downside of this is that you will have to go through 2FA every time you sign a transaction thereafter.


# Mechanism Paper

About the Hyphen Key Management Mechanism

## Background

### Problem

#### Wallet Trilemma

There’s no existing wallet that satisfies User Experience, Recoveriness, and Security.

1. **User Experience (UX)**: Creating and connecting a wallet should be easy and hassle-free as the user experience on Web2.
2. **Recoveriness**: When a user loses the primary mean of accessing the wallet, it should provide a secondary method to recover access.
3. **Security**: The private key should be managed and stored in a secure manner to prevent being stolen.

<figure><img src="/files/xPrPJjtoC2PtLhEv4zSd" alt=""><figcaption></figcaption></figure>

#### Lack of Mobile Resources

Currently, there are not many resources when developing a mobile app on Flow. Most of the resources are focused on building on the web and it’s hard to find the libraries/resources for mobile development.

### Key Idea

#### Flow Account System

In the Flow account system, the account and the key are decoupled, so it is possible to add or detach multiple keys into an account to use it as a multi-signature account, or each key can have a different cryptographic signature scheme.

Interestingly, Flow is the only chain that supports the NIST P-256 (`secp256p1`) curve, which is also the only curve that is supported by TEE in both Android and iOS. This characteristic encourages optimizing a wallet system for mobile user experience.

## Hyphen: The Key Management Scheme for Ultimate Mobile Wallet UX and Security

***

We introduce a Hyphen: A secure key management scheme leveraging Flow’s native account system and TEE in mobile devices.

In MEOW, a user account is set up as a multi-sig account with three types of keys: device key, server key, and recovery key. At least 2 keys are required to send a transaction.

The device key is stored safely on the mobile device’s secure hardware chip: *TEE*. it can be seen as utilizing users’ mobile devices as a cold wallet. to improve the recoverability and user experience, the server key and recovery key are used: the server key is stored on the Hyphen's server, and the recovery key is stored on the user’s cloud storage. with the keys distributed each different place, the maximum level of security can be assured while sacrificing any user experience.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b5042ccd-bfd0-4a9a-82c7-870dd025e096/Untitled.png)

#### Glossaries

* **App**: A client app uses Hyphen.
* **User**: The user using the app. The owner of the account.
* **TEE (Trusted Execution Environment)**: An isolated hardware for secure operations. When it’s used for public key cryptography, it allows signing or verifying a signature while keeping the private key inside of the hardware.
* **Server Key**: One of the default keys of a Hyphen account, which is stored on Hyphen's server.
* **Cloud Key**: One of the default keys of a Hyphen account, which is stored on the user’s own cloud storage such as iCloud Keychain, Google Cloud, etc.
* **Device Key**: A key stored in TEE in a user’s mobile device.

### Key Features

#### Mobile-Native Key Management through TEE

The key is isolated from the mobile OS using a specialized hardware chip to protect it from any security threats, and an app can only request to sign/verify a transaction.

#### Hybrid Custody with Multi-Signature

Hyphen achieves a form of hybrid custody by mixing self-custodial keys and a custodial key for an account. An account is composed of three types of keys: Server Key, Device Key, and Recovery Key.

The server key is a custodial key that is hosted by the Hyphen server. Device Keys are self-custodial keys safely stored in the TEE of users’ mobile devices. The recovery Key is a backup key used in case of device loss or app uninstall.

To send a transaction, at least 2 keys are required. This scheme maintains perfect checks and balances in terms of security: even if any of the keys is being stolen or used maliciously, the account access is still protected.

### Initial Setup

After the initial sign-up process (e.g. Social Login), **an** **N-of-2** **multi-sig account is created with** **three keys.**

![Diagram 1. Initial Key Distribution on Account Creation. Every key’s weight should be set as same.](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a4659bdc-10ed-4cbe-bc36-c3d5073988a7/Untitled.png)

**Diagram 1.** Initial Key Distribution on Account Creation. Every key’s weight should be set as same.

* **Recovery Key**: stored on the user’s cloud (e.g. Apple iCloud, Google Cloud).
* **Server Key**: stored in the Hyphen server. often mentioned as a custodial key.
* **Device Key**: stored in the TEE of the user’s mobile device. never accessible on the outside.

### Sending a Transaction

**To send a transaction, it is required to reach at least a quorum of 2 keys**. So even if a key is being stolen by malicious actors, the account is safe because they still need one more key to get control. However, it’s not feasible because these keys are stored in different locations.

By default, the current device’s key and the server key are used to sign a transaction. Using the device key requires biometric auth from a user (e.g. Face ID, Touch ID), and the server key requires JWT auth to the Hyphen server, which is internally handled by Hyphen SDK.

![Diagram 2. Illustrates a process of signing & sending a transaction in Hyphen.](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6323311c-470f-463b-bf9f-abae1d2e65cb/Untitled.png)

***Diagram 2.*** Illustrates a process of signing & sending a transaction in Hyphen.

### Managing Keys

As a user uses more apps using Hyphen with more devices, more device keys can be created and registered into the account. If a user uninstalls an app or lost a device, they can revoke the access of the device key to the account.

Hyphen provides session-management features just like the familiar Web2 services like Google, Facebook, and Whatsapp.

#### Signing In from Another App / Device

To sign in from another app or another device, a new device key should be created and added to the account. First, the app needs to let the user sign in with the login methods provided by Hyphen. After signing in, the app will be authenticated to the Hyphen server, thus getting access to the server key. However, since adding the key requires sending a transaction, which requires at least 2 keys— the app needs another device’s help since one is not enough.

This is where the **2FA (2-Factor Authentication)** comes in. The app (new device) requests 2FA to the user’s other devices, and the user authorizes it on the other device. The Hyphen server acts as a relayer that relays the 2FA request/responses.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c844f1ca-da27-46f1-a974-e0c0fa9cf633/Untitled.png)

#### Revoking a Device

When a user loses a device or tries to sign out from an app, the device key should be revoked from the account. A transaction should be sent to remove the device from the account’s key list.

### Lifecycle of Device Key

Although the key is named as “device key”, it’s actually app-specific: since the Android and iOS sandboxes each app’s storage, it’s no exception for TEE keystores. Even on the same device, the device key should be created per app installation and can be reset if:

* the user deletes the app
* the user changes biometric auth information (e.g. on iOS, adding a new finger to Touch ID)

the limitation of the device key is that it has a shorter lifespan, as you can see. normally this would be not a good characteristic of be crypto wallet— so we leverage it with the multi-sig scheme and, the recovery key.

### Recovering an Account

When a user loses access to the account, for example when they lost all devices registered or device keys are being reset, Hypen provides a password-reset-like-UX to recover the account.

**The recovery key** is a backup key used in the case when the user loses access to the account. It’s stored on the user’s own cloud storage so the user can access it anytime. Using the recovery key and the server key, a user can send the transaction to register a new device.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4b1773ae-5c86-4e52-a8f0-6e7584df7911/Untitled.png)

\<aside> \<img src="/icons/cloud-yes\_lightgray.svg" alt="/icons/cloud-yes\_lightgray.svg" width="40px" /> **Types of Cloud Storages** The Cloud storage can differ by the platform:

* Apple: iCloud Keychain
* Android: Google Cloud \</aside>

#### The Necessity of the Recovery Key

Without the recovery key, there’s a risk of the user being lost access to the account if they only have one device registered and lost the device since the multi-sig requires at least 2 keys.

But if we have another backup key, the user can still maintain access through the key and the server key, because both are stored in a remote location. For checks and balances, the recovery key is stored on the user’s own cloud account.

Thanks to the multi-sig, it’s safe to store the recovery key on the cloud. Mobile wallets storing the private key on the cloud are often criticized that it’s unsafe. But it's safe even if the recovery key gets hacked— because the recovery key alone, which is just one of the keys, is not enough to send a transaction.

#### **Customizable** **Security Level**

For some apps, adding a step in onboarding to create a recovery key can be a burden. MEOW offers a variety of options for the apps:

1. **With Cloud Key**: Recommended.
2. **No Cloud Key**: Not recommended in terms of recoverability, but could be the best in terms of initial onboarding UX. An app can remind users to create the backup key later.

   ![Example: Rainbow Wallet nudges the user to back up their key on iCloud later.](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d5fd7296-b342-4198-850e-a4efe55b748c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-06-04_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_7.14.56.png)

   **Example**: Rainbow Wallet nudges the user to back up their key on iCloud later.

#### **Why Not Seed Phrase?**

The recovery key acts a similar role to the seed phrase. Both are used as backup methods when a user lost their key— but unlike the seed phrase, it’s more secure and enables easy UX for the user. Here’s the comparison:

| Scenario                       | Seed Phrase                                                                                   | Recovery Key                                                                                  |
| ------------------------------ | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| When a user onboards           | Write down the seed phrase, and double-check to make sure that they wrote it correctly.       | Connect iCloud / Google Cloud with a click of a button.                                       |
| Where it is stored?            | Offline recommended, but any uncautious user can keep it online (e.g. Memo App, 1Password, …) | Encrypted on User’s Cloud                                                                     |
| What happens when it’s stolen? | The access of accounts created with the seed phrase is being exposed to the hacker.           | Nothing. To get access, the hacker also needs to hack the Hypen server to get the server key. |
| How to cope with the hacks?    | Move all assets in the account immediately into the new account.                              | Just revoke the lost key and reissue a new one.                                               |

### Achieving Self Custody

A user can take full control of his/her own account by following methods. Note that these methods are mutually exclusive: one or more methods can be applied by the user.

#### Method 1. Revoke Server Key

Hosting a server key on the Hypen server makes Meow “hybrid custody”. But since the user has the majority of keys, **it can be made self-custody by just revoking the server key.**

After revoking the server key, the user will always need to do 2FA every time they send a transaction because two keys are required.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d853580f-78ec-4611-b747-683ce5400cb0/Untitled.png)

#### Method 2. Link to Your Own Account

The best situation in the crypto world is having their own personal wallet. Of course, the user could barely care about crypto things like self-sovereignty at the beginning, but as time flies and the user’s asset grows, the user might think that it’s time to switch over a safe, personal wallet owned by themselves.

Luckily, Flow has the capability of account linking, enabling an account to be linked to another account as the parent account. The parent account is able to control the child including its assets.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/911deec1-c43d-405e-804c-6bbe549c6bf3/Untitled.png)


# Quick Start

Start by Using Hyphen iOS SDK

## Create an App in the Dashboard

You need to create an app in the [Hyphen Dashboard](https://hyphen.at/dashboard/new) before using Hyphen SDK. After creating an app, an app secret will be issued. The app secret is required to initialize the SDK.

## Install the SDK using SPM

We recommend Swift Package Manager to install our SDK.

{% tabs %}
{% tab title="Swift" %}
Edit `Package.swift` of your project to add Hyphen SDK.

```swift
import PackageDescription

let package = Package(
    name: "my-project",
    dependencies: [
        .package(url: "https://github.com/hyphen-at/hyphen-ios-sdk.git"),
        ...
    ],
    targets: [...]
)
```

{% endtab %}

{% tab title="Using Xcode" %}

1. In Xcode, Open `.xcodeproj` file of your project.
2. Go to *Package Dependencies*.
3. Click + button below to add a dependency.
4. Enter **`https://github.com/hyphen-at/hyphen-ios-sdk.git`** URL.
5. Click "*Add Package*".
   {% endtab %}
   {% endtabs %}

## Configuring the SDK

On `AppDelegate.swift` of your application, insert the following initialization code.

{% tabs %}
{% tab title="Swift" %}

```swift
import FirebaseMessaging
import HyphenAuthenticate
import HyphenCore
import HyphenUI
import UIKit
import UserNotifications

@main
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
    ) -> Bool {
        // initializes Hyphen SDK.
        Hyphen.shared.appSecret = "<INSERT-YOUR-SECRET-HERE>"
        Hyphen.shared.network = .flowTestnet // or flowMainNet
        HyphenAuthenticateAppDelegate.shared.application(application)
        HyphenUI.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    /// Updates the push token for Hyphen 2FA.
    func application(_: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Hyphen.shared.apnsToken = deviceToken
    }
    
    /// Handles the Hyphen 2FA silent push.
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) async -> UIBackgroundFetchResult {
        await Hyphen.shared.application(application, didReceiveRemoteNotification: userInfo)

        return .newData
    }
    
    /// Handles the Hyphen 2FA notification. 
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
        await Hyphen.shared.userNotificationCenter(center, didReceive: response)
    }
    
    /// Handle when the app is opened through the 2FA notification
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        HyphenAuthenticateAppDelegate.shared.application(app, open: url, options: options)
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}
*Sorry, the example is not provided yet.*

{% endtab %}
{% endtabs %}


# Authenticating Users

Use HyphenAuthenticate to sign in users

Authentication services are provided in the [HyphenAuthenticate](https://github.com/hyphen-at/hyphen-ios-sdk/tree/main/HyphenAuthenticate) module of the SDK. You can sign in, create an account, or get the user's key and account information through the module.

### Signing In

{% embed url="<https://github.com/hyphen-at/swirl-ios-client/blob/09cfc9e925517cf31fd40218e1a715a82ffb05cb/Projects/Feature/SignIn/Sources/SignInCore.swift>" %}


# Using Hyphen UI Kit

For easy integration of the Hyphen into your app, we offer a separate SDK module called **Hyphen UI Kit**. This library comprises a set of ready-to-use UIs that can be integrated into your app in an instant.

* **Easy Integration:** you just need a few lines of code to integrate it.&#x20;
* **Customizable**: the color, fonts, and themes are fully customizable so they can seamlessly fit with your app's branding.
* **Localized**: the UI kit is provided with localizations over N+ languages  *(Coming Soon!)*

You can check it in the SDK package: [**HyphenUI**](< https://github.com/hyphen-at/hyphen-ios-sdk/tree/main/HyphenUI>)**.** We currently offer:

* **Sign-In Screen**: Onboard your user seamlessly just with a few lines of code.
* **Account Management Screen**: Just like the session management UIs in Web2, users can manage their devices and add/remove keys as they want.
* **2-Factor Authorization UI**: We handle the stuff like push and authorization.


# Account Management Screen

Use [**`HyphenKeyListViewScreen`**](https://github.com/hyphen-at/hyphen-ios-sdk/blob/main/HyphenUI/Sources/KeyList/HyphenKeyListScreen.swift) to show the account management screen to the user. The user can manage and revoke keys and devices in their account.

You can launch the screen with a single line of the code:

{% tabs %}
{% tab title="Swift" %}

```swift
Hyphen.shared.openKeyManager()
```

{% endtab %}

{% tab title="Objective-C" %}
*Example code is not provided yet.*
{% endtab %}
{% endtabs %}


# Sample Project

Explore the sample project using our iOS SDK

{% embed url="<https://github.com/hyphen-at/swirl-ios-client>" %}


# Quick Start

Start by Using Hyphen Android SDK

## Create an App in the Dashboard

You need to create an app in the [Hyphen Dashboard](https://hyphen.at/dashboard/new) before using Hyphen SDK. After creating an app, an app secret will be issued. The app secret is required to initialize the SDK.

## Install the SDK using Gradle

We recommend Gradle to install our SDK.

{% tabs %}
{% tab title="build.gradle" %}
Edit `build.gradle` of your app level module to add Hyphen SDK.

```gradle
implementation "at.hyphen:android-sdk-core:1.0.0-alpha06"
implementation "at.hyphen:android-sdk-authenticate:1.0.0-alpha06"
implementation "at.hyphen:android-sdk-networking:1.0.0-alpha06"
implementation "at.hyphen:android-sdk-ui:1.0.0-alpha06"
implementation "at.hyphen:android-sdk-flow:1.0.0-alpha06"
```

{% endtab %}

{% tab title="build.gradle.kts" %}
Edit `build.gradle.kts` of your app level module to add Hyphen SDK.

```kotlin
implementation("at.hyphen:android-sdk-core:1.0.0-alpha06")
implementation("at.hyphen:android-sdk-authenticate:1.0.0-alpha06")
implementation("at.hyphen:android-sdk-networking:1.0.0-alpha06")
implementation("at.hyphen:android-sdk-ui:1.0.0-alpha06")
implementation("at.hyphen:android-sdk-flow:1.0.0-alpha06")
```

{% endtab %}
{% endtabs %}

## Firebase Configuration

Need your Firebase project to use Hyphen SDK. After enabling Google Authenticate and Push Messaging in your Firebase project, complete the Firebase setup through the `google-services.json` file.&#x20;

Please read the link below to set up Firebase.

<https://firebase.google.com/docs/android/setup>

## Configuring the SDK

### Declaring Permission

The following permissions are required to use the Hyphen SDK. Insert the code snippet below your `AndroidManifest.xml`.

<pre class="language-xml"><code class="lang-xml"><strong>&#x3C;!-- Required for hyphen networking (communicate with hyphen api server / flow network) -->
</strong><strong>&#x3C;uses-permission android:name="android.permission.INTERNET" />
</strong><strong>
</strong><strong>&#x3C;!-- Required for signing with device key -->
</strong>&#x3C;uses-permission android:name="android.permission.USE_BIOMETRIC" />

&#x3C;!-- Required for 2fa / transaction request notifications -->
&#x3C;uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
</code></pre>

### Register Hyphen Activity

SDK has its own activity to handle 2FA authenticate, transaction requests. Register the activity in your `AndroidManifest.xml`.

<pre class="language-xml"><code class="lang-xml"><strong>&#x3C;activity
</strong>    android:name="at.hyphen.android.sdk.ui.twofactor.Hyphen2FAActivity"
    android:exported="true" />
</code></pre>

### Initialize SDK

Insert the code below into the `onCreate` area of the class that inherited the `Application` used within the app.

{% tabs %}
{% tab title="\[YourActivityName]Activity.kt" %}

```kotlin
import at.hyphen.android.sdk.core.Hyphen

class MainActivity : Application() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // ... 
        
        Hyphen.initialize(context = this)
        Hyphen.appSecret = "<YOUR-APP-SECRET>"
        Hyphen.network = Hyphen.NetworkType.TESTNET // or MAINNET
    }
}
```

{% endtab %}
{% endtabs %}


# Authenticating Users

Use HyphenAuthenticate to sign in users

Hyphen Authentication are provided in the [`at.hyphen:android-sdk-authenticate`](https://github.com/hyphen-at/hyphen-android-sdk/tree/main/authenticate) module of the SDK. You can sign in, create an account, or get the user's key and account information through this module.

{% hint style="info" %}
**Recovery Key Support for Android SDK is Currently in Beta**

As the SDK uses Google Play Credential Store to store recovery keys, it is required for the app to be published on Play Store in order to use the feature. If you want to access the feature, please feel free to reach out to <jun@meowauth.xyz>!
{% endhint %}

## Integrate Hyphen Authenticate

### Implementation `HyphenAuthenticateDelegate`&#x20;

You must implement the `HyphenAuthenateDelegate` interface from `Activity` class where you want to use Hyphen authentication.

{% tabs %}
{% tab title="YourActivity.kt" %}

<pre class="language-kotlin"><code class="lang-kotlin">class YourActivity: AppCompatActivity, <a data-footnote-ref href="#user-content-fn-1">HyphenAuthenticateDelegate</a> {
    
    // HyphenAuthenticateDelegate
    override var hyphenAuthenticateActivityResultLauncher: ActivityResultLauncher&#x3C;Intent> =
        (this as ComponentActivity).registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            hyphenActivityResultCallback?.invoke(it)
        }

    // IMPORTANT: you must set null this value.
    override var hyphenActivityResultCallback: ((ActivityResult) -> Unit)? = null
}
</code></pre>

{% endtab %}
{% endtabs %}

### Authenticate with Google Account

{% hint style="info" %}
Currently, Hyphen SDK only support Google Account.&#x20;
{% endhint %}

You must use the Web Client ID on the Google Cloud Platform for Google Authentication. See also [**Get your Google API client ID**](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid).

{% tabs %}
{% tab title="YourActivity.kt" %}

```kotlin
HyphenAuthenticate.authenticate(
    activity = activityContext,
    webClientId = "<YOUR-WEB-CLIENT-ID>" // ex. 201778913659-dn4bo82q6hce3kfp7vstp04b22nh5hbi.apps.googleusercontent.com
)
```

{% endtab %}
{% endtabs %}

### Get Hyphen Account

In authenticate successfully, you can get hyphen account.

{% tabs %}
{% tab title="YourActivity.kt" %}

<pre class="language-kotlin"><code class="lang-kotlin">val hyphenAccount = HyphenAuthenticate.getAccount(context = activityContext) // return object: <a data-footnote-ref href="#user-content-fn-2">HyphenAccount</a>
Log.e(hyphenAccount.addresses.first().address) // print "0x&#x3C;FLOW_ADDRESS>"
</code></pre>

{% endtab %}

{% tab title="Second Tab" %}

{% endtab %}
{% endtabs %}

[^1]: <https://github.com/hyphen-at/hyphen-android-sdk/blob/main/authenticate/src/main/kotlin/at/hyphen/android/sdk/authenticate/HyphenAuthenticateDelegate.kt>

[^2]: <https://github.com/hyphen-at/hyphen-android-sdk/blob/main/core/src/main/kotlin/at/hyphen/android/sdk/core/common/account/HyphenAccount.kt>


# Handling 2FA / Transaction Request

How to handle an attempt to 2fa authenticate or when a request for signing transaction.

## Request Notification Permission

You must request **Notification Permission** to receive notifications from Android 13+.

{% embed url="<https://developer.android.com/develop/ui/views/notifications/notification-permission>" %}

## Add Firebase Messaging Service

`FirebaseMessagingService` must be implemented to receive push notifications. If the service is already exist, you only need to add code related to Hyphen.

{% tabs %}
{% tab title="SampleMessagingService.kt" %}

```kotlin
import at.hyphen.android.sdk.ui.HyphenUI
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage

class SampleMessagingService : FirebaseMessagingService() {

    override fun onMessageReceived(message: RemoteMessage) {
        HyphenUI.onFirebaseMessageReceived(applicationContext, message)
    }
}
```

{% endtab %}

{% tab title="AndroidManifest.xml" %}

```xml
<service
    android:name=".SampleMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
```

{% endtab %}
{% endtabs %}

## Handling Hyphen Notification Click

Add the code below to the root activity to ensure proper handling when click Hyphen push notification.

{% tabs %}
{% tab title="\[YourActivityName]Activity.kt" %}

```kotlin
class MainActivity : AppCompatActivity(), HyphenAuthenticateDelegate {
    
    // ...
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        intent?.let {
            HyphenUI.handleHyphenIntent(this, it)
        }
    }
    
    // ...
    
    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)

        intent?.let {
            HyphenUI.handleHyphenIntent(this, it)
        }
    }
}
```

{% endtab %}
{% endtabs %}


# Using Hyphen UI Kit

For easy integration of the Hyphen into your app, we offer a separate SDK module called **Hyphen UI Kit**. This library comprises a set of ready-to-use UIs that can be integrated into your app in an instant.

* **Easy Integration:** you just need a few lines of code to integrate it.&#x20;
* **Customizable**: the color, fonts, and themes are fully customizable so they can seamlessly fit with your app's branding.
* **Localized**: the UI kit is provided with localizations over N+ languages  *(Coming Soon!)*

You can check it in the SDK package: `at.hyphen:android-sdk-ui`**.** We currently offer:

* **Sign-In Screen**: Onboard your user seamlessly just with a few lines of code.
* **Account Management Screen**: Just like the session management UIs in Web2, users can manage their devices and add/remove keys as they want.
* **2-Factor Authorization UI**: We handle the stuff like push and authorization.


# Sending Transactions

To send a transaction with Hyphen Android SDK

## Creating, Signing, and Sending Transactions

To create, sign, and send a transaction with the Hyphen Android SDK, follow these steps:

```kotlin
// Define the Cadence script for the transaction
val cadenceScript = """
    transaction {
        execute {
            log("Hello World!!!")
        }
    }
""".trimIndent()

// Create, sign, and send the transaction
val txId = HyphenFlow.signAndSendTransaction(
    cadenceScript = cadenceScript,
    arguments = emptyList(),
    withAuthorizer = false
)

// Log the transaction ID
Log.e("Transaction ID", txId) // Outputs: "0x<TXID>"
```

#### Explanation:

* `cadenceScript`: The Cadence script to be executed in the transaction.
* `arguments`: List of arguments for the transaction. In this example, it's an empty list.
* `withAuthorizer`: A boolean indicating whether the transaction requires an authorizer. Set to `false` in this example.
* `txId`: The transaction ID returned by the `signAndSendTransaction` method.

## Utilities

### Wait for Transaction Seal

To wait for a transaction to be sealed on the Flow blockchain, you can use `waitForSeal` utility method.

```kotlin
val txId = FlowId(hex = "0x<TXID>")
val txResult = waitForSeal(txId = txId) // return FlowTransactionResult object
```

### Get Transaction Result

To retrieve the result of a specific transaction:

```kotlin
val txId = FlowId(hex = "0x<TXID>")
val txResult = getTransactionResult(txId = txId) // return FlowTransactionResult object
```


# Quick Start

Get Started with the Hyphen Flutter SDK

## Create an App in the Dashboard

You need to create an app in the [Hyphen Dashboard](https://hyphen.at/dashboard/new) before using Hyphen SDK. After creating an app, an app secret will be issued. The app secret is required to initialize the SDK.

### Installing the SDK

1. Open your project's `pubspec.yaml` file.
2. Add Dependency: Under the `dependencies` section of your `pubspec.yaml` file, add the "hyphen\_flutter\_sdk" plugin as follows:

```yaml
dependencies:
  flutter:
    sdk: flutter
  hyphen_flutter_sdk: ^1.0.0
```

Replace `^1.0.0` with the desired version of the plugin. The `^` symbol indicates that your project can use any compatible version greater than or equal to the specified version.

1. Run `flutter pub get`: After adding the dependency, save the `pubspec.yaml` file, and run the following command in your terminal to fetch the plugin:

```
flutter pub get
```

This command will download the plugin and its dependencies into your Flutter project.

### Initializing the SDK

Initialize the Hyphen SDK within your Flutter app's `main.dart` file or in the initialization code of your Flutter app. Here's an example of how you might initialize the SDK:

```dart
import 'package:flutter/material.dart';
import 'package:hyphen_flutter_sdk/core/hyphen.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  Widget build(BuildContext context) {

    // Initialize the Hyphen SDK
    Hyphen.initialize(context);
    Hyphen.appSecret = "<YOUR-APP-SECRET>";
    Hyphen.network = NetworkType.TESTNET;

    return MaterialApp(
      title: 'My App',
      home: MyHomePage(),
    );
  }
}
```


# Authenticating Users

In Flutter, Hyphen Authentication functionalities are provided by the `authenticate` module of the SDK. This module enables users to perform various authentication tasks such as signing in, creating an account, and retrieving the user's key and account information.

### Integrate Hyphen Authenticate

The `HyphenAuthenticate` class provides functionalities for authentication using the Hyphen SDK.

* `getAccount`: Retrieves the user's account information.
* `authenticate`: Initiates the authentication process with the provided `webClientId`.

```dart
try {
  // Retrieve user's account information
  await HyphenAuthenticate.getAccount();

  // Authenticate user with webClientId
  await HyphenAuthenticate.authenticate("your_web_client_id");
} catch (e) {
  // Handle errors
  print("Error: $e");
}
```

### Authenticate with Google Account

{% hint style="info" %}
Currently, Hyphen SDK only supports authentication via **Google Account.**
{% endhint %}

You must use the Web Client ID on the Google Cloud Platform for Google Authentication. See also [Get your Google API client ID](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid).

```dart
HyphenAuthenticate.authenticate(
    webClientId = "<YOUR-WEB-CLIENT-ID>" // ex. 201778913659-dn4bo82q6hce3kfp7vstp04b22nh5hbi.apps.googleusercontent.com
)
```

Once authenticated successfully, you can get your HyphenAccount and retrieve information such as the account address.

```dart
final HyphenAccount hyphenAccount = await HyphenAuthenticate.getAccount();
if (hyphenAccount != null) {
  // Access the first address
  final String firstAddress = hyphenAccount.addresses.first.address;
  print("Address: $firstAddress"); // Print the first address
} 
```


# Using Hyphen UI Kit

For seamless integration of Hyphen into your app, leverage the Hyphen UI Kit, a dedicated SDK module. This library provides a comprehensive set of pre-designed UI components, facilitating effortless integration into your application.

#### Key Features:

* Effortless Integration: Integrate Hyphen UI Kit with just a few lines of code, streamlining the incorporation process into your application.
* Customization Options: The UI components offer extensive customization options, allowing you to tailor colors, fonts, and themes to align seamlessly with your app's branding.

#### Available Components:

1. **Sign-In Screen**: Simplify user onboarding with a streamlined sign-in experience, requiring minimal code integration.
2. **Account Management Screen**: Empower users with comprehensive device and key management capabilities, reminiscent of session management UIs in Web2 applications.
3. **2-Factor Authorization UI**: Seamlessly handle 2-factor authentication processes, including push notifications and authorization prompts, enhancing security and user experience.


# Sending Transactions

To send a transaction with Hyphen Android SDK

## Sending Transactions

How to send a transaction with the Hyphen Flutter SDK

### Creating, Signing, and Sending Transactions

To create, sign, and send transactions with the Hyphen Flutter SDK, follow these steps:

```dart
try {
  // Define Cadence script
  final String cadenceScript = "...";

  // Define arguments
  final List<Map<String, dynamic>> arguments = [...];

  // Sign and send transaction
  final String txId = await HyphenFlow.signAndSendTransaction(
    cadenceScript,
    arguments,
    true,
  );

  // Handle transaction result
  print("Transaction result: $txId");
} catch (e) {
  print("Error: $e");
}
```

* `cadenceScript`: The Cadence script to be executed in the transaction.
* `arguments`: List of arguments for the transaction.
* `withAuthorizer`: A boolean indicating whether the transaction requires an authorizer.
* `txId`: The transaction ID returned by the `signAndSendTransaction` method.


# Example App

Explore the sample project using our Flutter SDK:&#x20;

<https://github.com/hyphen-at/hyphen-flutter-sdk/tree/setup/example>


# Into the Hybrid Custody

```
#allowAccountLinking

import FungibleToken from "../contracts/utility/FungibleToken.cdc"
import FlowToken from "../contracts/utility/FlowToken.cdc"
import MetadataViews from "../contracts/utility/MetadataViews.cdc"

import HybridCustody from "../contracts/utility/HybridCustody.cdc"
import CapabilityFactory from "../contracts/utility/CapabilityFactory.cdc"
import CapabilityFilter from "../contracts/utility/CapabilityFilter.cdc"

/// Links child account to the parent account.
/// Parent account gets capabilities of managing Swirl tokens and NFTs.
transaction(
    pubKey: String,
    factoryAddress: Address,
    filterAddress: Address
) {

    prepare(child: AuthAccount, parent: AuthAccount) {
        /* --- Link the AuthAccount Capability --- */
        //
        var acctCap = child.linkAccount(HybridCustody.LinkedAccountPrivatePath)
            ?? panic("problem linking account Capability for new account")

        // Create a OwnedAccount & link Capabilities
        let ownedAccount <- HybridCustody.createOwnedAccount(acct: acctCap)
        child.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath)
        child
            .link<&HybridCustody.OwnedAccount{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>(
                HybridCustody.OwnedAccountPrivatePath,
                target: HybridCustody.OwnedAccountStoragePath
            )
        child
            .link<&HybridCustody.OwnedAccount{HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>(
                HybridCustody.OwnedAccountPublicPath,
                target: HybridCustody.OwnedAccountStoragePath
            )

        // Get a reference to the OwnedAccount resource
        let owned = child.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath)!

        // Get the CapabilityFactory.Manager Capability
        let factory = getAccount(factoryAddress)
            .getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(
                CapabilityFactory.PublicPath
            )
        assert(factory.check(), message: "factory address is not configured properly")

        // Get the CapabilityFilter.Filter Capability
        let filter = getAccount(filterAddress).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath)
        assert(filter.check(), message: "capability filter is not configured properly")

        // Configure access for the delegatee parent account
        owned.publishToParent(parentAddress: parent.address, factory: factory, filter: filter)

        /* --- Add delegation to parent account --- */
        //
        // Configure HybridCustody.Manager if needed
        if parent.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil {
            let m <- HybridCustody.createManager(filter: filter)
            parent.save(<- m, to: HybridCustody.ManagerStoragePath)
        }

        // Link Capabilities
        parent.unlink(HybridCustody.ManagerPublicPath)
        parent.unlink(HybridCustody.ManagerPrivatePath)
        parent.link<&HybridCustody.Manager{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>(
            HybridCustody.ManagerPrivatePath,
            target: HybridCustody.ManagerStoragePath
        )
        parent.link<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(
            HybridCustody.ManagerPublicPath,
            target: HybridCustody.ManagerStoragePath
        )

        // Claim the ChildAccount Capability
        let inboxName = HybridCustody.getChildAccountIdentifier(parent.address)
        let cap = parent
            .inbox
            .claim<&HybridCustody.ChildAccount{HybridCustody.AccountPrivate, HybridCustody.AccountPublic, MetadataViews.Resolver}>(
                inboxName,
                provider: child.address
            ) ?? panic("child account cap not found")

        // Get a reference to the Manager and add the account
        let managerRef = parent.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath)
            ?? panic("manager no found")
        managerRef.addAccount(cap: cap)
    }
}
```


# Authenticating Users

Using REST API directly to sign in or up with Hyphen account.

{% hint style="info" %}
Currently, Hyphen only supports [**Firebase Auth**](https://firebase.google.com/docs/auth) for OAuth 2.0 Provider. To add your own provider, please get in touch with us!&#x20;
{% endhint %}

## Process

1. **Request Firebase Auth to the user.** The client will get a Firebase ID token.&#x20;
2. In the client, **check that&#x20;*****the device key*****&#x20;exists on the keystore.**
   1. If It does, go to [**A. Signing In with Challenge**](#a.-signing-in-with-challenge)
   2. Else, go to [**B. Signing In with 2FA.**](#b.-signing-in-with-2fa)

### A. Signing In with Challenge

1. **Call Auth Request Challenge API.** The client needs to send a request with the device's public key and the Firebase ID token.&#x20;
   1. If HTTP 200 -> The server will respond with random challenge data. Go to A.2.
   2. If HTTP 400 with `PleaseSignUp` -> Go to [**C. Creating an Account.**](#c.-creating-an-account)

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signin/challenge" method="post" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

2. **Sign the Challenge Data with the Device Key**. The client should request the device keystore to sign the challenge data received from the server. The data is hex-encoded but don't decode the hex before signing it— just sign the UTF-8 binary data directly.\
   \
   The signature should be ECDSA P-256 signature with SHA2-256 digest, encoded in [IEEE P.1363](https://datatracker.ietf.org/doc/draft-ietf-httpbis-message-signatures/) format (a.k.a. r||s concat format).
3. **Call Auth Finish SignIn Challenge API**. Send the signature along with the original challenge data. The signature should be encoded in a hex string.
   1. If HTTP 200 -> Go to [**D. Finishing Authorization**](#d.-finishing-authorization)
   2. If HTTP 400 with PleaseDeploy: The account contract needs to be set up on the chain you're using.

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signin/challenge/respond" method="post" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

### B. Signing In with 2FA

On the source device (i.e. who currently tries to sign in), It should request 2FA Sign-in.

1. **Generate a Device Key**. The device key needs to be stored on the device's HSM keystore, such as Secure Enclave or Android Keystore. The keypair should be ECDSA with P-256 (secp256p1) curve.
2. **Call Auth Request 2FA API**: Send a request with the device registration data, including the public key generated in the previous step and the push token of the device.
   * If HTTP 200 -> The server will respond with the 2FA request data. A push will be sent on the random device registered on the user's account.

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signin/2fa" method="post" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

On the other device which is selected for 2FA target, it should receive a Firebase push with the 2FA request data.

1. **Request user's approval**: Make sure that the client is displaying enough information for the user to know about the request.
   1. If approves -> Go to step 2.
   2. If denies -> **Call Deny 2FA request API** to cancel the request.
2. **Send the transaction in the request using the device key.**
3. **Sign the payload in the request using the device key.** Note that the payload is hex-encoded, so you need to decode it before signing it.
4. **Call Approve 2FA request API**: with the signature.
5. Close the screen.

Now go back to the source device. It will receive a foreground data push as the status changes.

1. **Receive the silent push: `2fa-status-update`.**
   1. If approved -> a transaction ID, credentials, and account info will be given. Go to step 2.
   2. If denied -> Show the user an error message that the other device rejected the request.
2. **Wait for the transaction ID to confirm**. The transaction registers your new key to the account.&#x20;
3. **Go to** [**D. Finishing Authorization.**](#d.-finishing-authorization)

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signin/2fa/finish" method="post" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

### C. Creating an Account&#x20;

1. **Generate a Device Key**. The device key needs to be stored on the device's HSM keystore, such as Secure Enclave or Android Keystore. The keypair should be ECDSA with P-256 (secp256p1) curve.
2. **Call Sign Up API**. Send a request with the device registration data, including the public key generated in the previous step and the push token of the device.
   * *If HTTP 200* -> Go to [**D. Finishing Authorization**](broken://pages/kmnbaEaCk5zv3iVEdcqM)
   * *If HTTP 400* with PleaseDeploy: The account contract needs to be set up on the chain you're using.

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signup" method="post" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

### D. Finishing Authorization

1. The server will respond with the credentials and account info.
2. **Store the credential data in the local storage**. The credential object consists of a JWT access token and a refresh token.

All done! The client can redirect the user to the home screen of your application.


# Handling 2FA Push

Use Hyphen API to handle 2FA request 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"
    }
  }
}
```


# API Reference

Using REST API Directly


# Account

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/account/v1/me" method="get" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/account/v1/parent" method="post" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}


# Auth

### Authentication

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/refresh" method="post" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

### Sign Up

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signup" method="post" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

### Sign In (with 2FA)

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signin/2fa" method="post" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signin/2fa/finish" method="post" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

### Sign In (with Challenge)

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signin/challenge" method="post" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/signin/challenge/respond" method="post" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

### Recover Account

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/auth/v1/recover" method="post" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}


# Device

### Device Management

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/device/v1/devices" method="get" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/device/v1/devices/{publicKey}" method="put" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

### 2FA (Two-Factor Authorization)

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/device/v1/2fa/{id}" method="get" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/device/v1/2fa/{id}" method="put" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/device/v1/2fa/{id}" method="delete" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/device/v1/2fa/{id}/approve" method="post" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}


# Key

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/key/v1/keys" method="get" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/key/v1/recovery" method="post" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}


# Sign

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/sign/v1/cadence/transaction" method="post" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}

{% openapi src="/files/fga64SW2USHfQ96R2eO1" path="/sign/v1/cadence/paymaster" method="post" expanded="true" %}
[swagger.json](https://2622738714-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoXzDQclFMZX61nsrgiVE%2Fuploads%2FeAwTMXbA20yh1IgDdtbC%2Fswagger.json?alt=media\&token=a0c57374-3eaa-4701-a283-21b37d0bec21)
{% endopenapi %}


