Skip to content

Latest commit

 

History

History
1407 lines (1129 loc) · 47.6 KB

EXAMPLES.md

File metadata and controls

1407 lines (1129 loc) · 47.6 KB

Examples using Auth0.Android

Authenticate with any Auth0 connection

The connection must first be enabled in the Auth0 dashboard for this Auth0 application.

WebAuthProvider.login(account)
    .withConnection("twitter")
    .start(this, callback)

Specify audience

WebAuthProvider.login(account)
    .withAudience("https://{YOUR_AUTH0_DOMAIN}/api/v2/")
    .start(this, callback)

The sample above requests tokens with the audience required to call the Management API endpoints.

Replace {YOUR_AUTH0_DOMAIN} with your actual Auth0 domain (i.e. mytenant.auth0.com). If you've set up the tenant to use "Custom Domains", use that value here.

Specify scope

WebAuthProvider.login(account)
    .withScope("openid profile email read:users")
    .start(this, callback)

The default scope used is openid profile email. Regardless of the scopes passed here, the openid scope is always enforced.

Specify Connection scope

WebAuthProvider.login(account)
    .withConnectionScope("email", "profile", "calendar:read")
    .start(this, callback)

Specify Parameter

To prompt the user to login or to send custom parameters in the request, .withParameters method can be used.

WebAuthProvider.login(account)
    .withParameters(mapOf("prompt" to "login", "custom" to "value"))
    .start(this, callback)

Customize the Custom Tabs UI

If the device where the app is running has a Custom Tabs compatible Browser, a Custom Tab will be preferred for the logout flow. You can customize the Page Title visibility, the Toolbar color, and the supported Browser applications by using the CustomTabsOptions class.

val ctOptions = CustomTabsOptions.newBuilder()
    .withToolbarColor(R.color.ct_toolbar_color)
    .showTitle(true)
    .build()
 
WebAuthProvider.login(account)
    .withCustomTabsOptions(ctOptions)
    .start(this, callback)
Using Java
CustomTabsOptions options = CustomTabsOptions.newBuilder()
   .withToolbarColor(R.color.ct_toolbar_color)
   .showTitle(true)
   .build();

WebAuthProvider.login(account)
   .withCustomTabsOptions(options)
   .start(MainActivity.this, callback);

Changing the Return To URL scheme

This configuration will probably match what you've done for the authentication setup.

WebAuthProvider.logout(account)
    .withScheme("myapp")
    .start(this, logoutCallback)

Trusted Web Activity

Trusted Web Activity is a feature provided by some browsers to provide a native look and feel to the custom tabs.

To use this feature, there are some additional steps you must take:

  • We need the SHA256 fingerprints of the app’s signing certificate. To get this, you can run the following command on your APK
keytool -printcert -jarfile sample-debug.apk
  • The fingerprint has to be updated in the Auth0 Dashboard under Applications > Specific Application > Settings > Advanced Settings > Device Settings > Key Hashes
  • App's package name has to be entered in the field above

Once the above prerequisites are met, you can call your login method as below to open your web authentication in Trusted Web Activity.

WebAuthProvider.login(account)
    .withTrustedWebActivity()
    .await(this)

Authentication API

The client provides methods to authenticate the user against the Auth0 server.

Create a new instance by passing the account:

val authentication = AuthenticationAPIClient(account)
Using Java
AuthenticationAPIClient authentication = new AuthenticationAPIClient(account);

Note: If your Auth0 account has the "Bot Protection" feature enabled, your requests might be flagged for verification. Read how to handle this scenario on the Bot Protection section.

Login with database connection

authentication
    .login("[email protected]", "a secret password", "my-database-connection")
    .validateClaims() //mandatory
    .start(object: Callback<Credentials, AuthenticationException> {
        override fun onFailure(exception: AuthenticationException) { }

        override fun onSuccess(credentials: Credentials) { }
    })
Using coroutines
try {
    val credentials = authentication
        .login("[email protected]", "a secret password", "my-database-connection")
        .validateClaims()
        .await()
    println(credentials)
} catch (e: AuthenticationException) {
    e.printStacktrace()
}
Using Java
authentication
    .login("[email protected]", "a secret password", "my-database-connection")
    .validateClaims() //mandatory
    .start(new Callback<Credentials, AuthenticationException>() {
        @Override
        public void onSuccess(@Nullable Credentials payload) {
            //Logged in!
        }

        @Override
        public void onFailure(@NonNull AuthenticationException error) {
            //Error!
        }
    });

The default scope used is openid profile email. Regardless of the scopes set to the request, the openid scope is always enforced.

