com.auth0.authentication.AuthenticationAPIClient.java Source code

Java tutorial

Introduction

Here is the source code for com.auth0.authentication.AuthenticationAPIClient.java

Source

/*
 * AuthenticationAPIClient.java
 *
 * Copyright (c) 2015 Auth0 (http://auth0.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.auth0.authentication;

import com.auth0.Auth0;
import com.auth0.authentication.result.Credentials;
import com.auth0.authentication.result.DatabaseUser;
import com.auth0.authentication.result.Delegation;
import com.auth0.authentication.result.UserProfile;
import com.auth0.request.AuthenticationRequest;
import com.auth0.request.ParameterizableRequest;
import com.auth0.request.Request;
import com.auth0.request.internal.RequestFactory;
import com.auth0.util.Telemetry;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.OkHttpClient;

import java.util.Map;

import static com.auth0.authentication.ParameterBuilder.GRANT_TYPE_AUTHORIZATION_CODE;
import static com.auth0.authentication.ParameterBuilder.GRANT_TYPE_PASSWORD;

/**
 * API client for Auth0 Authentication API.
 *
 * @see <a href="https://auth0.com/docs/auth-api">Auth API docs</a>
 */
public class AuthenticationAPIClient {

    private static final String DEFAULT_DB_CONNECTION = "Username-Password-Authentication";
    private static final String SMS_CONNECTION = "sms";
    private static final String EMAIL_CONNECTION = "email";
    private static final String USERNAME_KEY = "username";
    private static final String PASSWORD_KEY = "password";
    private static final String EMAIL_KEY = "email";
    private static final String PHONE_NUMBER_KEY = "phone_number";
    private static final String USER_ID_KEY = "user_id";
    private static final String CLIENT_ID_KEY = "clientID";
    private static final String DELEGATION_PATH = "delegation";
    private static final String ACCESS_TOKEN_PATH = "access_token";
    private static final String SIGN_UP_PATH = "signup";
    private static final String DB_CONNECTIONS_PATH = "dbconnections";
    private static final String CHANGE_PASSWORD_PATH = "change_password";
    private static final String UNLINK_PATH = "unlink";
    private static final String PASSWORDLESS_PATH = "passwordless";
    private static final String START_PATH = "start";
    private static final String OAUTH_PATH = "oauth";
    private static final String TOKEN_PATH = "token";
    private static final String RESOURCE_OWNER_PATH = "ro";
    private static final String TOKEN_INFO_PATH = "tokeninfo";
    private static final String OAUTH_CODE_KEY = "code";
    private static final String OAUTH_CODE_VERIFIER_KEY = "code_verifier";
    private static final String REDIRECT_URI_KEY = "redirect_uri";

    private final Auth0 auth0;
    private final OkHttpClient client;
    private final ObjectMapper mapper;
    private final RequestFactory factory;

    private String defaultDatabaseConnection = DEFAULT_DB_CONNECTION;

    /**
     * Creates a new API client instance providing Auth0 account info.
     *
     * @param auth0 account information
     */
    public AuthenticationAPIClient(Auth0 auth0) {
        this(auth0, new OkHttpClient(), new ObjectMapper());
    }

    private AuthenticationAPIClient(Auth0 auth0, OkHttpClient client, ObjectMapper mapper) {
        this.auth0 = auth0;
        this.client = client;
        this.mapper = mapper;
        this.factory = new RequestFactory();
        final Telemetry telemetry = auth0.getTelemetry();
        if (telemetry != null) {
            factory.setClientInfo(telemetry.getValue());
        }
    }

    public String getClientId() {
        return auth0.getClientId();
    }

    public String getBaseURL() {
        return auth0.getDomainUrl();
    }

    /**
     * Set the value of 'User-Agent' header for every request to Auth0 Authentication API
     *
     * @param userAgent value to send in every request to Auth0
     */
    public void setUserAgent(String userAgent) {
        factory.setUserAgent(userAgent);
    }

