Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods to request access token synchronous #625

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions library/java/net/openid/appauth/AuthState.java
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,128 @@ public void update(@Nullable RegistrationResponse regResponse) {
mAuthorizationException = null;
}

/**
* Ensures that a non-expired access token is available before invoking the provided action.
* @return
*/
@NonNull
public Tokens getSynchronousFreshToken(
@NonNull AuthorizationService service) throws AuthorizationException {
return getSynchronousFreshToken(
service,
NoClientAuthentication.INSTANCE,
Collections.<String, String>emptyMap(),
SystemClock.INSTANCE);
}

/**
* Ensures that a non-expired access token is available before invoking the provided action.
* @return
*/
@NonNull
public Tokens getSynchronousFreshToken(
@NonNull AuthorizationService service,
@NonNull ClientAuthentication clientAuth) throws AuthorizationException {
return getSynchronousFreshToken(
service,
clientAuth,
Collections.<String, String>emptyMap(),
SystemClock.INSTANCE);
}

/**
* Ensures that a non-expired access token is available before invoking the provided action.
* If a token refresh is required, the provided additional parameters will be included in this
* refresh request.
* @return
*/
@NonNull
public Tokens getSynchronousFreshToken(
@NonNull AuthorizationService service,
@NonNull Map<String, String> refreshTokenAdditionalParams) throws ClientAuthentication.UnsupportedAuthenticationMethod, AuthorizationException {
return getSynchronousFreshToken(
service,
getClientAuthentication(),
refreshTokenAdditionalParams,
SystemClock.INSTANCE);
}

/**
* Ensures that a non-expired access token is available before invoking the provided action.
* If a token refresh is required, the provided additional parameters will be included in this
* refresh request.
* @return
*/
@NonNull
public Tokens getSynchronousFreshToken(
@NonNull AuthorizationService service,
@NonNull ClientAuthentication clientAuth,
@NonNull Map<String, String> refreshTokenAdditionalParams) throws AuthorizationException {
return getSynchronousFreshToken(
service,
clientAuth,
refreshTokenAdditionalParams,
SystemClock.INSTANCE);
}

@VisibleForTesting
@NonNull
Tokens getSynchronousFreshToken(
@NonNull final AuthorizationService service,
@NonNull final ClientAuthentication clientAuth,
@NonNull final Map<String, String> refreshTokenAdditionalParams,
@NonNull final Clock clock) throws AuthorizationException {
checkNotNull(service, "service cannot be null");
checkNotNull(clientAuth, "client authentication cannot be null");
checkNotNull(refreshTokenAdditionalParams,
"additional params cannot be null");
checkNotNull(clock, "clock cannot be null");

if (!getNeedsTokenRefresh(clock)) {
String accessToken = getAccessToken();
String idToken = getIdToken();
if (accessToken != null && idToken != null) {
return new Tokens(accessToken, idToken);
}
}

if (mRefreshToken == null) {
throw AuthorizationException.fromTemplate(
AuthorizationRequestErrors.CLIENT_ERROR,
new IllegalStateException("No refresh token available and token have expired"));
}

TokenResponse response = null;
AuthorizationException exception = null;
try {
response = service.performSynchronousTokenRequest(createTokenRefreshRequest(refreshTokenAdditionalParams), clientAuth);
} catch (AuthorizationException e) {
exception = e;
}

update(response, exception);

if (response != null) {
String accessToken = getAccessToken();
String idToken = getIdToken();
if (accessToken != null && idToken != null) {
return new Tokens(accessToken, idToken);
} else {
exception = AuthorizationException.fromTemplate(
AuthorizationException.GeneralErrors.JSON_DESERIALIZATION_ERROR,
new Exception(""));
}
}

if (exception != null) {
throw exception;
} else {
throw AuthorizationException.fromTemplate(
AuthorizationException.GeneralErrors.JSON_DESERIALIZATION_ERROR,
new Exception(""));
}
}

/**
* Ensures that a non-expired access token is available before invoking the provided action.
*/
Expand Down Expand Up @@ -743,6 +865,29 @@ void execute(
@Nullable AuthorizationException ex);
}

public static class Tokens {
/**
* Result of the synchronous function that return accessToken
*/
private @NonNull final String accessToken;
private @NonNull final String idToken;

public Tokens(@NonNull String accessToken, @NonNull String idToken) {
this.accessToken = accessToken;
this.idToken = idToken;
}

@NonNull
public String getAccessToken() {
return accessToken;
}

@NonNull
public String getIdToken() {
return idToken;
}
}

