Skip to content

Commit c247ae6

Browse files
authored
Merge pull request #146 from ihormartsekha-okta/im-OKTA-265220-error-handling-for-OIDC-sign-in
Create new AuthorizationException type for Token Validation errors
2 parents 55642ba + 467a182 commit c247ae6

2 files changed

Lines changed: 83 additions & 15 deletions

File tree

library/src/main/java/com/okta/oidc/OktaIdToken.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ public interface Clock {
9090
String mSignature;
9191

9292
private static final Long MILLIS_PER_SECOND = 1000L;
93-
private static final Long TEN_MINUTES_IN_SECONDS = 600L;
93+
private static final int SECONDS_IN_ONE_MINUTE = 60;
94+
private static final Long TEN_MINUTES_IN_SECONDS = 10L * SECONDS_IN_ONE_MINUTE;
9495

9596
/**
9697
* The address in the claims section.
@@ -301,62 +302,62 @@ public void validate(TokenRequest request, Clock clock) throws AuthorizationExce
301302

302303
if (!"RS256".equals(mHeader.alg)) {
303304
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
304-
new IllegalStateException("JWT Header 'alg' of [" + mHeader.alg + "] " +
305-
"is not supported, only RSA256 signatures are supported"));
305+
AuthorizationException.TokenValidationError
306+
.createNotSupportedAlgorithmException(mHeader.alg));
306307
}
307308
if (providerConfig.issuer != null) {
308309
if (!mClaims.iss.equals(providerConfig.issuer)) {
309310
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
310-
new IllegalStateException("Issuer mismatch"));
311+
AuthorizationException.TokenValidationError.ISSUER_MISMATCH);
311312
}
312313

313314
Uri issuerUri = Uri.parse(mClaims.iss);
314315
if (!issuerUri.getScheme().equals("https")) {
315316
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
316-
new IllegalStateException("Issuer must be an https URL"));
317+
AuthorizationException.TokenValidationError.ISSUER_NOT_HTTPS_URL);
317318
}
318319

319320
if (TextUtils.isEmpty(issuerUri.getHost())) {
320321
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
321-
new IllegalStateException("Issuer host can not be empty"));
322+
AuthorizationException.TokenValidationError.ISSUER_HOST_EMPTY);
322323
}
323324

324325
if (issuerUri.getFragment() != null || issuerUri.getQueryParameterNames().size() > 0) {
325326
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
326-
new IllegalStateException(
327-
"Issuer URL contains query parameters or fragment components"));
327+
AuthorizationException.TokenValidationError
328+
.ISSUER_URL_CONTAIN_OTHER_COMPONENTS);
328329
}
329330
}
330331

331332
String clientId = config.getClientId();
332333
if (!this.mClaims.aud.contains(clientId)) {
333334
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
334-
new IllegalStateException("Audience mismatch"));
335+
AuthorizationException.TokenValidationError.AUDIENCE_MISMATCH);
335336
}
336337

337338
long nowInSeconds = clock.getCurrentTimeMillis() / MILLIS_PER_SECOND;
338339
if (nowInSeconds > mClaims.exp) {
339340
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
340-
new IllegalStateException("ID Token expired"));
341+
AuthorizationException.TokenValidationError.ID_TOKEN_EXPIRED);
341342
}
342343

343344
if (Math.abs(nowInSeconds - mClaims.iat) > TEN_MINUTES_IN_SECONDS) {
344345
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
345-
new IllegalStateException("Issued at time is more than 10 minutes "
346-
+ "before or after the current time"));
346+
AuthorizationException.TokenValidationError.createWrongTokenIssuedTime(
347+
TEN_MINUTES_IN_SECONDS.intValue() / SECONDS_IN_ONE_MINUTE));
347348
}
348349

349350
if (GrantTypes.AUTHORIZATION_CODE.equals(request.getGrantType())) {
350351
String expectedNonce = request.getNonce();
351352
if (!TextUtils.equals(mClaims.nonce, expectedNonce)) {
352353
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
353-
new IllegalStateException("Nonce mismatch"));
354+
AuthorizationException.TokenValidationError.NONCE_MISMATCH);
354355
}
355356
}
356357

357358
if (request.getMaxAge() != null && mClaims.auth_time <= 0) {
358359
throw AuthorizationException.fromTemplate(ID_TOKEN_VALIDATION_ERROR,
359-
new IllegalStateException("max_age provided but auth_time is missing"));
360+
AuthorizationException.TokenValidationError.AUTH_TIME_MISSING);
360361
}
361362
}
362363

library/src/main/java/com/okta/oidc/util/AuthorizationException.java

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,12 @@ public final class AuthorizationException extends Exception {
129129
/**
130130
* The error type for persistence specific errors.
131131
*/
132-
public static final int TYPE_ENCRYPTION_ERROR = 4;
132+
public static final int TYPE_ENCRYPTION_ERROR = 5;
133+
134+
/**
135+
* The error type for OAuth token validation errors.
136+
*/
137+
public static final int TYPE_OAUTH_VALIDATION_TOKEN_ERROR = 6;
133138

134139
@VisibleForTesting
135140
static final String KEY_TYPE = "type";
@@ -524,6 +529,63 @@ public static AuthorizationException byEncryptionException(EncryptionException e
524529
}
525530
}
526531

