package com.auth0.client.auth; import com.auth0.json.auth.UserInfo; import com.auth0.net.*; import com.auth0.utils.Asserts; import com.fasterxml.jackson.core.type.TypeReference; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor; import okhttp3.logging.HttpLoggingInterceptor.Level; /** * Class that provides an implementation of some of the Authentication and Authorization API methods defined in https://auth0.com/docs/api/authentication. * To begin create a new instance of {@link #AuthAPI(String, String, String)} using the tenant domain, client id and client secret. */ @SuppressWarnings("WeakerAccess") public class AuthAPI { private static final String KEY_CLIENT_ID = "client_id"; private static final String KEY_CLIENT_SECRET = "client_secret"; private static final String KEY_GRANT_TYPE = "grant_type"; private static final String KEY_USERNAME = "username"; private static final String KEY_PASSWORD = "password"; private static final String KEY_AUDIENCE = "audience"; private static final String KEY_EMAIL = "email"; private static final String KEY_CONNECTION = "connection"; private static final String KEY_TOKEN = "token"; private static final String KEY_REFRESH_TOKEN = "refresh_token"; private static final String PATH_OAUTH = "oauth"; private static final String PATH_TOKEN = "token"; private static final String PATH_DBCONNECTIONS = "dbconnections"; private static final String PATH_REVOKE = "revoke"; private final OkHttpClient client; private final String clientId; private final String clientSecret; private final HttpUrl baseUrl; private final TelemetryInterceptor telemetry; private final HttpLoggingInterceptor logging; /** * Create a new instance with the given tenant's domain, client id and client secret. These values can be obtained at https://manage.auth0.com/#/clients/{YOUR_CLIENT_ID}/settings. * * @param domain tenant's domain. * @param clientId the client's id. * @param clientSecret the client's secret. */ public AuthAPI(String domain, String clientId, String clientSecret) { Asserts.assertNotNull(domain, "domain"); Asserts.assertNotNull(clientId, "client id"); Asserts.assertNotNull(clientSecret, "client secret"); this.baseUrl = createBaseUrl(domain); if (baseUrl == null) { throw new IllegalArgumentException("The domain had an invalid format and couldn't be parsed as an URL."); } this.clientId = clientId; this.clientSecret = clientSecret; telemetry = new TelemetryInterceptor(); logging = new HttpLoggingInterceptor(); logging.setLevel(Level.NONE); client = new OkHttpClient.Builder() .addInterceptor(logging) .addInterceptor(telemetry) .build(); } /** * Avoid sending Telemetry data in every request to the Auth0 servers. */ public void doNotSendTelemetry() { telemetry.setEnabled(false); } /** * Whether to enable or not the current HTTP Logger for every Request, Response and other sensitive information. * * @param enabled whether to enable the HTTP logger or not. */ public void setLoggingEnabled(boolean enabled) { logging.setLevel(enabled ? Level.BODY : Level.NONE); } //Visible for Testing OkHttpClient getClient() { return client; } //Visible for Testing HttpUrl getBaseUrl() { return baseUrl; } private HttpUrl createBaseUrl(String domain) { String url = domain; if (!domain.startsWith("https://") && !domain.startsWith("http://")) { url = "https://" + domain; } return HttpUrl.parse(url); } /** * Creates an instance of the {@link AuthorizeUrlBuilder} with the given redirect url. * i.e.: * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * String url = auth.authorizeUrl("https://me.auth0.com/callback") * .withConnection("facebook") * .withAudience("https://api.me.auth0.com/users") * .withScope("openid contacts") * .withState("my-custom-state") * .build(); * } * </pre> * * @param redirectUri the redirect_uri value to set, white-listed in the client settings. Must be already URL Encoded. * @return a new instance of the {@link AuthorizeUrlBuilder} to configure. */ public AuthorizeUrlBuilder authorizeUrl(String redirectUri) { Asserts.assertValidUrl(redirectUri, "redirect uri"); return AuthorizeUrlBuilder.newInstance(baseUrl, clientId, redirectUri); } /** * Creates an instance of the {@link LogoutUrlBuilder} with the given return-to url. * i.e.: * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * String url = auth.logoutUrl("https://me.auth0.com/home", true) * .useFederated(true) * .withAccessToken("A9CvPwFojaBIA9CvI"); * } * </pre> * * @param returnToUrl the redirect_uri value to set, white-listed in the client settings. Must be already URL Encoded. * @param setClientId whether the client_id value must be set or not. This affects the white-list that the Auth0's Dashboard uses to validate the returnTo url. * @return a new instance of the {@link LogoutUrlBuilder} to configure. */ public LogoutUrlBuilder logoutUrl(String returnToUrl, boolean setClientId) { Asserts.assertValidUrl(returnToUrl, "return to url"); return LogoutUrlBuilder.newInstance(baseUrl, clientId, returnToUrl, setClientId); } /** * Request the user information related to the access token. * i.e.: * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * UserInfo result = auth.userInfo("A9CvPwFojaBIA9CvI").execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param accessToken a valid access token belonging to an API signed with RS256 algorithm and containing the scope 'openid'. * @return a Request to execute. */ public Request<UserInfo> userInfo(String accessToken) { Asserts.assertNotNull(accessToken, "access token"); String url = baseUrl .newBuilder() .addPathSegment("userinfo") .build() .toString(); CustomRequest<UserInfo> request = new CustomRequest<>(client, url, "GET", new TypeReference<UserInfo>() { }); request.addHeader("Authorization", "Bearer " + accessToken); return request; } /** * Request a password reset for the given email and database connection. The response will always be successful even if * there's no user associated to the given email for that database connection. * i.e.: * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * auth.resetPassword("me@auth0.com", "db-connection").execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param email the email associated to the database user. * @param connection the database connection where the user was created. * @return a Request to execute. */ public Request resetPassword(String email, String connection) { Asserts.assertNotNull(email, "email"); Asserts.assertNotNull(connection, "connection"); String url = baseUrl .newBuilder() .addPathSegment(PATH_DBCONNECTIONS) .addPathSegment("change_password") .build() .toString(); VoidRequest request = new VoidRequest(client, url, "POST"); request.addParameter(KEY_CLIENT_ID, clientId); request.addParameter(KEY_EMAIL, email); request.addParameter(KEY_CONNECTION, connection); return request; } /** * Creates a sign up request with the given credentials and database connection. * "Requires Username" option must be turned on in the Connection's configuration first. * i.e.: * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * Map<String, String> fields = new HashMap<String, String>(); * fields.put("age", "25); * fields.put("city", "Buenos Aires"); * auth.signUp("me@auth0.com", "myself", "topsecret", "db-connection") * .setCustomFields(fields) * .execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param email the desired user's email. * @param username the desired user's username. * @param password the desired user's password. * @param connection the database connection where the user is going to be created. * @return a Request to configure and execute. */ public SignUpRequest signUp(String email, String username, String password, String connection) { Asserts.assertNotNull(username, "username"); VoidRequest request = (VoidRequest) this.signUp(email, password, connection); request.addParameter(KEY_USERNAME, username); return request; } /** * Creates a sign up request with the given credentials and database connection. * i.e.: * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * Map<String, String> fields = new HashMap<String, String>(); * fields.put("age", "25); * fields.put("city", "Buenos Aires"); * auth.signUp("me@auth0.com", "topsecret", "db-connection") * .setCustomFields(fields) * .execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param email the desired user's email. * @param password the desired user's password. * @param connection the database connection where the user is going to be created. * @return a Request to configure and execute. */ public SignUpRequest signUp(String email, String password, String connection) { Asserts.assertNotNull(email, "email"); Asserts.assertNotNull(password, "password"); Asserts.assertNotNull(connection, "connection"); String url = baseUrl .newBuilder() .addPathSegment(PATH_DBCONNECTIONS) .addPathSegment("signup") .build() .toString(); VoidRequest request = new VoidRequest(client, url, "POST"); request.addParameter(KEY_CLIENT_ID, clientId); request.addParameter(KEY_EMAIL, email); request.addParameter(KEY_PASSWORD, password); request.addParameter(KEY_CONNECTION, connection); return request; } /** * Creates a log in request using the 'Password' grant and the given credentials. * i.e.: * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * TokenHolder result = auth.login("me@auth0.com", "topsecret") * .setScope("openid email nickname") * .execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param emailOrUsername the identity of the user. * @param password the password of the user. * @return a Request to configure and execute. */ public AuthRequest login(String emailOrUsername, String password) { Asserts.assertNotNull(emailOrUsername, "email or username"); Asserts.assertNotNull(password, "password"); String url = baseUrl .newBuilder() .addPathSegment(PATH_OAUTH) .addPathSegment(PATH_TOKEN) .build() .toString(); TokenRequest request = new TokenRequest(client, url); request.addParameter(KEY_CLIENT_ID, clientId); request.addParameter(KEY_CLIENT_SECRET, clientSecret); request.addParameter(KEY_GRANT_TYPE, "password"); request.addParameter(KEY_USERNAME, emailOrUsername); request.addParameter(KEY_PASSWORD, password); return request; } /** * Creates a log in request using the 'Password Realm' grant and the given credentials. * Default used realm and audience are defined in the "API Authorization Settings" in the account's advanced settings in the Auth0 Dashboard. * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * TokenHolder result = auth.login("me@auth0.com", "topsecret", "my-realm") * .setAudience("https://myapi.me.auth0.com/users") * .execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param emailOrUsername the identity of the user. * @param password the password of the user. * @param realm the realm to use. * @return a Request to configure and execute. */ public AuthRequest login(String emailOrUsername, String password, String realm) { Asserts.assertNotNull(emailOrUsername, "email or username"); Asserts.assertNotNull(password, "password"); Asserts.assertNotNull(realm, "realm"); String url = baseUrl .newBuilder() .addPathSegment(PATH_OAUTH) .addPathSegment(PATH_TOKEN) .build() .toString(); TokenRequest request = new TokenRequest(client, url); request.addParameter(KEY_CLIENT_ID, clientId); request.addParameter(KEY_CLIENT_SECRET, clientSecret); request.addParameter(KEY_GRANT_TYPE, "http://auth0.com/oauth/grant-type/password-realm"); request.addParameter(KEY_USERNAME, emailOrUsername); request.addParameter(KEY_PASSWORD, password); request.addParameter("realm", realm); return request; } /** * Creates a request to get a Token for the given audience using the 'Client Credentials' grant. * Default used realm is defined in the "API Authorization Settings" in the account's advanced settings in the Auth0 Dashboard. * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * TokenHolder result = auth.requestToken("https://myapi.me.auth0.com/users") * .setRealm("my-realm") * .execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param audience the audience of the API to request access to. * @return a Request to configure and execute. */ public AuthRequest requestToken(String audience) { Asserts.assertNotNull(audience, "audience"); String url = baseUrl .newBuilder() .addPathSegment(PATH_OAUTH) .addPathSegment(PATH_TOKEN) .build() .toString(); TokenRequest request = new TokenRequest(client, url); request.addParameter(KEY_CLIENT_ID, clientId); request.addParameter(KEY_CLIENT_SECRET, clientSecret); request.addParameter(KEY_GRANT_TYPE, "client_credentials"); request.addParameter(KEY_AUDIENCE, audience); return request; } /** * Creates a request to revoke an existing Refresh Token. * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * auth.revokeToken("ej2E8zNEzjrcSD2edjaE") * .execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param refreshToken the refresh token to revoke. * @return a Request to execute. */ public Request<Void> revokeToken(String refreshToken) { Asserts.assertNotNull(refreshToken, "refresh token"); String url = baseUrl .newBuilder() .addPathSegment(PATH_OAUTH) .addPathSegment(PATH_REVOKE) .build() .toString(); VoidRequest request = new VoidRequest(client, url, "POST"); request.addParameter(KEY_CLIENT_ID, clientId); request.addParameter(KEY_CLIENT_SECRET, clientSecret); request.addParameter(KEY_TOKEN, refreshToken); return request; } /** * Creates a request to renew the authentication and get fresh new credentials using a valid Refresh Token and the 'refresh_token' grant. * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * TokenHolder result = auth.renewAuth("ej2E8zNEzjrcSD2edjaE") * .execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param refreshToken the refresh token to use to get fresh new credentials. * @return a Request to configure and execute. */ public AuthRequest renewAuth(String refreshToken) { Asserts.assertNotNull(refreshToken, "refresh token"); String url = baseUrl .newBuilder() .addPathSegment(PATH_OAUTH) .addPathSegment(PATH_TOKEN) .build() .toString(); TokenRequest request = new TokenRequest(client, url); request.addParameter(KEY_CLIENT_ID, clientId); request.addParameter(KEY_CLIENT_SECRET, clientSecret); request.addParameter(KEY_GRANT_TYPE, "refresh_token"); request.addParameter(KEY_REFRESH_TOKEN, refreshToken); return request; } /** * Creates a request to exchange the code obtained in the /authorize call using the 'Authorization Code' grant. * <pre> * {@code * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne"); * try { * TokenHolder result = auth.exchangeCode("SnWoFLMzApDskr", "https://me.auth0.com/callback") * .setScope("openid name nickname") * .execute(); * } catch (Auth0Exception e) { * //Something happened * } * } * </pre> * * @param code the authorization code received from the /authorize call. * @param redirectUri the redirect uri sent on the /authorize call. * @return a Request to configure and execute. */ public AuthRequest exchangeCode(String code, String redirectUri) { Asserts.assertNotNull(code, "code"); Asserts.assertNotNull(redirectUri, "redirect uri"); String url = baseUrl .newBuilder() .addPathSegment(PATH_OAUTH) .addPathSegment(PATH_TOKEN) .build() .toString(); TokenRequest request = new TokenRequest(client, url); request.addParameter(KEY_CLIENT_ID, clientId); request.addParameter(KEY_CLIENT_SECRET, clientSecret); request.addParameter(KEY_GRANT_TYPE, "authorization_code"); request.addParameter("code", code); request.addParameter("redirect_uri", redirectUri); return request; } }