Skip to content
Open
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 @@ -2341,6 +2341,11 @@ private static void addUserAttributesToOAuthMessage(OAuthMessage oAuthMessage, S
authorizationGrantCacheEntry.setMaxAge(sessionDataCacheEntry.getoAuth2Parameters().getMaxAge());
authorizationGrantCacheEntry.setTokenBindingValue(tokenBindingValue);
authorizationGrantCacheEntry.setSessionContextIdentifier(sessionDataCacheEntry.getSessionContextIdentifier());
authorizationGrantCacheEntry.setSessionDataKeyConsent(oAuthMessage.getSessionDataKeyFromConsent());
if (log.isDebugEnabled()) {
log.debug(String.format("Retrieved sessionDataKeyConsent: %s from oAuthMessage and set to " +
"authorizationGrantCacheEntry.", oAuthMessage.getSessionDataKeyFromConsent()));
}
authorizationGrantCacheEntry.setAccessTokenExtensionDO(tokenExtendedAttributes);
if (isApiBasedAuthenticationFlow(oAuthMessage)) {
authorizationGrantCacheEntry.setApiBasedAuthRequest(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.wso2.carbon.identity.oauth.action.model.AccessToken;
import org.wso2.carbon.identity.oauth.action.model.PreIssueAccessTokenEvent;
import org.wso2.carbon.identity.oauth.action.model.RefreshToken;
import org.wso2.carbon.identity.oauth.action.model.Session;
import org.wso2.carbon.identity.oauth.action.model.TokenRequest;
import org.wso2.carbon.identity.oauth.common.OAuthConstants;
import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException;
Expand Down Expand Up @@ -137,6 +138,18 @@ private PreIssueAccessTokenEvent getEvent(OAuthTokenReqMessageContext tokenMessa
}
eventBuilder.request(getRequest(tokenReqDTO));

String sessionDataKeyConsent = (String) tokenMessageContext.getProperty(
OAuthConstants.SESSION_DATA_KEY_CONSENT);
if (StringUtils.isNotEmpty(sessionDataKeyConsent)) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Retrieve sessionDataKeyConsent: %s from tokenMessageContext and pass to " +
"preIssueAccessToken event", sessionDataKeyConsent));
}
eventBuilder.session(new Session.Builder()
.sessionDataKeyConsent(sessionDataKeyConsent)
.build());
}

