A Flutter SDK for Cotter's Authentication Services. This package helps you add passwordless login to your app using the following methods:
- Sign in with device
- Sign in with email using OTP
- Sign in with phone number using OTP
- Sign in with email using magic link
- Sign in with phone number magic link
As mentioned, there are 3 different ways to authenticate users. You can also combine the authentication methods, for example: Register the user after verifying their emails, then use Sign in with device for subsequent logins.
To use this SDK, you can create a free account at Cotter to get your API keys.
Update your minSdkVersion
to 18
inside android/app/build.gradle
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
...
}
Signing in with device works like Google Prompt. It allows users to sign in to your website or app automatically from a device that they trust, or in one-tap by approving the login request from your app.
Find a quickstart tutorial in our documentation.
To register a new user, we need to create a new user in Cotter and register the current device as trusted.
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
var user = await cotter.signUpWithDevice(identifier: emailAddress);
// You can also verify the user's email here, check
// "Sign in with Email > Verify Email for a logged-in user" below
user = await user.verifyEmailWithOTP(redirectURL: "myexample://auth_callback");
} catch(e) {
print(e);
}
To authenticate your user, the SDK will check and verify if the current device is trusted. If it is trusted, users can sign in automatically.
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
var event = await cotter.signInWithDevice(
identifier: emailAddress,
context: context,
);
} catch(e) {
print(e);
}
if (event.approved) {
_goToDashboard();
}
Signing in from a Trusted Device
If the user signed-in from a trusted device, the event will automatically be approved and the user can proceed to the dashboard.
Signing in from a Non-Trusted Device
Otherwise, the SDK will show a prompt that asks the user to approve the login from the device that they trust. Inside your app in the trusted device, the SDK will show a prompt asking if the user want to approve the login.
// Show prompt to approve the login from the trusted device
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
var user = await cotter.getUser();
var event = await user.checkNewSignInRequest(context: context);
} catch(e) {
print(e);
}
You can authenticate users by sending a verification code to their email/phone and checking if they can enter the code correctly. Ideally, you would combine this method with the sign up with device method above. For example, there are several flows that you can use:
- Sign up and authenticate solely by sending a verification code to the user's email.
- Verify the user's email, then register user's device as trusted. Subsequent logins using sign in with device.
- Sign up with user's device, then verify user's email after registration. Subsequent logins using sign in with device.
The email and phone number verification uses OAuth PKCE flow, which requires the app to open a secure in-app browser to authenticate and redirect back to the app with an authorization code.
To do this, you need to set up deep-linking.
Example URL: myexample://auth_callback
. Add the following to your ios/Runner/Info.plist
.
<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>
Example URL: myexample://auth_callback
. Add the following to your android/app/src/main/AndroidManifest.xml
.
<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 -->
<!-- π Change this to your own URL scheme -->
<data android:scheme="myexample" android:host="auth_callback"/>
</intent-filter>
</activity>
<!-- Until here -->
</application>
</manifest>
Enter myexample://auth_callback
in the simulator's browser and see if it redirects to your app.
Make sure you have set up the deep-linking above.
This method will:
- Verify the user's email
- Then create a new user in Cotter if successful
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
);
// If you want to follow flow 2 above, you can register the user's device as trusted here
user = await user.registerDevice();
} catch(e) {
print(e);
}
To authenticate by verifying user's email:
This method will create a new user if one doesn't exist.
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);
}
To verify the email of a user that is currently logged-in:
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);
}
Make sure you have set up the deep-linking above.
This method will:
- 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.
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:
try {
var user = await cotter.signUpWithPhoneOTPViaSMS(
redirectURL: "myexample://auth_callback",
phone: inputController.text,
);
} catch (e) {
print(e);
}
- Using WhatsApp:
try {
var user = await cotter.signUpWithPhoneOTPViaWhatsApp(
redirectURL: "myexample://auth_callback",
phone: inputController.text,
);
} catch (e) {
print(e);
}
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.
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:
try {
var user = await cotter.signInWithPhoneOTPViaSMS(
redirectURL: "myexample://auth_callback",
phone: inputController.text,
);
} catch (e) {
print(e);
}
- Using WhatsApp:
try {
var user = await cotter.signInWithPhoneOTPViaWhatsApp(
redirectURL: "myexample://auth_callback",
phone: inputController.text,
);
} catch (e) {
print(e);
}
To verify the phone number of a user that is currently logged-in:
- Using SMS:
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:
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);
}
The SDK automatically stores OAuth tokens (access token, id token, and refresh token) in the device's secure storage, along with the user information.
To get the logged-in user information:
Cotter cotter = new Cotter(apiKeyID: API_KEY_ID);
try {
var user = await cotter.getUser();
} catch(e) {
print(e);
}
The SDK can fetch stored tokens for you and refresh them as needed if they're expired.
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);
}