28 August 2025
This post is part of Wear OS Spotlight Week. Today, we're focusing on implementing Credential Manager on Wear OS, aiming to streamline the authentication experience.
For all software developers, crafting a fast and secure authentication flow is paramount, and this is equally important on Wear OS.
The traditional Wear OS methods require users to have their phone nearby to complete authentication, often with a separate mobile flow or 2-factor auth code.
Credential Manager's arrival simplifies this process, allowing for authentication directly from a user's watch with no need for a nearby phone.
As a unified API, Credential Manager enables you to reuse your mobile app’s code on Wear OS, streamlining development across form factors. With a single tap, users can authenticate with passwords, federated identities like Sign in with Google, or passkeys, the new industry standard for security.
Passkeys are built on the principle of asymmetric encryption. During creation, a system authenticator generates a unique, mathematically linked pair of keys: a public key that is securely stored online with the service, and a private key that remains exclusively on the user's device.
When signing in, the device uses the private key to cryptographically prove to the service that it possesses the key.
This process is highly secure because the private key never leaves the device during authorization (only during syncs from credential providers) and can only be used with the user's explicit permission. This makes passkeys resistant to server breaches, as a breach could only ever expose the public half of the key pair. Additionally, since there is no passphrase to steal, passkeys are virtually phishing-proof.
The user experience of passkeys is seamless: to log in, a user confirms their presence with their device's lock (e.g., biometric credential or PIN), and they are signed in. This eliminates the need to remember complex passphrases and provides a faster, more secure method of authentication that works seamlessly across devices.
Credential Manager should be the base of a Wear app’s authentication flow. Developers should decide which of its built-in methods to implement based on what is implemented in their mobile experiences, and based on the variety of authentication methods their users need.
Passkeys are the preferred built-in solution due to their inherent security and simplicity, but the other built-in options Credential Manager provides can also be implemented. Passwords are valuable because of their familiarity to users, and federated identities like Sign in with Google provide users with the comfort of a trusted provider.
Developers should maintain at least one of their existing authentication options as a backup as they transition their users to Credential Manager. If Credential Manager is dismissed by a user, or if all of its methods fail, or if credentials are not available, developers can present their backup options.
The Wear Authentication developer guide includes details on supported Wear OS backup authentication options. These include solutions like OAuth 2.0, which has traditionally been a popular choice on Wear OS; and data layer token sharing, which can be used to automatically authenticate users at app launch time if their phone is nearby to sync a signed in account.
Read the full Wear sign-in design guidance to learn about all the best practices for designing your authentication flow, including our special guidance around data layer token sharing.
At its core, Credential Manager consolidates multiple authentication methods into a single, unified API call: getCredential. By configuring a GetCredentialRequest with your authentication options, you can use the response to validate a user's identity with your app's server that contains the credentials, like so:
val request = GetCredentialRequest(getCredentialOptions()) val getCredentialResponse = credentialManager.getCredential(activity, request) login(getCredentialResponse.credential)
For a truly seamless experience, a user's credentials must sync effortlessly from their other devices to their watch, since it is currently not possible to create credentials on Wear OS.
To enable this, you must add an entry for Wear OS in your Digital Asset Links to associate your Wear OS app with other versions of your app. Be sure to precisely fill out the asset link entry, including your app's applicationId and the SHA-256 cryptographic hash from your application’s digital signature. You can test them out with our app link verification guide.
To allow users to sign in with Credential Manager, provide getCredential with options for the three built-in authentication types: passkeys, passwords, and federated identities like Sign in With Google.
// Adding options is part of creating the credential request GetCredentialRequest(getCredentialOptions())) // Furnish list of CredentialOptions for the request suspend fun getCredentialOptions(): List<CredentialOption> { return listOf( // Passkey: Furnish a GetPublicKeyCredentialOption with public key // data from your authentication server GetPublicKeyCredentialOption(authServer.getPublicKeyRequestOptions()), // Password: Add the provided GetPasswordOption type in your list GetPasswordOption(), // Federated Identity: Add your desired option type (GetGoogleIdOption, below) // to orchestrate a token exchange with the federated identity server. GetGoogleIdOption.Builder().setServerClientId(SERVER_CLIENT_ID).build(), ) }
When getCredential is called, Credential Manager will use the options developers provide to present users with a UI to choose how they want to log in.
After a user selects their desired credential in the Credential Manager UI, use the result of getCredential (which contains the selected credential) to route to your authentication handlers.
// getCredential returns the selected credential login(getCredentialResponse.credential) // Route to your credential handling functions to login suspend fun login(credential: Credential): LoginResult { when (credential) { is PublicKeyCredential -> { return authHandler.loginWithPasskey(credential.authenticationResponseJson) } is PasswordCredential -> { return authHandler.loginWithPassword(credential.id, credential.password) } is CustomCredential -> { return authHandler.loginWithCustomCredential( credential.type, credential.data) } // ‘else’ case, etc…
The handling logic for each of the above loginWith’x’ methods is slightly different, although they all set up network calls to dedicated authentication endpoints. Below are simplified versions of these methods which demonstrate network calls to authenticate users based on their selected method.
Passkeys require the signed passkey JSON data. Your server will use this data to cryptographically verify the user.
suspend fun loginWithPasskey(passkeyResponseJSON: String): LoginResult { val validatedPasskey = httpClient.post( "myendpoint/passkey", passkeyResponseJSON, /*other args*/) return LoginResult(validatedPasskey) }
Passwords require network logic to validate the username and password, our example uses subsequent calls to validate the username first. Your backend will validate these against its user database.
suspend fun loginWithPassword(userName: String, password: String): LoginResult { val validatedUserName = httpClient.post( "myendpoint/username", userName, /*other args*/) val validatedPassword = httpClient.post( "myendpoint/password", password, validatedUserName, /*other args*/) return LoginResult(ValidatedPassword) }
Federated identities like Sign in with Google require that a secure connection is established between your server and your app. Our sample shows a challenge-response flow initiated from the server, but a client generated nonce works as well.
Our sample server provides a challenge to our app on request (federatedSessionId, below) which is subsequently used to validate the federated token to authenticate the user.
suspend fun loginWithCustomCredential(type: String, data: Bundle): LoginResult { if (type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) { token = GoogleIdTokenCredential.createFrom(data).idToken } // Establish a federated session for with your server and obtain its info val federatedSessionId = httpClient.post("myendpoint/ObtainFederatedSession", /*federated backend address=*/"https://accounts.google.com") // Validate the token with the established federated session. val validatedCustomCredential = httpClient.post( "myendpoint/verifyToken", token, federatedSessionID, /*federated backend address=*/"https://accounts.google.com") return LoginResult(validatedCustomCredential) }
If a user taps dismiss, or swipes back from Credential Manager, a GetCredentialCancellationException will be thrown for developers to use to navigate to their backup login screens, which will provide secondary authentication options to users. These options are detailed in the Designing Authentication with Credential Manager section, above.
// Catch the user dismissal catch (e: GetCredentialCancellationException) { // Trigger event that navigates to ‘BackupLoginScreen’ uiEvents.send(UiEvent.NavigateToBackupLogin) }
Special Note: The version of Google Sign in that exists outside of Credential Manager is now deprecated and will be removed, and should not be provided as a secondary option to avoid presenting two buttons for the same purpose.
See the Wear OS transition guide for more details.
Implementing Credential Manager on Wear OS is a straightforward process that delivers significant benefits. By adopting this API, you can provide your users with a secure, seamless, and efficient way to authenticate. To begin implementation, explore our developer documentation and official sample app.
To learn how apps have migrated to Credential Manager on Wear OS, check out our case study with Todoist, who were able to streamline their authentication whilst reusing their mobile implementation.
For a look at how passkeys can improve login success rate, you can read all about how X adopted passkeys to achieve a more secure and user-friendly authentication experience.
Finally, you can watch the new credential manager video blog on YouTube to reinforce everything you’ve learned here.
Happy coding!