Login using MFA with One Time Password code

This call requires the client to have the MFA Client Grant Type enabled. Check this article to learn how to enable it.

When you sign in to a multifactor authentication enabled connection using the login method, you receive an error standing that MFA is required for that user along with an mfa_token value. Use this value to call loginWithOTP and complete the MFA flow passing the One Time Password from the enrolled MFA code generator app.

authentication
    .loginWithOTP("the mfa token", "123456")
    .validateClaims() //mandatory
    .start(object: Callback<Credentials, AuthenticationException> {
        override fun onFailure(exception: AuthenticationException) { }

        override fun onSuccess(credentials: Credentials) { }
    })
Using coroutines
try {
    val credentials = authentication
        .loginWithOTP("the mfa token", "123456")
        .validateClaims()
        .await()
    println(credentials)
} catch (e: AuthenticationException) {
    e.printStacktrace()
}
Using Java
authentication
    .loginWithOTP("the mfa token", "123456")
    .validateClaims() //mandatory
    .start(new Callback<Credentials, AuthenticationException>() {
        @Override
        public void onSuccess(@Nullable Credentials payload) {
            //Logged in!
        }

        @Override
        public void onFailure(@NonNull AuthenticationException error) {
            //Error!
        }
    });

The default scope used is openid profile email. Regardless of the scopes set to the request, the openid scope is always enforced.

Passwordless Login

This feature requires your Application to have the Passwordless OTP enabled. See this article to learn how to enable it.

Passwordless is a 2 step flow:

Step 1: Request the code

authentication
    .passwordlessWithEmail("[email protected]", PasswordlessType.CODE, "my-passwordless-connection")
    .start(object: Callback<Void, AuthenticationException> {
        override fun onFailure(exception: AuthenticationException) { }

        override fun onSuccess(result: Void?) { }
    })
Using coroutines
try {
    val result = authentication
        .passwordlessWithEmail("[email protected]", PasswordlessType.CODE, "my-passwordless-connection")
        .await()
    println(result)
} catch (e: AuthenticationException) {
    e.printStacktrace()
}
Using Java
authentication
    .passwordlessWithEmail("[email protected]", PasswordlessType.CODE, "my-passwordless-connection")
    .start(new Callback<Void, AuthenticationException>() {
        @Override
        public void onSuccess(Void payload) {
            //Code sent!
        }

        @Override
        public void onFailure(@NonNull AuthenticationException error) {
            //Error!
        }
    });

Step 2: Input the code

authentication
    .loginWithEmail("[email protected]", "123456", "my-passwordless-connection")
    .validateClaims() //mandatory
    .start(object: Callback<Credentials, AuthenticationException> {
       override fun onFailure(exception: AuthenticationException) { }

       override fun onSuccess(credentials: Credentials) { }
   })
Using coroutines
try {
    val credentials = authentication
        .loginWithEmail("[email protected]", "123456", "my-passwordless-connection")
        .validateClaims()
        .await()
    println(credentials)
} catch (e: AuthenticationException) {
    e.printStacktrace()
}
Using Java
authentication
    .loginWithEmail("[email protected]", "123456", "my-passwordless-connection")
    .validateClaims() //mandatory
    .start(new Callback<Credentials, AuthenticationException>() {
        @Override
        public void onSuccess(@Nullable Credentials payload) {
            //Logged in!
        }

        @Override
        public void onFailure(@NonNull AuthenticationException error) {
            //Error!
        }
    });

The default scope used is openid profile email. Regardless of the scopes set to the request, the openid scope is always enforced.

Sign Up with a database connection

authentication
    .signUp("[email protected]", "a secret password", "my-database-connection")
    .validateClaims() //mandatory
    .start(object: Callback<Credentials, AuthenticationException> {
        override fun onFailure(exception: AuthenticationException) { }

        override fun onSuccess(credentials: Credentials) { }
    })
Using coroutines
try {
    val credentials = authentication
        .signUp("[email protected]", "a secret password", "my-database-connection")
        .validateClaims()
        .await()
    println(credentials)
} catch (e: AuthenticationException) {
    e.printStacktrace()
}
Using Java
authentication
    .signUp("[email protected]", "a secret password", "my-database-connection")
    .validateClaims() //mandatory
    .start(new Callback<Credentials, AuthenticationException>() {
        @Override
        public void onSuccess(@Nullable Credentials payload) {
            //Signed Up & Logged in!
        }

        @Override
        public void onFailure(@NonNull AuthenticationException error) {
            //Error!
        }
    });

