Skip to content

Commit

Permalink
ES-1053 (#693)
Browse files Browse the repository at this point in the history
* ES-1053

Signed-off-by: ase-101 <sunkadaeanusha@gmail.com>

* ES-1053

Signed-off-by: ase-101 <sunkadaeanusha@gmail.com>

---------

Signed-off-by: ase-101 <sunkadaeanusha@gmail.com>
  • Loading branch information
ase-101 authored May 14, 2024
1 parent 024b022 commit 4001398
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ public class OIDCTransaction implements Serializable {
String oauthDetailsHash;
ConsentAction consentAction;

//signup redirect secret
String secretCode;

//PKCE support
ProofKeyCodeExchange proofKeyCodeExchange;
List<String> requestedCredentialScopes;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.mosip.esignet.core.dto;

import io.mosip.esignet.core.constants.ErrorConstants;
import lombok.Data;

import javax.validation.constraints.NotBlank;

@Data
public class SignupRedirectRequest {

@NotBlank(message = ErrorConstants.INVALID_TRANSACTION_ID)
private String transactionId;

@NotBlank
private String pathFragment;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import lombok.Data;

@Data
public class IdTokenHintResponse {
public class SignupRedirectResponse {

public String transactionId;
public String idTokenHint;
public String idToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
*/
package io.mosip.esignet.core.spi;

import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import io.mosip.esignet.core.dto.*;
Expand Down Expand Up @@ -72,8 +70,14 @@ public interface AuthorizationService {
* @param authCodeRequest
*/
AuthCodeResponse getAuthCode(AuthCodeRequest authCodeRequest) throws EsignetException;



IdTokenHintResponse getIdTokenHint(String transactionId, HttpServletResponse response);


/**
* Validates the transaction and prepares the ID token and sets up a cookie.
* ID token sent in the response will be used as hint from signup service
* @param signupRedirectRequest
* @param response
* @return
*/
SignupRedirectResponse prepareSignupRedirect(SignupRedirectRequest signupRedirectRequest, HttpServletResponse response);
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,14 @@ public interface TokenService {
* @return
*/
String getSignedJWT(String applicationId, JSONObject payload);

/**
* Creates ID token with the given subject and audience
* @param subject
* @param audience
* @param validitySeconds
* @param transaction
* @return
*/
String getIDToken(String subject, String audience, int validitySeconds, OIDCTransaction transaction);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public enum Action {
OAUTH_CLIENT_UPDATE("client-mgmt-service"),
GET_OAUTH_DETAILS("esignet-service"),
TRANSACTION_STARTED("esignet-service"),
SETUP_TOKEN_ID_HINT("esignet-service"),
PREPARE_SIGNUP_REDIRECT("esignet-service"),
SEND_OTP("esignet-service"),
AUTHENTICATE("esignet-service"),
GET_AUTH_CODE("esignet-service"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import io.mosip.esignet.core.util.IdentityProviderUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.bind.annotation.*;

import java.util.Map;
Expand Down Expand Up @@ -58,14 +57,15 @@ public ResponseWrapper<OAuthDetailResponse> getOauthDetails(@Valid @RequestBody
return responseWrapper;
}

@GetMapping("/setup-id-token-hint")
public ResponseWrapper<IdTokenHintResponse> getIdTokenHint(@RequestHeader Map<String, String> headers, HttpServletResponse response) {
@GetMapping("/prepare-signup-redirect")
public ResponseWrapper<SignupRedirectResponse> prepareSignupRedirect(@Valid @RequestBody RequestWrapper<SignupRedirectRequest> requestWrapper,
HttpServletResponse response) {
ResponseWrapper responseWrapper = new ResponseWrapper();
try {
responseWrapper.setResponse(authorizationService.getIdTokenHint(headers.get("oauth-details-key"), response));
responseWrapper.setResponse(authorizationService.prepareSignupRedirect(requestWrapper.getRequest(), response));
responseWrapper.setResponseTime(IdentityProviderUtil.getUTCDateTime());
} catch (EsignetException ex) {
auditWrapper.logAudit(Action.SETUP_TOKEN_ID_HINT, ActionStatus.ERROR, AuditHelper.buildAuditDto(headers.getOrDefault("oauth-details-key","")), ex);
auditWrapper.logAudit(Action.PREPARE_SIGNUP_REDIRECT, ActionStatus.ERROR, AuditHelper.buildAuditDto(requestWrapper.getRequest().getTransactionId()), ex);
throw ex;
}
return responseWrapper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,17 @@ mosip.esignet.header-filter.paths-to-validate={'${server.servlet.path}/authoriza
'${server.servlet.path}/authorization/authenticate', \
'${server.servlet.path}/authorization/v2/authenticate', \
'${server.servlet.path}/authorization/v3/authenticate', \
'${server.servlet.path}/authorization/auth-code'}
'${server.servlet.path}/authorization/auth-code',\
'${server.servlet.path}/authorization/prepare-signup-redirect'}

#This property is used for captcha validation and allowed values are send-otp and pwd.
#captcha validation is enabled for send-otp and pwd.
mosip.esignet.captcha.required=send-otp,pwd

mosip.esignet.host=http://localhost:8088
mosip.esignet.signup-id-token-expire-seconds=180
mosip.esignet.signup-id-token-audience=mosip-signup-client

## ------------------------------------------ e-Signet binding ---------------------------------------------------------

mosip.esignet.binding.salt-length=16
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ mosip.esignet.header-filter.paths-to-validate={'${server.servlet.path}/authoriza
#captcha validation is enabled for send-otp and pwd.
mosip.esignet.captcha.required=pwd

mosip.esignet.host=http://localhost:8088
mosip.esignet.signup-id-token-expire-seconds=180
mosip.esignet.signup-id-token-audience=mosip-signup-client

## ------------------------------------------ e-Signet binding ---------------------------------------------------------

mosip.esignet.binding.salt-length=16
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.mosip.esignet.core.exception.InvalidTransactionException;
import io.mosip.esignet.core.spi.AuthorizationService;
import io.mosip.esignet.core.spi.ClientManagementService;
import io.mosip.esignet.core.spi.TokenService;
import io.mosip.esignet.core.util.AuditHelper;
import io.mosip.esignet.core.util.AuthenticationContextClassRefUtil;
import io.mosip.esignet.core.util.IdentityProviderUtil;
Expand All @@ -31,7 +32,6 @@
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;

Expand All @@ -51,9 +51,8 @@
@Slf4j
@Service
public class AuthorizationServiceImpl implements AuthorizationService {

@Autowired
private Environment environment;

private static String COOKIE_VALUE = "{\"code\":\"%s\",\"pathFragment\":\"%s\"}";

@Autowired
private ClientManagementService clientManagementService;
Expand All @@ -65,7 +64,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
private CacheUtilService cacheUtilService;

@Autowired
private TokenServiceImpl tokenServiceImpl;
private TokenService tokenService;

@Autowired
private AuthenticationContextClassRefUtil authenticationContextClassRefUtil;
Expand All @@ -90,16 +89,6 @@ public class AuthorizationServiceImpl implements AuthorizationService {

@Value("${mosip.esignet.auth-txn-id-length:10}")
private int authTransactionIdLength;

@Value("${mosip.esignet.id-token-hint-cookie.max-age:180}")
private int cookieMaxAge;

@Value("${mosip.esignet.host}")
private String domain;

@Value("${server.servlet.path}")
private String servletPath;


//Number of times generate-link-code could be invoked per transaction
@Value("${mosip.esignet.generate-link-code.limit-per-transaction:10}")
Expand All @@ -114,6 +103,18 @@ public class AuthorizationServiceImpl implements AuthorizationService {
@Value("#{'${mosip.esignet.captcha.required}'.split(',')}")
private List<String> captchaRequired;

@Value("${mosip.esignet.signup-id-token-expire-seconds:60}")
private int signupIDTokenValidity;

@Value("${mosip.esignet.signup-id-token-audience:mosip-signup-client}")
private String signupIDTokenAudience;

@Value("${mosip.esignet.host}")
private String domain;

@Value("${server.servlet.path}")
private String servletPath;


@Override
public OAuthDetailResponseV1 getOauthDetails(OAuthDetailRequest oauthDetailReqDto) throws EsignetException {
Expand Down Expand Up @@ -314,7 +315,7 @@ private Pair<OAuthDetailResponse, OIDCTransaction> checkAndBuildOIDCTransaction(
oidcTransaction.setAuthTransactionId(getAuthTransactionId(oAuthDetailResponse.getTransactionId()));
oidcTransaction.setLinkCodeQueue(new LinkCodeQueue(2));
oidcTransaction.setCurrentLinkCodeLimit(linkCodeLimitPerTransaction);
oidcTransaction.setSecretCode(IdentityProviderUtil.generateB64EncodedHash(ALGO_SHA3_256, transactionId));
oidcTransaction.setSecretCode(IdentityProviderUtil.createTransactionId(oAuthDetailResponse.getTransactionId()));
oidcTransaction.setRequestedCredentialScopes(authorizationHelperService.getCredentialScopes(oauthDetailReqDto.getScope()));
return Pair.of(oAuthDetailResponse, oidcTransaction);
}
Expand Down Expand Up @@ -422,27 +423,23 @@ private String getAuthTransactionId(String oidcTransactionId) {
}

@Override
public IdTokenHintResponse getIdTokenHint(String transactionId, HttpServletResponse response) {
OIDCTransaction oidcTransaction = cacheUtilService.getAuthenticatedTransaction(transactionId);
public SignupRedirectResponse prepareSignupRedirect(SignupRedirectRequest signupRedirectRequest, HttpServletResponse response) {
OIDCTransaction oidcTransaction = cacheUtilService.getAuthenticatedTransaction(signupRedirectRequest.getTransactionId());
if(oidcTransaction == null) {
throw new InvalidTransactionException();
}
String uuid = UUID.fromString(transactionId).toString();
String secret = oidcTransaction.getSecretCode();
setCookie(response,uuid,secret);
IdTokenHintResponse idTokenHintResponse = new IdTokenHintResponse();
idTokenHintResponse.setTransactionId(transactionId);
idTokenHintResponse.setIdTokenHint(tokenServiceImpl.getIDTokenHint(oidcTransaction,uuid));
return idTokenHintResponse;
}


public void setCookie(HttpServletResponse response,String key, String value) {
Cookie cookie = new Cookie(key,value);
cookie.setMaxAge(cookieMaxAge);
cookie.setSecure(true);
cookie.setDomain(domain);
cookie.setPath(servletPath);
response.addCookie(cookie);
String uuid = UUID.fromString(signupRedirectRequest.getTransactionId()).toString();
SignupRedirectResponse signupRedirectResponse = new SignupRedirectResponse();
signupRedirectResponse.setTransactionId(signupRedirectRequest.getTransactionId());
signupRedirectResponse.setIdToken(tokenService.getIDToken(uuid, signupIDTokenAudience, signupIDTokenValidity, oidcTransaction));

String cookieValue = String.format(COOKIE_VALUE, oidcTransaction.getSecretCode(), signupRedirectRequest.getPathFragment());
Cookie cookie = new Cookie(uuid, IdentityProviderUtil.b64Encode(cookieValue));
cookie.setMaxAge(signupIDTokenValidity);
cookie.setSecure(true);
cookie.setDomain(domain);
cookie.setPath(servletPath);
response.addCookie(cookie);
return signupRedirectResponse;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,12 @@ public class TokenServiceImpl implements TokenService {

@Value("${mosip.esignet.id-token-expire-seconds:60}")
private int idTokenExpireSeconds;

@Value("${mosip.esignet.id-token-hint-expire-seconds:60}")
private int idTokenHintExpireSeconds;

@Value("${mosip.esignet.access-token-expire-seconds:60}")
private int accessTokenExpireSeconds;

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

@Value("${mosip.esignet.id-token-hint.aud}")
private String idTokenHintAud;

@Value("#{${mosip.esignet.openid.scope.claims}}")
private Map<String, List<String>> claims;
Expand All @@ -97,37 +91,20 @@ public class TokenServiceImpl implements TokenService {

@Override
public String getIDToken(@NonNull OIDCTransaction transaction) {
JSONObject payload = new JSONObject();
payload.put(ISS, issuerId);
payload.put(SUB, transaction.getPartnerSpecificUserToken());
payload.put(AUD, transaction.getClientId());
long issueTime = IdentityProviderUtil.getEpochSeconds();
payload.put(IAT, issueTime);
payload.put(EXP, issueTime + (idTokenExpireSeconds<=0 ? 3600 : idTokenExpireSeconds));
payload.put(AUTH_TIME, transaction.getAuthTimeInSeconds());
payload.put(NONCE, transaction.getNonce());
List<String> acrs = authenticationContextClassRefUtil.getACRs(transaction.getProvidedAuthFactors());
payload.put(ACR, String.join(SPACE, acrs));
JSONObject payload = buildIDToken(transaction.getPartnerSpecificUserToken(),
transaction.getClientId(), idTokenExpireSeconds, transaction);
payload.put(ACCESS_TOKEN_HASH, transaction.getAHash());
return getSignedJWT(Constants.OIDC_SERVICE_APP_ID, payload);
}

public String getIDTokenHint(@NonNull OIDCTransaction transaction, String subject) {
JSONObject payload = new JSONObject();
payload.put(ISS, issuerId);
payload.put(SUB, subject);
payload.put(AUD, idTokenHintAud);
long issueTime = IdentityProviderUtil.getEpochSeconds();
payload.put(IAT, issueTime);
payload.put(EXP, issueTime + (idTokenHintExpireSeconds<=0 ? 3600 : idTokenHintExpireSeconds));
payload.put(AUTH_TIME, transaction.getAuthTimeInSeconds());
payload.put(NONCE, transaction.getNonce());
List<String> acrs = authenticationContextClassRefUtil.getACRs(transaction.getProvidedAuthFactors());
payload.put(ACR, String.join(SPACE, acrs));

@Override
public String getIDToken(@NonNull String subject, @NonNull String audience, int validitySeconds,
@NonNull OIDCTransaction transaction) {
JSONObject payload = buildIDToken(transaction.getPartnerSpecificUserToken(),
transaction.getClientId(), validitySeconds, transaction);
return getSignedJWT(Constants.OIDC_SERVICE_APP_ID, payload);
}


@Override
public String getAccessToken(OIDCTransaction transaction, String cNonce) {
JSONObject payload = new JSONObject();
Expand Down Expand Up @@ -217,6 +194,22 @@ public String getSignedJWT(String applicationId, JSONObject payload) {
return responseDto.getJwtSignedData();
}

private JSONObject buildIDToken(String subject, String audience, int validitySeconds,
OIDCTransaction transaction) {
JSONObject payload = new JSONObject();
payload.put(ISS, issuerId);
payload.put(SUB, subject);
payload.put(AUD, audience);
long issueTime = IdentityProviderUtil.getEpochSeconds();
payload.put(IAT, issueTime);
payload.put(EXP, issueTime + (validitySeconds<=0 ? 3600 : validitySeconds));
payload.put(AUTH_TIME, transaction.getAuthTimeInSeconds());
payload.put(NONCE, transaction.getNonce());
List<String> acrs = authenticationContextClassRefUtil.getACRs(transaction.getProvidedAuthFactors());
payload.put(ACR, String.join(SPACE, acrs));
return payload;
}

private boolean isSignatureValid(String jwt) {
JWTSignatureVerifyRequestDto signatureVerifyRequestDto = new JWTSignatureVerifyRequestDto();
signatureVerifyRequestDto.setApplicationId(Constants.OIDC_SERVICE_APP_ID);
Expand Down

0 comments on commit 4001398

Please sign in to comment.