# Sign in with Email/Phone Number

> **Concepts:** Learn about how [**Sign in with Email/Phone Number**](/features/verify-email-phone.md) 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.md#step-1-import-cotter-as-a-dependency)
2. &#x20;[Setup Deep Linking](/sdk-reference/flutter/sign-in-with-email-phone-number.md#step-2-setup-deep-linking)
3. [Signing up](/sdk-reference/flutter/sign-in-with-email-phone-number.md#step-3-signing-up)
4. [Logging in](/sdk-reference/flutter/sign-in-with-email-phone-number.md#step-4-logging-in)
5. [Verifying a logged-in user](/sdk-reference/flutter/sign-in-with-email-phone-number.md#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.md).

### 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.md)
{% 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.md)
{% 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.md)
{% 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.md)
* [Rate Limit the number of authentication requests](/protecting-your-account/rate-limit.md)
* [Enable reCAPTCHA to prevent automated abuse](/protecting-your-account/enable-recaptcha-to-protect-against-automated-abuse.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.cotter.app/sdk-reference/flutter/sign-in-with-email-phone-number.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
