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-1053 #693

Merged
merged 2 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@ -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);
ase-101 marked this conversation as resolved.
Show resolved Hide resolved
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
Loading