The default scope used is openid profile email. Regardless of the scopes set to the request, the openid scope is always enforced.

Get user information

authentication
   .userInfo("user access_token")
   .start(object: Callback<UserProfile, AuthenticationException> {
       override fun onFailure(exception: AuthenticationException) { }

       override fun onSuccess(profile: UserProfile) { }
   })
Using coroutines
try {
    val user = authentication
        .userInfo("user access_token")
        .await()
    println(user)
} catch (e: AuthenticationException) {
    e.printStacktrace()
}
Using Java
authentication
   .userInfo("user access_token")
   .start(new Callback<UserProfile, AuthenticationException>() {
       @Override
       public void onSuccess(@Nullable UserProfile payload) {
           //Got the profile!
       }

       @Override
       public void onFailure(@NonNull AuthenticationException error) {
           //Error!
       }
   });

Credentials Manager

Secure Credentials Manager

This version adds encryption to the data storage. Additionally, in those devices where a Secure Lock Screen has been configured it can require the user to authenticate before letting them obtain the stored credentials. The class is called SecureCredentialsManager.

Usage

The usage is similar to the previous version, with the slight difference that the manager now requires a valid android Context as shown below:

val storage = SharedPreferencesStorage(this)
val manager = SecureCredentialsManager(this, account, storage)
Using Java
Storage storage = new SharedPreferencesStorage(this);
SecureCredentialsManager manager = new SecureCredentialsManager(this, account, storage);

Requiring Authentication

You can require the user authentication to obtain credentials. This will make the manager prompt the user with the device's configured Lock Screen, which they must pass correctly in order to obtain the credentials. This feature is only available on devices where the user has setup a secured Lock Screen (PIN, Pattern, Password or Fingerprint).

To enable authentication you must supply an instance of FragmentActivity on which the authentication prompt to be shown, and an instance of LocalAuthenticationOptions to configure the authentication prompt with details like title and authentication level when creating an instance of SecureCredentialsManager as shown in the snippet below.

val localAuthenticationOptions =
    LocalAuthenticationOptions.Builder().setTitle("Authenticate").setDescription("Accessing Credentials")
        .setAuthenticationLevel(AuthenticationLevel.STRONG).setNegativeButtonText("Cancel")
        .setDeviceCredentialFallback(true)
        .build()
val storage = SharedPreferencesStorage(this)
val manager = SecureCredentialsManager(
    this, account, storage, fragmentActivity,
    localAuthenticationOptions
)
Using Java
LocalAuthenticationOptions localAuthenticationOptions =
        new LocalAuthenticationOptions.Builder().setTitle("Authenticate").setDescription("Accessing Credentials")
                .setAuthenticationLevel(AuthenticationLevel.STRONG).setNegativeButtonText("Cancel")
                .setDeviceCredentialFallback(true)
                .build();
Storage storage = new SharedPreferencesStorage(context);
SecureCredentialsManager secureCredentialsManager = new SecureCredentialsManager(
        context, auth0, storage, fragmentActivity,
        localAuthenticationOptions);

Points to be Noted:

On Android API 29 and below, specifying DEVICE_CREDENTIAL alone as the authentication level is not supported. On Android API 28 and 29, specifying STRONG as the authentication level along with enabling device credential fallback is not supported.

Creating LocalAuthenticationOptions object for requiring Authentication while using SecureCredentialsManager

LocalAuthenticationOptions class exposes a Builder class to create an instance of it. Details about the methods are explained below:

  • setTitle(title: String): Builder - Sets the title to be displayed in the Authentication Prompt.
  • setSubTitle(subtitle: String?): Builder - Sets the subtitle of the Authentication Prompt.
  • setDescription(description: String?): Builder - Sets the description for the Authentication Prompt.
  • setAuthenticationLevel(authenticationLevel: AuthenticationLevel): Builder - Sets the authentication level, more on this can be found here
  • setDeviceCredentialFallback(enableDeviceCredentialFallback: Boolean): Builder - Enables/disables device credential fallback.
  • setNegativeButtonText(negativeButtonText: String): Builder - Sets the negative button text, used only when the device credential fallback is disabled (or) the authentication level is not set to AuthenticationLevel.DEVICE_CREDENTIAL.
  • build(): LocalAuthenticationOptions - Constructs the LocalAuthenticationOptions instance.

AuthenticationLevel Enum Values

AuthenticationLevel is an enum that defines the different levels of authentication strength required for local authentication mechanisms.

