26
26
import org .apache .cxf .message .Message ;
27
27
import org .wso2 .carbon .apimgt .api .APIManagementException ;
28
28
import org .wso2 .carbon .apimgt .api .OAuthTokenInfo ;
29
+ import org .wso2 .carbon .apimgt .api .dto .KeyManagerConfigurationDTO ;
29
30
import org .wso2 .carbon .apimgt .common .gateway .constants .JWTConstants ;
30
31
import org .wso2 .carbon .apimgt .common .gateway .dto .JWTValidationInfo ;
31
32
import org .wso2 .carbon .apimgt .common .gateway .dto .TokenIssuerDto ;
32
33
import org .wso2 .carbon .apimgt .impl .APIConstants ;
33
34
import org .wso2 .carbon .apimgt .impl .APIConstants .JwtTokenConstants ;
34
35
import org .wso2 .carbon .apimgt .impl .RESTAPICacheConfiguration ;
36
+ import org .wso2 .carbon .apimgt .impl .dto .KeyManagerDto ;
37
+ import org .wso2 .carbon .apimgt .impl .factory .KeyManagerHolder ;
35
38
import org .wso2 .carbon .apimgt .impl .jwt .JWTValidator ;
36
39
import org .wso2 .carbon .apimgt .impl .jwt .SignedJWTInfo ;
37
40
import org .wso2 .carbon .apimgt .impl .utils .APIUtil ;
52
55
import java .net .URL ;
53
56
import java .text .ParseException ;
54
57
import java .util .Date ;
55
- import java .util .HashMap ;
56
- import java .util .List ;
57
58
import java .util .Map ;
58
59
59
- import static org .wso2 .carbon .apimgt .rest .api .common .APIMConfigUtil .getRestApiJWTAuthAudiences ;
60
-
61
60
/**
62
61
* This OAuthJwtAuthenticatorImpl class specifically implemented for API Manager store and publisher rest APIs'
63
62
* JWT based authentication.
@@ -223,10 +222,11 @@ private JWTValidationInfo validateJWTToken(SignedJWTInfo signedJWTInfo, String j
223
222
224
223
JWTValidationInfo jwtValidationInfo ;
225
224
String issuer = signedJWTInfo .getJwtClaimsSet ().getIssuer ();
225
+ String subject = signedJWTInfo .getJwtClaimsSet ().getSubject ();
226
226
227
227
if (StringUtils .isNotEmpty (issuer )) {
228
228
//validate Issuer
229
- if (tokenIssuers != null && tokenIssuers . containsKey ( issuer )) {
229
+ if (tokenIssuers != null && validateIssuer ( subject , issuer , maskedToken )) {
230
230
if (isRESTApiTokenCacheEnabled ) {
231
231
JWTValidationInfo tempJWTValidationInfo = (JWTValidationInfo ) getRESTAPITokenCache ().get (jti );
232
232
if (tempJWTValidationInfo != null ) {
@@ -256,27 +256,38 @@ private JWTValidationInfo validateJWTToken(SignedJWTInfo signedJWTInfo, String j
256
256
}
257
257
}
258
258
//info not in cache. validate signature and exp
259
- JWTValidator jwtValidator = APIMConfigUtil .getJWTValidatorMap ().get (issuer );
260
- jwtValidationInfo = jwtValidator .validateToken (signedJWTInfo );
261
- if (jwtValidationInfo .isValid ()) {
262
- //valid token
263
- if (isRESTApiTokenCacheEnabled ) {
264
- getRESTAPITokenCache ().put (jti , jwtValidationInfo );
259
+ JWTValidator jwtValidator = getJWTValidator (issuer , subject , maskedToken );
260
+ if (jwtValidator != null ) {
261
+ jwtValidationInfo = jwtValidator .validateToken (signedJWTInfo );
262
+ if (jwtValidationInfo .isValid ()) {
263
+ //valid token
264
+ if (isRESTApiTokenCacheEnabled ) {
265
+ getRESTAPITokenCache ().put (jti , jwtValidationInfo );
266
+ }
267
+ } else {
268
+ //put in invalid cache
269
+ if (isRESTApiTokenCacheEnabled ) {
270
+ getRESTAPIInvalidTokenCache ().put (jti , jwtValidationInfo );
271
+ }
272
+ //invalid credentials : 900901 error code
273
+ log .error ("JWT token validation failed. Reason: Invalid Credentials. " +
274
+ "Make sure you have provided the correct security credentials in the token :"
275
+ + maskedToken );
265
276
}
266
277
} else {
267
- //put in invalid cache
268
- if (isRESTApiTokenCacheEnabled ) {
269
- getRESTAPIInvalidTokenCache ().put (jti , jwtValidationInfo );
270
- }
271
- //invalid credentials : 900901 error code
272
- log .error ("JWT token validation failed. Reason: Invalid Credentials. " +
273
- "Make sure you have provided the correct security credentials in the token :"
274
- + maskedToken );
278
+ log .error ("JWT token issuer validation failed. Reason: Cannot find a JWTValidator for the " +
279
+ "issuer present in the JWT: " + issuer );
280
+ return null ;
275
281
}
276
282
} else {
277
283
//invalid issuer. invalid token
278
- log .error ("JWT token issuer validation failed. Reason: Issuer present in the JWT (" + issuer
279
- + ") does not match with the token issuer (" + tokenIssuers .keySet ().toString () + ")" );
284
+ if (tokenIssuers != null ) {
285
+ log .error ("JWT token issuer validation failed. Reason: Issuer present in the JWT (" + issuer
286
+ + ") does not match with the token issuer (" + tokenIssuers .keySet () + ")" );
287
+ } else {
288
+ log .error ("JWT token issuer validation failed. Reason: Issuer present in the JWT (" + issuer
289
+ + ") does not match with the token issuer." );
290
+ }
280
291
return null ;
281
292
}
282
293
} else {
@@ -286,6 +297,72 @@ private JWTValidationInfo validateJWTToken(SignedJWTInfo signedJWTInfo, String j
286
297
return jwtValidationInfo ;
287
298
}
288
299
300
+ /**
301
+ * Get logged-in organization from the sub claim of the token.
302
+ *
303
+ * @param subject Sub claim value
304
+ * @param maskedToken Masked token for logging
305
+ * @return Organization
306
+ */
307
+ private String getOrganizationFromSubject (String subject , String maskedToken ) {
308
+ if (subject == null ) {
309
+ log .error ("Subject is not found in the token " + maskedToken );
310
+ return null ;
311
+ }
312
+ return MultitenantUtils .getTenantDomain (subject );
313
+ }
314
+
315
+ /**
316
+ * Retrieve JWT Validator for the given issuer.
317
+ *
318
+ * @param issuer Issuer from the token
319
+ * @param subject Subject from the token
320
+ * @param maskedToken Masked token string for logging
321
+ * @return JWTValidator implementation for the given issuer.
322
+ */
323
+ private JWTValidator getJWTValidator (String issuer , String subject , String maskedToken ) {
324
+
325
+ JWTValidator jwtValidator = APIMConfigUtil .getJWTValidatorMap ().get (issuer );
326
+ if (jwtValidator == null ) {
327
+ String organization = getOrganizationFromSubject (subject , maskedToken );
328
+ if (StringUtils .isNotEmpty (issuer ) && StringUtils .isNotEmpty (organization )) {
329
+ KeyManagerDto keyManagerDto = KeyManagerHolder .getKeyManagerByIssuer (organization , issuer );
330
+ if (keyManagerDto != null && keyManagerDto .getJwtValidator () != null ) {
331
+ jwtValidator = keyManagerDto .getJwtValidator ();
332
+ }
333
+ }
334
+ }
335
+ return jwtValidator ;
336
+ }
337
+
338
+ /**
339
+ * Validate issuer in the token against the registered token issuers/default key manager issuer.
340
+ *
341
+ * @param subject Subject to derive the logged-in organization
342
+ * @param tokenIssuer Token issuer from the token
343
+ * @param maskedToken Masked token for logging purposes
344
+ * @return if issuer validation fails or sucess
345
+ * @throws APIManagementException if an error occurs during validation
346
+ */
347
+ private boolean validateIssuer (String subject , String tokenIssuer , String maskedToken )
348
+ throws APIManagementException {
349
+
350
+ String organization = getOrganizationFromSubject (subject , maskedToken );
351
+ if (tokenIssuers != null && !tokenIssuers .isEmpty ()) {
352
+ return tokenIssuers .containsKey (tokenIssuer );
353
+ }
354
+ KeyManagerConfigurationDTO keyManagerConfigurationDTO =
355
+ APIUtil .getDefaultKeyManagerConfiguration (organization );
356
+ if (keyManagerConfigurationDTO == null ) {
357
+ //invalid issuer. invalid token
358
+ log .error ("JWT token issuer validation failed. Reason: Default Key Manager configuration cannot be"
359
+ + " found for the organization: " + organization );
360
+ return false ;
361
+ }
362
+ return StringUtils .equals (tokenIssuer , (String ) keyManagerConfigurationDTO .getAdditionalProperties ()
363
+ .get (APIConstants .KeyManager .ISSUER ));
364
+ }
365
+
289
366
/**
290
367
* Retrieve token issuer details from deployment.toml file.
291
368
*
0 commit comments