# Getting Started

Get started quickly using Cotter's SDK to integrate passwordless login and email/phone number verification into your websites and apps

## Get Started

Cotter allows you to build a login system in just a few minutes. **There are 3 main methods of logging-in.** You can either choose 1 or combine the methods.&#x20;

Quickly get started with **our 5-minutes tutorials**:

💬 **Sign in with email/phone number:** Authenticate using a Magic Link or Verification Code. [Learn More  →](/features/verify-email-phone)

* [HTML Quickstart](/quickstart-guides/html-magic-link-with-email-and-phone)
* [React Quickstart](/quickstart-guides/react-magic-link-with-email-and-phone)
* [Next.js Tutorial](https://blog.cotter.app/passwordless-login-with-email-and-json-web-token-jwt-authentication-with-nextjs/)
* [Angular Tutorial](https://blog.cotter.app/angular-send-email-sms-and-whatsapp-verification-code-for-new-users-worldwide/)
* [Webflow Tutorial](https://blog.cotter.app/integrate-cotter-magic-link-to-webflow-in-less-than-15-minutes/)

🔐 **Sign In with Device on Mobile Apps:** Authenticate users based on the ownership of a device. [Learn More  →](/features/passwordless-login)

* [React Native Quickstart](/quickstart-guides/react-native-quickstart)
* [Flutter Quickstart](/quickstart-guides/flutter-sign-in-with-device)
* [iOS Quickstart](/quickstart-guides/ios-quickstart)

🧬 **Sign in with WebAuthn:** Authenticate users using TouchID/Windows Hello from your website. [Learn More  →](/features/sign-in-with-webauthn)

* [React Quickstart](/quickstart-guides/react-webauthn)

## Customization

Customize Cotter's Login Form from the [Dashboard > Branding](https://dev.cotter.app/). Currently this works for the Web SDKs for Sign in with Email/Phone using a Magic Link and OTP.

## Dig Deeper

Follow the full SDK reference for a fully customizable login flow and get OAuth 2.0 support for your API endpoints.

* [Web](/sdk-reference/web)
* [React Native](/sdk-reference/react-native)
* [Flutter](/sdk-reference/flutter)
* [iOS](/sdk-reference/ios)
* [Android](/sdk-reference/android)
* [Other Mobile App Frameworks](/sdk-reference/api-for-other-mobile-apps)
* [Backend: Handling Response](/sdk-reference/backend-handling-response) that you receive when a user authenticates

## OAuth 2.0: Get Access Token from Cotter

Cotter can generate OAuth Tokens (access token, id token, and refresh token) to be used for your APIs. This functionality is available from Cotter's SDK.

{% content-ref url="/pages/-M4HUyscnpVWv0mVDRba" %}
[🗝️ Getting Access Token](/getting-access-token/handling-authentication-with-cotter)
{% endcontent-ref %}

## Example Apps

[HTML Example](https://codesandbox.io/s/client-side-working-example-hiy8w?file=/index.html)

[ReactNative Example App](https://github.com/cotterapp/react-native-sdk/tree/master/examples)

[Flutter Example App](https://github.com/cotterapp/flutter-sdk/tree/master/example)

[Android Example App](https://github.com/cotterapp/android-example)

[Biometric and PIN iOS Example](https://github.com/cotterapp/ios-sdk)


# Features & Concepts

There are several steps and features that makes Cotter a powerful authentication service for you. To use the most out of Cotter, you can combine the 3 features that we offer: **Sign in with Device**, **Sign in with Email/Phone Number**, and **WebAuthn (Biometrics for websites).**

## 1. Sign in with Device with an In-App Prompt

Trusted devices works like DUO or Apple's Trusted Devices. Basically, we turn your mobile device into a security key such that your account is only accessible from the devices that you trust.

We create a cryptographic key pair and store it in your device's secure storage. We'll then use cryptographic functions to check the requests produced by the key in your device to authenticate account access. Learn more at [FIDO](https://fidoalliance.org/how-fido-works/).

Trusted devices are available for mobile apps, and websites with an accompanying mobile app. If your website doesn't have a mobile app and you want to utilize this functionality, your users can download the Cotter app instead (coming soon).

{% content-ref url="/pages/-M14UhiTa9EBe1jIQi4Y" %}
[Sign In with Device](/features/passwordless-login)
{% endcontent-ref %}

## 2. **Sign in with Email or Phone Number with Magic Link or OTP**

Cotter works as a universal single-sign-on for all websites and apps that integrates Cotter. Users who have verified their email or phone number once in any of the participating apps will not need to re-verify email & phone number.&#x20;

> Your users don't need to be redirected to Cotter's website, or even know that Cotter exists. You can embed Cotter inside your website and app.

{% content-ref url="/pages/-M0QHRAfzQweYx\_DczkH" %}
[Sign In with Email/Phone Number](/features/verify-email-phone)
{% endcontent-ref %}

## 3. WebAuthn

WebAuthn allows websites to authenticate users using TouchID or Windows Hello from the browser. This feature makes it faster for your users to login. Users can set up their current device to allow login with WebAuthn, and the user will be prompted to use TouchID or Windows Hello on subsequent logins.

{% content-ref url="/pages/-MDcc97fCNNgjEv4ihN4" %}
[Sign In with WebAuthn](/features/sign-in-with-webauthn)
{% endcontent-ref %}


# Sign In with Email/Phone Number

Sign in faster using a magic link or OTP sent to users via email, SMS, or WhatsApp.

## How it works

Users can authenticate using just their email or phone number. On their first time using Cotter, they will be prompted to enter a verification code or press a magic link

{% hint style="success" %}
On subsequent authentication requests through any website, they will be able to **instantly get verified without needing a verification code.**
{% endhint %}

## First Ever Sign Up

On their first ever signup to a website/app that uses Cotter, the user have to enter a verification code.

![First ever signup to a website/app that uses Cotter](/files/-M9M-J5jm5GYC1L6z5P6)

#### 1. The user enter their email or phone number

#### 2. They will receive a verification code or magic link to their email or phone number.

The user will then enter the verification code or press the magic link

#### 3. We will return a response in the callback function with the user's email or phone number and a JWT token to verify that the user is authenticated.

{% hint style="success" %}
**✨At this point, we tie the user's browser or device with their email / phone number.** When the user then sign up to another website that uses Cotter, the user **don't need to re-verify their email or phone number.** Instead, our website or SDK will check if we recognize the device with the credentials, and will automatically go to step 3 above.
{% endhint %}

## Subsequent Logins

After the first signup, anytime the user need to sign-up or login to **any website or app that uses Cotter**, the user just needs to enter the email or phone number they want to use, and they'll be **instantly verified**.

![On subsequent logins, the user don't need to enter verification code](/files/-M9M0Ho0FN1zEdY-5_0K)

#### 1. The user enter their email or phone number

#### 2. The user is already verified, and can continue their registration or login :tada:&#x20;

{% hint style="info" %}
Currently our sessions lasts for 30 days. After 30 days, the user have to re-enter a verification code.

In the near future, we will allow you to check **the last login timestamp** and **decide** whether you'd like to **send another verification code or not**.
{% endhint %}

## Quickstarts

{% content-ref url="/pages/-M4bSYVl2B09-3932YAm" %}
[Broken mention](broken://pages/-M4bSYVl2B09-3932YAm)
{% endcontent-ref %}

## Guides

#### Web

{% content-ref url="/pages/-M0ROJn7tosL9qeumqFd" %}
[Sign In with Email/Phone Number](/sdk-reference/web/web-sdk-verify-email-phone)
{% endcontent-ref %}

#### React Native

{% content-ref url="/pages/-M1dk\_Rosjlkj52Hq\_6R" %}
[Sign In with Email/Phone Number](/sdk-reference/react-native/react-native-sdk-verify-email-phone)
{% endcontent-ref %}

#### iOS

{% content-ref url="/pages/-M35aUobglanfiWqjf7S" %}
[Sign In with Email/Phone Number](/sdk-reference/ios/ios-sdk-verify-email-phone)
{% endcontent-ref %}

#### Android

{% content-ref url="/pages/-M0et2D8qa-mVoDh-tjp" %}
[Sign In with Email/Phone Number](/sdk-reference/android/android-sdk-1)
{% endcontent-ref %}

#### Other Mobile Apps

{% content-ref url="/pages/-M0QVaCMLRWd0jUsIcYn" %}
[Verify Email/Phone Number](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps)
{% endcontent-ref %}


# Sign In with Device

Sign in faster with a prompt sent to your mobile app.

## What is a Trusted Device?

![Cotter's Trusted Device SDK](/files/-M14Xwy2Vq-ECRII3PXW)

A Trusted Device is a mobile device that you trust, where you can access your account without needing a password. When you try to access your account from a Non-Trusted Device, it will require an approval from the app installed in the Trusted Device.

Usually, your first Trusted Device will be the mobile phone you used to register to an app. You can then add other devices to be a Trusted Device for your account.

## How it's Secure

![How It Works](/files/-M85aPVXclped6ySpTsB)

Cotter's SDK follows the [FIDO Protocol](https://fidoalliance.org/how-fido-works/) to implement Trusted Devices. The way it works is that Cotter's SDK will generate cryptographic key pair that replaces your password. This key will be stored securely in your device. Because of this, your device is called a Trusted Device, because your device is the only one who knows this keys.

Because of the way asymmetric cryptography works, your secret key is never sent over the internet to any server, not even Cotter's server. We only need to verify that requests are actually made with the key using cryptographic functions, to ensure that it is actually coming from your Trusted Device.&#x20;

## Quickstarts

{% content-ref url="/pages/-M6Eb1vLZRs1\_0ZAMKrR" %}
[React Native – Sign in with Device](/quickstart-guides/react-native-quickstart)
{% endcontent-ref %}

{% content-ref url="/pages/-M6EaO2M9ot00du8W\_bR" %}
[iOS – Sign in with Device](/quickstart-guides/ios-quickstart)
{% endcontent-ref %}

{% content-ref url="/pages/-M9aaGUmTZYju\_sjd17x" %}
[Flutter – Sign in with Device](/quickstart-guides/flutter-sign-in-with-device)
{% endcontent-ref %}

## Guides

#### React Native

{% content-ref url="/pages/-M2kTuM6W90CT9IpIxHq" %}
[Sign In with Device](/sdk-reference/react-native/react-native-sdk-passwordless-login)
{% endcontent-ref %}

#### Flutter

{% content-ref url="/pages/-M9angupK73fWUZjM7fe" %}
[Sign In with Device](/sdk-reference/flutter/sign-in-with-device)
{% endcontent-ref %}

#### iOS

{% content-ref url="/pages/-M4q-LjGBIcIq3PgnYt6" %}
[Sign In with Device](/sdk-reference/ios/ios-sdk-passwordless-login)
{% endcontent-ref %}

#### Android

{% content-ref url="/pages/-M14g3WtD4o3ZG9N2j9G" %}
[Sign In with Device](/sdk-reference/android/older-sdk-version/android-sdk)
{% endcontent-ref %}


# How it works

There are several different cases for user registration and login, and we're going to cover them all:

### Registration

![Registration with Cotter's Trusted Device](/files/-MEjHpcQLCKgGtxbn6XJ)

When a user registers to your app, you can immediately call Cotter's SDK to register the mobile device as a Trusted Device. This way, the user's account can now be accessed from the trusted device without needing a password.

### Login from a Trusted Device

![Logging-in from a Trusted Device](/files/-MEjHwvrecyViDUP48sd)

Logging in from a trusted device is instant. Your users can immediately access their account without doing anything. Your app can optionally require Biometric/PIN to make it more secure.

### Login from a Non-Trusted Device

![Login from a Non-Trusted Device](/files/-MEjI28l_lHOpx2OlPl6)

If your user try to login from a different phone, or from a web browser, then they will receive a prompt in their Trusted Device, asking them to approve the login request. Once approved, the user can access their account from Non-Trusted Device. **But this doesn't mean that the new device is now a trusted device.** Subsequent logins from a Non-Trusted Device will **always** require approval, even when approved.

### Registering a New Device as a Trusted Device

![Adding a New Trusted Device](/files/-MEjInS_0JWSfygUjI2v)

The user can register another device as an additional Trusted Device. The Trusted Device will need to scan a QR Code shown in the other device to explicitly register that other device as a Trusted Device.

Once the new device is registered as a Trusted Device, the user can now access their account from both devices with ease.

## Quickstarts

{% content-ref url="/pages/-M6Eb1vLZRs1\_0ZAMKrR" %}
[React Native – Sign in with Device](/quickstart-guides/react-native-quickstart)
{% endcontent-ref %}

{% content-ref url="/pages/-M9aaGUmTZYju\_sjd17x" %}
[Flutter – Sign in with Device](/quickstart-guides/flutter-sign-in-with-device)
{% endcontent-ref %}

{% content-ref url="/pages/-M6EaO2M9ot00du8W\_bR" %}
[iOS – Sign in with Device](/quickstart-guides/ios-quickstart)
{% endcontent-ref %}

## SDK Reference

#### React Native

{% content-ref url="/pages/-M2kTuM6W90CT9IpIxHq" %}
[Sign In with Device](/sdk-reference/react-native/react-native-sdk-passwordless-login)
{% endcontent-ref %}

#### Flutter

{% content-ref url="/pages/-M9angupK73fWUZjM7fe" %}
[Sign In with Device](/sdk-reference/flutter/sign-in-with-device)
{% endcontent-ref %}

**iOS**

{% content-ref url="/pages/-M4q-LjGBIcIq3PgnYt6" %}
[Sign In with Device](/sdk-reference/ios/ios-sdk-passwordless-login)
{% endcontent-ref %}

#### Android

{% content-ref url="/pages/-MCxyo3ix-jqWDQysF0g" %}
[Sign In with Device](/sdk-reference/android/sign-in-with-device)
{% endcontent-ref %}


# Sign In with WebAuthn

WebAuthn allows users to authenticate with their device's authenticator, like TouchID or Windows Hello, to websites via their browsers.

![](/files/-MCkItqjAs5Z18uMfGn-)

Because WebAuthn is not supported on all browsers yet, **the WebAuthn feature always have a fallback method to Magic Link or OTP verification.**

### Signing Up

![](/files/-MEjNFATmbGxrmg0Ndmb)

When the user first sign up for your website, they don't have a WebAuthn credential set up. The user will first enter their **email** or **phone number:**

* **Step 1:** Verify the user's email or phone number using OTP or Magic Link
* **Step 2:** The SDK will automatically prompt the user to set up WebAuthn for this device.
* **Step 3:** If the user agrees and touch the TouchID sensor, the user can subsequently login using TouchID from the current laptop.

### **Logging In**

![](/files/-MEjO9pP2Gmnu_xbj73G)

The user may or may not have any WebAuthn credentials set up:

* **Step 1:** The user enters their email or phone number
* **Step 2:** The SDK checks if the user have any WebAuthn credential set up
  * **Yes, the user have a WebAuthn Credential**: The SDK will automatically ask the user to login using WebAuthn. The user have the option to fallback to use Magic Link or OTP
  * **No, the user never setup WebAuthn before:** The SDK will continue authenticating the user using Magic Link or OTP, then ask the user to setup WebAuthn on the current laptop.


# All Guides & Tutorials

Quickly get started with **our 5-minutes tutorials**:

💬 **Sign in with email/phone number:** Authenticate using a Magic Link or Verification Code. [Learn More  →](/features/verify-email-phone)

* [HTML Quickstart](/quickstart-guides/html-magic-link-with-email-and-phone)
* [React Quickstart](/quickstart-guides/react-magic-link-with-email-and-phone)
* [Next.js Tutorial](https://blog.cotter.app/passwordless-login-with-email-and-json-web-token-jwt-authentication-with-nextjs/)
* [Angular Tutorial](https://blog.cotter.app/angular-send-email-sms-and-whatsapp-verification-code-for-new-users-worldwide/)
* [Webflow Tutorial](https://blog.cotter.app/integrate-cotter-magic-link-to-webflow-in-less-than-15-minutes/)

🔐 **Sign In with Device on Mobile Apps:** Authenticate users based on the ownership of a device. [Learn More  →](/features/passwordless-login)

* [React Native Quickstart](/quickstart-guides/react-native-quickstart)
* [Flutter Quickstart](/quickstart-guides/flutter-sign-in-with-device)
* [iOS Quickstart](/quickstart-guides/ios-quickstart)

🧬 **Sign in with WebAuthn:** Authenticate users using TouchID/Windows Hello from your website. [Learn More  →](/features/sign-in-with-webauthn)

* [React Quickstart](/quickstart-guides/react-webauthn)


# HTML – Sign in with Email/Phone

Easily add email and phone number verification using Cotter's SDK. Send a magic link to your users via email, SMS or WhatsApp in just a few lines of code.

Cotter's Magic Link authenticates your user using a link. It allows seamless, fast, and secure way for your user to login to your Web App. Follow this simple guide to see how it works!

{% embed url="<https://www.youtube.com/watch?v=WpfVzqiz0vw>" %}

### Step 0: Get the template code

To follow this guide, let's use our [Hello World Template](https://codesandbox.io/s/client-side-template-prf4d) in CodeSandbox!

### Step 1: Import Cotter as a dependency

Copy the following script to the head section of your HTML page

```markup
<!-- 1️⃣ Get Cotter SDK -->

<script
    src="https://unpkg.com/cotter@0.3.32/dist/cotter.min.js"
    type="text/javascript"
></script>
```

### Step 2: Setup a `div` with id `cotter-form-container` element to contain cotter's form

```markup
<!-- 2️⃣ Setup a div to contain the form -->
<div id="cotter-form-container" style="width: 300px; height: 300px;"></div>
```

Please note that id has to be `cotter-form-container` for the form to show up.

### Step 3: Initialize Cotter

> You need your API\_KEY\_ID for this step. Create a developer account and create a Project to get your API keys. The API Keys will only be shown once!

Grab your `API_KEY_ID` from the dashboard, then replace `<YOUR_API_KEY_ID>` with your `API_KEY_ID`.

```markup
<!-- 3️⃣ Show the form -->
<script>
  var cotter = new Cotter("<YOUR_API_KEY_ID>"); // 👈 Specify your API KEY ID here

  cotter
    .withFormID("form_default") // Use customization for form "form_default"
    .signInWithLink() // use .signInWithOTP() to send an OTP
    .showEmailForm()  // use .showPhoneForm() to send magic link to a phone number 
    .then(payload => {
    // Read what Cotter Returns
    var respDiv = document.getElementById("user-response");
    respDiv.innerHTML = `<pre>${JSON.stringify(payload, null, 4)}</pre>`;
  });
</script>
```

> 🎉 You're done! Now that you know how it works on the client side, let's check out how to process Cotter's payload in your backend so you can start registering and logging in users in your backend server.

Here's a [fully working example](https://codesandbox.io/s/magic-link-working-example-c5j7f) of the guide we went over.

{% hint style="info" %}
To send code/link via SMS or WhatsApp, you'll need to add some balance to you project in the [Dashboard](https://dev.cotter.app/).
{% endhint %}

## 👉 What's Next?

### Customize the Form

You can customize the form from the [Dashboard](https://dev.cotter.app/) > Branding. You can also [add Custom CSS](/sdk-reference/web/web-sdk-verify-email-phone/styling#adding-your-own-css).

### Add Additional Fields

[**Add Additional Fields**](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#additional-fields) like Name, Address, etc to the login form.&#x20;

### **Register User to your Backend**

When the user successfully authenticated, send Cotter's response to your backend to either create a new user or log the user in. Learn how to send Cotter's response and verify it in your backend:

{% content-ref url="/pages/-MDcFwnPyQzT3MiP1tHe" %}
[Backend: Handling Response](/sdk-reference/backend-handling-response)
{% endcontent-ref %}


# React – Sign in with Email/Phone

Easily add email and phone number verification using Cotter's SDK. Send a magic link to your users via email, SMS or WhatsApp in just a few lines of code.

Cotter's Magic Link authenticates your user using a link. It allows seamless, fast, and secure way for your user to login to your Web App. Follow this simple guide to see how it works!

![Add Magic Link Authentication in just a few minutes](/files/-MBrfjtocuzcysGXyr38)

### Step 1: Import Cotter as a dependency

```
yarn add cotter
```

### Step 2: Initialize Cotter and Show the Form

```javascript
import React, { useEffect, useState } from "react";
import Cotter from "cotter"; //  1️⃣  Import Cotter

function App() {
  const [payload, setpayload] = useState(null);

  //  2️⃣ Initialize and show the form
  useEffect(() => {
    var cotter = new Cotter(API_KEY_ID); // 👈 Specify your API KEY ID here
    cotter
      .withFormID("form_default") // Use customization for form "form_default"
      .signInWithLink() // use .signInWithOTP() to send an OTP
      .showEmailForm()  // use .showPhoneForm() to send magic link to a phone number 
      .then(response => {
        setpayload(response); // show the response in our state
      })
      .catch(err => console.log(err));
  }, []);

  return (
    <div>
      {/*  3️⃣  Put a <div> that will contain the form */}
      <div id="cotter-form-container" style={{ width: 300, height: 300 }} />
      
      <pre>{JSON.stringify(payload, null, 4)}</pre>
    </div>
  );
}

export default App;
```

[Create a free account and go to the dashboard](https://dev.cotter.app/) to get your `API_KEY_ID` .

1. Import Cotter
2. Call `signInWithLink` to use Magic Link or `signInWithOTP` to use OTP, followed by `showEmailForm` or `showPhoneForm`, and get the response as a promise.
3. Setup a `<div>` with `id="cotter-form-container"` that will contain the form.

{% hint style="info" %}
To send code/link via SMS or WhatsApp, you'll need to add some balance to you project in the [Dashboard](https://dev.cotter.app/).
{% endhint %}

### 🎉 **You're done!**

Here's a [working example of the React app that we just made](https://codesandbox.io/s/react-magic-link-complete-90vig?file=/src/App.js).

## ✏️ **Ready to setup & customize your login?** <a href="#ready-to-setup-and-customize-cotters-js-sdk" id="ready-to-setup-and-customize-cotters-js-sdk"></a>

### **Customize the Form**

Easily customize the form from [Cotter's Dashboard](https://dev.cotter.app/) > Branding.

### **Full JS SDK Reference**

{% content-ref url="/pages/-M0ROJn7tosL9qeumqFd" %}
[Sign In with Email/Phone Number](/sdk-reference/web/web-sdk-verify-email-phone)
{% endcontent-ref %}

## 👉 What's Next?

### Customize the Form

You can customize the form from the [Dashboard](https://dev.cotter.app/) > Branding. You can also [add Custom CSS](/sdk-reference/web/web-sdk-verify-email-phone/styling#adding-your-own-css).

### Add Additional Fields

[**Add Additional Fields**](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#additional-fields) like Name, Address, etc to the login form.&#x20;

### **Register User to your Backend**

When the user successfully authenticated, send Cotter's response to your backend to either create a new user or log the user in. Learn how to send Cotter's response and verify it in your backend:

{% content-ref url="/pages/-MDcFwnPyQzT3MiP1tHe" %}
[Backend: Handling Response](/sdk-reference/backend-handling-response)
{% endcontent-ref %}


# React – WebAuthn

Sign in users faster using WebAuthn: sign in with TouchID or Windows Hello from a web browser.

Cotter's WebAuthn authenticates your user using either TouchID or Windows Hello **from their browser**. This means it works **from your website.** It allows seamless, fast, and secure way for your user to login to your Web App. Follow this simple guide to see how it works!

![WebAuthn with Cotter's JS SDK](/files/-MCkItqjAs5Z18uMfGn-)

{% hint style="success" %}
[**Try it live**](https://cotter.herokuapp.com/) **on our Example Next.js Project.** Check out the complete code on [Github](https://github.com/cotter-code/guide-next-webauthn).
{% endhint %}

### Step 1: Import Cotter as a dependency

```
yarn add cotter
```

*(This feature is available on version >= 0.3.4)*

### Step 2: Initialize Cotter and Show the Form

```javascript
import React, { useEffect, useState } from "react";
import Cotter from "cotter"; //  1️⃣  Import Cotter

function App() {
  const [payload, setpayload] = useState(null);

  //  2️⃣ Initialize and show the form
  useEffect(() => {
    var cotter = new Cotter(API_KEY_ID); // 👈 Specify your API KEY ID here
    cotter
      .withFormID("form_default") // Use customization for form "form_default"
      .signInWithWebAuthnOrLink()
      .showEmailForm()
      .then(response => {
        setpayload(response); // show the response in our state
      })
      .catch(err => console.log(err));
  }, []);

  return (
    <div>
      {/*  3️⃣  Put a <div> that will contain the form */}
      <div id="cotter-form-container" style={{ width: 300, height: 300 }} />
      
      <pre>{JSON.stringify(payload, null, 4)}</pre>
    </div>
  );
}

export default App;
```

​[Create a free account and go to the dashboard](https://dev.cotter.app/) to get your `API_KEY_ID` .

1. Import Cotter
2. Call `signInWithWebAuthnOrLink` to use WebAuthn with fallback Magic Link, followed by `showEmailForm` or `showPhoneForm`, and get the response as a promise.
3. Setup a `<div>` with `id="cotter-form-container"` that will contain the form.

### 🎉 **You're done!** <a href="#youre-done" id="youre-done"></a>

## ✏️ **Ready to setup & customize your login?** <a href="#ready-to-setup-and-customize-cotters-js-sdk" id="ready-to-setup-and-customize-cotters-js-sdk"></a>

### **Customize the Form** <a href="#customize-the-form" id="customize-the-form"></a>

Easily customize the form from [Cotter's Dashboard](https://dev.cotter.app/) > Branding.

### **Full JS SDK Reference** <a href="#full-js-sdk-reference" id="full-js-sdk-reference"></a>

{% content-ref url="/pages/-MCk3X5Dh4Fkxp20D4Rt" %}
[Sign In with WebAuthn](/sdk-reference/web/sign-in-with-webauthn)
{% endcontent-ref %}

## 👉 What's Next?

### Customize the Form

You can customize the form from the [Dashboard](https://dev.cotter.app/) > Branding. You can also [add Custom CSS](/sdk-reference/web/web-sdk-verify-email-phone/styling#adding-your-own-css).

### Add Additional Fields

[**Add Additional Fields**](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#additional-fields) like Name, Address, etc to the login form.&#x20;

### **Register User to your Backend**

When the user successfully authenticated, send Cotter's response to your backend to either create a new user or log the user in. Learn how to send Cotter's response and verify it in your backend:

{% content-ref url="/pages/-MDcFwnPyQzT3MiP1tHe" %}
[Backend: Handling Response](/sdk-reference/backend-handling-response)
{% endcontent-ref %}


# React Native – Sign in with Device

Cotter's Passwordless SDK authenticates your user based on their device. It allows a seamless, fast, and secure way for your user to login to your React Native App.

## Sign in with Device

In this guide we'll allow users to **Sign In with Device** using Cotter's SDK.&#x20;

{% hint style="info" %}
Make sure you're using `react-native` version **< 0.63**
{% endhint %}

### Step 1️: Get Cotter SDK <a href="#step-1-get-cotter-sdk" id="step-1-get-cotter-sdk"></a>

**Head to** [**https://dev.cotter.app/rules**](https://dev.cotter.app/rules) **and allow Trusted Devices in the dashboard.**

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

```
yarn add react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}

{% tab title="npm" %}

```
npm install  --save react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}
{% endtabs %}

**Make sure you use version >= 0.2.0.** Checkout additional steps [for Android](https://docs.cotter.app/trusted-devices/react-native-sdk#for-android), [React Native < 0.60](https://docs.cotter.app/trusted-devices/react-native-sdk#using-react-native-less-than-0-60), and [Manual Installation](https://docs.cotter.app/trusted-devices/react-native-sdk#manual-installation).

### Step 2️: Wrap your root component with `connectCotterWrapper`

```javascript
import {connectCotterWrapper} from 'react-native-cotter';

class MyApp extends Component {...}
export default connectCotterWrapper(MyApp);
```

### Step 3️: Register user and trust this device

This method `signUpWithDevice` will register the user based on the user's `identifier` to Cotter and then trust the current device.

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

```javascript
import { Cotter } from "react-native-cotter";

const register = (identifier) => {
  // Signup the user and trust this device
  var cotter = new Cotter(API_KEY_ID);
  cotter.signUpWithDevice(
    identifier, // User's email, phone or username
    (response) => {console.log(response)}, // OnSuccess function
    (error) => {console.log(error)}, // OnError function
  );
};
```

{% endtab %}

{% tab title="Response" %}

```javascript
// Returns the newly created User object in Cotter
// along with oauth tokens

{
  "ID": "cccccccc-cccc-cccc-cccc-cccccccccccc",
  "client_user_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
  "created_at": "2020-06-09T03:50:04.005779Z",
  "default_method": "TRUSTED_DEVICE",
  "deleted_at": null,
  "enrolled": [
    "TRUSTED_DEVICE"
  ],
  "identifiers": [
    "hello@cotter.app"
  ],
  "issuer": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  "oauth_token": {
    "access_token": "eyJhbGciOi...",
    "auth_method": "TRUSTED_DEVICE",
    "expires_in": 3600,
    "id_token": "eyJhbGciOiJFU...",
    "refresh_token": "6177:MufdKMIk2XP...",
    "token_type": "Bearer"
  },
  "update_at": "2020-06-09T03:50:04.77575278Z"
}
```

{% endtab %}
{% endtabs %}

### Step 4️: Logging In from a Trusted Device

Use the `signInWithDevice` method to login with the same `identifier` as the one registered above.

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

```javascript
import { Cotter } from "react-native-cotter";

const login = (identifier) => { 
    var cotter = new Cotter(API_KEY_ID);
    cotter.signInWithDevice(
        identifier, // User's email, phone or username
        (response) => {console.log(response)}, // OnSuccess function
        (error) => {console.log(error)}, // OnError function
    );
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "ID": 1361, // Event ID
  "CreatedAt": "2020-02-27T22:22:48.705212512Z",
  "UpdatedAt": "2020-02-27T22:22:48.705212512Z",
  "DeletedAt": null,
  "client_user_id": "1014", // your client's User ID
  "issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API Key
  "event": "<EVENT NAME>",// requested event (LOGIN, or TRANSACTION, etc)
  "ip": "192.168.232.2", 
  "location": "Unknown",
  "timestamp": "1582842167",
  "method": "TRUSTED_DEVICE", // auth method: TRUSTED_DEVICE (other choices are PIN / BIOMETRIC)
  "new": false, // Is this a new pending event. More explanation below about Non-Trusted Device
  "approved": true, // Is this event approved.
  "signature": "oonMGCAxp3..." // Signature to make sure this event comes from Cotter's server
}
```

{% endtab %}
{% endtabs %}

## 🎉 You're Done

![](/files/-M7506mlkqL9gr-du1gI)

Now you can allow your users to **Sign In with Device** with just 1 tap. When your sessions expires, you can also **Sign In User Silently** by just calling the method `signInWithDevice` above.

#### Find a more detailed version of this guide in our Trusted Devices section

{% content-ref url="/pages/-M2kTuM6W90CT9IpIxHq" %}
[Sign In with Device](/sdk-reference/react-native/react-native-sdk-passwordless-login)
{% endcontent-ref %}

## 👉 What's Next? <a href="#whats-next" id="whats-next"></a>

### Verify User's Email or Phone Number

{% content-ref url="/pages/-M1dk\_Rosjlkj52Hq\_6R" %}
[Sign In with Email/Phone Number](/sdk-reference/react-native/react-native-sdk-verify-email-phone)
{% endcontent-ref %}

### Logging-in from Another Device

{% content-ref url="/pages/-M2pWufvpIxKQcm3t4RP" %}
[Authenticate from a Non-Trusted Device](/sdk-reference/react-native/react-native-sdk-passwordless-login/authenticate-from-a-non-trusted-device)
{% endcontent-ref %}


# iOS – Sign in with Device

Cotter's Passwordless SDK authenticates your user using the FIDO protocol. It allows a seamless, fast, and secure way for your user to login to your iOS App. Authenticate your users in 4 simple steps:

## Sign in with Device

In this guide we'll allow users to **Sign In with Device** using Cotter's SDK.&#x20;

### Step 1: Import Cotter as a dependency

**Head to** [**https://dev.cotter.app/rules**](https://dev.cotter.app/rules) **and allow Trusted Devices in the dashboard.**&#x20;

We use Cocoapods as our SDK host. Add `pod 'Cotter'` to your podfile then run `pod install`.

### Step 2: Initialize Cotter

In your app's `AppDelegate.swift`, initialize Cotter with your API keys.

```swift
import Cotter // IMPORTANT: IMPORT COTTER AS DEPENDENCY
...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  Cotter.configureWithLaunchOptions(
    launchOptions: launchOptions,
    apiSecretKey: <your-secret-key>,
    apiKeyID: <your-api-id>
  )
}
```

### Step 3: Register your user

To register, simply call the following function anywhere in your app. Make sure you do `import Cotter` at the start of the swift file.

```swift
Passwordless.shared.register( 
  identifier: <user-email>
)
```

### Step 4: Logging in

When a user logs in, call the following function in your view controller

```swift
Passwordless.shared.parentVC = self
Passwordless.shared.login(
  identifier: <user-email>, 
  cb: { (token: CotterOAuthToken?, err:Error?) in
    if err != nil {
        // case 1: handle error as necessary
    }
    if token == nil {
        // case 2: user is unauthorized
    }
    // case 3: user is authorized
})
```

Then you can handle 3 cases mentioned in the comments in any way you like.

## 👉 Next steps

That's it! You have successfully build a seamless, fast, and secure login mechanism with Cotter.

#### Integrate Cotter's push notification services

{% content-ref url="/pages/-MCiZep6L97mjBj93JTl" %}
[Push Notification](/sdk-reference/ios/ios-sdk-passwordless-login/ios-push-notification)
{% endcontent-ref %}

#### Verify User's Email or Phone Number

{% content-ref url="/pages/-M35aUobglanfiWqjf7S" %}
[Sign In with Email/Phone Number](/sdk-reference/ios/ios-sdk-verify-email-phone)
{% endcontent-ref %}


# Flutter – Sign in with Device

Cotter's Passwordless SDK authenticates your user based on their device. It allows a seamless, fast, and secure way for your user to login to your React Native App.

## Sign in with Device

In this guide we'll allow users to **Sign In with Device** using Cotter's SDK.&#x20;

Add [Cotter's SDK](https://pub.dev/packages/cotter#-installing-tab-) to your `pubspec.yaml` , then run `flutter pub get`. You may need to restart your flutter for it to run pod install (stop flutter run and re run it).

```yaml
dependencies:
  cotter:
```

**For Android**: Update `minSdkVersion` to `18` [following the installation instructions](/sdk-reference/flutter).

Import Cotter in your `lib/main.dart`, then initialize it inside `HomePageState`.

```dart
import 'package:cotter/cotter.dart'; // Import Cotter

class HomePageState extends State {
  ...

  // 1⃣ Initialize Cotter
  Cotter cotter = new Cotter(apiKeyID: API_KEY_ID); // 👈 Specify your API KEY ID here

  // 2️⃣ Sign Up Function
  void signUp() async {
    try {
      // 🚀 One-line Sign Up
      var user = await cotter.signUpWithDevice(identifier: inputController.text);
    } catch (e) {
      print(e)
    }
  }
  
  // 3️⃣ Login Function
  void login(BuildContext context) async {
    try {
      // 🚀 One-line Login
      var event = await cotter.signInWithDevice(identifier: inputController.text, context: context);
    } catch (e) {
      print(e)
    }
  }
  
  @override
  Widget build(BuildContext context) { ... }
} 
```

You can [create a free account at Cotter](https://dev.cotter.app) to get your `API_KEY_ID`.

**Sign Up:** Call `cotter.signUpWithDevice` and input the user's email, phone, or username. This will create a new user in Cotter and trust the current device to allow logins. This function returns a [User object](/api-reference/user-api/user-object).

**Sign In:** Call `cotter.signInWithDevice` . If the user is logging-in [from a device that they trust](/features/passwordless-login), they'll automatically be approved. This function returns an [Event object](/api-reference/event-object).

{% hint style="info" %}
**Make sure you allow Trusted Devices Method in the** [**dashboard**](https://dev.cotter.app)**.**
{% endhint %}

## 🎉 That's It!

Simply call the `signUp` and `login` functions from your login page, and you can allow your users to **Sign In with Device** with just 1 ta&#x70;**.**

![Passwordless Login with Flutter using Cotter's SDK](/files/-M9agBdGGVrDaZwCHkiO)

## What's Next?

**1. Approve login requests from a non-trusted device**

{% content-ref url="/pages/-M9arREAyqZyslAP96BY" %}
[Authenticate from a Non-Trusted Device](/sdk-reference/flutter/sign-in-with-device/authenticate-from-a-non-trusted-device)
{% endcontent-ref %}

#### 2. Getting the Logged-in User

{% content-ref url="/pages/-M9awQf5uqyq2X9ZO4JM" %}
[Getting the Logged-in User](/sdk-reference/flutter/getting-the-logged-in-user)
{% endcontent-ref %}

#### 3. Getting Access Token, ID Token, and Refresh Token

{% content-ref url="/pages/-M9axG8HPu6f\_at6wCcT" %}
[Getting OAuth Tokens](/sdk-reference/flutter/getting-oauth-tokens)
{% endcontent-ref %}


# Web

The easiest way to add Passwordless Login and Email/Phone Number Verification to your Websites.

## Complete Guides

#### Sign in with Email/Phone Number

Authenticate users by sending a Magic Link or OTP via email, SMS, or WhatsApp

{% content-ref url="/pages/-M0ROJn7tosL9qeumqFd" %}
[Sign In with Email/Phone Number](/sdk-reference/web/web-sdk-verify-email-phone)
{% endcontent-ref %}

#### Sign in with WebAuthn

Authenticate users using TouchID or Windows Hello from your website.

{% content-ref url="/pages/-MCk3X5Dh4Fkxp20D4Rt" %}
[Sign In with WebAuthn](/sdk-reference/web/sign-in-with-webauthn)
{% endcontent-ref %}

#### Sign in with Device

If you have **mobile app**, you can ask the user to approve the login request from their phone.

{% content-ref url="/pages/-M3o-GTINgCweb4ATz-9" %}
[Sign In with Device](/sdk-reference/web/web-sdk-passwordless-login)
{% endcontent-ref %}

## Quickstart Guides

{% content-ref url="/pages/-MBpqtdlR54h41rVFGWy" %}
[React – Sign in with Email/Phone](/quickstart-guides/react-magic-link-with-email-and-phone)
{% endcontent-ref %}

{% content-ref url="/pages/-MAUbWpO8XONhF-vdxAG" %}
[HTML – Sign in with Email/Phone](/quickstart-guides/html-magic-link-with-email-and-phone)
{% endcontent-ref %}

{% content-ref url="/pages/-MCjwlfkbi3ejQQ0qQ4K" %}
[React – WebAuthn](/quickstart-guides/react-webauthn)
{% endcontent-ref %}


# Sign In with Email/Phone Number

Our JavaScript SDK offers the easiest way to integrate Cotter 's email/phone verification. You can simply embed Cotter's Login Form and it will do the heavy lifting for you.

> **Concepts:** Learn about how [**Sign in with Email/Phone Number**](/features/verify-email-phone) works.

### What you're building

![Cotter's Embeddable Login Form using the JS SDK](/files/-M9M-J5jm5GYC1L6z5P6)

## Try it out

{% hint style="info" %}
**Quickly try out how it works in our** [**Hello World Example**](https://codesandbox.io/s/client-side-working-example-hiy8w?file=/index.html)**!** 🎉
{% endhint %}

### Overview

Verifying **email and phone number** in your website using our JavaScript SDK consists of the following steps:

1. Embed Cotter in your website
2. Receive a Callback with user's data and a `token` from Cotter
3. Send the `token` to your backend server

## Steps

1. [Setup Cotter](/sdk-reference/web/web-sdk-verify-email-phone#1-setup-cotter) with your `API_KEY_ID` and some config
2. [Show Cotter form](/sdk-reference/web/web-sdk-verify-email-phone#2-show-cotter-form)
3. [Send the Payload](/sdk-reference/web/web-sdk-verify-email-phone#step-3-sending-the-payload-to-your-backend-server) containing the user's information to your backend server

### Step 1: Setup Cotter <a href="#id-1-setup-cotter" id="id-1-setup-cotter"></a>

#### Include Javascript SDK <a href="#include-javascript-sdk" id="include-javascript-sdk"></a>

To use our Javascript SDK, include the script below in your HTML page or use the npm package.

{% tabs %}
{% tab title="<script>" %}

```markup
<script
    src="https://unpkg.com/cotter@0.3.32/dist/cotter.min.js"
    type="text/javascript"
></script>
```

Make sure you check for the latest version at <https://www.npmjs.com/package/cotter>
{% endtab %}

{% tab title="npm" %}

```
npm install cotter --save
```

{% endtab %}

{% tab title="yarn" %}

```
yarn add cotter
```

{% endtab %}
{% endtabs %}

#### Initialize Cotter <a href="#initialize-cotter" id="initialize-cotter"></a>

Initialize Cotter in your Login page. If using HTML, put this script at the **bottom** of your `<body>` . If you're using React, put this in `useEffect` or `componentDidMount` . We want this script to run right after the page is loaded.

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

```javascript
<script>
  var cotter = new Cotter("<YOUR_API_KEY_ID>"); // 👈 Specify your API KEY ID here

   cotter
    .withFormID("form_default") // Use customization for form "form_default"
    .signInWithLink()  // to send a verification code, use .signInWithOTP()
    .showEmailForm()  // to send via phone number use .showPhoneForm()
    .then(payload => {
      // payload is Cotter's token containing user information
      console.log("Cotter User Information", payload);
      // ==================================
      // TODO: Login to backend
      // ==================================
  })
  .catch(err => console.log(err));
</script>
```

{% endtab %}

{% tab title="React" %}

```javascript
import Cotter from "cotter"; //  1️⃣  Import Cotter

//...

useEffect(() => {
  var cotter = new Cotter(API_KEY_ID); // 👈 Specify your API KEY ID here
  
  cotter
    .withFormID("form_default") // Use customization for form "form_default"
    .signInWithLink()  // to send a verification code, use .signInWithOTP()
    .showEmailForm()  // to send via phone number use .showPhoneForm()
    .then(payload => {
      // payload is Cotter's token containing user information
      console.log("Cotter User Information", payload);
      // ==================================
      // TODO: Login to backend
      // ==================================
  })
  .catch(err => console.log(err));
}, []);
```

{% endtab %}
{% endtabs %}

**Adding more fields**

You can also [**add more fields**](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#additional-fields), customize the styles, and intercept the authentication request before it's sent. Check out how to [Customize the Form](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization).

**Send Code via WhatsApp**

Instead of using SMS, you can also send the code via WhatsApp. Go to the [Dashboard](https://dev.cotter.app/) > Branding and chose "Phone" on top of the preview.

{% hint style="info" %}
To send code/link via SMS or WhatsApp, you'll need to add some balance to you project in the [Dashboard](https://dev.cotter.app/).
{% endhint %}

### Step 2: Show Cotter Form <a href="#id-2-show-cotter-form" id="id-2-show-cotter-form"></a>

**Add the `<div>` container with id "cotter-form-container"**

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

```markup
<div
  id="cotter-form-container"
  style="width: 300px; height: 300px;"
></div>
```

{% endtab %}

{% tab title="React" %}

```markup
{/*  3️⃣  Put a <div> that will contain the form */}
<div id="cotter-form-container" style={{ width: 300, height: 300 }} />
```

{% endtab %}
{% endtabs %}

### What we have so far <a href="#id-3-what-we-have-so-far" id="id-3-what-we-have-so-far"></a>

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

```markup
<script
    src="https://unpkg.com/cotter@0.3.32/dist/cotter.min.js"
    type="text/javascript"
></script>

<div
  id="cotter-form-container"
  style="width: 300px; height: 300px;"
></div>

<script>
  var cotter = new Cotter("<YOUR_API_KEY_ID>"); // 👈 Specify your API KEY ID here

  cotter
    .withFormID("form_default") // Use customization for form "form_default"
    .signInWithLink()
    .showEmailForm()  // to send via phone number use .showPhoneForm()
    .then(payload => {
      console.log("Cotter User Information", payload);
      // TODO: Login to server
    })
    .catch(err => console.log(err));
</script>
```

{% endtab %}

{% tab title="React" %}

```javascript
import React, { useEffect } from "react";
import Cotter from "cotter"; //  1️⃣  Import Cotter

function App() {

  //  2️⃣ Initialize and show the form
  useEffect(() => {
    var cotter = new Cotter(API_KEY_ID); // 👈 Specify your API KEY ID here
    
    cotter
      .withFormID("form_default") // Use customization for form "form_default"
      .signInWithLink() 
      .showEmailForm()
      .then(payload => {
        console.log("Cotter User Information", payload);
        // TODO: Login to server
    })
    .catch(err => console.log(err));
  }, []);

  return (
    <div>
      {/*  3️⃣  Put a <div> that will contain the form */}
      <div id="cotter-form-container" style={{ width: 300, height: 300 }} />
    </div>
  );
}

export default App;
```

{% endtab %}
{% endtabs %}

### Step 3: Sending the Payload to your Backend Server

You can get the authentication response in the `then` callback function and send it to your server. For example:

```javascript
var cotter = new Cotter("<YOUR_API_KEY_ID>");

cotter
  .withFormID("form_default") // Use customization for form "form_default"
  .signInWithLink()
  .showEmailForm() 
  .then(payload => {
    console.log("Cotter User Information", payload);
    
    // TODO: Login to Server
    axios
      .post("http://localhost:3005/login", payload)
      .then((resp) => console.log("Response From Server", resp))
      .catch((err) => console.log(err));
  })
  .catch(err => console.log(err));

```

The `payload` that you receive from the promise is a JSON Object with the following format:

```javascript
{
    "email": "myemail@gmail.com", // User's email (or phone number)
    "oauth_token": {
        "access_token": "eyJhbGciOiJFUzI1NiIsImt...", // Access Token to validate
        "id_token": "eyJhbGciOiJFUzI1Ni...",
        "refresh_token": "27805:CNf76faa8trMhjXM...",
        "expires_in": 3600,
        "token_type": "Bearer",
        "auth_method": "OTP"
    },
    "user": {
        "ID": "abcdefgh-abcd-abcd-abcd-af6f81fb5432", // [Deprecated] Cotter User ID
        "created_at": "2020-07-21T05:50:14.182738Z",
        "updated_at": "2020-07-21T06:00:47.115096Z",
        "deleted_at": "0001-01-01T00:00:00Z",
        "issuer": "<YOUR_API_KEY_ID>",
        "identifier": "myemail@gmail.com"
    }
}
```

{% hint style="warning" %}
Please use the identifier (email/phone number) as your main way to identify users, **user.ID is deprecated.**
{% endhint %}

{% hint style="info" %}
You should [verify the access token ](/getting-access-token/verifying-jwt-tokens)to ensure this is valid and it comes from Cotter's server.
{% endhint %}

### Handling the response in your backend

{% content-ref url="/pages/-MDcFwnPyQzT3MiP1tHe" %}
[Backend: Handling Response](/sdk-reference/backend-handling-response)
{% endcontent-ref %}

## 🎉 You're done!

## Securing your Project

Since you'll be using your API Key from a front-end website or mobile app, your `API_KEY_ID` is exposed to anyone inspecting your code. Here are some ways to prevent abuse:

* [Only allow your website/app to use your API Key](/protecting-your-account/only-allow-your-website-app-to-use-your-api-key)
* [Rate Limit the number of authentication requests](/protecting-your-account/rate-limit)
* [Enable reCAPTCHA to prevent automated abuse](/protecting-your-account/enable-recaptcha-to-protect-against-automated-abuse)

## What's Next

* [**Styling the Form**](/sdk-reference/web/web-sdk-verify-email-phone/styling)**:** You can add Styling from the dashboard or add custom CSS
* [**Add Additional Fields**](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#additional-fields)**:** Add fields like Name, Address, etc to the login form
* [**Check the email/phone before logging in**](/sdk-reference/web/web-sdk-verify-email-phone/checking-the-email-or-phone-before-sending-a-verification-code): Useful for employees-only portals, RSVP, waitlists, checking if the user is registered, etc.


# Customize the Form

## Customize the Form from the Dashboard

You can now customize the form from [Cotter's Dashboard](https://dev.cotter.app/) > Project > Design.

![](/files/-MXtivfWqAyXySH8FbeI)

**Customizable Features that are not available in the Dashboard:**

* [Add Additional fields](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#additional-fields)
* [Advanced Styling](/sdk-reference/web/web-sdk-verify-email-phone/styling)

## Advanced Configuration Reference

You can use our advanced config to have more functionality:

#### Usage:

```javascript
var cotter = new Cotter(config); // 👈 Put the config in here

  cotter
    .signInWithLink()
    .showEmailForm()
    .then(payload => {})
    .catch(err => {});
```

#### Config:

Here's an example of a complete configuration:

```javascript
var config = {
  // Required Basic Configuration
  ApiKeyID: "<your-API-KEY-ID>",
  Type: "PHONE", // EMAIL or PHONE
  ContainerID: "cotter-container-signup",
  IdentifierField: "phone",

  // Change Country Code
  CountryCode: ["+62"], // IT HAS TO BE AN ARRAY

  // Styling
  ButtonBackgroundColor: "#000000",
  ButtonTextColor: "#ffffff",
  ButtonText: "Sign Up Now",
  ButtonBorderColor: "#D2D3FF", // don't specify for no border

  // Adding fields
  AdditionalFields: [
    {
      label: "Full Name",
      name: "name",
      placeholder: "Enter your full name"
    }
  ],

  // Intercepting before authentication is processed
  OnBegin: payload => {
    if (payload.identifier != "+12345678910") {
      return "Phone Number is not allowed";
    }
    return null;
  },
  
  // Add WhatsApp option to send verification code
  // (only for type = PHONE)
  // ################################################
  // # 👈Please read the section about WhatsApp 👈 #
  // #       to comply with WhatsApp's Policy       # 
  // ################################################
  PhoneChannels: ["SMS", "WHATSAPP"], // default = ["SMS"]
  
  // WhatsApp Button Styling
  ButtonWAText: "Continue with",
  ButtonWATextSubtitle:
    "Tell your customer that you'll be sending the verification code via WhatsApp",
  ButtonWABackgroundColor: "#128C7E",
  ButtonWABorderColor: "#075E54",
  ButtonWATextColor: "#ffffff",
  ButtonWALogoColor: "white",
};
```

## **Description**

| Field Name                                                                          | Description                                                                                                                                                                                                        | Required |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
| ApiKeyID                                                                            | your `API_KEY_ID`                                                                                                                                                                                                  | Y        |
| Type                                                                                | `"EMAIL"` or `"PHONE"`                                                                                                                                                                                             | Y        |
| ContainerID                                                                         | `id` of the `<div>` where you want to serve Cotter Form.                                                                                                                                                           | Y        |
| ButtonBackgroundColor                                                               | Button background color. Use HEX format (e.g. `#000000`)                                                                                                                                                           | N        |
| ButtonTextColor                                                                     | Button text color. Use HEX format (e.g. `#FFFFFF`)                                                                                                                                                                 | N        |
| ButtonText                                                                          | Button text. Default is "Sign Up Without Password"                                                                                                                                                                 | N        |
| [AdditionalFields](/sdk-reference/web/web-sdk-verify-email-phone#additional-fields) | Additional fields that you want to include in the JSON obj. Check the specs for [Additional Fields Object](/sdk-reference/web/web-sdk-verify-email-phone#additional-fields) below                                  | N        |
| IdentifierField                                                                     | Field name for the identifier to include in the JSON obj. For example, if you want to include the phone number in the JSON obj under key `"phone"`, then fill in `IdentifierField = "phone"`                       | Y        |
| [OnBegin](/sdk-reference/web/web-sdk-verify-email-phone#onbegin)                    | A function that will be called before the signup process begin. Check the specs for [OnBegin](/sdk-reference/web/web-sdk-verify-email-phone#onbegin) below                                                         | N        |
| [CountryCode](/sdk-reference/web/web-sdk-verify-email-phone#countrycode)            | **An Array** of country code options for the user                                                                                                                                                                  | N        |
| PhoneChannels                                                                       | **An Array** of channels to send verification code to your users. Default value is `["SMS"].`Available values are `SMS` and `WHATSAPP.` For example, to allow both SMS and WhatsApp, specify `["SMS", "WHATSAPP"]` | N        |

### **Additional Fields**

The additional fields will be used to generate more `<input>` fields for your users. The format is as following:

```javascript
var config = {
  ...
  AdditionalFields: [
          {
            label: "Full Name",
            name: "name",
            placeholder: "Enter your full name"
          },
          {
            label: "Address",
            name: "address",
            placeholder: "Enter your address"
          },
          {
            label: "Prefilled Info",
            name: "prefilled",
            type: "hidden", // type can be "hidden", "text" or "number"
            initial_value: "autofill value"
          }
        ],
}
```

> Available types: `hidden`, `text`, `number`

This will be sent to you to the [RedirectURL](/sdk-reference/web/web-sdk-verify-email-phone#redirecturl) endpoint above as part of the JSON object. For example, with the fields above, you'll get a JSON object like the following.

**JSON Object to be sent to `RedirectURL`:**

```javascript
{
  "token": {...},
  [IdentifierField]: "+12345678910",

  // Additional Fields
  "name": "Hello World",
  "address": "Street Address",
  "prefilled": "autofill value"
  ...
}
```

### **OnBegin**

This function will be invoked before the Signup/Login process. You will receive the following payload as a parameter to your function

**Payload passed into your OnBegin function**

```javascript
var payload = {
  identifier: "+12345678910",
  identifier_type: "PHONE",
  device_type: "BROWSER",
  device_name: "Chrome ..."
};
```

You can do a check against the `identifier` here before the form is submitted.

If you include `OnBegin` key, you have to either **return an error string**, or **return null** if you want to continue the authentication process:

**A. If you want to continue submission:**

```javascript
return null;
```

**B. If you want to stop submission with an error:**

```javascript
return "Your error message";
```

Example:

```javascript
 OnBegin: payload => {
  if (payload.identifier != "+12345678910") {
    return "Phone Number is not allowed";
  }
  // No error, continue submission
  return null;
},
```

###

### **CountryCode**

The default value is `CountryCode: ["+1"];`

{% hint style="warning" %}
If you specify this, **you have to use an array** even though you only need 1 country code!
{% endhint %}

ex. with 1 country code

```javascript
CountryCode: ["+1"];
```

ex. with 2 country codes

```javascript
CountryCode: ["+62", "+91"];
```

{% hint style="success" %}
Our SMS and WhatsApp messaging works **worldwide**!
{% endhint %}


# Checking the email or phone before sending a verification code

You can call a function to check the entered email or phone before we send a verification code or magic link.

## Use case

* You only allow specific list of pre-screened phone numbers or emails
* You want to know if the user is already registered, and redirect them to a Login page instead
* You want to check if the email is from a certain domain

**Check out this** [**example code that only allows business email to login**](https://codesandbox.io/s/html-magic-link-restrict-email-ellxs?file=/index.html)**.**

## Implementation

```javascript
var cotter = new Cotter("<YOUR_API_KEY_ID>"); 

cotter
  .signInWithLink(myOnBeginFunction) // 👈 Pass in your function here
  .showEmailForm()
  .then(payload => {})
  .catch(payload => {});
```

### The `OnBegin` function specification

This function will be invoked before we send the verification code or magic link. You will receive the following payload as a parameter to your function

**Payload passed into your OnBegin function**

```javascript
var payload = {
  identifier: "+12345678910",
  identifier_type: "PHONE",
  device_type: "BROWSER",
  device_name: "Chrome ...",
  client_json: { // This is available if you set up AdditionalFields
    "name": "Hello World",
    "address": "Street Address"
  }
};
```

You can do a check against the `identifier` here before the form is submitted.

If you include `OnBegin` key, you have to either **return an error string**, or **return null** if you want to continue the authentication process:

**A. If you want to continue submission:**

```javascript
return null;
```

**B. If you want to stop submission with an error:**

```javascript
return "Your error message";
```

Example with sync function:

```javascript
 const myOnBeginFunction = payload => {
  if (payload.identifier != "+12345678910") {
    return "Phone Number is not allowed";
  }
  // No error, continue submission
  return null;
}
```

Example with async function:

```javascript
 const myOnBeginFunction = async (payload)=> {
    try {
     let allowed = await checkIfPhoneAllowed(payload.identifier);
     if (!allowed) {
       return "Phone Number is not allowed";
     }
    } catch (e) {
     return e.message; // Make sure this is a string!
    }
    // No error, continue submission
    return null;
}
```

### You should also check the email when Verifying the JWT Token

When a user logs in to Cotter, [the SDK generates an `access_token`](/getting-access-token/handling-authentication-with-cotter) that you can send to your backend API to authorize a request. This access token contains the user's email or phone number. **You should check if the email or phone number is allowed to log in in your backend server** [**when validating the JWT token**](/getting-access-token/verifying-jwt-tokens) **in addition to the OnBegin function.**


# Sending Code or Link via WhatsApp

By default, when the user enters a phone number, we'll send a verification code or magic link using SMS. You can add the option for your users to receive the verification code or link as a WhatsApp message.

## How to Add the WhatsApp Option

![PhoneChannels options](/files/-M4XQyDsEHvFz_lzI2ql)

In your [dashboard](https://dev.cotter.app) > Branding, **select the WhatsApp checkbox** for the Magic Link Form tab and OTP Verification Tab.

![Enabling WhatsApp to send a verification code](/files/-MEjZueJr88m3iLy-DDA)

### WhatsApp Opt-In Requirements

WhatsApp requires that your application implement explicit user opt-ins to deliver messages over WhatsApp.&#x20;

{% hint style="info" %}
⚠️[**Read WhatsApp Opt-In Requirements**](https://developers.facebook.com/docs/whatsapp/guides/opt-in/)
{% endhint %}

To do this, we provide the **WhatsApp Button Configuration > Subtitle** to explicitly inform your users that they will be receiving a verification code via WhatsApp.&#x20;

The default value for the **Subtitle** is *"If you choose to login with WhatsApp, we will send a WhatsApp message to this number with a verification code"*

{% hint style="danger" %}
We make the **Subtitle** customizable so you can **use a language that your customers understand.**&#x20;

Please make sure that your customer is informed that they will be receiving a WhatsApp message, otherwise your application may be suspended.
{% endhint %}


# Styling

## Styling from the Dashboard

Generally, you can customize the form's texts and colors from [Dashboard](https://dev.cotter.app) > Project > Design.

![Customize the design of your login form](/files/-MWQcHEgRhBj89UPBGc7)

## Adding your own CSS

{% hint style="success" %}
This is now available with **`signInWithOTP`**&#x61;nd **`signInWithLink`**
{% endhint %}

To style the form, add field `Styles` to the configuration and specify the CSS styling for each element. The styling accepts a JavaScript object with `camelCased` properties rather than a CSS string, for example:

```javascript
// You need to pass a `config` object instead of your `API_KEY_ID` to cotter:

// 1) Make a Config for adding Styles
var config = {
  ApiKeyID: "<your-API-KEY-ID>",
  Styles: {
    input_label: {
      fontFamily: "Roboto",
      fontSize: 15,
      color: "red",
      fontWeight: 700,
    },
    input_text_container_default: {
      backgroundColor: "#fce883",
      padding: "20px 60px",
    },
    input_text: {
      backgroundColor: "#fce883",
      fontFamily: "Roboto",
      fontSize: 20,
    },
    button_container: {
      borderRadius: 0,
    },
    button_text: {
      color: "aqua",
    },
  },
  
  // your other config...
}

// 2) Then pass the config to Cotter
var cotter = new Cotter(config); // 👈 Put the config in here
cotter
  .signInWithOTP()
  .showEmailForm()
  .then(payload => {})
  .catch(err => {});
```

You can find the element name by checking the `class` of the element you want to style. For reference, here are the elements that you can modify:

* `form_container`
* `loadingImg`
* `input_label`
* `verification_label`
* `pin_row`
* `pin_input`
* `verification_subtitle`
* `error`
* `input_row_container`
* `input_row`
* `input_text_container_default`
* `input_text`
* `input_text_container`
* `country_code`
* `country_code_blank`
* `input_text_p`
* `check_input`

#### Available Fonts

Not all fonts are available because the fonts are not loaded inside Cotter's form. These are the list of fonts that you can use:

* Roboto
* Lato
* Overpass
* Open Sans
* Akkurat
* Akkurat-Bold
* Poppins
* Inter


# Older SDK

If you're looking for an older version of the documentation, you can find it in the sub pages of this section.


# Customize the Form

## Customize the Form from the Dashboard

You can now customize the form from [Cotter's Dashboard](https://dev.cotter.app/) > Branding.

![Customizing the Form from Cotter](/files/-MCk6p50lCyKYHP5NYNn)

**Customizable Features that are not available in the Dashboard:**

* [Add custom fields](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#additional-fields)
* [Advanced Styling](/sdk-reference/web/web-sdk-verify-email-phone/styling)

## Advanced Configuration Reference

You can use our advanced config to have more functionality:

#### Usage:

```javascript
var cotter = new Cotter(config); // 👈 Put the config in here

  cotter
    .signInWithLink()
    .showEmailForm()
    .then(payload => {})
    .catch(err => {});
```

#### Config:

Here's an example of a complete configuration:

```javascript
var config = {
  // Required Basic Configuration
  ApiKeyID: "<your-API-KEY-ID>",
  Type: "PHONE", // EMAIL or PHONE
  ContainerID: "cotter-container-signup",
  RedirectURL: "https://yourwebsite.com/account/create",
  SkipRedirectURL: false,
  IdentifierField: "phone",
  OnSuccess: payload => {
    // SET Token Cookie/localstorage here
    window.localStorage.setItem("access_token", payload.access_token);
    window.localStorage.setItem("refresh_token", payload.refresh_token);
    window.location.href = "/signin";
  },

  // Change Country Code
  CountryCode: ["+62"], // IT HAS TO BE AN ARRAY

  // Styling
  ButtonBackgroundColor: "#000000",
  ButtonTextColor: "#ffffff",
  ButtonText: "Sign Up Now",
  ButtonBorderColor: "#D2D3FF", // don't specify for no border

  // Adding fields
  AdditionalFields: [
    {
      label: "Full Name",
      name: "name",
      placeholder: "Enter your full name"
    }
  ],

  // Intercepting before authentication is processed
  OnBegin: payload => {
    if (payload.identifier != "+12345678910") {
      return "Phone Number is not allowed";
    }
    return null;
  }
  
  // Add WhatsApp option to send verification code
  // (only for type = PHONE)
  // ################################################
  // # 👈Please read the section about WhatsApp 👈 #
  // #       to comply with WhatsApp's Policy       # 
  // ################################################
  PhoneChannels: ["SMS", "WHATSAPP"], // default = ["SMS"]
  
  // WhatsApp Button Styling
  ButtonWAText: "Continue with",
  ButtonWATextSubtitle:
    "Tell your customer that you'll be sending the verification code via WhatsApp",
  ButtonWABackgroundColor: "#128C7E",
  ButtonWABorderColor: "#075E54",
  ButtonWATextColor: "#ffffff",
  ButtonWALogoColor: "white",
};
```

## **Description**

| Field Name                                                                                                         | Description                                                                                                                                                                                                                                                                                                                                  | Required |
| ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| ApiKeyID                                                                                                           | your `API_KEY_ID`                                                                                                                                                                                                                                                                                                                            | Y        |
| [RedirectURL](/sdk-reference/web/web-sdk-verify-email-phone#redirecturl)                                           | The backend URL where we will send a [JSON obj](/sdk-reference/web/web-sdk-verify-email-phone#redirecturl) containing form information and a token                                                                                                                                                                                           | Y        |
| [SkipRedirectURL](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#skip-redirecturl) | `true` or `false` . If set to `true`, then it will skip sending the [JSON obj](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#redirecturl) to the RedirectURL, and instead automatically pass it to [`OnSuccess`](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#onsuccess). |          |
| Type                                                                                                               | `"EMAIL"` or `"PHONE"`                                                                                                                                                                                                                                                                                                                       | Y        |
| ContainerID                                                                                                        | `id` of the `<div>` where you want to serve Cotter Form.                                                                                                                                                                                                                                                                                     | Y        |
| ButtonBackgroundColor                                                                                              | Button background color. Use HEX format (e.g. `#000000`)                                                                                                                                                                                                                                                                                     | N        |
| ButtonTextColor                                                                                                    | Button text color. Use HEX format (e.g. `#FFFFFF`)                                                                                                                                                                                                                                                                                           | N        |
| ButtonText                                                                                                         | Button text. Default is "Sign Up Without Password"                                                                                                                                                                                                                                                                                           | N        |
| [AdditionalFields](/sdk-reference/web/web-sdk-verify-email-phone#additional-fields)                                | Additional fields that you want to include in the JSON obj. Check the specs for [Additional Fields Object](/sdk-reference/web/web-sdk-verify-email-phone#additional-fields) below                                                                                                                                                            | N        |
| IdentifierField                                                                                                    | Field name for the identifier to include in the JSON obj. For example, if you want to include the phone number in the JSON obj under key `"phone"`, then fill in `IdentifierField = "phone"`                                                                                                                                                 | Y        |
| [OnBegin](/sdk-reference/web/web-sdk-verify-email-phone#onbegin)                                                   | A function that will be called before the signup process begin. Check the specs for [OnBegin](/sdk-reference/web/web-sdk-verify-email-phone#onbegin) below                                                                                                                                                                                   | N        |
| [OnSuccess](/sdk-reference/web/web-sdk-verify-email-phone#onsuccess)                                               | A function that will be called when the signup process succeed. It's recommended to set your localStorage/cookies here and redirect to your dashboard. Check the specs for [OnSuccess](/sdk-reference/web/web-sdk-verify-email-phone#onsuccess) below                                                                                        | Y        |
| [CountryCode](/sdk-reference/web/web-sdk-verify-email-phone#countrycode)                                           | **An Array** of country code options for the user                                                                                                                                                                                                                                                                                            | N        |
| PhoneChannels                                                                                                      | **An Array** of channels to send verification code to your users. Default value is `["SMS"].`Available values are `SMS` and `WHATSAPP.` For example, to allow both SMS and WhatsApp, specify `["SMS", "WHATSAPP"]`                                                                                                                           | N        |

### **RedirectURL**

![Authentication Flow if you use RedirectURL](/files/-M5UW0-ySdRevCZaWiTs)

We will send a JSON Object to this backend URL. The JSON Object will have the following format:

```javascript
{
  "token": {
    "identifier": "+12345678910",
    "identifier_type": "PHONE",
    "receiver": "<your API_KEY_ID>",
    "expire_at": "1582679175", // Unix timestamp
    "signature": "7en+KvbLpMkA7Z93K5y3a12tDo2x3n..." //Signature
  },
  [IdentifierField]: "+12345678910",

  // Additional Fields
  "field_1": "Value inside field 1",
  "field_2": "Value inside field 2",
  ...
}
```

Description:

**payload.Token**

Contains the email or phone number of the user. You should [verify the signature](/sdk-reference/web/web-sdk-verify-email-phone#verifying-the-signature) to ensure that the request comes from Cotter's server.

**payload.IdentifierField**

This field name will be the same as what you specify in the `config` above. For example, if your `IdentifierField = "PHONE"`, you'll get

```javascript
{
  ...
  "PHONE": "+12345678910",
}
```

**payload.AdditionalFields**

The rest will be filled with any additional fields you specify in the `config`. For example if you add fields for `"name"` and `"address"`, you'll get:

```javascript
{
  ...
  "name": "Hello World",
  "address": "123 Street",
}
```

Read how to specify [Additional Fields Object](/sdk-reference/web/web-sdk-verify-email-phone#additional-fields) below.

### Skip RedirectURL

![Authentication Flow if you use SkipRedirectURL](/files/-M4RUuBaXZjEyuAR32Np)

If you want to handle Cotter's token in a function instead of sending it to your backend `redirect_url`, you can specify

```javascript
{
  ...
  SkipRedirectURL: true,
}
```

When this is true, the [JSON payload](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#redirecturl) as specified in RedirectURL section will be passed into your [OnSuccess](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#onsuccess) function.

### **Additional Fields**

The additional fields will be used to generate more `<input>` fields for your users. The format is as following:

```javascript
var config = {
  ...
  AdditionalFields: [
          {
            label: "Full Name",
            name: "name",
            placeholder: "Enter your full name"
          },
          {
            label: "Address",
            name: "address",
            placeholder: "Enter your address"
          },
          {
            label: "Prefilled Info",
            name: "prefilled",
            type: "hidden", // type can be "hidden", "text" or "number"
            initial_value: "autofill value"
          }
        ],
}
```

> Available types: `hidden`, `text`, `number`

This will be sent to you to the [RedirectURL](/sdk-reference/web/web-sdk-verify-email-phone#redirecturl) endpoint above as part of the JSON object. For example, with the fields above, you'll get a JSON object like the following.

**JSON Object to be sent to `RedirectURL`:**

```javascript
{
  "token": {...},
  [IdentifierField]: "+12345678910",

  // Additional Fields
  "name": "Hello World",
  "address": "Street Address",
  "prefilled": "autofill value"
  ...
}
```

### **OnBegin**

This function will be invoked before the Signup/Login process. You will receive the following payload as a parameter to your function

**Payload passed into your OnBegin function**

```javascript
var payload = {
  identifier: "+12345678910",
  identifier_type: "PHONE",
  device_type: "BROWSER",
  device_name: "Chrome ..."
};
```

You can do a check against the `identifier` here before the form is submitted.

If you include `OnBegin` key, you have to either **return an error string**, or **return null** if you want to continue the authentication process:

**A. If you want to continue submission:**

```javascript
return null;
```

**B. If you want to stop submission with an error:**

```javascript
return "Your error message";
```

Example:

```javascript
 OnBegin: payload => {
  if (payload.identifier != "+12345678910") {
    return "Phone Number is not allowed";
  }
  // No error, continue submission
  return null;
},
```

### **OnSuccess**

This function will be called **after** the call to `RedirectURL` is successful. Your function will receive the payload returned from `RedirectURL`.

It is recommended to set your sessions or cookies here, and then redirect to another page in your website. For example:

```javascript
OnSuccess: payload => {
  // You can set your own sessions for your website
  window.localStorage.setItem("access_token", payload.access_token);
  window.localStorage.setItem("refresh_token", payload.refresh_token);
  window.location.href = "/dashboard";
};
```

**OnSuccess when SkipRedirectURL is true**

When you set `SkipRedirectURL: true` , `OnSuccess` will instead receive the JSON Payload token that was described in [RedirectURL section](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#redirecturl).

### **CountryCode**

The default value is `CountryCode: ["+1"];`

{% hint style="warning" %}
If you specify this, **you have to use an array** even though you only need 1 country code!
{% endhint %}

ex. with 1 country code

```javascript
CountryCode: ["+1"];
```

ex. with 2 country codes

```javascript
CountryCode: ["+62", "+91"];
```

{% hint style="info" %}
Currently, the only available countries are **United States**, **India** and **Indonesia**. Chat us on Intercom if you need other countries, we just need to switch it on.
{% endhint %}


# Sign in with Social Login

Sign in with Social Login allows you to authenticate users using their social accounts like Google, Github, etc.

### What you're building

Allow users to sign in with a Social Account, **without getting duplicate accounts.** With Cotter, we automatically link the user's existing account with their Social Account.

![Sign in with Github and Google using Cotter's SDK](/files/-MH_8SXtlVChNPg3Of_Z)

### Key Features

* Simple integration, **no additional code**.
* **Auto-linking with existing accounts**, users can either log in with their email or with Github and it will go to the same user account.
* Connect Social Provider Account from inside your Settings page to allow easy API integrations. (For example, a user never uses the "Sign in with Github", but you need their Github access token to enable deployment: you can ask them to press a button to connect their Github account).

### Overview

1. To enable this feature, you need to first configure [Sign in with Email/Phone Number](/sdk-reference/web/web-sdk-verify-email-phone) as the primary login method.

{% content-ref url="/pages/-M0ROJn7tosL9qeumqFd" %}
[Sign In with Email/Phone Number](/sdk-reference/web/web-sdk-verify-email-phone)
{% endcontent-ref %}

2\. You can then add options for the user to sign in using a Social Login Provider in the Dashboard.

#### Available Providers

* Github
* Google
* [Let us know what you need](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA)

## Steps

1. Integrate Cotter's Login Form following [Sign in with Email/Phone Number](/sdk-reference/web/web-sdk-verify-email-phone) as the primary login method.
2. [Set up your Social Login](/sdk-reference/web/sign-in-with-social-login-provider#setting-up-social-login-provider).
3. [Connecting a Social Account to an existing user ](/sdk-reference/web/sign-in-with-social-login-provider#connecting-a-social-account-to-an-existing-user)that **didn't use Sign in with a Social Provider** from a button.&#x20;
4. [Getting the Access Token from the Social Provider](/sdk-reference/web/sign-in-with-social-login-provider#getting-access-tokens-from-the-social-login-provider) to use their API.

## Setting Up Social Login Provider

{% hint style="info" %}
Make sure you are using the JS Package **version >= 0.3.16**
{% endhint %}

1. You will need to create an "App" for the Social Login Provider. Follow the instructions for each login provider. You will need to enter the credentials to [Dashboard](https://dev.cotter.app/) > Social Login
   * [Github Instructions](/sdk-reference/web/sign-in-with-social-login-provider/github-instructions)
   * [Google Instructions](/sdk-reference/web/sign-in-with-social-login-provider/google-instructions)
2. Go to [Dashboard](https://dev.cotter.app/) > Project > Form & Fields, check the login provider that you want to enable under "Social Login Providers" and press **Save**.

![](/files/-MWQcmxqcP-fnFc--n12)

4\. That's it, you should now see the "Sign in with Github" button below the email/phone number input.&#x20;

### Receiving the User Response after successful login

You will receive responses the same way as [**Sign in with email/phone**](/sdk-reference/web/web-sdk-verify-email-phone), inside the `then` callback that you provided.

```javascript
var cotter = new Cotter(API_KEY_ID); // Specify your API KEY ID here
cotter
  .withFormID("form_default") // Use customization for form "form_default"
  .signInWithLink()
  .showEmailForm()
  .then((response) => {
    setpayload(response); // 👈 You'll receive the response here, as usual
  })
  .catch((err) => console.log(err));
```

## How it works

If a user signed-in with Github and the Github username is user1 and the Github email is <user1@gmail.com>:

* **Case 1 – If a user with Github username `user1` has logged-in before** and already associated with a user (`userID: 123`), then the user can automatically log in.
* **Case 2 – If a user with email** [**user1@gmail.com**](mailto:user1@gmail.com) **doesn't exist**: It will create a new user with email <user1@gmail.com>, and the user can log in both with their Github account or with email <user1@gmail.com> using a magic link or OTP.
* **Case 3 – If a user with email** [**user1@gmail.com**](mailto:user1@gmail.com) **already exists**: It will ask the user if they want to link the account for <user1@gmail.com> with the Github account. If they agree, the user can always login both with their Github account or with email <user1@gmail.com> using a magic link or OTP.

![Case 3: Prompt for the user to connect their existing account with the Github account](/files/-MGMSCjt9Zb2Ur1eQwLM)

### Connecting a Social Account to an existing user

You can alternatively **connect the user's Github/Google account when the user is already logged** **in.** For example, your website may have a Settings page, and the user can press a button "Connect Github Login" to allow them to connect their Github Account and use it to log in.

Add a button on your website, and call this function to connect the user's Github Account with the currently logged in user.

```javascript
const connectToGithub = async () => {
  var cotter = new Cotter(apiKey)
  const accessToken = await cotter.tokenHandler.getAccessToken()
  cotter.connectSocialLogin("GITHUB", accessToken?.token) // pass in the provider's name
}
```

## Getting Access Tokens from the Social Login Provider

When your users sign in with Github, Github returns an `access_token` that you can use to call their API. We store this information for you. To get the `access_token` for each user, follow the instructions below:

{% content-ref url="/pages/-MGMR-Umubw7gOJeVnX8" %}
[Getting Access Tokens from Social Login Providers](/sdk-reference/web/sign-in-with-social-login-provider/getting-access-tokens-from-social-login-providers)
{% endcontent-ref %}

## UI Considerations

Logging-in to social providers **requires us to redirect the user to the provider's page to login** when necessary. When the login process is done, the users will be redirected back **to the page where you embedded the form**:

For example:

* Login Page that contains the form: `example.com/login`
  * User login to Github: redirect to `github.com/oauth/....`
  * User finish login: redirect back to `example.com/login?code=xyz&state=abc`

As you can see, we pass in a `code` and `state` and other parameters when redirecting back to your login page. **Cotter's Login Form automatically handles this query parameters** for you and will resolve the Promise with the usual Cotter user information response.&#x20;

**With that said, make sure your UI takes into account that the user will be redirected away and then back to your login page.**

<br>


# Getting Access Tokens from Social Login Providers

Cotter stores all OAuth Tokens that are returned from the Social Login Providers when a user sign in. You can retrieve the tokens using the following API Requests.

## Get a List of Access Tokens from All Social Logins for a User

<mark style="color:blue;">`GET`</mark> `https://www.cotter.app/api/v0/oauth/token/list/:cotter_user_id`

This will give you a list of access tokens that the user has for each social login provider that is associated with their account.

#### Path Parameters

| Name             | Type   | Description               |
| ---------------- | ------ | ------------------------- |
| cotter\_user\_id | string | The user's Cotter User ID |

#### Headers

| Name             | Type   | Description         |
| ---------------- | ------ | ------------------- |
| API\_KEY\_ID     | string | Your API Key ID     |
| API\_SECRET\_KEY | string | Your API Secret Key |

{% tabs %}
{% tab title="200 A list of OAuth Tokens from each provider that is associated with the user. " %}

```javascript
[
  {
    "ID": "abcdefgh-abcd-abcd-abcd-24339e5457ff",  // OAuth Token ID
    "created_at": "2020-09-04T04:51:10.778976Z",
    "updated_at": "2020-09-04T04:51:10.778977Z",
    "deleted_at": "0001-01-01T00:00:00Z",
    "company_id": "abcdefgh-abcd-abcd-abcd-f901315ad31b",  // Your API KEY ID
    "user_id": "abcdefgh-abcd-abcd-abcd-22bc178867ef",     // The user's ID
    "provider_user_id": "20483939",                        // User ID from the Provider (Github User ID)
    "provider_username": "putrikarunia",                   // Login/username from the provider (Github Username)
    "identity_provider": "GITHUB",
    "tokens": {                      // OAuth tokens returned by the provider
      "access_token": "abcdefghabcdefghabcdefghabcdefghabcdefg",
      "scope": "repo,user",
      "token_type": "bearer"
    }
  },
  {
    "ID": "abcdefgh-abcd-abcd-abcd-24339e5457ff",  // OAuth Token ID
    "created_at": "2020-09-04T04:51:10.778976Z",
    "updated_at": "2020-09-04T04:51:10.778977Z",
    "deleted_at": "0001-01-01T00:00:00Z",
    "company_id": "abcdefgh-abcd-abcd-abcd-f901315ad31b",  // Your API KEY ID
    "user_id": "abcdefgh-abcd-abcd-abcd-22bc178867ef",     // The user's ID
    "provider_user_id": "20483939",                        // User ID from the Provider (Github User ID)
    "provider_username": "putrikarunia",                   // Login/username from the provider (Github Username)
    "identity_provider": "GOOGLE",
    "tokens": {                      // OAuth tokens returned by the provider
      "access_token": "abcdefghabcdefghabcdefghabcdefghabcdefgh",
      "scope": "repo,user",
      "token_type": "bearer"
    }
  },
  ...
]
```

{% endtab %}
{% endtabs %}

## Get an Access Token from a Specific Provider for a User

<mark style="color:blue;">`GET`</mark> `https://www.cotter.app/api/v0/oauth/token/:provider_name/:cotter_user_id`

Get an access token from one provider for the user.

#### Path Parameters

| Name             | Type   | Description               |
| ---------------- | ------ | ------------------------- |
| cotter\_user\_id | string | The user's Cotter User ID |
| provider\_name   | string | One of: `GITHUB`          |

#### Headers

| Name             | Type   | Description         |
| ---------------- | ------ | ------------------- |
| API\_KEY\_ID     | string | Your API Key ID     |
| API\_SECRET\_KEY | string | Your API Secret Key |

{% tabs %}
{% tab title="200 A Token Object with the OAuth tokens from the provider for the user." %}

```javascript
{
  "ID": "abcdefgh-abcd-abcd-abcd-24339e5457ff",  // OAuth Token ID
  "created_at": "2020-09-04T04:51:10.778976Z",
  "updated_at": "2020-09-04T04:51:10.778977Z",
  "deleted_at": "0001-01-01T00:00:00Z",
  "company_id": "abcdefgh-abcd-abcd-abcd-f901315ad31b",  // Your API KEY ID
  "user_id": "abcdefgh-abcd-abcd-abcd-22bc178867ef",     // The user's ID
  "provider_user_id": "20483939",                        // User ID from the Provider (Github User ID)
  "provider_username": "putrikarunia",                   // Login/username from the provider (Github Username)
  "identity_provider": "GITHUB",
  "tokens": {                      // OAuth tokens returned by the provider
    "access_token": "abcdefghabcdefghabcdefghabcdefghabcdefg",
    "scope": "repo,user",
    "token_type": "bearer"
  }
}
```

{% endtab %}
{% endtabs %}

## Delete an Access Token from a Specific Provider for a User

<mark style="color:red;">`DELETE`</mark> `https://www.cotter.app/api/v0/oauth/token/:provider_name/:cotter_user_id`

Delete an access token from a provider for the user.

#### Path Parameters

| Name             | Type   | Description               |
| ---------------- | ------ | ------------------------- |
| cotter\_user\_id | string | The user's Cotter User ID |
| provider\_name   | string | One of: `GITHUB`          |

#### Headers

| Name             | Type   | Description         |
| ---------------- | ------ | ------------------- |
| API\_KEY\_ID     | string | Your API Key ID     |
| API\_SECRET\_KEY | string | Your API Secret Key |

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

```
{
  "ID": "00000000-0000-0000-0000-000000000000",
  "created_at": "0001-01-01T00:00:00Z",
  "updated_at": "2020-09-04T05:09:16.136898006Z",
  "deleted_at": "2020-09-04T05:09:16.13689443Z",
  "company_id": "00000000-0000-0000-0000-000000000000",
  "user_id": "00000000-0000-0000-0000-000000000000",
  "provider_user_id": "",
  "provider_username": "",
  "identity_provider": "",
  "tokens": null
}
```

{% endtab %}
{% endtabs %}


# Github Instructions

How to get a Client ID and Client Secret from Github, and what to fill in for the Authorization Callback URL.

## Making Github OAuth Apps

1. Go to the [Dashboard](https://dev.cotter.app/) > Social Login > New Login Connection > Github.
2. Follow [Github's instruction on making a Github OAuth App](https://docs.github.com/en/developers/apps/creating-an-oauth-app). Enter your website/app's information as instructed.
3. At Step 8 about **Authorization callback URL**, enter the following: `https://www.cotter.app/api/v0/oauth/token/callback/GITHUB`
4. Copy your Client ID, Client Secret Key, and [Scopes](https://docs.github.com/en/developers/apps/scopes-for-oauth-apps) to your Dashboard > Social Login > Github:

![Enter your Client ID, Client Secret, and Scopes](/files/-MGMOSGx-ajL6KE9Bh8k)

### That's it!

Go back to the [Social Login instructions](/sdk-reference/web/sign-in-with-social-login-provider#setting-up-social-login-provider) to finish your setup.


# Google Instructions

How to get a Client ID and Client Secret from Google OAuth, and what to fill in for the Authorized redirect URIs.

### Create Google OAuth 2.0 Credentials

1. Go to the [Google Credentials page](https://console.developers.google.com/apis/credentials).
2. Click **Create credentials > OAuth client ID**.&#x20;
3. If you're asked to **Configure Consent Screen**, make sure you choose **External** User Type, and enter your app's information.
4. Select the **Web application** application type.
5. Name your OAuth 2.0 client.
6. Add your website URL as Authorized JavaScript origins.
7. Add `https://www.cotter.app/api/v0/oauth/token/callback/GOOGLE` as the **Authorized redirect URIs.**
8. Click **Create.**
9. Copy your Client ID and Client Secret to your Dashboard > Social Login > Google, then add the [Scopes](https://developers.google.com/identity/protocols/oauth2/scopes) that you need.

![](/files/-MH_FtedP3WRlU8TceOG)

&#x20; 8\.  Go to the OAuth consent screen section in Google's console, make sure the **User type** is **External** to allow any user with a Google account, then verify the [OAuth Consent Screen at Google's console](https://console.developers.google.com/apis/credentials/consent).

![](/files/-MXp040PzHFif7iggmRf)

### That's it!

Go back to the [Social Login instructions](/sdk-reference/web/sign-in-with-social-login-provider#setting-up-social-login-provider) to finish your setup.


# Sign In with WebAuthn

Sign in with WebAuthn allows you to authenticate users using TouchID or Windows Hello from their browser. This means, you can use biometric authentication from your website.

> **Concepts:** Learn about how [**Sign in with WebAuthn**](/features/sign-in-with-webauthn) works.

![WebAuthn with Cotter's JS SDK](/files/-MCkIqSr1NpqGEsST7of)

### Overview

WebAuthn authentication works the following way:

1. A new user would be prompted to either enter a verification code or magic link sent to their email or phone.
2. Once the user verified their email/phone, the SDK will automatically prompt the user if they want to register this device for fast logins next time.
3. The user can press "Enable TouchID" and successfully register their laptop.
4. When the user login next time, the user will automatically be prompted to use TouchID to login. As a fallback method, the user can choose to send a link or code to their email/phone instead.

## Steps

### Step 1: Import Cotter

#### Include Javascript SDK <a href="#include-javascript-sdk" id="include-javascript-sdk"></a>

To use our Javascript SDK, include the script below in your HTML page or use the npm package.

{% tabs %}
{% tab title="<script>" %}

```markup
<script
    src="https://unpkg.com/cotter@0.3.32/dist/cotter.min.js"
    type="text/javascript"
></script>
```

Make sure you check for the latest version at <https://www.npmjs.com/package/cotter>
{% endtab %}

{% tab title="npm" %}

```
npm install cotter --save
```

{% endtab %}

{% tab title="yarn" %}

```
yarn add cotter
```

{% endtab %}
{% endtabs %}

### Step 2: Set up a `div` element to contain cotter's form

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

```markup
<!-- 2️⃣ Setup a div to contain the form -->
<div id="cotter-form-container" style="width: 300px; height: 300px;"></div>
```

{% endtab %}

{% tab title="React" %}

```markup
{/* 2️⃣ Setup a div to contain the form */}
<div id="cotter-form-container" style={{ width: 300, height: 300 }}></div>
```

{% endtab %}
{% endtabs %}

Please note that **id** has to be `cotter-form-container` for the form to show up.

### Step 3: Initialize Cotter and show the form

Grab your `API_KEY_ID` from [the dashboard](https://dev.cotter.app), then replace `<YOUR_API_KEY_ID>` with your `API_KEY_ID`.

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

```markup
<!-- 3️⃣ Show the form -->
<script>
  var cotter = new Cotter("<YOUR_API_KEY_ID>"); // 👈 Specify your API KEY ID here

  cotter
    .withFormID("form_default") // Use customization for form "form_default"
    .signInWithWebAuthnOrLink() // or signInWithWebAuthnOrOTP()
    .showEmailForm()            // or showPhoneForm()
    .then(payload => console.log(payload))
    .catch(err => console.log(err));
</script>
```

{% endtab %}

{% tab title="React" %}

```javascript
import Cotter from "cotter";
//...

useEffect(() => {
    // 3️⃣  Show the form
    var cotter = new Cotter("<YOUR_API_KEY_ID>"); // 👈 Specify your API KEY ID here

    cotter
      .withFormID("form_default") // Use customization for form "form_default"
      .signInWithWebAuthnOrLink() // or signInWithWebAuthnOrOTP()
      .showEmailForm() // or showPhoneForm()
      .then((payload) => console.log(payload))
      .catch((err) => console.log(err));
  }, []);
```

{% endtab %}
{% endtabs %}

There are several options for the fallback method:

* Method:&#x20;
  * Magic Link: use `signInWithWebAuthnOrLink()`&#x20;
  * OTP: use `signInWithWebAuthnOrOTP()`
* Channel:&#x20;
  * Email: use `showEmailForm()`&#x20;
  * Phone: use `showPhoneForm()` (you can send code/link via SMS or WhatsApp by setting it up in Dashboard > Branding)

## 🎉 You're done!


# Register WebAuthn for a logged-in user

The login form automatically prompt the user to register WebAuthn, however, it's possible that the user dismissed it.&#x20;

{% hint style="warning" %}
The following guide only works with [Cotter's NPM package](https://www.npmjs.com/package/cotter) that can be installed using:

```markup
yarn add cotter

<!-- or -->

npm i cotter --save

<!-- or -->

<script
    src="https://unpkg.com/cotter@0.3.10-beta.5/dist/cotter.min.js"
    type="text/javascript"
></script>
```

This feature is **not supported** on imports using

```markup
<script src="https://js.cotter.app/lib/cotter.js" type="text/javascript"></script>
```

{% endhint %}

**To add the option to register WebAuthn in your Settings page**, you can do the following:

#### 1. Check if WebAuthn is available

WebAuthn is supported in some browsers like Google Chrome, but not all browsers and not all devices supports it. **Do the following to decide whether or not to show the button to setup WebAuthn:**

```javascript
// 1) Check if WebAuthn is available
Cotter.isWebAuthnAvailable().then(available => 
    setwebauthnAvailable(available)
);
```

#### 2. Register this device for WebAuthn

If WebAuthn is available, you can register WebAuthn for the user on button press:

```javascript
var cotter = new Cotter(API_KEY_ID)
var user = cotter.getLoggedInUser() // Get the logged-in user

if (user) { // If there's a logged-in user
  user
    .registerWebAuthn() // register WebAuthn
    .then(resp => console.log(resp))
    .catch(err => console.log(err))
}
```


# Sign In with Device

Our JavaScript SDK offers the easiest way to add "Sign in with device" into your website. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Device**](/features/passwordless-login) works.

{% hint style="warning" %}
There are major updates planned for this feature. [Contact us in Slack](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA) so we can help you prepare for it.
{% endhint %}

### Overview

Authenticating users using Trusted Devices with Cotter's JavaScript SDK consists of the following steps:

1. Embed Cotter in your website
2. Receive a Callback with user's authentication event and a `token` from Cotter
3. Validate the token and proceed with logins or transactions.

Keep in mind that this works if you have a companion app that [allows users to register their device as a Trusted Device.](/features/passwordless-login/how-it-works)

### What you're building

![Cotter's Authentication Request Modal](/files/-M3o22jdQ1gCzqvmCvSx)

There are 2 ways to present Cotter's authentication prompt from your website:

* **In the Login form**: after entering their email/phone number, if there's a trusted device, it will automatically prompt the user to approve the login from their device.
* **As a popup**: When a user triggers an action that requires authentication, like doing a transaction or opening sensitive information, you can open a popup that will ask the user to approve the transaction from their device.

In both cases, you will receive an Event Response (the same event response that you'll receive in mobile app) that you can pass in your backend server during the login/transaction function and check if the user has authenticated and approved the event.

## Required Steps

1. [Allow Trusted Devices and OTP methods in your dashboard](/sdk-reference/web/web-sdk-passwordless-login#step-1-setting-authentication-methods)
2. [Show Login Form or Pop Up Prompt](/sdk-reference/web/web-sdk-passwordless-login#show-the-login-form-or-popup-prompt)

### Step 1: Setting Authentication Methods

You need to set allowed methods for authenticating your users. To allow `TRUSTED DEVICES` (and `OTP` if you want to allow it as a fallback method), go to <https://dev.cotter.app/rules>

![Allow Trusted Devices (and OTP)](/files/-M3o60_0FqlqITY2Rqrz)

{% hint style="warning" %}
Remember to set the correct Project in the dropdown list
{% endhint %}

### **Step 2: Show the Login Form or Popup Prompt**

We will cover the Login Form here, and the Pop Up in the next guide.

* [Steps for Login Form](/sdk-reference/web/web-sdk-passwordless-login#steps-for-login-form)
* [Steps for Pop Up Prompt](/sdk-reference/web/web-sdk-passwordless-login/steps-for-pop-up-authentication-prompt)

## Steps for Login Form

![Cotter's Login Form using Trusted Device](/files/-M3oL1aDSso5gULXOKXD)

1. [Setup Cotter](/sdk-reference/web/web-sdk-passwordless-login#1-setup-cotter) with your `API_KEY_ID` and some config
2. [Show Cotter form](/sdk-reference/web/web-sdk-passwordless-login#2-show-cotter-form)
3. [Receive a Callback](/sdk-reference/web/web-sdk-passwordless-login#step-3-receive-callback-in-your-redirecturl) in your `OnSuccess` function the authentication event and a `signature` from Cotter in your backend server

### Step 1: Setup Cotter <a href="#id-1-setup-cotter" id="id-1-setup-cotter"></a>

#### Include Javascript SDK <a href="#include-javascript-sdk" id="include-javascript-sdk"></a>

To use our Javascript SDK, include the script below in your HTML page.

```markup
<script src="https://js.cotter.app/lib/cotter.js" type="text/javascript"></script>
```

#### Initialize Cotter <a href="#initialize-cotter" id="initialize-cotter"></a>

Initialize Cotter in your HTML page, below the script you imported above. For React apps, initialize this in `index.html`. You can use it later in any of your pages.

```markup
<script>
  var cotter = new CotterLogin(config);
</script>
```

#### Configuration <a href="#configuration" id="configuration"></a>

Here's a basic configuration example:

```javascript
var config = {
  ApiKeyID: "<your-API-KEY-ID>",
  
  // Type of identity your want to collect: EMAIL or PHONE
  Type: "PHONE",
  
  // div id of the container for Cotter Form
  ContainerID: "cotter-container-login",
  SkipRedirectURL: true,
  
  // the JSON key for the phone number or email to be posted to RedirectURL
  // Read more on RedirectURL JSON Object below
  IdentifierField: "phone",
  
  // OnBegin here is MANDATORY
  // You need to return the Cotter User ID. (If you used client_user_id, 
  // use that instead).
  OnBegin: async payload => {      
    var { userID, err } = await getUserIDFromIdentifier(
      payload.identifier
    );
    return {
      userID: userID,
      err: err
    };
  },
  
  // After the user successfully logged-in, the SDK will return a payload
  // about the login request into this OnSuccess function.
  OnSuccess: payload => {
    console.log(payload)
  },
  
  // (optional) Allow login using OTP sent to email or phone number
  // if user doesn't have Trusted Device set up
  // or if user choose to use OTP instead of trusted device
  // (To enable this, you have to set OTP true in the dashboard)
  AllowOTPFallback: true,
};

// Getting the User ID usnig Cotter's API
const getUserIDFromIdentifier = async (identifier) => {
  try {
    const resp = await fetch(
      `https://www.cotter.app/api/v0/user?identifier=${encodeURIComponent(
        identifier
      )}`,
      { headers: { API_KEY_ID: apiKey } }
    );
    const respJSON = await resp.json();
    const nullUUID = "00000000-0000-0000-0000-000000000000";
    return {
      userID: respJSON.ID === nullUUID ? null : respJSON.client_user_id,
      err: respJSON.ID === nullUUID ? "User not found" : null
    };
  } catch (e) {
    return {
      userID: null,
      err: e.message
    };
  }
};
```

**Advanced Customization**

[Check out how you can customize the form and change the configuration](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization).

### Step 2: Show Cotter Form <a href="#id-2-show-cotter-form" id="id-2-show-cotter-form"></a>

To show the cotter form,

**1. Add the `<div>` container.**

Put a `<div>` with the `id` you specified above as `ContainerID` inside your page. For example, if your `"ContainerID": "cotter-container-login"`, put this in your html:

```markup
<div
  id="cotter-container-login"
  style="width: 300px; height: 300px;"
></div>
```

**2. Show the form**

```markup
<script>
  cotter.showForm();
</script>
```

### What we have so far <a href="#id-3-what-we-have-so-far" id="id-3-what-we-have-so-far"></a>

{% code title="index.html" %}

```markup
<script
  src="https://js.cotter.app/lib/cotter.js"
  type="text/javascript"
></script>

<div id="cotter-container-login" style="width: 300px; height: 300px;"></div>

<script>
  var config = {
    ApiKeyID: "API_KEY_ID",
    SkipRedirectURL: true,
    Type: "PHONE", // EMAIL or PHONE
    ContainerID: "cotter-container-login",
    IdentifierField: "phone",
    OnSuccess: payload => {
      console.log(payload)
    },
    AllowOTPFallback: true,
    OnBegin: async payload => {      
      var { userID, err } = await getUserIDFromIdentifier(
        payload.identifier
      );
      return {
        userID: userID,
        err: err
      };
    },
  };
  var cotter = new CotterLogin(config);
  cotter.showForm();
  
  
  // Getting the User ID usnig Cotter's API
  const getUserIDFromIdentifier = async (identifier) => {
    try {
      const resp = await fetch(
        `https://www.cotter.app/api/v0/user?identifier=${encodeURIComponent(
          identifier
        )}`,
        { headers: { API_KEY_ID: apiKey } }
      );
      const respJSON = await resp.json();
      const nullUUID = "00000000-0000-0000-0000-000000000000";
      return {
        userID: respJSON.ID === nullUUID ? null : respJSON.client_user_id,
        err: respJSON.ID === nullUUID ? "User not found" : null
      };
    } catch (e) {
      return {
        userID: null,
        err: e.message
      };
    }
  };
</script>
```

{% endcode %}

### Step 3: Receive the Response in your OnSuccess function

We will send a JSON Object describing the Authentication Event to your `OnSuccess` function. The JSON Object will have the following format:

```javascript
{
  "ID": 2397,
  "CreatedAt": "2020-04-01T05:28:12.540127Z",
  "UpdatedAt": "2020-04-01T05:28:16.599833Z",
  "DeletedAt": null,
  "client_user_id": "<YOUR USER ID>",
  "issuer": "<YOUR API KEY ID>",
  "event": "LOGIN_WEB",
  "ip": "123.347.26.236",
  "location": "San Mateo",
  "timestamp": "1585718892",
  "method": "TRUSTED_DEVICE",
  "new": false,
  "approved": true,
  "signature": "reBafVsmpxO7km5jpRQ3xA..."
}
```

{% hint style="info" %}
You should [verify the signature ](/sdk-reference/web/web-sdk-passwordless-login#validate-cotters-event-response)in the event response to ensure this is valid and it comes from Cotter's server.
{% endhint %}

## Validate Cotter's Event Response

Learn how to validate Cotter's event response here:

{% content-ref url="/pages/-M2pWffm56vOPZk07RZ7" %}
[Validating Cotter's Event Response](/older-api/validating-cotters-event-response)
{% endcontent-ref %}

## 🎉 You're done!

## Next Steps

{% content-ref url="/pages/-M3o4Cgu-uvdZvuU7AgZ" %}
[Steps for Pop Up Authentication Prompt](/sdk-reference/web/web-sdk-passwordless-login/steps-for-pop-up-authentication-prompt)
{% endcontent-ref %}

{% content-ref url="/pages/-M3oW3WPjWEiptnzU4qQ" %}
[Advanced Customization for Login Form](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization)
{% endcontent-ref %}


# Steps for Pop Up Authentication Prompt

Show a popup to prompt users to authenticate using a Trusted Device before proceeding with a transaction or opening sensitive information

In this guide, we'll go over how to show a Pop Up that prompts the user to authenticate with their trusted device before moving forward with an action.

### What you're building

![Cotter's Authentication Request Modal](/files/-M3o22jdQ1gCzqvmCvSx)

{% hint style="info" %}
Make sure you have done the [Required Steps](/sdk-reference/web/web-sdk-passwordless-login#required-steps) before you proceed.
{% endhint %}

## Steps

1. [Setup Cotter](/sdk-reference/web/web-sdk-passwordless-login/steps-for-pop-up-authentication-prompt#step-1-setup-cotter) with your `API_KEY_ID` and some config
2. [Show Cotter form](/sdk-reference/web/web-sdk-passwordless-login/steps-for-pop-up-authentication-prompt#2-show-cotter-form)
3. [Receive a Event Response in `OnSuccess`](/sdk-reference/web/web-sdk-passwordless-login/steps-for-pop-up-authentication-prompt#step-3-receive-event-response-callback)

### Step 1: Setup Cotter

#### Include Javascript SDK <a href="#include-javascript-sdk" id="include-javascript-sdk"></a>

To use our Javascript SDK, include the script below in your HTML page.

```markup
<script
  src="https://js.cotter.app/lib/cotter.js"
  type="text/javascript"
></script>
```

#### Initialize Cotter <a href="#initialize-cotter" id="initialize-cotter"></a>

Initialize Cotter in your HTML page, below the script you imported above. For React apps, initialize this in `index.html`. You can use it later in any of your pages.

```markup
<script>
  var cotterAuthReq = new CotterAuthRequest(config);
</script>
```

#### Configuration <a href="#configuration" id="configuration"></a>

Here's a basic configuration example:

```javascript
var config = {
  ApiKeyID: "<your-API-KEY-ID>",
  
  // (optional) The email/phone number of your user 
  // (used to send OTP as fallback method)
  Identifier: "putrikarunian@gmail.com",
  IdentifierType: "EMAIL", // type of Identifier above (EMAIL or PHONE)
  
  // The Cotter User ID of this user
  UserID: "<your-user-id>",
  
  // Allow login using OTP sent to email or phone number
  // if use doesn't have Trusted Device set up
  // or if user choose to use OTP instead of trusted device
  // (To enable this, you have to set OTP true in the dashboard)
  AllowOTPFallback: true,
  
  // OnSuccess here will receive the JSON Event Response as a result
  // of the user Authenticating from their trusted device
  // You should send this JSON Event Response to your backend
  OnSuccess: async payload => {
    // For example, send this payload and the transaction data to submit
    // a transaction, and validate the payload in your backend
    console.log(payload);
    
    // NOTE: The popup will immediately close, so set a Loading in your
    // frontend while submitting data to your backend
  }
};
```

**Advanced Customization**

[Check out how you can customize the form and change the configuration](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization-for-pop-up-authentication-prompt).

### Step 2: Show Cotter Auth Request Pop Up <a href="#id-2-show-cotter-form" id="id-2-show-cotter-form"></a>

To show the the popup

```markup
<script>
  cotterAuthReq.show();
</script>
```

### What we have so far

```markup
<script
  src="https://js.cotter.app/lib/cotter.js"
  type="text/javascript"
></script>
<script>
  var config = {
    ApiKeyID: "<your-API-KEY-ID>",
    
    // (optional) The email/phone number of your user 
    // (used to send OTP as fallback method)
    Identifier: "<your-user-phone-number>",
    IdentifierType: "PHONE", // type of Identifier above (EMAIL or PHONE)
    
    // The UserID you registered to Cotter in the Required Steps
    UserID: "<your-user-id>",
    
    // Allow login using OTP sent to email or phone number
    // if use doesn't have Trusted Device set up
    // or if user choose to use OTP instead of trusted device
    // (To enable this, you have to set OTP true in the dashboard)
    AllowOTPFallback: true,
    
    // OnSuccess here will receive the JSON Event Response as a result
    // of the user Authenticating from their trusted device
    // You should send this JSON Event Response to your backend
    OnSuccess: async payload => {
      // For example, send this payload and the transaction data to submit
      // a transaction, and validate the payload in your backend
      console.log(payload);
      
      // NOTE: The popup will immediately close, so set a Loading in your
      // frontend while submitting data to your backend
    }
  };
  var cotterAuthReq = new CotterAuthRequest(config);
  
  function openAuthRequest() {
    cotterAuthReq.show();
  }
</script>
<div onClick="openAuthRequest()" style="background-color:#f0f0f0; padding: 20px">
  Open Auth Request Popup
</div>
```

### Step 3: Receive Event Response Callback

We will pass a JSON Object describing the Authentication Event to your `OnSuccess` function. The JSON Object will have the following format:

```javascript
{
  "ID": 2397,
  "CreatedAt": "2020-04-01T05:28:12.540127Z",
  "UpdatedAt": "2020-04-01T05:28:16.599833Z",
  "DeletedAt": null,
  "client_user_id": "<YOUR USER ID>",
  "issuer": "<YOUR API KEY ID>",
  "event": "LOGIN_WEB",
  "ip": "123.347.26.236",
  "location": "San Mateo",
  "timestamp": "1585718892",
  "method": "TRUSTED_DEVICE",
  "new": false,
  "approved": true,
  "signature": "reBafVsmpxO7km5jpRQ3xA..."
}
```

{% hint style="info" %}
You should [verify the signature](/sdk-reference/web/web-sdk-passwordless-login/steps-for-pop-up-authentication-prompt#validate-cotters-event-response) in the event response to ensure this is valid and it comes from Cotter's server.
{% endhint %}

On this step, you should pass this payload to your backend together with any data you need to submit a transaction, or any other api calls to your server.

## Validate Cotter's Event Response

Learn how to validate Cotter's event response here:

{% content-ref url="/pages/-M2pWffm56vOPZk07RZ7" %}
[Validating Cotter's Event Response](/older-api/validating-cotters-event-response)
{% endcontent-ref %}

## :tada: You're done!

## Next Steps

Customize your Pop Up prompt

{% content-ref url="/pages/-M3ob6vjow8Bwd5zU9zY" %}
[Advanced Customization for Pop Up Authentication Prompt](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization-for-pop-up-authentication-prompt)
{% endcontent-ref %}


# Advanced Customization for Login Form

## Configuration Reference

Here's an example of a complete configuration:

```javascript
var config = {
  // Required Basic Configuration
  ApiKeyID: "<your-API-KEY-ID>",
  Type: "PHONE",
  ContainerID: "cotter-container-login",
  RedirectURL: "https://yourwebsite.com/account/create",
  IdentifierField: "phone",
  OnSuccess: payload => {
    window.localStorage.setItem("session", payload.session);
    window.location.href = "/dashboard";
  }
  OnBegin: async payload => {      
    var userID = await this.getUserIDFromPhone(payload.identifier);
    return {
      userID: userID,
      err: null
    };
  },
  // ---------

  // Change Country Code
  CountryCode: ["+62"], // IT HAS TO BE AN ARRAY

  // Styling
  ButtonBackgroundColor: "#000000",
  ButtonTextColor: "#ffffff",
  ButtonText: "Sign Up Now",
  ErrorColor: "#ff0000",
  
  // Event Name in the Event Response
  EventName: "LOGIN_EMPLOYEE",
  
  // Allow OTP as a fallback method for TrustedDevice
  AllowOTPFallback: true,

  // Skipping RedirectURL and receive event response in OnSuccess
  SkipRedirectURL: true,
  
  // Change the prompt message
  AuthRequestText: {
    title: "Approve this login from your phone",
    subtitle: "A notification is sent to your trusted device to confirm it's you",
    image: "https://yourwebsite.com/image.png",
    titleError: "Something went wrong",
    subtitleError: "We are unable to confirm it's you, please try again",
    imageError: "https://yourwebsite.com/image.png",
    imageSuccess: "https://yourwebsite.com/image.png",
    switchOTPText: "Authenticate with OTP instead"
  },
  
  // Receive error message
  OnError: payload => {
    console.log("ERROR", payload);
  }
};
```

## **Description**

| Field Name                                                                                                         | Description                                                                                                                                                                                                                                                                                                              | Required |
| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
| ApiKeyID                                                                                                           | your `API_KEY_ID`                                                                                                                                                                                                                                                                                                        | Y        |
| [RedirectURL](/sdk-reference/web/web-sdk-verify-email-phone#redirecturl)                                           | The backend URL where we will send a [JSON obj](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization#redirecturl) containing form information and a token                                                                                                                                                | Y        |
| Type                                                                                                               | `"EMAIL"` or `"PHONE"`                                                                                                                                                                                                                                                                                                   | Y        |
| ContainerID                                                                                                        | `id` of the `<div>` where you want to serve Cotter Form.                                                                                                                                                                                                                                                                 | Y        |
| [OnBegin](/sdk-reference/web/web-sdk-verify-email-phone#onbegin)                                                   | A function that will be called before the login process begin. Return UserID and error if applicable. Check the specs for [OnBegin](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization#onbegin) below.                                                                                                 | Y        |
| [SkipRedirectURL](/sdk-reference/web/web-sdk-verify-email-phone/older-sdk/advanced-customization#skip-redirecturl) | `true` or `false` . If set to `true`, then it will skip sending the [JSON obj](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization#redirecturl) to the RedirectURL, and instead automatically pass it to [`OnSuccess`](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization#onsuccess). | N        |
| ButtonBackgroundColor                                                                                              | Button background color. Use HEX format (e.g. `#000000`)                                                                                                                                                                                                                                                                 | N        |
| ButtonTextColor                                                                                                    | Button text color. Use HEX format (e.g. `#FFFFFF`)                                                                                                                                                                                                                                                                       | N        |
| ButtonText                                                                                                         | Button text. Default is "Sign Up Without Password"                                                                                                                                                                                                                                                                       | N        |
| ErrorColor                                                                                                         | Text color for error messages. Use HEX format (e.g. `#FFFFFF`)                                                                                                                                                                                                                                                           | N        |
| EventName                                                                                                          | Tag this authentication event with a name, like `WITHDRAWAL` to make it easier to segment your event logs.                                                                                                                                                                                                               | N        |
| IdentifierField                                                                                                    | Field name for the identifier to include in the JSON obj. For example, if you want to include the phone number in the JSON obj under key `"phone"`, then fill in `IdentifierField = "phone"`                                                                                                                             | Y        |
| AllowOTPFallback                                                                                                   | Allow user to choose to authenticate using OTP (One-Time-Password) instead of Trusted Device if they want.                                                                                                                                                                                                               | N        |
| [OnSuccess](/sdk-reference/web/web-sdk-verify-email-phone#onsuccess)                                               | A function that will be called when the signup process succeed. It's recommended to set your localStorage/cookies here and redirect to your dashboard. Check the specs for [OnSuccess](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization#onsuccess) below                                             | Y        |
| [CountryCode](/sdk-reference/web/web-sdk-verify-email-phone#countrycode)                                           | **An Array** of country code options for the user                                                                                                                                                                                                                                                                        | N        |
| AuthRequestText                                                                                                    | A dictionary to customize the text in the authentication prompt                                                                                                                                                                                                                                                          | N        |
| OnError                                                                                                            | A function that will be called if there's an error, and will receive the error message or object                                                                                                                                                                                                                         | N        |

### **RedirectURL**

We will send a JSON Object to this backend URL. The JSON Object will have the following format:

```javascript
{
  "ID": 2397,
  "CreatedAt": "2020-04-01T05:28:12.540127Z",
  "UpdatedAt": "2020-04-01T05:28:16.599833Z",
  "DeletedAt": null,
  "client_user_id": "<YOUR USER ID>",
  "issuer": "<YOUR API KEY ID>",
  "event": "LOGIN_WEB",
  "ip": "123.347.26.236",
  "location": "San Mateo",
  "timestamp": "1585718892",
  "method": "TRUSTED_DEVICE",
  "new": false,
  "approved": true,
  "signature": "reBafVsmpxO7km5jpRQ3xA..."
}
```

Description:

**payload.client\_user\_id**

Contains your user's `user_id` that you used when registering the user to Cotter. This should be the `user_id` from your backend.

**payload.event**

The event tag that you specified. The default value is `LOGIN_WEB`. You should specify this with a relevant tag, like `WITHDRAW` ,`PAYMENT` ,etc. You can specify it using the `EventName` attribute in the config.

**Validate the event response**

You should validate the event response to make sure it comes from Cotter's server

{% content-ref url="/pages/-M2pWffm56vOPZk07RZ7" %}
[Validating Cotter's Event Response](/older-api/validating-cotters-event-response)
{% endcontent-ref %}

### Skip RedirectURL

If you want to handle Cotter's Event Response in a function instead of sending it to your backend `redirect_url`, you can specify

```javascript
{
  ...
  SkipRedirectURL: true,
}
```

When this is true, the [JSON payload](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization#redirecturl) as specified in RedirectURL section will be passed into your [OnSuccess](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization#onsuccess) function.

### **OnBegin**

This function will be invoked before the Login process. **You are expected to return a `user_id` at the end of the function**. You will receive the following payload as a parameter to your function

**Payload passed into your OnBegin function**

```javascript
var payload = {
  identifier: "+12345678910",
  identifier_type: "PHONE",
  device_type: "BROWSER",
  device_name: "Chrome ..."
};
```

1. You can do a check against the `identifier` here before the form is submitted.
2. You should get the `user_id` based on the identifier in your backend (or use the identifier if that's what you used). This should be the same as the user id used in the [Required Steps.](/sdk-reference/web/web-sdk-passwordless-login#step-2-creating-a-user)

**A. If there is no error:**

```javascript
return {
    userID: "<your-user-id>",
    err: null,
}
```

**B. If there is an error:**

```javascript
return {
    userID: "anything",
    err: "Error message",
}
```

Example:

```javascript
 OnBegin: payload => {
  if (payload.identifier != "+12345678910") {
    return {
      userID: null,
      err: "Phone Number is not allowed",
    }
  }
  // No error, continue submission
  var userID = this.getUserIDFromPhone(payload.identifier);
  return {
    userID: userID,
    err: null,
  };
},
```

### **OnSuccess**

This function will be called **after** the call to `RedirectURL` is successful. Your function will receive the payload returned from `RedirectURL`.

It is recommended to set your sessions or cookies here, and then redirect to another page in your website. For example:

```javascript
OnSuccess: payload => {
  // You can set your own sessions for your website
  window.localStorage.setItem("access_token", payload.access_token);
  window.localStorage.setItem("refresh_token", payload.refresh_token);
  window.location.href = "/dashboard";
};
```

**OnSuccess when SkipRedirectURL is true**

When you set `SkipRedirectURL: true` , `OnSuccess` will instead receive the JSON Event REsponse that was described in RedirectURL section.

### **CountryCode**

The default value is `CountryCode: ["+1"];`

{% hint style="warning" %}
If you specify this, **you have to use an array** even though you only need 1 country code!
{% endhint %}

ex. with 1 country code

```javascript
CountryCode: ["+1"];
```

ex. with 2 country codes

```javascript
CountryCode: ["+62", "+91"];
```

{% hint style="info" %}
Currently, the only available countries are **United States**, **India** and **Indonesia**. Chat us on Intercom if you need other countries, we just need to switch it on.
{% endhint %}

### AllowOTPFallback

If you set this to true, your users will have the option to use OTP to login/authenticate. You should set OTP Fallback as true in your dashboard under Rules.


# Advanced Customization for Pop Up Authentication Prompt

The configuration for the Pop Up Prompt is using the configuration for the Login Form, so you can use the [attributes explained in the Login Form.](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization)

```javascript
var configAuthRequest = {
  // Required config
  ApiKeyID: "<your-api-key-id>",
  Identifier: "<your-user-email-or-phone>",
  IdentifierType: "EMAIL", // or PHONE
  UserID: "<your-user-id>",
  OnSuccess: payload => {
    console.log(payload);
    if (payload.approved) {
      setTimeout(() => {
        console.log("hel");
      }, 3000);
      // window.location.href = "/";
    }
  }
  //------
  
  // Allow OTP fallback for Trusted Device
  AllowOTPFallback: true,
  
  // Styling
  ButtonBackgroundColor: "#000000",
  ButtonTextColor: "#ffffff",
  ErrorColor: "#ff0000",
  AccentColor: "#fff000",
  ButtonText: "Authenticate",
  
  // Change the prompt message
  AuthRequestText: {
    title: "Approve this login from your phone",
    subtitle: "A notification is sent to your trusted device to confirm it's you",
    image: "https://yourwebsite.com/image.png",
    titleError: "Something went wrong",
    subtitleError: "We are unable to confirm it's you, please try again",
    imageError: "https://yourwebsite.com/image.png",
    imageSuccess: "https://yourwebsite.com/image.png",
    switchOTPText: "Authenticate with OTP instead"
  },
  
  // Event Name in the Event Response
  EventName: "TRANSACTION",
};
```

Read the specifications for each of these attributes in the [config for Login Form](/sdk-reference/web/web-sdk-passwordless-login/advanced-customization#description).


# Getting Access Token and Logged-In User Info

{% hint style="warning" %}
The following guide only works with [Cotter's NPM package](https://www.npmjs.com/package/cotter) that can be installed using:

```markup
yarn add cotter

<!-- or -->

npm i cotter --save

<!-- or -->

<script
    src="https://unpkg.com/cotter@0.3.17/dist/cotter.min.js"
    type="text/javascript"
></script>
```

This feature is **not supported** on imports using

```markup
<script src="https://js.cotter.app/lib/cotter.js" type="text/javascript"></script>
```

{% endhint %}

## Get Access Token

After the user Authenticates, the SDK automatically stores the Access Token and Refresh Token for your convenience. To get the access token:

```javascript
var cotter = new Cotter(API_KEY_ID)
cotter.tokenHandler.getAccessToken().then(tok => {
  console.log(tok)
})
```

This function **automatically refreshes the access token if it's expired.** The returned access token would be in the form of a [CotterAccessToken](https://github.com/cotterapp/cotter-token-js#cotteraccesstoken) object which contains both the string and the decoded token.

## Get Logged-in User

After the user Authenticates, the SDK automatically stores the User Information. To get that information:

```javascript
var cotter = new Cotter(API_KEY_ID)
var user = cotter.getLoggedInUser()
console.log(user)
```

## Log Out

To log out and remove these data, do the following:

```javascript
var cotter = new Cotter(API_KEY_ID)
await cotter.logOut()
```

## Update Refresh Token (for Custom Claims)

`version >= 0.3.24`

If you used this [API to get custom claims](/api-reference/oauth-tokens-api/requesting-custom-claims-on-your-access-token), and you received a new `refresh_token` , you can update the refresh token stored in the user's browser by calling this method:

```javascript
var cotter = new Cotter(API_KEY_ID)
const resp = await cotter.tokenHandler.updateTokensWithRefreshToken(newRefreshTok)

console.log(resp)
// resp:
{
   "access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IlNQQUNFX0pXVF9QVUJMSUM6ODAyOEFBQTMtRUMyRC00QkFBLUJFN0EtN0M4MzU5Q0NCOUY5IiwidHlwIjoiSldUIn0.eyJhZGRyZXNzIjoiSEVsbG9vIiwiYWdlIjoxMjUsImF1ZCI6IjQ3Nzc1YTllLWY1OTktNDk5OS1iNmVhLWY5MDEzMTVhZDMxYiIsImF1dGhlbnRpY2F0aW9uX21ldGhvZCI6Ik9UUCIsImNsaWVudF91c2VyX2lkIjoiZWNhZGJkMmMtNTZmOC00MDc4LWI0NWQtZjE3Nzg2ZWQ0OTllIiwiZXhwIjoxNjA0NjMxMzAyLCJpYXQiOjE2MDQ2Mjc3MDIsImlkZW50aWZpZXIiOiJwdXRyaWthcnVuaWFuQGdtYWlsLmNvbSIsImlzcyI6Imh0dHBzOi8vd3d3LmNvdHRlci5hcHAiLCJqdGkiOiIwZjMzNWI0Yi1mYTk5LTRjOWMtOTIwNS1hNmVlYzA3MjA4YzMiLCJuYW1lIjoiUHV0cmkiLCJzY29wZSI6ImFjY2VzcyIsInN1YiI6ImVjYWRiZDJjLTU2ZjgtNDA3OC1iNDVkLWYxNzc4NmVkNDk5ZSIsInR5cGUiOiJhY2Nlc3NfdG9rZW4ifQ.l5jEBYQnbkPQsahBQSmQhOx4DZhto5UIXVTMm3ODcuYTxQOHdWvc2tmlslvUM6huRO5j4d1VCzh9UFpA4zGqTQ",
   "id_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IlNQQUNFX0pXVF9QVUJMSUM6ODAyOEFBQTMtRUMyRC00QkFBLUJFN0EtN0M4MzU5Q0NCOUY5IiwidHlwIjoiSldUIn0.eyJhZGRyZXNzIjoiSEVsbG9vIiwiYWdlIjoxMjUsImF1ZCI6IjQ3Nzc1YTllLWY1OTktNDk5OS1iNmVhLWY5MDEzMTVhZDMxYiIsImF1dGhfdGltZSI6IjE2MDQ2Mjc2OTYiLCJjbGllbnRfdXNlcl9pZCI6ImVjYWRiZDJjLTU2ZjgtNDA3OC1iNDVkLWYxNzc4NmVkNDk5ZSIsImV4cCI6MTYwNDYzMTMwMiwiaWF0IjoxNjA0NjI3NzAyLCJpZGVudGlmaWVyIjoicHV0cmlrYXJ1bmlhbkBnbWFpbC5jb20iLCJpc3MiOiJodHRwczovL3d3dy5jb3R0ZXIuYXBwIiwianRpIjoiMmNmMDE4NTEtNTQyZC00YjZjLTkzZmItZmM5YWUwODQxN2NjIiwibmFtZSI6IlB1dHJpIiwic3ViIjoiZWNhZGJkMmMtNTZmOC00MDc4LWI0NWQtZjE3Nzg2ZWQ0OTllIiwidHlwZSI6ImlkX3Rva2VuIn0.PZmaMOjEySln6ujOLeCwYc8Gn2QX4r6cgvt2DnJPJ67cOEBvOWLBc28wgnGGIFq3PIpIAtkg3z2Sv6PfZSodeQ",
   "refresh_token": "75063:kisZxVuaTy9LrkzmzXTrmtV1dH7ACYJ951GR99fV86n1CTIOMB",
   "expires_in": 3600,
   "token_type": "Bearer",
   "auth_method": "OTP"
}
```

After calling this metho&#x64;**,** it will automatically update all the `access_token`, `id_token` and `refresh_token` with the new value.&#x20;

**This means, when you call `cotter.tokenHandler.getAccessToken()` next, you'll get an access token that has all the custom attributes.**

{% hint style="info" %}
Access tokens and id tokens that are generated using this refresh token and [subsequently returned refresh token](/api-reference/oauth-tokens-api#refresh-token-rotation) will always have the custom claims.

**New Logins** **will not have the custom claims** ​because we don't attach the custom claims to the user itself.
{% endhint %}

## OAuth Token Specification

{% content-ref url="/pages/-M4HUyse-9aPxMqUoSRy" %}
[Cotter's OAuth 2.0 Tokens Specification](/getting-access-token/handling-authentication-with-cotter)
{% endcontent-ref %}


# Sending Successful Form Submission

![](/files/-MYJKReaM-vsWpZagmk7)

If you configured sending successful form submission on the dashboard, and you're using the JS SDK (`cotter` or `cotter-react`), you can add the following to your success function:

{% tabs %}
{% tab title="`cotter` JS SDK" %}

```javascript

import Cotter, { CotterEnum } from 'cotter';

var cotterApiKeyID = "<YOUR_API_KEY_ID>"
var cotter = new Cotter(cotterApiKeyID); // 👈 Specify your API KEY ID here
var formID = "form_default"
cotter
  .withFormID(formID) // Use customization for form "form_default"
  .signInWithLink() // Sign In with Magic Link
  .showPhoneForm() // Send Magic Link via Phone Number
  .then((resp) => {   
  
      // == 
      // RUN SUCCESSFUL FORM SUBMISSION 
      // SETUP THAT YOU HAVE IN THE DASHBOARD 
      // ==
      fetch(`${CotterEnum.WorkerURL}/completion/form?form-id=${encodeURIComponent(formID)}`,{
          method: "POST",
          headers: {
            API_KEY_ID: cotterApiKeyID,
            "Content-type": "application/json",
          },
          body: JSON.stringify(resp),
        }).then((_) => {
            window.location.href = "/protected";// redirect to the protected page
        })       
      // == 
      
  })
  .catch(err => {
    console.log(err) // handle error
  });
```

{% endtab %}

{% tab title="`cotter-react` React SDK" %}

```jsx
import { LoginForm } from "cotter-react";
import { CotterEnum } from 'cotter';
//...

const onSuccess = (resp) => {
  // == 
  // RUN SUCCESSFUL FORM SUBMISSION 
  // SETUP THAT YOU HAVE IN THE DASHBOARD 
  // ==
  fetch(`${CotterEnum.WorkerURL}/completion/form?form-id=${encodeURIComponent(formID)}`,{
      method: "POST",
      headers: {
        API_KEY_ID: cotterApiKeyID,
        "Content-type": "application/json",
      },
      body: JSON.stringify(resp),
    }).then((_) => { 
        window.location.href = "/protected";// redirect to the protected page
    })       
  // == 
}

//...
<LoginForm
  type="EMAIL" // - EMAIL or PHONE
  authMethod="MAGIC_LINK" // - OTP or MAGIC_LINK
  onSuccess={onSuccess} // - A function that runs after the login/signup is successful
  onError={(err) => console.log(err)} // - A function that runs if the login/signup encountered an error
  width={340} // - Width & height of the form
  height={300} //  Recommended at least 300x300
  formID={"form_default"}
/>
```

{% endtab %}
{% endtabs %}


# FAQ & Troubleshooting

### **Why do I get "Cannot read property 'digest' of undefined"?**

Our SDK uses [Web Cryptography API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) to create [SHA-256 digest](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest) for the OAuth 2.0 flow.

According to the spec of `crypto.subtle` (from [MDN web docs](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle#Browser_compatibility)), this feature is available only in [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) (HTTPS), in some or all [supporting browsers](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle#Browser_compatibility).

In most browsers, secure origins are origins that match at least one of the following (scheme, host, port) patterns:

```
(https, *, *)
(wss, *, *)
(*, localhost, *)
(*, 127/8, *)
(*, ::1/128, *)
(file, *, —)
```

If you're running your application from a secure origin, it's possible that your browser doesn't support the Web Crypto API. For a compatibility table, please check <https://caniuse.com/#feat=mdn-api_subtlecrypto>

### **Why does it not show up in incognito?**

Chrome and some other browsers doesn't allow cross-origin cookies by default. Try allowing cookies and it should work. We are currently working on an alternative solution, get updates by [joining our Slack channel](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA).

### **WebAuthn showing an error \[object Object]**

Yes, we are making our error messages better 😉. In the meantime, a possible cause is not enabling cross-origin cookie. Make sure that it's enabled and you don't have an ad-blocker.

### I'm seeing "misconfiguration by the owner"

![](/files/-MEjUQCUMLZu76GWl-Uy)

Usually this can be caused by one of the problems below:

* **You're not using a valid API\_KEY\_ID.** If you're using one of the example CodeSandbox, make sure you paste in your own API key.
* **You are accessing a project from a domain that is not listed in Allowed URLs.** Go to your dashboard > Settings > Allowed URLs and check if your current domain is listed. (If you're seeing "\*", then all domains should be allowed). [Read more here](/protecting-your-account/only-allow-your-website-app-to-use-your-api-key).

### Can I style other components like the input label color?

Yes, you can extend the styling more than what is available on the dashboar&#x64;**.** Check out the [styling guide here](/sdk-reference/web/web-sdk-verify-email-phone/styling).

### Can I send the email from my own domain?

Not as of now. We are looking for a way to allow you to add CNAME configurations to point your domain to us, but it is not yet available. If you need this feature, make a feature request at [our Slack channel](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA).

For now, you can change the **From Name,** like this:

![](/files/-MEjW3ONLKp_jbAPHrfc)

### My login form looks cut-off, what is the correct height for the div container?

Generally you should follow this:

* width: 300px
* height: 300px

If you're adding Social Login, WhatsApp, or Captcha, make the **height 500px.**

### **My login doesn't want to fill up the div container's width**

This can happen if you're using a modal and the modal is not loaded or have `display: none` when you are calling `showEmailForm` or `showPhoneForm` (or `showForm` for older SDK versions). The solution: only show Cotter's form **when the div container and it's parents are loaded with a fixed width and visible display.**

### **I don't receive a verification email, it's automatically showing "Success"**

That is expected! We are an identity provider and this means that whenever a user verifies their email with Cotter, **they don't need to re-verify their email again** when logging-in to a website that uses Cotter. This is the same concept as Sign in with Google, you don't need to re-enter your password when signing in to Medium or Asana if you're already logged-in with Google. [Learn more about how our sign-in works](/features/verify-email-phone).

### How do my users Log Out of Cotter?

![Logging Out of Cotter](/files/-MMHT2xmYbB7bZxYXglK)

There are 2 things you need to do to log out your users:

1. **Log Out from your app**. When the user logs in, the JS SDK automatically stores the access\_token/refresh\_token such that it's always available for the logged-in user. To log out the user from your app, [call the SDK's Log Out](/sdk-reference/web/getting-access-token-and-logged-in-user-info#log-out) function or remove any stored access\_token/refresh\_token from your website or app.
2. **Log Out from Cotter.** Because Cotter is an Identity Provider like Google, after you log out the user from your app following step 1 above, **your user can log back in to your app without being asked to re-verify their email if they were active in the past 30 days.** If you want the user to be asked to re-verify their email, the user must log out from Cotter (equivalent to logging out of their Google Account). To do this, direct your user to <https://js.cotter.app/logout> where they can log out from their account.


# React Native

The easiest way to add Passwordless Login and Email/Phone Number Verification to your React Native app.

## **Complete Guides**

#### **Sign in with Device**

Allow users to login automatically from a device that they trust.

{% content-ref url="/pages/-M2kTuM6W90CT9IpIxHq" %}
[Sign In with Device](/sdk-reference/react-native/react-native-sdk-passwordless-login)
{% endcontent-ref %}

#### Sign in with Email/Phone Number

Authenticate users by sending a Magic Link or OTP via email, SMS, or WhatsApp

{% content-ref url="/pages/-M1dk\_Rosjlkj52Hq\_6R" %}
[Sign In with Email/Phone Number](/sdk-reference/react-native/react-native-sdk-verify-email-phone)
{% endcontent-ref %}

## Quickstart Guides

{% content-ref url="/pages/-M6Eb1vLZRs1\_0ZAMKrR" %}
[React Native – Sign in with Device](/quickstart-guides/react-native-quickstart)
{% endcontent-ref %}


# Installation

Installing Cotter's React Native SDK can be done using yarn or npm.

{% hint style="info" %}
Make sure you're using `react-native` version **< 0.63**
{% endhint %}

## **Install Cotter**

Make sure you are using version >= 0.2.0.&#x20;

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

```
yarn add react-native-cotter
```

{% endtab %}

{% tab title="npm" %}

```
npm install react-native-cotter --save
```

{% endtab %}
{% endtabs %}

**Installing Dependencies for bare React Native project**

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

```
yarn add react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
```

{% endtab %}

{% tab title="npm" %}

```
npm install  --save react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
```

{% endtab %}
{% endtabs %}

### For iOS CocoaPods

```swift
cd ios && pod install && cd ..
```

### For Android

Add the following to your `android/app/build.gradle`

```
android {
  ...
  defaultConfig {
    ...
    missingDimensionStrategy 'react-native-camera', 'general'
    multiDexEnabled true
  }
}
```

**Using React Native >= 0.60**

🎉You're done! Linking the package manually is not required anymore with [Autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md).

## **(Optional) Additional guides for installation**

{% tabs %}
{% tab title="Using React Native < 0.60 " %}

```swift
react-native link
cd ios && pod install && cd ..
```

{% endtab %}

{% tab title="Manual Installation" %}
Cotter's package doesn't need any manual installation. Cotter is dependent on several packages. Please refer to their docs for manual installation.

* [`react-native-inappbrowser-reborn`](https://www.npmjs.com/package/react-native-inappbrowser-reborn#getting-started) &#x20;
* [`react-native-securerandom`](https://www.npmjs.com/package/react-native-securerandom#installation)
* [`react-native-sha256`](https://www.npmjs.com/package/react-native-sha256#installation)
* [`react-native-permissions`](https://github.com/react-native-community/react-native-permissions)&#x20;
* [`react-native-camera`](https://github.com/react-native-community/react-native-camera)&#x20;
* [`react-native-svg`](https://github.com/react-native-community/react-native-svg)
* [`rn-secure-storage`](https://www.npmjs.com/package/rn-secure-storage)
* [`react-native-randombytes`](https://www.npmjs.com/package/react-native-randombytes)&#x20;
  {% endtab %}
  {% endtabs %}


# Sign In with Device

Our React Native SDK offers the easiest way to integrate Cotter's Passwordless Login. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Device**](/features/passwordless-login) works.

### What you're building

![Trusted Devices using Cotter's React Native SDK](/files/-M2kXnFknrHlQ5gjoZar)

## Steps

1. [Import Cotter as a dependency](/sdk-reference/react-native/react-native-sdk-passwordless-login#step-1-import-cotter-as-a-dependency)
2. [Set up Cotter in your Project](/sdk-reference/react-native/react-native-sdk-passwordless-login#step-2-set-up-cotter-in-your-project)
3. [Register the current device as a Trusted Device](/sdk-reference/react-native/react-native-sdk-passwordless-login#step-4-register-this-device-as-a-trusted-device)
4. [Authenticate from a Trusted Device and Non-Trusted Device](/sdk-reference/react-native/react-native-sdk-passwordless-login#step-5-authenticate-from-a-trusted-device-and-non-trusted-device)

### Step 1: Import Cotter as a dependency

{% hint style="info" %}
Make sure you're using `react-native` version **< 0.63**
{% endhint %}

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

```java
yarn add react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}
{% endtabs %}

*(Optional) Checkout additional steps* [*for Android, React Native < 0.60, and Manual Installation*](/sdk-reference/react-native/installation)*.*

### Step 2: Set up Cotter in your Project

To allow Cotter to display modals, **wrap your root component with `connectCotterWrapper` :**

```javascript
import {connectCotterWrapper} from 'react-native-cotter';

class MyApp extends Component {
}

MyApp = connectCotterWrapper(MyApp);
```

### Step 3: Register user and trust this device

This method `signUpWithDevice` will register the user based on the user's `identifier` to Cotter and then trust the current device.&#x20;

```javascript
import { Cotter } from "react-native-cotter";

class SignUp extends Component {
  ...
  register = (identifier) => {
    // Signup the user and trust this device
    var cotter = new Cotter(API_KEY_ID);
    cotter.signUpWithDevice(
      identifier, // User's email, phone or username
      this.onSuccess,
      this.onError,
    );
  };

  onSuccess = resp => {
    alert('Success');
    console.log(resp);
  };
  
  onError = err => {
    alert('Error');
    console.log(err);
  };
  ...
}
```

### Step 4: Authenticate from a Trusted Device and Non-Trusted Device

To request an authentication from Cotter's SDK, you would need to call `cotter.signInWithDevice` . **This will automatically detect whether the current device is a Trusted Device or not.**

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

```javascript
// Request authentication
var cotter = new Cotter(API_KEY_ID);
cotter.signInWithDevice(
  identifier, // User's email, phone or username
  this.onSuccess,
  this.onError,
);
```

{% endtab %}

{% tab title="JSON Response" %}

```javascript
{
  // OAuth 2.0 Tokens
  "oauth_token": { 
    "access_token": "eyJhbGciOiJFUzI1N...",
    "auth_method": "TRUSTED_DEVICE",
    "expires_in": 3600,
    "id_token": "eyJhbGciOiJFUzI...",
    "refresh_token": "33625:anGsIfvFd...",
    "token_type": "Bearer"
  },
  // Information about the login request
  "ID": 2535926,
  "CreatedAt": "2020-08-01T01:36:24.321921222Z",
  "DeletedAt": "0001-01-01T00:00:00Z",
  "UpdatedAt": "2020-08-01T01:36:24.321921425Z",
  "approved": true, // This login request is approved
  "client_user_id": "abcdefgh-d15c-466f-aaa3-f029a8e534c3",
  "event": "LOGIN",
  "ip": "172.92.5.201",
  "issuer": "<YOUR API KEY ID>",
  "location": "San Francisco",
  "method": "TRUSTED_DEVICE",
  "new": false,
  "signature": "L6x8sLHyPTOMCxudw34YTFGCEO4dGvakJl0g9dIDjUp2gaXbD7Yfxo86Dr7OEtHYmSYegSJkwmZjMzDnPvltDQ==",
  "timestamp": "1596245784",
  "user_id": "00000000-0000-0000-0000-000000000000"
}
```

{% endtab %}
{% endtabs %}

Cotter's SDK will find the user from `userEmail` , this must be the same as the one used to register the user during sign up.

This function returns `oauth_token` , including an `access_token` that you should [validate in your backend](/getting-access-token/verifying-jwt-tokens). You can either:

* use this `access_token` to protect all of your API endpoints, or
* you can use your own session tokens. You'll need to validate this `access_token` before you generate your session tokens in the backend.

{% hint style="info" %}
Cotter's SDK automatically stores the OAuth tokens for you. Check out how to [get access tokens and logged-in user information](/sdk-reference/react-native/getting-stored-oauth-tokens-and-user-information).
{% endhint %}

#### Trusted and Non-Trusted Device

When an Authentication Event is requested using method `TRUSTED_DEVICE`, there are 2 possible cases:

#### Case 1: The current device is a Trusted Device

If the current device is a Trusted Device, it should automatically be approved, and you will receive an access token.

```java
{
  ...
  "method": "TRUSTED_DEVICE",
  "new": false, // Is this a new pending event (should be false).
  "approved": true // Is this event approved (should be true).
  "signature": "oonMGCAxp3..." // Signature to make sure this event comes from Cotter's server
}
```

You should see a result that the event is **not new**, and that it's **approved**. You'll receive the `oauth_token` because the user is successfully authenticated, and you can pass this to your backend to authorize access for the user.

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

#### Case 2: The current device is NOT a Trusted Device

We'll cover this in the next guide:

{% content-ref url="/pages/-M2pWufvpIxKQcm3t4RP" %}
[Authenticate from a Non-Trusted Device](/sdk-reference/react-native/react-native-sdk-passwordless-login/authenticate-from-a-non-trusted-device)
{% endcontent-ref %}

## 🎉 You're done!


# Add Email/Phone Verification

{% hint style="info" %}
Available from version > `react-native-cotter@0.3.6`
{% endhint %}

Most of the time, it's required for you to check if the user's identifier (their email or phone number) is valid. With Cotter, you can verify your users via email, SMS, or WhatsApp.

## Step 1: Set Up Deep Linking

The verification will follow OAuth's PKCE flow which will open an in-app browser where your user can enter the OTP sent to their email/phone.

Pick a unique URL scheme for redirecting the user back to your app after the verification in the in-app browser is successful. For this example, we'll use `myexample://auth_callback` .

**Follow the guide under Sign in with Email/Phone –** [**Step 3: Setting up Deep Linking**](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-3-setup-deep-linking)**,** then come back to this step.

## Step 2: Update your Sign Up Function

Make sure you followed the guide for [Sign In with Device](/sdk-reference/react-native/react-native-sdk-passwordless-login). Update your [Sign Up Function that you made on Step 3](/sdk-reference/react-native/react-native-sdk-passwordless-login#step-3-register-user-and-trust-this-device).

### Verifying Before Registering the User

**Verify the email/phone number first**, then continue registering the user if verification is successful

This will work the following way:

1. The user will be asked to verify their email or phone number
2. If verification successful, we'll create a new User
3. Then, set up the current device as trusted.

**If the user already exists but doesn't have a Trusted Device** set up (for example from logging-in to your website):

1. The user will be asked to verify their email or phone number
2. Then if verification successful, set up the current device as trusted.

```javascript
import { Cotter } from "react-native-cotter";

class SignUp extends Component {
  ...
  // Remove your `register` function, and update to the following
  // signupOrLogin function.
  // This function handles both Sign Up and Login
  
  const signupOrLogin = async () => {
    let cotter = new Cotter(API_KEY_ID);

    try {
      // Fetch user using the User API
      // identifier = user's email
      const user = await cotter.getUserByIdentifier(identifier);
      console.log(user);

      if (validUUID(user.ID) && user.enrolled.includes('TRUSTED_DEVICE')) {
        // ========================
        //      Existing User
        // ========================
        // A) User already have a trusted device
        // proceed with SignInWithDevice
        cotter.signInWithDevice(
          identifier,
          resp => console.log('Success', resp),
          errs => console.log('Error', errs),
        );
      } else {
        // ===========================================================
        //      New User OR Existing but No Trusted Device
        // ===========================================================
        // B) User doesn't exists OR exists doesn't have a trusted device
        // 1️⃣ Verify Email
        await cotter.signInWithEmailLink(
          'myexample://auth_callback',
          resp => {
            // 2️⃣  Proceed with registering this device as a trusted device
            registerDeviceAsTrusted();
          },
          err => alert('Error', err),
          {email: identifier},
        );
      }
    } catch (e) {
      alert('Error', err);
    }
  };
  
  const registerDeviceAsTrusted = async () => {
    let cotter = new Cotter(API_KEY_ID);
    const user = await cotter.getLoggedInUser();
    user.registerDevice(
      resp => console.log('Success', resp),
      err => console.log('Error', err),
    );
  };
  ...
}
```


# Authenticate from a Non-Trusted Device

## Steps

There are 2 steps in authenticating a Non-Trusted Device

1. [Make a request](/sdk-reference/android/older-sdk-version/android-sdk/old-authenticate-from-a-non-trusted-device#step-1-making-an-authentication-request) from the Non-Trusted Device and wait for approval. The SDK will automatically show a prompt asking the user to approve this request from the Trusted Device.
2. [Approve the request](/sdk-reference/android/older-sdk-version/android-sdk/old-authenticate-from-a-non-trusted-device#step-2-how-to-approve-a-pending-request-from-a-trusted-device) from a Trusted Device. The user should tap a button to **check if there's a login request.** They can then **approve or reject** the login request.
3. When the login request is approved, the Non-Trusted Device will receive `oauth_tokens` . You should validate these tokens in your backend.

## Step 1: Make an Authentication Request

![Requesting Login from a Non-Trusted Device](/files/-MDc9F-J-GWdDSrrfo-z)

Same as how it works with [Authenticating from a Trusted Device](/sdk-reference/react-native/react-native-sdk-passwordless-login#step-6-authenticate-from-a-trusted-device-or-non-trusted-device), to authenticate a device, call `cotter.signInWithDevice` . **This will automatically detect whether the current device is a Trusted Device or not.**

```java
// 1️⃣ Request trusted device authentication
var cotter = new Cotter(API_KEY_ID);
cotter.signInWithDevice(
  identifier,  // User's email, phone or username
  this.onRequestSuccess,
  this.onRequestError,
);
```

When an Authentication Event is requested using method `TRUSTED_DEVICE`, there are 2 possible cases:

### Case 1: The current device is a Trusted Device

You will be automatically approved if the request is coming from a Trusted Device. This is covered in [Authenticate from a Trusted Device](/sdk-reference/react-native/react-native-sdk-passwordless-login#case-1-the-current-device-is-a-trusted-device).

### Case 2: The current device is NOT a Trusted Device

If the current device is **not** a Trusted Device, then it will create a **pending event.** This pending event will need to be **approved by a Trusted Device**.&#x20;

## Step 2: How to approve a pending request from a Trusted Device

![Press a button to approve a login request from the Trusted Device](/files/-MDc9P7EfBfYlPg6CrWG)

To check if there's a pending event for this user, you can call the `getNewEvent` method from a Trusted Device.

```javascript
approveLogin = async () => {
    let cotter = new Cotter(API_KEY_ID);
    try {
      let user = await cotter.getLoggedInUser();
      await user.checkNewSignInRequest();
    } catch (err) {
      console.log(err);
    }
}

...
<Button 
  onPress={this.approveLogin} 
  title="Approve Login"
/>
```

{% hint style="info" %}
**In the future,** your app that is installed in a Trusted Device will receive a notification and this method is invoked when the notification is opened.
{% endhint %}

**For now,** you should **tell your user to press a button and invoke this method** when they try to login from a non-trusted device.

The SDK will open a prompt in the Trusted Device asking if the user want to approve or reject the authentication request

![Prompt to approve request from a Trusted Device](/files/-M17zwpUdHmduY0qosRm)

In the prompt in the user's Non-Trusted Device (right picture), you can [Customize the Prompt](/sdk-reference/react-native/older-sdk-versions/sign-in-with-device/customization) to ask them to press a specific button in the Trusted Device to invoke the prompt on the left.

## Step 3: Get the access token and validate it in your back end

![When approved, you'll receive the JSON response](/files/-MDc9ZeXYMnwb3rADgRh)

When the user approved the login request in the Trusted Device, your `onSuccess` function in the authentication request in [step 1](/sdk-reference/react-native/react-native-sdk-passwordless-login/authenticate-from-a-non-trusted-device#step-1-make-an-authentication-request) will be invoked and will receive a JSON response with an access token:

{% tabs %}
{% tab title="JSON Response" %}

```javascript
{
  // OAuth 2.0 Tokens
  "oauth_token": { 
    "access_token": "eyJhbGciOiJFUzI1N...",
    "auth_method": "TRUSTED_DEVICE",
    "expires_in": 3600,
    "id_token": "eyJhbGciOiJFUzI...",
    "refresh_token": "33625:anGsIfvFd...",
    "token_type": "Bearer"
  },
  // Information about the login request
  "ID": 2535926,
  "CreatedAt": "2020-08-01T01:36:24.321921222Z",
  "DeletedAt": "0001-01-01T00:00:00Z",
  "UpdatedAt": "2020-08-01T01:36:24.321921425Z",
  "approved": true, // This login request is approved
  "client_user_id": "abcdefgh-d15c-466f-aaa3-f029a8e534c3",
  "event": "LOGIN",
  "ip": "172.92.5.201",
  "issuer": "<YOUR API KEY ID>",
  "location": "San Francisco",
  "method": "TRUSTED_DEVICE",
  "new": false,
  "signature": "L6x8sLHyPTOMCxudw34YTFGCEO4dGvakJl0g9dIDjUp2gaXbD7Yfxo86Dr7OEtHYmSYegSJkwmZjMzDnPvltDQ==",
  "timestamp": "1596245784",
  "user_id": "00000000-0000-0000-0000-000000000000"
}
```

{% endtab %}
{% endtabs %}

You should pass the `access_token` to your backend when logging-in users, approving transactions, etc. Learn how to validate the access token:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}


# Add a new Trusted Device

## Steps

![Adding a New Trusted Device](/files/-M4uoteieJmjnucL2sPA)

There are 3 steps that's needed to add a new Trusted Device:

1. Check if this device is a Trusted Device
2. [Show the QR Code](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device#step-1-show-the-qr-code-of-the-new-device) of the New Non-Trusted Device
3. [Scan the New Device's QR Code](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device#step-2-scanning-the-qr-code-from-a-trusted-device) using the Trusted Device

## Step 1:  Check if this device is a Trusted Device

```javascript
checkIfThisDeviceTrusted = async () => {
  var cotter = new Cotter(API_KEY_ID);
  try {
      let user = await cotter.getLoggedInUser();
      var trusted = await user.isThisDeviceTrusted();
    } catch (err) {
      console.log(err);
    }
};
```

## Step 2: Show the QR Code of the New Device

Show a QR Code in the **New Device**, this will be scanned to register this new device. To show the QR Code of the new device:

```javascript
trustThisDevice = async () => {
  var cotter = new Cotter(API_KEY_ID);
  try {
    let user = await cotter.getLoggedInUser();
    user.trustThisDevice(onSuccessTrust, onErrorTrust);
  } catch (e) {
    console.log(e);
  }
};
onSuccessTrust = () => {
  alert('Success');
};
onErrorTrust = (errmsg, err) => {
  alert(errmsg);
};

...
<Button onPress={this.trustThisDevice} title="Make Device a Trusted Device" />
```

This will open a modal with a QR Code that can be scanned from the Trusted Device.&#x20;

## Step 3: Scan the QR Code from a Trusted Device

The Trusted Device should scan the QR Code shown in the new device.

### Setup Permission to Allow Camera

Update your `Info.plist` in `ios/YourProject/Info.plist`

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

  <!-- Add Camera Permission -->
  
  <key>NSCameraUsageDescription</key>
  <string>YOUR TEXT</string>

  <!-- … -->

</dict>
</plist>
```

#### Android

Add the permission below to your app `android/app/src/main/AndroidManifest.xml` file:

```markup
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.myawesomeapp">

  <!-- Add Camera Permission -->
  <uses-permission android:name="android.permission.CAMERA" />
  <!-- … -->

</manifest>
```

### Scan the QR Code

To open the Scanner modal:

```java
scanQRCode = async () => {
  var cotter = new Cotter(API_KEY_ID);
  try {
    let user = await cotter.getLoggedInUser();
    user.scanQRCode();
  } catch (e) {
    console.log(e);
  }
};
...
<Button onPress={this.scanQRCode} title="Add New Device" />
```

This will open a modal where the user can scan the QR Code displayed on the new device.

{% hint style="warning" %}
If you get the error **"Cannot choose between the following variants of project :react-native-camera"**:

Add the following to your`android/app/build.gradle`

```
android {
  ...
  defaultConfig {
    ...
    missingDimensionStrategy 'react-native-camera', 'general' <-- insert this line
  }
}
```

{% endhint %}

{% hint style="warning" %}
If you get the error **"Execution failed for task ':app:mergeDexDebug'"**

Add the following to your`android/app/build.gradle`

```
android {
  ...
  defaultConfig {
    ...
    multiDexEnabled true     <-- insert this line
  }
}
```

{% endhint %}

{% hint style="danger" %}
&#x20;**If you encounter the error Invalid RNPermission X. Should be one of: ()**

1. Clean up Xcode stale data with `npx react-native-clean-project --remove-iOS-build --remove-iOS-pods`
   {% endhint %}


# Remove Trusted Device

You can remove the current device from being a trusted device.

{% hint style="info" %}
**In the future,** you can also revoke other trusted devices from any trusted device.
{% endhint %}

### Removing this Trusted Device

To remove the current device from being a trusted device, do the following:

```java
removeDevice = async () => {
  var cotter = new Cotter(API_KEY_ID);
  try {
    let user = await cotter.getLoggedInUser();
    var resp = await user.removeDevice();
    console.log(resp);
  } catch (err) {
    console.log(err);
  }
};
```

{% hint style="info" %}
You cannot remove the last Trusted Device. You will need to have 1 remaining Trusted Device.
{% endhint %}


# Sign In with Email/Phone Number

Our React Native SDK offers the easiest way to integrate Cotter 's email/phone verification. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Email/Phone Number**](/features/verify-email-phone) works.

### Overview

Verifying **email and phone number** in your mobile app using our React Native SDK consists of the following steps:

1. Call Cotter's Login function
2. Setup deep linking
3. Receive user's email or phone number, and whether or not it's verified

### What you're building

![Cotter's React Native SDK on Android and iOS](/files/-M1dlHYUGE23fmcVeogG)

## Steps

1. [Import Cotter as a dependency](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-1-import-cotter-as-a-dependency)
2. [Sign Up or Login with Email or Phone](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-2-signing-up-or-logging-in)
3. [Setup deep linking](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-3-setup-deep-linking): Cotter's authentication will redirect back to your application using a URL scheme.
4. [Receive the Token](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-4-receiving-the-token-in-onsuccess-or-onerror): Include the returned OAuth token and email/phone number in your server

### Step 1: Import Cotter as a dependency

{% hint style="info" %}
Make sure you're using `react-native` version **< 0.63**
{% endhint %}

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

```java
yarn add react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}
{% endtabs %}

*(Optional) Checkout additional steps* [*for Android, React Native < 0.60, and Manual Installation*](/sdk-reference/react-native/installation)*.*&#x20;

### Step 2: Signing Up or Logging In

#### Sign Up: Use the sign up method to:

* Verify the user's email or phone number
* Then create a new user in Cotter if successful
* **If you passed-in the email/phone into the function**: if your user already exists, **it will return an error "User already exists"**
* **If you does not pass in the email/phone into the function:** users can enter their email/phone in the pop-up browser, but it will NOT check if the user already exists. It will behave like the Log In method below.

> **Tip:** Use the "Sign Up" method and pass in the user's email/phone to differentiate new and existing user. You can have an input text and collect the user's email/phone.

#### Log In: Use the login method to:

* To authenticate a user based on their email.
* If the user doesn't exist, this method **will automatically create a new user.**

> **Tip:** Use the "Log In" method to login or register user on the same page

{% tabs %}
{% tab title="Using Email" %}

```javascript
import { Cotter } from 'react-native-cotter';

let cotter = new Cotter(API_KEY_ID);   // your API_KEY_ID
await cotter.signUpWithEmailLink(      // use Email & Magic Link
  'myexample://auth_callback',         // (setup later) URL Scheme for deep linking
  (resp) => {console.log(resp)},       // Success Callback Function
  (err) => {console.log(err)}, // Error Callback Function
  {email: this.state.email},           // (Optional) , if you leave this blank, user can enter email in the in-app browser
);
```

#### Sign Up

* **Magic Link:** use `cotter.signUpWithEmailLink`
* **OTP:** use `cotter.signUpWithEmailOTP`

#### Sign In

* **Magic Link:** use `cotter.signInWithEmailLink`
* **OTP:** use `cotter.signInWithEmailOTP`

**If no email is specified**, the user can enter the email in the in-app browser
{% endtab %}

{% tab title="Using Phone" %}

```javascript
import { Cotter } from 'react-native-cotter';

let cotter = new Cotter(API_KEY_ID);            // your API_KEY_ID
await cotter.signUpWithPhoneLink(               // use Phone & Magic Link
  'myexample://auth_callback',                  // (setup later) URL Scheme for deep linking
  (resp) => {console.log(resp)},                // Success Callback Function
  (err) => {console.log(err)},          // Error Callback Function
  {phone: this.state.phone, channel: "SMS" },   // (Optional), if you leave this blank, user can enter email in the in-app browser
);
```

#### **Sign Up**

* **Magic Link:** use `cotter.signUpWithPhoneLink`
* **OTP:** use `cotter.signUpWithPhoneOTP`

#### **Sign In**

* **Magic Link:** use `cotter.signInWithPhoneLink`
* **OTP:** use `cotter.signInWithPhoneOTP`

#### Channels: (default to "SMS" if phone is specified)

* To use SMS: `{phone: this.state.phone, channel: "SMS"}`
* To use WhatsApp: `{phone: this.state.phone, channel: "WHATSAPP"}`

**If no phone number is specified**, the user can enter the phone number in the in-app browser and there will be buttons for WhatsApp and SMS available (based on your settings in the Dashboard > Branding).

{% hint style="info" %}
To send code/link via SMS or WhatsApp, you'll need to add some balance to you project in the [Dashboard](https://dev.cotter.app/).
{% endhint %}
{% endtab %}
{% endtabs %}

{% hint style="success" %}
**Try this now!** You should see an in-app browser looking like the image below popping up and ask you to authenticate.
{% endhint %}

![](/files/-M1doM-5Nwjmc8sO7YxE)

### Step 3: Setup Deep Linking

Pick a unique URL scheme for redirecting the user back to your app after the verification in the in-app browser is successful. For this example, we'll use `myexample://auth_callback` .

{% hint style="warning" %}
Make sure your URL scheme (the front part before `://`) doesn't have an underscore or other special characters. To test it out, enter your Redirect URL here: <https://jsfiddle.net/omd02jn5/>
{% endhint %}

### Setup in Android

Add this in your [android/app/src/main/**AndroidManifest.xml**](https://github.com/cotterapp/react-native-cotter/blob/9bc206e3b5c815a5ded65627e8bb172db293f83d/examples/android/app/src/main/AndroidManifest.xml#L23)

```markup
<activity
    android:name=".MainActivity"
    android:launchMode="singleTask"> <!-- Make launchMode to singleTask --> 
    
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <!--    Setup Deep Linking Here    -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- This is for myexample://auth_callback -->
        <!-- 👇 Change this to your own URL scheme -->
        <data android:scheme="myexample" android:host="auth_callback"/>
    </intent-filter>
    <!--  end -->
    
</activity>
```

### Setup in iOS

Add this in your [ios/\<YourAppName>/Info.plist](https://github.com/cotterapp/react-native-cotter/blob/master/examples/ios/examples/Info.plist#L26)

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

	<!-- ADD THE LINES FROM HERE -->
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLName</key>
			<string>myexample</string> <!-- 👈 Change this to your own URL Scheme -->
			<key>CFBundleURLSchemes</key>
			<array>
				<string>myexample</string> <!-- 👈 Change this to your own URL Scheme -->
			</array>
		</dict>
	</array>
	<!-- TO HERE -->

	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
```

If you're targeting iOS 9.x or newer, add the following lines to [YourApp/ios/YourApp/AppDelegate.m](https://github.com/proyecto26/react-native-inappbrowser/blob/master/example/ios/example/AppDelegate.m):

{% tabs %}
{% tab title="iOS 9.x or newer" %}

```swift
// Add the header at the top of the file:
#import <React/RCTLinkingManager.h>

// Add this above `@end`:
- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}
```

{% endtab %}
{% endtabs %}

If you're targeting iOS 8.x or older, you can use the following code instead, add the following lines to [YourApp/ios/YourApp/AppDelegate.m](https://github.com/proyecto26/react-native-inappbrowser/blob/master/example/ios/example/AppDelegate.m):

```swift
// Add the header at the top of the file:
#import <React/RCTLinkingManager.h>

// Add this above `@end`:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  return [RCTLinkingManager application:application openURL:url
                      sourceApplication:sourceApplication annotation:annotation];
}
```

If your app is using Universal Links, you'll need to add the following code as well, add the following lines to [YourApp/ios/YourApp/AppDelegate.m](https://github.com/proyecto26/react-native-inappbrowser/blob/master/example/ios/example/AppDelegate.m):

```swift
// Add this above `@end`:
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
 restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
 return [RCTLinkingManager application:application
                  continueUserActivity:userActivity
                    restorationHandler:restorationHandler];
}
```

### Setup in React Native Project

{% tabs %}
{% tab title="React Navigation" %}
In your App.js

```javascript
import { createStackNavigator } from 'react-navigation'

// Import Cotter's Loading Page
import { LoadingPage } from 'react-native-cotter';
 
const Main = createStackNavigator(
  {
    ...
    Register: { screen: Register },
    
    // Redirect users Cotter's Handler page
    CotterLoadingVerify: { 
      screen: LoadingPage, // Use Cotter's Loading Page
      path: 'auth_callback' // Enable Deep linking redirection
    },
    
  }
)
```

If you have Nested Stack Navigator, add `path: ''` to every parent stack.

```javascript
// TARGET STACK
const RegisterStack = createStackNavigator(
  {
    Register: { screen: Register },
    CotterLoadingVerify: {
      screen: LoadingPage, // Use Cotter's Loading Page
      path: 'auth_callback', // Enable Deep linking redirection
    },
  }
);

// PARENT 1
const RootStack = createStackNavigator(
  {
    // ADD path: '' here
    RegisterStack: { screen: RegisterStack, path: '' },
    ...
  }
);

// PARENT of PARENT 1
const RootSwitch = createSwitchNavigator(
  {
    Splash: { screen: Splash },
    // ADD path: '' here
    RootStack: { screen: RootStack, path: '' },
  }
);
```

{% endtab %}

{% tab title="React Navigation 5 or above" %}
If you're using the [newer version of React Navigation](https://reactnavigation.org/docs/deep-linking#deep-link-integration), copy paste the code below to your App.js or index.js.

**In your App.js or index.js**

```javascript
import { NavigationContainer, useLinking } from '@react-navigation/native';

function App() {
  const ref = React.useRef();

  const {getInitialState} = useLinking(ref, {
    prefixes: ['https://myexample.cotter.app', 'myexample://'],
    config: {
      CotterLoadingVerify: 'auth_callback', // ADD THIS TO REDIRECT TO COTTER'S HANDLER PAGE
    },
  });

  const [isReady, setIsReady] = React.useState(false);
  const [initialState, setInitialState] = React.useState();

  React.useEffect(() => {
    Promise.race([
      getInitialState(),
      new Promise(resolve =>
        // Timeout in 150ms if `getInitialState` doesn't resolve
        // Workaround for https://github.com/facebook/react-native/issues/25675
        setTimeout(resolve, 150)
      ),
    ])
      .catch(e => {
        console.error(e);
      })
      .then(state => {
        if (state !== undefined) {
          setInitialState(state);
        }

        setIsReady(true);
      });
  }, [getInitialState]);

  if (!isReady) {
    return null;
  }

  return (
    <NavigationContainer initialState={initialState} ref={ref}>
      {/* content 👈 */}
    </NavigationContainer>
  );
}
```

**In your Router**

```javascript
import {LoadingPage} from 'react-native-cotter';

function Router() {
  return (
    <Stack.Navigator>
      ...
      
      // Add CotterLoadingVerify page
      <Stack.Screen
        name="CotterLoadingVerify"
        component={LoadingPage}
        options={{headerShown: false}}
      />
      
    </Stack.Navigator>
  );
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Remember to make sure your [callbackURL in Step 2 ](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-2-call-cotters-verify-function)is correct.
{% endhint %}

{% hint style="success" %}
**Try it again!** You should see the in-app browser redirecting back after you've successfully verified.
{% endhint %}

### Step 4: Receiving the Token in onSuccess or onError

#### onError

The `onError` function that you pass in will receive 2 parameters: `errorMessage` (string) and `errorResponse` (object). The `errorResponse` is an http response from attempt to verify the user's email/phone in Cotter's server.

#### onSuccess

The `onSuccess` function that you pass in will receive a `response` object that looks like this:

```javascript
{
  "identifier": {
    "ID": "f4286df9-a923-429c-bc33-5089ffed5f68",
    "created_at": "2020-07-21T22:53:21.211367Z",
    "updated_at": "2020-07-21T22:53:21.211367Z",
    "deleted_at": "0001-01-01T00:00:00Z",
    "identifier": "putri@cotter.app", // User's email
    "identifier_type": "EMAIL",
    "device_type": "BROWSER",
    "device_name": "Mozilla/5.0 (Linux; Android 9; Android SDK built for x86 Build/PSR1.180720.075) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
    "expiry": "2020-08-20T22:53:21.19705Z",
    "timestamp": "2020-07-21T22:53:21.19705Z"
  },
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUz...", // Validate this access token
    "id_token": "eyJhbGciOiJFUzI1...",
    "refresh_token": "27944:lb31DY5pG229n...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "OTP"
  },
  "token": {...},
  "user": {
    "ID": "643a42c7-316a-4abe-b27e-f4d0f903bfea", // [Deprecated] Cotter uesr ID
    "identifier": "putri@cotter.app",
    ...
  }
}
```

{% hint style="warning" %}
Please use the identifier (email/phone number) as your main way to identify users, **user.ID is deprecated.**
{% endhint %}

This JSON object contains 3 objects, `identifier` , `oauth_token` and `user` .&#x20;

* The identifier object contains information about the user's email or phone number, device type and name, and expiry.
* The `oauth_token` contains an `access_token` that you can validate in your backend.
* The `user` contains the User object in Cotter, which includes a "Cotter User ID". **You should associate your user with this Cotter User ID for reference.**

{% hint style="success" %}
You should include this JSON Object into your call to your backend for **Login** or **Registration**. Your backend should then verify that [the access token is valid.](/getting-access-token/verifying-jwt-tokens)
{% endhint %}

## Validating Cotter's Access Token&#x20;

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

## 🎉 You're done!

## Securing your Project

Since you'll be using your API Key from a front-end website or mobile app, your `API_KEY_ID` is exposed to anyone inspecting your code. Here are some ways to prevent abuse:

* [Only allow your website/app to use your API Key](/protecting-your-account/only-allow-your-website-app-to-use-your-api-key)
* [Rate Limit the number of authentication requests](/protecting-your-account/rate-limit)
* [Enable reCAPTCHA to prevent automated abuse](/protecting-your-account/enable-recaptcha-to-protect-against-automated-abuse)


# Getting Stored OAuth Tokens and User Information

Cotter's SDK automatically stores the returned `access_token`, `id_token` , `refresh_token` and user information in the device's secure storage when the user successfully authenticates.

## Get Access Token

This function automatically refreshes the access token when needed.

```javascript
const getAccessToken = async () => {
  var cotter = new Cotter(API_KEY_ID);
  try {
    var accessToken = await cotter.tokenHandler.getAccessToken();
    console.log('Access Token', accessToken);
  } catch (err) {
    console.log('Access Token Error', err);
  }
};
```

[Check out how the Access Token Object looks like.](https://github.com/cotterapp/cotter-token-js#usage-with-cotter)

## Get ID Token

This function automatically refreshes the id token when needed.

```javascript
const getIDToken = async () => {
  var cotter = new Cotter(API_KEY_ID);
  try {
    var idToken = await cotter.tokenHandler.getIDToken();
    console.log('ID Token', idToken);
  } catch (err) {
    console.log('ID Token Error', err);
  }
};
```

[Check out how the ID Token Object looks like.](https://github.com/cotterapp/cotter-token-js#usage-with-cotter)

## Get Logged-in User

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

```javascript
const getLoggedInUser = async () => {
    var cotter = new Cotter(API_KEY_ID);
    try {
        let user = await cotter.getLoggedInUser();
    } catch(err) {
        console.log('Get user error', err);
    }
};
```

{% endtab %}

{% tab title="User Object" %}

```typescript
class User {
  ID: string; // Cotter User ID
  issuer: string;
  client_user_id: string;
  enrolled: string[];
  identifier: string;
}
```

{% endtab %}
{% endtabs %}

## Log Out

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

```javascript
const logOut = async () => {
  var cotter = new Cotter(API_KEY_ID);
  try {
    await cotter.logOut();
  } catch (err) {
    console.log(err);
  }
};
```

{% endtab %}
{% endtabs %}

## OAuth Token Specification

{% content-ref url="/pages/-M4HUyse-9aPxMqUoSRy" %}
[Cotter's OAuth 2.0 Tokens Specification](/getting-access-token/handling-authentication-with-cotter)
{% endcontent-ref %}


# FAQ

### **Why do I get an error saying "user already exist" when calling `cotter.signUpWithDevice` or `cotter.trustedDevice.enrollDevice` ?**

This function does 2 things: register a new user to Cotter and enroll the current device as a trusted device. Calling **`signUpWithDevice`** or `enrollDevice` for a user ID that already exist in Cotter will result in the error "user already exist".

For example, this can happen when you:

* Call cotter.trustedDevice.enrollDevice from an already trusted device.
* Call cotter.trustedDevice.enrollDevice from a device B, when device A has already been registered as a trusted device.

Also note that **`signUpWithDevice`**  & **`enrollDevice` only works on the user's first ever trusted device**. For additional trusted devices, you have to use the **Add New Trusted Device** method using the QR code.

### **What happens if you call `cotter.trustedDevice.getNewEvent` with a non-trusted device?**

Nothing happens. `getNewEvent` fetches a new un-approved authentication request that are generated when the user tries to login from a non-trusted device. However, since you're calling `getNewEvent` from a non-trusted device, this device cannot approve the request.

### **What happens if you call `signInWithDevice` or`cotter.trustedDevice.reqAuth` when you have not registered the user with Cotter yet?**

Will get errMsg: “Something went wrong”. You need a user that is registered to Cotter to be able to use any trusted device functionalities, except **`signUpWithDevice`**  & **`enrollDevice`** (because that function will also register a new user to Cotter for you).

### **What happens if you call `user.removeDevice` or`cotter.trustedDevice.removeDevice` from a non-trusted device?**

Will get err: “This device is not a trusted device.”


# Older SDK Versions


# Sign in with Email/Phone

Our React Native SDK offers the easiest way to integrate Cotter 's email/phone verification. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Email/Phone Number**](/features/verify-email-phone) works.

### Overview

Verifying **email and phone number** in your mobile app using our React Native SDK consists of the following steps:

1. Call Cotter's Login function
2. Setup deep linking
3. Receive user's email or phone number, and whether or not it's verified

### What you're building

![Cotter's React Native SDK on Android and iOS](/files/-M1dlHYUGE23fmcVeogG)

## Steps

1. [Import Cotter as a dependency](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-1-import-cotter-as-a-dependency)
2. [Call Cotter's Verify function](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-2-call-cotters-verify-function): This function will handle the WebView, verifying phone number or email, and request the identity from Cotter's server.
3. [Setup deep linking](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-3-setup-deep-linking): Cotter's authentication will redirect back to your application using a URL scheme.
4. [Receive the Token](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-4-receiving-the-token-in-onsuccess-or-onerror): Include the returned token and email/phone number in your server

### Step 1: Import Cotter as a dependency

{% hint style="info" %}
Make sure you're using `react-native` version **< 0.63**
{% endhint %}

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

```java
yarn add react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}
{% endtabs %}

*(Optional) Checkout additional steps* [*for Android, React Native < 0.60, and Manual Installation*](/sdk-reference/react-native/installation)*.*&#x20;

### Step 2: Call Cotter's Verify function

Cotter's verify function will open a Custom Chrome Tab or ASWebAuthenticationSession in your app to authenticate the user. You have 2 choices

1. Open the web browser with **no predefined input**. The user can enter email or phone in the in-app browser.

```javascript
// ADD Cotter's Verify Class
import { Verify } from 'react-native-cotter';

class Register extends PureComponent {
  ...
  openCotterAuth = async () => {
    var verify = new Verify(
      'myexample://auth_callback',     // (setup later) URL Scheme for deep linking
      API_KEY_ID,                      // your API_KEY_ID
      this.onError,                    // error callback Function, receives (errorMessage, errorObject)
      this.onSuccess,                  // error callback Function, receives (errorMessage, errorObject)
    );
    await verify.openAuth('EMAIL'); // EMAIL or PHONE
    // You will need balance to send SMS. Otherwise, use "EMAIL"
  };
  onError = (errorMessage, errorObject) => {
    alert(errorMessage);
    console.log(errorObject);
  };
  onSuccess = response => {
    alert("Success");
    console.log(response);
  };
  ...
}
```

2\. Open the web browser **after the user entered their email or phone number in your app**'s input field

```javascript
import { Verify } from 'react-native-cotter';

class Register extends PureComponent {
  ...
  openCotterAuth = async () => {
    var verify = new Verify(
      'myexample://auth_callback',     // (setup later) URL Scheme for deep linking
      API_KEY_ID,                      // your API_KEY_ID
      this.onError,                    // error callback Function, receives (errorMessage, errorObject)
      this.onSuccess,                  // error callback Function, receives (errorMessage, errorObject)
    );
    await verify.openAuthWithInput('EMAIL', this.state.email); // EMAIL or PHONE
  };
  onError = (errorMessage, errorObject) => {
    alert(errorMessage);
    console.log(errorObject);
  };
  onSuccess = response => {
    alert("Success");
    console.log(response);
  };
  ...
}
```

{% hint style="success" %}
**Try this now!** You should see an in-app browser looking like the image below popping up and ask you to authenticate.
{% endhint %}

![](/files/-M1doM-5Nwjmc8sO7YxE)

**Send Code via WhatsApp**

Instead of using SMS, you can also [send the verification code via WhatsApp.](/sdk-reference/react-native/older-sdk-versions/sending-code-via-whatsapp)

### Step 3: Setup Deep Linking

Pick a unique URL scheme for redirecting the user back to your app after the verification in the in-app browser is successful. For this example, we'll use `myexample://auth_callback` .

### Setup in Android

Add this in your [AndroidManifest.xml](https://github.com/cotterapp/react-native-sdk/blob/9bc206e3b5c815a5ded65627e8bb172db293f83d/examples/android/app/src/main/AndroidManifest.xml#L23)

```markup
<activity
    android:name=".MainActivity"
    android:launchMode="singleTask"> <!-- Make launchMode to singleTask --> 
    
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <!--    Setup Deep Linking Here    -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- This is for myexample://auth_callback -->
        <!-- 👇 Change this to your own URL scheme -->
        <data android:scheme="myexample" android:host="auth_callback"/>
    </intent-filter>
    <!--  end -->
    
</activity>
```

### Setup in iOS

Add this in your [Info.plist](https://github.com/cotterapp/react-native-cotter/blob/master/examples/ios/examples/Info.plist#L26)

```markup
<key>CFBundleURLTypes</key>
<array>
	<dict>
		<key>CFBundleTypeRole</key>
		<string>Editor</string>
		<key>CFBundleURLName</key>
		<string>myexample</string> <!-- 👈 Change this to your own URL Scheme -->
		<key>CFBundleURLSchemes</key>
		<array>
			<string>myexample</string> <!-- 👈 Change this to your own URL Scheme -->
		</array>
	</dict>
</array>
```

If you're targeting iOS 9.x or newer, add the following lines to [YourApp/ios/YourApp/AppDelegate.m](https://github.com/proyecto26/react-native-inappbrowser/blob/master/example/ios/example/AppDelegate.m):

{% tabs %}
{% tab title="iOS 9.x or newer" %}

```swift
// Add the header at the top of the file:
#import <React/RCTLinkingManager.h>

// Add this above `@end`:
- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}
```

{% endtab %}
{% endtabs %}

If you're targeting iOS 8.x or older, you can use the following code instead, add the following lines to [YourApp/ios/YourApp/AppDelegate.m](https://github.com/proyecto26/react-native-inappbrowser/blob/master/example/ios/example/AppDelegate.m):

```swift
// Add the header at the top of the file:
#import <React/RCTLinkingManager.h>

// Add this above `@end`:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  return [RCTLinkingManager application:application openURL:url
                      sourceApplication:sourceApplication annotation:annotation];
}
```

If your app is using Universal Links, you'll need to add the following code as well, add the following lines to [YourApp/ios/YourApp/AppDelegate.m](https://github.com/proyecto26/react-native-inappbrowser/blob/master/example/ios/example/AppDelegate.m):

```swift
// Add this above `@end`:
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
 restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
 return [RCTLinkingManager application:application
                  continueUserActivity:userActivity
                    restorationHandler:restorationHandler];
}
```

### Setup in React Native Project

{% tabs %}
{% tab title="React Navigation" %}
In your App.js

```javascript
import { createStackNavigator } from 'react-navigation'

// Import Cotter's Loading Page
import { LoadingPage } from 'react-native-cotter';
 
const Main = createStackNavigator(
  {
    ...
    Register: { screen: Register },
    
    // Redirect users Cotter's Handler page
    CotterLoadingVerify: { 
      screen: LoadingPage, // Use Cotter's Loading Page
      path: 'auth_callback' // Enable Deep linking redirection
    },
    
  }
)
```

If you have Nested Stack Navigator, add `path: ''` to every parent stack.

```javascript
// TARGET STACK
const RegisterStack = createStackNavigator(
  {
    Register: { screen: Register },
    CotterLoadingVerify: {
      screen: LoadingPage, // Use Cotter's Loading Page
      path: 'auth_callback', // Enable Deep linking redirection
    },
  }
);

// PARENT 1
const RootStack = createStackNavigator(
  {
    // ADD path: '' here
    RegisterStack: { screen: RegisterStack, path: '' },
    ...
  }
);

// PARENT of PARENT 1
const RootSwitch = createSwitchNavigator(
  {
    Splash: { screen: Splash },
    // ADD path: '' here
    RootStack: { screen: RootStack, path: '' },
  }
);
```

{% endtab %}

{% tab title="React Navigation 5 or above" %}
If you're using the [newer version of React Navigation](https://reactnavigation.org/docs/deep-linking#deep-link-integration), copy paste the code below to your App.js or index.js.

**In your App.js or index.js**

```javascript
import { NavigationContainer, useLinking } from '@react-navigation/native';

function App() {
  const ref = React.useRef();

  const {getInitialState} = useLinking(ref, {
    prefixes: ['https://myexample.cotter.app', 'myexample://'],
    config: {
      CotterLoadingVerify: 'auth_callback', // ADD THIS TO REDIRECT TO COTTER'S HANDLER PAGE
    },
  });

  const [isReady, setIsReady] = React.useState(false);
  const [initialState, setInitialState] = React.useState();

  React.useEffect(() => {
    Promise.race([
      getInitialState(),
      new Promise(resolve =>
        // Timeout in 150ms if `getInitialState` doesn't resolve
        // Workaround for https://github.com/facebook/react-native/issues/25675
        setTimeout(resolve, 150)
      ),
    ])
      .catch(e => {
        console.error(e);
      })
      .then(state => {
        if (state !== undefined) {
          setInitialState(state);
        }

        setIsReady(true);
      });
  }, [getInitialState]);

  if (!isReady) {
    return null;
  }

  return (
    <NavigationContainer initialState={initialState} ref={ref}>
      {/* content 👈 */}
    </NavigationContainer>
  );
}
```

**In your Router**

```javascript
import {LoadingPage} from 'react-native-cotter';

function Router() {
  return (
    <Stack.Navigator>
      ...
      
      // Add CotterLoadingVerify page
      <Stack.Screen
        name="CotterLoadingVerify"
        component={LoadingPage}
        options={{headerShown: false}}
      />
      
    </Stack.Navigator>
  );
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Remember to make sure your [callbackURL in Step 2 ](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-2-call-cotters-verify-function)is correct.
{% endhint %}

{% hint style="success" %}
**Try it again!** You should see the in-app browser redirecting back after you've successfully verified.
{% endhint %}

### Step 4: Receiving the Token in onSuccess or onError

#### onError

The `onError` function that you pass in will receive 2 parameters: `errorMessage` (string) and `errorResponse` (object). The `errorResponse` is an http response from attempt to verify the user's email/phone in Cotter's server.

#### onSuccess

The `onSuccess` function that you pass in will receive a `response` object that looks like this:

```javascript
{
  "identifier": {
    "ID": "3e61e1e9-3528-48d7-a7a2-b985141f4d67",
    "created_at": "2020-02-15T03:45:25.656423Z",
    "update_at": "2020-02-19T06:55:19.121863Z",
    "deleted_at": null,
    "identifier": "+12345678910",
    "identifier_type": "PHONE",
    "public_key": "hU1ZOLxyfq548Vh45TcOlqHug/WHqVJfoxApgq8Wfks=",
    "device_type": "BROWSER",
    "device_name": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36",
    "expiry": "2020-03-20T06:55:19.121783Z"
  },
  "token": {
    "identifier": "+12345678910",
    "identifier_type": "PHONE",
    "receiver": "<your API_KEY_ID>",
    "expire_at": "1584687591",
    "signature": "G8dOKR6qLj+GiB0pD2aggVVdYddFoyy..."
  }
}
```

This JSON object contains 2 objects, `identifier` and `token` .&#x20;

* The identifier object contains information about the user's email or phone number, device type and name, and expiry.
* The token contains the user's phone number, your `API_KEY_ID` in the `receiver` field, and a signature to ensure this is from Cotter. The token tells you that this `identifier` is verified.

{% hint style="success" %}
You should include this JSON Object into your call to your backend for **Login** or **Registration**. Your backend should then verify that the [signature of the token](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-5-validating-the-token) is valid.
{% endhint %}

For example, if you have an existing Register function:

```javascript
class Register extends PureComponent {
  ...
  // YOUR BUTTON SHOULD INVOKE openCotterAuth ON PRESS
  openCotterAuth = async () => {
    var verify = new Verify(
      'myexample://auth_callback',     // (setup later) URL Scheme for deep linking
      API_KEY_ID,                      // your API_KEY_ID
      this.onError,                    // error callback Function, receives (errorMessage, errorObject)
      this.onSuccess,                  // error callback Function, receives (errorMessage, errorObject)
    );
    await verify.openAuthWithInput("EMAIL", this.state.email); // EMAIL or PHONE
    // You will need balance to send SMS. Otherwise, use "EMAIL"
  };

  onError = (errorMessage, errorObject) => {
    alert(errorMessage);
    console.log(errorObject);
  };
  
  onSuccess = response => {
    this.submitRegister(response.identifier.identifier, response.token);
  };

  // YOUR Register API post email and token
  submitRegister = (email, token) => {
    axios
      .post(
        "https://example.com/user/register",
        { email: email, token: token }, // SEND Cotter's token to your backend
        false
      )
      .then(response => {
        ...
        this.props.navigation.navigate("Dashboard");
      })
      .catch(error => {});
  };
  ...
}
```

## Validating Cotter's Access Token&#x20;

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

## 🎉 You're done!

## Securing your Project

Since you'll be using your API Key from a front-end website or mobile app, your `API_KEY_ID` is exposed to anyone inspecting your code. Here are some ways to prevent abuse:

* [Only allow your website/app to use your API Key](/protecting-your-account/only-allow-your-website-app-to-use-your-api-key)
* [Rate Limit the number of authentication requests](/protecting-your-account/rate-limit)
* [Enable reCAPTCHA to prevent automated abuse](/protecting-your-account/enable-recaptcha-to-protect-against-automated-abuse)


# Sending Code via WhatsApp

By default, when the user enters a phone number, we'll send a verification code using SMS. You can add the option for your users to receive the verification code as a WhatsApp message.

## How to Add the WhatsApp Option

![PhoneChannels options](/files/-M4XUkbnSGjPvQe9Z5Us)

### 1) Using **`verify.openAuth('PHONE')`**

If you choose to [open the in-app browser with **no predefined input**](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-2-call-cotters-verify-function) using where the user can enter email or phone in the in-app browser), update your code like below:

```javascript
class Register extends PureComponent {
  ...
  openCotterAuth = async () => {
    var verify = new Verify(...);
    
    await verify.openAuth(
      'PHONE', 
      ['SMS', 'WHATSAPP'], // 👈 Add ['SMS', 'WHATSAPP'] as a parameter
    );
  };
};
```

Possible options are:

* `['SMS']` (default, sms only)
* `['SMS', 'WHATSAPP']` (allow both)
* `['WHATSAPP']` (WhatsApp only)

### 2) Using **`verify.openAuthWithInput('PHONE', this.state.phone)`**

If you choose to [open the web browser **after the user entered their email or phone number in your app**'s](/sdk-reference/react-native/react-native-sdk-verify-email-phone#step-2-call-cotters-verify-function) input field, you can set it to **automatically send the verification code via WhatsApp:**

```javascript
class Register extends PureComponent {
  ...
  openCotterAuth = async () => {
    var verify = new Verify(...);
    await verify.openAuthWithInput(
      'PHONE', 
      this.state.phone, 
      'WHATSAPP', // 👈 'WHATSAPP' as a parameter to automatically send code via WhatsApp
    );
  };
  
  
  // Possible options are:
  // - 'SMS'                (default, send code via SMS)
  // - 'WHATSAPP'           (send code via WHATSAPP)
  ...
}
```

Possible options are:

* `'SMS'` (default, send code via SMS)
* `'WHATSAPP'` (send code via WhatsApp)

#### WhatsApp Opt-In Requirements

WhatsApp requires that your application implement explicit user opt-ins to deliver messages over WhatsApp.&#x20;

{% hint style="info" %}
⚠️[**Read WhatsApp Opt-In Requirements**](https://developers.facebook.com/docs/whatsapp/guides/opt-in/)
{% endhint %}

Since the in-app browser will *automatically send the code via WhatsApp*, **you need to** **inform your users that we will send a message to their WhatsApp account.** Make sure you make the following things clear:

* The phone number that will receive the message
* That the user does an action (press a button or a checkbox) to acknowledge that the code is sent via WhatsApp (show the WhatsApp logo, using assets and color from <https://whatsappbrand.com/>)
* If necessary, add a text explaining what message you'll be sending, for example:*"If you choose to login with WhatsApp, we will send a WhatsApp message to this number with a verification code".*

{% hint style="danger" %}
Please make sure that **your customer is informed that they will be receiving a WhatsApp message**, otherwise your application may be **suspended**. This includes using the appropriate language that your users can understand.
{% endhint %}


# Sign In with Device

Our React Native SDK offers the easiest way to integrate Cotter's Passwordless Login. You can simply call a function and it does most of the heavy lifting and authentication for you.

### What you're building <a href="#what-youre-building" id="what-youre-building"></a>

![Trusted Devices using Cotter's React Native SDK](https://gblobscdn.gitbook.com/assets%2F-M0QGDMRD8y_Kd-BpOvT%2F-M2k7vbf1vx5ChiypmkE%2F-M2kXnFknrHlQ5gjoZar%2Fimage.png?alt=media\&token=8dea76d7-cd81-43f1-a3de-18c5d298ce5f)

## Steps <a href="#steps" id="steps"></a>

1. ​[Import Cotter as a dependency](https://docs.cotter.app/sdk-reference/react-native/react-native-sdk-passwordless-login#step-1-import-cotter-as-a-dependency)​
2. ​[Set up Cotter in your Project](https://docs.cotter.app/sdk-reference/react-native/react-native-sdk-passwordless-login#step-2-set-up-cotter-in-your-project)​
3. ​[Set Allowed Methods](https://docs.cotter.app/sdk-reference/react-native/react-native-sdk-passwordless-login#step-3-setting-authentication-methods) in the Dashboard to allow Trusted Devices
4. ​[Register the current device as a Trusted Device](https://docs.cotter.app/sdk-reference/react-native/react-native-sdk-passwordless-login#step-4-register-this-device-as-a-trusted-device)​
5. ​[Authenticate from a Trusted Device and Non-Trusted Device](https://docs.cotter.app/sdk-reference/react-native/react-native-sdk-passwordless-login#step-5-authenticate-from-a-trusted-device-and-non-trusted-device)​

### Step 1: Import Cotter as a dependency <a href="#step-1-import-cotter-as-a-dependency" id="step-1-import-cotter-as-a-dependency"></a>

{% hint style="info" %}
Make sure you're using `react-native` version **< 0.63**
{% endhint %}

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

```java
yarn add react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}

{% tab title="npm" %}

```
npm install --save react-native-cotter react-native-device-info rn-secure-storage react-native-randombytes react-native-camera react-native-svg react-native-securerandom buffer react-native-inappbrowser-reborn react-native-sha256
npx pod-install ios
```

{% endtab %}
{% endtabs %}

*(Optional) Checkout additional steps* [*for Android, React Native < 0.60, and Manual Installation*](https://docs.cotter.app/sdk-reference/react-native/installation)*.*

### Step 2: Set up Cotter in your Project <a href="#step-2-set-up-cotter-in-your-project" id="step-2-set-up-cotter-in-your-project"></a>

To allow Cotter to display modals, **wrap your root component with** **`connectCotterWrapper`** **:**

```javascript
import {connectCotterWrapper} from 'react-native-cotter';

class MyApp extends Component {
}

MyApp = connectCotterWrapper(MyApp);
```

### Step 3: Setting Authentication Methods <a href="#step-3-setting-authentication-methods" id="step-3-setting-authentication-methods"></a>

You need to set allowed methods for authenticating your users. To allow `TRUSTED DEVICES`, go to <https://dev.cotter.app/rules>​

![Set Trusted Device as an Allowed Method](https://gblobscdn.gitbook.com/assets%2F-M0QGDMRD8y_Kd-BpOvT%2F-M1592EVZf_MTC478_zE%2F-M15A0xW-OgzjEnPqgx6%2Fimage.png?alt=media\&token=fd86e433-c49d-4324-817a-8f7a852c1450)

{% hint style="warning" %}
Remember to set the correct Project in the dropdown list
{% endhint %}

### Step 4: Register this device as a Trusted Device <a href="#step-4-register-this-device-as-a-trusted-device" id="step-4-register-this-device-as-a-trusted-device"></a>

After registering your `user_id` to Cotter using the API above, you should enroll the current device as the user's first Trusted Device.

```javascript
import { Cotter } from "react-native-cotter";

class SignUp extends Component {
  ...
  onFinishRegistration = (userID) => {
    // Initialize Cotter
    var cotter = new Cotter(
      <API_KEY_ID>,
      userID,
      [email] // (optional) associated array of emails or phone numbers for the user
    );
    // Enroll device as Trusted Device
    cotter.trustedDevice.enrollDevice(
      this.onEnrollSuccess, 
      this.onEnrollError
    );
  };

  onEnrollSuccess = resp => {
    alert('Success');
    console.log(resp);
    
    //navigate to dashboard
    this.props.navigation.navigate('Dashboard');
  };
  
  onEnrollError = err => {
    alert('Error');
    console.log(err);
  };
  ...
}
```

### ​ <a href="#undefined" id="undefined"></a>

### Step 5: Authenticate from a Trusted Device and Non-Trusted Device <a href="#step-5-authenticate-from-a-trusted-device-and-non-trusted-device" id="step-5-authenticate-from-a-trusted-device-and-non-trusted-device"></a>

To request an authentication from Cotter's SDK, you would need to call `cotter.trustedDevice.requestAuth` . **This will automatically detect whether the current device is a Trusted Device or not.**

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

```javascript
// Requesting an authentication using Cotter
var cotter = new Cotter(
  <API_KEY_ID>,
  userID,
);
cotter.trustedDevice.requestAuth(
  'EVENT NAME',
  this.onRequestSuccess,
  this.onRequestError,
);
```

{% endtab %}

{% tab title="JSON Response" %}

```javascript
{
  "ID": 1361, // Event ID
  "CreatedAt": "2020-02-27T22:22:48.705212512Z",
  "UpdatedAt": "2020-02-27T22:22:48.705212512Z",
  "DeletedAt": null,
  "client_user_id": "1014", // your client's User ID
  "issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API Key
  "event": "<EVENT NAME>",// requested event (LOGIN, or TRANSACTION, etc)
  "ip": "192.168.232.2", 
  "location": "Unknown",
  "timestamp": "1582842167",
  "method": "TRUSTED_DEVICE", // auth method: TRUSTED_DEVICE (other choices are PIN / BIOMETRIC)
  "new": false, // Is this a new pending event. More explanation below about Non-Trusted Device
  "approved": true, // Is this event approved.
  "signature": "oonMGCAxp3..." // Signature to make sure this event comes from Cotter's server
}
```

{% endtab %}
{% endtabs %}

#### How it works with your Login system: <a href="#how-it-works-with-your-login-system" id="how-it-works-with-your-login-system"></a>

![Authenticate using Cotter's Trusted Devices](https://gblobscdn.gitbook.com/assets%2F-M0QGDMRD8y_Kd-BpOvT%2F-M2pXd25NjYd0DtX9RL0%2F-M2p_RbT8vVg6-xK1MQZ%2Fimage.png?alt=media\&token=32f4fb5d-973e-4b8c-a070-74d0b3b1dfc7)

For example, on your "Login" button, set `onPress` to `this.authenticate` . This will invoke Cotter's SDK to authenticate the user. You would need to:

1. (optional) Fetch the `user_id` you used for this user on [Step 4](https://docs.cotter.app/sdk-reference/react-native/react-native-sdk-passwordless-login#step-4-register-this-device-as-a-trusted-device) when you registered the user to Cotter. You can do this using a function like `this.getUserID` below to get `user_id` based on the email/phone. (If you're just using the email/phone as the `user_id`, that's fine too.)
2. Use the `user_id` to initialize a `cotter` object, and call `cotter.trustedDevice.requestAuth`. You can pass in an `EVENT NAME` here. This could be any string to tag the event type (`LOGIN`, `TRANSACTION` , etc).
3. Login your user by validating the Cotter's response in your backend server.

```java
import { Cotter } from 'react-native-cotter';

class Login extends Component {

  // Start Authentication here
  authenticate = async () => {
    // (1) Getting user ID 
    // that you registered in Step 4 from YOUR BACKEND SERVER
    // based on the email/phone number 
    const userID = await this.getUserID(this.state.email);

    // (2) request authentication
    var cotter = new Cotter(
      <API_KEY_ID>,
      userID,
    );
    cotter.trustedDevice.requestAuth(
      'EVENT NAME',
      this.onRequestSuccess,
      this.onRequestError,
    );
  };

  onRequestError = (errorMessage, error) => {
    alert(errorMessage);
    console.log(error);
  };

  onRequestSuccess = response => {
    alert('Success');
    console.log(response);
    
    // (3) Login user in backend
    // Check how the `response` look like in the next tab above
    // Pass in the `response` to YOUR BACKEND SERVER when:
    // Logging in your users, making a transaction, or fetching sensitive data
    this.submitLogin(response);
  };
  ...
}
```

#### Trusted and Non-Trusted Device <a href="#trusted-and-non-trusted-device" id="trusted-and-non-trusted-device"></a>

When an Authentication Event is requested using method `TRUSTED_DEVICE`, there are 2 possible cases:

#### Case 1: The current device is a Trusted Device <a href="#case-1-the-current-device-is-a-trusted-device" id="case-1-the-current-device-is-a-trusted-device"></a>

If the current device is a Trusted Device, it should automatically be approved, and you will receive a JSON response containing the requested `Event` and whether or not it's approved. The approval is based on whether or not the signature included in the request from the SDK is valid.

```java
{
  ...
  "method": "TRUSTED_DEVICE",
  "new": false, // Is this a new pending event (should be false).
  "approved": true // Is this event approved (should be true).
  "signature": "oonMGCAxp3..." // Signature to make sure this event comes from Cotter's server
}
```

You should see a result that the event is **not new**, and that it's **approved**. This is because the signature from the Trusted Device is sufficient to prove that the device is authorized.

When passing this Event Response to your backend, you need to check if this JSON is valid and if it comes from Cotter's server.

Checkout how to verify the OAuth Tokens from Cotter her&#x65;**:**

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

#### Case 2: The current device is NOT a Trusted Device <a href="#case-2-the-current-device-is-not-a-trusted-device" id="case-2-the-current-device-is-not-a-trusted-device"></a>

We'll cover this in the next guide:[Authenticate from a Non-Trusted Device/sdk-reference/react-native/react-native-sdk-passwordless-login/authenticate-from-a-non-trusted-device](https://docs.cotter.app/sdk-reference/react-native/react-native-sdk-passwordless-login/authenticate-from-a-non-trusted-device)

## 🎉 You're done! <a href="#youre-done" id="youre-done"></a>


# Authenticate from a Non-Trusted Device

## Steps

There are 2 steps that's needed to add a new Trusted Device:

1. Check if this device is a Trusted Device
2. [Show the QR Code](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device#step-1-show-the-qr-code-of-the-new-device) of the New Non-Trusted Device
3. [Scan the New Device's QR Code](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device#step-2-scanning-the-qr-code-from-a-trusted-device) using the Trusted Device

## Step 1:  Check if this device is a Trusted Device

```javascript
componentDidMount() {
  var cotter = new Cotter(
    API_KEY_ID,
    userID,
  );
  this.cotter = cotter;
  this.checkIfThisDeviceTrusted();
}

checkIfThisDeviceTrusted = () => {
  this.cotter.trustedDevice
    .trustedDeviceEnrolled()
    .then(trusted => {
      // trusted = True or False
      this.setState({trusted: trusted});
    })
    .catch(err => console.log(err));
};
```

## Step 2: Show the QR Code of the New Device

To show the QR Code of the new device:

```javascript
trustThisDevice = () => {
  this.cotter.trustedDevice.trustThisDevice(this.onSuccessTrust, this.onErrorTrust);
};
onSuccessTrust = () => {
  alert('Success');
};
onErrorTrust = (errmsg, err) => {
  alert(errmsg);
};

...
<Button onPress={this.trustThisDevice} title="Make Device a Trusted Device" />
```

This will open a modal with a QR Code that can be scanned from the Trusted Device.&#x20;

## Step 3: Scan the QR Code from a Trusted Device

### Setup Permission to Allow Camera

Update your `Info.plist` in `ios/YourProject/Info.plist`

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

  <!-- Add Camera Permission -->
  
  <key>NSCameraUsageDescription</key>
  <string>YOUR TEXT</string>

  <!-- … -->

</dict>
</plist>
```

#### Android

Add the permission below to your app `android/app/src/main/AndroidManifest.xml` file:

```markup
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.myawesomeapp">

  <!-- Add Camera Permission -->
  <uses-permission android:name="android.permission.CAMERA" />
  <!-- … -->

</manifest>
```

{% hint style="warning" %}
If you get the error **"Cannot choose between the following variants of project :react-native-camera"**:

Add the following to your`android/app/build.gradle`

```
android {
  ...
  defaultConfig {
    ...
    missingDimensionStrategy 'react-native-camera', 'general' <-- insert this line
  }
}
```

{% endhint %}

{% hint style="warning" %}
If you get the error **"Execution failed for task ':app:mergeDexDebug'"**

Add the following to your`android/app/build.gradle`

```
android {
  ...
  defaultConfig {
    ...
    multiDexEnabled true     <-- insert this line
  }
}
```

{% endhint %}

### Scan the QR Code

To open the Scanner modal:

```java
scanQRCode = () => {
   this.cotter.trustedDevice.scanQRCode();
};
...
<Button onPress={this.scanQRCode} title="Add New Device" />
```

This will open a modal where the user can scan the QR Code displayed on the new device.

{% hint style="danger" %}
&#x20;**If you encounter the error Invalid RNPermission X. Should be one of: ()**

1. Clean up Xcode stale data with `npx react-native-clean-project --remove-iOS-build --remove-iOS-pods`
   {% endhint %}


# Add a new Trusted Device

## Steps

There are 2 steps that's needed to add a new Trusted Device:

1. Check if this device is a Trusted Device
2. [Show the QR Code](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device#step-1-show-the-qr-code-of-the-new-device) of the New Non-Trusted Device
3. [Scan the New Device's QR Code](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device#step-2-scanning-the-qr-code-from-a-trusted-device) using the Trusted Device

## Step 1:  Check if this device is a Trusted Device

```javascript
componentDidMount() {
  var cotter = new Cotter(
    API_KEY_ID,
    userID,
  );
  this.cotter = cotter;
  this.checkIfThisDeviceTrusted();
}

checkIfThisDeviceTrusted = () => {
  this.cotter.trustedDevice
    .trustedDeviceEnrolled()
    .then(trusted => {
      // trusted = True or False
      this.setState({trusted: trusted});
    })
    .catch(err => console.log(err));
};
```

## Step 2: Show the QR Code of the New Device

To show the QR Code of the new device:

```javascript
trustThisDevice = () => {
  this.cotter.trustedDevice.trustThisDevice(this.onSuccessTrust, this.onErrorTrust);
};
onSuccessTrust = () => {
  alert('Success');
};
onErrorTrust = (errmsg, err) => {
  alert(errmsg);
};

...
<Button onPress={this.trustThisDevice} title="Make Device a Trusted Device" />
```

This will open a modal with a QR Code that can be scanned from the Trusted Device.&#x20;

## Step 3: Scan the QR Code from a Trusted Device

### Setup Permission to Allow Camera

Update your `Info.plist` in `ios/YourProject/Info.plist`

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

  <!-- Add Camera Permission -->
  
  <key>NSCameraUsageDescription</key>
  <string>YOUR TEXT</string>

  <!-- … -->

</dict>
</plist>
```

#### Android

Add the permission below to your app `android/app/src/main/AndroidManifest.xml` file:

```markup
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.myawesomeapp">

  <!-- Add Camera Permission -->
  <uses-permission android:name="android.permission.CAMERA" />
  <!-- … -->

</manifest>
```

{% hint style="warning" %}
If you get the error **"Cannot choose between the following variants of project :react-native-camera"**:

Add the following to your`android/app/build.gradle`

```
android {
  ...
  defaultConfig {
    ...
    missingDimensionStrategy 'react-native-camera', 'general' <-- insert this line
  }
}
```

{% endhint %}

{% hint style="warning" %}
If you get the error **"Execution failed for task ':app:mergeDexDebug'"**

Add the following to your`android/app/build.gradle`

```
android {
  ...
  defaultConfig {
    ...
    multiDexEnabled true     <-- insert this line
  }
}
```

{% endhint %}

### Scan the QR Code

To open the Scanner modal:

```java
scanQRCode = () => {
   this.cotter.trustedDevice.scanQRCode();
};
...
<Button onPress={this.scanQRCode} title="Add New Device" />
```

This will open a modal where the user can scan the QR Code displayed on the new device.

{% hint style="danger" %}
&#x20;**If you encounter the error Invalid RNPermission X. Should be one of: ()**

1. Clean up Xcode stale data with `npx react-native-clean-project --remove-iOS-build --remove-iOS-pods`
   {% endhint %}


# Customization

You can change the text and logo in most of the UI that's provided by Cotter's React Native SDK.

## Getting started

To start customizing the UI, you will pass a config containing the text that you want to the functions that you've used in the previous guides:

## Approve Authentication Request Prompt

![Approve Authentication Request](/files/-M2qaSfFdhMp4Xgdgrvz)

To customize this page, do the following:

```javascript
const yourLogo = require('../assets/images/yourLogo.png');

// You don't have to update everything, you can set only the 'title' if you want
var authApproveText = {
  title: 'Are you trying to sign in?',
  subtitle: 'Someone is trying to sign in to your account from another device',
  logo: yourLogo,
  buttonNo: "No, it's not me",
  buttonYes: 'Yes',
};

approveLogin = async () => {
  this.cotter.trustedDevice.getNewEvent(authApproveText); // <-- pass in here
};
```

## Request Authentication Prompt

![Request Authentication](/files/-M2qbX5z6vycUGFP7a5-)

To customize this page, do the following:

```javascript
const tapImage = require('../assets/images/tapImage.png');
const warningImage = require('../assets/images/warningImage.png');

// You don't have to update everything, you can set only the 'title' if you want
const authReqText = {
  title: 'Approve this login from your phone',
  subtitle: "A notification is sent to your trusted device to confirm it's you",
  image: tapImage,
  titleError: 'Something went wrong',
  subtitleError: "We are unable to confirm it's you, please try again",
  imageError: warningImage,
};

this.cotter.trustedDevice.requestAuth(
  'EVENT NAME',
  this.onRequestSuccess,
  this.onRequestError,
  authReqText, // <-- pass in here
);
```

## Show QR Code Modal

![Show QR Code Modal](/files/-M2qoAbYX0Nk_-l-bWMN)

To customize this page, do the following:

```javascript
const checkImage = require('../assets/images/checkImage.png');
const warningImage = require('../assets/images/warningImage.png');

// You don't have to update everything, you can set only the 'title' if you want
const showQRText = {
  title: 'Register This Device',
  subtitle: 'Please scan this QR Code from a Trusted Device',
  imageSuccess: checkImage,
  imageError: warningImage,
};

showQRCode = () => {
  this.cotter.trustedDevice.trustThisDevice(
    this.onSuccessTrust,
    this.onErrorTrust,
    showQRText, // <-- pass in here
  );
};
```

## QR Code Scanner Modal

![QR Code Scanner Modal](/files/-M2qowYiYX7CJxtGVIHE)

To customize this page, do the following:

```javascript
const checkImage = require('../assets/images/checkImage.png');
const warningImage = require('../assets/images/warningImage.png');

// You don't have to update everything, you can set only the 'title' if you want
const scanQRText = {
  title: 'Scan QR Code',
  subtitle: 'Scan the QR Code from the new device',
  imageSuccess: checkImage,
  imageError: warningImage,
  blocked:
    'Camera is blocked. Please go to Settings and allow access to camera.',
};

scanQRCode = () => {
  this.cotter.trustedDevice.scanQRCode(scanQRText); // <-- pass in here
};
```


# Flutter

The easiest way to add Passwordless Login and Email/Phone Number Verification to your Flutter app.

## Installation

Add Cotter to your `pubspec.yaml` , then run `flutter pub get`.

```yaml
dependencies:
  cotter:
```

Check the latest releases in [pub.dev](https://pub.dev/packages/cotter#-installing-tab-). You may need to restart your flutter for it to run pod install (stop flutter run and re run it).

#### For Android:

Update the `minSdkVersion` inside `android/app/build.gradle` to 18:

```yaml
defaultConfig {
    // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
    applicationId "com.example.example"
    minSdkVersion 18   // 👈 Update this line
    targetSdkVersion 28
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}
```

## **Complete Guides**

#### **Sign in with Device**

Allow users to login automatically from a device that they trust.

{% content-ref url="/pages/-M9angupK73fWUZjM7fe" %}
[Sign In with Device](/sdk-reference/flutter/sign-in-with-device)
{% endcontent-ref %}

#### Sign in with Email/Phone Number

Authenticate users by sending a Magic Link or OTP via email, SMS, or WhatsApp.

{% content-ref url="/pages/-MATH96wMixRR82bWQJJ" %}
[Sign in with Email/Phone Number](/sdk-reference/flutter/sign-in-with-email-phone-number)
{% endcontent-ref %}

## Quickstart Guides

{% content-ref url="/pages/-M9aaGUmTZYju\_sjd17x" %}
[Flutter – Sign in with Device](/quickstart-guides/flutter-sign-in-with-device)
{% endcontent-ref %}


# Sign In with Device

Our Flutter SDK offers the easiest way to integrate Cotter's Sign In with Device. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Device**](/features/passwordless-login) works.

{% hint style="warning" %}
There are major updates planned for this feature. [Contact us in Slack](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA) so we can help you prepare for it.
{% endhint %}

### What you're building

![Sign In with Device using Cotter's Flutter SDK](/files/-M9arn7Qne8dqUy2XJOi)

### Import Cotter as a dependency

Add Cotter to your `pubspec.yaml` , then run `flutter pub get`.

```yaml
dependencies:
  cotter:
```

Check the latest releases in [pub.dev](https://pub.dev/packages/cotter#-installing-tab-). You may need to restart your flutter for it to run pod install (stop flutter run and re run it).

**For Android**: Update `minSdkVersion` to `18` [following the installation instructions](/sdk-reference/flutter).

### Step 1: Import and Initialize Cotter

Import Cotter in your `lib/main.dart`, then initialize it inside `HomePageState`.

```dart
import 'package:cotter/cotter.dart'; // Import Cotter

class HomePageState extends State {
  ...

  // 1️⃣ Initialize Cotter
  Cotter cotter = new Cotter(apiKeyID: API_KEY_ID); // 👈 Specify your API KEY ID here

  // 2️⃣ TODO: Make Sign Up Function
  // 3️⃣ TODO: Make Login Function
  
  @override
  Widget build(BuildContext context) { ... }
}
```

You can [create a free account at Cotter](https://dev.cotter.app) to get your `API_KEY_ID`.

{% hint style="info" %}
**Make sure you allow Trusted Devices Method in the** [**dashboard**](https://dev.cotter.app)**.**
{% endhint %}

### Step 2: Make a Sign Up Function

To sign up, call `cotter.signUpWithDevice` and input the user's email. This will create a new user in Cotter and trust the current device to allow logins. This function returns the [User object](/api-reference/user-api/user-object).

```dart
  // 2️⃣  Make Sign Up Function
  void signUp(BuildContext context) async {
    try {
      // 🚀 One-line Sign Up
      // Create a new user in Cotter and trust the current device
      var user = await cotter.signUpWithDevice(identifier: inputController.text);
      print(user);
    } catch (e) {
      print(e);
    }
  }
```

#### (Optional) You can also verify the user's email or phone number at this step

To verify the user's email or phone number, follow this guide **after you've finished this one**.

{% content-ref url="/pages/-MAYBXzMAs8ZfSEUGux-" %}
[Add Email/Phone Verification](/sdk-reference/flutter/sign-in-with-device/add-email-phone-verification)
{% endcontent-ref %}

### Step 3: Make a Login Function

To sign in, call `cotter.signInWithDevice` . If the user is logging-in from a device that they trust, they'll automatically be approved. This function returns the [Event object](/api-reference/event-object).

```dart
  // 3️⃣  Make Login Function
  void login(BuildContext context) async {
    try {
      // 🚀 One-line Login
      var event = await cotter.signInWithDevice(identifier: inputController.text, context: context);
      print(event);
    } catch (e) {
      print(e);
    }
  }
```

#### To get the Logged-in User Info and Access Token, follow this section:

{% content-ref url="/pages/-M9axG8HPu6f\_at6wCcT" %}
[Getting OAuth Tokens](/sdk-reference/flutter/getting-oauth-tokens)
{% endcontent-ref %}

{% content-ref url="/pages/-M9awQf5uqyq2X9ZO4JM" %}
[Getting the Logged-in User](/sdk-reference/flutter/getting-the-logged-in-user)
{% endcontent-ref %}

#### Logging-in From a Trusted Device

If the user logged-in from a trusted device, their login request will automatically be approved.

#### Logging-in From a Non-Trusted Device

If the user logged-in from a different device that is not trusted, they will be presented with a prompt asking them to approve the login from their trusted phone inside your app. We'll cover this in the next guide.

## Validating Cotter's Access Token&#x20;

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

## 🎉 You're done! <a href="#youre-done" id="youre-done"></a>

### Add Email/Phone Verification

You can also verify your user's email or phone number during the sign up process.

{% content-ref url="/pages/-MAYBXzMAs8ZfSEUGux-" %}
[Add Email/Phone Verification](/sdk-reference/flutter/sign-in-with-device/add-email-phone-verification)
{% endcontent-ref %}

### Getting the Logged-in User

Cotter's SDK automatically saves the logged-in user in your device's secure storage. Check out how to get the user information:

{% content-ref url="/pages/-M9awQf5uqyq2X9ZO4JM" %}
[Getting the Logged-in User](/sdk-reference/flutter/getting-the-logged-in-user)
{% endcontent-ref %}

### Getting OAuth Tokens

Cotter also automatically generates an `access_token`, `id_token` , and `refresh_token` that is securely stored in the device's secure storage. Check how to get these tokens:

{% content-ref url="/pages/-M9axG8HPu6f\_at6wCcT" %}
[Getting OAuth Tokens](/sdk-reference/flutter/getting-oauth-tokens)
{% endcontent-ref %}


# Add Email/Phone Verification

Most of the time, it's required for you to check if the user's identifier (their email or phone number) is valid. With Cotter, you can verify your users via email, SMS, or WhatsApp.&#x20;

## Step 1: Set Up Deep Linking

The verification will follow OAuth's PKCE flow which will open an in-app browser where your user can enter the OTP sent to their email/phone.

Pick a unique URL scheme for redirecting the user back to your app after the verification in the in-app browser is successful. For this example, we'll use `myexample://auth_callback` .

**Setup in iOS**

Add the following to your `ios/Runner/Info.plist`.

{% code title="ios/Runner/Info.plist" %}

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

	<!-- ADD THE LINES FROM HERE -->
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLName</key>
			<string>myexample</string> <!-- 👈 Change this to your own URL Scheme -->
			<key>CFBundleURLSchemes</key>
			<array>
				<string>myexample</string> <!-- 👈 Change this to your own URL Scheme -->
			</array>
		</dict>
	</array>
	<!-- TO HERE -->

	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
```

{% endcode %}

**Setup in Android**

Add the following to your `android/app/src/main/AndroidManifest.xml`.

{% code title="android/app/src/main/AndroidManifest.xml" %}

```markup
<manifest ...>
    <application ...> 
    ...

    <!-- Add the lines from here -->
    <activity android:name=".CallbackActivity" >
      <intent-filter android:label="flutter_web_auth">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- 👇 This is for myexample://auth_callback -->
        <data android:scheme="myexample" android:host="auth_callback"/>
      </intent-filter>
    </activity>
    <!-- Until here -->

  </application>
</manifest>
```

{% endcode %}

> You may need to stop `flutter-run` and re-run it to see the changes.

## Step 2: Update your Sign Up Function

Make sure you followed the guide for [Sign In with Device](https://docs.cotter.app/sdk-reference/flutter/sign-in-with-device). Update your [Sign Up Function that you made on Step 2](https://docs.cotter.app/sdk-reference/flutter/sign-in-with-device#step-2-make-a-sign-up-function).

There are 2 ways to do this:

* [**Verify the email/phone number first**](/sdk-reference/flutter/sign-in-with-device/add-email-phone-verification#option-1-verifying-before-registering-the-user), then continue registering the user if verification is successful
* [**Finish registering the user first**](/sdk-reference/flutter/sign-in-with-device/add-email-phone-verification#option-2-verifying-after-registering-the-user), then allow the user to verify their email/phone number later.

### Option 1: Verifying Before Registering the User

This will work the following way:

1. The user will be asked to verify their email or phone number
2. If verification successful, we'll create a new User
3. Then, set up the current device as trusted.

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

```dart
// 2️⃣  Make Sign Up Function
  void signUp(BuildContext context) async {
    try {
      // ❌ Remove this line
      var user = await cotter.signUpWithDevice(identifier: inputController.text);
      
      // ✅ Add the lines from here
      // (1) Verify the user's email
      // (2) Create the user if verification successful
      var user = await cotter.signUpWithEmailOTP(
        redirectURL: "myexample://auth_callback",
        email: inputController.text,
      );
      // (3) Set up the current device as trusted
      user = await user.registerDevice();
      // To here
       
    } catch (e) {
      print(e);
    }
  }
```

{% endtab %}

{% tab title="Phone (with Pre-built UI)" %}

```dart
// 2️⃣  Make Sign Up Function
void signUp(BuildContext context) async {
  try {
    // ❌ Remove this line
    var user = await cotter.signUpWithDevice(identifier: inputController.text);
    
    // ✅ Add the lines from here
    // (1) Verify the user's phone
    // (2) Create the user if verification successful
    var user = await cotter.signUpWithPhoneOTP(
      redirectURL: "myexample://auth_callback",
      channels: [PhoneChannel.SMS, PhoneChannel.WHATSAPP], // optional, default is SMS
    );
    // (3) Set up the current device as trusted
    user = await user.registerDevice();
    // To here
     
  } catch (e) {
    print(e);
  }
}
```

{% endtab %}

{% tab title="SMS" %}

```dart
// 2️⃣  Make Sign Up Function
void signUp(BuildContext context) async {
  try {
    // ❌ Remove this line
    var user = await cotter.signUpWithDevice(identifier: inputController.text);
    
    // ✅ Add the lines from here
    // (1) Verify the user's phone
    // (2) Create the user if verification successful
    var user = await cotter.signInWithPhoneOTPViaSMS(
                  redirectURL: "myexample://auth_callback",
                  phone: inputController.text,
                );
    // (3) Set up the current device as trusted
    user = await user.registerDevice();
    // To here
     
  } catch (e) {
    print(e);
  }
}
```

{% endtab %}

{% tab title="WhatsApp" %}

```dart
// 2️⃣  Make Sign Up Function
void signUp(BuildContext context) async {
  try {
    // ❌ Remove this line
    var user = await cotter.signUpWithDevice(identifier: inputController.text);
    
    // ✅ Add the lines from here
    // (1) Verify the user's phone
    // (2) Create the user if verification successful
    var user = await cotter.signInWithPhoneOTPViaWhatsApp(
                  redirectURL: "myexample://auth_callback",
                  phone: inputController.text,
                );
    // (3) Set up the current device as trusted
    user = await user.registerDevice();
    // To here
     
  } catch (e) {
    print(e);
  }
}
```

{% endtab %}
{% endtabs %}

### Option 2: Verifying After Registering the User

This will work the following way:

1. The user will enter some identifier (email or phone number), and we'll create a new User with that unverified identifier.
2. Set up the current device as trusted.
3. Once the user is logged-in, ask the user to verify their email/phone number

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

```dart
// 2️⃣  Make Sign Up Function
void signUp(BuildContext context) async {
  try {
    var user = await cotter.signUpWithDevice(identifier: inputController.text);
    
    // ✅ Add the line below
    // (3) Verify the user's email 
    user = await user.verifyEmailWithOTP(redirectURL: "myexample://auth_callback");
     
  } catch (e) {
    print(e);
  }
}
```

{% endtab %}

{% tab title="SMS" %}

```dart
// 2️⃣  Make Sign Up Function
void signUp(BuildContext context) async {
  try {
    var user = await cotter.signUpWithDevice(identifier: inputController.text);
    
    // ✅ Add the line below
    // (3) Verify the user's phone 
    user = await user.verifyPhoneWithOTPViaSMS(redirectURL: "myexample://auth_callback");

  } catch (e) {
    print(e);
  }
}
```

{% endtab %}

{% tab title="WhatsApp" %}

```dart
// 2️⃣  Make Sign Up Function
void signUp(BuildContext context) async {
  try {
    var user = await cotter.signUpWithDevice(identifier: inputController.text);
    
    // ✅ Add the line below
    // (3) Verify the user's phone 
    user = await user.verifyPhoneWithOTPViaWhatsApp(redirectURL: "myexample://auth_callback");

  } catch (e) {
    print(e);
  }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Make sure that the `identifier` that you used inside `cotter.signUpWithDevice` has the correct type. (If the identifier is an email, then use `verifyEmailWithOTP`, and so on).
{% endhint %}

## 🎉 You're done! <a href="#youre-done" id="youre-done"></a>

You should now be able to enter your email or phone number during the signup process.


# Authenticate from a Non-Trusted Device

When a user requested to login from a device that they don't trust, the user will be asked to approve the login from a trusted device.

This involves 2 parts:

* **In the Non-Trusted Device:** Login as usual using `cotter.signInWithDevice`
* **In the Trusted Device:** Approve the request using `cotter.checkNewSignInRequest`

## Step 1: Login from the Non-Trusted Device

There's no change here, you just need to login normally using `cotter.signInWithDevice`

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
void login(BuildContext context) async {
  try {
    // 🚀 One-line Login
    var event = await cotter.signInWithDevice(identifier: inputController.text, context: context);
    print(event);
  } catch (e) {
    print(e);
  }
}
```

The SDK will detect that the request is coming from a non-trusted device, and will present a prompt like this:

![Login Request from a Non-Trusted Device](/files/-M9b3oMwZ0XqALG0bl7W)

The function will wait for the request to be approved, or timeout after 3 minutes.

## Step 2: Approve the login request from the Trusted Device

Inside your app that is inside the Trusted Device, call the function `cotter.checkNewSignInRequest` . The user need to be logged-in to approve a login request.

#### How does my app know if this device is trusted?

Get the logged-in user, then call `cotter.isThisDeviceTrusted()` to check.

```dart
 Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
 
void isThisDeviceTrusted() async {
  try {
    var user = await cotter.getUser();
    var trusted = await user.isThisDeviceTrusted();
    print(trusted);
  } catch (e) {
    print(e);
  }
}
```

#### Approving the request

In the future, you can set up a push-notification to receive the login request in your app. For now, present a button in your Settings page and call `user.checkNewSignInRequest` .

```dart
void approveLogin() async {
  try {
    var user = await cotter.getUser();
    Event event = await user.checkNewSignInRequest(context: context);
    print(event);
  } catch (e) {
    print(e);
  }
}
```

This will present the user with a prompt asking if the user want to approve the login request.

![Approving the Login Request from a Trusted Device](/files/-M9b3vwysxWBJGXTv1YD)

## What happens then?

If the user approved the request, you'll get back an event with `{approved: true}` **in the non-trusted device in Step 1**. The SDK will [automatically store the logged-in user](/sdk-reference/flutter/getting-the-logged-in-user) and [access tokens](/sdk-reference/flutter/getting-oauth-tokens) in the device's secure storage.


# Sign in with Email/Phone Number

Our Flutter SDK offers the easiest way to integrate Cotter 's email/phone verification. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Email/Phone Number**](/features/verify-email-phone) works.

### Overview <a href="#overview" id="overview"></a>

Verifying **email and phone number** in your mobile app using our Flutter SDK consists of the following steps:

1. Call Cotter's Login function
2. Setup deep linking
3. Receive user's email or phone number, and whether or not it's verified

### What you're building <a href="#what-youre-building" id="what-youre-building"></a>

![Sign in with Phone Number via SMS or WhatsApp using Cotter's Flutter SDK](/files/-MAUae53veN1fusT15hG)

## Steps

1. [Import Cotter as a dependency](/sdk-reference/flutter/sign-in-with-email-phone-number#step-1-import-cotter-as-a-dependency)
2. &#x20;[Setup Deep Linking](/sdk-reference/flutter/sign-in-with-email-phone-number#step-2-setup-deep-linking)
3. [Signing up](/sdk-reference/flutter/sign-in-with-email-phone-number#step-3-signing-up)
4. [Logging in](/sdk-reference/flutter/sign-in-with-email-phone-number#step-4-logging-in)
5. [Verifying a logged-in user](/sdk-reference/flutter/sign-in-with-email-phone-number#step-5-verifying-a-logged-in-user)

### Step 1: Import Cotter as a dependency

Add Cotter to your `pubspec.yaml` , then run `flutter pub get`.

{% code title="pubspec.yaml" %}

```yaml
dependencies:
  cotter:
```

{% endcode %}

Check the latest releases in [pub.dev](https://pub.dev/packages/cotter#-installing-tab-). You may need to restart your flutter for it to run pod install (stop flutter run and re run it).

**For Android**: Update `minSdkVersion` to `18` [following the installation instructions](/sdk-reference/flutter).

### Step 2: Setup Deep Linking

The verification will follow OAuth's PKCE flow which will open an in-app browser where your user can enter the OTP sent to their email/phone.&#x20;

Pick a unique URL scheme for redirecting the user back to your app after the verification in the in-app browser is successful. For this example, we'll use `myexample://auth_callback` .

{% hint style="warning" %}
Make sure your URL scheme (the front part before `://`) doesn't have an underscore or other special characters. To test it out, enter your Redirect URL here: <https://jsfiddle.net/omd02jn5/>
{% endhint %}

**Setup in iOS**

Add the following to your `ios/Runner/Info.plist`.

{% code title="ios/Runner/Info.plist" %}

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

	<!-- ADD THE LINES FROM HERE -->
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLName</key>
			<string>myexample</string> <!-- 👈 Change this to your own URL Scheme -->
			<key>CFBundleURLSchemes</key>
			<array>
				<string>myexample</string> <!-- 👈 Change this to your own URL Scheme -->
			</array>
		</dict>
	</array>
	<!-- TO HERE -->

	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
```

{% endcode %}

**Setup in Android**

Add the following to your `android/app/src/main/AndroidManifest.xml`.

{% code title="android/app/src/main/AndroidManifest.xml" %}

```markup
<manifest ...>
    <application ...> 
    ...

    <!-- Add the lines from here -->
    <activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
      <intent-filter android:label="flutter_web_auth">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- 👇 This is for myexample://auth_callback -->
        <data android:scheme="myexample" android:host="auth_callback"/>
      </intent-filter>
    </activity>
    <!-- Until here -->

  </application>
</manifest>
```

{% endcode %}

> You may need to stop `flutter-run` and re-run it to see the changes.

### Step 3: Signing Up

Make sure you have set up the deep-linking above.

{% tabs %}
{% tab title="Using Email" %}
Use the sign up method to:

* Verify the user's email
* Then create a new user in Cotter if successful

```dart
import 'package:cotter/cotter.dart'; // Import Cotter

Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var user = await cotter.signUpWithEmailOTP(
    redirectURL: "myexample://auth_callback",
    email: inputController.text, // Optional, if you leave this blank, user can enter email in the in-app browser
  );
  print(user);
} catch (e) {
  print(e);
}
```

{% endtab %}

{% tab title="Using Phone Number" %}
Use the sign up method to:

* Verify the user's phone number
* Then create a new user in Cotter if successful

**Option 1:** You want to use Cotter's input form inside the in-app browser. This helps with validating the input.

```dart
import 'package:cotter/cotter.dart'; // Import Cotter

Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var user = await cotter.signUpWithPhoneOTP(
    redirectURL: "myexample://auth_callback",
    channels: [PhoneChannel.SMS, PhoneChannel.WHATSAPP], // optional, default is SMS
  );
} catch (e) {
  print(e);
}
```

**Option 2:** You want to use your own input form and buttons. You can present 2 buttons to allow sending the OTP via WhatsApp or SMS.

* **Using SMS:**

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var user = await cotter.signUpWithPhoneOTPViaSMS(
              redirectURL: "myexample://auth_callback",
              phone: inputController.text,
            );
} catch (e) {
  print(e);
}
```

* **Using WhatsApp:**

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var user = await cotter.signUpWithPhoneOTPViaWhatsApp(
              redirectURL: "myexample://auth_callback",
              phone: inputController.text,
            );
} catch (e) {
  print(e);
}
```

{% endtab %}
{% endtabs %}

### Step 4: Logging-In

{% tabs %}
{% tab title="Using Email" %}
To authenticate an existing user by verifying their email:

> This method will create a new user if one doesn't exist.

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var user = await cotter.signInWithEmailOTP(
        redirectURL: "myexample://auth_callback",
        email: inputController.text, // Optional, if you leave this blank, user can enter email in the in-app browser
      );
} catch(e) {
  print(e);
}
```

{% endtab %}

{% tab title="Using Phone Number" %}
To authenticate by verifying user's phone number:

**Option 1:** You want to use Cotter's input form inside the in-app browser. This helps with validating the input.

> This method will create a new user if one doesn't exist.

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var user = await cotter.signInWithPhoneOTP(
    redirectURL: "myexample://auth_callback",
    channels: [PhoneChannel.SMS, PhoneChannel.WHATSAPP], // optional, default is SMS
  );
} catch (e) {
  print(e);
}
```

**Option 2:** You want to use your own input form and buttons. You can present 2 buttons to allow sending the OTP via WhatsApp or SMS.

* **Using SMS:**

```dart
try {
  var user = await cotter.signInWithPhoneOTPViaSMS(
              redirectURL: "myexample://auth_callback",
              phone: inputController.text,
            );
} catch (e) {
  print(e);
}
```

* **Using WhatsApp:**

```dart
try {
  var user = await cotter.signInWithPhoneOTPViaWhatsApp(
              redirectURL: "myexample://auth_callback",
              phone: inputController.text,
            );
} catch (e) {
  print(e);
}
```

{% hint style="info" %}
To send code/link via SMS or WhatsApp, you'll need to add some balance to you project in the [Dashboard](https://dev.cotter.app/).
{% endhint %}
{% endtab %}
{% endtabs %}

### Step 5: Verifying a logged-in user

{% tabs %}
{% tab title="Using Email" %}
To verify the email of a user that is currently logged-in:

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var user = await cotter.getUser();
  user = await user.verifyEmailWithOTP(redirectURL: "myexample://auth_callback");
} catch (e) {
  print(e);
}
```

{% endtab %}

{% tab title="Using Phone Number" %}
To verify the phone number of a user that is currently logged-in:

* Using SMS:

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var user = await cotter.getUser();
  user = await user.verifyPhoneWithOTPViaSMS(redirectURL: "myexample://auth_callback");
} catch (e) {
  print(e);
}
```

* Using WhatsApp:

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var user = await cotter.getUser();
  user = await user.verifyPhoneWithOTPViaWhatsApp(redirectURL: "myexample://auth_callback");
} catch (e) {
  print(e);
}
```

{% endtab %}
{% endtabs %}

## Validating Cotter's Access Token&#x20;

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

## 🎉 You're done! <a href="#youre-done" id="youre-done"></a>

### Getting the Logged-in User

Cotter's SDK automatically saves the logged-in user in your device's secure storage. Check out how to get the user information:

{% content-ref url="/pages/-M9awQf5uqyq2X9ZO4JM" %}
[Getting the Logged-in User](/sdk-reference/flutter/getting-the-logged-in-user)
{% endcontent-ref %}

### Getting OAuth Tokens

Cotter also automatically generates an `access_token`, `id_token` , and `refresh_token` that is securely stored in the device's secure storage. Check how to get these tokens:

{% content-ref url="/pages/-M9axG8HPu6f\_at6wCcT" %}
[Getting OAuth Tokens](/sdk-reference/flutter/getting-oauth-tokens)
{% endcontent-ref %}

## Securing your Project

Since you'll be using your API Key from a front-end website or mobile app, your `API_KEY_ID` is exposed to anyone inspecting your code. Here are some ways to prevent abuse:

* [Only allow your website/app to use your API Key](/protecting-your-account/only-allow-your-website-app-to-use-your-api-key)
* [Rate Limit the number of authentication requests](/protecting-your-account/rate-limit)
* [Enable reCAPTCHA to prevent automated abuse](/protecting-your-account/enable-recaptcha-to-protect-against-automated-abuse)


# Getting the Logged-in User

When the user authenticates successfully, Cotter's SDK will automatically store the logged-in user in the device's secure storage.

To get the user information, call `cotter.getUser()` :

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
void getLoggedInUser() async {
  try {
    var user = await cotter.getUser();
    print(user);
  } catch (e) {
    print(e);
  }
}
```

This function returns the [User object](/api-reference/user-api/user-object).

If the user's `id_token` is expired, this function will automatically attempt to refresh the token, resulting in refreshing the user's session, and return the User object if successful. Otherwise, this function will throw an error if the session is expired.

{% hint style="warning" %}
Please use the identifier (email/phone number) as your main way to identify users, **Cotter's User ID is deprecated.**
{% endhint %}


# Getting OAuth Tokens

When a user successfully logged-in, Cotter will generate an `access_token` , an `id_token` , and a `refresh_token` [that you can use in your backend API](https://blog.cotter.app/passwordless-login-with-email-and-json-web-token-jwt-authentication-with-nextjs/).&#x20;

The SDK automatically stores these tokens in the device's secure storage.

#### Getting The Tokens

```dart
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
  var accessToken = await cotter.getAccessToken();
  var idToken = await cotter.getIDToken();
  var refreshToken = await cotter.getRefreshToken();
} catch (e) {
  print(e);
}
```

This function will automatically refresh the `access_token` and `id_token` if it's expired.

## OAuth Token Specification

{% content-ref url="/pages/-M4HUyse-9aPxMqUoSRy" %}
[Cotter's OAuth 2.0 Tokens Specification](/getting-access-token/handling-authentication-with-cotter)
{% endcontent-ref %}


# Signing a User Out

This page shows you how to log your users out of your application

## Using our logout function

We provide a simple logout function that signs out the currently logged in user. Here's how to use it:

```dart
Cotter cotter = new Cotter(apiKeyID: YOUR_API_KEY_ID);
void yourLogoutFunction() async {
    await cotter.logOut();
    _goToLoginPage(); // your own logic after logging out
}
```

#### What does \`logOut\` do ?

It clears out OAuth tokens inside the device's secure storage. The cleared out items include:

1. Access Token
2. Refresh Token
3. ID Token
4. Token Type


# iOS

The easiest way to add Passwordless Login and Email/Phone Number Verification to your iOS app.

## Complete Guides

#### **Sign in with Device**

Allow users to login automatically from a device that they trust.

#### Sign in with Email/Phone Number

Authenticate users by sending a Magic Link or OTP via email, SMS, or WhatsApp

{% content-ref url="/pages/-M4q-LjGBIcIq3PgnYt6" %}
[Sign In with Device](/sdk-reference/ios/ios-sdk-passwordless-login)
{% endcontent-ref %}

{% content-ref url="/pages/-M35aUobglanfiWqjf7S" %}
[Sign In with Email/Phone Number](/sdk-reference/ios/ios-sdk-verify-email-phone)
{% endcontent-ref %}

## Quickstart Guides

{% content-ref url="/pages/-M6EaO2M9ot00du8W\_bR" %}
[iOS – Sign in with Device](/quickstart-guides/ios-quickstart)
{% endcontent-ref %}


# Sign In with Email/Phone Number

Our iOS SDK offers the easiest way to verify email/phone numbers in your Swift app. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Email/Phone Number**](/features/verify-email-phone) works.

## Overview

Verifying email and phone number in your mobile app using our iOS SDK consists of the following steps:&#x20;

1. Import Cotter
2. Call Cotter's Login function
3. Receive user's email or phone number, and whether or not it's verified

## What you're building

![Cotter's iOS SDK](/files/-M35bY9Zdu6RH-CxKHWp)

## Steps

1. [Import Cotter as dependency](/sdk-reference/ios/ios-sdk-verify-email-phone#step-1-import-cotter-as-dependency)
2. [Initialize Cotter with your API Key](/sdk-reference/ios/ios-sdk-verify-email-phone#step-2-initialize-cotter-with-your-api-key)
3. [Call Cotter's Login Function](/sdk-reference/ios/ios-sdk-verify-email-phone#step-3-call-cotters-login-function)
4. [Receive Token](/sdk-reference/ios/ios-sdk-verify-email-phone#step-4-receive-token)

### Step 1. Import Cotter as dependency

We use Cocoapods as our SDK host. If you're using Cocoapods, add this to your Podfile

```
pod 'Cotter'
```

Then simply run `pod install`

### Step 2. Initialize Cotter with your API Key

You will have to do `import Cotter` on the file that will use Cotter. Then do initialization as follows

```swift
import Cotter

...

let cotter = Cotter(
    apiSecretKey: <your-api-secret-key>,
    apiKeyID: <your-api-key-id>,
    cotterURL: "https://www.cotter.app/api/v0",
    userID: <your-user-id>, // user's id that will be created later
    configuration: <your-cotter-config>
)
```

example:

```swift
import Cotter

...

let cotter = Cotter(
    apiSecretKey: "<API_SECRET_KEY>",
    apiKeyID: "<API_KEY_ID>",
    cotterURL: "https://www.cotter.app/api/v0",
    userID: "hello@example.com",
    configuration: [:]
  );
```

### Step 3. Call Cotter's login function

```swift
cotter.startPasswordlessLogin(
            parentView: <your-view-controller>,
            input: <your-input-text>,
            identifierField: <identifier>,
            type: <PHONE-or-EMAIL>,
            directLogin: <true-or-false>,
            cb: <your-callback>
        )
```

example:

```swift
@available(iOS 12.0, *)
class LoginViewController: UIViewController {
    @IBOutlet weak var loginButton: UIButton!
    @IBOutlet weak var phoneInput: UITextField!

    ...

    @IBAction func login(_ sender: Any) {
        // get the text input
        let textInput = self.phoneInput.text ?? ""

        func authCb(accessToken: String, error: Error?) -> Void{
            guard let error = error else {
                print("error logging in!")
                return
            }

            // error handling
            print("success!")
        }

        let cotter = Cotter(
          apiSecretKey: "<API_SECRET_KEY>",
          apiKeyID: "<API_KEY_ID>",
          cotterURL: "https://www.cotter.app/api/v0",
          userID: "hello@example.com",
          configuration: [:]
        );

        cotter.startPasswordlessLogin(
            parentView: self,
            input: textInput,
            identifierField: "phone",
            type: "PHONE",
            directLogin: true,
            cb: authCb
        )
    }
}
```

To login and enter email or phone number in Cotter's window, simply set the directLogin to false and set the input to empty string

```swift
cotter.startPasswordlessLogin(
    parentView: self,
    input: "",
    identifierField: "phone",
    type: "PHONE",
    directLogin: false,
    cb: authCb
)
```

{% hint style="info" %}
To send code/link via SMS or WhatsApp, you'll need to add some balance to you project in the [Dashboard](https://dev.cotter.app/).
{% endhint %}

### Step 4: Receive Token

The token will be received in the callback function. The token will be in the form as the following:

```javascript
"token": {
  "identifier": "+12345678910",
  "identifier_type": "PHONE",
  "receiver": "<your API_KEY_ID>",
  "expire_at": "1584687591",
  "signature": "G8dOKR6qLj+GiB0pD2aggVVdYddFoyy..."
}
```

The token contains the user's phone number, your API\_KEY\_ID in the receiver field, and a signature to ensure this is from Cotter. The token tells you that this identifier is verified.&#x20;

{% hint style="success" %}
You should include this JSON Object into your call to your backend for **Login** or **Registration**. Your backend should then verify that the [signature of the token](/sdk-reference/ios/ios-sdk-verify-email-phone#step-5-validating-token) is valid.
{% endhint %}

## Validating Cotter's Access Token&#x20;

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

## 🎉 You're done!

## Securing your Project

Since you'll be using your API Key from a front-end website or mobile app, your `API_KEY_ID` is exposed to anyone inspecting your code. Here are some ways to prevent abuse:

* [Only allow your website/app to use your API Key](/protecting-your-account/only-allow-your-website-app-to-use-your-api-key)
* [Rate Limit the number of authentication requests](/protecting-your-account/rate-limit)
* [Enable reCAPTCHA to prevent automated abuse](/protecting-your-account/enable-recaptcha-to-protect-against-automated-abuse)


# Sign In with Device

Cotter's iOS SDK offers the easiest way to integrate "Sign in with device" to your ios app. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Device**](/features/passwordless-login) works.

{% hint style="warning" %}
There are major updates planned for this feature. [Contact us in Slack](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA) so we can help you prepare for it.
{% endhint %}

## **Overview**

Authenticating users using Trusted Devices with Cotter's iOS SDK consists of the following steps:&#x20;

1. Import and Initialize Cotter
2. Calling functions to register devices a Trusted Device
3. Calling functions to authenticate from Trusted and Non-Trusted Device

## **What you're building**

![Trusted Devices on Cotter's iOS SDK](/files/-M4unD9nGFHygOK6WdJj)

## **Steps**

1. [Import Cotter as a dependency](/sdk-reference/ios/ios-sdk-passwordless-login#step-1-import-cotter-as-a-dependency)
2. [Set Allowed Methods](/sdk-reference/ios/ios-sdk-passwordless-login#step-2-set-allowed-authentication-methods-in-the-developer-dashboard) in the Dashboard to allow Trusted Devices
3. [Create a User](/sdk-reference/ios/ios-sdk-passwordless-login#3-creating-a-user): Create a user in Cotter by specifying your user ID.
4. [Initialize Cotter in your main View Controller](/sdk-reference/ios/ios-sdk-passwordless-login#4-initialize-cotter)
5. [Register current device as a Trusted Device](/sdk-reference/ios/ios-sdk-passwordless-login#5-register-this-device-as-a-registered-trusted-device)
6. [Authenticate from a Trusted Device](/sdk-reference/ios/ios-sdk-passwordless-login#6-authenticate-from-a-trusted-device)

### Step 1. Import Cotter as a dependency

We use Cocoapods as our SDK host. If you're using Cocoapods, add this to your Podfile

```
pod 'Cotter'
```

Then simply run `pod install`

### Step 2. Set allowed Authentication Methods in the Developer Dashboard

You need to set allowed methods for authenticating your users. To allow TRUSTED DEVICES, go to <https://dev.cotter.app/rules>

![Set Trusted Device as an Allowed Method](/files/-M4unVMnNfijkPUZ5bPg)

> Remember to select the correct project in the dropdown list.

### Step 3. Creating a User

#### Registering a new User

```swift
Passwordless.shared.registerWith(identifier: input){ (user: CotterUser?, err:Error?) in
    if err != nil {
        // handle error here
    }
    
    if user != nil {
        // you can save cotter's user id in your database here
    }
}
```

Check out [Create a User API Reference](/api-reference/user-api#create-a-user) for a full description

### Step 4. Initialize Cotter

You can now use the SDK to enroll and verify Trusted Devices for the user you just created.

In your main view controller, initialize Cotter with API keys, Cotter's URL to use (prod/test), and your User ID.

Production Endpoint: `https://www.cotter.app/api/v0`

```swift
let cotter = Cotter(
  apiSecretKey: <your-api-secret-key>,
  apiKeyID: <your-api-key-id>,
  cotterURL: "https://www.cotter.app/api/v0",
  userID: <your-user-id>, // user's id that will be registered/created later
  configuration: [:]
)
```

Example:

```swift
class SomeUIVC: UIViewController {
  var cotter: Cotter?

  override func viewDidLoad() {
    super.viewDidLoad()

    // cotter initialization here
    self.cotter = Cotter(
      apiSecretKey: "588d6f67-0981-4718-899b-bcd512de1aca",
      apiKeyID: "w4FK6Zz0XIhtGY3o5biI",
      cotterURL: "https://www.cotter.app/api/v0",
      userID: "hello@example.com",
      configuration: [:]
    )
  }
}
```

### Step 5. Register this device as a Registered Trusted Device

Right after your user finished their registration to your app, you should automatically register the device as a Trusted Device (or prompt your user if they want to make this device as a Trusted Device).

To enroll the current device as a TrustedDevice, call the `CotterAPIService.shared.enrollTrustedDevice` function, which takes in a `userID` as `String` and a Callback function as parameters.

Example:

```swift
func enrollTrustedDeviceForUser(userID: String) {
  CotterAPIService.shared.enrollTrustedDevice(userID: userID, cb: { response in
      switch response {
      case .success(let user):
        print("Successfully enrolled existing user \(user.id): \(user.enrolled)")
      case .failure(let err):
        // you can put exhaustive error handling here
        print(err.localizedDescription)
      }
  })
}

enrollTrustedDeviceForUser(userID: "hello@example.com")
```

JSON Result:

```swift
{
  "ID": "746500ae-a5cd-4692-8fd0-49d57cf7cc57", // User ID from Cotter's system
  "created_at": "2020-02-25T04:08:26.174597Z",
  "update_at": "2020-02-27T22:20:31.333154814Z",
  "deleted_at": null,
  "issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API KEY ID
  "client_user_id": "1014", // you client's user ID
  "enrolled": [ // Enrolled Authentication Methods
    "PIN",
    "BIOMETRIC",
    "TRUSTED_DEVICE" // This should be added to enrolled methods once enrollDevice successful
  ],
  "default_method": "TRUSTED_DEVICE" // This will automatically be the last enrolled method, which is Trusted Device in this case
}
```

This method is only for the first Trusted Device. You will get an error if you attempt to enroll another Trusted Device using enrollDevice when there's already a Trusted Device for the account. To enroll other devices, see [Add a new Trusted Device](/sdk-reference/ios/ios-sdk-passwordless-login/add-a-new-trusted-device).

### Step 6. Authenticate from a Trusted Device

To authenticate a device, call the `cotter?.loginWithTrustedDevice` function, which takes in the current view controller and a Callback function as parameters. This will automatically detect whether the current device is a Trusted Device or not.

Example:

```swift
class SomeUIVC: UIViewController {
  var cotter: Cotter?

  override func viewDidLoad() {
    super.viewDidLoad()

    self.cotter = Cotter(
      apiSecretKey: "588d6f67-0981-4718-899b-bcd512de1aca",
      apiKeyID: "w4FK6Zz0XIhtGY3o5biI",
      cotterURL: "https://www.cotter.app/api/v0",
      userID: "hello@example.com",
      configuration: [:]
    )
  }

  // On Button Click
  @IBAction func login(_ sender: UIButton) {
    func callback(token: String, err: Error?) {
      if err != nil {
        // Failed login. Go to Error Page View Controller
        print(err?.localizedDescription)
        self.performSegue(withIdentifier: "segueToErrorView", sender: self)
        return
      }
      // Successful login. Go to Dashboard View Controller
      self.performSegue(withIdentifier: "segueToDashboard", sender: self)
    }

    /* Login using Cotter's Trusted Device Feature. */
    self.cotter?.loginWithTrustedDevice(vc: self, cb: callback)
  }
}
```

JSON Result:

```swift
{
  "ID": 1361, // Event ID
  "CreatedAt": "2020-02-27T22:22:48.705212512Z",
  "UpdatedAt": "2020-02-27T22:22:48.705212512Z",
  "DeletedAt": null,
  "client_user_id": "1014", // your client's User ID
  "issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API Key
  "event": "<EVENT NAME>",// requested event (LOGIN, or TRANSACTION, etc)
  "ip": "192.168.232.2", 
  "location": "Unknown",
  "timestamp": "1582842167",
  "method": "TRUSTED_DEVICE", // auth method: TRUSTED_DEVICE (other choices are PIN / BIOMETRIC)
  "new": false, // Is this a new pending event. More explanation below about Non-Trusted Device
  "approved": true, // Is this event approved.
  "signature": "oonMGCAxp3..." // Signature to make sure this event comes from Cotter's server
}
```

An `Event` is an Authentication Event, where your app requests Cotter's SDK to authenticate the user. `EVENT_NAME` refers to what type of authentication event was requested. Example: `LOGIN` event, `TRANSACTION` event, `UPDATE_PROFILE` event, etc.

When an Authentication Event is requested using method `TRUSTED_DEVICE`, there are 2 possible cases:

### Case 1: The current device is a Trusted Device

If the current device is a Trusted Device, it should automatically be approved, and you will receive a JSON result containing the requested `Event` and whether or not it's approved. The approval is based on whether or not the signature included in the request from the SDK is valid.

```swift
{
  ...
  "method": "TRUSTED_DEVICE",
  "new": false, // Is this a new pending event (should be false).
  "approved": true // Is this event approved (should be true).
}
```

You should see a result that the event is not new, and that it's approved. This is because the signature from the Trusted Device is sufficient to prove that the device is authorized.

The callback that you provided to the `cotter?.loginWithTrustedDevice` function will be called on login success or failure. For instance, as seen in the above example, if the login is approved, the callback function will be called with no error, and you can redirect the user to the Dashboard View Controller. However, if the login is not approved, then an error will exist and you can redirect the user to the Error Page View Controller.

When passing this Event Response to your backend, you need to check if this JSON is valid and if it comes from Cotter's server.

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

### Case 2: The current device is not a Trusted Device

We'll cover this in the next guide:

{% content-ref url="/pages/-M4q65HwSchGnw-\_fZc6" %}
[Add a New Trusted Device](/sdk-reference/ios/ios-sdk-passwordless-login/add-a-new-trusted-device)
{% endcontent-ref %}


# Authenticate from a Non-Trusted Device

## **Steps**

There are 2 steps in authenticating a Non-Trusted Device

1. [Make a request](/sdk-reference/ios/ios-sdk-passwordless-login/authenticate-from-a-non-trusted-device#step-1-making-an-authentication-request) from the Non-Trusted Device and wait for approval
2. [Approve the request](/sdk-reference/ios/ios-sdk-passwordless-login/authenticate-from-a-non-trusted-device#step-2-how-to-approve-a-pending-request-from-a-trusted-device) from a Trusted Device.&#x20;

### **Step 1: Making an Authentication Request**

Same as how it works with [Authenticating from a Trusted Device](/sdk-reference/ios/ios-sdk-passwordless-login#step-6-authenticate-from-a-trusted-device), to authenticate a device, call the `cotter?.loginWithTrustedDevice` function. **This will automatically detect whether the current device is a Trusted Device or not.**

Example:

```swift
class SomeUIVC: UIViewController {
  var cotter: Cotter?

  override func viewDidLoad() {
    super.viewDidLoad()

    self.cotter = Cotter(
      apiSecretKey: "588d6f67-0981-4718-899b-bcd512de1aca",
      apiKeyID: "w4FK6Zz0XIhtGY3o5biI",
      cotterURL: "https://www.cotter.app/api/v0",
      userID: "hello@example.com",
      configuration: [:]
    );
  }

  // On Button Click
  @IBAction func login(_ sender: UIButton) {
    func callback(token: String, err: Error?) {
      if err != nil {
        // Failed login. Go to Error Page View Controller
        print(err?.localizedDescription)
        self.performSegue(withIdentifier: "segueToErrorView", sender: self)
        return
      }
      // Successful login. Go to Dashboard View Controller
      self.performSegue(withIdentifier: "segueToDashboard", sender: self)
    }

    /* Login using Cotter's Trusted Device Feature. */
    self.cotter?.loginWithTrustedDevice(vc: self, cb: callback)
  }
}
```

JSON Result:

```swift
{
  "ID": 1361, // Event ID
  "CreatedAt": "2020-02-27T22:22:48.705212512Z",
  "UpdatedAt": "2020-02-27T22:22:48.705212512Z",
  "DeletedAt": null,
  "client_user_id": "1014", // your client's User ID
  "issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API Key
  "event": "<EVENT NAME>",// requested event (LOGIN, or TRANSACTION, etc)
  "ip": "192.168.232.2", 
  "location": "Unknown",
  "timestamp": "1582842167",
  "method": "TRUSTED_DEVICE", // auth method: TRUSTED_DEVICE (other choices are PIN / BIOMETRIC)
  "new": false, // Is this a new pending event. More explanation in the next section about Non-Trusted Device
  "approved": true, // Is this event approved.
  "signature": "oonMGCAxp3..." // Signature to make sure this event comes from Cotter's server
}
```

**Event Name**

The `event` refers to what type of authentication event was requested. Example: `LOGIN` event, `TRANSACTION` event, `UPDATE_PROFILE` event, etc.

**Callback Class**

The callback should be filled with the View Controller to go to when the request is approved. For example, if this is a login request, and the request is approved, then the callback can perform a segue to your Dashboard View Controller. Else, if the login completely failed (`.failure`), then the callback can perform a segue to your 'Login Failed' View Controller.

When an Authentication Event is requested `TRUSTED_DEVICE`, there are 2 possible cases:

#### **Case 1: The current device is a Trusted Device**

You will be automatically approved if the request is coming from a Trusted Device. This is covered in [Authenticate from a Trusted Device](/sdk-reference/ios/ios-sdk-passwordless-login#step-6-authenticate-from-a-trusted-device).

#### **Case 2: The current device is NOT a Trusted Device**

If the current device is **not** a Trusted Device, then it will create a **pending event**. This pending event will need to be **approved by a Trusted Device**. You should see a JSON result looking like:

```swift
{
  ...
  "method": "TRUSTED_DEVICE",
  // This is a pending event. 
  // It will turn to false once there's a Trusted Device 
  // responded to approve or reject this event
  "new": true, 

  // Since it's a pending event, it's not yet approved.
  "approved": false
}
```

The SDK will automatically show a prompt for the user to approve the event from a Trusted Device.

### **Step 2: How to approve a pending request from a Trusted Device**

> Note: This is the flow for the user's trusted device to approve the login from the non-trusted device.

To check if there's a pending event for this user, you can call the `cotter?.getEventTrustedDevice` method from a Trusted Device. You can also [setup a Push Notification to be sent to the Trusted Device](/sdk-reference/ios/ios-sdk-passwordless-login/ios-push-notification) when there's a new Login Request.

Example:

```swift
class SomeUIVC: UIViewController {
  var cotter: Cotter?

  override func viewDidLoad() {
    super.viewDidLoad()

    self.cotter = Cotter(
      apiSecretKey: "588d6f67-0981-4718-899b-bcd512de1aca",
      apiKeyID: "w4FK6Zz0XIhtGY3o5biI",
      cotterURL: "https://www.cotter.app/api/v0",
      userID: "hello@example.com",
      configuration: [:]
    )
  }

  // On Button Click
  @IBAction func checkForPendingEvent(_ sender: UIButton) {

    /* Checks for pending event associated with the userID. If an event exists, 
        a prompt in the trusted device would pop up. If not, nothing happens. 
        As such, you don't need to pass in a callback function in this case. */
    self.cotter?.getEventTrustedDevice(vc: self, cb: nil)
  }
}
```

> In the future, your app that is installed in a Trusted Device will receive a notification and this method is invoked when the notification is opened.

For now, you should tell your user to press a button and invoke this method when they try to login from a non-trusted device.

The SDK will open a prompt in the Trusted Device asking if the user want to approve or reject the authentication request

![Prompt to approve request from a Trusted Device](/files/-M4uoEprkSyImPXQGf6w)

### **Receiving the result and check if the request was approved**

> Note: This is the flow for the user's non-trusted device that requested the login.

#### **Case 1: The request is approved**

When the request is approved, the SDK will automatically redirect to your Callback function, which will be in the form of [`FinalAuthCallback`](/sdk-reference/ios/older-versions/ios-sdk#the-callbacks).

#### Handle the response in your callback class

If the login is approved, then an `Error` does not exist, and you can redirect the user to, for instance, the Dashboard View Controller (Successful login).

Example:

```swift
func callback(token: String, err: Error?) {
  if err != nil {
    // Failed login. Go to Error Page View Controller
    print(err?.localizedDescription)
    self.performSegue(withIdentifier: "segueToErrorView", sender: self)
    return
  }
  // Successful login. Go to Dashboard View Controller
  self.performSegue(withIdentifier: "segueToDashboard", sender: self)
}
```

#### **Case 2: The request is rejected**

However, if the request is not approved for 1 minute, the prompt in the Non-Trusted Device will show an error message (will auto-dismiss in 3 seconds). The prompt will not show an error immediately when the Trusted Device denied the request, i.e. there is no difference between rejection and no response from the Trusted Device.

![Authentication request from a Non-Trusted Device](/files/-M4uoTKTWEXuqEXE0n1g)

In this case, after the prompt is dismissed, the user will remain on the same view where he/she started the prompted the authentication request.


# Push Notification

In this tutorial, we're going to setup push notification for authenticating non-trusted devices.

## Prerequisites

* Your APNS Certificate (.p12 file)
* Your APNS Certificate's password (optional)

## Setting up

![](/files/-MCiZzIO8ehb7GRYrVTb)

* Go to developer [dashboard](https://dev.cotter.app/) > Settings > Notifications
* Make sure you picked **APNS(iOS)**
* Upload your APNS certificate, and the password if you have a password set for your certificate

## That's it.

Your users should be able to receive push notification authentication request. If you haven't configured Cotter in your iOS Application, follow this [guide](/quickstart-guides/ios-quickstart).


# Check if Trusted Device is Enrolled

There are 3 methods that you can use to check about Trusted Device enrollment:&#x20;

1. [Check if this device is a Trusted Device](/sdk-reference/ios/ios-sdk-passwordless-login/check-if-trusted-device-is-enrolled#check-if-this-device-is-a-trusted-device)
2. [Check if this account has any Trusted Device](/sdk-reference/ios/ios-sdk-passwordless-login/check-if-trusted-device-is-enrolled#check-if-this-account-has-any-trusted-device)&#x20;

## **Check if this device is a Trusted Device**

Example:

```swift
func checkTrustedDeviceStatusForThisDevice(userID: String) {
  func callback(response: CotterResult<EnrolledMethods>) {
    switch response {
    case .success(let resp):
      if resp.enrolled && resp.method == "TRUSTED_DEVICE" {
        // This user's is enrolled in trusted device feature
      }
    case .failure(let err):
      // Server returned error
    }
  }

  CotterAPIService.shared.getTrustedDeviceStatus(userID: userID, cb: callback)
}
```

JSON Result:

```swift
{
  "enrolled": true, // If this user's device is enrolled in the below method, will show true
  "method": "TRUSTED_DEVICE" 
}
```

## **Check if this account has any Trusted Device**

Example:

```swift
func checkTrustedDeviceStatusForUser(userID: String) {
  func callback(response: CotterResult<EnrolledMethods>) {
    switch response {
    case .success(let resp):
      if resp.enrolled && resp.method == "TRUSTED_DEVICE" {
        // This user is enrolled in trusted device feature (may not be on this device but another one)
      }
    case .failure(let err):
      // Server returned error
    }
  }

CotterAPIService.shared.getTrustedDeviceEnrolledAny(userID: userID, cb: cb)
}
```

JSON Result:

```swift
{
  "enrolled": true, // If user is enrolled in the below method, will show true
  "method": "TRUSTED_DEVICE" 
}
```


# Add a New Trusted Device

## Steps

There are 2 steps that's needed to add a new Trusted Device:&#x20;

1. [Show the QR Code](/sdk-reference/ios/ios-sdk-passwordless-login/add-a-new-trusted-device#step-1-show-the-qr-code-of-the-new-device) of the New Device&#x20;
2. [Scan the New Device's QR Code](/sdk-reference/ios/ios-sdk-passwordless-login/add-a-new-trusted-device#step-2-scanning-the-qr-code-from-a-trusted-device) using the Trusted Device

![Scan the New Device's QR Code using the Trusted Device](/files/-M4uoteieJmjnucL2sPA)

## **Step 1:  Show the QR Code of the New Device**

To show the QR Code of the new device:

Example:

```swift
class SomeUIVC: UIViewController {
  var cotter: Cotter?

  override func viewDidLoad() {
    super.viewDidLoad()

    self.cotter = Cotter(
      apiSecretKey: "588d6f67-0981-4718-899b-bcd512de1aca",
      apiKeyID: "w4FK6Zz0XIhtGY3o5biI",
      cotterURL: "https://www.cotter.app/api/v0",
      userID: "hello@example.com",
      configuration: [:]
    )
  }

  // On Button Click
  @IBAction func registerThisDeviceAsTrusted(_ sender: UIButton) {
    func callback(token:String, err: Error?) {
        if err != nil {
          print(err?.localizedDescription)
          self.textLabel.text = "Failed to register new device!"
          return
        }
        self.textLabel.text = "Successfully registered new device!"
    }

    /* Open QR Code that can be scanned from the Trusted Device. */
    self.cotter?.registerNewDevice(vc: self, cb: callback)
  }
}
```

This will open a View Controller with a QR Code that can be scanned from the Trusted Device. After registration is done, the above-defined callback function would be called.

For the above callback, a text label in the `SomeUIVC` view controller will be populated to let the user know whether registration succeeded or failed. Otherwise, if desired, the user can also perform a segue to another view controller (on success or failure) in the callback function as well.

![Different Cases of Registering a New Device as a Trusted Device](/files/-M4uozLhaNOLCw8FDkYt)

### **Case 1: Successfully registered as a Trusted Device**

Once it's scanned and registered successfully as, the View Controller will show a success image, and close after 3 seconds.

### **Case 2: The new device is still not registered as a Trusted Device after 3 minutes**

If after 3 minutes, the new device is still not registered as a Trusted Device, it will show an error message.

## **Step 2: Scanning the QR Code from a Trusted Device**

To scan the QR Code from a Trusted Device, call the function:

```swift
class SomeUIVC: UIViewController {
  var cotter: Cotter?

  override func viewDidLoad() {
    super.viewDidLoad()

    self.cotter = Cotter(
      apiSecretKey: "588d6f67-0981-4718-899b-bcd512de1aca",
      apiKeyID: "w4FK6Zz0XIhtGY3o5biI",
      cotterURL: "https://www.cotter.app/api/v0",
      userID: "hello@example.com",
      configuration: [:]
    )
  }

  // On Button Click
  @IBAction func executeScanOfOtherDevice(_ sender: UIButton) {
    /* Open a View Controller that opens the Camera to scan the
       QR Code on the other non-trusted device. We do not need 
       to pass in a callback, as the user will be redirected
       back to this view controller. */
    self.cotter?.scanNewDevice(vc: self, cb: nil)
  }
}
```

This will open a View Controller that opens the Camera. It will ask for permission to access the camera. When the QR Code is detected and scanned, it will automatically attempt to register the new device. After scanning is done, the user will be redirected back to the above `SomeUIVC` view controller.

![Scanning the New Device's QR Code from the Trusted Device](/files/-M4up2YT_AA7ImJL6wzm)

When the QR Code is detected and registered, the SDK will automatically show whether it was successful or if there's an error.

## 🎉 **You're done!**

Now you can authenticate from the new device without requiring approval.


# Remove Trusted Device

You can remove the current device from being a trusted device.

> In the future, you can also revoke other trusted devices from any trusted device.

## Removing this Trusted Device

To remove the current device from being a trusted device, do the following:

Example:

```swift
class SomeUIVC: UIViewController {
  @IBOutlet weak var textLabel: UILabel!

  var cotter: Cotter?

  override func viewDidLoad() {
    super.viewDidLoad()

    self.cotter = Cotter(
      apiSecretKey: "588d6f67-0981-4718-899b-bcd512de1aca",
      apiKeyID: "w4FK6Zz0XIhtGY3o5biI",
      cotterURL: "https://www.cotter.app/api/v0",
      userID: "hello@example.com",
      configuration: [:]
    )
  }

  // On Button Click
  @IBAction func removeTrustedDevice(_ sender: Any) {
    func callback(token: String, err: Error?) {
      if err != nil {
        self.textLabel.text = err?.localizedDescription
        return
      }
      self.textLabel.text = token
    }

    CotterWrapper.cotter?.removeTrustedDevice(vc: self, cb: callback)
  }
}
```

For the above callback, a text label will be populated to let the user know whether the removal succeeded or failed. Otherwise, if desired, the user can also perform a segue to another view controller in the callback function as well.

For the above example, clicking on the button linked to `removeTrustedDevice` in the view controller will make a request to remove the current device as a trusted device. If the request is successful, no error will exist, and a text label will be populated to let the user know whether the removal succeeded or failed. Otherwise, if desired, the user can also perform a segue to another view controller (on success or failure) in the callback function as well.

> You cannot remove the last Trusted Device. You will need to have 1 remaining Trusted Device.


# Older Versions

These guides are used in the older versions of the SDK. If you are still using this, please migrate to the latest version. These versions are still maintained by Cotter team.

**For iOS SDK v1.4.x or below**

{% content-ref url="/pages/-M4q-LjGBIcIq3PgnYt6" %}
[Sign In with Device](/sdk-reference/ios/ios-sdk-passwordless-login)
{% endcontent-ref %}

{% content-ref url="/pages/-M35aUobglanfiWqjf7S" %}
[Sign In with Email/Phone Number](/sdk-reference/ios/ios-sdk-verify-email-phone)
{% endcontent-ref %}

{% content-ref url="/pages/-M1js7LYj-yGbUtx3rXD" %}
[Biometric/Pin](/sdk-reference/ios/older-versions/ios-sdk)
{% endcontent-ref %}


# Biometric/Pin

Cotter's iOS SDK helps you easily add a Biometric prompt or PIN fallback to your app. This is useful for protecting transactions or sensitive information like medical records.

## **Overview**

Enabling PIN and Biometric using Cotter's Android SDK consists of:&#x20;

1. &#x20;Initializing Cotter
2. Calling functions to start PIN and Biometric Enrollment
3. Verify Biometric or PIN before an action
4. Enabling and disabling Biometric or PIN in Settings

## **What you're building**

![Biometric and PIN using Cotter's SDK](/files/-M1mbKWBJgNvHm6yxJyC)

## **Steps**

1. [Installation](https://docs.cotter.app/)
2. [Set allowed Authentication Methods in the Developer Dashboard](https://docs.cotter.app/)
3. [Initialize Cotter as a dependency](https://docs.cotter.app/)
4. [Create a User](https://docs.cotter.app/)
5. [Enroll PIN or Biometric](https://docs.cotter.app/)
6. [Verify PIN and/or Biometrics on an action](https://docs.cotter.app/)

### Step 1. Installation

We use Cocoapods as our SDK host. If you're using Cocoapods, add this to your Podfile

```
pod 'Cotter'
```

Otherwise please open an issue at [our Github Issues page](https://github.com/cotterapp/ios-sdk/issues).

Then simply run `pod install`

### Step 2. Set allowed Authentication Methods in the Developer Dashboard

You need to set allowed methods for authentication your users. To allow `PIN` and `BIOMETRIC`, go to <https://dev.cotter.app/rules>

![Set both Biometric and PIN to be allowed](/files/-M1jsi9aV_pWttLHG9ou)

{% hint style="info" %}
Remember to select the correct project in the dropdown list
{% endhint %}

### Step 3. Initializing Cotter

You will have to do `import Cotter` on the file that will use Cotter. Then do initialization as follows

```swift
import Cotter...let cotter = Cotter(
 apiSecretKey: <your-api-secret-key>,
 apiKeyID: <your-api-key-id>,
 cotterURL: "https://www.cotter.app/api/v0",
 userID: <your-user-id>, // user’s id that will be created later
 configuration: <your-cotter-config>
)
```

example:

```swift
import Cotter...let cotter = Cotter(
 apiSecretKey: "<API_SECRET_KEY>",
 apiKeyID: "<API_KEY_ID>",
 cotterURL: "https://www.cotter.app/api/v0",
 userID: "hello@example.com",
 configuration: [:]
 );
```

### Step 4. Creating a User

#### 1. Registering a new User

```swift
/* https://www.cotter.app/api/v0/user/create */
CotterAPIService.shared.registerUser(
  userID: <your-user-id>,
  cb: { response in
    // handle Result (Swift 5 enum) callback here
  }
)
```

Response:

```swift
{
  "ID": "9449e9e9-00e0-4d6b-a4b6-28c5b22b0b0f",
  "created_at": "2020-01-21T12:40:21.200727668Z",
  "update_at": "2020-01-21T12:40:21.200727668Z",
  "deleted_at": null,
  "issuer": "<your key ID>",
  "client_user_id": "<Your User Identifier (string)>",
  "enrolled": [],
  "default_method": null
}
```

Example:

```swift
// registerUserCb is a simple callback that handles the cases for 
// the API Call
func registerUserCb(_ response: CotterResult<CotterUser>){
    switch response{
    case .success(let user):
        print("successfully registered the \(user)")

    case .failure(let err):
        // you can put exhaustive error handling here
        switch err{
        case CotterAPIError.decoding:
            print("this is decoding error on registering user")
            break
        case CotterAPIError.network:
            print("this is network error on registering user")
            break
        case CotterAPIError.status:
            print("this is not successful error")
            break
        default:
            print("error registering user: \(err)")
        }
    }
}

// call the API using our client
CotterAPIService.shared.registerUser(
    userID: "hello@example.com",
    cb: registerUserCb
)
```

#### 2. Get a User

```swift
/* https://www.cotter.app/api/v0/user/:your_user_id */
CotterAPIService.shared.getUser(
  userID: <your-user-id>,
  cb: { response in
    // handle Result (Swift 5 enum) callback here
  }
)
```

Response:

```swift
{
  "ID": "9449e9e9-00e0-4d6b-a4b6-28c5b22b0b0f",
  "created_at": "2020-01-21T12:40:21.200727668Z",
  "update_at": "2020-01-21T12:40:21.200727668Z",
  "deleted_at": null,
  "issuer": "<your key ID>",
  "client_user_id": "<Your User Identifier (string)>",
  "enrolled": ["PIN", "BIOMETRIC"],
  "default_method": "BIOMETRIC"
}
```

Example:

```swift
func enrollCb(response: CotterResult<CotterUser>) {
    switch response {
    case .success(let resp):
        self.yourLabel.text = resp.enrolled.joined(separator: ", ")
    case .failure(let err):
        // we can handle multiple error results here
        switch err {
        case CotterAPIError.status(code: 500):
            print("internal server error")
        case CotterAPIError.status(code: 404):
            print("user not found")
        default:
            print(err.localizedDescription)
        }
    }
}

CotterAPIService.shared.getUser(userID:self.userID, cb:enrollCb)
```

### Step 5. Enroll PIN or Biometric

For starting authentication flow, you need to have the following:

* The `UIViewController` you want to attach Cotter to
* The `UIViewController` MUST have a `NavigationController` set (i.e. `self.navigationController` cannot be `nil`)

```swift
// initialization
let cotter = Cotter(...)cotter.PinEnrollment.startEnrollment(
 vc: <your-vc>,
 animated: true,
 cb: <your-callback>
);
```

example:

```swift
class SomeUIVC: UIViewController {
  var cotter: Cotter?
  override func viewDidLoad() {
      super.viewDidLoad()
      // cotter initialization here
      self.cotter = Cotter(
          apiSecretKey: "588d6f67-0981-4718-899b-bcd512de1aca",
          apiKeyID: "w4FK6Zz0XIhtGY3o5biI",
          userID: "hello@example.com",
          cotterURL: "https://www.cotter.app/api/v0",
          configuration: [:]
        );
      cotter.startEnrollment(
        vc: self,
        animated: true,
        cb: { (token: String, err: Error?) in
            if err != nil {
                // handle error
                return
            }
            // handle success
        }
      )
  }
}
```

### Step 6. Verify PIN and/or Biometrics on an action

The verification flow will automatically prompt for Biometric Verification if the user's device has an enrolled biometric. Otherwise, it will fallback to entering PIN. Starting the verification flow is exactly the same as starting the Enrollment Flow on step 5.

```swift
// initialization
let cotter = Cotter(...)

cotter.PinEnrollment.startTransaction(
  vc: <your-vc>,
  animated: true,
  cb: <your-callback>,
  hideClose: false, // hideClose param gives you the choice to show/hide the close button
  configuration: [:]
);
```

Example:

```swift
class SomeUIVC: UIViewController {
  var cotter: Cotter?

  override func viewDidLoad() {
      super.viewDidLoad()

      // cotter initialization here
      self.cotter = Cotter(
          apiSecretKey: "<your API_SECRET_KEY>",
          apiKeyID: "<your API_KEY_ID>",
          userID: "hello@example.com",
          cotterURL: "https://www.cotter.app/api/v0"
        );

      cotter.startTransaction(
        vc: self,
        animated: true,
        cb: { (token: String, err: Error?) in 
            if err != nil {
                // handle error
                return
            }
            // handle success
        },
        hideClose: false
      )
  }
}
```

## 🎉 You're done!

## Additional Notes

### The Callbacks

There are 2 types of callback:

### 1. HTTP Callbacks

HTTP Callbacks are the ones that you pass in to the start flow functions (startEnrollment, startTransaction, startUpdateProfile).

The callback form is as such:

```swift
// FinalCallbackAuth is the general callback function declaration
public typealias FinalAuthCallback = (_ token: String, _ error: Error?) -> Void
```

It takes in a token String and an optional Error object. Currently token String is not used in PIN or Biometric authentication. We're working on making the token useable.

#### Error types

For the FinalAuthCallback function, there are 3 types of CotterError that exist in the SDK. These include:

1\. `CotterError.biometricEnrollment`

* This error is produced when you fail to enroll your biometrics during the Enrollment flow in startEnrollment.

2\. `CotterError.biometricVerification`

* &#x20;This error is produced when you fail to verify your biometrics during the Transaction flow in startTransaction.

3\. `CotterError.keychainError`

* This error is produced in all flows when the user's device is unable to attain its corresponding public/private keys.

However, you will almost always only encounter `CotterError.biometricEnrollment` and `CotterError.biometricVerification` errors in the resulting `FinalAuthCallback` function.

### 2. Authentication Callbacks

Authentication Callbacks are the ones that is passed in through the `CotterAPIService`. It uses the latest Result enum in Swift 5. The Authentication callback works similarly as the HTTP Callback, it can handle success and error cases as shown on the previous example.

The callback form is as such:

```swift
public typealias ResultCallback<Value> = (Result<Value, Error>) -> Void
```

What this definition says is that it takes a Result of a type Value, which Value can be any of the pre defined Cotter classes (such as `CotterUser`, `CotterEvent`, etc), and returns nothing.

#### Error types

When using the `CotterAPIService` class to call Cotter's API endpoints, there are 5 types of Cotter errors that you might encounter:

1\. `CotterAPIError.encoding`

* An 'encoding' error refers to an error encoding the HTTP Request.

2\. `CotterAPIError.decoding`

* A 'decoding' error refers to an error decoding the HTTP Response.

3\. `CotterAPIError.status(code: Int)`

* A 'status' error refers to the response having an invalid HTTP Status Code - e.g. Status 400 (Bad Request Error)

4\. `CotterAPIError.server(message: String)`

* A 'server' error means that our API server has responded to the request with an error message.

5\. `CotterAPIError.network`

* A 'network' error means that network conditions are poor, which can be due to bad internet connection.


# Android

The easiest way to add Passwordless Login and Email/Phone Number Verification to your Android app.

## Complete Guides

#### **Sign in with Device**

Allow users to login automatically from a device that they trust.

{% content-ref url="/pages/-M14g3WtD4o3ZG9N2j9G" %}
[Sign In with Device](/sdk-reference/android/older-sdk-version/android-sdk)
{% endcontent-ref %}

#### Sign in with Email/Phone Number

Authenticate users by sending a Magic Link or OTP via email, SMS, or WhatsApp.

{% content-ref url="/pages/-M0et2D8qa-mVoDh-tjp" %}
[Sign In with Email/Phone Number](/sdk-reference/android/android-sdk-1)
{% endcontent-ref %}

#### Add Biometric/Pin

Add a second layer to protect your mobile app using Biometric or Pin.

{% content-ref url="/pages/-M0RaVb\_ggDYyCQyIUXy" %}
[Biometric/Pin](/sdk-reference/android/android-sdk-2)
{% endcontent-ref %}


# Sign In with Device

Our Android SDK offers the easiest way to integrate "Sign in with device" to your android app. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Device**](/features/passwordless-login) works.

{% hint style="warning" %}
There are major updates planned for this feature. [Contact us in Slack](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA) so we can help you prepare for it.
{% endhint %}

### Overview

Authenticating users using Trusted Devices with Cotter's Android SDK consists of the following steps:

1. Import and Initialize Cotter
2. Sign up a new user and trust the current device
3. Sign in existing user from a Trusted and a Non-Trusted Device

### What you're building

![Trusted Devices on Cotter's Android SDK](/files/-MCxz4OO0itqLUNl45ES)

### Step 1: Import Cotter as a dependency

Add JitPack repository your project level `build.gradle` at the end of repositories.

{% code title="build.gradle" %}

```java
allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}
```

{% endcode %}

Add the Cotter's SDK as a dependency in your app level `build.gradle` .

```java
android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
  ...
  implementation 'com.github.cotterapp:android-sdk:0.4.0'
}
```

Check the latest version here <https://github.com/cotterapp/android-sdk/releases>. Then sync your gradle files.

### Step 2: Initialize Cotter

You can now use the SDK to sign up a new user and setup the device as trusted, or sign in an existing user using the trusted device

In your MainActivity, initialize Cotter with your `API_KEY_ID`. You can get it from the [Dashboard](https://dev.cotter.app/).

```java
Cotter.init(
    this.getApplicationContext(),
    "<API_KEY_ID>",
);
```

Example:

{% code title="MainActivity.java" %}

```java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        Cotter.init(
            this.getApplicationContext(),
            "<API_KEY_ID>",
        );
    }
    ...
```

{% endcode %}

### Step 3: Sign Up a New User and Register This Device as Trusted

1. The user should enter an identifier (an email, phone number, or username).
2. The SDK will register a new user with that identifier, and then enroll the current device as a Trusted Device.

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

```java
Cotter.signUpWithDevice(this, userEmail, new Callback() {
    @Override
    public void onSuccess(JSONObject result) {
        Toast.makeText(getApplicationContext(), result.toString(), Toast.LENGTH_SHORT).show();
        Log.e("Success Register Device", result.toString() );
    }

    @Override
    public void onError(String error) {
        Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
    }
});
```

{% endtab %}

{% tab title="JSON result" %}

```javascript
{
  "ID": "abcdefgh-abcd-abcd-abcd-571d2e2772c7", // Cotter User ID
  "client_user_id": "abcdefgh-abcd-abcd-abcd-571d2e2772c7",
  "created_at": "2020-07-23T23:29:21.85269Z",
  "default_method": "TRUSTED_DEVICE",
  "deleted_at": "0001-01-01T00:00:00Z",
  "enrolled": [
    "TRUSTED_DEVICE"
  ],
  "identifier": "user@cotter.app",
  "identifiers": null,
  "issuer": "YOUR_API_KEY_ID", // this is your API Key ID
  "oauth_token": {
    "access_token": "eyJhbGciO...",
    "id_token": "eyJhbGciOiJFU...",
    "refresh_token": "3:LGOY0pIVof6LgkWo...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "TRUSTED_DEVICE"
  },
  "updated_at": "2020-07-23T23:29:22.090131709Z"
}
```

{% endtab %}
{% endtabs %}

This method is only for the first Trusted Device. You will get an error if you attempt to enroll another Trusted Device using `enrollDevice` when there's already a Trusted Device for the account. To enroll other devices, see [Add a new Trusted Device](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device).

### Step 4: Authenticate from a Trusted Device <a href="#step-6-authenticate-from-a-trusted-device" id="step-6-authenticate-from-a-trusted-device"></a>

To authenticate a device, call the `Cotter.signInWithDevice` function. This will automatically detect whether the current device is a Trusted Device or not.

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

```java
Cotter.signInWithDevice(this, userEmail, this, Dashboard.class, new Callback() {
    @Override
    public void onSuccess(JSONObject result) {
        Toast.makeText(getApplicationContext(), result.toString(), Toast.LENGTH_SHORT).show();
        Log.e("Success logging in", result.toString() );
    }

    @Override
    public void onError(String error) {
        Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
    }
});
```

{% endtab %}
{% endtabs %}

#### Parameters

```java
signInWithDevice(Context ctx, String identifier, AppCompatActivity act, Class callbackClass, Callback callback)
```

* `identifier`: Your user's identifier (email/phone/username) that was used to register this user in Step 3.
* `callbackClass` : The next activity class that you want to redirect to when the authentication request is finished
* `callback` : A custom Callback function that implements 2 methods: `onSuccess` and `onError`.

When an Authentication Event is requested using method `TRUSTED_DEVICE`, there are 2 possible cases:

#### Case 1: The current device is a Trusted Device

If the current device is a Trusted Device, it should automatically be approved, and you will receive a JSON result containing the requested `Event` and whether or not it's approved. The approval is based on whether or not the signature included in the request from the SDK is valid.

```java
{
  ...
  "method": "TRUSTED_DEVICE",
  "new": false, // Is this a new pending event (should be false).
  "approved": true, // Is this event approved (should be true).
  
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUz...", // validate this access token
    "id_token": "eyJhbGciOiJFUz...",
    "refresh_token": "5:cYIfabtspE1cBeD7KP...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "TRUSTED_DEVICE"
  }
}

```

You should see a result that the event is **not new**, and that it's **approved**. This is because the signature from the Trusted Device is sufficient to prove that the device is authorized.

When passing this Response to your backend, you need to check if this JSON is valid and if it comes from Cotter's server by validating the **access token**.

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

#### Case 2: The current device is NOT a Trusted Device

We'll cover this in the next guide:

{% content-ref url="/pages/-MCy35gqHGzwuHzfIPSw" %}
[Authenticate from a Non-Trusted Device](/sdk-reference/android/sign-in-with-device/authenticate-from-a-non-trusted-device)
{% endcontent-ref %}

## 🎉 You're done!

## Next Steps <a href="#next-steps" id="next-steps"></a>

{% content-ref url="/pages/-MCy35gqHGzwuHzfIPSw" %}
[Authenticate from a Non-Trusted Device](/sdk-reference/android/sign-in-with-device/authenticate-from-a-non-trusted-device)
{% endcontent-ref %}

{% content-ref url="/pages/-M180XVPUU9yBlVfPetR" %}
[Add a new Trusted Device](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device)
{% endcontent-ref %}

{% content-ref url="/pages/-M0RnBPsArsqJgT5vkKe" %}
[Customization](/sdk-reference/android/android-sdk-2/customization)
{% endcontent-ref %}


# Authenticate from a Non-Trusted Device

There are 2 steps in authenticating a Non-Trusted Device

1. [Make a request](/sdk-reference/android/sign-in-with-device/authenticate-from-a-non-trusted-device#step-1-making-an-authentication-request) from the Non-Trusted Device and wait for approval
2. [Approve the request](/sdk-reference/android/sign-in-with-device/authenticate-from-a-non-trusted-device#step-2-how-to-approve-a-pending-request-from-a-trusted-device) from a Trusted Device.&#x20;

## Step 1: Making an Authentication Request  <a href="#step-1-making-an-authentication-request" id="step-1-making-an-authentication-request"></a>

Same as how it works with [Authenticating from a Trusted Device](/sdk-reference/android/sign-in-with-device), to authenticate a device, call the `TrustedDeviceHelper.requestAuth` function. **This will automatically detect whether the current device is a Trusted Device or not.**

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

```java
Cotter.signInWithDevice(this, userEmail, this, Dashboard.class, new Callback() {
    @Override
    public void onSuccess(JSONObject result) {
        Toast.makeText(getApplicationContext(), result.toString(), Toast.LENGTH_SHORT).show();
        Log.e("Success authenticating user", result.toString() );
    }

    @Override
    public void onError(String error) {
        Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
    }
});
```

{% endtab %}

{% tab title="JSON result" %}

```javascript
{
  "ID": 1361, // Event ID
  "CreatedAt": "2020-02-27T22:22:48.705212512Z",
  "UpdatedAt": "2020-02-27T22:22:48.705212512Z",
  "DeletedAt": null,
  "client_user_id": "abcdefgh-abcd-abcd-abcd-af968d9951d3", // your user's Cotter User ID
  "issuer": "YOUR API KEY ID", // your API Key
  "event": "LOGIN",// requested event (LOGIN, or TRANSACTION, etc)
  "ip": "192.168.232.2", 
  "location": "Unknown",
  "timestamp": "1582842167",
  "method": "TRUSTED_DEVICE", // auth method: TRUSTED_DEVICE (other choices are PIN / BIOMETRIC)
  "new": false, // Is this a new pending event. More explanation below about Non-Trusted Device
  "approved": true // Is this event approved.
    
  // If authentication successful:
  // Validate the oauth_token.access_token
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUz...", // validate this access token
    "id_token": "eyJhbGciOiJFUz...",
    "refresh_token": "5:cYIfabtspE1cBeD7KP...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "TRUSTED_DEVICE"
  }
}
```

{% endtab %}
{% endtabs %}

#### Callback Class <a href="#callback-class" id="callback-class"></a>

The `Callback.class` should be filled with the Activity to go to when the request is approved. For example, if this is a login request, then the Callback Activity can be your Dashboard class.

When an Authentication Event is requested `TRUSTED_DEVICE`, there are 2 possible cases:

### Case 1: The current device is a Trusted Device

You will be automatically approved if the request is coming from a Trusted Device. This is covered in [Authenticate from a Trusted Device.](/sdk-reference/android/sign-in-with-device)

### Case 2: The current device is NOT a Trusted Device

If the current device is **not** a Trusted Device, then it will create a **pending event.** This pending event will need to be **approved by a Trusted Device**.&#x20;

The SDK will automatically show a prompt for the user to approve the event from a Trusted Device. The user then need to approve this request from their Trusted Device:

## Step 2: How to approve a pending request from a Trusted Device <a href="#step-2-how-to-approve-a-pending-request-from-a-trusted-device" id="step-2-how-to-approve-a-pending-request-from-a-trusted-device"></a>

To check if there's a pending event for this user, you can call the `getNewEvent` method from a Trusted Device.

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

```java
TrustedDeviceHelper.getNewEvent(getApplicationContext(), this);
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**In the future,** your app that is installed in a Trusted Device will receive a notification and this method is invoked when the notification is opened.
{% endhint %}

**For now,** you should **tell your user to press a button and invoke this method** when they try to login from a non-trusted device.

The SDK will open a prompt in the Trusted Device asking if the user want to approve or reject the authentication request.

![Prompt to approve request from a Trusted Device](/files/-MCy3rmAh-081JEZqYSX)

In the prompt in the user's Non-Trusted Device (right picture), you can [Customize the Prompt](/sdk-reference/android/sign-in-with-device/customization) to ask them to press a specific button in the Trusted Device to invoke the prompt on the left.

## Receiving the result and check if the request was approved <a href="#receiving-the-result-and-check-if-the-request-was-approved" id="receiving-the-result-and-check-if-the-request-was-approved"></a>

**In the non-trusted device** where the user is trying to log in, you'll receive the result in the callback class that your specified.

### Case 1: The request is approved <a href="#case-1-the-request-is-approved" id="case-1-the-request-is-approved"></a>

When the request is approved, the SDK will automatically redirect to your Callback Class. In the callback class, you should handle the response from Cotter

#### Handle the response in your callback class <a href="#handle-the-response-in-your-callback-class" id="handle-the-response-in-your-callback-class"></a>

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_dashboard);

    // Handle the response
    TrustedDeviceResponse trustDevResp = TrustedDeviceHelper.handleResponse(getIntent());
    if (trustDevResp != null) {
        Log.i("TrustedDeviceResponse: ", Integer.toString(trustDevResp.event.ID));
    }
}
```

You'll receive a response in the form of a `TrustedDeviceResponse` object.

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

```java
public class TrustedDeviceResponse {
    public String response; // The response in the form of a string
    public Event event; // The same response, but parsed in to an Event object
    public boolean approved; // Whether or not the event was approved
    public String error; // If there's any error, this will contain the error, otherwise null
}
```

{% endtab %}

{% tab title="String Response" %}

```javascript
{
  "ID": 1364,
  "CreatedAt": "2020-02-28T00:08:52.715136Z",
  "UpdatedAt": "2020-02-28T00:08:56.917318Z",
  "DeletedAt": null,
  "client_user_id": "abcdefgh-abcd-abcd-abcd-af968d9951d3", // user's cotter user id
  "issuer": "YOUR API KEY ID",
  "event": "LOGIN",
  "ip": "10.0.2.15",
  "location": "",
  "timestamp": "1582848531",
  "method": "TRUSTED_DEVICE",
  "new": false, // Should now be false because the Trusted Device already responded
  "approved": true // In this case 1, this should be true. Otherwise your Callback class won't be open
  
  // If authentication successful:
  // Validate the oauth_token.access_token
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUz...", // validate this access token
    "id_token": "eyJhbGciOiJFUz...",
    "refresh_token": "5:cYIfabtspE1cBeD7KP...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "TRUSTED_DEVICE"
  }
}
```

{% endtab %}
{% endtabs %}

When this authentication request is approved, you'll receive an access token in the **String Response**. Send this access token to your backend to validate it:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

### Case 2: The request is rejected

If the request is **not approved** for 1 minute, the prompt in the Non-Trusted Device will show an error message. The prompt will not show an error immediately when the Trusted Device denied the request, i.e. there is no difference between rejection and no response from the Trusted Device.

![Authentication request from a Non-Trusted Device](/files/-MCy4SiWHFDUpwN_htV7)

In this case, the [`onError` function that you passed in earlier](/sdk-reference/android/sign-in-with-device/authenticate-from-a-non-trusted-device#step-1-making-an-authentication-request) will be called with an error message of `Event is not approved`.


# Check if Trusted Device is Enrolled

There are 3 methods that you can use to check about Trusted Device enrollment:

1. Check if this device is a Trusted Device
2. Check if this account has any Trusted Device
3. Check if Trusted Device is the default method of authentication for this account

## Check if this device is a Trusted Device

```java
Cotter.methods.trustedDeviceEnrolled(new CotterMethodChecker() {
    @Override
    public void onCheck(boolean enrolled) {
        thisDeviceEnrolled.setText("TrustedDevice enrolled this device: " + enrolled);
    }
});
```

## �Check if this account has any Trusted Device

```java
Cotter.methods.trustedDeviceEnrolledAny(new CotterMethodChecker() {
    @Override
    public void onCheck(boolean enrolled) {
        anyDeviceEnrolled.setText("TrustedDevice enrolled any device: " + enrolled);
    }
});
```

## �Check if Trusted Device is the default method

```java
Cotter.methods.trustedDeviceDefault(new CotterMethodChecker() {
    @Override
    public void onCheck(boolean defaultMethod) {
        trustedDeviceDefault.setText("TrustedDevice default: " + defaultMethod);
    }
});
```


# Add a new Trusted Device

## Steps

There are 2 steps that's needed to add a new Trusted Device:

1. [Show the QR Code](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device#step-1-show-the-qr-code-of-the-new-device) of the New Device
2. [Scan the New Device's QR Code](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device#step-2-scanning-the-qr-code-from-a-trusted-device) using the Trusted Device

![Scan the New Device's QR Code using the Trusted Device](/files/-M19NBju1jJXzPBx6G45)

## Step 1:  Show the QR Code of the New Device

To show the QR Code of the new device:

```java
TrustedDeviceHelper.startEnrollThisDeviceAsTrusted(this, this);
```

This will open an Activity with a QR Code that can be scanned from the Trusted Device.&#x20;

![Different Cases of Registering a New Device as a Trusted Device](/files/-M19Sg5WxruFr5hnqOuD)

#### Case 1: Successfully registered as a Trusted Device

Once it's scanned and registered successfully as, the Activity will show a success image, and close after 3 seconds.

#### Case 2: The new device is still not registered as a Trusted Device after 3 minutes

If after 3 minutes, the new device is still not registered as a Trusted Device, it will show an error message.

## Step 2: Scanning the QR Code from a Trusted Device

To scan the QR Code from a Trusted Device, call the function:

```java
TrustedDeviceHelper.startEnrollOtherDeviceAsTrusted(this, this);
```

This will open an Activity that opens the Camera. It will ask for permission to access the camera. When the QR Code is detected and scanned, it will automatically attempt to register the new device.

![Scanning the New Device's QR Code from the Trusted Device](/files/-M19f9CEfe8YGuFX2E14)

When the QR Code is detected and registered, the SDK will automatically show whether it was successful or if there's an error.

## :tada: You're done!

Now you can authenticate from the new device without requiring approval.&#x20;


# Remove Trusted Device

You can remove the current device from being a trusted device.

{% hint style="info" %}
**In the future,** you can also revoke other trusted devices from any trusted device.
{% endhint %}

### Removing this Trusted Device

To remove the current device from being a trusted device, do the following:

```java
TrustedDeviceHelper.removeDevice(this, new Callback() {
    @Override
    public void onSuccess(JSONObject result) {
        Toast.makeText(getApplicationContext(), result.toString(), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onError(String error) {
        Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
    }
});
```

{% hint style="info" %}
You cannot remove the last Trusted Device. You will need to have 1 remaining Trusted Device.
{% endhint %}


# Customization

You can change the text and logo in most of the UI that's provided by Cotter's Android SDK.

## Getting started

To start customizing the UI, you can copy and paste the code below and edit the text as you like.

{% hint style="danger" %}
Make sure to do `Cotter.init` **before** setting any of the UI customization
{% endhint %}

## Approve Authentication Request Prompt

![ApproveRequest page](/files/-M19xPSu0WBH_aViSHhb)

To customize this page, do the following:

```java
Cotter.strings.setApproveRequestStrings(Strings.Title, "ARE you trying to sign in?");
Cotter.strings.setApproveRequestStrings(Strings.Subtitle, "SOMEONE is trying to sign in to your account from another device.");
Cotter.strings.setApproveRequestStrings(Strings.ButtonYes, "YES");
Cotter.strings.setApproveRequestStrings(Strings.ButtonNo, "NO");
// To update the logo, add an image in your /res/drawable folder
Cotter.colors.setLogo(R.drawable.logo);
```

## �Request Authentication Prompt

![RequestAuth prompt](/files/-M1AHeV_e98wQCNw1Rfr)

To customize this prompt, do the following:

```java
Cotter.strings.setRequestAuthStrings(Strings.DialogTitle, "APPROVE this login from your phone");
Cotter.strings.setRequestAuthStrings(Strings.DialogSubtitle, "A NOTIFICATION is sent to your trusted device to confirm it's you.");
Cotter.strings.setRequestAuthErrorStrings(Strings.DialogTitle, "SOMETHING went wrong");
Cotter.strings.setRequestAuthErrorStrings(Strings.DialogSubtitle, "WE'RE UNABLE to confirm that it's you. Please try again.");
```

## �QR Code Scanner Dialog

![SuccessSheet dialog](/files/-M1AIlGR0xkR5gqKkJm3)

To customize the text in this dialog, do the following:

```java
Cotter.strings.setSuccessSheetStrings(Strings.DialogTitle, "SUCCESS Registering New Device");
Cotter.strings.setSuccessSheetStrings(Strings.DialogSubtitle, "YOU CAN now use your new device to access your account without approval.");
Cotter.strings.setSuccessSheetErrorStrings(Strings.DialogTitle, "UNABLE to Register New Device");
Cotter.strings.setSuccessSheetErrorStrings(Strings.DialogSubtitle, "PLEASE try again.");
```

## Show �QR Code Page

![QRCodeShow page](/files/-M1AJdFW8XiJKAyLFI_t)

To update this page, do the following:

```java
Cotter.strings.setQRCodeShowStrings(Strings.Title, "REGISTER this Device");
Cotter.strings.setQRCodeShowStrings(Strings.Subtitle, "PLEASE scan this QR Code from a Trusted Device.");
Cotter.strings.setQRCodeShowErrorStrings(Strings.Title, "SOMETHING went wrong");
Cotter.strings.setQRCodeShowErrorStrings(Strings.Subtitle, "THE REQUEST timed out. Please try again.");
```

�


# Sign In with Email/Phone Number

Our Android SDK offers the easiest way to verify email/phone numbers in your android app. You can simply call a function and it does most of the heavy lifting and authentication for you.

> **Concepts:** Learn about how [**Sign in with Email/Phone Number**](/features/verify-email-phone) works.

### Overview

Verifying **email and phone number** in your mobile app using our Android SDK consists of the following steps:

1. Import Cotter
2. Call Cotter's Login function
3. Receive user's email or phone number, and whether or not it's verified

### What you're building

![Cotter's Android SDK](/files/-M0evnVY2weMqYHvqNDO)

## Steps

1. [Import Cotter as a dependency](/sdk-reference/android/android-sdk-1#step-1-import-cotter-as-a-dependency)
2. [Initialize Cotter with your API Keys](/sdk-reference/android/android-sdk-1#step-2-initialize-cotter-with-your-api-keys)
3. [Register URL Scheme](/sdk-reference/android/android-sdk-1#step-3-register-a-url-scheme): Cotter's authentication will redirect back to your application using this URL scheme.
4. [Call Cotter's Login function](/sdk-reference/android/android-sdk-1#step-4-call-cotters-login-function): This function will handle the WebView, verifying phone number or email, and request the identity from Cotter's server.
5. [Receive the Token](/sdk-reference/android/android-sdk-1#step-5-receive-the-token): Include the returned token and email/phone number in your server

### Step 1: Import Cotter as a dependency

Add JitPack repository your project level `build.gradle` at the end of repositories.

{% code title="build.gradle" %}

```java
allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}
```

{% endcode %}

Add the Cotter's SDK as a dependency in your app level `build.gradle` .

```java
android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
  ...
  implementation 'com.github.cotterapp:android-sdk:0.3.0'
}
```

Check the latest version here <https://github.com/cotterapp/android-sdk/releases>.

Then sync your gradle files.

### Step 2: Initialize Cotter with your API keys

Initialize Cotter with your `API_KEY_ID` . Call the function below in your MainActivity

```java
 Cotter.init(this.getApplicationContext(), 
         "https://www.cotter.app/api/v0",
         <API_KEY_ID>);
```

For example:

{% code title="MainActivity.java" %}

```java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

         Cotter.init(this.getApplicationContext(), 
            "https://www.cotter.app/api/v0",
            <API_KEY_ID>);
    }
    ...
```

{% endcode %}

### Step 3: Register a URL Scheme

You also need to decide a callback URL scheme that you want to use. If your app's package name is `com.example.myapplication` then you should use something like this for your URL Scheme callback.

```
com.example.myapplication://auth_callback
```

{% hint style="warning" %}
Make sure your URL scheme (the front part before `://`) doesn't have an underscore or other special characters. To test it out, enter your Redirect URL here: <https://jsfiddle.net/omd02jn5/>
{% endhint %}

&#x20;This URL scheme will be called when Cotter's verification is done and want to go back to your app. Read more about[ Android deep-linking.](https://developer.android.com/training/app-links/deep-linking)

You need to register this URL Scheme to receive the callback. Add the following in your `AndroidManifest.xml` . Check the example in the [example's Github repo](https://github.com/cotterapp/android-example/blob/master/app/src/main/AndroidManifest.xml).

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

```markup
<!--   ADD THE LINES FROM HERE    -->
<activity android:name="com.cotter.app.RedirectUriReceiverActivity" android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        
        <!--   This example is for: com.example.myapplication://auth_callback    -->
        <!--   Change it to your own url    -->
        <data android:scheme="com.example.myapplication" 
                android:host="auth_callback"/>
    </intent-filter>
</activity>
<!--   TO HERE    -->
```

{% endcode %}

### Step 4: Call Cotter's Login function

To open up Cotter's Login screen, you can either have your user input their email or phone number inside Cotter's screen, or you can provide a text input where your users can enter their email / phone, and you can pass that to Cotter's screen.

a) To login and enter the email or phone number in Cotter's window:

```java
 // Provide Context and the URL Scheme
 Cotter.newIdentity(this, "com.example.myapplication://auth_callback")
    .login(
        "EMAIL",             // EMAIL or PHONE
        this,                // Context
        Dashboard.class);    // Callback Class
```

b) �To login with the email or phone number that your user entered in your app:

```java
// Provide Context and the URL Scheme
 Cotter.newIdentity(this, "com.example.myapplication://auth_callback")
    .loginWithInput( // Context
       "EMAIL",                     // EMAIL or PHONE
       input.getText().toString(),  // User's email
       this,                        // Context
       CallbackActivity.class);     // Callback Class
```

{% hint style="info" %}
To send code/link via SMS or WhatsApp, you'll need to add some balance to you project in the [Dashboard](https://dev.cotter.app/).
{% endhint %}

You need to provide a class to redirect to when Cotter's email/phone number verification is complete.

{% hint style="success" %}
**Try it now!** You can check if it works so far by invoking the login function above on a button click. It should load Cotter's Authentication page on a WebView.
{% endhint %}

### Step 5: Receive the Token

In your `CallbackActivity` class, receive the token from the `intent` inside `onCreate` using this function.

```java
String resp = IdentityManager.handleResponse(getIntent());
if (resp != null) {
    Log.i("Login Response: ", resp);
}
String error = IdentityManager.handleError(getIntent());
if (error != null) {
    Log.i("Login Error: ", error);
}
```

For example, if your `CallbackActivity` class is called `Dashboard`:

```java
public class Dashboard extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dashboard);
        
        // Handle response here
        String resp = IdentityManager.handleResponse(getIntent());
        if (resp != null) {
            Log.i("Login Response: ", resp);
        }
        
        // Handle error here 
        String error = IdentityManager.handleError(getIntent());
        if (error != null) {
            Log.i("Login Error: ", error);
        }
    }
}
```

The `resp` will consist of the following JSON Object as a string.

```javascript
{
  "identifier": {
    "ID": "f4286df9-a923-429c-bc33-5089ffed5f68",
    "created_at": "2020-07-21T22:53:21.211367Z",
    "updated_at": "2020-07-21T22:53:21.211367Z",
    "deleted_at": "0001-01-01T00:00:00Z",
    "identifier": "putri@cotter.app", // User's email
    "identifier_type": "EMAIL",
    "device_type": "BROWSER",
    "device_name": "Mozilla/5.0 (Linux; Android 9; Android SDK built for x86 Build/PSR1.180720.075) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
    "expiry": "2020-08-20T22:53:21.19705Z",
    "timestamp": "2020-07-21T22:53:21.19705Z"
  },
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUz...", // Validate this access token
    "id_token": "eyJhbGciOiJFUzI1...",
    "refresh_token": "27944:lb31DY5pG229n...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "OTP"
  },
  "token": {...},
  "user": {
    "ID": "643a42c7-316a-4abe-b27e-f4d0f903bfea", // Cotter uesr ID
    "identifier": "putri@cotter.app",
    ...
  }
}
```

This JSON object contains 3 objects, `identifier` , `oauth_token` and `user` .&#x20;

* The identifier object contains information about the user's email or phone number, device type and name, and expiry.
* The `oauth_token` contains an `access_token` that you can validate in your backend.
* The `user` contains the User object in Cotter, which includes a "Cotter User ID". **You should associate your user with this Cotter User ID for reference.**

{% hint style="success" %}
You should include this JSON Object into your call to your backend for **Login** or **Registration**. Your backend should then verify that [the access token is valid.](/getting-access-token/verifying-jwt-tokens)
{% endhint %}

## Validating Cotter's Access Token&#x20;

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

## 🎉 You're done!

## Securing your Project

Since you'll be using your API Key from a front-end website or mobile app, your `API_KEY_ID` is exposed to anyone inspecting your code. Here are some ways to prevent abuse:

* [Only allow your website/app to use your API Key](/protecting-your-account/only-allow-your-website-app-to-use-your-api-key)
* [Rate Limit the number of authentication requests](/protecting-your-account/rate-limit)
* [Enable reCAPTCHA to prevent automated abuse](/protecting-your-account/enable-recaptcha-to-protect-against-automated-abuse)

## Next Steps

### Implement Trusted Devices

{% content-ref url="/pages/-M14g3WtD4o3ZG9N2j9G" %}
[Sign In with Device](/sdk-reference/android/older-sdk-version/android-sdk)
{% endcontent-ref %}

### Add Biometric/PIN

{% content-ref url="/pages/-M0RaVb\_ggDYyCQyIUXy" %}
[Biometric/Pin](/sdk-reference/android/android-sdk-2)
{% endcontent-ref %}


# Biometric/Pin

Cotter's Android SDK helps you easily add a Biometric prompt or PIN fallback to your app. This is useful for protecting transactions or sensitive information like medical records.

{% hint style="warning" %}
There are major updates planned for this feature. [Contact us in Slack](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA) so we can help you prepare for it.
{% endhint %}

### Overview

Enabling PIN and Biometric using Cotter's Android SDK consists of:

1. Initializing Cotter
2. Calling functions to start Pin Enrollment and Biometric Enrollment
3. Verify Biometric or PIN before a transaction
4. Enabling and disabling Biometric or PIN in Settings

### What you're building

![PIN and Biometric using Cotter's Android SDK](/files/-M0Ray-M6mEFhHMiUl5r)

## Steps

1. [Import Cotter as a dependency](/sdk-reference/android/android-sdk-2#step-1-import-cotter-as-a-dependency)
2. [Set allowed Authentication Methods](/sdk-reference/android/android-sdk-2#step-2-setting-authentication-methods) in the Dashboard
3. [Create a User](/sdk-reference/android/android-sdk-2#step-3-creating-a-user)
4. [Initialize Cotter](/sdk-reference/android/android-sdk-2#step-4-initialize-cotter) in your Main Activity
5. [Enroll Biometrics and PIN](/sdk-reference/android/android-sdk-2#step-5-enroll-biometric-and-pin): PIN is recommended as a fallback method
6. [Verify Biometrics](/sdk-reference/android/android-sdk-2#step-6-verify-biometrics-on-transactions) before a transaction

### Step 1: Import Cotter as a dependency

Add JitPack repository your project level `build.gradle` at the end of repositories.

{% code title="build.gradle" %}

```java
allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}
```

{% endcode %}

Add the Cotter's SDK as a dependency in your app level `build.gradle` .

```java
android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
  ...
  implementation 'com.github.cotterapp:android-sdk:0.3.2'
}
```

Check the latest version here <https://github.com/cotterapp/android-sdk/releases>.

Then sync your gradle files.

### Step 2: Setting Authentication Methods

You need to set allowed methods for authenticating your users. To allow `PIN` and `BIOMETRIC`, go to <https://dev.cotter.app/rules>

![Set both Biometric and PIN to be allowed](/files/-M0Rd71oJbeus4CzKUIm)

{% hint style="info" %}
Remember to set the correct Project in the dropdown list.
{% endhint %}

### Step 3: Creating a User

#### 1. Registering a User <a href="#id-1-registering-a-user" id="id-1-registering-a-user"></a>

Your server should do this request to Cotter's server **during Registration**.

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

```http
curl -XPOST \
-H 'API_KEY_ID: <your key id>' \
-H 'API_SECRET_KEY: <your secret key>' \
-H "Content-Type: application/json" \
-d '{"client_user_id": "<Your User Identifier (string)>"}' \
'https://www.cotter.app/api/v0/user/create'
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "ID": "9449e9e9-00e0-4d6b-a4b6-28c5b22b0b0f",
  "created_at": "2020-01-21T12:40:21.200727668Z",
  "update_at": "2020-01-21T12:40:21.200727668Z",
  "deleted_at": null,
  "issuer": "<your key ID>",
  "client_user_id": "<Your User Identifier (string)>",
  "enrolled": [],
  "default_method": null
}
```

{% endtab %}
{% endtabs %}

Check out [Create a User API Reference](/api-reference/user-api#create-a-user) for full Description

#### 2. Get user data

To retrieve user's data:

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

```
curl -XGET \
-H 'API_KEY_ID: <your key id>' \
-H 'API_SECRET_KEY: <your secret key>' \
'https://www.cotter.app/api/v0/user/:your_user_id'
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "ID": "9449e9e9-00e0-4d6b-a4b6-28c5b22b0b0f",
  "created_at": "2020-01-21T12:40:21.200727668Z",
  "update_at": "2020-01-21T12:40:21.200727668Z",
  "deleted_at": null,
  "issuer": "<your key ID>",
  "client_user_id": "<Your User Identifier (string)>",
  "enrolled": ["PIN", "BIOMETRIC"],
  "default_method": "BIOMETRIC"
}
```

{% endtab %}
{% endtabs %}

Check out [Get User API Reference](/api-reference/user-api#get-user) for full Description

### Step 4: Initialize Cotter

You can now use the SDK to enroll and verify Biometric and PIN for the user you just created.

#### Initialize <a href="#initialize" id="initialize"></a>

In your MainActivity, initialize Cotter with API keys, Cotter's URL to use (prod/test), and your User ID.

Production Endpoint: `https://www.cotter.app/api/v0`

```java
Cotter.init(
    this.getApplicationContext(),
    "https://www.cotter.app/api/v0",
    "<User ID>",
    "<API_KEY_ID>",
    "<API_SECRET_KEY>"
);
```

Example:

{% code title="MainActivity.java" %}

```java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        Cotter.init(
            this.getApplicationContext(),
            "https://www.cotter.app/api/v0",
            "hello@example.com",
            "588d6f67-0981-4718-899b-bcd512de1aca",
            "w4FK6Zz0XIhtGY3o5biI");
    }
    ...
```

{% endcode %}

### Step 5: Enroll Biometric and PIN

to enroll **both Biometric and PIN**, start the `PinEnrollment` flow in your Activity, pass in the `Callback` Screen, and the `Event` Tag. `Event` Tag is used for your own logging, for example, a pin enrollment event can be called `"PIN_ENROLLMENT_ANDROID"` to tag a pin enrollment event from an Android device.

```java
Cotter.PinEnrollment.startFlow(view, CallBack.class, "EVENT TAG");
```

Example:

```java
// Inside your app, use a button onClick that calls
// openEnroll to start the flow
public void openEnrollment(View view) {
    Cotter.PinEnrollment.startFlow(view, Dashboard.class, "PIN_BIO_ENROLLMENT");
}
```

#### How `PinEnrollment` looks like

After entering the PIN, the user will automatically be prompted to **Enroll Biometrics** if the device supports it. Entering a PIN is required as a fallback method.

![PinEnrollment Flow](/files/-M0Ri4hUvJsW7AYs-tt-)

### **Step 6: Verify Biometrics on Transactions**

The `PinVerification` flow will automatically prompt for **Biometric Verification** if the user's device has an enrolled biometric, otherwise, it will fallback to entering PIN. Starting the `PinVerification` flow is exactly the same as starting the `PinEnrollment` flow.

```java
Cotter.setOnResetPin(new PinResetInterface() {
            @Override
            public void onResetPin(User user, Callback callback) {...}
})
Cotter.PinVerification.startFlow(view, CallBack.class, "EVENT TAG");
```

In the verification page, there is a button called `Forgot PIN` . This is used to send a verification code to allow the user to reset their PIN.

`setOnResetPin` is used to set a callback function that will be called to reset the pin. You need to provide a function that follows the `PinResetInterface`. This function should **call your server to initiate the PIN reset request.**

#### **When your `onResetPin` function is called, it should call your server, and your server needs to do the following:**

1. Based on the currently logged-in user, find out the user's Email and name
2. Call Cotter's [Reset PIN API](/api-reference/reset-pin-api) to send the pin reset code
3. Receive the response from Cotter which contains the fields `success`, `challenge_id`, `challenge`
4. Call the `callback.onSuccess` with a `JSONObject` containing the following JSON object:

```javascript
{
  "success": true,           // from the response from the Reset PIN API
  "challenge_id": 123,       // from the response from the Reset PIN API
  "challenge": "abcde12345", // from the response from the Reset PIN API
  "sending_method": "EMAIL",
  "sending_destination": "user@email.com"
}
```

Example:

```java
// In onCreate, set the onResetPin callback
protected void onCreate(Bundle savedInstanceState) {
     Cotter.setOnResetPin(new PinResetInterface() {
        @Override
        public void onResetPin(User user, Callback callback) {
            // 1. Call your server
            // 2. Get the response from your server
            // 3. Construct a JSONObject and pass it to callback.onSuccess
            JSONObject req = new JSONObject();
            try {
                req.put("success", response.getBoolean("success"));
                req.put("challenge_id", response.getInt("challenge_id"));
                req.put("challenge", response.getString("challenge"));
                req.put("sending_method", sendingMethod);
                req.put("sending_destination", sendingDestination);
            } catch (Exception e) {
                callback.onError(e.toString());
            }
    
            callback.onSuccess(req);
        }
    });
}

// Inside your app, use a button onClick that calls
// openPinVerification to start the flow
public void openPinVerification(View view) {
    Cotter.PinVerification.startFlow(view, Dashboard.class, "LOGIN");
}
```

{% hint style="info" %}
**Reset PIN** functionality is an **update** that is available starting from **version `0.4.5`**
{% endhint %}

## 🎉 You're done!


# Advanced Methods

These are useful methods that you can use in your app to fully support Biometrics and PIN.

To complete the functionalities, we provide the following methods:

1. [Enable and disable Biometric](/sdk-reference/android/android-sdk-2/advanced-methods#enable-and-disable-biometric)
2. [Check if Biometric or PIN are Enrolled](/sdk-reference/android/android-sdk-2/advanced-methods#check-if-methods-are-enrolled)
3. [Change PIN](/sdk-reference/android/android-sdk-2/advanced-methods#change-pin)

## Enable and Disable Biometric

Biometrics entries are **unique per device**. When the user disable their biometrics, their biometrics enrollment for that device will be deleted from the database. When they enable biometrics, they are essentially enrolling that device again for biometrics, and therefore **they will be prompted to verify the biometrics again.**

To do enable/disable biometrics, you have to **initialize Cotter's biometrics prompt** first using:

```java
Cotter.initBiometricSwitch(context, fragmentActivity, activity, cotterBiometricCallback);
```

Generally, `context`, `fragmentActivity`, and `activity` can be filled with `this` inside your activity.

**Here is a step-by-step guide on enabling and disabling Biometrics**

### 1. Create a Callback

This callback will called when biometric is enabled or disabled.

**cotterBiometricCallback**

```java
CotterBiometricCallback cotterBiometricCallback = new CotterBiometricCallback() {
  @Override
  public void onSuccess(boolean enrolled) {
      res.setText("Success" + enrolled);
      updateMethods();
  }

  @Override
  public void onCanceled() {
      res.setText("Canceled");
      updateMethods();
  }

  @Override
  public void onError(String s) {
      res.setText("Error" + s);
      updateMethods();
  }
};
```

**onSuccess**

`onSuccess` will be called when the biometric is *successfully* enabled or disabled.

* If the user successfully **disabled** biometrics, we will call `onSuccess(false)`. So `enrolled = false` here, because biometrics is no longer enrolled.
* If the user successfully **enabled** biometrics, we will call `onSuccess(true)`.

**onCanceled**

`onCanceled` will be called when the user try to enable biometrics, but they **canceled the biometric prompt** (dismissed the prompt).

**onError**

`onError` will be caleld when an error occur while enabling or disabling biometrics.

### 2. Initialize Cotter Biometric

{% hint style="warning" %}
You have to **initialize Cotter Biometric** before using it to **enable or disable**
{% endhint %}

```java
Cotter.initBiometricSwitch(Context ctx, FragmentActivity fragmentAct, Activity act, CotterBiometricCallback callback)
```

Example:

{% code title="MainActivity.java -> OnCreate" %}

```java
Cotter.initBiometricSwitch(this, this, this, cotterBiometricCallback);
```

{% endcode %}

### 3. Enable Biometric

Remember, you have to **initialize Cotter Biometric** first before calling this function.

```java
Cotter.biometricPrompt.enableBiometric();
```

### 4. Disable Biometric

Remember, you have to **initialize Cotter Biometric** first before calling this function.

```java
Cotter.biometricPrompt.disableBiometric();
```

### 5. Checking the Enrolled status of Biometric after enable/disable

You can check again if the biometric is correctly enabled/disabled using the function below:

```java
Cotter.methods.biometricEnrolled(new CotterMethodChecker() {
    @Override
    public void onCheck(boolean isEnrolled) {
        // Check if biometric enrolled
        bioEnrolled.setText("Biometric enrolled: " + isEnrolled);
    }
});
```

## Check if Methods are Enrolled

You can check if an authentication method is enrolled and available for a user.

### 1. Check if Biometric is available

```java
Cotter.methods.biometricAvailable(CotterMethodChecker callback)
```

This method is used to check if biometric is available on the user's device. You need to provide a callback of type `CotterMethodChecker` to handle the result.

Example:

```java
Cotter.methods.biometricAvailable(new CotterMethodChecker() {
    @Override
    public void onCheck(boolean b) {
        // Check if biometric available and enabled
        bioAvailable.setText("Biometric available: " + b);
    }
});
```

### 2. Check if Biometrics is enrolled in the current device

```java
Cotter.methods.biometricEnrolled(CotterMethodChecker callback)
```

This method is used to check if biometric is enrolled. You need to provide a callback of type `CotterMethodChecker` to handle the result.

Example:

```java
Cotter.methods.biometricEnrolled(new CotterMethodChecker() {
    @Override
    public void onCheck(boolean isEnrolled) {
        // Check if biometric enrolled
        bioEnrolled.setText("Biometric enrolled: " + isEnrolled);
    }
});
```

### 3. Check if Pin is enrolled

```java
Cotter.methods.pinEnrolled(CotterMethodChecker callback)
```

This method is used to check if pin is enrolled. You need to provide a callback of type `CotterMethodChecker` to handle the result.

Example:

```java
Cotter.methods.pinEnrolled(new CotterMethodChecker() {
    @Override
    public void onCheck(boolean b) {
        // Check if biometric available and enabled
        pinEnrolled.setText("Pin Enrolled: " + b);
    }
});
```

## **Change Pin**

Starting the `PinChange` flow is exactly the same as starting the `PinEnrollment` flow.

```
Cotter.PinChange.startFlow(view, CallBack.class, "EVENT TAG");
```

Example:

```
// Inside your app, use a button onClick that calls
// openPinChange to start the flow
public void openPinChange(View view) {
    Cotter.PinChange.startFlow(view, Dashboard.class, "PIN_CHANGE");
}
```


# Customization

You can customize your screen texts, colors, images, and buttons.

See a [Complete Example](/sdk-reference/android/android-sdk-2/customization#complete-example) below.

## Understand the Flow

There are 4 flows:

* `PinEnrollment`: This flow allows the user to **register their PIN** for the first time, then **register their biometrics**
* `PinVerification`: This is used to **verify the user's biometrics** or use **PIN**.&#x20;
* `PinReset` : This is used to reset user's pin if they forgot the pin by sending a code to their email or phone number.
* `PinChange` : This is used to change the user's pin if they know what the current pin is.

### 1. Customize the header title of each screen

* Screen names for Pin Enrollment flow: `PinEnrollmentEnterPin` ,`PinEnrollmentReEnterPin`
* Screen names for Pin Verification flow: `PinVerification`
* Screen names for Pin Reset flow: `PinReset` , `PinResetEnterPin` , `PinResetReEnterPin`
* Screen names for Pin Change flow: `PinChangeVerifyPin` , `PinChangeEnterPin` , `PinChangeReEnterPin`

**To customize the Header** of each screen, call `Cotter.strings.setHeaders(<ScreenName>, <YourHeader>)`.&#x20;

{% hint style="info" %}
Do this right after your call `Cotter.init`, **before starting any flow**.
{% endhint %}

```java
Cotter.init(this.getApplicationContext(), "https://www.cotter.app/api/v0",
  getString(R.string.user_id), // user id
  getString(R.string.api_key_id), // api key id
  getString(R.string.api_secret_key)); // api secret key

  // Setting strings for Headers
  Cotter.strings.setHeaders(ScreenNames.PinEnrollmentEnterPin, "Activate PIN");
  Cotter.strings.setHeaders(ScreenNames.PinEnrollmentReEnterPin, "Confirm PIN");
  Cotter.strings.setHeaders(ScreenNames.PinVerification, "Verification");
  Cotter.strings.setHeaders(ScreenNames.PinChangeVerifyPin, "Change PIN");
  Cotter.strings.setHeaders(ScreenNames.PinChangeEnterPin, "New PIN");
  Cotter.strings.setHeaders(ScreenNames.PinChangeReEnterPin, "Confirm New PIN");
  Cotter.strings.setHeaders(ScreenNames.PinReset, "Reset PIN");
  Cotter.strings.setHeaders(ScreenNames.PinResetEnterPin, "Activate New PIN");
  Cotter.strings.setHeaders(ScreenNames.PinResetReEnterPin, "Confirm New PIN");
```

### 2. Customize Pin-Enrollment Enter Pin Screen <a href="#id-2-customize-pin-enrollment-enter-pin-screen" id="id-2-customize-pin-enrollment-enter-pin-screen"></a>

This is the first screen on the `PinEnrollment` flow.

![PinEnrollment Flow](/files/-M7p2NQtmiJhD3CLR3or)

To set the text for a specific element on a screen, specify the screen name, the name of the element, and the value.

The syntax is like the following:

```java
Cotter.strings.set___<ScreenName>___Strings(Strings.__<ElementName>__, <ValueYouWant>)
```

For example, if you want to **update the Title to "Enter PIN"**:

```java
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.Title, "Enter PIN");
```

{% hint style="danger" %}
Put this **below Cotter.init** before you start any flow.
{% endhint %}

### Advanced Customization <a href="#advanced-customization" id="advanced-customization"></a>

#### Setting the colors <a href="#setting-the-colors" id="setting-the-colors"></a>

As shown in the picture, there are 3 main colors that you can set:

* `ColorAccent`
* `ColorPrimary`
* `ColorDanger`

```java
Cotter.colors.setColorPrimary("#5E9051");
Cotter.colors.setColorAccent("#53228B");
Cotter.colors.setColorDanger("#B00020");
```

For the full list of colors that you can set, see [Setting Colors and Images](/sdk-reference/android/android-sdk-2/styling#setting-colors-and-images).

#### Error Text Views <a href="#error-text-views" id="error-text-views"></a>

There are 3 different places where errors may pop up:

* `PinEnrollmentEnterPin`: Error happens when the PIN is too weak, with the error `Strings.ErrorCombination`. To update:

```java
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.ErrorCombination, "Your PIN is too weak!");
```

* `PinEnrollmentReEnterPin`: Error happens when the PIN is not the same as the PIN entered originally, with the error `Strings.ErrorNoMatch`. To update:

```java
Cotter.strings.setPinEnrollmentReEnterPinStrings(Strings.ErrorNoMatch, "Your PINs don't match");
```

* `PinVerification`: Error happens when the user entered an invalid PIN, with the error `Strings.ErrorInvalid`. To update:

```java
Cotter.strings.setPinVerificationStrings(Strings.ErrorInvalid, "Invalid PIN");
```

### 3. Customize PinEnrollmentSuccess page <a href="#id-3-customize-pinenrollmentsuccess-page" id="id-3-customize-pinenrollmentsuccess-page"></a>

To change the image, add the image in your `/drawable` directory, and add the following:

```java
Cotter.colors.setSuccessImage(R.drawable.check);
```

To edit the texts, it's the same as the instructions before. See the [Complete Example](/sdk-reference/android/android-sdk-2/customization#complete-example) below.

For the full list of images that you can set, see [Setting Colors and Images](/sdk-reference/android/android-sdk-2/styling#setting-colors-and-images).

### 4. Customize PinVerification and PinReset Screen

![PinVerification and PinReset screen](/files/-M3Kuep-ncJie4aCOVCh)

```java
// PinVerification
Cotter.strings.setPinVerificationStrings(Strings.ForgotPin, "Lupa PIN");

// PinReset
Cotter.strings.setPinReset(Strings.Title, "Verification Code");
Cotter.strings.setPinReset(Strings.Subtitle, "We have sent the code to");
Cotter.strings.setPinReset(Strings.ResendCode, "Reset Code");
Cotter.strings.setPinReset(Strings.ErrorInvalid, "The code you entered is wrong");
```

### 4. Customize Alert Dialogs. <a href="#id-4-customize-alert-dialogs" id="id-4-customize-alert-dialogs"></a>

There are 2 places where an AlertDialog will pop-up:

* `PinEnrollmentEnterPin`: AlertDialog pop up when the user clicks 'X' to exit the screen.
* `PinVerification`: On a very rare occasion, the signature generated by the biometrics may be invalid. In which case, the AlertDialog will pop up.

To update the AlertDialog strings on the `PinEnrollmentEnterPin` screen:

```java
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.DialogTitle, "Are you sure you don't want to setup PIN?");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.DialogSubtitle, "Setting up your PIN is important to secure your account.");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.DialogPositiveButton, "Setup PIN");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.DialogNegativeButton, "Next Time");
```

### 5. Customize Biometric Prompt <a href="#id-5-customize-biometric-prompt" id="id-5-customize-biometric-prompt"></a>

There are 2 screens where the BiometricPrompt will pop up:

* `PinEnrollmentSuccess`: when Pin Enrollment is successful, user can then enroll their biometrics
* `PinVerification`: When user's default authentication method is biometrics, user will be prompted to verify their biometrics when an authentication is required.

## Complete Example

See the full list of API for:

* [Setting strings](/sdk-reference/android/android-sdk-2/setting-strings)
* [Setting colors and images](/sdk-reference/android/android-sdk-2/styling#setting-colors-and-images)

{% code title="MainActivity.java" %}

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // Initialize Cotter
        Cotter.init(this.getApplicationContext(), "https://www.cotter.app/api/v0","<your user id>", "<your api key id>", "<your api secret key>");
        
        // ------ Setting strings for Headers ------ //
        Cotter.strings.setHeaders(ScreenNames.PinEnrollmentEnterPin, "Aktivasi PIN");
        Cotter.strings.setHeaders(ScreenNames.PinEnrollmentReEnterPin, "Konfirmasi PIN");
        Cotter.strings.setHeaders(ScreenNames.PinVerification, "Verifikasi");
        Cotter.strings.setHeaders(ScreenNames.PinChangeVerifyPin, "Ganti PIN");
        Cotter.strings.setHeaders(ScreenNames.PinChangeEnterPin, "PIN Baru");
        Cotter.strings.setHeaders(ScreenNames.PinChangeReEnterPin, "Konfirmasi PIN Baru");
        Cotter.strings.setHeaders(ScreenNames.PinReset, "Reset PIN");
        Cotter.strings.setHeaders(ScreenNames.PinResetEnterPin, "Aktivasi PIN Baru");
        Cotter.strings.setHeaders(ScreenNames.PinResetReEnterPin, "Konfirmasi PIN Baru");
       

        // ----- SETTING STRINGS FOR PIN VERIFICATION -----//
        // Pin Verification page
        Cotter.strings.setPinVerificationStrings(Strings.Title, "Masukkan PIN");
        Cotter.strings.setPinVerificationStrings(Strings.ForgotPin, "Lupa PIN");
        Cotter.strings.setPinVerificationStrings(Strings.ErrorInvalid, "PIN tidak sesuai.");
        // Biometric prompt
        Cotter.strings.setPinVerificationStrings(Strings.BiometricTitle, "Verifikasi");
        Cotter.strings.setPinVerificationStrings(Strings.BiometricSubtitle,
                "Sentuh sensor sidik jari untuk melanjutkan");
        Cotter.strings.setPinVerificationStrings(Strings.BiometricNegativeButton, "Input PIN");
        // Alert dialog when biometric signature is not valid
        Cotter.strings.setPinVerificationStrings(Strings.DialogTitle, "Biometric kamu tidak bisa diverifikasi");
        Cotter.strings.setPinVerificationStrings(Strings.DialogSubtitle, "Kamu boleh masukkan PIN atau coba lagi.");
        Cotter.strings.setPinVerificationStrings(Strings.DialogPositiveButton, "Input PIN");
        Cotter.strings.setPinVerificationStrings(Strings.DialogNegativeButton, "Coba lagi");
        // ----- END SETTING STRINGS FOR PIN VERIFICATION -----//


        // Setting colors
        Cotter.colors.setColorPrimary("#5E9051");
        Cotter.colors.setColorAccent("#53228B");
        Cotter.colors.setColorDanger("#B00020");
}

public void openPinEnrollment(View view) {
    Cotter.PinEnrollment.startFlow(view, <CallbackScreen>.class, "PIN_ENROLLMENT");
}

public void openPinVerification(View view) {
    Cotter.user.setUserInformation("Emily", "hello@gmail.com", "EMAIL");
    Cotter.PinVerification.startFlow(view, <CallbackScreen>.class, "LOGIN");
}
```

{% endcode %}

## Screen Names Reference

![Pin Enrollment Flow](/files/-M3KCJaqQ9yvoSrSXzDy)

![](/files/-M3KCTmJtMnPbda6Ref8)

![](/files/-M3KCXPphXnc18z3PYcw)

![](/files/-M3KENDAERQnAQkQXYPI)


# Setting Strings

{% hint style="info" %}
Make sure you are using version `>= 0.4.3`
{% endhint %}

To set the text for a specific element on a screen, specify the screen name, the name of the element, and the value.

The syntax is like the following:

```java
Cotter.strings.set___<ScreenName>___Strings(Strings.__<ElementName>__, <ValueYouWant>)
```

For example, if you want to **update the Title to "Enter PIN"**:

```java
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.Title, "Enter PIN");
```

#### Full list of the API to change strings:

```java
// Headers
Cotter.strings.setHeaders(ScreenNames.PinEnrollmentEnterPin, "Activate Pin");
Cotter.strings.setHeaders(ScreenNames.PinEnrollmentReEnterPin, "Confirm Pin");
Cotter.strings.setHeaders(ScreenNames.PinVerification, "Verify Pin");
Cotter.strings.setHeaders(ScreenNames.PinChangeVerifyPin, "Change Pin");
Cotter.strings.setHeaders(ScreenNames.PinChangeEnterPin, "Enter New Pin");
Cotter.strings.setHeaders(ScreenNames.PinChangeReEnterPin, "Confirm New Pin");
Cotter.strings.setHeaders(ScreenNames.RegisterDeviceQRScanner, "Scan QR Code");
Cotter.strings.setHeaders(ScreenNames.RegisterDeviceQRShow, "");
Cotter.strings.setHeaders(ScreenNames.PinReset, "Forgot Pin");
Cotter.strings.setHeaders(ScreenNames.PinResetEnterPin, "Enter New Pin");
Cotter.strings.setHeaders(ScreenNames.PinResetReEnterPin, "Confirm New Pin");
        
// Pin Enrollment Default Strings
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.Title, "Enter Pin");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.ShowPin, "Show Pin");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.HidePin, "Hide Pin");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.ErrorCombination, "Your PIN is weak. Please enter a stronger PIN.");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.DialogTitle, "Are you sure you don't want to setup PIN?");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.DialogSubtitle, "Setting up your PIN is important to secure your account.");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.DialogPositiveButton, "Setup PIN");
Cotter.strings.setPinEnrollmentEnterPinStrings(Strings.DialogNegativeButton, "Next Time");

Cotter.strings.setPinEnrollmentReEnterPinStrings(Strings.Title, "Re-Enter Pin");
Cotter.strings.setPinEnrollmentReEnterPinStrings(Strings.ShowPin, "Show Pin");
Cotter.strings.setPinEnrollmentReEnterPinStrings(Strings.HidePin, "Hide Pin");
Cotter.strings.setPinEnrollmentReEnterPinStrings(Strings.ErrorNoMatch, "Your PIN doesn't match your previous PIN.");
Cotter.strings.setPinEnrollmentReEnterPinStrings(Strings.ErrorOther, "Something went wrong.");

Cotter.strings.setPinEnrollmentSuccessStrings(Strings.Title, "Successfully Activated PIN");
Cotter.strings.setPinEnrollmentSuccessStrings(Strings.Subtitle, "You can now use your PIN to unlock your account and make transactions");
Cotter.strings.setPinEnrollmentSuccessStrings(Strings.ButtonText, "Done");
Cotter.strings.setPinEnrollmentSuccessStrings(Strings.BiometricTitle, "Biometric Verification");
Cotter.strings.setPinEnrollmentSuccessStrings(Strings.BiometricSubtitle, "Protect your account using Biometrics");
Cotter.strings.setPinEnrollmentSuccessStrings(Strings.BiometricNegativeButton, "Cancel");
Cotter.strings.setPinEnrollmentSuccessStrings(Strings.DialogTitle, "Something went wrong");
Cotter.strings.setPinEnrollmentSuccessStrings(Strings.DialogSubtitle, "You may have already enrolled Biometrics before.");
Cotter.strings.setPinEnrollmentSuccessStrings(Strings.DialogPositiveButton, "Skip this step");
Cotter.strings.setPinEnrollmentSuccessStrings(Strings.DialogNegativeButton, "Try Again");

// Pin Verification default strings
Cotter.strings.setPinVerificationStrings(Strings.Title, "Enter Pin");
Cotter.strings.setPinVerificationStrings(Strings.ForgotPin, "Forgot Pin");
Cotter.strings.setPinVerificationStrings(Strings.ErrorInvalid, "Your PIN is invalid");
Cotter.strings.setPinVerificationStrings(Strings.ErrorOther, "Something went wrong.");
Cotter.strings.setPinVerificationStrings(Strings.BiometricTitle, "Verify Biometric");
Cotter.strings.setPinVerificationStrings(Strings.BiometricSubtitle, "Verify your biometric to continue");
Cotter.strings.setPinVerificationStrings(Strings.BiometricNegativeButton, "Input Pin");
Cotter.strings.setPinVerificationStrings(Strings.DialogTitle, "Unable to verify biometric");
Cotter.strings.setPinVerificationStrings(Strings.DialogSubtitle, "Do you want to try again or enter pin instead?");
Cotter.strings.setPinVerificationStrings(Strings.DialogPositiveButton, "Input PIN");
Cotter.strings.setPinVerificationStrings(Strings.DialogNegativeButton, "Try Again");

// Pin Change default strings
Cotter.strings.setPinChangeVerifyPinStrings(Strings.Title, "Enter Current Pin");
Cotter.strings.setPinChangeVerifyPinStrings(Strings.ShowPin, "Show Pin");
Cotter.strings.setPinChangeVerifyPinStrings(Strings.HidePin, "Hide Pin");
Cotter.strings.setPinChangeVerifyPinStrings(Strings.ErrorInvalid, "Your PIN is invalid");
Cotter.strings.setPinChangeVerifyPinStrings(Strings.ErrorOther, "Something went wrong");

Cotter.strings.setPinChangeEnterPinStrings(Strings.Title, "Enter New Pin");
Cotter.strings.setPinChangeEnterPinStrings(Strings.ShowPin, "Show Pin");
Cotter.strings.setPinChangeEnterPinStrings(Strings.HidePin, "Hide Pin");
Cotter.strings.setPinChangeEnterPinStrings(Strings.ErrorCombination, "Your PIN is weak. Please enter a stronger PIN.");
Cotter.strings.setPinChangeEnterPinStrings(Strings.DialogTitle, "Are you sure you don't want to setup your new PIN?");
Cotter.strings.setPinChangeEnterPinStrings(Strings.DialogSubtitle, "You will be able to use your old PIN if you don't setup a new PIN.");
Cotter.strings.setPinChangeEnterPinStrings(Strings.DialogPositiveButton, "Setup New PIN");
Cotter.strings.setPinChangeEnterPinStrings(Strings.DialogNegativeButton, "Next Time");

Cotter.strings.setPinChangeReEnterPinStrings(Strings.Title, "Re-Enter New Pin");
Cotter.strings.setPinChangeReEnterPinStrings(Strings.ShowPin, "Show Pin");
Cotter.strings.setPinChangeReEnterPinStrings(Strings.HidePin, "Hide Pin");
Cotter.strings.setPinChangeReEnterPinStrings(Strings.ErrorNoMatch, "Your PIN doesn't match your previous PIN.");

Cotter.strings.setPinChangeSuccessStrings(Strings.Title, "Successfully Change PIN");
Cotter.strings.setPinChangeSuccessStrings(Strings.Subtitle, "You can now use your new PIN to unlock your account and make transactions");
Cotter.strings.setPinChangeSuccessStrings(Strings.ButtonText, "Done");

// Enable Disable Biometric
Cotter.strings.setBiometricChangeStrings(Strings.BiometricTitle, "Biometric Verification");
Cotter.strings.setBiometricChangeStrings(Strings.BiometricSubtitle, "Protect your account using Biometrics");
Cotter.strings.setBiometricChangeStrings(Strings.BiometricNegativeButton, "Cancel");
Cotter.strings.setBiometricChangeStrings(Strings.DialogTitle, "Something went wrong");
Cotter.strings.setBiometricChangeStrings(Strings.DialogSubtitle, "You may have already enrolled Biometrics before.");
Cotter.strings.setBiometricChangeStrings(Strings.DialogDisabledSubtitle, "You may not have Biometrics enrolled.");
Cotter.strings.setBiometricChangeStrings(Strings.DialogPositiveButton, "Cancel");
Cotter.strings.setBiometricChangeStrings(Strings.DialogNegativeButton, "Try Again");

// Network Error Dialog
Cotter.strings.setNetworkErrorStrings(Strings.DialogTitle, "Connection Lost");
Cotter.strings.setNetworkErrorStrings(Strings.DialogSubtitle, "Please establish a stronger internet connection and try again.");
Cotter.strings.setNetworkErrorStrings(Strings.DialogPositiveButton, "Try Again");

// HTTP Error Dialog (Not Used)
Cotter.strings.setHttpErrorStrings(Strings.DialogTitle, "Something went wrong");
Cotter.strings.setHttpErrorStrings(Strings.DialogSubtitle, "Looks like there's an error on our end. Please try again.");
Cotter.strings.setHttpErrorStrings(Strings.DialogPositiveButton, "Try Again");

// Approve login from Non-Trusted Device
Cotter.strings.setApproveRequestStrings(Strings.Title, "Are you trying to sign in?");
Cotter.strings.setApproveRequestStrings(Strings.Subtitle, "Someone is trying to sign in to your account from another device.");
Cotter.strings.setApproveRequestStrings(Strings.ButtonYes, "Yes");
Cotter.strings.setApproveRequestStrings(Strings.ButtonNo, "No, it's not me");

// Request Auth
Cotter.strings.setRequestAuthStrings(Strings.DialogTitle, "Approve this login from your phone");
Cotter.strings.setRequestAuthStrings(Strings.DialogSubtitle, "A notification is sent to your trusted device to confirm it's you.");
Cotter.strings.setRequestAuthErrorStrings(Strings.DialogTitle, "Something went wrong");
Cotter.strings.setRequestAuthErrorStrings(Strings.DialogSubtitle, "We're unable to confirm that it's you. Please try again.");

// Success Sheet
Cotter.strings.setSuccessSheetStrings(Strings.DialogTitle, "Success Registering New Device");
Cotter.strings.setSuccessSheetStrings(Strings.DialogSubtitle, "You can now use your new device to access your account without approval.");
Cotter.strings.setSuccessSheetErrorStrings(Strings.DialogTitle, "Unable to Register New Device");
Cotter.strings.setSuccessSheetErrorStrings(Strings.DialogSubtitle, "Please try again.");

// QR Code Show
Cotter.strings.setQRCodeShowStrings(Strings.Title, "Register this Device");
Cotter.strings.setQRCodeShowStrings(Strings.Subtitle, "Please scan this QR Code from a Trusted Device.");
Cotter.strings.setQRCodeShowErrorStrings(Strings.Title, "Something went wrong");
Cotter.strings.setQRCodeShowErrorStrings(Strings.Subtitle, "The request timed out. Please try again.");

// Reset Pin
Cotter.strings.setPinResetStrings(Strings.Title, "Verification Code");
Cotter.strings.setPinResetStrings(Strings.Subtitle, "We have sent a code to");
Cotter.strings.setPinResetStrings(Strings.ResendCode, "Resend Code");
Cotter.strings.setPinResetStrings(Strings.ErrorInvalid, "The code you entered is invalid");
Cotter.strings.setPinResetStrings(Strings.ErrorOther, "Something went wrong");

Cotter.strings.setPinResetEnterPinStrings(Strings.Title, "Enter New Pin");
Cotter.strings.setPinResetEnterPinStrings(Strings.ShowPin, "Show Pin");
Cotter.strings.setPinResetEnterPinStrings(Strings.HidePin, "Hide Pin");
Cotter.strings.setPinResetEnterPinStrings(Strings.ErrorCombination, "Your PIN is weak. Please enter a stronger PIN.");
Cotter.strings.setPinResetEnterPinStrings(Strings.DialogTitle, "Are you sure you don't want to setup your new PIN?");
Cotter.strings.setPinResetEnterPinStrings(Strings.DialogSubtitle, "You will be able to use your old PIN if you don't setup a new PIN.");
Cotter.strings.setPinResetEnterPinStrings(Strings.DialogPositiveButton, "Setup New PIN");
Cotter.strings.setPinResetEnterPinStrings(Strings.DialogNegativeButton, "Next Time");

Cotter.strings.setPinResetReEnterPinStrings(Strings.Title, "Re-Enter New Pin");
Cotter.strings.setPinResetReEnterPinStrings(Strings.ShowPin, "Show Pin");
Cotter.strings.setPinResetReEnterPinStrings(Strings.HidePin, "Hide Pin");
Cotter.strings.setPinResetReEnterPinStrings(Strings.ErrorNoMatch, "Your PIN doesn't match your previous PIN.");

Cotter.strings.setPinResetSuccessStrings(Strings.Title, "Successfully made a new Pin");
Cotter.strings.setPinResetSuccessStrings(Strings.Subtitle, "You can now use your new PIN to unlock your account");
Cotter.strings.setPinResetSuccessStrings(Strings.ButtonText, "Done");
```


# Styling

{% hint style="info" %}
Make sure you are using version `>= 0.4.3`
{% endhint %}

Starting from version `0.4.3` , we've moved the styling to `styles.xml`.&#x20;

You can check `res/layout/...` and find the component you'd like to style and check the `style=@style/<StyleName>` to make your custom style. You can find the default styles at [`res/values/styles.xml`](https://github.com/cotterapp/android-sdk/blob/master/app/src/main/res/values/styles.xml) .

#### For example, you can change the Pin Bullet Size:

If you look at [`res/layout/pin_input.xml`](https://github.com/cotterapp/android-sdk/blob/master/app/src/main/res/layout/pin_input.xml) , you'll see:

```markup
<TextView
    android:id="@+id/input_1"
    android:text="\u25CF"
    style="@style/CotterPinInputBullet"
/>
```

This means that you can define your own style for the bullets of the pin by adding the following code to **your project's `res/values/styles.xml`**

```markup
<resources>
    ...
    <!-- Add the following block, 
        change the value for `android:textColor` to `50dp`
        to make the bullets bigger -->
    <style name="CotterPinInputBullet">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:fontFamily">@font/akkurat_bold</item>
        <item name="android:textColor">@color/colorLightGrey</item>
        <item name="android:textSize">50dp</item> 
        <item name="android:paddingHorizontal">10dp</item>
    </style>

</resources>
```

## Setting Colors and Images

There are some colors and images that are set using `Cotter.colors.setColor` as mentioned [here](https://docs.cotter.app/sdk-reference/android/android-sdk-2/customization#setting-the-colors).

**Full list of API to set colors and images:**

```java
Cotter.colors.setColorBackground(colorString)
Cotter.colors.setColorBlack(colorString)
Cotter.colors.setColorPrimary(colorString)
Cotter.colors.setColorAccent(colorString)
Cotter.colors.setColorDanger(colorString)
Cotter.colors.setColorDangerLight(colorString)
Cotter.colors.setColorPrimaryLight(colorString)
Cotter.colors.setColorSuperLightGrey(colorString)
Cotter.colors.setSuccessImage(imageInt)
Cotter.colors.setErrorImage(imageInt)
Cotter.colors.setHttpErrorImage(imageInt)
Cotter.colors.setNetworkErrorImage(imageInt)
Cotter.colors.setLogo(imageInt)
Cotter.colors.setTap(imageInt)
```

**List of colors and images that overrides the styling colors and images**

* Container background color = `ColorBackground`&#x20;
* "Show PIn" text = `ColorPrimary`&#x20;
* "Error" Text = `ColorDanger`
* Network error dialog: `NetworkErrorImage`&#x20;
* HTTP error dialog: `HttpErrorImage` (not used)

**Pin Box (for Reset Pin)**&#x20;

* Empty: BoxBackgroundColor = `ColorSuperLightGrey`&#x20;
* Filled: BoxBackgroundColor = `ColorPrimaryLight`&#x20;
* Filled: Box Text Color = `ColorBlack`&#x20;
* Error: BoxBackgroundColor = `ColorDangerLight`&#x20;
* Error: Box Text Color = `ColorDanger`

**Pin Bullet**&#x20;

* Empty: Bullet = `ColorPrimaryLight`&#x20;
* Filled: Bullet = `ColorAccent`&#x20;
* Error: Bullet = `ColorDanger`

**Trusted Device QR Code**&#x20;

* QR Code Color: `ColorBlack`&#x20;
* QR Code background: `ColorWhite`

**Success Image on RegisterDeviceQRShowActivity, RegisterDeviceQRScannerActivity, PinEnrollmentSuccessActivity**&#x20;

* Success Image: `SuccessImage`

**Error Image on RegisterDeviceQRShowActivity, RegisterDeviceQRScannerActivity, RequestAuthSheet**&#x20;

* Error Image: `ErrorImage`

**ApproveRequestActivity**&#x20;

* Logo Image: `Logo`

**RequestAuthSheet**&#x20;

* Tap Image: `Tap`


# Older SDK Version


# Sign In with Device

Our Android SDK offers the easiest way to integrate "Sign in with device" to your android app. You can simply call a function and it does most of the heavy lifting and authentication for you.

### Overview

Authenticating users using Trusted Devices with Cotter's Android SDK consists of the following steps:

1. Import and Initialize Cotter
2. Calling functions to register devices a Trusted Device
3. Calling functions to authenticate from Trusted and Non-Trusted Device

### What you're building

![Trusted Devices on Cotter's Android SDK](/files/-M14tFiVaq9sa4niiIfj)

## Steps

1. [Import Cotter as a dependency](/sdk-reference/android/older-sdk-version/android-sdk#step-1-import-cotter-as-a-dependency)
2. [Set Allowed Methods](/sdk-reference/android/older-sdk-version/android-sdk#step-2-setting-authentication-methods) in the Dashboard to allow Trusted Devices
3. [Create a User](/sdk-reference/android/older-sdk-version/android-sdk#step-3-creating-a-user): Create a user in Cotter by specifying your user ID.
4. [Initialize Cotter in your MainActivity](/sdk-reference/android/older-sdk-version/android-sdk#step-4-initialize-cotter)
5. [Register current device as a Trusted Device](/sdk-reference/android/older-sdk-version/android-sdk#step-5-register-this-device-as-a-registered-device)
6. [Authenticate from a Trusted Device](/sdk-reference/android/older-sdk-version/android-sdk#step-6-authenticate-from-a-trusted-device)

### Step 1: Import Cotter as a dependency

Add JitPack repository your project level `build.gradle` at the end of repositories.

```java
allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}
```

Add the Cotter's SDK as a dependency in your app level `build.gradle` .

```java
android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
  ...
  implementation 'com.github.cotterapp:android-sdk:0.3.9'
}
```

Check the latest version here <https://github.com/cotterapp/android-sdk/releases>. Then sync your gradle files.

### Step 2: Setting Authentication Methods

You need to set allowed methods for authenticating your users. To allow `TRUSTED DEVICES`, go to <https://dev.cotter.app/rules>

![Set Trusted Device as an Allowed Method](/files/-M15A0xW-OgzjEnPqgx6)

{% hint style="warning" %}
Remember to set the correct Project in the dropdown list
{% endhint %}

### Step 3: Creating a User

To use Cotter's sign in with device functionality, **you need to first register the user to Cotter.**

#### Registering a User to Cotter <a href="#id-1-registering-a-user" id="id-1-registering-a-user"></a>

Your server should do this request to Cotter's server **during Registration**.

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

```http
curl -XPOST \
-H 'API_KEY_ID: <your key id>' \
-H 'API_SECRET_KEY: <your secret key>' \
-H "Content-Type: application/json" \
-d '{"client_user_id": "<Your User Identifier (string)>"}' \
'https://www.cotter.app/api/v0/user/create'
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "ID": "9449e9e9-00e0-4d6b-a4b6-28c5b22b0b0f",
  "created_at": "2020-01-21T12:40:21.200727668Z",
  "update_at": "2020-01-21T12:40:21.200727668Z",
  "deleted_at": null,
  "issuer": "<your key ID>",
  "client_user_id": "<Your User Identifier (string)>",
  "enrolled": [],
  "default_method": null
}
```

{% endtab %}
{% endtabs %}

Check out [Create a User API Reference](/api-reference/user-api#create-a-user) for full Description

### Step 4: Initialize Cotter

You can now use the SDK to enroll and verify Trusted Devices for the user you just created.

In your MainActivity, initialize Cotter with API keys, Cotter's URL to use (prod/test), and your User ID.

Production Endpoint: `https://www.cotter.app/api/v0`

```java
Cotter.init(
    this.getApplicationContext(),
    "https://www.cotter.app/api/v0",
    "<User ID>", // fill with empty string for Verify EMAIL & PHONE
    "<API_KEY_ID>",
    "<API_SECRET_KEY>" // fill with empty string for Verify EMAIL & PHONE
);
```

Example:

{% code title="MainActivity.java" %}

```java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        Cotter.init(
            this.getApplicationContext(),
            "https://www.cotter.app/api/v0",
            "hello@example.com",
            "<API_KEY_ID>",
            "<API_SECRET_KEY>");
    }
    ...
```

{% endcode %}

### Step 5: Register this device as a Registered Device

Right after your user finished their registration to your app, you should automatically register the device as a Trusted Device (or prompt your user if they want to make this device as a Trusted Device).

To enroll the current device as a TrustedDevice:

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

```java
TrustedDeviceHelper.enrollDevice(this, new Callback() {
    @Override
    public void onSuccess(JSONObject result) {
        Toast.makeText(getApplicationContext(), result.toString(), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onError(String error) {
        Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
    }
});
```

{% endtab %}

{% tab title="JSON result" %}

```javascript
{
  "ID": "746500ae-a5cd-4692-8fd0-49d57cf7cc57", // User ID from Cotter's system
  "created_at": "2020-02-25T04:08:26.174597Z",
  "update_at": "2020-02-27T22:20:31.333154814Z",
  "deleted_at": null,
  "issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API KEY ID
  "client_user_id": "1014", // you client's user ID
  "enrolled": [ // Enrolled Authentication Methods
    "PIN",
    "BIOMETRIC",
    "TRUSTED_DEVICE" // This should be added to enrolled methods once enrollDevice successful
  ],
  "default_method": "TRUSTED_DEVICE" // This will automatically be the last enrolled method, which is Trusted Device in this case
}
```

{% endtab %}
{% endtabs %}

�This method is only for the first Trusted Device. You will get an error if you attempt to enroll another Trusted Device using `enrollDevice` when there's already a Trusted Device for the account. To enroll other devices, see [Add a new Trusted Device](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device).

### Step 6: Authenticate from a Trusted Device

To authenticate a device, call the `TrustedDeviceHelper.requestAuth` function. This will automatically detect whether the current device is a Trusted Device or not.

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

```java
TrustedDeviceHelper.requestAuth(this, "<EVENT NAME>", this, Dashboard.class, new Callback() {
    @Override
    public void onSuccess(JSONObject result) {
        Toast.makeText(getApplicationContext(), result.toString(), Toast.LENGTH_SHORT).show();
        
        // Pass in the `result` to your backend server when:
        // - Logging in your users
        // - making a transaction
        // - fetching sensitive data
    }

    @Override
    public void onError(String error) {
        Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
    }
});
```

{% endtab %}

{% tab title="JSON result" %}

```javascript
{
  "ID": 1361, // Event ID
  "CreatedAt": "2020-02-27T22:22:48.705212512Z",
  "UpdatedAt": "2020-02-27T22:22:48.705212512Z",
  "DeletedAt": null,
  "client_user_id": "1014", // your client's User ID
  "issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API Key
  "event": "<EVENT NAME>",// requested event (LOGIN, or TRANSACTION, etc)
  "ip": "192.168.232.2", 
  "location": "Unknown",
  "timestamp": "1582842167",
  "method": "TRUSTED_DEVICE", // auth method: TRUSTED_DEVICE (other choices are PIN / BIOMETRIC)
  "new": false, // Is this a new pending event. More explanation below about Non-Trusted Device
  "approved": true, // Is this event approved.
  "signature": "oonMGCAxp3..." // Signature to make sure this event comes from Cotter's server
}
```

{% endtab %}
{% endtabs %}

�**Parameters**

```java
requestAuth(Context ctx, String event, AppCompatActivity act, Class callbackClass, Callback callback)
```

* `event`: A name to identify the event action type, like "LOGIN" or "TRANSACTION".
* `callbackClass` : The next activity class that you want to redirect to when the authentication request is finished
* `callback` : A custom Callback function that implements 2 methods: `onSuccess` and `onError`.

When an Authentication Event is requested using method `TRUSTED_DEVICE`, there are 2 possible cases:

#### Case 1: The current device is a Trusted Device

If the current device is a Trusted Device, it should automatically be approved, and you will receive a JSON result containing the requested `Event` and whether or not it's approved. The approval is based on whether or not the signature included in the request from the SDK is valid.

```java
{
  ...
  "method": "TRUSTED_DEVICE",
  "new": false, // Is this a new pending event (should be false).
  "approved": true, // Is this event approved (should be true).
  
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUz...", // validate this access token
    "id_token": "eyJhbGciOiJFUz...",
    "refresh_token": "5:cYIfabtspE1cBeD7KP...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "TRUSTED_DEVICE"
  }
}

```

You should see a result that the event is **not new**, and that it's **approved**. This is because the signature from the Trusted Device is sufficient to prove that the device is authorized.

When passing this Response to your backend, you need to check if this JSON is valid and if it comes from Cotter's server by validating the **access token**.

Checkout how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

#### Case 2: The current device is NOT a Trusted Device

We'll cover this in the next guide:

{% content-ref url="/pages/-M180MWupb\_alhptlzJO" %}
[Authenticate from a Non-Trusted Device](/sdk-reference/android/older-sdk-version/android-sdk/old-authenticate-from-a-non-trusted-device)
{% endcontent-ref %}

## 🎉 You're done!

## Next Steps

{% content-ref url="/pages/-M180MWupb\_alhptlzJO" %}
[Authenticate from a Non-Trusted Device](/sdk-reference/android/older-sdk-version/android-sdk/old-authenticate-from-a-non-trusted-device)
{% endcontent-ref %}

{% content-ref url="/pages/-M180XVPUU9yBlVfPetR" %}
[Add a new Trusted Device](/sdk-reference/android/sign-in-with-device/add-a-new-trusted-device)
{% endcontent-ref %}

{% content-ref url="/pages/-M18-UXHt3c3ixTn76S4" %}
[Customization](/sdk-reference/android/sign-in-with-device/customization)
{% endcontent-ref %}

## Add Biometric / PIN

{% content-ref url="/pages/-M0RaVb\_ggDYyCQyIUXy" %}
[Biometric/Pin](/sdk-reference/android/android-sdk-2)
{% endcontent-ref %}


# Authenticate from a Non-Trusted Device

## Steps

There are 2 steps in authenticating a Non-Trusted Device

1. [Make a request](/sdk-reference/android/older-sdk-version/android-sdk/old-authenticate-from-a-non-trusted-device#step-1-making-an-authentication-request) from the Non-Trusted Device and wait for approval
2. [Approve the request](/sdk-reference/android/older-sdk-version/android-sdk/old-authenticate-from-a-non-trusted-device#step-2-how-to-approve-a-pending-request-from-a-trusted-device) from a Trusted Device.&#x20;

## Step 1: Making an Authentication Request&#x20;

Same as how it works with [Authenticating from a Trusted Device](/sdk-reference/android/older-sdk-version/android-sdk#step-6-authenticate-from-a-trusted-device), to authenticate a device, call the `TrustedDeviceHelper.requestAuth` function. **This will automatically detect whether the current device is a Trusted Device or not.**

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

```java
TrustedDeviceHelper.requestAuth(this, "<EVENT NAME>", this, Callback.class, new Callback() {
    @Override
    public void onSuccess(JSONObject result) {
        Toast.makeText(getApplicationContext(), result.toString(), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onError(String error) {
        Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
    }
});
```

{% endtab %}

{% tab title="JSON result" %}

```javascript
{
  "ID": 1361, // Event ID
  "CreatedAt": "2020-02-27T22:22:48.705212512Z",
  "UpdatedAt": "2020-02-27T22:22:48.705212512Z",
  "DeletedAt": null,
  "client_user_id": "1014", // your client's User ID
  "issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API Key
  "event": "<EVENT NAME>",// requested event (LOGIN, or TRANSACTION, etc)
  "ip": "192.168.232.2", 
  "location": "Unknown",
  "timestamp": "1582842167",
  "method": "TRUSTED_DEVICE", // auth method: TRUSTED_DEVICE (other choices are PIN / BIOMETRIC)
  "new": false, // Is this a new pending event. More explanation below about Non-Trusted Device
  "approved": true // Is this event approved.
    
  // If authentication successful:
  // Validate the oauth_token.access_token
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUz...", // validate this access token
    "id_token": "eyJhbGciOiJFUz...",
    "refresh_token": "5:cYIfabtspE1cBeD7KP...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "TRUSTED_DEVICE"
  }
}

```

{% endtab %}
{% endtabs %}

#### Event Name

The `EVENT_NAME` should be filled by your app, according to what type of authentication event was requested. Example: `LOGIN` event, `TRANSACTION` event, `UPDATE_PROFILE` event, etc.

#### Callback Class

The `Callback.class` should be filled with the Activity to go to when the request is approved. For example, if this is a login request, then the Callback Activity can be your Dashboard class.

When an Authentication Event is requested `TRUSTED_DEVICE`, there are 2 possible cases:

### Case 1: The current device is a Trusted Device

You will be automatically approved if the request is coming from a Trusted Device. This is covered in [Authenticate from a Trusted Device](/sdk-reference/android/older-sdk-version/android-sdk#step-6-authenticate-from-a-trusted-device).

### Case 2: The current device is NOT a Trusted Device

If the current device is **not** a Trusted Device, then it will create a **pending event.** This pending event will need to be **approved by a Trusted Device**. You should see a JSON result looking like:

```java
{
  ...
  "method": "TRUSTED_DEVICE",
  // This is a pending event. 
  // It will turn to false once there's a Trusted Device 
  // responded to approve or reject this event
  "new": true, 
  
  // Since it's a pending event, it's not yet approved.
  "approved": false
}
```

The SDK will automatically show a prompt for the user to approve the event from a Trusted Device. The user then need to approve this request from their Trusted Device.

## Step 2: How to approve a pending request from a Trusted Device

To check if there's a pending event for this user, you can call the `getNewEvent` method from a Trusted Device.

```java
TrustedDeviceHelper.getNewEvent(this, this);
```

{% hint style="info" %}
**In the future,** your app that is installed in a Trusted Device will receive a notification and this method is invoked when the notification is opened.
{% endhint %}

**For now,** you should **tell your user to press a button and invoke this method** when they try to login from a non-trusted device.

The SDK will open a prompt in the Trusted Device asking if the user want to approve or reject the authentication request

![Prompt to approve request from a Trusted Device](/files/-M17zwpUdHmduY0qosRm)

In the prompt in the user's Non-Trusted Device (right picture), you can [Customize the Prompt](/sdk-reference/android/sign-in-with-device/customization) to ask them to press a specific button in the Trusted Device to invoke the prompt on the left.

## Receiving the result and check if the request was approved

**In the non-trusted device** where the user is trying to log in, you'll receive the result in the callback class that your specified.

### Case 1: The request is approved

When the request is approved, the SDK will automatically redirect to your Callback Class. In the callback class, you should handle the response from Cotter

#### Handle the response in your callback class

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_dashboard);

    // Handle the response
    TrustedDeviceResponse trustDevResp = TrustedDeviceHelper.handleResponse(getIntent());
    if (trustDevResp != null) {
        Log.i("TrustedDeviceResponse: ", Integer.toString(trustDevResp.event.ID));
    }
}
```

You'll receive a response in the form of a `TrustedDeviceResponse` object.

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

```java
public class TrustedDeviceResponse {
    public String response; // The response in the form of a string
    public Event event; // The same response, but parsed in to an Event object
    public boolean approved; // Whether or not the event was approved
    public String error; // If there's any error, this will contain the error, otherwise null
}
```

{% endtab %}

{% tab title="String Response" %}

```javascript
{
  "ID": 1364,
  "CreatedAt": "2020-02-28T00:08:52.715136Z",
  "UpdatedAt": "2020-02-28T00:08:56.917318Z",
  "DeletedAt": null,
  "client_user_id": "1014",
  "issuer": "afcabd98-745f-4b9e-98de-af968d9951d3",
  "event": "LOGIN",
  "ip": "10.0.2.15",
  "location": "",
  "timestamp": "1582848531",
  "method": "TRUSTED_DEVICE",
  "new": false, // Should now be false because the Trusted Device already responded
  "approved": true // In this case 1, this should be true. Otherwise your Callback class won't be open
  
  // If authentication successful:
  // Validate the oauth_token.access_token
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUz...", // validate this access token
    "id_token": "eyJhbGciOiJFUz...",
    "refresh_token": "5:cYIfabtspE1cBeD7KP...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "TRUSTED_DEVICE"
  }
}
```

{% endtab %}
{% endtabs %}

When this authentication request is approved, you'll receive an access token in the **String Response**. Send this access token to your backend to validate it:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

### Case 2: The request is rejected

If the request is **not approved** for 1 minute, the prompt in the Non-Trusted Device will show an error message. The prompt will not show an error immediately when the Trusted Device denied the request, i.e. there is no difference between rejection and no response from the Trusted Device.

![Authentication request from a Non-Trusted Device](/files/-M18TY85OnpSrt64xBPF)

In this case, the [`onError` function that you passed in earlier](/sdk-reference/android/older-sdk-version/android-sdk/old-authenticate-from-a-non-trusted-device#making-an-authentication-request) will be called with an error message of `Event is not approved`.


# Python (for CLI)

Our Python SDK makes it easy to add a login flow to your python scripts and CLI.

## How it works

1. Use calls your CLI to log in, for example `mycli login`
2. The CLI will attempt to open a browser with a link to log in and display the link to the user.
3. The user logs in on their browser, which then sends a code back to the CLI
4. Our SDK processes the code and return an access token and refresh token to your CLI.

## Installation

```
pip install cotter
```

Find the latest versions here <https://pypi.org/project/cotter/>

## Usage

Get your `API_KEY_ID` from [Cotter's Dashboard](https://dev.cotter.app/).

### Step 1: Copy [`cotter_login_success.html`](https://github.com/cotterapp/python-sdk/blob/master/example/cotter_login_success.html) from the `example` folder.

You can make your own Success page. After the user successfully logged-in, the website will redirect to `http://localhost:port` and you should show a "Success message" and tell the user to go back to your terminal. Feel free to copy our example page and modify it.

**Put the success page with the name `cotter_login_success.html` at the same directory as where you put the code below.**

### Step 2: Call Cotter's login function

```python
import cotter
api_key = "YOUR API KEY ID"
port = 8080 # Open a port to receive code from the website after successful authentication
response = cotter.login_with_email_link(api_key, port)
print(response)
```

## Available methods:

#### Using Email

```python
# Use Magic Link
response = login_with_email_link(api_key, port)
# Use OTP
response = login_with_email_otp(api_key, port)
```

#### Using Phone Number

```python
# Use Magic Link
response = login_with_phone_link(api_key, port)
# Use OTP
response = login_with_phone_otp(api_key, port)
```

## Storing the tokens

#### Store the tokens to a file:

```python
from cotter import tokenhandler
tokenhandler.store_token_to_file(response["oauth_token"], "cottertoken.json")
```

#### Get the tokens from a file (automatically refresh if needed):

```python
from cotter import tokenhandler
oauth_token = tokenhandler.get_token_from_file("cottertoken.json", api_key)
```

## Refreshing tokens (if not using the functions above)

```python
# This will only refresh if needed
from cotter import tokenhandler
oauth_token = tokenhandler.refresh_token(oauth_token, api_key)
```

## Validating tokens

```python
from cotter import validate
access_token_decoded = validate.validate_access_token(response["oauth_token"]["access_token"], api_key)
id_token_decoded = validate.validate_id_token(response["oauth_token"]["id_token"], api_key)
```

## Troubleshooting

#### Allowed Origin Error

If you get an error like this:

```javascript
{
  "msg": "The redirect URL http://localhost:1234 or the parent origin :// is not in the list of allowed URLs. Please contact the site owner.",
  "type": ""
}
```

You may have set up a list of Allowed URLs on the dashboard. Make sure you add these 2 URLs:

* `http://localhost:<PORT>` based on the port you used above
* `://` (this is a bug, join our [Slack channel](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA) to receive updates)<br>


# API for Other Mobile Apps or CLI

For cli apps and other mobile apps languages and frameworks that are not yet supported, you can use our API. Here are the guides on how to use them.

## Complete Guides

#### Sign in with Email/Phone Number

Authenticate users by sending a Magic Link or OTP via email, SMS, or WhatsApp.

{% content-ref url="/pages/-M0QVaCMLRWd0jUsIcYn" %}
[Verify Email/Phone Number](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps)
{% endcontent-ref %}


# Verify Email/Phone Number

Authentication API that can be called from your mobile apps. This API utilizes an in-app webview with cookies sharing to allow a single-sign-on for bypassing email and phone number verification.

> **Concepts:** Learn about how [**Sign in with Email/Phone Number**](/features/verify-email-phone) works.

### Overview

Verifying **email and phone number** in your mobile app using our Authentication API consists of the following steps:

1. Open a WebView within your app with shared cookies
2. Direct users to Cotter's Auth page
3. Redirect back to your app with an authorization code
4. Call Cotter API with the authorization code
5. Get back the user's email or phone number, and whether or not it's verified

Here's an example on opening the in-app Browser from iOS and Android

* **Android**: Use the [Trusted Web Activity](https://developers.google.com/web/updates/2019/02/using-twa)
* **iOS:** Use the [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/authenticating_a_user_through_a_web_service)

### What you're building

![Authentication API for Android and iOS](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-M0QGDMRD8y_Kd-BpOvT%2F-M0QUEFxHD9nbe2hsnLf%2F-M0QabDr--EjvJZ95y6m%2Fimage.png?alt=media\&token=601a7625-9a3c-4022-a6e5-9a843bf1af9e)

### Authorization Flow

For mobile apps, we're going to use the [OAuth 2.0 Authorization Code Flow with Proof Key for Code Exchange (PKCE)](https://tools.ietf.org/html/rfc7636). This flow is recommended for Mobile Apps because:

* Mobile apps can't securely store the Secret Key. This is because decompiling the App will reveal the Secret Key, and there's only one secret key so it'll be the same for all users.
* Sending tokens to Custom URL schemes (ex. YourApp\://) will potentially expose the tokens to malicious apps.

## Steps

1. [Create a `code_verifier`](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#step-1-create-a-code-verifier) and a `code_challenge`
2. [Request Authorization from Cotter](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#step-2-request-authorization-from-cotter): Redirect user to Cotter to verify their email/phone and receive an `authorization_code` back to your app.
3. [Request Tokens and Identity](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#step-3-request-tokens-and-identity): Send your `authorization_code` and `code_verifier` to Cotter server and get back a `token` and the user's email or phone number.
4. [Include Token to your server](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#step-4-include-the-token-to-your-server): The token contains the user's verified email/phone number and a signature. Include this to your signup/login request to your backend

### Step 1: Create a Code Verifier

A `code_verifier` is a cryptographically-random key that will be sent to Cotter along with the `authorization_code` on Step 3. Read more about what are [code challenge and verifier](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#what-is-state-and-code-challenge).

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

```javascript
function dec2hex(dec) {
  return ('0' + dec.toString(16)).substr(-2)
}

function generateRandomString() {
  var array = new Uint32Array(56/2);
  window.crypto.getRandomValues(array);
  return Array.from(array, dec2hex).join('');
}

var verifier = generateRandomString();
```

{% endtab %}

{% tab title="Python" %}

```python
import os
import base64
verifier_bytes = os.urandom(32)
code_verifier = base64.urlsafe_b64encode(verifier_bytes).rstrip(b'=')
```

{% endtab %}

{% tab title="Java" %}

```java
// import android.util.Base64;
SecureRandom sr = new SecureRandom();
byte[] code = new byte[32];
sr.nextBytes(code);
String verifier = Base64.encodeToString(code, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
```

{% endtab %}

{% tab title="Swift 3" %}

```swift
var buffer = [UInt8](repeating: 0, count: 32)
_ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer)
let verifier = Data(bytes: buffer).base64EncodedString()
    .replacingOccurrences(of: "+", with: "-")
    .replacingOccurrences(of: "/", with: "\_")
    .replacingOccurrences(of: "=", with: "")
    .trimmingCharacters(in: .whitespaces)
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
NSMutableData *data = [NSMutableData dataWithLength:32];
int result __attribute__((unused)) = SecRandomCopyBytes(kSecRandomDefault, 32, data.mutableBytes);
NSString *verifier = [[[[data base64EncodedStringWithOptions:0]
                        stringByReplacingOccurrencesOfString:@"+" withString:@"-"]
                        stringByReplacingOccurrencesOfString:@"/" withString:@"_"]
                        stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]];
```

{% endtab %}
{% endtabs %}

### Step 1-b: Create a Code Challenge from Code Verifier

A `code_challenge` is the hashed version of your `code_verifier`. We will send this hash on step 2 when you're requesting an authentication from Cotter.&#x20;

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

```javascript
function sha256(plain) { // returns promise ArrayBuffer
  const encoder = new TextEncoder();
  const data = encoder.encode(plain);
  return window.crypto.subtle.digest('SHA-256', data);
}

function base64urlencode(a) {
      var str = "";
      var bytes = new Uint8Array(a);
      var len = bytes.byteLength;
      for (var i = 0; i < len; i++) {
        str += String.fromCharCode(bytes[i]);
      }
      return btoa(str)
        .replace(/\+/g, "-")
        .replace(/\//g, "_")
        .replace(/=+$/, "");
    }

async function challenge_from_verifier(v) {
  hashed = await sha256(v);
  base64encoded = base64urlencode(hashed);
  return base64encoded;
}

var challenge = await challenge_from_verifier(verifier);
```

{% endtab %}

{% tab title="Python" %}

```python
import hashlib
import base64
challenge_bytes = hashlib.sha256(code_verifier).digest()
code_challenge = base64.urlsafe_b64encode(challenge_bytes).rstrip(b'=')
```

{% endtab %}

{% tab title="Java" %}

```java
// import android.util.Base64;
byte[] codeVerifierBytes = codeVerifier.getBytes("US-ASCII");
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(codeVerifierBytes);
byte[] codeChallengeBytes = md.digest();
String codeChallenge = Base64.encodeToString(codeChallengeBytes, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
```

{% endtab %}

{% tab title="Swift 3" %}

```swift
// Dependency: Apple Common Crypto library
// http://opensource.apple.com//source/CommonCrypto
guard let data = verifier.data(using: .utf8) else { return nil }
var buffer = [UInt8](repeating: 0,  count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
    _ = CC_SHA256($0, CC_LONG(data.count), &buffer)
}
let hash = Data(bytes: buffer)
let challenge = hash.base64EncodedString()
    .replacingOccurrences(of: "+", with: "-")
    .replacingOccurrences(of: "/", with: "\_")
    .replacingOccurrences(of: "=", with: "")
    .trimmingCharacters(in: .whitespaces)
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
// Dependency: Apple Common Crypto library
// http://opensource.apple.com//source/CommonCrypto
u_int8_t buffer[CC_SHA256_DIGEST_LENGTH * sizeof(u_int8_t)];
memset(buffer, 0x0, CC_SHA256_DIGEST_LENGTH);
NSData *data = [verifier dataUsingEncoding:NSUTF8StringEncoding];
CC_SHA256([data bytes], (CC_LONG)[data length], buffer);
NSData *hash = [NSData dataWithBytes:buffer length:CC_SHA256_DIGEST_LENGTH];
NSString *challenge = [[[[hash base64EncodedStringWithOptions:0]
                         stringByReplacingOccurrencesOfString:@"+" withString:@"-"]
                         stringByReplacingOccurrencesOfString:@"/" withString:@"_"]
                         stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]];
```

{% endtab %}
{% endtabs %}

The `code_challenge` is sent first so that later in step 3, Cotter's server can verify that `hash(code_verifier)` is the same as `code_challenge` and that you are indeed made the original request.

#### Checking your code challenge and verifier

To check if your `code_challenge` and `code_verifier` are correctly generated and formatted, try comparing it with codes generated here <https://example-app.com/pkce>

### Step 2: Request Authorization from Cotter

Open Cotter's Auth URL from a WebView from your app.

```http
https://js.cotter.app/app?
    api_key=<api_key_id>
    &redirect_url=yourapp://
    &type=PHONE
    &code_challenge=<code_challenge>
    &state=<state>
```

| Query Parameter  | Type   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| ---------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `api_key`        | string | Your `API_KEY_ID`                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `redirect_url`   | string | <p>Your app's URL scheme where Cotter Auth will redirect back your users to your app</p><p><strong>Example:</strong> <code>com.example.app:redirect\_uri\_path</code> or <code>[http://127.0.0.1:port](https://docs.cotter.app/sdk-reference/api-for-other-mobile-apps/http:/127.0.0.1:port)</code></p>                                                                                                                                          |
| `type`           | string | `EMAIL` or `PHONE`                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `code_challenge` | string | The `code_challenge` you created in [Step 1](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#step-1-b-create-a-code-challenge-from-code-verifier)                                                                                                                                                                                                                                                                                   |
| `state`          | string | A random string that you generate from your application before opening to Cotter's Auth (ex. `abcXYZ456`). This is not the same as your `code_verifier`. You need to check if the `state` included by Cotter in the `redirect_url` is the same as the initial `state` that you set to make sure the request is for you. Learn more about [state](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#what-is-state-and-code-challenge). |

{% hint style="warning" %}
Make sure the scheme of your `redirect_url` (the front part before `://`) doesn't have an underscore or other special characters. To test it out, enter your `redirect_url` here: <https://jsfiddle.net/omd02jn5/>
{% endhint %}

Here's an example on opening the in-app Browser from iOS and Android

* **Android**: Use the [Trusted Web Activity](https://developers.google.com/web/updates/2019/02/using-twa)
* **iOS:** Use the [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/authenticating_a_user_through_a_web_service)

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

```swift
// Using ASWebAuthenticationSession
// https://developer.apple.com/documentation/authenticationservices/authenticating_a_user_through_a_web_service

guard let authURL = URL(string: "https://js.cotter.app/app?api_key=<api_key_id>&redirect_url=yourapp://&type=PHONE&code_challenge=<code_challenge>&state=<state>") else { return }
let scheme = "yourapp://"


self.authSession = ASWebAuthenticationSession(url: authURL, callbackURLScheme: scheme)
{ callbackURL, error in
    // Handle the callback.
}
if #available(iOS 13.0, *) {
    self.authSession?.presentationContextProvider = self
} else {
    // Fallback on earlier versions
}
self.authSession?.start()
```

{% endtab %}

{% tab title="Android" %}

```java
// Dependencies: Trusted Web Activity
// https://developers.google.com/web/updates/2019/02/using-twa

//build.gradle (Module:app)
android {
        ...
    compileOptions {
       sourceCompatibility JavaVersion.VERSION_1_8
       targetCompatibility JavaVersion.VERSION_1_8
    }
}
dependencies {
    implementation 'com.google.androidbrowserhelper:androidbrowserhelper:1.0.0'
}

// MainActivity.java
import com.google.androidbrowserhelper.trusted.TwaLauncher;

public class MainActivity extends AppCompatActivity {
    ...
    static Uri LAUNCH_URI = Uri.parse("https://js.cotter.app/app?api_key=<api_key_id>&redirect_url=yourapp://&type=PHONE&code_challenge=<code_challenge>&state=<state>");
    
    public void login(View view) {
        new TwaLauncher(this).launch(LAUNCH_URI);
    }
}
```

{% endtab %}

{% tab title="CLI" %}

1. Open the user's browser with the URL above, also display it so the user can click it if opening the browser doesn't work.
2. Listen to the localhost port that you specified in the redirect URL
3. When the browser redirect back to you with the response below, handle the request and use the code, state, and challenge\_id to continue to Step 3.
   {% endtab %}
   {% endtabs %}

### Response

After the user's email or phone is verified, Cotter will redirect back to your app using `redirect_url` that you specified in step 2.

```http
yourapp://?
    code=<authorization_code>
    &state=<state>
    &challenge_id=<challenge_id>
```

{% hint style="info" %}
You should check that the `state` is the same as the initial state you passed in to the URL [here](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#step-2-request-authorization-from-cotter).
{% endhint %}

#### Check out how to [Handle the URL Scheme](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps/handling-url-scheme).

###

### Step 3: Request Tokens and Identity

In this step, you'll use your `code_verifier` , `authorization_code` and the `challenge_id` to request `tokens` and the user's email or phone number from Cotter's server.

{% hint style="info" %}
Your `authorization_token` is valid for 5 minutes, and can only be used once.
{% endhint %}

```
curl -XPOST \
-H 'Content-type: application/json' \
-H 'API_KEY_ID: <api_key_id>' \
-d '{
  "code_verifier": "<code_verifier>",
  "authorization_code": "<authorization_code>",
  "challenge_id": <challenge_id>,
  "redirect_url": "<redirect_url>"
}' 'https://www.cotter.app/api/v0/verify/get_identity?oauth_token=true'
```

## Request Tokens

<mark style="color:green;">`POST`</mark> `https://www.cotter.app/api/v0/verify/get_identity`

Request for tokens and user's email or phone number verification state (successfully verified or not).&#x20;

#### Query Parameters

| Name         | Type    | Description                                                                      |
| ------------ | ------- | -------------------------------------------------------------------------------- |
| oauth\_token | boolean | If `true`, will return OAuth Tokens (read "Handling Authentication with Cotter") |

#### Headers

| Name         | Type   | Description        |
| ------------ | ------ | ------------------ |
| API\_KEY\_ID | string | Your `API_KEY_ID`  |
| Content-Type | string | `application/json` |

#### Request Body

| Name                | Type    | Description                                                |
| ------------------- | ------- | ---------------------------------------------------------- |
| code\_verifier      | string  | Your `code_verifier` created in Step 1                     |
| authorization\_code | string  | The `authorization_code` received in Step 2                |
| challenge\_id       | integer | The `challenge_id` received in Step 2                      |
| redirect\_url       | string  | This MUST match the `redirect_url` you specified in Step 2 |

{% tabs %}
{% tab title="200 You'll receive the full information about the user from the object identifier and a token which contains a signature that you need to verify." %}

```
{
  "identifier": {
    "ID": "f4286df9-a923-429c-bc33-5089ffed5f68",
    "created_at": "2020-07-21T22:53:21.211367Z",
    "updated_at": "2020-07-21T22:53:21.211367Z",
    "deleted_at": "0001-01-01T00:00:00Z",
    "identifier": "putri@cotter.app", // User's email
    "identifier_type": "EMAIL",
    "device_type": "BROWSER",
    "device_name": "Mozilla/5.0 (Linux; Android 9; Android SDK built for x86 Build/PSR1.180720.075) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
    "expiry": "2020-08-20T22:53:21.19705Z",
    "timestamp": "2020-07-21T22:53:21.19705Z"
  },
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUz...", // Validate this access token
    "id_token": "eyJhbGciOiJFUzI1...",
    "refresh_token": "27944:lb31DY5pG229n...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "OTP"
  },
  "token": {...},
  "user": {
    "ID": "643a42c7-316a-4abe-b27e-f4d0f903bfea", // Cotter uesr ID
    "identifier": "putri@cotter.app",
    ...
  }
}
```

{% endtab %}

{% tab title="500 Challenge Expired means that either the authorization\_token is expired (you have 5 minutes to use the token), or you already used the token once." %}

```
{
  "msg": "Challenge Expired"
}
```

{% endtab %}
{% endtabs %}

### Step 4: Include the Token to your Server

Now that the email or phone number is verified, you can continue your Sign Up or Login process by submitting the email or phone number to your server, either now or after the user enters more information.

{% hint style="success" %}
You should include this `oauth_tokens` into your call to your backend for **Login** or **Registration**. Your backend should then verify that the access token is valid.
{% endhint %}

## Validating Cotter's Access Token&#x20;

Check out how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

## 🎉 You're done!

## Securing your Project

Since you'll be using your API Key from a front-end website or mobile app, your `API_KEY_ID` is exposed to anyone inspecting your code. Here are some ways to prevent abuse:

* [Only allow your website/app to use your API Key](/protecting-your-account/only-allow-your-website-app-to-use-your-api-key)
* [Rate Limit the number of authentication requests](/protecting-your-account/rate-limit)
* [Enable reCAPTCHA to prevent automated abuse](/protecting-your-account/enable-recaptcha-to-protect-against-automated-abuse)

## What are State and Code Challenge?

#### State:

Your app generates `state=XYZ` in the beginning of the auth flow. You should expect that Cotter's response on [Step 2](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#response) when Cotter redirect back to your `redirect_url`, the state is the same (`state == XYZ`). This makes sure that the redirect was in response to your initial authentication request.

#### code\_challenge / verifier:&#x20;

This is needed for installed apps / SPA because they cannot store the Api Secret Key securely, so the `code_challenge` and `code_verifier` is for Cotter to make sure that the original App that requested authentication on [Step 2](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#step-2-request-authorization-from-cotter) is the same as the one that asked for access token on [Step 3](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#step-3-request-tokens-and-identity).


# Handling URL Scheme

To receive incoming deep-link to your RedirectURL from the trusted webview, you need to add some setup your project.

{% tabs %}
{% tab title="Android" %}
You can check this [complete guide on deep-links in Android](https://developer.android.com/training/app-links/deep-linking).

### Add Intent Filter to Receive Incoming Link

Here's an example on receiving an incoming link of `cotterexample://login`

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

```markup
<activity android:name="com.example.cotterexample.Login">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Accepts URIs that begin with "cotterexample://login" -->
        <data android:scheme="cotterexample"
            android:host="login" />
    </intent-filter>
</activity>
```

{% endcode %}

### Read data from incoming intents <a href="#handling-intents" id="handling-intents"></a>

You should generally do this during [`onCreate()`](https://developer.android.com/reference/android/app/Activity.html#onCreate\(android.os.Bundle\)) or [`onStart()`](https://developer.android.com/reference/android/app/Activity.html#onStart\(\))

```java
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Read data from incoming intents 
    Intent intent = getIntent();
    if (Intent.ACTION_VIEW.equals(intent.getAction())) {
        Uri uri = intent.getData();
        // Use this data to perform your http request to get Cotter token 
        String authCode = uri.getQueryParameter(AUTH_CODE);
        String state = uri.getQueryParameter(STATE);
        String challengeID = uri.getQueryParameter(CHALLENGE_ID);
    }
}
```

{% endtab %}

{% tab title="Second Tab" %}

{% endtab %}
{% endtabs %}


# Backend: Handling Response

### Register or Login your user to your Database

When the user is authenticated, you will receive a response similar to this from your front end. **Your frontend is reponsible for sending this payload to your server.**

```javascript
{
    "email": "myemail@gmail.com", // User's email (or phone number)
    "oauth_token": {
        "access_token": "eyJhbGciOiJFUzI1NiIsImt...", // Access Token to validate
        "id_token": "eyJhbGciOiJFUzI1Ni...",
        "refresh_token": "27805:CNf76faa8trMhjXM...",
        "expires_in": 3600,
        "token_type": "Bearer",
        "auth_method": "OTP"
    },
    "user": {
        "ID": "abcdefgh-abcd-abcd-abcd-af6f81fb5432", // Cotter User ID
        "created_at": "2020-07-21T05:50:14.182738Z",
        "updated_at": "2020-07-21T06:00:47.115096Z",
        "deleted_at": "0001-01-01T00:00:00Z",
        "issuer": "<YOUR_API_KEY_ID>",
        "identifier": "putrikarunian@gmail.com"
    }
}
```

Send this payload to your backend to register or login the user in your database. A typical flow would look like this:

1. Validate the access token
2. Check if the email exists in your database
   * If it doesn't exists: Create a new user
   * If it exists: Continue login
3. (Optional) If you want to use your own session tokens, set the cookie here after validating the access token.
4. (Optional) if you want to use Cotter's tokens, either store Cotter's access token in the cookie or on the front-end side.

**Examples:**

{% tabs %}
{% tab title="Node.js (Express)" %}

```javascript
const express = require("express");
const app = express();
var cors = require("cors");
var bodyParser = require("body-parser");
var cotterNode = require("cotter-node");
var cotterToken = require("cotter-token-js");
var session = require("express-session");
const port = 3005;
app.use(cors());
app.use(bodyParser.json());


// EXAMPLE LOGIN ENDPOINT
app.post("/login", async (req, res) => {
  console.log(req.body);

  // Validate access token
  const access_token = req.body.oauth_token.access_token;
  var valid = false;
  try {
    valid = await cotterNode.CotterValidateJWT(access_token);
  } catch (e) {
    valid = false;
  }
  if (!valid) {
    res.status(403).end("Invalid access token");
    return;
  }

  // (Optional) Read access token
  let decoded = new cotterToken.CotterAccessToken(access_token);
  console.log(decoded);
    
  // (Optional) Register or Login User

  // (Optional) Set access token as cookie

  res.status(200).json(decoded.payload).end();
});

app.listen(port, () =>
  console.log(`Example app listening at http://localhost:${port}`)
);

```

{% endtab %}

{% tab title="Python (Flask)" %}

```python
from flask import Flask
from flask import request
from flask_cors import CORS
import requests
from jose import jwt

CotterJWKSURL="https://www.cotter.app/api/v0/token/jwks"

app = Flask(__name__)
CORS(app)


@app.route('/login', methods=['POST'])
def login(name=None):
    req = request.get_json();

    # Getting jwt key
    r = requests.get(url = CotterJWKSURL);
    data = r.json();
    print(data);
    public_key = data["keys"][0];

    # Getting access token and validate it
    token = req["oauth_token"]["access_token"]
    resp = jwt.decode(token, public_key, algorithms='ES256', audience=API_KEY_ID)
    
    # User Authenticated!
    # 1) If user doesn't exist, register user to db. Otherwise, continue
    # 2) Either use Cotter's Access Token for your entire API authorization
    #    OR
    #    You can Generate your JWT Tokens or other session management here
    
    return resp;
```

{% endtab %}
{% endtabs %}

## Validating Cotter's Access Token&#x20;

Read more on how to verify the OAuth Tokens from Cotter here:

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}


# Only Allow Your Website/App to Use Your API Key

Your `API_KEY_ID` is most likely used in your front-end code, which may be exposed. To only allow your website or app to use your API key, you can set the **Allowed Origins** under **Settings** tab in the [Dashboard](https://dev.cotter.app/).

![Set Allowed Origins and Redirect URL in Settings > Allowed URLs](/files/-MA_fRiiSTFbE0LBCfAP)

#### For Websites

This will do 2 things:

* Cotter's JS SDK will only load if the **parent** is listed.
* If you pass in `RedirectURL` parameter, the JS SDK will only allow it if it's listed.

#### For Mobile Apps

You will always need to specify a RedirectURL when using Cotter's Email or Phone Number verification. The RedirectURL is the deep-link to your app that is used by the in-app browser to redirect back to your app. **You need to add this redirect URL to the list.**&#x20;

**Specifically, you will need these 4 origins in your list:**

* `://`
* `https://js.cotter.app`
* `myurlscheme://`
* `myurlscheme://myhostname`

Replace `myurlscheme` and `myhostname` with your own scheme and hostname that you used as the RedirectURL.

### How to Specify the Origins

{% hint style="danger" %}

* Make sure you list the **origin, not the full URL.**
* **Do not include a trailing `/`**.&#x20;
  * ✅ `https://mywebsite.com`&#x20;
  * ❌ `https://mywebsite.com/`
    {% endhint %}

Make sure you [follow the syntax below](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin):

```
<scheme> "://" <hostname> [ ":" <port> ]
```

#### Example for Websites:

* `http://localhost:3000`
* `https://dev.cotter.app`
* `myexample://auth_callback` (for mobile app, please refer to the example above).

{% hint style="info" %}
You can use `http://localhost:3000` for development. Remember to remove it when moving to production.
{% endhint %}

To allow **any parent and any RedirectURL:** specify `*` in the input.


# Rate Limit

### Rate Limit for SMS and WhatsApp messages

We set a default rate limit for requests that requires a message to be sent via SMS or WhatsApp. The default limitation can be seen in the image below, and can be updated in the Dashboard under [Rules](https://dev.cotter.app/rules).

![Default Rate Limit Values in the Dashboard](/files/-M59sYb03SzGvg_ePLNM)

### Understanding the Rate Limit rules

The above rate limit rule means that within the last 5 minutes:

* there can be at most 10 requests with the same IP address as the current request
* there can be at most 5 requests with the same phone number as the current request
* there can be at most 5 requests with the same Session ID as the current request

{% hint style="info" %}
The Session ID is set in the user's `localStorage` in their browser. If the user clears their `localStorage` or go to a different browser, the Session ID will be different.
{% endhint %}

### Example

**Identifier Max: 5 requests -** Let's say you have made a request with phone number `+12345678910` 5 times within the last 5 minutes. The sixth time you make a request with phone number `+12345678910` within that 5 minutes, it will be blocked. However, once 5 minutes has passed since your first request, you can make a request with phone number `+12345678910`again.

**Session ID Max: 5 requests -** If you make 3 requests with phone number `+11111111111` and 2 requests with phone number `+3333333333`, and then make a request with phone number `+55555555555`within 5 minutes, all in the same browser, then the last request will be blocked. This is because your Session ID is the same for all 6 requests.


# Enable reCAPTCHA to Protect Against Automated Abuse

We have decided to enable Google's reCAPTCHA to protects you against spam and other types of automated abuse from making verification requests, especially using SMS or WhatsApp as it will cause you to lose money from sending these messages.

## Enabling Google reCAPTCHA

reCAPTCHA is now available for the [JavaScript SDK](/sdk-reference/web/web-sdk-verify-email-phone). We are using [invisible Google reCAPTCHA v2](https://developers.google.com/recaptcha/docs/invisible). This means that the reCAPTCHA prompt will be automatically triggered when the user press "Login", and if Google has suspicions about this request, it may prompt the user to solve some puzzle. Here's how it looks:

![reCAPTCHA using Cotter's JS SDK](/files/-M6IDwN56_baF7yJq50j)

### Step 1: To show the reCAPTCHA in your website, enable it in the dashboard:

Go to Settings > Branding > Magic Link Form or OTP Verification Form > Phone Number

![Captcha settings can only be enabled for Phone Number verification](/files/-MBMP8YU5UDdl-eQeF0M)

#### Adjust your container `div` to fit the reCAPTCHA prompt:

**Option 1:** Set the container `div` to at least `530px` high, `300px` wide.

```markup
<div
    id="cotter-container-signup"
    style="width: 300px; height: 530px"
></div>
```

**Option 2:** Allow scrolling.

```markup
<div
    id="cotter-container-signup"
    style="overflow: scroll; width: 300px; height: 300px;"
></div>
```

### Step 2: To Require valid reCAPTCHA, update Rules in your dashboard

Under **Rules**, turn on Captcha Required:

![Require Captcha for Email/Phone Number verification](/files/-M6IEy5tQkGMcrbRSvZh)

Note that **any verification request for email/phone number will require a valid reCAPTCHA token.**

## See reCAPTCHA in action

To test how reCAPTCHA looks like:

#### Step 1: Inspect element and click Toggle Device Toolbar

![Inspect Element > Toggle Device Toolbar](/files/-M6IHdRF7mWZPM-QZZI_)

#### Step 2: Click the dropdown to select a device and click Edit&#x20;

![Select from the Dropdown and click Edit](/files/-M6II5Mn8j1-UMVBrAvh)

#### Step 3: Add a custom device and name it Googlebot/2.1

![Add a custom device called Googlebot/2.1](/files/-M6IIXq2uNyxDEd1jvfu)

From now on, if you're using this emulated device, you will be prompted to solve the reCAPTCHA.


# Cotter's OAuth 2.0 Tokens Specification

Allowing Cotter handling your full authentication is the fastest way to get your whole authentication suite setup. Cotter can generate an access token, id token, and refresh tokens for you.

## Handling Authentication

After successful authentication, Cotter returns ID, access, and refresh tokens as defined by the OpenID Connect (OIDC) open standard:

* The [ID Token](http://openid.net/specs/openid-connect-core-1_0.html#IDToken) contains information about the authenticated user such as email , phone number , `client_user_id` (the user's id in your server), and `auth_time` (last authenticated time).
* The [Access Token](https://tools.ietf.org/html/rfc6749#section-1.4) contains scopes and the authentication method used to authenticate the user: `OTP`, `TRUSTED DEVICE` , `PIN` or `BIOMETRIC` .
* The [Refresh Token](https://tools.ietf.org/html/rfc6749#section-1.5) contains the information necessary to obtain a new ID and access token.

{% hint style="warning" %}
🔒It is important to **secure all tokens** in transit and storage.
{% endhint %}

## Client Libraries

Currently, we have the following libraries available to handle Cotter's OAuth Tokens:

* Cotter Token JS for Decoding JWT Tokens: [`cotter-token-js`](https://github.com/cotterapp/cotter-token-js)&#x20;
* Cotter Node for validating JWT Tokens in your server: [`cotter-node`](https://github.com/cotterapp/cotter-node-js)&#x20;

## Cotter's Access Token

Cotter's Access token is a [JSON Web Tokens (JWTs)](https://jwt.io/) that is used to tell your backend API that the user has been authorized to call the API and perform some action (defined in the `scopes` attribute of the token).&#x20;

Access tokens expires every 1 hour. [Check the guide on renewing access and id tokens](/getting-access-token/renewing-expired-tokens).

Token example:

{% tabs %}
{% tab title="Access Token" %}

```javascript
eyJhbGciOiJFUzI1NiIsImtpZCI6IlNQQUNFX0pXVF9QVU...
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Example decoded token" %}

```javascript
{      
  "sub": "09efb1b-e50f-41fd-8530-88ffbcd80f59",                  // [Deprecated] Cotter's User id
  "client_user_id": "09efb1b-e50f-41fd-8530-88ffbcd80f59",       // deprecated
  "authentication_method": "TRUSTED_DEVICE",                     // How the user is authenticated
  "type": "access_token", 
  "identifier": "putri@cotter.app",                   // User's email or phone number
  "scope": "access",                                  // Scope of this access token (coming soon)
  "aud": "<your API KEY ID>",                         // Your API_KEY_ID
  "exp": 1586231136,                                  // Expires at
  "iat": 1586227536,                                  // Issued at
  "jti": "47fba8ba-abcd-efgh-a22c-a2b8b72e8b98",
  "iss": "https://www.cotter.app"            
}
```

{% endtab %}
{% endtabs %}

### How to use the Access Token to allow API calls

1\. On every API call to your server, attach the access token as a header

```
Authorization: Bearer <access token>
```

2\. In your server, using a middleware, check if the access token is valid

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

3\. If the access token is valid, allow the API call to proceed.

## Cotter's ID Token

Cotter's ID token is a [JSON Web Tokens (JWTs)](https://jwt.io/) that is used to provide information about the user. ID tokens expires every 1 hour. [Check the guide on renewing access and id tokens](/getting-access-token/renewing-expired-tokens).

{% tabs %}
{% tab title="Example Decoded Token" %}

```javascript
{
  "sub": "43e53999-c31e-4ed9-a196-71031a05f297",              // [Deprecated] Cotter User ID
  "client_user_id": "43e53999-c31e-4ed9-a196-71031a05f297",   // Deprecated
  "auth_time": "1591756112",                                  // Last authenticated time (Unix timestamp)
  "identifier": "putri@cotter.app",
  "type": "id_token",
  "aud": "<your API KEY ID>",       // your API KEY ID
  "exp": 1586232181,                // Expires at
  "iat": 1586228581,                // Issued at
  "jti": "886773db-abcd-efgh-aefc-6ab481b24099",
  "iss": "https://www.cotter.app"
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
You should not trust this information after it's expired
{% endhint %}

## Cotter's Refresh Token

Cotter's Refresh Token is an opaque token (i.e. a random string) that is used to generate a new `access token` and `id token` when they're expired. Refresh tokens expires every 30 days. You need to re-authenticate the user to get a new refresh token. [Check the guide on renewing access and id tokens](/getting-access-token/renewing-expired-tokens).

```
14:Pmw3r3rgaw0rLupUDU4hjwJFisv8EaHRaoy5gw54ZmSebaWHDh
```

## 🥳 Getting Started

### 1. Getting The Tokens using the SDK

Easily get tokens using the SDK by adding a simple paramater, ex. `getOAuthToken = true`

{% content-ref url="/pages/-M4HcAeSw6KbuSrnge2U" %}
[Getting the Tokens](/getting-access-token/getting-the-tokens)
{% endcontent-ref %}

### 2. Storing & Removing the Token

The SDK generally automatically store tokens securely for you, and provides a function to easily remove the tokens to logout your users.

{% content-ref url="/pages/-M4I5Ed9v0BbC7OxzSFn" %}
[Storing and Removing Tokens](/getting-access-token/storing-and-removing-tokens)
{% endcontent-ref %}

### 3. Renewing Expired Tokens

The SDK generally automatically renews expired `access_token` and `id_token` whenever a valid `refresh_token` exists.

{% content-ref url="/pages/-M4I0B0i0yghkR8yjphX" %}
[Renewing Expired Tokens](/getting-access-token/renewing-expired-tokens)
{% endcontent-ref %}

### 4. Verifying the Tokens

You need to verify the JWT tokens in your backend server. Fortunately, there are a lot of good libraries that do this.

{% content-ref url="/pages/-M4HZPVawEx2QN3qLmnv" %}
[Verifying JWT Tokens](/getting-access-token/verifying-jwt-tokens)
{% endcontent-ref %}

### 5. Adding Custom Claims to your JWT Token

If you have additional metadata from your backend server that you'd like to add to the JWT token (for example, the user's `role` or `name`, you can call Cotter's API to add the claims to Cotter's JWT token&#x20;

{% content-ref url="/pages/-MGMZEOXjRRPxy1zAe-9" %}
[Requesting Custom Claims on your Access Token](/api-reference/oauth-tokens-api/requesting-custom-claims-on-your-access-token)
{% endcontent-ref %}

## 🎉 You're done!


# Getting the Tokens

Getting an access token from Cotter's SDK is as easy as setting a variable to \`true\`.

Getting Cotter's OAuth Tokens is very easy using our current authentication methods. **You should automatically receive the `oauth_token` whenever a user is succesfully authenticated**, either using magic link, OTP, trusted device, or WebAuthn.

You will get an `oauth_token` object in the JSON response with all the tokens:

```javascript
{
  ...
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUzI1sInR5cC...",
    "auth_method": "TRUSTED_DEVICE", // authentication method used
    "expires_in": 3600,              // expiry in seconds
    "id_token": "eyJhbGciOiJFUz...",
    "refresh_token": "60:79hbLxl3aTjWWgCcIRnn...",
    "token_type": "Bearer"
  }
}
```

## Getting Tokens using the SDK

There are 3 places in the existing SDK where you can receive access tokens:

{% content-ref url="/pages/-M4HjKz1atn3w5W-syVN" %}
[During Email/Phone Verification](/getting-access-token/older-api/getting-the-tokens/during-authentication/during-email-phone-verification)
{% endcontent-ref %}

{% content-ref url="/pages/-M4I2UtJdxzuT\_JMHsKv" %}
[During Authentication](/getting-access-token/older-api/getting-the-tokens/during-authentication)
{% endcontent-ref %}

{% content-ref url="/pages/-M4IuiqhSaH4ha2Ye8zu" %}
[During enrolling Trusted Devices](/getting-access-token/older-api/getting-the-tokens/during-enrolling-trusted-devices)
{% endcontent-ref %}

## Getting Tokens using HTTP Request

You can also call Cotter's API to get the tokens.

{% content-ref url="/pages/-M4JGrVHEcLJ12a9rzYj" %}
[Using the Refresh Token](/getting-access-token/getting-the-tokens/using-http-requests)
{% endcontent-ref %}


# Get Tokens during Authentication

When a user logs in to your application using the **Sign in with Email/Phone** or the **Sign in with Device** method, Cotter will return OAuth tokens in the form of JWT Tokens.

* For **Sign in with Email/Phone:** The `authentication_method` = `OTP`
* For **Sign in with Device:** The `authentication_method` = `TRUSTED_DEVICE`

{% tabs %}
{% tab title="JS/HTML" %}
You will receive the access token when using these features:

{% content-ref url="/pages/-M0ROJn7tosL9qeumqFd" %}
[Sign In with Email/Phone Number](/sdk-reference/web/web-sdk-verify-email-phone)
{% endcontent-ref %}

{% content-ref url="/pages/-MCk3X5Dh4Fkxp20D4Rt" %}
[Sign In with WebAuthn](/sdk-reference/web/sign-in-with-webauthn)
{% endcontent-ref %}

{% hint style="success" %}
The JS SDK **automatically store your tokens securely**
{% endhint %}
{% endtab %}

{% tab title="React Native" %}
You will get an access token when using the following features:

{% content-ref url="/pages/-M2kTuM6W90CT9IpIxHq" %}
[Sign In with Device](/sdk-reference/react-native/react-native-sdk-passwordless-login)
{% endcontent-ref %}

{% content-ref url="/pages/-M1dk\_Rosjlkj52Hq\_6R" %}
[Sign In with Email/Phone Number](/sdk-reference/react-native/react-native-sdk-verify-email-phone)
{% endcontent-ref %}

{% hint style="success" %}
The React Native SDK **automatically** **store your tokens securely**
{% endhint %}
{% endtab %}

{% tab title="Flutter" %}
You will get an access token when using the following features:

{% content-ref url="/pages/-M9angupK73fWUZjM7fe" %}
[Sign In with Device](/sdk-reference/flutter/sign-in-with-device)
{% endcontent-ref %}

{% content-ref url="/pages/-MATH96wMixRR82bWQJJ" %}
[Sign in with Email/Phone Number](/sdk-reference/flutter/sign-in-with-email-phone-number)
{% endcontent-ref %}

{% hint style="success" %}
The Flutter SDK **automatically** **store your tokens securely**
{% endhint %}
{% endtab %}

{% tab title="Android" %}
You will get an access token when using the following features:

{% content-ref url="/pages/-MCxyo3ix-jqWDQysF0g" %}
[Sign In with Device](/sdk-reference/android/sign-in-with-device)
{% endcontent-ref %}

{% content-ref url="/pages/-M0et2D8qa-mVoDh-tjp" %}
[Sign In with Email/Phone Number](/sdk-reference/android/android-sdk-1)
{% endcontent-ref %}
{% endtab %}

{% tab title="API For Mobile Apps" %}
Using the [API for Mobile Apps](/sdk-reference/api-for-other-mobile-apps), you would follow this guide to [get the identity of the user.](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#step-3-request-tokens-and-identity) When the user's email or phone number is successfully verified, [you'll receive information about the user and a signature from Cotter.](/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps#response)

To also receive OAuth Tokens, add a query parameter `oauth_token=true` in the http request:

```
https://www.cotter.app/api/v0/verify/get_identity?oauth_token=true
```

The full request would be:

```
curl -XPOST \
-H 'Content-type: application/json' \
-H 'API_KEY_ID: <api_key_id>' \
-d '{
  "code_verifier": "<code_verifier>",
  "authorization_code": "<authorization_code>",
  "challenge_id": <challenge_id>,
  "redirect_url": "<redirect_url>"
}' 'https://www.cotter.app/api/v0/verify/get_identity?oauth_token=true'
```

You'll get the following response:

{% code title="JSON Response" %}

```javascript
{
  "identifier": {
    "ID": "2ddc26f6-f392-4d7e-8607-1f57d41da045",
    "created_at": "2020-04-05T04:50:55.931771Z",
    "deleted_at": null,
    "device_name": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Mobile/15E148 Safari/604.1",
    "device_type": "BROWSER",
    "expiry": "2020-05-07T03:34:58.729745Z",
    "identifier": "hello@gmail.com",
    "identifier_type": "EMAIL",
    "public_key": "FvozWVGHo9lWE5ilLOF...",
    "timestamp": "2020-04-07T03:34:58.729745Z",
    "update_at": "2020-04-07T03:34:58.733779Z"
  },
  "token": { // You can ignore this if you're using the oauth_token 
    "expire_at": "1588822498",
    "identifier": "hello@gmail.com",
    "identifier_id": "2ddc26f6-f392-4d7e-8607-1f57d41da045",
    "identifier_type": "EMAIL",
    "receiver": "<your API KEY ID>",
    "signature": "XIbztHLKQSqzbnuBgyC+GfAK...",
    "timestamp": "1586230498"
  },
  "oauth_token": {  // 👈 NEW OAuth Tokens 👈
    "access_token": "eyJhbGciOiJFUz...",
    "auth_method": "OTP",
    "expires_in": 3600,
    "id_token": "eyJhbGciOiJFUz...",
    "refresh_token": "94:qv2SAJN5u2u...",
    "token_type": "Bearer"
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="Other SDKs (coming soon)" %}
We'll add support for the other SDKs soon 😉. Stay tuned!
{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Tokens must be stored securely within your application.** Use [Android Keystore](https://developer.android.com/training/articles/keystore) for Android and [iOS KeyChain](https://developer.apple.com/documentation/security/keychain_services) for iOS apps.
{% endhint %}

## Getting and Removing tokens from the Storage

You need to pass the `access_token` to your backend server on every API calls. You also need to remove the tokens from storage to log out your users. Check out how to do that here:

{% content-ref url="/pages/-M4I5Ed9v0BbC7OxzSFn" %}
[Storing and Removing Tokens](/getting-access-token/storing-and-removing-tokens)
{% endcontent-ref %}

## Renewing Expired Tokens

Access tokens and ID tokens expires in 1 hour. When they're expired, you need to use the `refresh_token` to get new tokens. Check out how to renew expired tokens:

{% content-ref url="/pages/-M4I0B0i0yghkR8yjphX" %}
[Renewing Expired Tokens](/getting-access-token/renewing-expired-tokens)
{% endcontent-ref %}


# Using the Refresh Token

Other than the current flow in the SDK, you can also request for OAuth Tokens using http requests directly to Cotter's server.&#x20;

## Getting Tokens using Refresh Token

This is used to renew expired tokens:

{% content-ref url="/pages/-M4I0B0i0yghkR8yjphX" %}
[Renewing Expired Tokens](/getting-access-token/renewing-expired-tokens)
{% endcontent-ref %}


# Storing and Removing Tokens

OAuth Tokens **should be stored securely in the client**.&#x20;

* **Mobile apps:** Use [Android Keystore](https://developer.android.com/training/articles/keystore) for Android and [iOS KeyChain](https://developer.apple.com/documentation/security/keychain_services) for iOS apps.&#x20;
* **Websites:** Store the `access_token` in memory, the `refresh_token` is automatically included in an `httpOnly` and `Secure` cookie with domain `cotter.app` that is automatically included when requesting to refresh token. [Learn more on how to store JWT tokens securely.](https://dev.to/cotter/localstorage-vs-cookies-all-you-need-to-know-about-storing-jwt-tokens-securely-in-the-front-end-15id)

{% hint style="success" %}
Cotter's SDK generally handles token storage in your app.
{% endhint %}

### SDK Support

Some of our SDKs handles storing the tokens for you:

* **JavaScript** (from npm): [Getting the Logged-in User and OAuth tokens](/sdk-reference/web/getting-access-token-and-logged-in-user-info)
* **React Native**: [Getting the Logged-in User and OAuth tokens](/sdk-reference/react-native/getting-stored-oauth-tokens-and-user-information)
* **Flutter**: [Getting the Logged-in User](/sdk-reference/flutter/getting-the-logged-in-user) and [OAuth tokens](/sdk-reference/flutter/getting-oauth-tokens)


# Renewing Expired Tokens

Access Tokens and ID Tokens are valid for 1 hour. To generate new tokens, you need to use the Refresh Token.

## Renewing Tokens using the SDK

{% hint style="success" %}
Cotter's SDK generally handles this for you. Check the availability below.
{% endhint %}

{% tabs %}
{% tab title="React Native" %}
✅ Cotter's JavaScript SDK (from npm) **automatically renews your `access_token` and `id_token`** whenever there is a valid `refresh_token` in storage.&#x20;

This renewal is called when you call Cotter's Get Access Token function.

```javascript
cotter.tokenHandler.getAccessToken();
```

{% endtab %}

{% tab title="JavaScript (from npm)" %}
✅ Cotter's JavaScript SDK (from npm) **automatically renews your `access_token` and `id_token`** whenever there is a valid `refresh_token` in storage.

This renewal is called when you call [Cotter's Get Access Token function](/sdk-reference/web/getting-access-token-and-logged-in-user-info).

```javascript
cotter.tokenHandler.getAccessToken();
```

{% endtab %}

{% tab title="Flutter" %}
✅ Cotter's Flutter SDK **automatically renews your `access_token` and `id_token`** whenever there is a valid `refresh_token` in storage.

This renewal is called when you call [Cotter's Get Access Token function](/sdk-reference/flutter/getting-oauth-tokens).

```dart
var accessToken = await cotter.getAccessToken();
```

{% endtab %}

{% tab title="Other SDKs (coming soon)" %}
We'll add support for Android and iOS soon 😉. Stay tuned!
{% endtab %}
{% endtabs %}

## Renewing Tokens using Refresh Token

If Cotter's SDK doesn't support auto renewal, or if you you need to renew the tokens manually, you can make an HTTP request to Cotter's Server to renew the tokens using a `refresh_token`.

```javascript
curl -XPOST \
-H 'API_KEY_ID: <YOUR API KEY ID>' \
-H "Content-type: application/json" \
-d '{
  "grant_type": "refresh_token",
  "refresh_token": "<REFRESH_TOKEN>"
}' 'https://www.cotter.app/api/v0/token/<YOUR API KEY ID>'
```

## Get Token using Refresh Token

<mark style="color:green;">`POST`</mark> `https://www.cotter.app/api/v0/token/<YOUR API KEY ID>`

Getting new `access_token` and `id_token` using `refresh_token`

#### Path Parameters

| Name         | Type   | Description      |
| ------------ | ------ | ---------------- |
| API\_KEY\_ID | string | You `API_KEY_ID` |

#### Headers

| Name         | Type   | Description       |
| ------------ | ------ | ----------------- |
| Content-type | string | application/json  |
| API\_KEY\_ID | string | Your `API_KEY_ID` |

#### Request Body

| Name           | Type   | Description                   |
| -------------- | ------ | ----------------------------- |
| grant\_type    | string | Grant type is `refresh_token` |
| refresh\_token | string | Your `refresh_token`          |

{% tabs %}
{% tab title="200 Returns a new access\_token and id\_token. Does not return a new refresh\_token" %}

```javascript
{
  "access_token": "eyJhbGciOiJFU...",
  "auth_method": "OTP",
  "expires_in": 3600, // expiry in seconds
  "id_token": "eyJhbGciOiJFUzI1N...",
  "refresh_token": "235:s3kjlwkg035...",  // NEW refresh token
  "token_type": "Bearer"
}
```

{% endtab %}
{% endtabs %}

### Refresh Token Rotation

{% hint style="warning" %}
Note that **this does invalidates the old `refresh_token` and return a new `refresh_token`**
{% endhint %}

## **Renewing Expired Refresh Token**

When your `refresh_token` is expired, you have to re-authenticate your user. If you're using Trusted Devices and the current device is a trusted device, you can simply request authentication **silently, in the background by Signing in with Device.**


# Verifying JWT Tokens

## When to verify JWT Tokens?

In every API call to your backend server, you should include the `access_token` in the header of your requests. You need to **verify the `access_token` on each endpoint that you deem necessary**. Usually, you would **use a middleware so it automatically handles the verification** for each of your routes.

Some good JWT middleware libraries that you can use:

* Golang: <https://echo.labstack.com/cookbook/jwt>
* Node.js: <http://www.passportjs.org/>

### Don't have a backend server? Use the API.

{% content-ref url="/pages/-MIH\_Ye0dlvNM3xeG-xJ" %}
[Verify JWT Token using API (serverless)](/api-reference/oauth-tokens-api/verify-jwt-token-using-api-serverless)
{% endcontent-ref %}

## How to Verify the Access Token

The access token is a **JWT Token**, and it's signed using **Asymmetric Signing Algorithm ES256.** This means, unlike symmetric JWT tokens that are signed and verified using the same secret key, this asymmetric JWT Token is signed using a secret key that only Cotter knows, **but can be verified using a public key that you can find** [**here**](https://www.cotter.app/api/v0/token/jwks)**.**

* [ ] Make sure that the token is **not expired**
* [ ] Make sure that the `aud` matches your `API_KEY_ID`
* [ ] Check the `authentication_method` and `scopes` to match your API requirements (scopes are defaulted to `access` for now, and cannot be changed).
* [ ] Check that the JWT is well-formed.
* [ ] Check the signature.
* [ ] Check that the issuer `iss` = `https://www.cotter.app`

#### Checking other attributes of the user

If you want to check the user's email or phone number before allowing access, for example, you want to allow only emails with a specific domain to log in, you should do the check here.

## Third-Party JWT Libraries to Verify the Tokens

You can use third party libraries to verify JWT tokens. [Check the list of third party libraries here](https://jwt.io). Make sure you check for the algorithm that the JWT token uses.

#### For Cotter's JWT Tokens, use:

* Algorithm: `ES256`&#x20;
* Public Keys: <https://www.cotter.app/api/v0/token/jwks>&#x20;
  * take the key with `kid` = `SPACE_JWT_PUBLIC:8028AAA3-EC2D-4BAA-BE7A-7C8359CCB9F9`
  * Make sure you take the keys from this endpoint, and cache when necessary, but **don't hard-code it. The key may change.**

## Examples

Access tokens are usually included in `oauth_token.access_token` in the responses from the SDK, or in the Authorization Header.

{% tabs %}
{% tab title="Node.js" %}

```javascript
// Install Dependency
yarn add cotter-node

// Validate token
var cotterNode = require("cotter-node");
var cotterToken = require("cotter-token-js");

// Validate access token
const access_token = oauth_token.access_token;
try {
  var valid = await cotterNode.CotterValidateJWT(access_token);
} catch (e) {
  // Not Valid
  console.log(e)
}

// Read access token
let decoded = new cotterToken.CotterAccessToken(access_token);
console.log(decoded);

// Check that `aud` is your API KEY
const audience = decoded.getAudience();
if (audience !== YOUR_API_KEY_ID) {
  throw "Audience doesn't match"
}

// (Optional) Checking other attributes of the user
// Example, only allow my company domain to login
const email = decoded.getIdentifier();
if (email.split("@")[1] !== "cotter.app") {
  throw "Please use cotter business email instead of a personal email";
}
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
# Using https://github.com/nov/json-jwt

require 'net/http'
require 'json/jwt'


jwks_raw = Net::HTTP.get URI("https://www.cotter.app/api/v0/token/jwks")
jwk_set = JSON::JWK::Set.new(
  JSON.parse(
    jwks_raw
  )
)

access_token_string = 'eyJhbGciOiJFUzI1NiIsImtpZCI6IlNQQUNFX0pXVF9QVUJM...'
decoded_token = JSON::JWT.decode access_token_string, jwk_set

expected_aud = '<YOUR_API_KEY_ID>'
expected_iss = 'https://www.cotter.app'
unless (
  decoded_token[:iss] == expected_iss &&
  decoded_token[:aud] == expected_aud &&
  decoded_token[:sub].present? &&
  Time.at(decoded_token[:iat]).between?(5.minutes.ago, Time.now) &&
  Time.at(decoded_token[:exp]) > Time.now
)
  raise 'Access Token Verification Failed!'
end

print 'Cotter User id = ' + decoded_token[:sub]
```

{% endtab %}

{% tab title="Python (Flask)" %}

```python
# Install Dependencies
pip install cotter
pip install -U flask-cors

# Add a flask Endpoint
from flask import Flask
from flask import request
from flask_cors import CORS
from cotter import validate


app = Flask(__name__)
CORS(app)

@app.route('/login', methods=['POST'])
def login(name=None):
    req = request.get_json();

    # Getting access token and validate it
    token = req["oauth_token"]["access_token"]
    access_token_decoded = validate.validate_access_token(token, API_KEY_ID)

    # User Authenticated!
    # a) Either use Cotter's Access Token for your entire API authorization
    # OR
    # b) You can Generate your JWT Tokens or other session management here
    
    return resp;
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php 
require __DIR__ . '/vendor/autoload.php';
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\JWK;
use Jose\Component\Signature\Algorithm\ES256;
use Jose\Component\Signature\Serializer\JWSSerializerManager;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\Signature\JWSVerifier;

// The algorithm manager with the HS256 algorithm.
$algorithmManager = new AlgorithmManager([
  new ES256(),
]);

// We instantiate our JWS Verifier.
$jwsVerifier = new JWSVerifier(
  $algorithmManager
);

// The serializer manager. We only use the JWS Compact Serialization Mode.
$serializerManager = new JWSSerializerManager([
  new CompactSerializer(),
]);

$http = new GuzzleHttp\Client();
$response = $http->request('GET', 'https://www.cotter.app/api/v0/token/jwks', []);

$keys = json_decode((string) $response->getBody(), true);
$jwk = new JWK($keys["keys"][0]);


$token = "eyJhbGciOiJFUzI1NiIsImtpZCI6IlNQQU...";

// We try to load the token.
$jws = $serializerManager->unserialize($token);

// We verify the signature. This method does NOT check the header.
// The arguments are:
// - The JWS object,
// - The key,
// - The index of the signature to check. See 
$isVerified = $jwsVerifier->verifyWithKey($jws, $jwk, 0);

// continue your logic here
?>
```

{% endtab %}

{% tab title="Go" %}

```go
package middleware

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/google/uuid"
	"github.com/labstack/echo/v4"
	"gopkg.in/square/go-jose.v2"
	"gopkg.in/square/go-jose.v2/jwt"
)

// 👇 Enter your API KEY ID here
const API_KEY_ID = "YOUR_API_KEY_ID"
const JWKSURL = "https://www.cotter.app/api/v0/token/jwks"
const JWKSLookupKeyID = "SPACE_JWT_PUBLIC:8028AAA3-EC2D-4BAA-BE7A-7C8359CCB9F9"

func getKey() ([]byte, error) {
	// Fetch the JWT Public Key from the URL
	resp, err := http.Get(JWKSURL)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Parse the response into our keys struct
	keyset := make(map[string][]map[string]interface{})
	err = json.Unmarshal(body, &keyset)
	if err != nil {
		return nil, err
	}

	// It's a Key Set = there might be multiple keys
	// Find the key with kid = JWKSLookupKeyID
	if len(keyset["keys"]) <= 0 {
		return nil, errors.New("Key set is empty")
	}
	for _, k := range keyset["keys"] {
		if k["kid"] == JWKSLookupKeyID {
			key, err := json.Marshal(k)
			if err != nil {
				return nil, err
			}
			return key, nil
		}
	}
	return nil, errors.New("Cannot find key with kid")
}

// validateClientAccessToken validates access token created above
func validateClientAccessToken(accessToken string) (map[string]interface{}, error) {
	tok, err := jwt.ParseSigned(accessToken)
	if err != nil {
		return nil, errors.New("Fail parsing access token")
	}

	keys, err := getKey()
	if err != nil {
		return nil, err
	}
	key := jose.JSONWebKey{}
	key.UnmarshalJSON(keys)

	token := make(map[string]interface{})
	if err := tok.Claims(key, &token); err != nil {
		return nil, errors.New("Fail parsing access token to claims")
	}

	// Check that the aud is our API KEY ID
	apiKeyID, ok := token["aud"].(string)
	if !ok {
		return nil, errors.New("fail asserting aud from jwt.MapClaims")
	}
	if apiKeyID != API_KEY_ID {
		return nil, errors.New("Invalid aud, not meant for this api key id")
	}

	return token, nil
}

// This middleware assumes that your API endpoint looks like this:
// GET https://something.com/user/1
// Authorization: Bearer <access_token> 👈  We want to verify this

// CotterAuth is used to authenticate cotter access_tokens
func CotterAuth(next echo.HandlerFunc) echo.HandlerFunc {
	return func(ctx echo.Context) error {
		// get the access token from auth header
		reqToken := ctx.Request().Header.Get("Authorization")
		splitToken := strings.Split(reqToken, " ")
		if len(splitToken) != 2 {
			return errors.New("authorization header malformed")
		}
		tokenString := splitToken[1]

		// Validate that the access token and signature is valid
		token, err := validateClientAccessToken(tokenString)
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, err.Error())
		}

		// Read other claims, such as sub = Cotter User ID
		uIDStr, ok := token["sub"].(string)
		if !ok {
			return echo.NewHTTPError(http.StatusBadRequest, "fail asserting sub from jwt.MapClaims")
		}
		cotterUserID, err := uuid.Parse(uIDStr)
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, "invalid user id format in jwt claims")
		}
		fmt.Println("User logging in = ", cotterUserID)

		return next(ctx)
	}
}
```

{% endtab %}
{% endtabs %}




---

[Next Page](/llms-full.txt/1)