Enum Values:

  • STRONG: Any biometric (e.g., fingerprint, iris, or face) on the device that meets or exceeds the requirements for Class 3 (formerly Strong).
  • WEAK: Any biometric (e.g., fingerprint, iris, or face) on the device that meets or exceeds the requirements for Class 2 (formerly Weak), as defined by the Android CDD.
  • DEVICE_CREDENTIAL: The non-biometric credential used to secure the device (i.e., PIN, pattern, or password).

Handling Credentials Manager exceptions

In the event that something happened while trying to save or retrieve the credentials, a CredentialsManagerException will be thrown. These are some of the expected failure scenarios:

  • Invalid Credentials format or values. e.g. when it's missing the access_token, the id_token or the expires_at values.
  • Tokens have expired but no refresh_token is available to perform a refresh credentials request.
  • Device's Lock Screen security settings have changed (e.g. the PIN code was changed). Even when hasCredentials returns true, the encryption keys will be deemed invalid and until saveCredentials is called again it won't be possible to decrypt any previously existing content, since they keys used back then are not the same as the new ones.
  • Device is not compatible with some of the algorithms required by the SecureCredentialsManager class. This is considered a catastrophic event and might happen when the OEM has modified the Android ROM removing some of the officially included algorithms. Nevertheless, it can be checked in the exception instance itself by calling isDeviceIncompatible. By doing so you can decide the fallback for storing the credentials, such as using the regular CredentialsManager.

You can access the code property of the CredentialsManagerException to understand why the operation with CredentialsManager has failed and the message property of the CredentialsManagerException would give you a description of the exception.

Starting from version 3.0.0 you can even pass the exception to a when expression and handle the exception accordingly in your app's logic as shown in the below code snippet:

when(credentialsManagerException) {
    CredentialsManagerException.NO_CREDENTIALS - > {
        // handle no credentials scenario
    }

    CredentialsManagerException.NO_REFRESH_TOKEN - > {
        // handle no refresh token scenario
    }

    CredentialsManagerException.STORE_FAILED - > {
        // handle store failed scenario
    }
    // ... similarly for other error codes
}

Passkeys

User should have a custom domain configured and passkey grant-type enabled in the Auth0 dashboard to use passkeys.

To sign up a user with passkey

// Using Coroutines 
try {
    val challenge = authenticationApiClient.signupWithPasskey(
        "{user-data}",
        "{realm}"
    ).await()
    
    //Use CredentialManager to create public key credentials
    val request = CreatePublicKeyCredentialRequest(
        Gson().toJson(challenge.authParamsPublicKey)
    )

    val result = credentialManager.createCredential(requireContext(), request)

    val authRequest = Gson().fromJson(
        (result as CreatePublicKeyCredentialResponse).registrationResponseJson,
        PublicKeyCredentials::class.java
    )

    val userCredential = authenticationApiClient.signinWithPasskey(
        challenge.authSession, authRequest, "{realm}"
    )
        .validateClaims()
        .await()
} catch (e: CreateCredentialException) {
} catch (exception: AuthenticationException) {
}
Using Java
 authenticationAPIClient.signupWithPasskey("{user-data}", "{realm}")
        .start(new Callback<PasskeyRegistrationChallenge, AuthenticationException>() {
    @Override
    public void onSuccess(PasskeyRegistrationChallenge result) {
        CreateCredentialRequest request =
                new CreatePublicKeyCredentialRequest(new Gson().toJson(result.getAuthParamsPublicKey()));
        credentialManager.createCredentialAsync(getContext(),
                request,
                cancellationSignal,
                <executor>,
                new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
                    @Override
                    public void onResult(CreateCredentialResponse createCredentialResponse) {
                        PublicKeyCredentials credentials = new Gson().fromJson(
                                ((CreatePublicKeyCredentialResponse) createCredentialResponse).getRegistrationResponseJson(),
                                PublicKeyCredentials.class);

                        authenticationAPIClient.signinWithPasskey(result.getAuthSession(),
                                        credentials, "{realm}")
                                .start(new Callback<Credentials, AuthenticationException>() {
                                    @Override
                                    public void onSuccess(Credentials result) {}

                                    @Override
                                    public void onFailure(@NonNull AuthenticationException error) {}
                                });
                    }
                    @Override
                    public void onError(@NonNull CreateCredentialException e) {}
                });
    }

    @Override
    public void onFailure(@NonNull AuthenticationException error) {}
});

To sign in a user with passkey

