- About
- Documentation
- Requirements
- Installation
- Quickstart
- Building and Running Sample App
- Project Structure
- Code Structure
- Exception Types
- Useful Gradle Tasks
Twilio Passkeys SDK enables developers to easily add Passkeys into their existing authentication flows within their own mobile applications. The Verify Passkeys SDK supports passkeys creation and authentication using the FIDO/WebAuthn industry standard.
- Android Studio for Android development. Minimum version Hedgehog
- Xcode for iOS development. 15.x
- IntelliJ IDEA or Android Studio for shared code development.
- Android 9 (API Level 28) or higher
- iOS 16 or higher
- Gradle 8.2
- Java 17
- Download the .aar file from the release page.
- Create a folder
libs
in the module directory. - Copy/move the .aar file in
libs
folder. - Add the implementation statement in dependencies:
implementation(files("libs/TwilioPasskeys.aar"))
- Sync the project.
- Use the SDK by creating an instance of TwilioPasskey:
val twilioPasskey = TwilioPasskey(context)
- Download the XCFramework form the release page.
- Create a Framework folder or use any name of your preference.
- Copy/Move the XCFramework into the folder created at the previous step.
- On your Project Configurations > General > Frameworks, Libraries, and Embedded Content section, drag & drop the XCFramework.
- Import TwilioPasskeys in the files you will make use of it:
let twilioPasskey = TwilioPasskey()
Use the TwilioPasskey
instance to create a registration by calling the create(String, AppContext)
function.
The first param is a String
representation of a create passkey request, check how to create passkey payload (createPayload
).
The second param is an instance of a com.twilio.passkeys.AppContext
, it is created by passing the current Activity
instance in Android or the UIWindow
instance in iOS.
You can also call the create(CreatePasskeyRequest, AppContext)
function, where CreatePasskeyRequest
is a wrapper object of a create passkey payload schema.
Android
val createPasskeyResult = twilioPasskey.create(createPayload, AppContext(activity))
when(createPasskeyResult) {
is CreatePasskeyResult.Success -> {
// verify the createPasskeyResult.createPasskeyResponse against your backend and finish sign up
}
is CreatePasskeyResult.Error -> {
// handle error
}
}
iOS
let response = try await twilioPasskey.create(createPayload: createPayload, appContext: AppContext(uiWindow: window))
if let success = response as? CreatePasskeyResult.Success {
// verify the createPasskeyResult.createPasskeyResponse against your backend and finish sign up
} else if let error = response as? CreatePasskeyResult.Error {
// handle error
}
Use the TwilioPasskey
instance to authenticate a user by calling the authenticate(String, AppContext)
function.
The first param is a String
representation of an authentication request, it follows the schema of an authenticate passkey payload (authenticatePayload
).
The second param is an instance of a com.twilio.passkeys.AppContext
, it is created by passing the current Activity
instance in Android or the UIWindow
instance in iOS.
You can also call the authenticate(AuthenticatePasskeyRequest, AppContext)
function, which the AuthenticatePasskeyRequest
is a wrapper object of an authenticate passkey payload.
Android
val authenticatePasskeyResult = twilioPasskey.authenticate(authenticatePayload, AppContext(activity))
when(authenticatePasskeyResult) {
is AuthenticatePasskeyResult.Success -> {
// verify the authenticatePasskeyResult.authenticatePasskeyResponse against your backend
}
is AuthenticatePasskeyResult.Error -> {
// handle error
}
}
iOS
let response = try await twilioPasskey.authenticate(challengePayload: json, appContext: AppContext(uiWindow: window))
if let success = response as? AuthenticatePasskeyResult.Success {
// verify the authenticatePasskeyResult.authenticatePasskeyResponse against your backend and finish sign in.
} else if let error = response as? AuthenticatePasskeyResult.Error {
// handle error
}
The creation payload for creating a passkey is a String obtained by requesting your backend a challenge for registering a user, it uses the JSON schema:
{"rp":{"id":"your_backend","name":"PasskeySample"},"user":{"id":"WUV...5Ng","name":"1234567890","displayName":"1234567890"},"challenge":"WUY...jZQ","pubKeyCredParams":[{"type":"public-key","alg":-7}],"timeout":600000,"excludeCredentials":[],"authenticatorSelection":{"authenticatorAttachment":"platform","requireResidentKey":false,"residentKey":"preferred","userVerification":"preferred"},"attestation":"none"}
The authenticate payload for authenticating a user is a JSON with the schema:
{"publicKey":{"challenge":"WUM...2Mw","timeout":300000,"rpId":"your_backend","allowCredentials":[],"userVerification":"preferred"}}
- Clone this repository.
- Open the project in IntelliJ IDEA or Android Studio.
- Set your backend URL BaseUrl.
- Build and run the Android app from the
androidApp
module.
Note: To start sign up/in flows, the Android device must have a valid Google account to store and fetch passkeys.
Backend-side configuration for Android Sample App
- Make sure you already added support for digital asset links in your backend by checking whether an entry with the build sha256 value exists. You can generate a sha256 by running
./gradlew signingreport
. - Add the origin if you have not added it yet, following the official documentation.
- Clone this repository.
- Open the project in IntelliJ IDEA or Android Studio or open
iosApp
module in Xcode. - Configure your backend
- Set your backend domain and entitlements. Update these values in Constants.swift and iosApp.entitlements
- Constants.swift - Define your backend domain:
let domain: String = "passkeys-service.com" // Example domain; replace with your backend domain
- iosApp.entitlements - Specify entitlements:
<string>webcredentials:passkeys-service.com</string> <!-- Example entitlement; update with your domain -->
- Set your backend domain and entitlements. Update these values in Constants.swift and iosApp.entitlements
- Build and run the iOS app from the
iosApp
module.
Note: To start sign up/in flows, the iPhone must have a valid iCloud account to store and fetch passkeys.
shared
: This module contains the shared code, including business logic, data models, and utility functions.androidApp
: This module is specific to the Android platform and includes the Android sample app code.iosApp
: This module is specific to the iOS platform and includes the iOS sample app code.
The shared
module contains code shared between Android and iOS. This includes:
- Data models
- Business logic
- Utility functions
The androidApp
module contains Android-specific code, such as:
- Sample application that works as a code snippet for integrating with the Twilio Verify Passkeys SDK
- Android-specific UI components
The iosApp
module contains iOS-specific code, such as:
- Sample application that works as a code snippet for integrating with the Twilio Verify Passkeys SDK
- iOS-specific UI components
The TwilioException class offers structured error handling for various scenarios specific to Twilio's passkeys features. Each error type is represented by a subclass with a unique error code, enabling precise exception management. Below are the defined exception types:
Exception | Code | Description | Platform-Specific Details |
---|---|---|---|
DomException |
1001 | Handles errors related to WebAuthn DOM configurations for passkeys. | Android: Ensure /.well-known/assetlinks.json is set up. See Android Guide. iOS: Ensure /.well-known/apple-app-site-association is set up. See Apple Guide. |
UserCanceledException |
1002 | Thrown when the user voluntarily cancels an operation, allowing graceful termination of user actions. | N/A |
InterruptedException |
1003 | Thrown when an operation is interrupted, typically recoverable by retrying. | Android Only |
UnsupportedException |
1004 | Indicates the device does not support or has disabled the passkeys feature, preventing passkey operations. | N/A |
NoCredentialException |
1005 | Thrown when no passkey credentials are available, indicating no user credentials are set up for authentication. | Android Only |
MissingAttestationObjectException |
1006 | Raised when the attestation object is null, which is essential for passkey operations. | iOS Only |
InvalidPayloadException |
1007 | Thrown when the JSON payload is invalid, meaning the data does not meet the expected format or schema. | N/A |
GeneralException |
1008 | Represents a general or unspecified error used as a fallback for errors not fitting other categories. | N/A |
./gradlew :shared:iosSimulatorArm64Test
./gradlew :shared:testDebugUnitTest
To run tests with coverage report
./gradlew :shared:koverHtmlReportDebug
Code coverage rule only working on Android
./gradlew :shared:koverVerify
Check Ktlint rule violations
./gradlew ktlintCheck
Try to solve Ktlint rule violations
./gradlew ktlintFormat
Check Detekt rule violations
./gradlew detekt