/**
* Creates the required client authentication for the token endpoint based on information
* in the most recent registration response (if it is set).
Expand Down
73 changes: 48 additions & 25 deletions library/java/net/openid/appauth/AuthorizationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,27 @@ public void performTokenRequest(
.execute();
}

/**
* Sends a request to the authorization service to exchange a code granted as part of an
* authorization request for a token. The result of this request will be sent to the provided
* callback handler.
* @return TokenResponse
*/
public TokenResponse performSynchronousTokenRequest(
@NonNull TokenRequest request,
@NonNull ClientAuthentication clientAuthentication) throws AuthorizationException {
checkNotDisposed();
Logger.debug("Initiating code exchange request to %s",
request.configuration.tokenEndpoint);
TokenRequestTask tokenRequest = new TokenRequestTask(
request,
clientAuthentication,
mClientConfiguration.getConnectionBuilder(),
SystemClock.INSTANCE, null);
JSONObject json = tokenRequest.doInBackground();
return tokenRequest.parseJson(json);
}

/**
* Sends a request to the authorization service to dynamically register a client.
* The result of this request will be sent to the provided callback handler.
Expand Down Expand Up @@ -473,63 +494,65 @@ protected JSONObject doInBackground(Void... voids) {

@Override
protected void onPostExecute(JSONObject json) {
try {
TokenResponse tokenResponse = parseJson(json);
mCallback.onTokenRequestCompleted(tokenResponse, null);
} catch (AuthorizationException authorizationException) {
mCallback.onTokenRequestCompleted(null, authorizationException);
}
}

public TokenResponse parseJson(JSONObject json) throws AuthorizationException {
if (mException != null) {
mCallback.onTokenRequestCompleted(null, mException);
return;
throw mException;
}

if (json.has(AuthorizationException.PARAM_ERROR)) {
AuthorizationException ex;
try {
String error = json.getString(AuthorizationException.PARAM_ERROR);
ex = AuthorizationException.fromOAuthTemplate(
TokenRequestErrors.byString(error),
error,
json.optString(AuthorizationException.PARAM_ERROR_DESCRIPTION, null),
UriUtil.parseUriIfAvailable(
json.optString(AuthorizationException.PARAM_ERROR_URI)));
TokenRequestErrors.byString(error),
error,
json.optString(AuthorizationException.PARAM_ERROR_DESCRIPTION, null),
UriUtil.parseUriIfAvailable(
json.optString(AuthorizationException.PARAM_ERROR_URI)));
} catch (JSONException jsonEx) {
ex = AuthorizationException.fromTemplate(
GeneralErrors.JSON_DESERIALIZATION_ERROR,
jsonEx);
GeneralErrors.JSON_DESERIALIZATION_ERROR,
jsonEx);
}
mCallback.onTokenRequestCompleted(null, ex);
return;
throw ex;
}

TokenResponse response;
try {
response = new TokenResponse.Builder(mRequest).fromResponseJson(json).build();
} catch (JSONException jsonEx) {
mCallback.onTokenRequestCompleted(null,
AuthorizationException.fromTemplate(
GeneralErrors.JSON_DESERIALIZATION_ERROR,
jsonEx));
return;
throw AuthorizationException.fromTemplate(
GeneralErrors.JSON_DESERIALIZATION_ERROR,
jsonEx);
}

if (response.idToken != null) {
IdToken idToken;
try {
idToken = IdToken.from(response.idToken);
} catch (IdTokenException | JSONException ex) {
mCallback.onTokenRequestCompleted(null,
AuthorizationException.fromTemplate(
GeneralErrors.ID_TOKEN_PARSING_ERROR,
ex));
return;
throw AuthorizationException.fromTemplate(
GeneralErrors.ID_TOKEN_PARSING_ERROR,
ex);
}

try {
idToken.validate(mRequest, mClock);
} catch (AuthorizationException ex) {
mCallback.onTokenRequestCompleted(null, ex);
return;
throw ex;
}
}
Logger.debug("Token exchange with %s completed",
mRequest.configuration.tokenEndpoint);
mCallback.onTokenRequestCompleted(response, null);
mRequest.configuration.tokenEndpoint);
return response;
}

/**
Expand Down