Skip to content

Commit 736462f

Browse files
nisrulzMardaneus86
andcommitted
Idtoken time skew (#3)
* Skip issue time validation or change the allowed time skew Allows to completely disable ID tokens issue time validation, or change the default of 10 minutes to a custom allowed time skew in seconds. * Update documentation to change the allowed id token time skew --------- Co-authored-by: Tim Klingeleers <Mardaneus86@users.noreply.github.com>
1 parent b6e3461 commit 736462f

File tree

5 files changed

+145
-16
lines changed

5 files changed

+145
-16
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
619619

620620
ID Token validation was introduced in `0.8.0` but not all authorization servers or configurations support it correctly.
621621

622-
- For testing environments [setSkipIssuerHttpsCheck](https://github.com/openid/AppAuth-Android/blob/master/library/java/net/openid/appauth/AppAuthConfiguration.java#L129) can be used to bypass the fact the issuer needs to be HTTPS.
622+
- For testing environments [setSkipIssuerHttpsCheck](https://github.com/openid/AppAuth-Android/blob/master/library/java/net/openid/appauth/AppAuthConfiguration.java#L143) can be used to bypass the fact the issuer needs to be HTTPS.
623623

624624
```java
625625
AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
@@ -635,6 +635,22 @@ AuthorizationRequest authRequest = authRequestBuilder
635635
.build();
636636
```
637637

638+
- To change the default allowed time skew of 10 minutes for the issue time, [setAllowedIssueTimeSkew](https://github.com/openid/AppAuth-Android/blob/master/library/java/net/openid/appauth/AppAuthConfiguration.java#L159) can be used.
639+
640+
```java
641+
AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
642+
.setAllowedIssueTimeSkew(THIRTY_MINUTES_IN_SECONDS)
643+
.build()
644+
```
645+
646+
- For testing environments [setSkipIssueTimeValidation](https://github.com/openid/AppAuth-Android/blob/master/library/java/net/openid/appauth/AppAuthConfiguration.java#L151) can be used to bypass the issue time validation.
647+
648+
```java
649+
AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
650+
.setSkipIssueTimeValidation(true)
651+
.build()
652+
```
653+
638654
## Dynamic client registration
639655

640656
AppAuth supports the

library/java/net/openid/appauth/AppAuthConfiguration.java

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,21 @@ public class AppAuthConfiguration {
4242

4343
private final boolean mSkipIssuerHttpsCheck;
4444

45+
private final boolean mSkipIssueTimeValidation;
46+
47+
private final Long mAllowedIssueTimeSkew;
48+
4549
private AppAuthConfiguration(
4650
@NonNull BrowserMatcher browserMatcher,
4751
@NonNull ConnectionBuilder connectionBuilder,
48-
Boolean skipIssuerHttpsCheck) {
52+
Boolean skipIssuerHttpsCheck,
53+
Boolean skipIssueTimeValidation,
54+
Long allowedIssueTimeSkew) {
4955
mBrowserMatcher = browserMatcher;
5056
mConnectionBuilder = connectionBuilder;
5157
mSkipIssuerHttpsCheck = skipIssuerHttpsCheck;
58+
mSkipIssueTimeValidation = skipIssueTimeValidation;
59+
mAllowedIssueTimeSkew = allowedIssueTimeSkew;
5260
}
5361

5462
/**
@@ -76,6 +84,22 @@ public ConnectionBuilder getConnectionBuilder() {
7684
*/
7785
public boolean getSkipIssuerHttpsCheck() { return mSkipIssuerHttpsCheck; }
7886

87+
/**
88+
* Returns <code>true</code> if the ID token issue time validation is disables,
89+
* otherwise <code>false</code>.
90+
*
91+
* @see Builder#setSkipIssueTimeValidation(Boolean)
92+
*/
93+
public boolean getSkipIssueTimeValidation() { return mSkipIssueTimeValidation; }
94+
95+
/**
96+
* Returns the time in seconds that the ID token issue time is allowed to be
97+
* skewed.
98+
*
99+
* @see Builder#setAllowedIssueTimeSkew(Long)
100+
*/
101+
public Long getAllowedIssueTimeSkew() { return mAllowedIssueTimeSkew; }
102+
79103
/**
80104
* Creates {@link AppAuthConfiguration} instances.
81105
*/
@@ -84,6 +108,8 @@ public static class Builder {
84108
private BrowserMatcher mBrowserMatcher = AnyBrowserMatcher.INSTANCE;
85109
private ConnectionBuilder mConnectionBuilder = DefaultConnectionBuilder.INSTANCE;
86110
private boolean mSkipIssuerHttpsCheck;
111+
private boolean mSkipIssueTimeValidation;
112+
private Long mAllowedIssueTimeSkew;
87113
private boolean mSkipNonceVerification;
88114

89115
/**
@@ -119,6 +145,22 @@ public Builder setSkipIssuerHttpsCheck(Boolean skipIssuerHttpsCheck) {
119145
return this;
120146
}
121147

148+
/**
149+
* Disables issue time validation for the id token.
150+
*/
151+
public Builder setSkipIssueTimeValidation(Boolean skipIssueTimeValidation) {
152+
mSkipIssueTimeValidation = skipIssueTimeValidation;
153+
return this;
154+
}
155+
156+
/**
157+
* Sets the allowed time skew in seconds for id token issue time validation.
158+
*/
159+
public Builder setAllowedIssueTimeSkew(Long allowedIssueTimeSkew) {
160+
mAllowedIssueTimeSkew = allowedIssueTimeSkew;
161+
return this;
162+
}
163+
122164
/**
123165
* Creates the instance from the configured properties.
124166
*/
@@ -127,7 +169,9 @@ public AppAuthConfiguration build() {
127169
return new AppAuthConfiguration(
128170
mBrowserMatcher,
129171
mConnectionBuilder,
130-
mSkipIssuerHttpsCheck
172+
mSkipIssuerHttpsCheck,
173+
mSkipIssueTimeValidation,
174+
mAllowedIssueTimeSkew
131175
);
132176
}
133177

library/java/net/openid/appauth/AuthorizationService.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,9 @@ public void performTokenRequest(
506506
mClientConfiguration.getConnectionBuilder(),
507507
SystemClock.INSTANCE,
508508
callback,
509-
mClientConfiguration.getSkipIssuerHttpsCheck())
509+
mClientConfiguration.getSkipIssuerHttpsCheck(),
510+
mClientConfiguration.getSkipIssueTimeValidation(),
511+
mClientConfiguration.getAllowedIssueTimeSkew())
510512
.execute();
511513
}
512514

@@ -585,6 +587,8 @@ private static class TokenRequestTask
585587
private TokenResponseCallback mCallback;
586588
private Clock mClock;
587589
private boolean mSkipIssuerHttpsCheck;
590+
private boolean mSkipIssueTimeValidation;
591+
private Long mAllowedIssueTimeSkew;
588592

589593
private AuthorizationException mException;
590594

@@ -593,13 +597,17 @@ private static class TokenRequestTask
593597
@NonNull ConnectionBuilder connectionBuilder,
594598
Clock clock,
595599
TokenResponseCallback callback,
596-
Boolean skipIssuerHttpsCheck) {
600+
Boolean skipIssuerHttpsCheck,
601+
Boolean skipissueTimeValidation,
602+
Long allowedIssueTimeSkew) {
597603
mRequest = request;
598604
mClientAuthentication = clientAuthentication;
599605
mConnectionBuilder = connectionBuilder;
600606
mClock = clock;
601607
mCallback = callback;
602608
mSkipIssuerHttpsCheck = skipIssuerHttpsCheck;
609+
mSkipIssueTimeValidation = skipissueTimeValidation;
610+
mAllowedIssueTimeSkew = allowedIssueTimeSkew;
603611
}
604612

605613
@Override
@@ -710,7 +718,9 @@ protected void onPostExecute(JSONObject json) {
710718
idToken.validate(
711719
mRequest,
712720
mClock,
713-
mSkipIssuerHttpsCheck
721+
mSkipIssuerHttpsCheck,
722+
mSkipIssueTimeValidation,
723+
mAllowedIssueTimeSkew
714724
);
715725
} catch (AuthorizationException ex) {
716726
mCallback.onTokenRequestCompleted(null, ex);

library/java/net/openid/appauth/IdToken.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,14 @@ static IdToken from(String token) throws JSONException, IdTokenException {
204204

205205
@VisibleForTesting
206206
void validate(@NonNull TokenRequest tokenRequest, Clock clock) throws AuthorizationException {
207-
validate(tokenRequest, clock, false);
207+
validate(tokenRequest, clock, false, false, null);
208208
}
209209

210210
void validate(@NonNull TokenRequest tokenRequest,
211211
Clock clock,
212-
boolean skipIssuerHttpsCheck) throws AuthorizationException {
212+
boolean skipIssuerHttpsCheck,
213+
boolean skipIssueTimeValidation,
214+
@Nullable Long allowedIssueTimeSkew) throws AuthorizationException {
213215
// OpenID Connect Core Section 3.1.3.7. rule #1
214216
// Not enforced: AppAuth does not support JWT encryption.
215217

@@ -276,13 +278,16 @@ void validate(@NonNull TokenRequest tokenRequest,
276278
new IdTokenException("ID Token expired"));
277279
}
278280

279-
// OpenID Connect Core Section 3.1.3.7. rule #10
280-
// Validates that the issued at time is not more than +/- 10 minutes on the current
281-
// time.
282-
if (Math.abs(nowInSeconds - this.issuedAt) > TEN_MINUTES_IN_SECONDS) {
283-
throw AuthorizationException.fromTemplate(GeneralErrors.ID_TOKEN_VALIDATION_ERROR,
284-
new IdTokenException("Issued at time is more than 10 minutes "
285-
+ "before or after the current time"));
281+
282+
if (!skipIssueTimeValidation) {
283+
// OpenID Connect Core Section 3.1.3.7. rule #10
284+
// Validates that the issued at time is not more than the +/- configured allowed time skew,
285+
// or +/- 10 minutes as a default, on the current time.
286+
if (Math.abs(nowInSeconds - this.issuedAt) > (allowedIssueTimeSkew == null ? TEN_MINUTES_IN_SECONDS : allowedIssueTimeSkew)) {
287+
throw AuthorizationException.fromTemplate(GeneralErrors.ID_TOKEN_VALIDATION_ERROR,
288+
new IdTokenException("Issued at time is more than 10 minutes "
289+
+ "before or after the current time"));
290+
}
286291
}
287292

288293
// Only relevant for the authorization_code response type

library/javatests/net/openid/appauth/IdTokenTest.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ public void testValidate_shouldSkipNonHttpsIssuer()
272272
.setRedirectUri(TEST_APP_REDIRECT_URI)
273273
.build();
274274
Clock clock = SystemClock.INSTANCE;
275-
idToken.validate(tokenRequest, clock, true);
275+
idToken.validate(tokenRequest, clock, true, false, null);
276276
}
277277

278278
@Test(expected = AuthorizationException.class)
@@ -464,6 +464,60 @@ public void testValidate_shouldFailOnIssuedAtOverTenMinutesAgo() throws Authoriz
464464
idToken.validate(tokenRequest, clock);
465465
}
466466

467+
@Test
468+
public void testValidate_withSkipIssueTimeValidation() throws AuthorizationException {
469+
Long nowInSeconds = SystemClock.INSTANCE.getCurrentTimeMillis() / 1000;
470+
Long anHourInSeconds = (long) (60 * 60);
471+
IdToken idToken = new IdToken(
472+
TEST_ISSUER,
473+
TEST_SUBJECT,
474+
Collections.singletonList(TEST_CLIENT_ID),
475+
nowInSeconds,
476+
nowInSeconds - (anHourInSeconds * 2),
477+
TEST_NONCE,
478+
TEST_CLIENT_ID
479+
);
480+
TokenRequest tokenRequest = getAuthCodeExchangeRequestWithNonce();
481+
Clock clock = SystemClock.INSTANCE;
482+
idToken.validate(tokenRequest, clock, false, true, null);
483+
}
484+
485+
@Test(expected = AuthorizationException.class)
486+
public void testValidate_shouldFailOnIssuedAtOverConfiguredTimeSkew() throws AuthorizationException {
487+
Long nowInSeconds = SystemClock.INSTANCE.getCurrentTimeMillis() / 1000;
488+
Long anHourInSeconds = (long) (60 * 60);
489+
IdToken idToken = new IdToken(
490+
TEST_ISSUER,
491+
TEST_SUBJECT,
492+
Collections.singletonList(TEST_CLIENT_ID),
493+
nowInSeconds,
494+
nowInSeconds - anHourInSeconds - 1,
495+
TEST_NONCE,
496+
TEST_CLIENT_ID
497+
);
498+
TokenRequest tokenRequest = getAuthCodeExchangeRequestWithNonce();
499+
Clock clock = SystemClock.INSTANCE;
500+
idToken.validate(tokenRequest, clock, false, false, anHourInSeconds);
501+
}
502+
503+
@Test
504+
public void testValidate_withConfiguredTimeSkew() throws AuthorizationException {
505+
Long nowInSeconds = SystemClock.INSTANCE.getCurrentTimeMillis() / 1000;
506+
Long anHourInSeconds = (long) (60 * 60);
507+
IdToken idToken = new IdToken(
508+
TEST_ISSUER,
509+
TEST_SUBJECT,
510+
Collections.singletonList(TEST_CLIENT_ID),
511+
nowInSeconds,
512+
nowInSeconds - anHourInSeconds,
513+
TEST_NONCE,
514+
TEST_CLIENT_ID
515+
);
516+
TokenRequest tokenRequest = getAuthCodeExchangeRequestWithNonce();
517+
Clock clock = SystemClock.INSTANCE;
518+
idToken.validate(tokenRequest, clock, false, false, anHourInSeconds);
519+
}
520+
467521
@Test(expected = AuthorizationException.class)
468522
public void testValidate_shouldFailOnNonceMismatch() throws AuthorizationException {
469523
Long nowInSeconds = SystemClock.INSTANCE.getCurrentTimeMillis() / 1000;

0 commit comments

Comments
 (0)