return eventBuilder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ public class PreIssueAccessTokenEvent extends Event {

private final AccessToken accessToken;
private final RefreshToken refreshToken;
private final Session session;

private PreIssueAccessTokenEvent(Builder builder) {

this.accessToken = builder.accessToken;
this.refreshToken = builder.refreshToken;
this.session = builder.session;
this.request = builder.request;
this.organization = builder.organization;
this.tenant = builder.tenant;
Expand All @@ -56,13 +58,19 @@ public RefreshToken getRefreshToken() {
return refreshToken;
}

public Session getSession() {

return session;
}

/**
* Builder for the PreIssueAccessTokenEvent.
*/
public static class Builder {

private AccessToken accessToken;
private RefreshToken refreshToken;
private Session session;
private Request request;
private Organization organization;
private Tenant tenant;
Expand All @@ -82,6 +90,12 @@ public Builder refreshToken(RefreshToken refreshToken) {
return this;
}

public Builder session(Session session) {

this.session = session;
return this;
}

public Builder request(Request request) {

this.request = request;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.wso2.carbon.identity.oauth.action.model;

/**
* This class represents the session context sent in the event payload
* of the pre issue access token action.
*/
public class Session {

private final String sessionDataKeyConsent;

private Session(Builder builder) {

this.sessionDataKeyConsent = builder.sessionDataKeyConsent;
}

public String getSessionDataKeyConsent() {

return sessionDataKeyConsent;
}

/**
* Builder for Session.
*/
public static class Builder {

private String sessionDataKeyConsent;

public Builder sessionDataKeyConsent(String sessionDataKeyConsent) {

this.sessionDataKeyConsent = sessionDataKeyConsent;
return this;
}

public Session build() {

return new Session(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public class AuthorizationGrantCacheEntry extends CacheEntry {

private String oidcSessionId;

private String sessionDataKeyConsent;

private boolean isRequestObjectFlow;
private AccessTokenExtendedAttributes accessTokenExtendedAttributes;
private boolean isApiBasedAuthRequest;
Expand Down Expand Up @@ -310,6 +312,26 @@ public void setOidcSessionId(String oidcSessionId) {
this.oidcSessionId = oidcSessionId;
}

/**
* Get sessionDataKeyConsent value.
*
* @return sessionDataKeyConsent value.
*/
public String getSessionDataKeyConsent() {

return sessionDataKeyConsent;
}

/**
* Set sessionDataKeyConsent value.
*
* @param sessionDataKeyConsent sessionDataKeyConsent.
*/
public void setSessionDataKeyConsent(String sessionDataKeyConsent) {

this.sessionDataKeyConsent = sessionDataKeyConsent;
}

/**
* Get OIDC session Id value.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.oauth.OAuthUtil;
import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCache;
import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCacheEntry;
import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCacheKey;
import org.wso2.carbon.identity.oauth.cache.OAuthCache;
import org.wso2.carbon.identity.oauth.cache.OAuthCacheKey;
Expand Down Expand Up @@ -138,6 +139,7 @@ private void setPropertiesForTokenGeneration(OAuthTokenReqMessageContext tokReqM
// calculating it again when issuing the access token.
tokReqMsgCtx.addProperty(AUTHZ_CODE, tokenReq.getAuthorizationCode());
this.setRARPropertiesForTokenGeneration(tokReqMsgCtx);
setSessionDataKeyConsentProperty(tokReqMsgCtx, tokenReq.getAuthorizationCode());
}

private boolean validateCallbackUrlFromRequest(String callbackUrlFromRequest,
Expand Down Expand Up @@ -690,6 +692,20 @@ private void clearAccessTokenOAuthCache(AccessTokenDO accessTokenDO) {
}
}

private void setSessionDataKeyConsentProperty(OAuthTokenReqMessageContext tokReqMsgCtx, String authzCode) {

AuthorizationGrantCacheEntry grantCacheEntry = AuthorizationGrantCache.getInstance()
.getValueFromCacheByCode(new AuthorizationGrantCacheKey(authzCode));
if (grantCacheEntry != null && StringUtils.isNotEmpty(grantCacheEntry.getSessionDataKeyConsent())) {
tokReqMsgCtx.addProperty(OAuthConstants.SESSION_DATA_KEY_CONSENT,
grantCacheEntry.getSessionDataKeyConsent());
if (log.isDebugEnabled()) {
log.debug(String.format("Retrieved sessionDataKeyConsent: %s from AuthorizationGrantCacheEntry " +
"and set to OAuthTokenReqMessageContext.", grantCacheEntry.getSessionDataKeyConsent()));
}
}
}

private void resolveUserResidentOrgForOrganizationSSOUsers(AuthenticatedUser authenticatedUser, String authzCode) {

if (authenticatedUser.isFederatedUser() && FrameworkConstants.ORGANIZATION_LOGIN_IDP_NAME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.wso2.carbon.identity.oauth.action.model.AccessToken;
import org.wso2.carbon.identity.oauth.action.model.PreIssueAccessTokenEvent;
import org.wso2.carbon.identity.oauth.action.model.TokenRequest;
import org.wso2.carbon.identity.oauth.common.OAuthConstants;
import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration;
import org.wso2.carbon.identity.oauth.dao.OAuthAppDO;
import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder;
Expand Down Expand Up @@ -451,6 +452,42 @@ private List<AllowedOperation> getExpectedAllowedOperations() {
return allowedOperations;
}

/**
* Verifies that when SESSION_DATA_KEY_CONSENT is set on the token message context,
* it is populated on event.session.sessionDataKeyConsent in the PreIssueAccessToken action request payload.
*/
@Test
public void testBuildActionExecutionRequestWithSessionDataKeyConsent()
throws ActionExecutionRequestBuilderException, OrganizationManagementException {

String sessionDataKeyConsent = "test-session-data-key-consent";
MinimalOrganization minimalOrganization =
new MinimalOrganization.Builder().id(ORG_ID).name(ORG_NAME).organizationHandle(ORG_HANDLE)
.depth(ORG_DEPTH).build();

try (MockedStatic<OAuthComponentServiceHolder> oAuthComponentServiceHolder =
mockStatic(OAuthComponentServiceHolder.class)) {

oAuthComponentServiceHolder.when(OAuthComponentServiceHolder::getInstance)
.thenReturn(mockOAuthComponentServiceHolder);
when(mockOAuthComponentServiceHolder.getOrganizationManager()).thenReturn(mockOrganizationManager);
when(mockOrganizationManager.resolveOrganizationId(ORG_NAME)).thenReturn(ORG_ID);
when(mockOrganizationManager.getMinimalOrganization(anyString(), nullable(String.class)))
.thenReturn(minimalOrganization);

OAuthTokenReqMessageContext tokenMessageContext = getMockTokenMessageContext();
tokenMessageContext.addProperty(OAuthConstants.SESSION_DATA_KEY_CONSENT, sessionDataKeyConsent);

ActionExecutionRequest actionExecutionRequest = preIssueAccessTokenRequestBuilder
.buildActionExecutionRequest(
FlowContext.create().add("tokenMessageContext", tokenMessageContext), null);

PreIssueAccessTokenEvent event = (PreIssueAccessTokenEvent) actionExecutionRequest.getEvent();
Assert.assertNotNull(event.getSession());
Assert.assertEquals(event.getSession().getSessionDataKeyConsent(), sessionDataKeyConsent);
}
}

/**
* Encode the client ID and client secret as a Base64 encoded string.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,23 @@
import org.wso2.carbon.identity.oauth2.token.OauthTokenIssuer;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;

import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCache;
import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCacheEntry;
import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCacheKey;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

Expand Down Expand Up @@ -316,6 +321,104 @@ public void testIssueRefreshToken() throws IdentityOAuth2Exception {
}
}

/**
* Verifies that setSessionDataKeyConsentProperty() sets SESSION_DATA_KEY_CONSENT on the message context
* when the cache entry exists and sessionDataKeyConsent is populated.
*/
@Test
public void testSetSessionDataKeyConsentProperty_withValidConsent() throws Exception {

String authzCode = "test-authz-code";
String sessionDataKeyConsent = "test-session-data-key-consent";
AuthorizationCodeGrantHandler handler = new AuthorizationCodeGrantHandler();
OAuthTokenReqMessageContext tokReqMsgCtx = new OAuthTokenReqMessageContext(
new OAuth2AccessTokenReqDTO());

try (MockedStatic<AuthorizationGrantCache> mockCacheStatic =
mockStatic(AuthorizationGrantCache.class)) {

AuthorizationGrantCache mockCache = mock(AuthorizationGrantCache.class);
mockCacheStatic.when(AuthorizationGrantCache::getInstance).thenReturn(mockCache);

AuthorizationGrantCacheEntry cacheEntry = new AuthorizationGrantCacheEntry();
cacheEntry.setSessionDataKeyConsent(sessionDataKeyConsent);
when(mockCache.getValueFromCacheByCode(
any(AuthorizationGrantCacheKey.class))).thenReturn(cacheEntry);

Method method = AuthorizationCodeGrantHandler.class.getDeclaredMethod(
"setSessionDataKeyConsentProperty",
OAuthTokenReqMessageContext.class, String.class);
method.setAccessible(true);
method.invoke(handler, tokReqMsgCtx, authzCode);

assertEquals(tokReqMsgCtx.getProperty(OAuthConstants.SESSION_DATA_KEY_CONSENT),
sessionDataKeyConsent);
}
}

/**
* Verifies that setSessionDataKeyConsentProperty() does not set any property
* when the cache entry is null (e.g. cache and session store both miss).
*/
@Test
public void testSetSessionDataKeyConsentProperty_withNullCacheEntry() throws Exception {

String authzCode = "test-authz-code-null";
AuthorizationCodeGrantHandler handler = new AuthorizationCodeGrantHandler();
OAuthTokenReqMessageContext tokReqMsgCtx = new OAuthTokenReqMessageContext(
new OAuth2AccessTokenReqDTO());

try (MockedStatic<AuthorizationGrantCache> mockCacheStatic =
mockStatic(AuthorizationGrantCache.class)) {

AuthorizationGrantCache mockCache = mock(AuthorizationGrantCache.class);
mockCacheStatic.when(AuthorizationGrantCache::getInstance).thenReturn(mockCache);
when(mockCache.getValueFromCacheByCode(
any(AuthorizationGrantCacheKey.class))).thenReturn(null);

Method method = AuthorizationCodeGrantHandler.class.getDeclaredMethod(
"setSessionDataKeyConsentProperty",
OAuthTokenReqMessageContext.class, String.class);
method.setAccessible(true);
method.invoke(handler, tokReqMsgCtx, authzCode);

assertNull(tokReqMsgCtx.getProperty(OAuthConstants.SESSION_DATA_KEY_CONSENT));
}
}

/**
* Verifies that setSessionDataKeyConsentProperty() does not set any property
* when the cache entry exists but sessionDataKeyConsent is empty.
*/
@Test
public void testSetSessionDataKeyConsentProperty_withEmptyConsent() throws Exception {

String authzCode = "test-authz-code-empty";
AuthorizationCodeGrantHandler handler = new AuthorizationCodeGrantHandler();
OAuthTokenReqMessageContext tokReqMsgCtx = new OAuthTokenReqMessageContext(
new OAuth2AccessTokenReqDTO());

try (MockedStatic<AuthorizationGrantCache> mockCacheStatic =
mockStatic(AuthorizationGrantCache.class)) {

AuthorizationGrantCache mockCache = mock(AuthorizationGrantCache.class);
mockCacheStatic.when(AuthorizationGrantCache::getInstance).thenReturn(mockCache);

AuthorizationGrantCacheEntry cacheEntry = new AuthorizationGrantCacheEntry();
cacheEntry.setSessionDataKeyConsent("");
when(mockCache.getValueFromCacheByCode(
any(AuthorizationGrantCacheKey.class))).thenReturn(cacheEntry);

Method method = AuthorizationCodeGrantHandler.class.getDeclaredMethod(
"setSessionDataKeyConsentProperty",
OAuthTokenReqMessageContext.class, String.class);
method.setAccessible(true);
method.invoke(handler, tokReqMsgCtx, authzCode);

assertNull(tokReqMsgCtx.getProperty(OAuthConstants.SESSION_DATA_KEY_CONSENT));
}
}

private void setPrivateField(Object object, String fieldName, Object value) throws Exception {

Field field = object.getClass().getDeclaredField(fieldName);
Expand Down
Loading