    /**
     * Set the default Auth0 database connection name used. By default is 'Username-Password-Authentication'
     *
     * @param defaultDatabaseConnection name to use on every login with DB connection
     */
    public void setDefaultDatabaseConnection(String defaultDatabaseConnection) {
        this.defaultDatabaseConnection = defaultDatabaseConnection;
    }

    /**
     * Log in a user with email/username and password using a DB connection.
     * Example usage:
     * <pre><code>
     * client.login("{username or email}", "{password}")
     *      .setConnection("{database connection name}")
     *      .start(new BaseCallback<Credentials>() {
     *          {@literal}Override
     *          public void onSuccess(Credentials payload) { }
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) { }
     *      });
     * </code></pre>
     *
     * @param usernameOrEmail of the user depending of the type of DB connection
     * @param password        of the user
     * @return a request to configure and start that will yield {@link Credentials}
     */
    public AuthenticationRequest login(String usernameOrEmail, String password) {
        Map<String, Object> requestParameters = ParameterBuilder.newAuthenticationBuilder()
                .set(USERNAME_KEY, usernameOrEmail).set(PASSWORD_KEY, password).setGrantType(GRANT_TYPE_PASSWORD)
                .asDictionary();
        return loginWithResourceOwner(requestParameters);
    }

    /**
     * Log in a user with a OAuth 'access_token' of a Identity Provider like Facebook or Twitter using <a href="https://auth0.com/docs/auth-api#!#post--oauth-access_token">'\oauth\access_token' endpoint</a>
     * Example usage:
     * <pre><code>
     * client.loginWithOAuthAccessToken("{token}", "{connection name}")
     *      .setConnection("second-database")
     *      .start(new BaseCallback<Credentials>() {
     *          {@literal}Override
     *          public void onSuccess(Credentials payload) { }
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) { }
     *      });
     * </code></pre>
     *
     * @param token      obtained from the IdP
     * @param connection that will be used to authenticate the user, e.g. 'facebook'
     * @return a request to configure and start that will yield {@link Credentials}
     */
    public AuthenticationRequest loginWithOAuthAccessToken(String token, String connection) {
        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(OAUTH_PATH)
                .addPathSegment(ACCESS_TOKEN_PATH).build();

        Map<String, Object> parameters = ParameterBuilder.newAuthenticationBuilder().setClientId(getClientId())
                .setConnection(connection).setAccessToken(token).asDictionary();

        return factory.authenticationPOST(url, client, mapper).addAuthenticationParameters(parameters);
    }

    /**
     * Log in a user using a phone number and a verification code received via SMS (Part of passwordless login flow)
     * Example usage:
     * <pre><code>
     * client.loginWithPhoneNumber("{phone number}", "{code}")
     *      .start(new BaseCallback<Credentials>() {
     *          {@literal}Override
     *          public void onSuccess(Credentials payload) { }
     *
     *          {@literal}@Override
     *          public void onFailure(Auth0Exception error) { }
     *      });
     * </code></pre>
     *
     * @param phoneNumber      where the user received the verification code
     * @param verificationCode sent by Auth0 via SMS
     * @return a request to configure and start that will yield {@link Credentials}
     */
    public AuthenticationRequest loginWithPhoneNumber(String phoneNumber, String verificationCode) {
        Map<String, Object> parameters = ParameterBuilder.newAuthenticationBuilder().set(USERNAME_KEY, phoneNumber)
                .set(PASSWORD_KEY, verificationCode).setGrantType(GRANT_TYPE_PASSWORD).setClientId(getClientId())
                .setConnection(SMS_CONNECTION).asDictionary();
        return loginWithResourceOwner(parameters);
    }