//Using coroutines
try {

    val challenge =
        authenticationApiClient.passkeyChallenge("{realm}")
            .await()

    //Use CredentialManager to create public key credentials
    val request = GetPublicKeyCredentialOption(Gson().toJson(challenge.authParamsPublicKey))
    val getCredRequest = GetCredentialRequest(
        listOf(request)
    )
    val result = credentialManager.getCredential(requireContext(), getCredRequest)
    when (val credential = result.credential) {
        is PublicKeyCredential -> {
            val authRequest = Gson().fromJson(
                credential.authenticationResponseJson,
                PublicKeyCredentials::class.java
            )
            val userCredential = authenticationApiClient.signinWithPasskey(
                challenge.authSession,
                authRequest,
                "{realm}"
            )
                .validateClaims()
                .await()
        }

        else -> {}
    }
} catch (e: GetCredentialException) {
} catch (exception: AuthenticationException) {
}
Using Java
authenticationAPIClient.passkeyChallenge("realm")
                .start(new Callback<PasskeyChallenge, AuthenticationException>() {
    @Override
    public void onSuccess(PasskeyChallenge result) {
        GetPublicKeyCredentialOption option = new GetPublicKeyCredentialOption(new Gson().toJson(result.getAuthParamsPublicKey()));
        GetCredentialRequest request = new GetCredentialRequest(List.of(option));
        credentialManager.getCredentialAsync(getContext(),
                request,
                cancellationSignal,
                <executor>,
                new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
                    @Override
                    public void onResult(GetCredentialResponse getCredentialResponse) {
                        Credential credential = getCredentialResponse.getCredential();
                        if (credential instanceof PublicKeyCredential) {
                            String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
                            PublicKeyCredentials publicKeyCredentials = new Gson().fromJson(
                                    responseJson,
                                    PublicKeyCredentials.class
                            );
                            authenticationAPIClient.signinWithPasskey(result.getAuthSession(), publicKeyCredentials,"{realm}")
                                    .start(new Callback<Credentials, AuthenticationException>() {
                                        @Override
                                        public void onSuccess(Credentials result) {}

                                        @Override
                                        public void onFailure(@NonNull AuthenticationException error) {}
                                    });
                        }
                    }

                    @Override
                    public void onError(@NonNull GetCredentialException e) {}
                });
    }

    @Override
    public void onFailure(@NonNull AuthenticationException error) {}
});

Points to be Noted:

Passkeys are supported only on devices that run Android 9 (API level 28) or higher. To use passkeys ,user needs to add support for Digital Asset Links.

Bot Protection

If you are using the Bot Protection feature and performing database login/signup via the Authentication API, you need to handle the AuthenticationException#isVerificationRequired() error. It indicates that the request was flagged as suspicious and an additional verification step is necessary to log the user in. That verification step is web-based, so you need to use Universal Login to complete it.

val email = "[email protected]"
val password = "a secret password"
val realm = "my-database-connection"

val authentication = AuthenticationAPIClient(account)
authentication.login(email, password, realm).validateClaims()
    .start(object: Callback<Credentials, AuthenticationException> {
        override fun onFailure(exception: AuthenticationException) {
            if (exception.isVerificationRequired()) {
                val params = mapOf("login_hint" to email) // So the user doesn't have to type it again
                WebAuthProvider.login(account)
                    .withConnection(realm)
                    .withParameters(params)
                    .start(LoginActivity.this, object: Callback<Credentials, AuthenticationException> {
                        // You might already have a Callback instance defined

                        override fun onFailure(exception: AuthenticationException) {
                            // Handle error
                        }

                        override fun onSuccess(credentials: Credentials) {
                            // Handle WebAuth success
                        }
                    })
            }
            // Handle other errors
        }

        override fun onSuccess(credentials: Credentials) {
            // Handle API success
        }
    })
Using Java
final String email = "[email protected]";
final String password = "a secret password";
final String realm = "my-database-connection";

AuthenticationAPIClient authentication = new AuthenticationAPIClient(account);
authentication.login(email, password, realm).validateClaims()
        .start(new Callback<Credentials, AuthenticationException>() {

            @Override
            public void onSuccess(@Nullable Credentials payload) {
                // Handle API success
            }

            @Override
            public void onFailure(@NonNull AuthenticationException error) {
                if (error.isVerificationRequired()){
                    Map<String, Object> params = new HashMap<>();
                    params.put("login_hint", email); // So the user doesn't have to type it again
                    WebAuthProvider.login(account)
                            .withConnection(realm)
                            .withParameters(params)
                            .start(LoginActivity.this, new AuthCallback() {
                                // You might already have an AuthCallback instance defined

                                @Override
                                public void onFailure(@NonNull Dialog dialog) {
                                    // Error dialog available
                                }

                                @Override
                                public void onFailure(AuthenticationException exception) {
                                    // Error
                                }

                                @Override
                                public void onSuccess(@NonNull Credentials credentials) {
                                    // Handle WebAuth success
                                }
                            });
                }
            }
        });

