@@ -46,6 +46,7 @@ void getUserInfo_validToken_returnsUserInformation() throws Throwable {
46
46
.withNotBefore (oneHourAgo )
47
47
.withExpiresAt (inOneHour )
48
48
.withAudience (clientId )
49
+ .withIssuer (expectedIssuer )
49
50
.withPayload ("{\" email\" : \" test@example.com\" }" ));
50
51
var userInfo = testGetUserInfo (token );
51
52
assertEquals ("test@example.com" , userInfo .getEmail ());
@@ -58,11 +59,10 @@ void getUserInfo_nullToStore_throwsError() {
58
59
59
60
@ Test
60
61
void getUserInfo_noSuchToken_throwsError () throws MalformedURLException {
61
- var store = getStore ();
62
62
var subject = new MicrosoftAuthenticator (
63
63
clientId , "" , "" , "http://localhost:8888/keys"
64
64
) {{
65
- MicrosoftAuthenticator .credentialStore = store ;
65
+ MicrosoftAuthenticator .credentialStore = getStore () ;
66
66
}};
67
67
var error = assertThrows (AuthenticatorSecurityException .class , () -> subject .getUserInfo ("no_token_for_id" ));
68
68
assertEquals ("Token verification: TOKEN_MISSING" , error .getMessage ());
@@ -75,7 +75,8 @@ void getUserInfo_tokenSignatureNoKeyId_throwsError() {
75
75
.withNotBefore (oneHourAgo )
76
76
.withExpiresAt (inOneHour )
77
77
.withAudience (clientId )
78
- .withKeyId ((String ) null ));
78
+ .withIssuer (expectedIssuer )
79
+ .withKeyId (null ));
79
80
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
80
81
assertEquals ("Token verification: NO_KEY_ID" , error .getMessage ());
81
82
}
@@ -96,9 +97,11 @@ void getUserInfo_tokenSignatureMismatch_throwsError() {
96
97
97
98
@ Test
98
99
void getUserInfo_tokenNoExp_throwsError () {
99
- String token = signedToken (validSigningKey , t -> t . withAudience ( clientId )
100
+ String token = signedToken (validSigningKey , t -> t
100
101
.withIssuedAt (oneHourAgo )
101
102
.withNotBefore (oneHourAgo )
103
+ .withAudience (clientId )
104
+ .withIssuer (expectedIssuer )
102
105
);
103
106
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
104
107
assertEquals ("Token verification: NULL_EXPIRY" , error .getMessage ());
@@ -110,6 +113,7 @@ void getUserInfo_tokenNullExp_throwsError() {
110
113
.withIssuedAt (oneHourAgo )
111
114
.withNotBefore (oneHourAgo )
112
115
.withAudience (clientId )
116
+ .withIssuer (expectedIssuer )
113
117
.withExpiresAt ((Date ) null )
114
118
);
115
119
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
@@ -122,6 +126,7 @@ void getUserInfo_tokenExpired_throwsError() {
122
126
.withIssuedAt (oneHourAgo )
123
127
.withNotBefore (oneHourAgo )
124
128
.withAudience (clientId )
129
+ .withIssuer (expectedIssuer )
125
130
.withExpiresAt (oneHourAgo )
126
131
);
127
132
var error = assertThrows (TokenExpiredException .class , () -> testGetUserInfo (token ));
@@ -134,6 +139,7 @@ void getUserInfo_tokenNoIat_throwsError() {
134
139
.withExpiresAt (inOneHour )
135
140
.withNotBefore (oneHourAgo )
136
141
.withAudience (clientId )
142
+ .withIssuer (expectedIssuer )
137
143
);
138
144
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
139
145
assertEquals ("Token verification: NULL_ISSUED_AT" , error .getMessage ());
@@ -145,6 +151,7 @@ void getUserInfo_tokenNullIat_throwsError() {
145
151
.withExpiresAt (inOneHour )
146
152
.withNotBefore (oneHourAgo )
147
153
.withAudience (clientId )
154
+ .withIssuer (expectedIssuer )
148
155
.withIssuedAt ((Date ) null )
149
156
);
150
157
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
@@ -157,6 +164,7 @@ void getUserInfo_tokenIatFuture_throwsError() {
157
164
.withExpiresAt (inOneHour )
158
165
.withNotBefore (oneHourAgo )
159
166
.withAudience (clientId )
167
+ .withIssuer (expectedIssuer )
160
168
.withIssuedAt (inOneHour )
161
169
);
162
170
var error = assertThrows (IncorrectClaimException .class , () -> testGetUserInfo (token ));
@@ -169,6 +177,7 @@ void getUserInfo_tokenNoNbf_throwsError() {
169
177
.withExpiresAt (inOneHour )
170
178
.withIssuedAt (oneHourAgo )
171
179
.withAudience (clientId )
180
+ .withIssuer (expectedIssuer )
172
181
);
173
182
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
174
183
assertEquals ("Token verification: NULL_NOT_BEFORE" , error .getMessage ());
@@ -180,6 +189,7 @@ void getUserInfo_tokenNullNbf_throwsError() {
180
189
.withIssuedAt (oneHourAgo )
181
190
.withExpiresAt (inOneHour )
182
191
.withAudience (clientId )
192
+ .withIssuer (expectedIssuer )
183
193
.withNotBefore ((Date ) null )
184
194
);
185
195
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
@@ -192,6 +202,7 @@ void getUserInfo_tokenNbfFuture_throwsError() {
192
202
.withExpiresAt (inOneHour )
193
203
.withIssuedAt (oneHourAgo )
194
204
.withAudience (clientId )
205
+ .withIssuer (expectedIssuer )
195
206
.withNotBefore (inOneHour )
196
207
);
197
208
var error = assertThrows (IncorrectClaimException .class , () -> testGetUserInfo (token ));
@@ -231,13 +242,51 @@ void getUserInfo_tokenAudIncorrect_throwsError() {
231
242
var error = assertThrows (IncorrectClaimException .class , () -> testGetUserInfo (token ));
232
243
assertEquals ("The Claim 'aud' value doesn't contain the required audience." , error .getMessage ());
233
244
}
245
+
246
+ @ Test
247
+ void getUserInfo_tokenNoIssuer_throwsError () {
248
+ String token = signedToken (validSigningKey , t -> t
249
+ .withIssuedAt (oneHourAgo )
250
+ .withExpiresAt (inOneHour )
251
+ .withNotBefore (oneHourAgo )
252
+ .withAudience (clientId )
253
+ );
254
+ var error = assertThrows (MissingClaimException .class , () -> testGetUserInfo (token ));
255
+ assertEquals ("The Claim 'iss' is not present in the JWT." , error .getMessage ());
256
+ }
257
+
258
+ @ Test
259
+ void getUserInfo_tokenIssuerIncorrrect_throwsError () {
260
+ String token = signedToken (validSigningKey , t -> t
261
+ .withIssuedAt (oneHourAgo )
262
+ .withExpiresAt (inOneHour )
263
+ .withNotBefore (oneHourAgo )
264
+ .withAudience (clientId )
265
+ .withIssuer (null )
266
+ );
267
+ var error = assertThrows (IncorrectClaimException .class , () -> testGetUserInfo (token ));
268
+ assertEquals ("The Claim 'iss' value doesn't match the required issuer." , error .getMessage ());
269
+ }
270
+
271
+ @ Test
272
+ void getUserInfo_tokenNullIssuer_throwsError () {
273
+ String token = signedToken (validSigningKey , t -> t
274
+ .withIssuedAt (oneHourAgo )
275
+ .withExpiresAt (inOneHour )
276
+ .withNotBefore (oneHourAgo )
277
+ .withAudience (clientId )
278
+ .withIssuer ("some_bad_issuer" )
279
+ );
280
+ var error = assertThrows (IncorrectClaimException .class , () -> testGetUserInfo (token ));
281
+ assertEquals ("The Claim 'iss' value doesn't match the required issuer." , error .getMessage ());
282
+ }
234
283
}
235
284
236
285
class Helpers {
237
286
static UserFromAuthProvider testGetUserInfo (String token ) throws Throwable {
238
287
var store = getStore ();
239
288
var subject = new MicrosoftAuthenticator (
240
- clientId , "" , "" , "http://localhost:8888/keys"
289
+ clientId , tenantId , "" , "http://localhost:8888/keys"
241
290
) {{
242
291
MicrosoftAuthenticator .credentialStore = store ;
243
292
}};
@@ -271,6 +320,8 @@ static Cache<String, String> getStore() {
271
320
static Instant oneHourAgo = Instant .now ().minusSeconds (60 * 60 ).truncatedTo (ChronoUnit .SECONDS );
272
321
static Instant inOneHour = Instant .now ().plusSeconds (60 * 60 ).truncatedTo (ChronoUnit .SECONDS );
273
322
static String clientId = "the_client_id" ;
323
+ static String tenantId = "common" ;
324
+ static String expectedIssuer = "https://login.microsoftonline.com/common/v2.0" ;
274
325
}
275
326
276
327
class TestKeyServer {
0 commit comments