    /**
     * Log in a user using an email and a verification code received via Email (Part of passwordless login flow)
     * Example usage:
     * <pre><code>
     * client.loginWithEmail("{email}", "{code}")
     *      .start(new BaseCallback<Credentials>() {
     *          {@literal}Override
     *          public void onSuccess(Credentials payload) { }
     *
     *          {@literal}@Override
     *          public void onFailure(Auth0Exception error) { }
     *      });
     * </code></pre>
     *
     * @param email            where the user received the verification code
     * @param verificationCode sent by Auth0 via Email
     * @return a request to configure and start that will yield {@link Credentials}
     */
    public AuthenticationRequest loginWithEmail(String email, String verificationCode) {
        Map<String, Object> parameters = ParameterBuilder.newAuthenticationBuilder().set(USERNAME_KEY, email)
                .set(PASSWORD_KEY, verificationCode).setGrantType(GRANT_TYPE_PASSWORD).setClientId(getClientId())
                .setConnection(EMAIL_CONNECTION).asDictionary();
        return loginWithResourceOwner(parameters);
    }

    /**
     * Fetch the token information from Auth0
     * Example usage:
     * <pre><code>
     * client.tokenInfo("{id_token}")
     *      .start(new BaseCallback<UserProfile>() {
     *          {@literal}Override
     *          public void onSuccess(UserProfile payload) { }
     *
     *          {@literal}@Override
     *          public void onFailure(Auth0Exception error) { }
     *      });
     * </code></pre>
     *
     * @param idToken used to fetch it's information
     * @return a request to start
     */
    public Request<UserProfile> tokenInfo(String idToken) {
        return profileRequest().addParameter(ParameterBuilder.ID_TOKEN_KEY, idToken);
    }

    /**
     * Creates a user in a DB connection using <a href="https://auth0.com/docs/auth-api#!#post--dbconnections-signup">'/dbconnections/signup' endpoint</a>
     * Example usage:
     * <pre><code>
     * client.createUser("{email}", "{password}", "{username}")
     *      .setConnection("{connection name}")
     *      .start(new BaseCallback<DatabaseUser>() {
     *          {@literal}Override
     *          public void onSuccess(DatabaseUser payload) { }
     *
     *          {@literal}@Override
     *          public void onFailure(Auth0Exception error) { }
     *      });
     * </code></pre>
     *
     * @param email    of the user and must be non null
     * @param password of the user and must be non null
     * @param username of the user and must be non null
     * @return a request to start
     */
    public DatabaseConnectionRequest<DatabaseUser> createUser(String email, String password, String username) {
        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(DB_CONNECTIONS_PATH)
                .addPathSegment(SIGN_UP_PATH).build();

        Map<String, Object> parameters = ParameterBuilder.newBuilder().set(USERNAME_KEY, username)
                .set(EMAIL_KEY, email).set(PASSWORD_KEY, password).setConnection(defaultDatabaseConnection)
                .setClientId(getClientId()).asDictionary();
        final ParameterizableRequest<DatabaseUser> request = factory.POST(url, client, mapper, DatabaseUser.class)
                .addParameters(parameters);
        return new DatabaseConnectionRequest<>(request);
    }

    /**
     * Creates a user in a DB connection using <a href="https://auth0.com/docs/auth-api#!#post--dbconnections-signup">'/dbconnections/signup' endpoint</a>
     * Example usage:
     * <pre><code>
     * client.createUser("{email}", "{password}")
     *      .setConnection("{connection name}")
     *      .start(new BaseCallback<DatabaseUser>() {
     *          {@literal}Override
     *          public void onSuccess(DatabaseUser payload) { }
     *
     *          {@literal}@Override
     *          public void onFailure(Auth0Exception error) { }
     *      });
     * </code></pre>
     *
     * @param email    of the user and must be non null
     * @param password of the user and must be non null
     * @return a request to start
     */
    public DatabaseConnectionRequest<DatabaseUser> createUser(String email, String password) {
        return createUser(email, password, null);
    }