In the case of signup, you can add an additional parameter to make the user land directly on the signup page:

val params = mapOf(
    "login_hint" to email, 
    "screen_hint" to "signup"
)
Using Java
params.put("login_hint", email);
params.put("screen_hint", "signup");

Management API

The client provides a few methods to interact with the Users Management API.

Create a new instance passing the account and an access token with the Management API audience and the right scope:

val users = UsersAPIClient(account, "api access token")
Using Java
Auth0 account = Auth0.getInstance("client id", "domain");
UsersAPIClient users = new UsersAPIClient(account, "api token");

Link users

users
    .link("primary user id", "secondary user token")
    .start(object: Callback<List<UserIdentity>, ManagementException> {
    
        override fun onFailure(exception: ManagementException) { }
    
        override fun onSuccess(identities: List<UserIdentity>) { }
    })
Using coroutines
try {
    val identities = users
        .link("primary user id", "secondary user token")
        .await()
    println(identities)
} catch (e: ManagementException) {
    e.printStacktrace()
}
Using Java
users
    .link("primary user id", "secondary user token")
    .start(new Callback<List<UserIdentity>, ManagementException>() {
        @Override
        public void onSuccess(List<UserIdentity> payload) {
            //Got the updated identities! Accounts linked.
        }

        @Override
        public void onFailure(@NonNull ManagementException error) {
            //Error!
        }
    });

Unlink users

users
    .unlink("primary user id", "secondary user id", "secondary provider")
    .start(object: Callback<List<UserIdentity>, ManagementException> {
    
        override fun onFailure(exception: ManagementException) { }
    
        override fun onSuccess(identities: List<UserIdentity>) { }
    })
Using coroutines
try {
    val identities = users
        .unlink("primary user id", "secondary user id", "secondary provider")
        .await()
    println(identities)
} catch (e: ManagementException) {
    e.printStacktrace()
}
Using Java
users
    .unlink("primary user id", "secondary user id", "secondary provider")
    .start(new Callback<List<UserIdentity>, ManagementException>() {
        @Override
        public void onSuccess(List<UserIdentity> payload) {
            //Got the updated identities! Accounts linked.
        }

        @Override
        public void onFailure(@NonNull ManagementException error) {
            //Error!
        }
    });

Get User Profile

users
    .getProfile("user id")
    .start(object: Callback<UserProfile, ManagementException> {
    
        override fun onFailure(exception: ManagementException) { }
    
        override fun onSuccess(identities: UserProfile) { }
    })
Using coroutines
try {
    val user = users
        .getProfile("user id")
        .await()
    println(user)
} catch (e: ManagementException) {
    e.printStacktrace()
}
Using Java
users
    .getProfile("user id")
    .start(new Callback<UserProfile, ManagementException>() {
        @Override
        public void onSuccess(@Nullable UserProfile payload) {
            //Profile received
        }

        @Override
        public void onFailure(@NonNull ManagementException error) {
            //Error!
        }
    });

Update User Metadata

val metadata = mapOf(
    "name" to listOf("My", "Name", "Is"),
    "phoneNumber" to "1234567890"
)

users
    .updateMetadata("user id", metadata)
    .start(object: Callback<UserProfile, ManagementException> {
    
        override fun onFailure(exception: ManagementException) { }
    
        override fun onSuccess(identities: UserProfile) { }
    })
Using coroutines
val metadata = mapOf(
    "name" to listOf("My", "Name", "Is"),
    "phoneNumber" to "1234567890"
)

try {
    val user = users
        .updateMetadata("user id", metadata)
        .await()
    println(user)
} catch (e: ManagementException) {
    e.printStacktrace()
}
Using Java
Map<String, Object> metadata = new HashMap<>();
metadata.put("name", Arrays.asList("My", "Name", "Is"));
metadata.put("phoneNumber", "1234567890");

users
    .updateMetadata("user id", metadata)
    .start(new Callback<UserProfile, ManagementException>() {
        @Override
        public void onSuccess(@Nullable UserProfile payload) {
            //User Metadata updated
        }

        @Override
        public void onFailure(@NonNull ManagementException error) {
            //Error!
        }
    });

In all the cases, the user ID parameter is the unique identifier of the auth0 account instance. i.e. in google-oauth2|123456789 it would be the part after the '|' pipe: 123456789.

