Skip to content

Commit e715def

Browse files
committed
allow tokens without exp, iat, nbf
1 parent 06d15db commit e715def

File tree

2 files changed

+221
-315
lines changed

2 files changed

+221
-315
lines changed

src/main/java/uk/ac/cam/cl/dtg/segue/auth/MicrosoftAuthenticator.java

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014 Stephen Cummins
2+
* Copyright 2025 Barna Magyarkuti
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,9 +52,9 @@ public class MicrosoftAuthenticator implements IOAuth2Authenticator {
5252
static final int CREDENTIAL_CACHE_TTL_MINUTES = 10;
5353
// TODO: why do we cache idTokens? Why don't we just pass them around in code?
5454
static Cache<String, String> credentialStore = CacheBuilder
55-
.newBuilder()
56-
.expireAfterAccess(CREDENTIAL_CACHE_TTL_MINUTES, TimeUnit.MINUTES)
57-
.build();
55+
.newBuilder()
56+
.expireAfterAccess(CREDENTIAL_CACHE_TTL_MINUTES, TimeUnit.MINUTES)
57+
.build();
5858

5959
private final String scopes = "email";
6060
private final String callbackURL = "http://localhost:8004/auth/microsoft/callback";
@@ -65,17 +65,17 @@ public class MicrosoftAuthenticator implements IOAuth2Authenticator {
6565

6666
@Inject
6767
public MicrosoftAuthenticator(
68-
@Named(Constants.MICROSOFT_CLIENT_ID) final String clientId,
69-
@Named(Constants.MICROSOFT_TENANT_ID) final String tenantId,
70-
@Named(Constants.MICROSOFT_SECRET) final String clientSecret,
71-
@Named(Constants.MICROSOFT_JWKS_URL) final String jwksUrl
68+
@Named(Constants.MICROSOFT_CLIENT_ID) final String clientId,
69+
@Named(Constants.MICROSOFT_TENANT_ID) final String tenantId,
70+
@Named(Constants.MICROSOFT_SECRET) final String clientSecret,
71+
@Named(Constants.MICROSOFT_JWKS_URL) final String jwksUrl
7272
) throws MalformedURLException {
7373
this.clientId = clientId;
7474
this.tenantId = tenantId;
7575
this.clientSecret = clientSecret;
7676
provider = new UrlJwkProvider(new URL(jwksUrl));
7777
}
78-
78+
7979
@Override
8080
public AuthenticationProvider getAuthenticationProvider() {
8181
return AuthenticationProvider.MICROSOFT;
@@ -89,11 +89,11 @@ public String getFriendlyName() {
8989
@Override
9090
public String getAuthorizationUrl(String antiForgeryStateToken) {
9191
var parameters = AuthorizationRequestUrlParameters
92-
.builder(callbackURL, Collections.singleton(scopes))
93-
.responseMode(ResponseMode.QUERY)
94-
.prompt(Prompt.SELECT_ACCOUNT)
95-
.state(antiForgeryStateToken)
96-
.build();
92+
.builder(callbackURL, Collections.singleton(scopes))
93+
.responseMode(ResponseMode.QUERY)
94+
.prompt(Prompt.SELECT_ACCOUNT)
95+
.state(antiForgeryStateToken)
96+
.build();
9797
return client().getAuthorizationRequestUrl(parameters).toString();
9898
}
9999

@@ -111,11 +111,11 @@ public String extractAuthCode(String url) {
111111
}
112112

113113
@Override
114-
public String exchangeCode(String authorizationCode) throws CodeExchangeException{
114+
public String exchangeCode(String authorizationCode) throws CodeExchangeException {
115115
try {
116116
var authParams = AuthorizationCodeParameters
117-
.builder(authorizationCode, new URI(callbackURL)).scopes(Collections.singleton(scopes))
118-
.build();
117+
.builder(authorizationCode, new URI(callbackURL)).scopes(Collections.singleton(scopes))
118+
.build();
119119
var result = client().acquireToken(authParams).get();
120120

121121
String internalCredentialID = UUID.randomUUID().toString();
@@ -135,23 +135,23 @@ public UserFromAuthProvider getUserInfo(String internalProviderReference) throws
135135
var token = parseAndVerifyToken(tokenStr);
136136
// TODO: to support sign-ups, parse more info
137137
return new UserFromAuthProvider(
138-
token.getSubject(), null, null, token.getClaim("email").asString(),
139-
null, null, null, null, null, null
138+
token.getSubject(), null, null, token.getClaim("email").asString(),
139+
null, null, null, null, null, null
140140
);
141141
}
142142

143143
private ConfidentialClientApplication client() {
144144
var secret = ClientCredentialFactory.createFromSecret(clientSecret);
145145
try {
146146
return ConfidentialClientApplication.builder(clientId, secret)
147-
.authority(String.format("https://login.microsoftonline.com/%s", tenantId))
148-
.build();
147+
.authority(String.format("https://login.microsoftonline.com/%s", tenantId))
148+
.build();
149149
} catch (MalformedURLException e) {
150150
throw new RuntimeException(e);
151151
}
152152
}
153153

154-
private DecodedJWT parseAndVerifyToken (String tokenStr) throws AuthenticatorSecurityException {
154+
private DecodedJWT parseAndVerifyToken(String tokenStr) throws AuthenticatorSecurityException {
155155
// validating id token based on requirements at
156156
// https://learn.microsoft.com/en-us/entra/identity-platform/id-tokens
157157
// I've ignored "nonce" validation as RaspberryPi Authenticator also skips it
@@ -161,27 +161,16 @@ private DecodedJWT parseAndVerifyToken (String tokenStr) throws AuthenticatorSec
161161
var jwk = provider.get(keyId);
162162
var algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey());
163163
var verifier = JWT.require(algorithm)
164-
.withAudience(clientId)
165-
.withIssuer(String.format("https://login.microsoftonline.com/%s/v2.0", tenantId))
166-
.build();
164+
.withAudience(clientId)
165+
.withIssuer(String.format("https://login.microsoftonline.com/%s/v2.0", tenantId))
166+
.build();
167167
verifier.verify(tokenStr); // TODO: does this check validity of cert?
168168
if (null == keyId) {
169169
throw new AuthenticatorSecurityException("Token verification: NO_KEY_ID");
170170
}
171-
if (null == token.getExpiresAt()) {
172-
throw new AuthenticatorSecurityException("Token verification: NULL_EXPIRY");
173-
}
174-
if (null == token.getIssuedAt()) {
175-
throw new AuthenticatorSecurityException("Token verification: NULL_ISSUED_AT");
176-
}
177-
if (null == token.getNotBefore()) {
178-
throw new AuthenticatorSecurityException("Token verification: NULL_NOT_BEFORE");
179-
}
180-
}
181-
catch (InvalidPublicKeyException e) {
171+
} catch (InvalidPublicKeyException e) {
182172
throw new AuthenticatorSecurityException("Token verification: INVALID_PUBLIC_KEY");
183-
}
184-
catch (JwkException e) {
173+
} catch (JwkException e) {
185174
throw new AuthenticatorSecurityException(e.getMessage());
186175
}
187176
return token;

0 commit comments

Comments
 (0)