    /**
     * Creates a user in a DB connection using <a href="https://auth0.com/docs/auth-api#!#post--dbconnections-signup">'/dbconnections/signup' endpoint</a>
     * and then logs in
     * Example usage:
     * <pre><code>
     * client.signUp("{email}", "{password}", "{username}")
     *      .setConnection("{connection name}")
     *      .start(new BaseCallback<Credentials>() {
     *          {@literal}Override
     *          public void onSuccess(Credentials payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @param email    of the user and must be non null
     * @param password of the user and must be non null
     * @param username of the user and must be non null
     * @return a request to configure and start that will yield {@link Credentials}
     */
    public SignUpRequest signUp(String email, String password, String username) {
        final DatabaseConnectionRequest<DatabaseUser> createUserRequest = createUser(email, password, username);
        final AuthenticationRequest authenticationRequest = login(email, password);
        return new SignUpRequest(createUserRequest, authenticationRequest);
    }

    /**
     * Creates a user in a DB connection using <a href="https://auth0.com/docs/auth-api#!#post--dbconnections-signup">'/dbconnections/signup' endpoint</a>
     * and then logs in
     * Example usage:
     * <pre><code>
     * client.signUp("{email}", "{password}")
     *      .setConnection("{connection name}")
     *      .start(new BaseCallback<Credentials>() {
     *          {@literal}Override
     *          public void onSuccess(Credentials payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @param email    of the user and must be non null
     * @param password of the user and must be non null
     * @return a request to configure and start that will yield {@link Credentials}
     */
    public SignUpRequest signUp(String email, String password) {
        DatabaseConnectionRequest<DatabaseUser> createUserRequest = createUser(email, password);
        final AuthenticationRequest authenticationRequest = login(email, password);
        return new SignUpRequest(createUserRequest, authenticationRequest);
    }

    /**
     * Request a change password using <a href="https://auth0.com/docs/auth-api#!#post--dbconnections-change_password">'/dbconnections/change_password'</a>
     * Example usage:
     * <pre><code>
     * client.requestChangePassword("{email}")
     *      .start(new BaseCallback<Void>() {
     *          {@literal}Override
     *          public void onSuccess(Void payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @param email of the user that changes the password. It's also where the email will be sent with the link to perform the change password.
     * @return a request to configure and start
     */
    public DatabaseConnectionRequest<Void> requestChangePassword(String email) {
        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(DB_CONNECTIONS_PATH)
                .addPathSegment(CHANGE_PASSWORD_PATH).build();

        final Map<String, Object> parameters = ParameterBuilder.newBuilder().set(EMAIL_KEY, email)
                .setClientId(getClientId()).setConnection(defaultDatabaseConnection).asDictionary();
        final ParameterizableRequest<Void> request = factory.POST(url, client, mapper).addParameters(parameters);
        return new DatabaseConnectionRequest<>(request);
    }

    /**
     * Performs a <a href="https://auth0.com/docs/auth-api#!#post--delegation">delegation</a> request that will yield a new Auth0 'id_token'
     * Example usage:
     * <pre><code>
     * client.delegationWithIdToken("{id token}")
     *      .start(new BaseCallback<Delegation>() {
     *          {@literal}Override
     *          public void onSuccess(Delegation payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @param idToken issued by Auth0 for the user. The token must not be expired.
     * @return a request to configure and start
     */
    public DelegationRequest<Delegation> delegationWithIdToken(String idToken) {
        ParameterizableRequest<Delegation> request = delegation(Delegation.class)
                .addParameter(ParameterBuilder.ID_TOKEN_KEY, idToken);

        return new DelegationRequest<>(request).setApiType(DelegationRequest.DEFAULT_API_TYPE);
    }

    /**
     * Performs a <a href="https://auth0.com/docs/auth-api#!#post--delegation">delegation</a> request that will yield a new Auth0 'id_token'.
     * Check our <a href="https://auth0.com/docs/refresh-token">refresh token</a> docs for more information
     * Example usage:
     * <pre><code>
     * client.delegationWithRefreshToken("{refresh token}")
     *      .start(new BaseCallback<Delegation>() {
     *          {@literal}Override
     *          public void onSuccess(Delegation payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @param refreshToken issued by Auth0 for the user when using the 'offline_access' scope when logging in.
     * @return a request to configure and start
     */
    public DelegationRequest<Delegation> delegationWithRefreshToken(String refreshToken) {
        ParameterizableRequest<Delegation> request = delegation(Delegation.class)
                .addParameter(ParameterBuilder.REFRESH_TOKEN_KEY, refreshToken);

        return new DelegationRequest<>(request).setApiType(DelegationRequest.DEFAULT_API_TYPE);
    }

