Authenticator Apps (TOTP) via Mobile SDKs
Descope supports validating sign-up and sign-ins via Authenticator Applications which provide a Time-based One-time Password (TOTP). Google Authenticator, Microsoft Authenticator, and Authy are examples of authenticator apps. Descope generates the required QR code or key (also called a secret or seed) in order to configure new a new Authenticator.
Client SDK
Install SDK
// 1. Within XCode, go to File > Add Packages
// 2. Search for the URL of the git repo: https://github.com/descope/descope-swift
// 3. Configure your desired dependency rule
// 4. Click Add Package// 1. Within Android Studio, go to File > Project Structure > Dependencies > Add Dependency > 1 Library Dependency
// 2. Search for the dependency: "com.descope"
// 3. Configure your desired dependency rules
// 4. Click "Ok"// 1. From your Flutter project directory root, install the Descope SDK by running: flutter pub add descope
// 2. Or, add Descope to your pubspec.yml by including this line: descope: ^0.9.0
// View the package on pub.dev: https://pub.dev/packages/descope// 1. From your React Native project directory root, install the Descope SDK by running: npm i @descope/react-native-sdk
// View the package: https://github.com/descope/descope-react-nativeImport and initialize SDK
import DescopeKit
import AuthenticationServices
do {
Descope.setup(projectId: "__ProjectID__") { config in
// Optional: Only set baseURL if using a custom domain with Descope and managing token response with cookies
config.baseURL = "https://auth.app.example.com"
}
print("Successfully initialized Descope")
} catch {
print("Failed to initialize Descope")
print(error)
}import android.app.Application
import com.descope.Descope
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
try {
Descope.setup(this, projectId = "__ProjectID__") {
// Optional: Only set baseURL if using a custom domain with Descope and managing token response with cookies
baseUrl = "https://auth.app.example.com"
// Enable the logger
if (BuildConfig.DEBUG) {
logger = DescopeLogger()
}
}
} catch (e: Exception) {
Log.e("ERROR", e.stackTraceToString())
}
}
}import 'package:descope/descope.dart';
// Where your application state is being created
Descope.setup('<Your-Project-Id>', (config) {
// Optional: Only set baseURL if using a custom domain with Descope and managing token response with cookies
config.baseUrl = 'https://auth.app.example.com';
});
await Descope.sessionManager.loadSession();import { AuthProvider } from '@descope/react-native-sdk'
const AppRoot = () => {
return (
<AuthProvider
projectId="__ProjectID__"
// Optional: Only set baseURL if using a custom domain with Descope and managing token response with cookies
baseUrl = "https://auth.app.example.com"
>
<App />
</AuthProvider>
)
}User Sign-Up
The first step for implementing TOTP authentication is sign-up. In this step the user registers their TOTP app with the authentication service. Descope will generate a TOTP key (also called a secret or seed) that will be entered into the end user's authenticator app so that TOTP codes can be successfully verified. The new end user will be registered after the full TOTP sign-up flow has been successfully completed.
// Args:
// loginId: email or phone - becomes the unique ID for the user from here on and also used for delivery
let loginId = "email@company.com"
// user: Optional user object to populate new user information.
let user = { "name": "Joe Person", "phone": "+15555555555", "email": "email@company.com"}
do {
let totpResponse = try await Descope.totp.signUp(loginId: loginId, user: user)
print("Successfully initiated TOTP Sign Up")
print("TOTP QR Code: Returned as a UIImage within totpResponse.image")
print("TOTP Key: " + totpResponse.key)
print("TOTP Provisioning URL: " + totpResponse.provisioningURL)
} catch {
print("Failed to initiate TOTP Sign Up")
print(error)
}// Args:
// loginId: email or phone - becomes the unique ID for the user from here on and also used for delivery
// details: Optional user details object to populate new user information.
val details = SignUpDetails(
name = "firstName lastName",
email = "email@company.com",
phone = "+15555555555",
givenName = "firstName",
middleName = "middleName",
familyName = "lastName"
)
try {
Descope.totp.signUp(loginId = "email@company.com", details = details)
} catch (e: Exception) {
Log.e("ERROR", e.stackTraceToString())
}// Args:
// loginId: email or phone - becomes the unique ID for the user from here on and also used for delivery
const loginId = "email@company.com";
// details: Optional user details object to populate new user information.
final details = SignUpDetails(name: "Joe Person");
final totpResponse = await Descope.totp.signUp(loginId: loginId);
// Use one of the provided options to have the user add their credentials to the authenticator
// totpResponse.provisioningUrl
// totpResponse.image;
// totpResponse.key// Args:
// user: Optional user object to populate new user information.
const user = { "name": "Joe Person", "phone": "+15555555555", "email": "email@company.com"}
// loginId: email or phone - becomes the unique ID for the user from here on and also used for delivery
const loginId = "email@company.com"
const descopeSdk = useDescope();
const resp = await descopeSdk.totp.signUp(loginId, user);
if (!resp.ok) {
console.log("Failed to initialize TOTP signup")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
}
else {
console.log("Successfully initialized TOTP signup.")
console.log(resp.data)
}User Sign-In / Verify
For signing in, your application client must prompt the user for loginId, such as email or phone, and the code from the
authenticator application. Your client will then call the verify function. Upon successful verification, the user will be
logged in and the response will include the JWT information.
// Args:
// loginId: email or phone - must be same as provided at the time of signup.
let loginId = "email@company.com"
// code: code entered by the user from the authenticator application.
let code = "xxxx"
guard let session = Descope.sessionManager.session else { return }
var signInOptions: [SignInOptions] = [
.customClaims(["name": "{{user.name}}"]),
.mfa(refreshJwt: session.refreshJwt),
.stepup(refreshJwt: session.refreshJwt)
]
do {
let descopeSession = try await Descope.totp.verify(loginId: loginId, code: code, options: signInOptions)
print("Successfully verified TOTP Code")
print(descopeSession as Any)
} catch {
print("Failed to verify TOTP Code")
print(error)
}// Args:
// loginId: email or phone - must be same as provided at the time of signup.
// code: code entered by the user from the authenticator application.
try {
Descope.totp.verify(loginId = "email@company.com", code = "xxxx")
} catch (e: Exception) {
Log.e("ERROR", e.stackTraceToString())
}// Args:
// loginId: email or phone - must be same as provided at the time of signup.
const loginId = "email@company.com";
// code: code entered by the user from the authenticator application.
const code = "xxxx";
// options: Optional options to get custom claims in response
const options = SignInOptions(customClaims: {'name': '{{user.name}}'});
final authResponse = await Descope.totp.verify(loginId: loginId, code: code, options: options);// Args:
// loginId: email or phone - must be same as provided at the time of signup.
const loginId = "email@company.com"
// code: code entered by the user from the authenticator application.
const code = "xxxx"
// loginOptions (LoginOptions): this allows you to configure behavior during the authentication process.
const loginOptions = {
"stepup": false,
"mfa": false,
"customClaims": {"claim": "Value1"},
"templateOptions": {"option": "Value1"}
}
// refreshToken (optional): the user's current refresh token in the event of stepup/mfa
const descopeSdk = useDescope();
const resp = await descopeSdk.totp.verify(loginId, code, loginOptions);
if (!resp.ok) {
console.log("Failed to Sign-In via TOTP")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
}
else {
console.log("Successfully signed in via TOTP. " + JSON.stringify(resp.data))
}Update User
The update user call is used when you would like to associate a new authenticator method with an existing and authenticated user. You need to pass the refresh token or http request of an authenticated user. The update will work only if the user is authenticated.
// Args:
// loginId: email, phone or username of the authenticated user
let loginId = "email@company.com"
// refresh_token: string with the refresh token of the user. This should be extracted from cookies sent with the query.
let refresh_token = "xxxxxxxx"
do {
let totpResponse = try await Descope.totp.update(loginId: loginId, refreshJwt: descopeSession!.refreshJwt)
print("Successfully initiated TOTP Update")
print("TOTP QR Code: Returned as a UIImage within totpResponse.image")
print("TOTP Key: " + totpResponse.key)
print("TOTP Provisioning URL: " + totpResponse.provisioningURL)
} catch {
print("Failed to initiate TOTP Update")
print(error)
}// Args:
// loginId: email, phone or username of the authenticated user
// refresh_token: string with the refresh token of the user. This should be extracted from cookies sent with the query.
try {
Descope.totp.update(loginId = "email@company.com", refreshJwt = "xxx")
} catch (e: Exception) {
Log.e("ERROR", e.stackTraceToString())
}// Args:
// loginId: email, phone or username of the authenticated user
const loginId = "email@company.com";
// refresh_token: string with the refresh token of the user. This should be extracted from cookies sent with the query.
const refresh_token = "xxxxxxxx";
Descope.totp.update(loginId: loginId, refreshJwt: refresh_token);// Args:
// loginId: email, phone or username of the authenticated user
const loginId = "email@company.com"
// refreshToken: string with the refresh token of the user. This should be extracted from cookies sent with the query.
const refreshToken = "xxxxxxxx"
const descopeSdk = useDescope();
const resp = await descopeSdk.totp.update(loginId, refreshToken)
if (!resp.ok) {
console.log("Failed to initialized updating user's TOTP")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
}
else {
console.log("Successfully initialized updating user's TOTP: " + resp.data)
}Session Validation
The final step of completing the authentication with Descope is to validate the user session. Descope provides rich session management capabilities, including configurable session timeouts and logout functions. You can find the details and sample code for client session validation here.
Checkpoint
Your application is now integrated with Descope. Please test with sign-up or sign-in use case.