18
18
import org .json .JSONArray ;
19
19
import org .json .JSONObject ;
20
20
import org .junit .jupiter .api .Test ;
21
+ import org .junit .jupiter .api .function .ThrowingSupplier ;
21
22
import uk .ac .cam .cl .dtg .isaac .dos .users .UserFromAuthProvider ;
22
23
import uk .ac .cam .cl .dtg .segue .auth .exceptions .AuthenticatorSecurityException ;
23
24
24
25
import java .io .IOException ;
26
+ import java .net .MalformedURLException ;
25
27
import java .security .KeyPair ;
26
28
import java .security .KeyPairGenerator ;
27
29
import java .security .NoSuchAlgorithmException ;
38
40
39
41
class MicrosoftAuthenticatorTest extends Helpers {
40
42
@ Test
41
- void getUserInfo_validToken_returnsUserInformation () throws Exception {
43
+ void getUserInfo_validToken_returnsUserInformation () throws Throwable {
42
44
String token = signedToken (validSigningKey , t -> t
43
45
.withIssuedAt (oneHourAgo )
44
46
.withNotBefore (oneHourAgo )
@@ -49,9 +51,31 @@ void getUserInfo_validToken_returnsUserInformation() throws Exception {
49
51
assertEquals ("test@example.com" , userInfo .getEmail ());
50
52
}
51
53
54
+ @ Test
55
+ void getUserInfo_nullToStore_throwsError () {
56
+ assertThrows (NullPointerException .class , () -> testGetUserInfo (null ));
57
+ }
58
+
59
+ @ Test
60
+ void getUserInfo_noSuchToken_throwsError () throws MalformedURLException {
61
+ var store = getStore ();
62
+ var subject = new MicrosoftAuthenticator (
63
+ clientId , "" , "" , "http://localhost:8888/keys"
64
+ ) {{
65
+ MicrosoftAuthenticator .credentialStore = store ;
66
+ }};
67
+ var error = assertThrows (AuthenticatorSecurityException .class , () -> subject .getUserInfo ("no_token_for_id" ));
68
+ assertEquals ("Token verification: TOKEN_MISSING" , error .getMessage ());
69
+ }
70
+
52
71
@ Test
53
72
void getUserInfo_tokenSignatureNoKeyId_throwsError () {
54
- String token = signedToken (validSigningKey , t -> t .withKeyId ((String ) null ));
73
+ String token = signedToken (validSigningKey , t -> t
74
+ .withIssuedAt (oneHourAgo )
75
+ .withNotBefore (oneHourAgo )
76
+ .withExpiresAt (inOneHour )
77
+ .withAudience (clientId )
78
+ .withKeyId ((String ) null ));
55
79
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
56
80
assertEquals ("Token verification: NO_KEY_ID" , error .getMessage ());
57
81
}
@@ -118,10 +142,10 @@ void getUserInfo_tokenNoIat_throwsError() {
118
142
@ Test
119
143
void getUserInfo_tokenNullIat_throwsError () {
120
144
String token = signedToken (validSigningKey , t -> t
121
- .withExpiresAt (inOneHour )
122
- .withNotBefore (oneHourAgo )
123
- .withAudience (clientId )
124
- .withIssuedAt ((Date ) null )
145
+ .withExpiresAt (inOneHour )
146
+ .withNotBefore (oneHourAgo )
147
+ .withAudience (clientId )
148
+ .withIssuedAt ((Date ) null )
125
149
);
126
150
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
127
151
assertEquals ("Token verification: NULL_ISSUED_AT" , error .getMessage ());
@@ -130,10 +154,10 @@ void getUserInfo_tokenNullIat_throwsError() {
130
154
@ Test
131
155
void getUserInfo_tokenIatFuture_throwsError () {
132
156
String token = signedToken (validSigningKey , t -> t
133
- .withExpiresAt (inOneHour )
134
- .withNotBefore (oneHourAgo )
135
- .withAudience (clientId )
136
- .withIssuedAt (inOneHour )
157
+ .withExpiresAt (inOneHour )
158
+ .withNotBefore (oneHourAgo )
159
+ .withAudience (clientId )
160
+ .withIssuedAt (inOneHour )
137
161
);
138
162
var error = assertThrows (IncorrectClaimException .class , () -> testGetUserInfo (token ));
139
163
assertEquals (String .format ("The Token can't be used before %s." , inOneHour ), error .getMessage ());
@@ -142,9 +166,9 @@ void getUserInfo_tokenIatFuture_throwsError() {
142
166
@ Test
143
167
void getUserInfo_tokenNoNbf_throwsError () {
144
168
String token = signedToken (validSigningKey , t -> t
145
- .withExpiresAt (inOneHour )
146
- .withIssuedAt (oneHourAgo )
147
- .withAudience (clientId )
169
+ .withExpiresAt (inOneHour )
170
+ .withIssuedAt (oneHourAgo )
171
+ .withAudience (clientId )
148
172
);
149
173
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
150
174
assertEquals ("Token verification: NULL_NOT_BEFORE" , error .getMessage ());
@@ -153,10 +177,10 @@ void getUserInfo_tokenNoNbf_throwsError() {
153
177
@ Test
154
178
void getUserInfo_tokenNullNbf_throwsError () {
155
179
String token = signedToken (validSigningKey , t -> t
156
- .withIssuedAt (oneHourAgo )
157
- .withExpiresAt (inOneHour )
158
- .withAudience (clientId )
159
- .withNotBefore ((Date ) null )
180
+ .withIssuedAt (oneHourAgo )
181
+ .withExpiresAt (inOneHour )
182
+ .withAudience (clientId )
183
+ .withNotBefore ((Date ) null )
160
184
);
161
185
var error = assertThrows (AuthenticatorSecurityException .class , () -> testGetUserInfo (token ));
162
186
assertEquals ("Token verification: NULL_NOT_BEFORE" , error .getMessage ());
@@ -165,10 +189,10 @@ void getUserInfo_tokenNullNbf_throwsError() {
165
189
@ Test
166
190
void getUserInfo_tokenNbfFuture_throwsError () {
167
191
String token = signedToken (validSigningKey , t -> t
168
- .withExpiresAt (inOneHour )
169
- .withIssuedAt (oneHourAgo )
170
- .withAudience (clientId )
171
- .withNotBefore (inOneHour )
192
+ .withExpiresAt (inOneHour )
193
+ .withIssuedAt (oneHourAgo )
194
+ .withAudience (clientId )
195
+ .withNotBefore (inOneHour )
172
196
);
173
197
var error = assertThrows (IncorrectClaimException .class , () -> testGetUserInfo (token ));
174
198
assertEquals (String .format ("The Token can't be used before %s." , inOneHour ), error .getMessage ());
@@ -177,8 +201,8 @@ void getUserInfo_tokenNbfFuture_throwsError() {
177
201
@ Test
178
202
void getUserInfo_tokenNoAud_throwsError () {
179
203
String token = signedToken (validSigningKey , t -> t
180
- .withExpiresAt (inOneHour )
181
- .withIssuedAt (oneHourAgo )
204
+ .withExpiresAt (inOneHour )
205
+ .withIssuedAt (oneHourAgo )
182
206
);
183
207
var error = assertThrows (MissingClaimException .class , () -> testGetUserInfo (token ));
184
208
assertEquals ("The Claim 'aud' is not present in the JWT." , error .getMessage ());
@@ -187,10 +211,10 @@ void getUserInfo_tokenNoAud_throwsError() {
187
211
@ Test
188
212
void getUserInfo_tokenNullAud_throwsError () {
189
213
String token = signedToken (validSigningKey , t -> t
190
- .withIssuedAt (oneHourAgo )
191
- .withExpiresAt (inOneHour )
192
- .withNotBefore (oneHourAgo )
193
- .withAudience ((String ) null )
214
+ .withIssuedAt (oneHourAgo )
215
+ .withExpiresAt (inOneHour )
216
+ .withNotBefore (oneHourAgo )
217
+ .withAudience ((String ) null )
194
218
);
195
219
var error = assertThrows (IncorrectClaimException .class , () -> testGetUserInfo (token ));
196
220
assertEquals ("The Claim 'aud' value doesn't contain the required audience." , error .getMessage ());
@@ -199,46 +223,54 @@ void getUserInfo_tokenNullAud_throwsError() {
199
223
@ Test
200
224
void getUserInfo_tokenAudIncorrect_throwsError () {
201
225
String token = signedToken (validSigningKey , t -> t
202
- .withIssuedAt (oneHourAgo )
203
- .withExpiresAt (inOneHour )
204
- .withNotBefore (oneHourAgo )
205
- .withAudience ("intended_for_somebody_else" )
226
+ .withIssuedAt (oneHourAgo )
227
+ .withExpiresAt (inOneHour )
228
+ .withNotBefore (oneHourAgo )
229
+ .withAudience ("intended_for_somebody_else" )
206
230
);
207
231
var error = assertThrows (IncorrectClaimException .class , () -> testGetUserInfo (token ));
208
232
assertEquals ("The Claim 'aud' value doesn't contain the required audience." , error .getMessage ());
209
233
}
210
234
}
211
235
212
236
class Helpers {
213
- public static UserFromAuthProvider testGetUserInfo (String token ) throws Exception {
237
+ static UserFromAuthProvider testGetUserInfo (String token ) throws Throwable {
238
+ var store = getStore ();
239
+ var subject = new MicrosoftAuthenticator (
240
+ clientId , "" , "" , "http://localhost:8888/keys"
241
+ ) {{
242
+ MicrosoftAuthenticator .credentialStore = store ;
243
+ }};
244
+ store .put ("the_internal_id" , token );
245
+ return withKeyServer (() -> subject .getUserInfo ("the_internal_id" ));
246
+ }
247
+
248
+ static <T > T withKeyServer (ThrowingSupplier <T > fn ) throws Throwable {
214
249
var keyServer = TestKeyServer .withKey (validSigningKey ).start (8888 );
215
- Cache <String , String > store = CacheBuilder .newBuilder ()
216
- .expireAfterAccess (10 , TimeUnit .MINUTES )
217
- .build ();
218
250
try {
219
- var subject = new MicrosoftAuthenticator (
220
- clientId , "" , "" , "http://localhost:8888/keys"
221
- ) {{
222
- MicrosoftAuthenticator .credentialStore = store ;
223
- }};
224
- store .put ("the_internal_id" , token );
225
- return subject .getUserInfo ("the_internal_id" );
251
+ return fn .get ();
226
252
} finally {
227
253
keyServer .stop ();
228
254
}
229
255
}
230
256
231
- public static String signedToken (TestKeyPair key , Function <JWTCreator .Builder , JWTCreator .Builder > fn ) {
257
+ static String signedToken (TestKeyPair key , Function <JWTCreator .Builder , JWTCreator .Builder > fn ) {
232
258
var algorithm = Algorithm .RSA256 (key .publicKey (), key .privateKey ());
233
259
var token = fn .apply (JWT .create ().withKeyId (key .id ()));
234
260
return token .sign (algorithm );
235
261
}
236
262
237
- public static TestKeyPair validSigningKey = new TestKeyPair ();
238
- public static TestKeyPair invalidSigningKey = new TestKeyPair ();
239
- public static Instant oneHourAgo = Instant .now ().minusSeconds (60 * 60 ).truncatedTo (ChronoUnit .SECONDS );
240
- public static Instant inOneHour = Instant .now ().plusSeconds (60 * 60 ).truncatedTo (ChronoUnit .SECONDS );
241
- public static String clientId = "the_client_id" ;
263
+ static Cache <String , String > getStore () {
264
+ return CacheBuilder .newBuilder ()
265
+ .expireAfterAccess (10 , TimeUnit .MINUTES )
266
+ .build ();
267
+ }
268
+
269
+ static TestKeyPair validSigningKey = new TestKeyPair ();
270
+ static TestKeyPair invalidSigningKey = new TestKeyPair ();
271
+ static Instant oneHourAgo = Instant .now ().minusSeconds (60 * 60 ).truncatedTo (ChronoUnit .SECONDS );
272
+ static Instant inOneHour = Instant .now ().plusSeconds (60 * 60 ).truncatedTo (ChronoUnit .SECONDS );
273
+ static String clientId = "the_client_id" ;
242
274
}
243
275
244
276
class TestKeyServer {
0 commit comments