    /**
     * Performs a <a href="https://auth0.com/docs/auth-api#!#post--delegation">delegation</a> request that will yield a delegation token.
     * Example usage:
     * <pre><code>
     * client.delegationWithIdToken("{id token}", "{app type, e.g. firebase}")
     *      .start(new BaseCallback<Map<String, Object>>() {
     *          {@literal}Override
     *          public void onSuccess(Map<String, Object> payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @param idToken issued by Auth0 for the user. The token must not be expired.
     * @param apiType the delegation 'api_type' parameter
     * @return a request to configure and start
     */
    public DelegationRequest<Map<String, Object>> delegationWithIdToken(String idToken, String apiType) {
        ParameterizableRequest<Map<String, Object>> request = delegation()
                .addParameter(ParameterBuilder.ID_TOKEN_KEY, idToken);

        return new DelegationRequest<>(request).setApiType(apiType);
    }

    /**
     * Unlink a user identity calling <a href="https://auth0.com/docs/auth-api#!#post--unlink">'/unlink'</a> endpoint
     * Example usage:
     * <pre><code>
     * client.unlink("{auth0 user id}", "{user access token}")
     *      .start(new BaseCallback<Void>() {
     *          {@literal}Override
     *          public void onSuccess(Void payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @param userId      of the identity to unlink
     * @param accessToken of the main identity obtained after login
     * @return a request to start
     */
    public Request<Void> unlink(String userId, String accessToken) {
        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(UNLINK_PATH).build();

        final Map<String, Object> parameters = ParameterBuilder.newBuilder().setAccessToken(accessToken)
                .set(CLIENT_ID_KEY, getClientId()).set(USER_ID_KEY, userId).asDictionary();

        return factory.POST(url, client, mapper).addParameters(parameters);
    }

    /**
     * Start a passwordless flow with <a href="https://auth0.com/docs/auth-api#!#post--with_email">Email</a>
     * Example usage:
     * <pre><code>
     * client.passwordlessWithEmail("{email}", PasswordlessType.CODE)
     *      .start(new BaseCallback<Void>() {
     *          {@literal}Override
     *          public void onSuccess(Void payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @param email            that will receive a verification code to use for login
     * @param passwordlessType indicate whether the email should contain a code, link or magic link (android & iOS)
     * @return a request to configure and start
     */
    public ParameterizableRequest<Void> passwordlessWithEmail(String email, PasswordlessType passwordlessType) {
        final Map<String, Object> parameters = ParameterBuilder.newBuilder().set(EMAIL_KEY, email)
                .setSend(passwordlessType).setConnection(EMAIL_CONNECTION).asDictionary();

        return passwordless().addParameters(parameters);
    }

    /**
     * Start a passwordless flow with <a href="https://auth0.com/docs/auth-api#!#post--with_sms">SMS</a>
     * Example usage:
     * <pre><code>
     * client.passwordlessWithSms("{phone number}", PasswordlessType.CODE)
     *      .start(new BaseCallback<Void>() {
     *          {@literal}Override
     *          public void onSuccess(Void payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @param phoneNumber      where an SMS with a verification code will be sent
     * @param passwordlessType indicate whether the SMS should contain a code, link or magic link (android & iOS)
     * @return a request to configure and start
     */
    public ParameterizableRequest<Void> passwordlessWithSMS(String phoneNumber, PasswordlessType passwordlessType) {
        final Map<String, Object> parameters = ParameterBuilder.newBuilder().set(PHONE_NUMBER_KEY, phoneNumber)
                .setSend(passwordlessType).setConnection(SMS_CONNECTION).asDictionary();
        return passwordless().addParameters(parameters);
    }