Token Validation

The ID token received as part of the authentication flow is should be verified following the OpenID Connect specification.

If you are a user of Auth0 Private Cloud with "Custom Domains" still on the legacy behavior, you need to override the expected issuer to match your Auth0 domain before starting the authentication.

The validation is done automatically for Web Authentication

val account = Auth0.getInstance("{YOUR_CLIENT_ID}", "{YOUR_CUSTOM_DOMAIN}")

WebAuthProvider.login(account)
    .withIdTokenVerificationIssuer("https://{YOUR_AUTH0_DOMAIN}/")
    .start(this, callback)

For Authentication Client, the method validateClaims() has to be called to enable it.

val auth0 = Auth0.getInstance("YOUR_CLIENT_ID", "YOUR_DOMAIN")
val client = AuthenticationAPIClient(auth0)
client
     .login("{username or email}", "{password}", "{database connection name}")
     .validateClaims()
     .withIdTokenVerificationIssuer("https://{YOUR_AUTH0_DOMAIN}/")
     .start(object : Callback<Credentials, AuthenticationException> {
         override fun onSuccess(result: Credentials) { }
         override fun onFailure(error: AuthenticationException) { }
    })
Using coroutines
val auth0 = Auth0.getInstance("YOUR_CLIENT_ID", "YOUR_DOMAIN")
val client = AuthenticationAPIClient(auth0)

try {
    val credentials = client
        .login("{username or email}", "{password}", "{database connection name}")
        .validateClaims()
        .withIdTokenVerificationIssuer("https://{YOUR_AUTH0_DOMAIN}/")
        .await()
    println(credentials)
} catch (e: AuthenticationException) {
    e.printStacktrace()
}
Using Java
Auth0 auth0 = Auth0.getInstance("client id", "domain");
AuthenticationAPIClient client = new AuthenticationAPIClient(account);
client
   .login("{username or email}", "{password}", "{database connection name}")
   .validateClaims()
   .withIdTokenVerificationIssuer("https://{YOUR_AUTH0_DOMAIN}/")
   .start(new Callback<Credentials, AuthenticationException>() {
       @Override
       public void onSuccess(@Nullable Credentials payload) {
           //Logged in!
       }

       @Override
       public void onFailure(@NonNull AuthenticationException error) {
           //Error!
       }
   });

Organizations

Organizations is a set of features that provide better support for developers who build and maintain SaaS and Business-to-Business (B2B) applications. Note that Organizations is currently only available to customers on our Enterprise and Startup subscription plans.

Log in to an organization

WebAuthProvider.login(account)
    .withOrganization(organizationIdOrName)
    .start(this, callback)

Accept user invitations

Users can be invited to your organization via a link. Tapping on the invitation link should open your app. Since invitations links are https only, is recommended that your app supports Android App Links. In Enable Android App Links Support, you will find how to make the Auth0 server publish the Digital Asset Links file required by your application.

When your app gets opened by an invitation link, grab the invitation URL from the received Intent (e.g. in onCreate or onNewIntent) and pass it to .withInvitationUrl():

getIntent()?.data?.let {
    WebAuthProvider.login(account)
        .withInvitationUrl(invitationUrl)
        .start(this, callback)
}
Using Java
if (getIntent() != null && getIntent().getData() != null) {
    WebAuthProvider.login(account)
        .withInvitationUrl(getIntent().getData())
        .start(this, callback);
}

If the URL doesn't contain the expected values, an error will be raised through the provided callback.

Networking client customization

This library provides the ability to customize the behavior of the networking client for common configurations, as well the ability to define and use your own networking client implementation.

The Auth0 class can be configured with a NetworkingClient, which will be used when making requests. You can configure the default client with custom timeout values, any headers that should be sent on all requests, and whether to log request/response info (for non-production debugging purposes only). For more advanced configuration, you can provide your own implementation of NetworkingClient.

Timeout configuration

val netClient = DefaultClient(
    connectTimeout = 30,
    readTimeout = 30
)

val account = Auth0.getInstance("{YOUR_CLIENT_ID}", "{YOUR_DOMAIN}")
account.networkingClient = netClient
Using Java
DefaultClient netClient = new DefaultClient(
   connectTimeout = 30,
   readTimeout = 30
);
Auth0 account = Auth0.getInstance("client id", "domain");
account.networkingClient = netClient;

Logging configuration

val netClient = DefaultClient(
    enableLogging = true
)

