Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ES-394] #469

Merged
merged 3 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public interface OAuthService {
* @return
* @throws EsignetException
*/
TokenResponse getTokens(@Valid TokenRequest tokenRequest) throws EsignetException;
TokenResponse getTokens(@Valid TokenRequest tokenRequest,boolean isV2) throws EsignetException;

/**
* API to get list of IdP public keys
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public interface TokenService {
* exp : Expiration time on or after which the ID Token MUST NOT be accepted for processing.
* iat : OPTIONAL. Time at which the JWT was issued.
*/
void verifyClientAssertionToken(String clientId, String jwk, String clientAssertion) throws EsignetException;
void verifyClientAssertionToken(String clientId, String jwk, String clientAssertion,String audience) throws EsignetException;

/**
* Verifies access token signature and also the claims with expected values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public TokenResponse getToken(@RequestParam MultiValueMap<String,String> paramMa
throw new InvalidRequestException(violations.stream().findFirst().get().getMessageTemplate()); //NOSONAR isPresent() check is done before accessing the value
}
try {
return oAuthService.getTokens(tokenRequest);
return oAuthService.getTokens(tokenRequest,false);
} catch (EsignetException ex) {
auditWrapper.logAudit(Action.GENERATE_TOKEN, ActionStatus.ERROR, AuditHelper.buildAuditDto(paramMap.getFirst("client_id")), ex);
throw ex;
Expand All @@ -71,7 +71,7 @@ public TokenResponse getTokenV2(@RequestParam MultiValueMap<String,String> param
throw new InvalidRequestException(violations.stream().findFirst().get().getMessageTemplate()); //NOSONAR isPresent() check is done before accessing the value
}
try {
return oAuthService.getTokens(tokenRequest);
return oAuthService.getTokens(tokenRequest,true);
} catch (EsignetException ex) {
auditWrapper.logAudit(Action.GENERATE_TOKEN, ActionStatus.ERROR, AuditHelper.buildAuditDto(paramMap.getFirst("client_id")), ex);
throw ex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void getToken_withInvalidContentType_thenFail() throws Exception {
@Test
public void getToken_withValidInput_thenPass() throws Exception {
TokenResponse tokenResponse = new TokenResponse();
Mockito.when(oAuthServiceImpl.getTokens(Mockito.any(TokenRequest.class))).thenReturn(tokenResponse);
Mockito.when(oAuthServiceImpl.getTokens(Mockito.any(TokenRequest.class),Mockito.anyBoolean())).thenReturn(tokenResponse);

mockMvc.perform(post("/oauth/token")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
Expand All @@ -110,7 +110,7 @@ public void getToken_withValidInput_thenPass() throws Exception {

@Test
public void getToken_withInvalidInput_thenFail() throws Exception {
Mockito.when(oAuthServiceImpl.getTokens(Mockito.any(TokenRequest.class))).thenThrow(InvalidRequestException.class);
Mockito.when(oAuthServiceImpl.getTokens(Mockito.any(TokenRequest.class),Mockito.anyBoolean())).thenThrow(InvalidRequestException.class);
mockMvc.perform(post("/oauth/token")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE))
.andExpect(status().isBadRequest());
Expand All @@ -122,7 +122,7 @@ public void getToken_withInvalidInput_thenFail() throws Exception {

@Test
public void getToken_withRuntimeFailure_thenFail() throws Exception {
Mockito.when(oAuthServiceImpl.getTokens(Mockito.any(TokenRequest.class))).thenThrow(EsignetException.class);
Mockito.when(oAuthServiceImpl.getTokens(Mockito.any(TokenRequest.class),Mockito.anyBoolean())).thenThrow(EsignetException.class);
mockMvc.perform(post("/oauth/token")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("code", "code")
Expand All @@ -143,7 +143,7 @@ public void getToken_withRuntimeFailure_thenFail() throws Exception {
.param("client_assertion", "client_assertion"))
.andExpect(status().isInternalServerError());

Mockito.when(oAuthServiceImpl.getTokens(Mockito.any(TokenRequest.class))).thenThrow(NullPointerException.class);
Mockito.when(oAuthServiceImpl.getTokens(Mockito.any(TokenRequest.class),Mockito.anyBoolean())).thenThrow(NullPointerException.class);
mockMvc.perform(post("/oauth/token")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("code", "code")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,12 @@ public class OAuthServiceImpl implements OAuthService {
@Value("#{${mosip.esignet.oauth.key-values}}")
private Map<String, Object> oauthServerDiscoveryMap;

@Value("${mosip.esignet.discovery.issuer-id}")
private String discoveryIssuerId;


@Override
public TokenResponse getTokens(TokenRequest tokenRequest) throws EsignetException {
public TokenResponse getTokens(TokenRequest tokenRequest,boolean isV2) throws EsignetException {
String codeHash = authorizationHelperService.getKeyHash(tokenRequest.getCode());
OIDCTransaction transaction = cacheUtilService.getAuthCodeTransaction(codeHash);

Expand All @@ -89,7 +92,7 @@ public TokenResponse getTokens(TokenRequest tokenRequest) throws EsignetExceptio
ClientDetail clientDetailDto = clientManagementService.getClientDetails(transaction.getClientId());
IdentityProviderUtil.validateRedirectURI(clientDetailDto.getRedirectUris(), tokenRequest.getRedirect_uri());

authenticateClient(tokenRequest, clientDetailDto);
authenticateClient(tokenRequest, clientDetailDto,isV2);

boolean isTransactionVCScoped = isTransactionVCScoped(transaction);
if(!isTransactionVCScoped) { //if transaction is not VC scoped, only then do KYC exchange
Expand Down Expand Up @@ -202,23 +205,24 @@ private void validatePKCE(ProofKeyCodeExchange proofKeyCodeExchange, String code
throw new EsignetException(ErrorConstants.PKCE_FAILED);
}

private void authenticateClient(TokenRequest tokenRequest, ClientDetail clientDetail) throws EsignetException {
private void authenticateClient(TokenRequest tokenRequest, ClientDetail clientDetail,boolean isV2) throws EsignetException {
switch (tokenRequest.getClient_assertion_type()) {
case JWT_BEARER_TYPE:
validateJwtClientAssertion(clientDetail.getId(), clientDetail.getPublicKey(), tokenRequest.getClient_assertion());
validateJwtClientAssertion(clientDetail.getId(), clientDetail.getPublicKey(), tokenRequest.getClient_assertion(),
isV2? (String) oauthServerDiscoveryMap.get("token_endpoint") :discoveryIssuerId+"/oauth/token");
break;
default:
throw new InvalidRequestException(ErrorConstants.INVALID_ASSERTION_TYPE);
}
}

private void validateJwtClientAssertion(String ClientId, String jwk, String clientAssertion) throws EsignetException {
private void validateJwtClientAssertion(String clientId, String jwk, String clientAssertion,String audience) throws EsignetException {
if(clientAssertion == null || clientAssertion.isBlank())
throw new InvalidRequestException(ErrorConstants.INVALID_ASSERTION);

//verify signature
//on valid signature, verify each claims on JWT payload
tokenService.verifyClientAssertionToken(ClientId, jwk, clientAssertion);
tokenService.verifyClientAssertionToken(clientId, jwk, clientAssertion,audience);
}

private TokenResponse getTokenResponse(OIDCTransaction transaction, boolean isTransactionVCScoped) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,6 @@ public class TokenServiceImpl implements TokenService {
@Value("#{${mosip.esignet.openid.scope.claims}}")
private Map<String, List<String>> claims;

@Value("#{${mosip.esignet.discovery.key-values}}")
private Map<String, Object> discoveryMap;

@Value("${mosip.esignet.cnonce-expire-seconds:60}")
private int cNonceExpireSeconds;

Expand Down Expand Up @@ -140,7 +137,7 @@ public String getAccessToken(OIDCTransaction transaction, String cNonce) {
}

@Override
public void verifyClientAssertionToken(String clientId, String jwk, String clientAssertion) throws EsignetException {
public void verifyClientAssertionToken(String clientId, String jwk, String clientAssertion,String audience) throws EsignetException {
if(clientAssertion == null)
throw new EsignetException(ErrorConstants.INVALID_ASSERTION);

Expand All @@ -149,7 +146,7 @@ public void verifyClientAssertionToken(String clientId, String jwk, String clien
JWSKeySelector keySelector = new JWSVerificationKeySelector(JWSAlgorithm.RS256,
new ImmutableJWKSet(new JWKSet(RSAKey.parse(jwk))));
DefaultJWTClaimsVerifier claimsSetVerifier = new DefaultJWTClaimsVerifier(new JWTClaimsSet.Builder()
.audience(Collections.singletonList((String)discoveryMap.get("token_endpoint")))
.audience(Collections.singletonList(audience))
.issuer(clientId)
.subject(clientId)
.build(), REQUIRED_CLIENT_ASSERTION_CLAIMS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public void getTokens_withValidRequest_thenPass() throws KycExchangeException {
Mockito.when(authenticationWrapper.doKycExchange(Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(kycExchangeResult);
Mockito.when(tokenService.getAccessToken(Mockito.any(),Mockito.any())).thenReturn("test-access-token");
Mockito.when(tokenService.getIDToken(Mockito.any())).thenReturn("test-id-token");
TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest);
TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest,false);
Assert.assertNotNull(tokenResponse);
Assert.assertNotNull(tokenResponse.getId_token());
Assert.assertNotNull(tokenResponse.getAccess_token());
Expand Down Expand Up @@ -136,7 +136,7 @@ public void getTokens_withValidRequestWithPKCE_thenPass() throws KycExchangeExce
Mockito.when(authenticationWrapper.doKycExchange(Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(kycExchangeResult);
Mockito.when(tokenService.getAccessToken(Mockito.any(),Mockito.any())).thenReturn("test-access-token");
Mockito.when(tokenService.getIDToken(Mockito.any())).thenReturn("test-id-token");
TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest);
TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest,false);
Assert.assertNotNull(tokenResponse);
Assert.assertNotNull(tokenResponse.getId_token());
Assert.assertNotNull(tokenResponse.getAccess_token());
Expand All @@ -148,7 +148,7 @@ public void getTokens_withValidRequestWithPKCE_thenPass() throws KycExchangeExce
public void getTokens_withInvalidAuthCode_thenFail() {
TokenRequest tokenRequest = new TokenRequest();
try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,false);
} catch (InvalidRequestException ex) {
Assert.assertEquals(INVALID_TRANSACTION, ex.getErrorCode());
}
Expand Down Expand Up @@ -181,7 +181,7 @@ public void getTokens_withNullClientIdInRequest_thenPass() throws KycExchangeExc
Mockito.when(authenticationWrapper.doKycExchange(Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(kycExchangeResult);
Mockito.when(tokenService.getAccessToken(Mockito.any(), Mockito.any())).thenReturn("test-access-token");
Mockito.when(tokenService.getIDToken(Mockito.any())).thenReturn("test-id-token");
TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest);
TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest,false);
Assert.assertNotNull(tokenResponse);
Assert.assertNotNull(tokenResponse.getId_token());
Assert.assertNotNull(tokenResponse.getAccess_token());
Expand Down Expand Up @@ -217,7 +217,7 @@ public void getTokens_withEmptyClientIdInRequest_thenPass() throws KycExchangeEx
Mockito.when(authenticationWrapper.doKycExchange(Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(kycExchangeResult);
Mockito.when(tokenService.getAccessToken(Mockito.any(), Mockito.any())).thenReturn("test-access-token");
Mockito.when(tokenService.getIDToken(Mockito.any())).thenReturn("test-id-token");
TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest);
TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest,false);
Assert.assertNotNull(tokenResponse);
Assert.assertNotNull(tokenResponse.getId_token());
Assert.assertNotNull(tokenResponse.getAccess_token());
Expand All @@ -238,7 +238,7 @@ public void getTokens_withInvalidClientIdInRequest_thenFail() {
Mockito.when(cacheUtilService.getAuthCodeTransaction(Mockito.anyString())).thenReturn(oidcTransaction);

try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,false);
} catch (InvalidRequestException ex) {
Assert.assertEquals(INVALID_CLIENT_ID, ex.getErrorCode());
}
Expand All @@ -258,7 +258,7 @@ public void getTokens_withInvalidRedirectUri_thenFail() {
Mockito.when(cacheUtilService.getAuthCodeTransaction(Mockito.anyString())).thenReturn(oidcTransaction);

try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,false);
} catch (InvalidRequestException ex) {
Assert.assertEquals(INVALID_REDIRECT_URI, ex.getErrorCode());
}
Expand All @@ -268,7 +268,7 @@ public void getTokens_withInvalidRedirectUri_thenFail() {
tokenRequest.setRedirect_uri("https://test-redirect-uri/test/test-page");
Mockito.when(clientManagementService.getClientDetails(Mockito.anyString())).thenReturn(clientDetail);
try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,false);
} catch (InvalidRequestException ex) {
Assert.assertEquals(INVALID_REDIRECT_URI, ex.getErrorCode());
}
Expand All @@ -293,14 +293,14 @@ public void getTokens_withInvalidAssertionType_thenFail() {
tokenRequest.setRedirect_uri("https://test-redirect-uri/test/test-page");
Mockito.when(clientManagementService.getClientDetails(Mockito.anyString())).thenReturn(clientDetail);
try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,false);
} catch (InvalidRequestException ex) {
Assert.assertEquals(INVALID_ASSERTION_TYPE, ex.getErrorCode());
}

tokenRequest.setClient_assertion_type(JWT_BEARER_TYPE);
try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,false);
} catch (InvalidRequestException ex) {
Assert.assertEquals(INVALID_ASSERTION, ex.getErrorCode());
}
Expand Down Expand Up @@ -331,13 +331,13 @@ public void getTokens_withFailedDataExchange_thenFail() throws KycExchangeExcept
kycExchangeResult.setEncryptedKyc(null);
Mockito.when(authenticationWrapper.doKycExchange(Mockito.anyString(), Mockito.anyString(), Mockito.any())).thenReturn(null, kycExchangeResult);
try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,false);
} catch (EsignetException ex) {
Assert.assertEquals(DATA_EXCHANGE_FAILED, ex.getErrorCode());
}

try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,false);
} catch (EsignetException ex) {
Assert.assertEquals(DATA_EXCHANGE_FAILED, ex.getErrorCode());
}
Expand Down Expand Up @@ -366,7 +366,7 @@ public void getTokens_dataExchangeRuntimeException_thenFail() throws KycExchange
Mockito.when(authenticationWrapper.doKycExchange(Mockito.anyString(), Mockito.anyString(), Mockito.any()))
.thenThrow(new KycExchangeException("test-err-1"));
try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,false);
Assert.fail();
} catch (EsignetException ex) {
Assert.assertEquals("test-err-1", ex.getErrorCode());
Expand Down Expand Up @@ -440,7 +440,7 @@ public void getTokens_withInvalidPKCE_thenFail() {
Mockito.when(cacheUtilService.getAuthCodeTransaction(Mockito.anyString())).thenReturn(oidcTransaction);

try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,true);
Assert.fail();
} catch (EsignetException ex) {
Assert.assertEquals(PKCE_FAILED, ex.getErrorCode());
Expand Down Expand Up @@ -473,7 +473,7 @@ public void getTokens_withInvalidChallengeMethod_thenFail() {
Mockito.when(cacheUtilService.getAuthCodeTransaction(Mockito.anyString())).thenReturn(oidcTransaction);

try {
oAuthService.getTokens(tokenRequest);
oAuthService.getTokens(tokenRequest,true);
Assert.fail();
} catch (EsignetException ex) {
Assert.assertEquals(UNSUPPORTED_PKCE_CHALLENGE_METHOD, ex.getErrorCode());
Expand Down Expand Up @@ -511,7 +511,7 @@ public void getTokens_withVCScopedTransaction_thenPass() throws KycExchangeExcep
Mockito.when(securityHelperService.generateSecureRandomString(20)).thenReturn("test-nonce");
Mockito.when(tokenService.getAccessToken(Mockito.any(),Mockito.any())).thenReturn("test-access-token");

TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest);
TokenResponse tokenResponse = oAuthService.getTokens(tokenRequest,false);
Mockito.verifyNoInteractions(authenticationWrapper);
Assert.assertNotNull(tokenResponse);
Assert.assertNull(tokenResponse.getId_token());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void getIDToken_test() throws JSONException {
OIDCTransaction transaction = new OIDCTransaction();
transaction.setClientId("client-id");
transaction.setPartnerSpecificUserToken("psut");
transaction.setNonce("monce");
transaction.setNonce("nonce");
transaction.setAuthTimeInSeconds(22);
transaction.setAHash("access-token-hash");
transaction.setProvidedAuthFactors(new HashSet<>());
Expand Down Expand Up @@ -121,12 +121,12 @@ public void getAccessTokenWithNonce_test() throws JSONException {

@Test(expected = EsignetException.class)
public void verifyClientAssertionToken_withNullAssertion_thenFail() {
tokenService.verifyClientAssertionToken("client-id", publidKey, null);
tokenService.verifyClientAssertionToken("client-id", publidKey, null,"audience");
}

@Test(expected = InvalidRequestException.class)
public void verifyClientAssertionToken_withInvalidToken_thenFail() {
tokenService.verifyClientAssertionToken("client-id", publidKey, "client-assertion");
tokenService.verifyClientAssertionToken("client-id", publidKey, "client-assertion","audience");
}

@Test(expected = NotAuthenticatedException.class)
Expand Down
Loading