Passkeys with Mobile SDKs

This guide is meant for developers that are NOT using Descope Flows to design login screens and authentication methods.

If you'd like to use Descope Flows, Quick Start should be your starting point.

Descope supports passkeys which allow for users to authenticate via the FIDO Alliance's WebAuthn standard. This standard allows for users to authenticate using a variety of methods including biometrics, hardware tokens, and more.

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

Import and initialize SDK

Parameters:

  • baseUrl: Custom domain that must be configured to manage token response in cookies. This makes sure every request to our service is through your custom domain, preventing accidental domain blockages.
import DescopeKit

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Descope.setup(projectId: "<Your-Project-Id>")
    return true
}
import android.app.Application
import com.descope.Descope
 
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Descope.setup(this, projectId = "<Your-Project-Id>")
    }
}
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();

Setup and Authentication

Users can authenticate by creating a new passkey or using an existing one. To enable passkey authentication, first configure your Passkey/WebAuthn settings in the Descope Console.

Make sure Passkey/WebAuthn authentication is enabled and that the top-level domain is configured correctly.

For Android, follow Google's official Add support for Digital Asset Links setup guide. Complete the required asset links and manifest configuration so your app can be associated with your domain.

For iOS and macOS, refer to Apple's Supporting Passkeys guide. Make sure your app has an associated domain configured with the webcredentials service type, and that the value matches the top-level domain configured in the Descope Console.

Check Passkey Support

Before presenting passkey options to users, check whether passkeys are supported on the current device:

Passkeys require iOS 15 and above. You can check availability at runtime using:

if #available(iOS 15, *) {
    // Passkeys are supported; show passkey UI
} else {
    // Fall back to another authentication method
}

The Flutter SDK provides an explicit isSupported() method on Descope.passkey that checks whether passkeys are supported on the current device:

  • iOS: returns true on iOS 15 and above
  • Android: returns true on API 28 (Android 9) and above
  • Web: checks for WebAuthn browser support
try {
    final supported = await Descope.passkey.isSupported();
    if (supported) {
        // Passkeys are supported; show passkey UI
    } else {
        // Fall back to another authentication method
    }
} on DescopeException catch (e) {
    // Handle errors checking passkey support
}

Authenticate with a Passkey

Note

The passkey operations are all suspending functions that perform network requests before and after displaying the modal authentication view.

It is thus recommended to switch the user interface to a loading state before calling them, otherwise the user might accidentally interact with the app when the authentication view is not being displayed.

do {
    showLoading(true)
    let authResponse = try await Descope.passkey.signUpOrIn(loginId: "andy@example.com", options: [])
    let session = DescopeSession(from: authResponse)
    Descope.sessionManager.manageSession(session)
    showHomeScreen() 
} catch DescopeError.oauthNativeCancelled {
    showLoading(false)
    print("Authentication canceled")
} catch {
    showError(error)
}
// Enter loading state...

try {
    val authResponse = Descope.passkey.signUpOrIn(this@MyActivity, loginId)
    val session = DescopeSession(authResponse)
    Descope.sessionManager.manageSession(session)
} catch (e: DescopeException) {
    // Handle errors here
}

// Exit loading state...
try {
    showLoading(true);
    final authResponse = await Descope.passkey.signUpOrIn(loginId: loginId);
    final session = DescopeSession.fromAuthenticationResponse(authResponse);
    Descope.sessionManager.manageSession(session);
    showHomeScreen() 
} on DescopeException catch (e) {
    if (e == DescopeException.passkeyCancelled) {
      showLoading(false)
      print("Authentication canceled")
    } else {
      showError(error)
    }
}
Was this helpful?

On this page