Java tutorial
/* * Lock.java * * Copyright (c) 2016 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.android.lock; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.StyleRes; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import com.auth0.android.Auth0; import com.auth0.android.authentication.ParameterBuilder; import com.auth0.android.lock.LockCallback.LockEvent; import com.auth0.android.lock.internal.configuration.Options; import com.auth0.android.lock.internal.configuration.Theme; import com.auth0.android.lock.provider.AuthResolver; import com.auth0.android.lock.utils.CustomField; import com.auth0.android.lock.utils.LockException; import com.auth0.android.provider.AuthHandler; import com.auth0.android.util.Telemetry; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; public class Lock { private static final String TAG = Lock.class.getSimpleName(); private final LockCallback callback; private final Options options; /** * Listens to LockActivity broadcasts and fires the correct action on the LockCallback. */ private final BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent data) { processEvent(data); } }; private Lock(Options options, LockCallback callback) { this.options = options; this.callback = callback; } /** * Lock.Options holds the configuration used in the Auth0 Authentication API. * * @return the Lock.Options for this Lock instance. */ public Options getOptions() { return options; } /** * Creates a new Lock.Builder instance with the given account and callback. * * @param account details to use against the Auth0 Authentication API. * @param callback that will receive the authentication results. * @return a new Lock.Builder instance. */ @SuppressWarnings("unused") public static Builder newBuilder(@NonNull Auth0 account, @NonNull LockCallback callback) { return new Lock.Builder(account, callback); } /** * Creates a new Lock.Builder instance with the given callback. The account information * will be retrieved from the String resources file (strings.xml) using * the keys 'com_auth0_client_id' and 'com_auth0_domain'. * * @param callback that will receive the authentication results. * @return a new Lock.Builder instance. */ @SuppressWarnings("unused") public static Builder newBuilder(@NonNull LockCallback callback) { //noinspection ConstantConditions return newBuilder(null, callback); } /** * Builds a new intent to launch LockActivity with the previously configured options * * @param activity a valid Activity context * @return the intent to which the user has to call startActivity or startActivityForResult */ @SuppressWarnings("unused") public Intent newIntent(Activity activity) { Intent lockIntent = new Intent(activity, LockActivity.class); lockIntent.putExtra(Constants.OPTIONS_EXTRA, options); return lockIntent; } /** * Should be called on the Activity holding the Lock instance's OnDestroy method, as it * ensures the correct Lock lifecycle handling. * * @param activity a valid Activity context */ @SuppressWarnings("unused") public void onDestroy(Activity activity) { LocalBroadcastManager.getInstance(activity).unregisterReceiver(this.receiver); } private void initialize(Activity activity) { IntentFilter filter = new IntentFilter(); filter.addAction(Constants.AUTHENTICATION_ACTION); filter.addAction(Constants.SIGN_UP_ACTION); filter.addAction(Constants.CANCELED_ACTION); filter.addAction(Constants.INVALID_CONFIGURATION_ACTION); LocalBroadcastManager.getInstance(activity).registerReceiver(this.receiver, filter); } private void processEvent(Intent data) { String action = data.getAction(); switch (action) { case Constants.AUTHENTICATION_ACTION: Log.v(TAG, "AUTHENTICATION action received in our BroadcastReceiver"); if (data.getExtras().containsKey(Constants.ERROR_EXTRA)) { callback.onError(new LockException(data.getStringExtra(Constants.ERROR_EXTRA))); } else { callback.onEvent(LockEvent.AUTHENTICATION, data); } break; case Constants.SIGN_UP_ACTION: Log.v(TAG, "SIGN_UP action received in our BroadcastReceiver"); callback.onEvent(LockEvent.SIGN_UP, data); break; case Constants.CANCELED_ACTION: Log.v(TAG, "CANCELED action received in our BroadcastReceiver"); callback.onEvent(LockEvent.CANCELED, new Intent()); break; case Constants.INVALID_CONFIGURATION_ACTION: Log.v(TAG, "INVALID_CONFIGURATION_ACTION action received in our BroadcastReceiver"); callback.onError(new LockException(data.getStringExtra(Constants.ERROR_EXTRA))); break; } } /** * Helper Builder to generate the Lock.Options to use on the Auth0 Authentication. */ public static class Builder { private static final String TAG = Builder.class.getSimpleName(); private Options options; private LockCallback callback; /** * Creates a new Lock.Builder instance with the given account and callback. * * @param account details to use against the Auth0 Authentication API. * @param callback that will receive the authentication results. */ public Builder(Auth0 account, LockCallback callback) { HashMap<String, Object> defaultParams = new HashMap<>( ParameterBuilder.newAuthenticationBuilder().setDevice(Build.MODEL).asDictionary()); this.callback = callback; options = new Options(); options.setAccount(account); options.setAuthenticationParameters(defaultParams); } /** * Finishes the construction of the Lock.Options and generates a new Lock instance * with those Lock.Options. * * @param activity a valid Activity context * @return a new Lock instance configured as in the Builder. */ public Lock build(@NonNull Activity activity) { if (options.getAccount() == null) { Log.w(TAG, "com.auth0.android.Auth0 account details not defined. Trying to create it from the String resources."); try { options.setAccount(new Auth0(activity)); } catch (IllegalArgumentException e) { throw new IllegalStateException("Missing Auth0 account information.", e); } } if (callback == null) { Log.e(TAG, "You need to specify the callback object to receive the Authentication result."); throw new IllegalStateException("Missing callback."); } if (!options.allowForgotPassword() && !options.allowLogIn() && !options.allowSignUp()) { throw new IllegalStateException( "You disabled all the Lock screens (LogIn/SignUp/ForgotPassword). Please enable at least one."); } if (options.initialScreen() == InitialScreen.LOG_IN && !options.allowLogIn()) { throw new IllegalStateException( "You chose LOG_IN as the initial screen but you have also disabled that screen."); } if (options.initialScreen() == InitialScreen.SIGN_UP && !options.allowSignUp()) { throw new IllegalStateException( "You chose SIGN_UP as the initial screen but you have also disabled that screen."); } if (options.initialScreen() == InitialScreen.FORGOT_PASSWORD && !options.allowForgotPassword()) { throw new IllegalStateException( "You chose FORGOT_PASSWORD as the initial screen but you have also disabled that screen."); } Log.v(TAG, "Lock instance created"); if (options.getAccount().getTelemetry() != null) { Log.v(TAG, String.format("Using Telemetry %s (%s) and Library %s", Constants.LIBRARY_NAME, com.auth0.android.lock.BuildConfig.VERSION_NAME, com.auth0.android.auth0.BuildConfig.VERSION_NAME)); options.getAccount() .setTelemetry(new Telemetry(Constants.LIBRARY_NAME, com.auth0.android.lock.BuildConfig.VERSION_NAME, com.auth0.android.auth0.BuildConfig.VERSION_NAME)); } final Lock lock = new Lock(options, callback); lock.initialize(activity); return lock; } /** * Whether to use the Browser for Authentication with Identity Providers or the inner WebView. * * @param useBrowser or WebView. By default, the Authentication flow will use the Browser. * @return the current Builder instance * @deprecated This method has been deprecated since Google is no longer supporting WebViews to perform login. */ @Deprecated public Builder useBrowser(boolean useBrowser) { options.setUseBrowser(useBrowser); return this; } /** * Whether to use implicit grant or code grant when performing calls to /authorize. * Default is {@code false} * * @param useImplicitGrant if Lock will use implicit grant instead of code grant. * @return the current Builder instance */ public Builder useImplicitGrant(boolean useImplicitGrant) { options.setUsePKCE(!useImplicitGrant); return this; } /** * Whether the LockActivity can be closed when pressing the Back key or not. * * @param closable or not. By default, the LockActivity is not closable. * @return the current builder instance */ public Builder closable(boolean closable) { options.setClosable(closable); return this; } /** * Customize Lock's appearance. * * @param theme to use. * @return the current Builder instance */ private Builder withTheme(@NonNull Theme theme) { options.withTheme(theme); return this; } /** * Additional Authentication parameters can be set to use with different Identity Providers. * * @param authenticationParameters a non-null Map containing the parameters as Key-Values * @return the current builder instance */ public Builder withAuthenticationParameters(@NonNull Map<String, Object> authenticationParameters) { options.setAuthenticationParameters(new HashMap<>(authenticationParameters)); return this; } /** * Locally filters the Auth0 Connections that are shown in the login widgets. * * @param connections a non-null List containing the allowed Auth0 Connections. * @return the current builder instance */ public Builder allowedConnections(@NonNull List<String> connections) { options.setConnections(connections); return this; } /** * Username style to use in the Login and Sign Up text fields. Defaults to the Dashboard * configuration of "requires_username". * * @param style a valid UsernameStyle. * @return the current builder instance */ public Builder withUsernameStyle(@UsernameStyle int style) { options.setUsernameStyle(style); return this; } /** * Auth Button size to use when Social connections are available. If Social * is the only connection type it will default to the BIG size. If Database or * Enterprise are present and there's only one Social connection, the button will use the * BIG size. In the rest of the cases, it will use SMALL size. * * @param style a valid AuthButtonSize. * @return the current builder instance */ public Builder withAuthButtonSize(@AuthButtonSize int style) { options.setAuthButtonSize(style); return this; } /** * Authentication Style to use with the given strategy or connection name. It will override any lock defaults. * * @param connectionName to use this style with * @param style a valid Style with the Auth0.BackgroundColor, Auth0.Logo and Auth0.Name values defined. * @return the current builder instance */ public Builder withAuthStyle(@NonNull String connectionName, @StyleRes int style) { options.withAuthStyle(connectionName, style); return this; } /** * Decide which screen is going to show first when launching the Lock Activity. * * @param screen a valid InitialScreen. * @return the current builder instance */ public Builder initialScreen(@InitialScreen int screen) { options.setInitialScreen(screen); return this; } /** * Whether to show the Log In screen or not. It can be enabled/disabled locally, regardless the Dashboard configuration. * * @return the current builder instance */ public Builder allowLogIn(boolean allow) { options.setAllowLogIn(allow); return this; } /** * Whether to show the Sign Up screen or not. It can be enabled/disabled locally, regardless the Dashboard configuration. * * @return the current builder instance */ public Builder allowSignUp(boolean allow) { options.setAllowSignUp(allow); return this; } /** * Whether to show the Forgot Password screen or not. It can be enabled/disabled locally, regardless the Dashboard configuration. * * @return the current builder instance */ public Builder allowForgotPassword(boolean allow) { options.setAllowForgotPassword(allow); return this; } /** * Whether if the submit button will display a label or just an icon. * * @param useLabeledSubmitButton or icon. By default it will use icon. * @return the current builder instance */ public Builder useLabeledSubmitButton(boolean useLabeledSubmitButton) { options.setUseLabeledSubmitButton(useLabeledSubmitButton); return this; } /** * Change the connection name to use on the Database authentication flow. * Defaults to the first Database connection found. * * @param connectionName Must exist in the Application configuration on the Dashboard. * @return the current builder instance */ public Builder setDefaultDatabaseConnection(String connectionName) { options.useDatabaseConnection(connectionName); return this; } /** * Whether to login after a successful sign up callback. Defaults to true. * * @param login after sign up or not * @return the current builder instance */ public Builder loginAfterSignUp(boolean login) { options.setLoginAfterSignUp(login); return this; } /** * Uses the given AuthHandlers to query for AuthProviders on a new authentication request. * * @param handlers that Lock will query for AuthProviders. * @return the current builder instance */ public Builder withAuthHandlers(@NonNull AuthHandler... handlers) { AuthResolver.setAuthHandlers(Arrays.asList(handlers)); return this; } /** * Displays a second screen with the specified custom fields during sign up. * Each field must have a unique key. * * @param customFields the custom fields to display in the sign up flow. * @return the current builder instance */ public Builder withSignUpFields(List<CustomField> customFields) { final List<CustomField> withoutDuplicates = removeDuplicatedKeys(customFields); options.setCustomFields(withoutDuplicates); return this; } /** * Choose a custom Privacy Policy URL to access when the user clicks the link on the Sign Up form. * The default value is 'https://auth0.com/privacy' * * @param url a valid url to use. * @return the current builder instance */ public Builder setPrivacyURL(@NonNull String url) { options.setPrivacyURL(url); return this; } /** * Choose a custom Terms of Service URL to access when the user clicks the link on the Sign Up form. * The default value is 'https://auth0.com/terms' * * @param url a valid url to use. * @return the current builder instance */ public Builder setTermsURL(@NonNull String url) { options.setTermsURL(url); return this; } /** * Prompts the user to accept the Privacy Policy and Terms of Service before signing up. * The default value is false. * * @param mustAcceptTerms whether the user needs to accept the terms before sign up or not. * @return the current builder instance */ public Builder setMustAcceptTerms(boolean mustAcceptTerms) { options.setMustAcceptTerms(mustAcceptTerms); return this; } /** * Sets the Connection Scope to request when performing an Authentication with the given Connection. * * @param connectionName to which specify the scopes. * @param scope recognized by this specific authentication provider. * @return the current builder instance */ public Builder withConnectionScope(@NonNull String connectionName, @NonNull String... scope) { StringBuilder sb = new StringBuilder(); for (String s : scope) { sb.append(s.trim()).append(","); } if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); options.withConnectionScope(connectionName, sb.toString()); } return this; } private List<CustomField> removeDuplicatedKeys(List<CustomField> customFields) { int originalSize = customFields.size(); final List<CustomField> withoutDuplicates = new ArrayList<>(); Set<String> keySet = new HashSet<>(); for (CustomField field : customFields) { if (!keySet.contains(field.getKey())) { withoutDuplicates.add(field); } keySet.add(field.getKey()); } if (originalSize != withoutDuplicates.size()) { Log.w(TAG, "Some of the Custom Fields had a duplicate key and have been removed."); } return withoutDuplicates; } } }