    /**
     * Performs a custom <a href="https://auth0.com/docs/auth-api#!#post--delegation">delegation</a> request that will
     * yield a delegation token.
     * Example usage:
     * <pre><code>
     * client.delegation()
     *      .addParameter("api_type", "firebase")
     *      .start(new BaseCallback<Map<String, Object>>() {
     *          {@literal}Override
     *          public void onSuccess(Map<String, Object> payload) {}
     *
     *          {@literal}Override
     *          public void onFailure(Auth0Exception error) {}
     *      });
     * </code></pre>
     *
     * @return a request to configure and start
     */
    public ParameterizableRequest<Map<String, Object>> delegation() {
        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(DELEGATION_PATH).build();

        final Map<String, Object> parameters = ParameterBuilder.newBuilder().setClientId(getClientId())
                .setGrantType(ParameterBuilder.GRANT_TYPE_JWT).asDictionary();
        return factory.rawPOST(url, client, mapper).addParameters(parameters);
    }

    protected <T> ParameterizableRequest<T> delegation(Class<T> clazz) {
        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(DELEGATION_PATH).build();

        final Map<String, Object> parameters = ParameterBuilder.newBuilder().setClientId(getClientId())
                .setGrantType(ParameterBuilder.GRANT_TYPE_JWT).asDictionary();

        return factory.POST(url, client, mapper, clazz).addParameters(parameters);
    }

    /**
     * Start a custom passwordless flow
     *
     * @return a request to configure and start
     */
    public ParameterizableRequest<Void> passwordless() {
        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(PASSWORDLESS_PATH)
                .addPathSegment(START_PATH).build();

        final Map<String, Object> parameters = ParameterBuilder.newBuilder().setClientId(getClientId())
                .asDictionary();
        return factory.POST(url, client, mapper).addParameters(parameters);
    }

    /**
     * Fetch the user's profile after it's authenticated by a login request.
     * If the login request fails, the returned request will fail
     *
     * @param authenticationRequest that will authenticate a user with Auth0 and return a {@link Credentials}
     * @return a {@link ProfileRequest} that first logins and the fetches the profile
     */
    public ProfileRequest getProfileAfter(AuthenticationRequest authenticationRequest) {
        final ParameterizableRequest<UserProfile> profileRequest = profileRequest();
        return new ProfileRequest(authenticationRequest, profileRequest);
    }

    private AuthenticationRequest loginWithResourceOwner(Map<String, Object> parameters) {
        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(OAUTH_PATH)
                .addPathSegment(RESOURCE_OWNER_PATH).build();

        final Map<String, Object> requestParameters = ParameterBuilder.newBuilder().setClientId(getClientId())
                .setConnection(defaultDatabaseConnection).addAll(parameters).asDictionary();
        return factory.authenticationPOST(url, client, mapper).addAuthenticationParameters(requestParameters);
    }

    private ParameterizableRequest<UserProfile> profileRequest() {
        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(TOKEN_INFO_PATH).build();

        return factory.POST(url, client, mapper, UserProfile.class);
    }

    /**
     * Fetch the token information from Auth0, using the authorization_code grant type
     *
     * @param authorizationCode the authorization code received from the /authorize call.
     * @param codeVerifier      the code verifier used when requesting a code to /authorize.
     * @param redirectUri       the uri to redirect after a successful request.
     * @return a request to configure and start
     */
    public AuthenticationRequest token(String authorizationCode, String codeVerifier, String redirectUri) {
        Map<String, Object> parameters = ParameterBuilder.newBuilder().setClientId(getClientId())
                .setGrantType(GRANT_TYPE_AUTHORIZATION_CODE).set(OAUTH_CODE_KEY, authorizationCode)
                .set(OAUTH_CODE_VERIFIER_KEY, codeVerifier).set(REDIRECT_URI_KEY, redirectUri).asDictionary();

        HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(OAUTH_PATH)
                .addPathSegment(TOKEN_PATH).build();

        return factory.authenticationPOST(url, client, mapper).addAuthenticationParameters(parameters);
    }
}