val account = Auth0.getInstance("{YOUR_CLIENT_ID}", "{YOUR_DOMAIN}")
account.networkingClient = netClient
Using Java
DefaultClient netClient = new DefaultClient(
    enableLogging = true
);
Auth0 account = Auth0.getInstance("client id", "domain");
account.networkingClient = netClient;

Set additional headers for all requests

val netClient = DefaultClient(
    defaultHeaders = mapOf("{HEADER-NAME}" to "{HEADER-VALUE}")
)

val account = Auth0.getInstance("{YOUR_CLIENT_ID}", "{YOUR_DOMAIN}")
account.networkingClient = netClient
Using Java
Map<String, String> defaultHeaders = new HashMap<>();
defaultHeaders.put("{HEADER-NAME}", "{HEADER-VALUE}");

DefaultClient netClient = new DefaultClient(
    defaultHeaders = defaultHeaders
);
Auth0 account = Auth0.getInstance("client id", "domain");
account.networkingClient = netClient;

Advanced configuration

For more advanced configuration of the networking client, you can provide a custom implementation of NetworkingClient. This may be useful when you wish to reuse your own networking client, configure a proxy, etc.

class CustomNetClient : NetworkingClient {
    override fun load(url: String, options: RequestOptions): ServerResponse {
         // Create and execute the request to the specified URL with the given options
         val response = // ...

         // Return a ServerResponse from the received response data
         return ServerResponse(responseCode, responseBody, responseHeaders)
    }
}

val account = Auth0.getInstance("{YOUR_CLIENT_ID}", "{YOUR_DOMAIN}")
account.networkingClient = CustomNetClient()
Using Java
class CustomNetClient extends NetworkingClient {
   @Override
   public ServerResponse load(String url) {
      // Create and execute the request to the specified URL with the given options
      ServerResponse response = // ...

      // Return a ServerResponse from the received response data
      return ServerResponse(responseCode, responseBody, responseHeaders)
   }  
};

Auth0 account = Auth0.getInstance("client id", "domain");
account.networkingClient = new CustomNetClient();

Unit testing with JUnit 4 or JUnit 5

Handling Method getMainLooper in android.os.Looper not mocked errors

Your unit tests might break with Caused by: java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked due to the Looper being used internally by this library. There are two options to handle this:

  1. Use Robolectric Shadows - see this test for an example
  2. If your project does not use Robolectric and uses JUnit 4, you can create a Rule that you can add to your unit test:
import com.auth0.android.request.internal.CommonThreadSwitcher
import com.auth0.android.request.internal.ThreadSwitcher
import org.junit.rules.TestWatcher
import org.junit.runner.Description

public class CommonThreadSwitcherRule : TestWatcher() {
    override fun starting(description: Description) {
        super.starting(description)
        CommonThreadSwitcher.getInstance().setDelegate(object : ThreadSwitcher {
            override fun mainThread(runnable: Runnable) {
                runnable.run()
            }

            override fun backgroundThread(runnable: Runnable) {
                runnable.run()
            }
        })
    }

    override fun finished(description: Description) {
        super.finished(description)
        CommonThreadSwitcher.getInstance().setDelegate(null)
    }
}

See this test for an example of it being used.

  1. If you use JUnit 5 then you can create an Extension similar to the previous Rule for JUnit 4:
import com.auth0.android.request.internal.CommonThreadSwitcher
import com.auth0.android.request.internal.ThreadSwitcher
import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.BeforeEachCallback
import org.junit.jupiter.api.extension.ExtensionContext

class CommonThreadSwitcherExtension : BeforeEachCallback, AfterEachCallback {

    override fun beforeEach(context: ExtensionContext?) {
        CommonThreadSwitcher.getInstance().setDelegate(object : ThreadSwitcher {
            override fun mainThread(runnable: Runnable) {
                runnable.run()
            }

            override fun backgroundThread(runnable: Runnable) {
                runnable.run()
            }
        })
    }

    override fun afterEach(context: ExtensionContext?) {
        CommonThreadSwitcher.getInstance().setDelegate(null)
    }

}

Handling SSL errors

You might encounter errors similar to PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target, which means that you need to set up your unit tests in a way that ignores or trusts all SSL certificates. In that case, you may have to implement your own NetworkingClient so that you can supply your own SSLSocketFactory and X509TrustManager, and use that in creating your Auth0 object. See the DefaultClient class for an idea on how to extend NetworkingClient.

Proguard

The rules should be applied automatically if your application is using minifyEnabled = true. If you want to include them manually check the proguard directory. By default you should at least use the following files:

  • proguard-okio.pro
  • proguard-gson.pro
  • proguard-jetpack.pro