From 87bb15524fe9a7e61e543fcb4f9bbeb33663173b Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Tue, 17 Dec 2024 18:31:37 +0200 Subject: [PATCH 1/9] feat(jans-auth-server): update first party native authn implementation ( in backwards compatibility way) #10380 https://github.com/JanssenProject/jans/issues/10380 Signed-off-by: YuriyZ --- .../ws/rs/AuthorizationChallengeEndpoint.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeEndpoint.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeEndpoint.java index 44113ce1f93..57ddd927342 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeEndpoint.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeEndpoint.java @@ -1,6 +1,7 @@ package io.jans.as.server.authorize.ws.rs; import io.jans.as.model.util.QueryStringDecoder; +import io.jans.as.server.auth.DpopService; import io.jans.as.server.service.RequestParameterService; import jakarta.inject.Inject; import jakarta.servlet.http.HttpServletRequest; @@ -38,6 +39,8 @@ public Response requestAuthorizationPost( @FormParam("acr_values") String acrValues, @FormParam("auth_session") String authorizationChallengeSession, @FormParam("use_auth_session") String useAuthorizationChallengeSession, + @FormParam("device_session") String deviceSession, // old name in draft 00 + @FormParam("use_device_session") String useDeviceSession, // old name in draft 00 @FormParam("prompt") String prompt, @FormParam("state") String state, @FormParam("nonce") String nonce, @@ -63,6 +66,15 @@ public Response requestAuthorizationPost( authzRequest.setCodeChallenge(codeChallenge); authzRequest.setCodeChallengeMethod(codeChallengeMethod); authzRequest.setAuthzDetailsString(authorizationDetails); + authzRequest.setDpop(httpRequest.getHeader(DpopService.DPOP)); + + // backwards compatibilty: device_session (up to draft 02) vs auth_session (draft 02 and later) + if (authorizationChallengeSession == null && deviceSession != null) { + authzRequest.setAuthorizationChallengeSession(deviceSession); + } + if (useAuthorizationChallengeSession == null && useDeviceSession != null) { + authzRequest.setUseAuthorizationChallengeSession(Boolean.parseBoolean(useDeviceSession)); + } return authorizationChallengeService.requestAuthorization(authzRequest); } From 15c03efb9c7ecc89d86189948285943c7c54aa96 Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Tue, 17 Dec 2024 18:33:13 +0200 Subject: [PATCH 2/9] feat(jans-auth-server): updated redirect uri validation for First-Party Apps https://github.com/JanssenProject/jans/issues/10380 Signed-off-by: YuriyZ --- .../ws/rs/TokenRestWebServiceValidator.java | 15 ++++++++----- .../rs/TokenRestWebServiceValidatorTest.java | 22 +++++-------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidator.java b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidator.java index b498c42a64c..e87d36b368a 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidator.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidator.java @@ -82,7 +82,7 @@ public void validatePKCE(AuthorizationCodeGrant grant, String codeVerifier, OAut } public void validateParams(String grantType, String code, - String redirectUri, String refreshToken, OAuth2AuditLog auditLog) { + String refreshToken, OAuth2AuditLog auditLog) { log.debug("Starting to validate request parameters"); if (grantType == null || grantType.isEmpty()) { final String msg = "Grant Type is not set."; @@ -98,11 +98,6 @@ public void validateParams(String grantType, String code, log.trace(msg); throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, msg), auditLog)); } - if (StringUtils.isBlank(redirectUri)) { - final String msg = "redirect_uri is not set for AUTHORIZATION_CODE."; - log.trace(msg); - throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, msg), auditLog)); - } return; } @@ -173,6 +168,14 @@ public void validateGrant(AuthorizationGrant grant, Client client, Object identi validateGrant(grant, client, identifier, auditLog, null); } + public void validateRedirectUri(String redirectUri, OAuth2AuditLog auditLog) { + if (StringUtils.isBlank(redirectUri)) { + final String msg = "redirect_uri is not set for AUTHORIZATION_CODE."; + log.trace(msg); + throw new WebApplicationException(response(error(400, TokenErrorResponseType.INVALID_REQUEST, msg), auditLog)); + } + } + public void validateGrant(AuthorizationGrant grant, Client client, Object identifier, OAuth2AuditLog auditLog, Consumer onFailure) { if (grant == null) { diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidatorTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidatorTest.java index e9dadf62482..faf11a03f63 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidatorTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceValidatorTest.java @@ -172,7 +172,7 @@ public void validateActorTokenType_withValidTokenType_shouldPassSuccessfully() { @Test public void validateParams_whenGrantTypeIsBlank_shouldRaiseError() { try { - validator.validateParams("", "some_code", "https://my.redirect", "refresh_token", AUDIT_LOG); + validator.validateParams("", "some_code", "refresh_token", AUDIT_LOG); } catch (WebApplicationException e) { assertBadRequest(e.getResponse()); return; @@ -183,7 +183,7 @@ public void validateParams_whenGrantTypeIsBlank_shouldRaiseError() { @Test public void validateParams_whenGrantTypeIsAuthorizationCodeAndCodeIsBlank_shouldRaiseError() { try { - validator.validateParams(GrantType.AUTHORIZATION_CODE.getValue(), "", "https://my.redirect", "refresh_token", AUDIT_LOG); + validator.validateParams(GrantType.AUTHORIZATION_CODE.getValue(), "", "refresh_token", AUDIT_LOG); } catch (WebApplicationException e) { assertBadRequest(e.getResponse()); return; @@ -191,22 +191,10 @@ public void validateParams_whenGrantTypeIsAuthorizationCodeAndCodeIsBlank_should fail("No error for blank code for AUTHORIZATION_CODE grant type."); } - - @Test - public void validateParams_whenGrantTypeIsAuthorizationCodeAndRedirectUriIsBlank_shouldRaiseError() { - try { - validator.validateParams(GrantType.AUTHORIZATION_CODE.getValue(), "some_code", "", "refresh_token", AUDIT_LOG); - } catch (WebApplicationException e) { - assertBadRequest(e.getResponse()); - return; - } - fail("No error for blank redirect_uri for AUTHORIZATION_CODE grant type."); - } - @Test public void validateParams_whenGrantTypeIsRefreshTokenAndRefreshTokenIsBlank_shouldRaiseError() { try { - validator.validateParams(GrantType.REFRESH_TOKEN.getValue(), "some_code", "https://my.redirect", "", AUDIT_LOG); + validator.validateParams(GrantType.REFRESH_TOKEN.getValue(), "some_code", "", AUDIT_LOG); } catch (WebApplicationException e) { assertBadRequest(e.getResponse()); return; @@ -217,7 +205,7 @@ public void validateParams_whenGrantTypeIsRefreshTokenAndRefreshTokenIsBlank_sho @Test public void validateParams_whenGrantTypeIsAuthorizationCodeAndCodeIsNotBlank_shouldNotRaiseError() { try { - validator.validateParams(GrantType.AUTHORIZATION_CODE.getValue(), "some_code", "https://my.redirect", "", AUDIT_LOG); + validator.validateParams(GrantType.AUTHORIZATION_CODE.getValue(), "some_code", "", AUDIT_LOG); } catch (WebApplicationException e) { fail("Error occurs. We should not get it."); } @@ -226,7 +214,7 @@ public void validateParams_whenGrantTypeIsAuthorizationCodeAndCodeIsNotBlank_sho @Test public void validateParams_whenGrantTypeIsRefreshTokenAndRefreshTokenIsNotBlank_shouldNotRaiseError() { try { - validator.validateParams(GrantType.REFRESH_TOKEN.getValue(), "", "https://my.redirect", "refresh_token", AUDIT_LOG); + validator.validateParams(GrantType.REFRESH_TOKEN.getValue(), "", "refresh_token", AUDIT_LOG); } catch (WebApplicationException e) { fail("Error occurs. We should not get it."); } From d2140d6d31a3bc250ef73b32515212a1af50b3b0 Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Wed, 18 Dec 2024 12:35:58 +0200 Subject: [PATCH 3/9] feat(jans-auth-server): do not validate redirect_uri in First-Party Apps case https://github.com/JanssenProject/jans/issues/10380 Signed-off-by: YuriyZ --- .../model/common/AbstractAuthorizationGrant.java | 10 ++++++++++ .../server/token/ws/rs/TokenRestWebServiceImpl.java | 11 ++++++++--- .../java/io/jans/model/token/TokenAttributes.java | 12 ++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractAuthorizationGrant.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractAuthorizationGrant.java index cab786c5796..acd2c448214 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractAuthorizationGrant.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractAuthorizationGrant.java @@ -79,6 +79,7 @@ public abstract class AbstractAuthorizationGrant implements IAuthorizationGrant private String acrValues; private String sessionDn; + private boolean isAuthorizationChallenge; protected final ConcurrentMap txTokens = new ConcurrentHashMap<>(); protected final ConcurrentMap accessTokens = new ConcurrentHashMap<>(); @@ -110,6 +111,15 @@ public void setReferenceId(String referenceId) { this.referenceId = referenceId; } + public boolean isAuthorizationChallenge() { + return isAuthorizationChallenge; + } + + public AbstractAuthorizationGrant setAuthorizationChallenge(boolean authorizationChallenge) { + isAuthorizationChallenge = authorizationChallenge; + return this; + } + public Integer getStatusListIndex() { return statusListIndex; } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java index 95134a34a97..b32651ec9f2 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java @@ -185,7 +185,7 @@ grantType, code, redirectUri, username, refreshToken, clientId, prepareForLogs(r scope = ServerUtil.urlDecode(scope); // it may be encoded in uma case try { - tokenRestWebServiceValidator.validateParams(grantType, code, redirectUri, refreshToken, auditLog); + tokenRestWebServiceValidator.validateParams(grantType, code, refreshToken, auditLog); GrantType gt = GrantType.fromString(grantType); log.debug("Grant type: '{}'", gt); @@ -212,7 +212,7 @@ grantType, code, redirectUri, username, refreshToken, clientId, prepareForLogs(r executionContext.setAuthzDetails(authzDetails); if (gt == GrantType.AUTHORIZATION_CODE) { - return processAuthorizationCode(code, scope, codeVerifier, sessionIdObj, executionContext); + return processAuthorizationCode(code, scope, codeVerifier, sessionIdObj, redirectUri, executionContext); } else if (gt == GrantType.REFRESH_TOKEN) { return processRefreshTokenGrant(scope, refreshToken, idTokenPreProcessing, executionContext); } else if (gt == GrantType.CLIENT_CREDENTIALS) { @@ -434,7 +434,7 @@ private TokenEntity lockAndRemoveRefreshToken(String refreshTokenCode) { return null; } - private Response processAuthorizationCode(String code, String scope, String codeVerifier, SessionId sessionIdObj, ExecutionContext executionContext) { + private Response processAuthorizationCode(String code, String scope, String codeVerifier, SessionId sessionIdObj, String redirectUri, ExecutionContext executionContext) { Client client = executionContext.getClient(); log.debug("Attempting to find authorizationCodeGrant by clientId: '{}', code: '{}'", client.getClientId(), code); @@ -442,6 +442,11 @@ private Response processAuthorizationCode(String code, String scope, String code executionContext.setGrant(authorizationCodeGrant); log.trace("AuthorizationCodeGrant : '{}'", authorizationCodeGrant); + // validate redirectUri only for Authorization Code Flow. For First-Party App redirect uri is blank. It is perfectly valid case. + if (!authorizationCodeGrant.isAuthorizationChallenge()) { + tokenRestWebServiceValidator.validateRedirectUri(redirectUri, executionContext.getAuditLog()); + } + // if authorization code is not found then code was already used or wrong client provided = remove all grants with this auth code tokenRestWebServiceValidator.validateGrant(authorizationCodeGrant, client, code, executionContext.getAuditLog(), grant -> grantService.removeAllByAuthorizationCode(code)); tokenRestWebServiceValidator.validatePKCE(authorizationCodeGrant, codeVerifier, executionContext.getAuditLog()); diff --git a/jans-core/service/src/main/java/io/jans/model/token/TokenAttributes.java b/jans-core/service/src/main/java/io/jans/model/token/TokenAttributes.java index c5a6961d7b2..621ec7851da 100644 --- a/jans-core/service/src/main/java/io/jans/model/token/TokenAttributes.java +++ b/jans-core/service/src/main/java/io/jans/model/token/TokenAttributes.java @@ -26,6 +26,8 @@ public class TokenAttributes implements Serializable { private String x5cs256; @JsonProperty("online_access") private boolean onlineAccess; + @JsonProperty("authorization_challenge") + private boolean authorizationChallenge; @JsonProperty("attributes") private Map attributes; @JsonProperty("dpopJkt") @@ -35,6 +37,15 @@ public class TokenAttributes implements Serializable { @JsonProperty("statusListIndex") private Integer statusListIndex; + public boolean isAuthorizationChallenge() { + return authorizationChallenge; + } + + public TokenAttributes setAuthorizationChallenge(boolean authorizationChallenge) { + this.authorizationChallenge = authorizationChallenge; + return this; + } + public Integer getStatusListIndex() { return statusListIndex; } @@ -92,6 +103,7 @@ public String toString() { "onlineAccess='" + onlineAccess + '\'' + "dpopJkt='" + dpopJkt + '\'' + "authorizationDetails='" + authorizationDetails + '\'' + + "authorizationChallenge='" + authorizationChallenge + '\'' + '}'; } } From 95e9fcbac43dc3469288938f4c53cc6fc0ea4681 Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Wed, 18 Dec 2024 12:37:26 +0200 Subject: [PATCH 4/9] feat(jans-auth-server): set authorization_challenge flag when First-Party Apps is invoked Signed-off-by: YuriyZ --- .../server/authorize/ws/rs/AuthorizationChallengeService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeService.java index 841dc95b987..ed9ca039a96 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeService.java @@ -117,6 +117,8 @@ public void prepareAuthzRequest(AuthzRequest authzRequest) { if (StringUtils.isNotBlank(authzRequest.getAuthorizationChallengeSession())) { final AuthorizationChallengeSession session = authorizationChallengeSessionService.getAuthorizationChallengeSession(authzRequest.getAuthorizationChallengeSession()); + authorizationChallengeValidator.validateDpopJkt(session, authzRequest.getDpop()); + authzRequest.setAuthorizationChallengeSessionObject(session); if (session != null) { final Map attributes = session.getAttributes().getAttributes(); @@ -188,6 +190,7 @@ public Response authorize(AuthzRequest authzRequest) throws IOException, TokenBi authorizationGrant.setClaims(authzRequest.getClaims()); authorizationGrant.setSessionDn(sessionUser != null ? sessionUser.getDn() : "no_session_for_authorization_challenge"); // no need for session as at Authorization Endpoint authorizationGrant.setAcrValues(grantAcr); + authorizationGrant.setAuthorizationChallenge(true); authorizationGrant.save(); String authorizationCode = authorizationGrant.getAuthorizationCode().getCode(); From fa16ec72c6d3d749f82f9c1402477c71230cdccf Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Thu, 19 Dec 2024 13:27:23 +0200 Subject: [PATCH 5/9] feat(jans-auth-server): added dpop support for First-Party Apps Signed-off-by: YuriyZ --- ...thorizationChallengeSessionAttributes.java | 16 +++++++++++ .../AuthorizationChallengeSessionService.java | 5 +++- .../rs/AuthorizationChallengeValidator.java | 28 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/jans-auth-server/common/src/main/java/io/jans/as/common/model/session/AuthorizationChallengeSessionAttributes.java b/jans-auth-server/common/src/main/java/io/jans/as/common/model/session/AuthorizationChallengeSessionAttributes.java index d1f1cf22f62..52f480c513d 100644 --- a/jans-auth-server/common/src/main/java/io/jans/as/common/model/session/AuthorizationChallengeSessionAttributes.java +++ b/jans-auth-server/common/src/main/java/io/jans/as/common/model/session/AuthorizationChallengeSessionAttributes.java @@ -18,6 +18,12 @@ public class AuthorizationChallengeSessionAttributes implements Serializable { @JsonProperty("acr_values") private String acrValues; + // jkt - JWK SHA-256 Thumbprint confirmation method. + // The value of the jkt member MUST be the base64url encoding (as defined in [RFC7515]) of the JWK SHA-256 Thumbprint + // (according to [RFC7638]) of the DPoP public key (in JWK format) to which the access token is bound. + @JsonProperty("jkt") + private String jkt; + @JsonProperty("attributes") private Map attributes; @@ -38,11 +44,21 @@ public void setAcrValues(String acrValues) { this.acrValues = acrValues; } + public String getJkt() { + return jkt; + } + + public AuthorizationChallengeSessionAttributes setJkt(String jkt) { + this.jkt = jkt; + return this; + } + @Override public String toString() { return "DeviceSessionAttributes{" + "acrValues='" + acrValues + '\'' + "attributes='" + attributes + '\'' + + "jkt='" + jkt + '\'' + '}'; } } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeSessionService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeSessionService.java index bc99f18ad8a..57767db1d2e 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeSessionService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeSessionService.java @@ -10,7 +10,10 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; -import java.util.*; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.UUID; /** * @author Yuriy Z diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeValidator.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeValidator.java index e6751ccf6e3..3e0929e688f 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeValidator.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeValidator.java @@ -1,10 +1,13 @@ package io.jans.as.server.authorize.ws.rs; import io.jans.as.common.model.registration.Client; +import io.jans.as.common.model.session.AuthorizationChallengeSession; import io.jans.as.model.authorize.AuthorizeErrorResponseType; import io.jans.as.model.common.GrantType; import io.jans.as.model.configuration.AppConfiguration; import io.jans.as.model.error.ErrorResponseFactory; +import io.jans.as.model.token.TokenErrorResponseType; +import io.jans.as.server.auth.DpopService; import io.jans.as.server.model.config.Constants; import io.jans.as.server.service.ScopeService; import jakarta.enterprise.context.RequestScoped; @@ -12,6 +15,7 @@ import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Response; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import java.util.Arrays; @@ -35,6 +39,30 @@ public class AuthorizationChallengeValidator { @Inject private ScopeService scopeService; + public void validateDpopJkt(AuthorizationChallengeSession session, String dpop) { + final String jkt = session.getAttributes().getJkt(); + if (StringUtils.isBlank(jkt)) { + return; + } + + try { + final String dpopJwkThumbprint = DpopService.getDpopJwkThumbprint(dpop); + if (jkt.equals(dpopJwkThumbprint)) { + return; + } else { + log.debug("Unable to match dpopJkt: {} with sessionJkt: {}", dpopJwkThumbprint, jkt); + } + } catch (Exception e) { + String msg = String.format("Failed to validate dpop jtk. jkt: %s, dpop: %s", jkt, dpop); + log.debug(msg, e); + } + + throw new WebApplicationException(errorResponseFactory + .newErrorResponse(Response.Status.BAD_REQUEST) + .entity(errorResponseFactory.getErrorAsJson(TokenErrorResponseType.INVALID_DPOP_PROOF, "", "Invalid DPoP.")) + .build()); + } + public void validateGrantType(Client client, String state) { if (client == null) { final String msg = "Unable to find client."; From 832ef3fbfd24fa78ea4e3b3dc6900b73360f308d Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Thu, 19 Dec 2024 17:57:08 +0200 Subject: [PATCH 6/9] missed file Signed-off-by: YuriyZ --- .../src/main/java/io/jans/as/server/auth/DpopService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/auth/DpopService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/auth/DpopService.java index fa3ef0043f7..d20182d4703 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/auth/DpopService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/auth/DpopService.java @@ -221,7 +221,7 @@ private Response.ResponseBuilder error(int status, TokenErrorResponseType type, return Response.status(status).type(MediaType.APPLICATION_JSON_TYPE).entity(errorResponseFactory.errorAsJson(type, reason)); } - public String getDpopJwkThumbprint(String dpopStr) throws InvalidJwtException, NoSuchAlgorithmException, JWKException, NoSuchProviderException { + public static String getDpopJwkThumbprint(String dpopStr) throws InvalidJwtException, NoSuchAlgorithmException, JWKException, NoSuchProviderException { final Jwt dpop = Jwt.parseOrThrow(dpopStr); JSONWebKey jwk = JSONWebKey.fromJSONObject(dpop.getHeader().getJwk()); return jwk.getJwkThumbprint(); From 01d18ba5cd76af5de6a91e001478a5843db21cf2 Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Fri, 20 Dec 2024 16:14:33 +0200 Subject: [PATCH 7/9] fixed bug with not passed authorization_challenge flag inside grant #10380 Signed-off-by: YuriyZ --- .../java/io/jans/as/server/model/common/AuthorizationGrant.java | 1 + .../io/jans/as/server/model/common/AuthorizationGrantList.java | 1 + 2 files changed, 2 insertions(+) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrant.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrant.java index 4b78e439674..0b7afad2a29 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrant.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrant.java @@ -201,6 +201,7 @@ private void initTokenFromGrant(TokenEntity token) { } token.getAttributes().setAuthorizationDetails(getAuthzDetailsAsString()); + token.getAttributes().setAuthorizationChallenge(isAuthorizationChallenge()); token.setScope(getScopesAsString()); token.setAuthMode(getAcrValues()); token.setSessionDn(getSessionDn()); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java index 4ab61cdd196..00c10688ad3 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java @@ -354,6 +354,7 @@ public AuthorizationGrant asGrant(TokenEntity tokenEntity) { result.setTokenEntity(tokenEntity); result.setReferenceId(tokenEntity.getReferenceId()); result.setStatusListIndex(tokenEntity.getAttributes().getStatusListIndex()); + result.setAuthorizationChallenge(tokenEntity.getAttributes().isAuthorizationChallenge()); if (StringUtils.isNotBlank(grantId)) { result.setGrantId(grantId); } From 4ebcc98868068a2721cf6035e1ec715927b89122 Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Fri, 20 Dec 2024 16:14:56 +0200 Subject: [PATCH 8/9] missed file Signed-off-by: YuriyZ --- .../jans/as/server/authorize/ws/rs/AuthzRequest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequest.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequest.java index c57bf15f17e..07edb4b60a7 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequest.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthzRequest.java @@ -55,6 +55,7 @@ public class AuthzRequest { private String claims; private String authReqId; private String dpopJkt; + private String dpop; private String authzDetailsString; private AuthzDetails authzDetails; private String httpMethod; @@ -95,6 +96,15 @@ public void setDpopJkt(String dpopJkt) { this.dpopJkt = dpopJkt; } + public String getDpop() { + return dpop; + } + + public AuthzRequest setDpop(String dpop) { + this.dpop = dpop; + return this; + } + public AuthorizationChallengeSession getAuthorizationChallengeSessionObject() { return authorizationChallengeSessionObject; } From 0d522bc4edf32f48da0c2cc4ece6a014fcb42a6e Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Fri, 20 Dec 2024 16:15:49 +0200 Subject: [PATCH 9/9] added dpop to sample Authorization Challenge custom script #10380 Signed-off-by: YuriyZ --- .../AuthorizationChallenge.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/script-catalog/authorization_challenge/AuthorizationChallenge.java b/docs/script-catalog/authorization_challenge/AuthorizationChallenge.java index b10303f78bf..c0554577fed 100644 --- a/docs/script-catalog/authorization_challenge/AuthorizationChallenge.java +++ b/docs/script-catalog/authorization_challenge/AuthorizationChallenge.java @@ -1,5 +1,6 @@ import io.jans.as.common.model.common.User; import io.jans.as.common.model.session.AuthorizationChallengeSession; +import io.jans.as.server.auth.DpopService; import io.jans.as.server.authorize.ws.rs.AuthorizationChallengeSessionService; import io.jans.as.server.service.UserService; import io.jans.as.server.service.external.context.ExternalScriptContext; @@ -128,9 +129,15 @@ private AuthorizationChallengeSession prepareAuthorizationChallengeSession(Exter AuthorizationChallengeSessionService authorizationChallengeSessionService = CdiUtil.bean(AuthorizationChallengeSessionService.class); boolean newSave = authorizationChallengeSessionObject == null; if (newSave) { -// authorizationChallengeSessionObject = authorizationChallengeSessionService.newAuthorizationChallengeSession(); + authorizationChallengeSessionObject = authorizationChallengeSessionService.newAuthorizationChallengeSession(); } + final String dpop = context.getHttpRequest().getHeader(DpopService.DPOP); + if (StringUtils.isNotBlank(dpop)) { + authorizationChallengeSessionObject.getAttributes().setJkt(getDpopJkt(dpop)); + } + + String username = context.getHttpRequest().getParameter(USERNAME_PARAMETER); if (StringUtils.isNotBlank(username)) { authorizationChallengeSessionObject.getAttributes().getAttributes().put(USERNAME_PARAMETER, username); @@ -160,6 +167,19 @@ private AuthorizationChallengeSession prepareAuthorizationChallengeSession(Exter return authorizationChallengeSessionObject; } + public String getDpopJkt(String dpop) { + if (StringUtils.isBlank(dpop)) { + return null; + } + + try { + return DpopService.getDpopJwkThumbprint(dpop); + } catch (Exception e) { + scriptLogger.error("Failed to get jkt from DPoP: " + dpop,e); + return null; + } + } + private String getParameterFromAuthorizationChallengeSession(ExternalScriptContext context, String parameterName) { final AuthorizationChallengeSession sessionObject = context.getAuthzRequest().getAuthorizationChallengeSessionObject(); if (sessionObject != null) {