532+
/**
533+
* Error codes related to failed during token validation.
534+
*/
535+
public static final class TokenValidationError {
536+
public static final int NOT_SUPPORTED_ALGORITHM_ERROR = 6000;
537+
public static final int ISSUER_MISMATCH_ERROR = 6001;
538+
public static final int ISSUER_NOT_HTTPS_URL_ERROR = 6002;
539+
public static final int ISSUER_HOST_EMPTY_ERROR = 6003;
540+
public static final int ISSUER_URL_CONTAIN_OTHER_COMPONENTS_ERROR = 6004;
541+
public static final int AUDIENCE_MISMATCH_ERROR = 6005;
542+
public static final int ID_TOKEN_EXPIRED_ERROR = 6006;
543+
public static final int ID_TOKEN_WRONG_ISSUED_TIME_ERROR = 6007;
544+
public static final int NONCE_MISMATCH_ERROR = 6008;
545+
public static final int AUTH_TIME_MISSING_ERROR = 6009;
546+
547+
public static AuthorizationException createNotSupportedAlgorithmException(String alg) {
548+
return tokenValidationEx(NOT_SUPPORTED_ALGORITHM_ERROR,
549+
"JWT Header 'alg' of [" + alg + "] " +
550+
"is not supported, only RSA256 signatures are supported");
551+
}
552+
553+
public static final AuthorizationException ISSUER_MISMATCH =
554+
tokenValidationEx(ISSUER_MISMATCH_ERROR, "Issuer mismatch");
555+
556+
public static final AuthorizationException ISSUER_NOT_HTTPS_URL =
557+
tokenValidationEx(ISSUER_NOT_HTTPS_URL_ERROR,
558+
"Issuer must be an https URL");
559+
560+
public static final AuthorizationException ISSUER_HOST_EMPTY =
561+
tokenValidationEx(ISSUER_HOST_EMPTY_ERROR,
562+
"Issuer host can not be empty");
563+
564+
public static final AuthorizationException ISSUER_URL_CONTAIN_OTHER_COMPONENTS =
565+
tokenValidationEx(ISSUER_URL_CONTAIN_OTHER_COMPONENTS_ERROR,
566+
"Issuer URL contains query parameters or fragment components");
567+
568+
public static final AuthorizationException AUDIENCE_MISMATCH =
569+
tokenValidationEx(AUDIENCE_MISMATCH_ERROR, "Audience mismatch");
570+
571+
public static final AuthorizationException ID_TOKEN_EXPIRED =
572+
tokenValidationEx(ID_TOKEN_EXPIRED_ERROR, "ID Token expired");
573+
574+
public static AuthorizationException createWrongTokenIssuedTime(int minutes) {
575+
return tokenValidationEx(ID_TOKEN_WRONG_ISSUED_TIME_ERROR,
576+
"Issued at time is more than "
577+
+ minutes + " minutes before or after the current time");
578+
}
579+
580+
public static final AuthorizationException NONCE_MISMATCH =
581+
tokenValidationEx(NONCE_MISMATCH_ERROR, "Nonce mismatch");
582+
583+
public static final AuthorizationException AUTH_TIME_MISSING =
584+
tokenValidationEx(AUTH_TIME_MISSING_ERROR,
585+
"max_age provided but auth_time is missing");
586+
587+
}
588+
527589
private static AuthorizationException generalEx(int code, @Nullable String errorDescription) {
528590
return new AuthorizationException(
529591
TYPE_GENERAL_ERROR, code, null, errorDescription, null, null);
@@ -544,6 +606,11 @@ private static AuthorizationException registrationEx(int code, @Nullable String
544606
TYPE_OAUTH_REGISTRATION_ERROR, code, error, null, null, null);
545607
}
546608

609+
private static AuthorizationException tokenValidationEx(int code, @Nullable String error) {
610+
return new AuthorizationException(
611+
TYPE_OAUTH_VALIDATION_TOKEN_ERROR, code, null, error, null, null);
612+
}
613+
547614
/**
548615
* Creates an exception based on one of the existing values defined in
549616
* {@link GeneralErrors}, {@link AuthorizationRequestErrors} or {@link TokenRequestErrors},

0 commit comments

Comments
 (0)