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

Validate TokenResponse #341

Merged
merged 2 commits into from
Nov 11, 2022
Merged
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
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,6 @@ android {

For more details on Java 8 support for your Android projects, refer to the [Android developer documentations](#https://developer.android.com/studio/write/java8-support)

### Proguard

The SDK uses GSON for serialization. As a result, proguard/R8 obfuscation can result in incorrect behavior or crashes. To avoid issues with Proguard/R8, add the following rules to proguard-rules.pro:

```
-keep class com.okta.oidc.** { *; }
```

### Sample app

A sample is contained within this repository. For more information on how to
Expand Down
14 changes: 14 additions & 0 deletions library/src/main/java/com/okta/oidc/net/request/TokenRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;

import com.google.gson.Gson;
import com.google.gson.JsonIOException;
Expand All @@ -46,6 +47,9 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class TokenRequest extends BaseRequest<TokenResponse, AuthorizationException> {
private static final String TAG = TokenRequest.class.getSimpleName();
@VisibleForTesting
public static final String INVALID_RESPONSE_WITH_HTTP_STATUS_CODE_ERROR =
"Invalid token response with status code %d";

private String code;
private String client_assertion;
Expand Down Expand Up @@ -146,6 +150,7 @@ public TokenResponse executeRequest(OktaHttpClient client) throws AuthorizationE
}
}
tokenResponse = new Gson().fromJson(json.toString(), TokenResponse.class);
tokenResponse.validate();
tokenResponse.setCreationTime(System.currentTimeMillis());
if (tokenResponse.getIdToken() != null) {
OktaIdToken idToken;
Expand All @@ -167,6 +172,15 @@ public TokenResponse executeRequest(OktaHttpClient client) throws AuthorizationE
AuthorizationException.GeneralErrors.JSON_DESERIALIZATION_ERROR, ex);
} catch (AuthorizationException ae) {
throw ae;
} catch (IllegalArgumentException e) {
if (response != null) {
throw new AuthorizationException(
String.format(INVALID_RESPONSE_WITH_HTTP_STATUS_CODE_ERROR,
response.getStatusCode()), e);
} else {
throw AuthorizationException.fromTemplate(AuthorizationException
.GeneralErrors.NETWORK_ERROR, e);
}
} catch (Exception e) {
throw AuthorizationException.fromTemplate(AuthorizationException
.GeneralErrors.NETWORK_ERROR, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@

package com.okta.oidc.net.response;

import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;

import com.google.gson.Gson;
import com.okta.oidc.storage.Persistable;
Expand All @@ -37,6 +40,13 @@ public class TokenResponse implements Persistable {
private String id_token;
private long expiresAt = -1;

@VisibleForTesting
public static final String MISSING_ACCESS_TOKEN_ERROR = "access_token is missing";
@VisibleForTesting
public static final String MISSING_TOKEN_TYPE_ERROR = "token_type is missing";
@VisibleForTesting
public static final String MISSING_EXPIRES_IN_ERROR = "expires_in is missing";

@NonNull
public String getAccessToken() {
return access_token;
Expand Down Expand Up @@ -83,6 +93,18 @@ public long getExpiresAt() {
return expiresAt;
}

public void validate() throws IllegalArgumentException {
if (TextUtils.isEmpty(access_token)) {
throw new IllegalArgumentException(MISSING_ACCESS_TOKEN_ERROR);
}
if (TextUtils.isEmpty(token_type)) {
throw new IllegalArgumentException(MISSING_TOKEN_TYPE_ERROR);
}
if (TextUtils.isEmpty(expires_in)) {
throw new IllegalArgumentException(MISSING_EXPIRES_IN_ERROR);
}
}

public static final Persistable.Restore<TokenResponse> RESTORE =
new Persistable.Restore<TokenResponse>() {
private static final String KEY = "TokenResponse";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,13 @@ public void executeRequestFailure() throws AuthorizationException {
TokenResponse response = mRequest.executeRequest(mHttpClient);
assertNull(response);
}
}

@Test
public void executeRequestFailedValidation() throws AuthorizationException {
mExpectedEx.expect(AuthorizationException.class);
String jws = TestValues.getJwt(mEndPoint.getUrl(), CUSTOM_NONCE, mConfig.getClientId());
mEndPoint.enqueueTokenWithMissingRequiredParams(jws);
TokenResponse response = mRequest.executeRequest(mHttpClient);
assertNull(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@

import static com.okta.oidc.net.response.TokenResponse.RESTORE;
import static com.okta.oidc.util.JsonStrings.TOKEN_RESPONSE;
import static com.okta.oidc.util.JsonStrings.TOKEN_RESPONSE_WITH_MISSING_ACCESS_TOKEN;
import static com.okta.oidc.util.JsonStrings.TOKEN_RESPONSE_WITH_MISSING_EXPIRES_IN;
import static com.okta.oidc.util.JsonStrings.TOKEN_RESPONSE_WITH_MISSING_TOKEN_TYPE;
import static com.okta.oidc.util.TestValues.ACCESS_TOKEN;
import static com.okta.oidc.util.TestValues.EXPIRES_IN;
import static com.okta.oidc.util.TestValues.ID_TOKEN;
import static com.okta.oidc.util.TestValues.REFRESH_TOKEN;
import static com.okta.oidc.util.TestValues.SCOPES;
import static com.okta.oidc.util.TestValues.TYPE_BEARER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

@RunWith(RobolectricTestRunner.class)
@Config(sdk = 27)
Expand Down Expand Up @@ -81,6 +85,32 @@ public void getKey() {
assertEquals(mToken.getKey(), RESTORE.getKey());
}

@Test
public void validatePasses() {
mToken.validate();
}

@Test
public void validateThrowsExceptionWhenAccessTokenIsMissing() {
mToken = RESTORE.restore(TOKEN_RESPONSE_WITH_MISSING_ACCESS_TOKEN);
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, mToken::validate);
assertEquals(TokenResponse.MISSING_ACCESS_TOKEN_ERROR, exception.getMessage());
}

@Test
public void validateThrowsExceptionWhenTokenTypeIsMissing() {
mToken = RESTORE.restore(TOKEN_RESPONSE_WITH_MISSING_TOKEN_TYPE);
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, mToken::validate);
assertEquals(TokenResponse.MISSING_TOKEN_TYPE_ERROR, exception.getMessage());
}

@Test
public void validateThrowsExceptionWhenExpiresInIsMissing() {
mToken = RESTORE.restore(TOKEN_RESPONSE_WITH_MISSING_EXPIRES_IN);
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, mToken::validate);
assertEquals(TokenResponse.MISSING_EXPIRES_IN_ERROR, exception.getMessage());
}

@Test
public void persist() {
String json = mToken.persist();
Expand Down
21 changes: 21 additions & 0 deletions library/src/test/java/com/okta/oidc/util/JsonStrings.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ public interface JsonStrings {
" \"id_token\" : \"%s\"" +
"}";

String TOKEN_MISSING_PARAMS = "{\n" +
" \"scope\" : \"openid email profile\",\n" +
" \"refresh_token\" : \"a9VpZDRCeFh3Nkk2VdY\",\n" +
" \"id_token\" : \"%s\"" +
"}";

String PROVIDER_CONFIG_OAUTH2 = "{\n" +
" \"issuer\": \"https://dev-486177.oktapreview.com/oauth2/default/\",\n" +
" \"authorization_endpoint\": \"https://dev-486177.oktapreview.com/oauth2/default/v1/authorize\",\n" +
Expand Down Expand Up @@ -238,6 +244,21 @@ public interface JsonStrings {
"\"scope\" : \"openid profile offline_access\",\n " +
"\"refresh_token\" : \"REFRESH_TOKEN\",\n\"id_token\" : \"ID_TOKEN\"\n}";

String TOKEN_RESPONSE_WITH_MISSING_ACCESS_TOKEN = "{ \"token_type\" : " +
"\"Bearer\",\n \"expires_in\" : 3600,\n " +
"\"scope\" : \"openid profile offline_access\",\n " +
"\"refresh_token\" : \"REFRESH_TOKEN\",\n\"id_token\" : \"ID_TOKEN\"\n}";

String TOKEN_RESPONSE_WITH_MISSING_TOKEN_TYPE = "{ \"access_token\" : " +
"\"ACCESS_TOKEN\",\n \"expires_in\" : 3600,\n " +
"\"scope\" : \"openid profile offline_access\",\n " +
"\"refresh_token\" : \"REFRESH_TOKEN\",\n\"id_token\" : \"ID_TOKEN\"\n}";

String TOKEN_RESPONSE_WITH_MISSING_EXPIRES_IN = "{ \"access_token\" : " +
"\"ACCESS_TOKEN\",\n\"token_type\" : " +
"\"Bearer\",\n \"scope\" : \"openid profile offline_access\",\n " +
"\"refresh_token\" : \"REFRESH_TOKEN\",\n\"id_token\" : \"ID_TOKEN\"\n}";

String INVALID_CLIENT = "{\n" +
" \"error\": \"invalid_client\",\n" +
" \"error_description\": \"No client credentials found.\"\n" +
Expand Down
5 changes: 5 additions & 0 deletions library/src/test/java/com/okta/oidc/util/MockEndPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import static com.okta.oidc.util.JsonStrings.INVALID_CLIENT;
import static com.okta.oidc.util.JsonStrings.PROVIDER_CONFIG;
import static com.okta.oidc.util.JsonStrings.PROVIDER_CONFIG_OAUTH2;
import static com.okta.oidc.util.JsonStrings.TOKEN_MISSING_PARAMS;
import static com.okta.oidc.util.JsonStrings.TOKEN_SUCCESS;
import static com.okta.oidc.util.JsonStrings.UNAUTHORIZED_INVALID_TOKEN;
import static com.okta.oidc.util.JsonStrings.USER_PROFILE;
Expand Down Expand Up @@ -135,6 +136,10 @@ public void enqueueTokenSuccess(String idToken) {
mServer.enqueue(jsonResponse(HTTP_OK, String.format(TOKEN_SUCCESS, idToken)));
}

public void enqueueTokenWithMissingRequiredParams(String idToken) {
mServer.enqueue(jsonResponse(HTTP_OK, String.format(TOKEN_MISSING_PARAMS, idToken)));
}

public void enqueueNativeRequestSuccess(String state, int delaySeconds) {
mServer.enqueue((emptyResponse(HTTP_MOVED_TEMP)
.setHeadersDelay(delaySeconds, TimeUnit.SECONDS)
Expand Down