com.google.api.client.auth.oauth2.AuthorizationCodeFlow Maven / Gradle / Ivy
Show all versions of google-oauth-client Show documentation
/*
* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.api.client.auth.oauth2;
import com.google.api.client.auth.oauth2.Credential.AccessMethod;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpExecuteInterceptor;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.UrlEncodedContent;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.Base64;
import com.google.api.client.util.Beta;
import com.google.api.client.util.Data;
import com.google.api.client.util.Clock;
import com.google.api.client.util.Joiner;
import com.google.api.client.util.Lists;
import com.google.api.client.util.Preconditions;
import com.google.api.client.util.store.DataStore;
import com.google.api.client.util.store.DataStoreFactory;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import static com.google.api.client.util.Strings.isNullOrEmpty;
/**
* Thread-safe OAuth 2.0 authorization code flow that manages and persists end-user credentials.
*
*
* This is designed to simplify the flow in which an end-user authorizes the application to access
* their protected data, and then the application has access to their data based on an access token
* and a refresh token to refresh that access token when it expires.
*
*
*
* The first step is to call {@link #loadCredential(String)} based on the known user ID to check if
* the end-user's credentials are already known. If not, call {@link #newAuthorizationUrl()} and
* direct the end-user's browser to an authorization page. The web browser will then redirect to the
* redirect URL with a {@code "code"} query parameter which can then be used to request an access
* token using {@link #newTokenRequest(String)}. Finally, use
* {@link #createAndStoreCredential(TokenResponse, String)} to store and obtain a credential for
* accessing protected resources.
*
*
* @since 1.7
* @author Yaniv Inbar
*/
public class AuthorizationCodeFlow {
/**
* Method of presenting the access token to the resource server (for example
* {@link BearerToken#authorizationHeaderAccessMethod}).
*/
private final AccessMethod method;
/** HTTP transport. */
private final HttpTransport transport;
/** JSON factory. */
private final JsonFactory jsonFactory;
/** Token server encoded URL. */
private final String tokenServerEncodedUrl;
/**
* Client authentication or {@code null} for none (see
* {@link TokenRequest#setClientAuthentication(HttpExecuteInterceptor)}).
*/
private final HttpExecuteInterceptor clientAuthentication;
/** Client identifier. */
private final String clientId;
/** Authorization server encoded URL. */
private final String authorizationServerEncodedUrl;
/** The Proof Key for Code Exchange (PKCE) or {@code null} if this flow should not use PKCE. */
private final PKCE pkce;
/** Credential persistence store or {@code null} for none. */
@Beta
@Deprecated
private final CredentialStore credentialStore;
/** Stored credential data store or {@code null} for none. */
@Beta
private final DataStore credentialDataStore;
/** HTTP request initializer or {@code null} for none. */
private final HttpRequestInitializer requestInitializer;
/** Clock passed along to Credential. */
private final Clock clock;
/** Collection of scopes. */
private final Collection scopes;
/** Credential created listener or {@code null} for none. */
private final CredentialCreatedListener credentialCreatedListener;
/** Refresh listeners provided by the client. */
private final Collection refreshListeners;
/**
* @param method method of presenting the access token to the resource server (for example
* {@link BearerToken#authorizationHeaderAccessMethod})
* @param transport HTTP transport
* @param jsonFactory JSON factory
* @param tokenServerUrl token server URL
* @param clientAuthentication client authentication or {@code null} for none (see
* {@link TokenRequest#setClientAuthentication(HttpExecuteInterceptor)})
* @param clientId client identifier
* @param authorizationServerEncodedUrl authorization server encoded URL
*
* @since 1.14
*/
public AuthorizationCodeFlow(AccessMethod method,
HttpTransport transport,
JsonFactory jsonFactory,
GenericUrl tokenServerUrl,
HttpExecuteInterceptor clientAuthentication,
String clientId,
String authorizationServerEncodedUrl) {
this(new Builder(method,
transport,
jsonFactory,
tokenServerUrl,
clientAuthentication,
clientId,
authorizationServerEncodedUrl));
}
/**
* @param builder authorization code flow builder
*
* @since 1.14
*/
protected AuthorizationCodeFlow(Builder builder) {
method = Preconditions.checkNotNull(builder.method);
transport = Preconditions.checkNotNull(builder.transport);
jsonFactory = Preconditions.checkNotNull(builder.jsonFactory);
tokenServerEncodedUrl = Preconditions.checkNotNull(builder.tokenServerUrl).build();
clientAuthentication = builder.clientAuthentication;
clientId = Preconditions.checkNotNull(builder.clientId);
authorizationServerEncodedUrl =
Preconditions.checkNotNull(builder.authorizationServerEncodedUrl);
requestInitializer = builder.requestInitializer;
credentialStore = builder.credentialStore;
credentialDataStore = builder.credentialDataStore;
scopes = Collections.unmodifiableCollection(builder.scopes);
clock = Preconditions.checkNotNull(builder.clock);
credentialCreatedListener = builder.credentialCreatedListener;
refreshListeners = Collections.unmodifiableCollection(builder.refreshListeners);
pkce = builder.pkce;
}
/**
* Returns a new instance of an authorization code request URL.
*
*
* This is a builder for an authorization web page to allow the end user to authorize the
* application to access their protected resources and that returns an authorization code. It uses
* the {@link #getAuthorizationServerEncodedUrl()}, {@link #getClientId()}, and
* {@link #getScopes()}. Sample usage:
*
*
*
private AuthorizationCodeFlow flow;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String url = flow.newAuthorizationUrl().setState("xyz")
.setRedirectUri("https://client.example.com/rd").build();
response.sendRedirect(url);
}
*
*/
public AuthorizationCodeRequestUrl newAuthorizationUrl() {
AuthorizationCodeRequestUrl url = new AuthorizationCodeRequestUrl(authorizationServerEncodedUrl, clientId);
url.setScopes(scopes);
if (pkce != null) {
url.setCodeChallenge(pkce.getChallenge());
url.setCodeChallengeMethod(pkce.getChallengeMethod());
}
return url;
}
/**
* Returns a new instance of an authorization code token request based on the given authorization
* code.
*
*
* This is used to make a request for an access token using the authorization code. It uses
* {@link #getTransport()}, {@link #getJsonFactory()}, {@link #getTokenServerEncodedUrl()},
* {@link #getClientAuthentication()}, {@link #getRequestInitializer()}, and {@link #getScopes()}.
*
*
*
static TokenResponse requestAccessToken(AuthorizationCodeFlow flow, String code)
throws IOException, TokenResponseException {
return flow.newTokenRequest(code).setRedirectUri("https://client.example.com/rd").execute();
}
*
*
* @param authorizationCode authorization code.
*/
public AuthorizationCodeTokenRequest newTokenRequest(String authorizationCode) {
HttpExecuteInterceptor pkceClientAuthenticationWrapper = new HttpExecuteInterceptor() {
@Override
public void intercept(HttpRequest request) throws IOException {
clientAuthentication.intercept(request);
if (pkce != null) {
Map data = Data.mapOf(UrlEncodedContent.getContent(request).getData());
data.put("code_verifier", pkce.getVerifier());
}
}
};
return new AuthorizationCodeTokenRequest(transport, jsonFactory,
new GenericUrl(tokenServerEncodedUrl), authorizationCode).setClientAuthentication(
pkceClientAuthenticationWrapper).setRequestInitializer(requestInitializer).setScopes(scopes);
}
/**
* Creates a new credential for the given user ID based on the given token response
* and stores it in the credential store.
*
* @param response token response
* @param userId user ID or {@code null} if not using a persisted credential store
* @return newly created credential
*/
@SuppressWarnings("deprecation")
public Credential createAndStoreCredential(TokenResponse response, String userId)
throws IOException {
Credential credential = newCredential(userId).setFromTokenResponse(response);
if (credentialStore != null) {
credentialStore.store(userId, credential);
}
if (credentialDataStore != null) {
credentialDataStore.set(userId, new StoredCredential(credential));
}
if (credentialCreatedListener != null) {
credentialCreatedListener.onCredentialCreated(credential, response);
}
return credential;
}
/**
* Loads the credential of the given user ID from the credential store.
*
* @param userId user ID or {@code null} if not using a persisted credential store
* @return credential found in the credential store of the given user ID or {@code null} for none
* found
*/
@SuppressWarnings("deprecation")
public Credential loadCredential(String userId) throws IOException {
// No requests need to be performed when userId is not specified.
if (isNullOrEmpty(userId)) {
return null;
}
if (credentialDataStore == null && credentialStore == null) {
return null;
}
Credential credential = newCredential(userId);
if (credentialDataStore != null) {
StoredCredential stored = credentialDataStore.get(userId);
if (stored == null) {
return null;
}
credential.setAccessToken(stored.getAccessToken());
credential.setRefreshToken(stored.getRefreshToken());
credential.setExpirationTimeMilliseconds(stored.getExpirationTimeMilliseconds());
} else if (!credentialStore.load(userId, credential)) {
return null;
}
return credential;
}
/**
* Returns a new credential instance based on the given user ID.
*
* @param userId user ID or {@code null} if not using a persisted credential store
*/
@SuppressWarnings("deprecation")
private Credential newCredential(String userId) {
Credential.Builder builder = new Credential.Builder(method).setTransport(transport)
.setJsonFactory(jsonFactory)
.setTokenServerEncodedUrl(tokenServerEncodedUrl)
.setClientAuthentication(clientAuthentication)
.setRequestInitializer(requestInitializer)
.setClock(clock);
if (credentialDataStore != null) {
builder.addRefreshListener(
new DataStoreCredentialRefreshListener(userId, credentialDataStore));
} else if (credentialStore != null) {
builder.addRefreshListener(new CredentialStoreRefreshListener(userId, credentialStore));
}
builder.getRefreshListeners().addAll(refreshListeners);
return builder.build();
}
/**
* Returns the method of presenting the access token to the resource server (for example
* {@link BearerToken#authorizationHeaderAccessMethod}).
*/
public final AccessMethod getMethod() {
return method;
}
/** Returns the HTTP transport. */
public final HttpTransport getTransport() {
return transport;
}
/** Returns the JSON factory. */
public final JsonFactory getJsonFactory() {
return jsonFactory;
}
/** Returns the token server encoded URL. */
public final String getTokenServerEncodedUrl() {
return tokenServerEncodedUrl;
}
/**
* Returns the client authentication or {@code null} for none (see
* {@link TokenRequest#setClientAuthentication(HttpExecuteInterceptor)}).
*/
public final HttpExecuteInterceptor getClientAuthentication() {
return clientAuthentication;
}
/** Returns the client identifier. */
public final String getClientId() {
return clientId;
}
/** Returns the authorization server encoded URL. */
public final String getAuthorizationServerEncodedUrl() {
return authorizationServerEncodedUrl;
}
/**
* {@link Beta}
* Returns the credential persistence store or {@code null} for none.
* @deprecated (to be removed in the future) Use {@link #getCredentialDataStore()} instead.
*/
@Beta
@Deprecated
public final CredentialStore getCredentialStore() {
return credentialStore;
}
/**
* {@link Beta}
* Returns the stored credential data store or {@code null} for none.
*
* @since 1.16
*/
@Beta
public final DataStore getCredentialDataStore() {
return credentialDataStore;
}
/** Returns the HTTP request initializer or {@code null} for none. */
public final HttpRequestInitializer getRequestInitializer() {
return requestInitializer;
}
/**
* Returns the space-separated list of scopes.
*
* @since 1.15
*/
public final String getScopesAsString() {
return Joiner.on(' ').join(scopes);
}
/** Returns the a collection of scopes. */
public final Collection getScopes() {
return scopes;
}
/**
* Returns the clock which will be passed along to the Credential.
* @since 1.9
*/
public final Clock getClock() {
return clock;
}
/**
* Returns the unmodifiable list of listeners for refresh token results.
*
* @since 1.15
*/
public final Collection getRefreshListeners() {
return refreshListeners;
}
/**
* Listener for a created credential after a successful token response in
* {@link #createAndStoreCredential}.
*
* @since 1.14
*/
public interface CredentialCreatedListener {
/**
* Notifies of a created credential after a successful token response in
* {@link #createAndStoreCredential}.
*
*
* Typical use is to parse additional fields from the credential created, such as an ID token.
*
*
* @param credential created credential
* @param tokenResponse successful token response
*/
void onCredentialCreated(Credential credential, TokenResponse tokenResponse) throws IOException;
}
/**
* An implementation of Proof Key for Code Exchange
* which, according to the OAuth 2.0 for Native Apps RFC,
* is mandatory for public native apps.
*/
private static class PKCE {
private final String verifier;
private String challenge;
private String challengeMethod;
public PKCE() {
verifier = generateVerifier();
generateChallenge(verifier);
}
private static String generateVerifier() {
SecureRandom sr = new SecureRandom();
byte[] code = new byte[32];
sr.nextBytes(code);
return Base64.encodeBase64URLSafeString(code);
}
/**
* Create the PKCE code verifier. It uses the S256 method but
* falls back to using the 'plain' method in the unlikely case
* that the SHA-256 MessageDigest algorithm implementation can't be
* loaded.
*/
private void generateChallenge(String verifier) {
try {
byte[] bytes = verifier.getBytes();
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(bytes, 0, bytes.length);
byte[] digest = md.digest();
challenge = Base64.encodeBase64URLSafeString(digest);
challengeMethod = "S256";
} catch (NoSuchAlgorithmException e) {
challenge = verifier;
challengeMethod = "plain";
}
}
public String getVerifier() {
return verifier;
}
public String getChallenge() {
return challenge;
}
public String getChallengeMethod() {
return challengeMethod;
}
}
/**
* Authorization code flow builder.
*
*
* Implementation is not thread-safe.
*
*/
public static class Builder {
/**
* Method of presenting the access token to the resource server (for example
* {@link BearerToken#authorizationHeaderAccessMethod}).
*/
AccessMethod method;
/** HTTP transport. */
HttpTransport transport;
/** JSON factory. */
JsonFactory jsonFactory;
/** Token server URL. */
GenericUrl tokenServerUrl;
/**
* Client authentication or {@code null} for none (see
* {@link TokenRequest#setClientAuthentication(HttpExecuteInterceptor)}).
*/
HttpExecuteInterceptor clientAuthentication;
/** Client identifier. */
String clientId;
/** Authorization server encoded URL. */
String authorizationServerEncodedUrl;
PKCE pkce;
/** Credential persistence store or {@code null} for none. */
@Deprecated
@Beta
CredentialStore credentialStore;
/** Stored credential data store or {@code null} for none. */
@Beta
DataStore credentialDataStore;
/** HTTP request initializer or {@code null} for none. */
HttpRequestInitializer requestInitializer;
/** Collection of scopes. */
Collection scopes = Lists.newArrayList();
/** Clock passed along to the Credential. */
Clock clock = Clock.SYSTEM;
/** Credential created listener or {@code null} for none. */
CredentialCreatedListener credentialCreatedListener;
/** Refresh listeners provided by the client. */
Collection refreshListeners = Lists.newArrayList();
/**
* @param method method of presenting the access token to the resource server (for example
* {@link BearerToken#authorizationHeaderAccessMethod})
* @param transport HTTP transport
* @param jsonFactory JSON factory
* @param tokenServerUrl token server URL
* @param clientAuthentication client authentication or {@code null} for none (see
* {@link TokenRequest#setClientAuthentication(HttpExecuteInterceptor)})
* @param clientId client identifier
* @param authorizationServerEncodedUrl authorization server encoded URL
*/
public Builder(AccessMethod method,
HttpTransport transport,
JsonFactory jsonFactory,
GenericUrl tokenServerUrl,
HttpExecuteInterceptor clientAuthentication,
String clientId,
String authorizationServerEncodedUrl) {
setMethod(method);
setTransport(transport);
setJsonFactory(jsonFactory);
setTokenServerUrl(tokenServerUrl);
setClientAuthentication(clientAuthentication);
setClientId(clientId);
setAuthorizationServerEncodedUrl(authorizationServerEncodedUrl);
}
/** Returns a new instance of an authorization code flow based on this builder. */
public AuthorizationCodeFlow build() {
return new AuthorizationCodeFlow(this);
}
/**
* Returns the method of presenting the access token to the resource server (for example
* {@link BearerToken#authorizationHeaderAccessMethod}).
*/
public final AccessMethod getMethod() {
return method;
}
/**
* Sets the method of presenting the access token to the resource server (for example
* {@link BearerToken#authorizationHeaderAccessMethod}).
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
* @since 1.11
*/
public Builder setMethod(AccessMethod method) {
this.method = Preconditions.checkNotNull(method);
return this;
}
/** Returns the HTTP transport. */
public final HttpTransport getTransport() {
return transport;
}
/**
* Sets the HTTP transport.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
* @since 1.11
*/
public Builder setTransport(HttpTransport transport) {
this.transport = Preconditions.checkNotNull(transport);
return this;
}
/** Returns the JSON factory. */
public final JsonFactory getJsonFactory() {
return jsonFactory;
}
/**
* Sets the JSON factory.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
* @since 1.11
*/
public Builder setJsonFactory(JsonFactory jsonFactory) {
this.jsonFactory = Preconditions.checkNotNull(jsonFactory);
return this;
}
/** Returns the token server URL. */
public final GenericUrl getTokenServerUrl() {
return tokenServerUrl;
}
/**
* Sets the token server URL.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
* @since 1.11
*/
public Builder setTokenServerUrl(GenericUrl tokenServerUrl) {
this.tokenServerUrl = Preconditions.checkNotNull(tokenServerUrl);
return this;
}
/**
* Returns the client authentication or {@code null} for none (see
* {@link TokenRequest#setClientAuthentication(HttpExecuteInterceptor)}).
*/
public final HttpExecuteInterceptor getClientAuthentication() {
return clientAuthentication;
}
/**
* Sets the client authentication or {@code null} for none (see
* {@link TokenRequest#setClientAuthentication(HttpExecuteInterceptor)}).
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
* @since 1.11
*/
public Builder setClientAuthentication(HttpExecuteInterceptor clientAuthentication) {
this.clientAuthentication = clientAuthentication;
return this;
}
/** Returns the client identifier. */
public final String getClientId() {
return clientId;
}
/**
* Sets the client identifier.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
* @since 1.11
*/
public Builder setClientId(String clientId) {
this.clientId = Preconditions.checkNotNull(clientId);
return this;
}
/** Returns the authorization server encoded URL. */
public final String getAuthorizationServerEncodedUrl() {
return authorizationServerEncodedUrl;
}
/**
* Sets the authorization server encoded URL.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
* @since 1.11
*/
public Builder setAuthorizationServerEncodedUrl(String authorizationServerEncodedUrl) {
this.authorizationServerEncodedUrl =
Preconditions.checkNotNull(authorizationServerEncodedUrl);
return this;
}
/**
* {@link Beta}
* Returns the credential persistence store or {@code null} for none.
* @deprecated (to be removed in the future) Use {@link #getCredentialDataStore()} instead.
*/
@Beta
@Deprecated
public final CredentialStore getCredentialStore() {
return credentialStore;
}
/**
* {@link Beta}
* Returns the stored credential data store or {@code null} for none.
*
* @since 1.16
*/
@Beta
public final DataStore getCredentialDataStore() {
return credentialDataStore;
}
/**
* Returns the clock passed along to the Credential or {@link Clock#SYSTEM} when system default
* is used.
* @since 1.9
*/
public final Clock getClock() {
return clock;
}
/**
* Sets the clock to pass to the Credential.
*
*
* The default value for this parameter is {@link Clock#SYSTEM}
*
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
* @since 1.9
*/
public Builder setClock(Clock clock) {
this.clock = Preconditions.checkNotNull(clock);
return this;
}
/**
* {@link Beta}
* Sets the credential persistence store or {@code null} for none.
*
*
* Warning: not compatible with {@link #setDataStoreFactory} or {@link #setCredentialDataStore},
* and if either of those is called before this method is called, this method will throw an
* {@link IllegalArgumentException}.
*
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
*
* @deprecated (to be removed in the future) Use
* {@link #setDataStoreFactory(DataStoreFactory)} or
* {@link #setCredentialDataStore(DataStore)} instead.
*/
@Beta
@Deprecated
public Builder setCredentialStore(CredentialStore credentialStore) {
Preconditions.checkArgument(credentialDataStore == null);
this.credentialStore = credentialStore;
return this;
}
/**
* {@link Beta}
* Sets the data store factory or {@code null} for none.
*
*
* Warning: not compatible with {@link #setCredentialStore}, and if it is called before this
* method is called, this method will throw an {@link IllegalArgumentException}.
*
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
*
* @since 1.16
*/
@Beta
public Builder setDataStoreFactory(DataStoreFactory dataStoreFactory) throws IOException {
return setCredentialDataStore(StoredCredential.getDefaultDataStore(dataStoreFactory));
}
/**
* {@link Beta}
* Sets the stored credential data store or {@code null} for none.
*
*
* Warning: not compatible with {@link #setCredentialStore}, and if it is called before this
* method is called, this method will throw an {@link IllegalArgumentException}.
*
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
*
* @since 1.16
*/
@Beta
public Builder setCredentialDataStore(DataStore credentialDataStore) {
Preconditions.checkArgument(credentialStore == null);
this.credentialDataStore = credentialDataStore;
return this;
}
/** Returns the HTTP request initializer or {@code null} for none. */
public final HttpRequestInitializer getRequestInitializer() {
return requestInitializer;
}
/**
* Sets the HTTP request initializer or {@code null} for none.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
*/
public Builder setRequestInitializer(HttpRequestInitializer requestInitializer) {
this.requestInitializer = requestInitializer;
return this;
}
/**
* Enables Proof Key for Code Exchange (PKCE) for this Athorization Code Flow.
* @since 1.31
*/
@Beta
public Builder enablePKCE() {
this.pkce = new PKCE();
return this;
}
/**
* Sets the collection of scopes.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
*
* @param scopes collection of scopes
* @since 1.15
*/
public Builder setScopes(Collection scopes) {
this.scopes = Preconditions.checkNotNull(scopes);
return this;
}
/** Returns a collection of scopes. */
public final Collection getScopes() {
return scopes;
}
/**
* Sets the credential created listener or {@code null} for none.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
*
* @since 1.14
*/
public Builder setCredentialCreatedListener(
CredentialCreatedListener credentialCreatedListener) {
this.credentialCreatedListener = credentialCreatedListener;
return this;
}
/**
* Adds a listener for refresh token results.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
*
* @param refreshListener refresh listener
* @since 1.15
*/
public Builder addRefreshListener(CredentialRefreshListener refreshListener) {
refreshListeners.add(Preconditions.checkNotNull(refreshListener));
return this;
}
/**
* Returns the listeners for refresh token results.
*
* @since 1.15
*/
public final Collection getRefreshListeners() {
return refreshListeners;
}
/**
* Sets the listeners for refresh token results.
*
*
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
*
*
* @since 1.15
*/
public Builder setRefreshListeners(Collection refreshListeners) {
this.refreshListeners = Preconditions.checkNotNull(refreshListeners);
return this;
}
/**
* Returns the credential created listener or {@code null} for none.
*
* @since 1.14
*/
public final CredentialCreatedListener getCredentialCreatedListener() {
return credentialCreatedListener;
}
}
}