Skip to content

Commit f919fec

Browse files
Introduce POST_TOKEN_ISSUANCE event and fire it after access token issuance.
1 parent 764a98c commit f919fec

File tree

6 files changed

+308
-6
lines changed

6 files changed

+308
-6
lines changed

components/org.wso2.carbon.identity.oauth.stub/src/main/resources/OAuth2TokenValidationService.wsdl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
<xs:sequence>
103103
<xs:element minOccurs="0" name="identifier" nillable="true" type="xs:string"/>
104104
<xs:element minOccurs="0" name="issuer" nillable="true" type="xs:string"/>
105-
<xs:element minOccurs="0" name="tokenType" nillable="true" type="xs:string"/>
105+
<xs:element minOccurs="0" name="tokenCategory" nillable="true" type="xs:string"/>
106106
</xs:sequence>
107107
</xs:complexType>
108108
<xs:complexType name="OAuth2TokenValidationRequestDTO_TokenValidationContextParam">
@@ -131,7 +131,7 @@
131131
<xs:complexType name="OAuth2TokenValidationResponseDTO_AuthorizationContextToken">
132132
<xs:sequence>
133133
<xs:element minOccurs="0" name="tokenString" nillable="true" type="xs:string"/>
134-
<xs:element minOccurs="0" name="tokenType" nillable="true" type="xs:string"/>
134+
<xs:element minOccurs="0" name="tokenCategory" nillable="true" type="xs:string"/>
135135
</xs:sequence>
136136
</xs:complexType>
137137
<xs:complexType name="OAuth2IntrospectionResponseDTO">
@@ -152,7 +152,7 @@
152152
<xs:element maxOccurs="unbounded" minOccurs="0" name="properties" nillable="true" type="xs:string"/>
153153
<xs:element minOccurs="0" name="scope" nillable="true" type="xs:string"/>
154154
<xs:element minOccurs="0" name="sub" nillable="true" type="xs:string"/>
155-
<xs:element minOccurs="0" name="tokenType" nillable="true" type="xs:string"/>
155+
<xs:element minOccurs="0" name="tokenCategory" nillable="true" type="xs:string"/>
156156
<xs:element minOccurs="0" name="userContext" nillable="true" type="xs:string"/>
157157
<xs:element minOccurs="0" name="username" nillable="true" type="xs:string"/>
158158
</xs:sequence>

components/org.wso2.carbon.identity.oauth.stub/src/main/resources/OAuthAdminService.wsdl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@
437437
<xs:element minOccurs="0" name="tokenEndpointAuthMethod" nillable="true" type="xs:string"/>
438438
<xs:element minOccurs="0" name="tokenEndpointAuthSignatureAlgorithm" nillable="true" type="xs:string"/>
439439
<xs:element minOccurs="0" name="tokenRevocationWithIDPSessionTerminationEnabled" type="xs:boolean"/>
440-
<xs:element minOccurs="0" name="tokenType" nillable="true" type="xs:string"/>
440+
<xs:element minOccurs="0" name="tokenCategory" nillable="true" type="xs:string"/>
441441
<xs:element minOccurs="0" name="userAccessTokenExpiryTime" type="xs:long"/>
442442
<xs:element minOccurs="0" name="username" nillable="true" type="xs:string"/>
443443
</xs:sequence>
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
package org.wso2.carbon.identity.oauth2.model;
19+
20+
import org.wso2.carbon.identity.openidconnect.OIDCConstants;
21+
22+
/**
23+
* Data object to contain information related to token issuance.
24+
*/
25+
public class TokenIssuanceDO {
26+
27+
private final String tokenId;
28+
private final String tokenType;
29+
private final String tenantDomain;
30+
private final String clientId;
31+
private final String grantType;
32+
private final OIDCConstants.TokenBillingCategory tokenBillingCategory;
33+
private final int appResidentTenantId;
34+
private final String issuedTime;
35+
private final String authorizedOrganizationId;
36+
37+
private TokenIssuanceDO(Builder builder) {
38+
39+
this.tokenId = builder.tokenId;
40+
this.tokenType = builder.tokenType;
41+
this.tenantDomain = builder.tenantDomain;
42+
this.clientId = builder.clientId;
43+
this.grantType = builder.grantType;
44+
this.tokenBillingCategory = builder.tokenBillingCategory;
45+
this.appResidentTenantId = builder.appResidentTenantId;
46+
this.issuedTime = builder.issuedTime;
47+
this.authorizedOrganizationId = builder.authorizedOrganizationId;
48+
}
49+
50+
public String getTokenId() {
51+
52+
return tokenId;
53+
}
54+
55+
public String getTokenType() {
56+
57+
return tokenType;
58+
}
59+
60+
public String getTenantDomain() {
61+
62+
return tenantDomain;
63+
}
64+
65+
public String getClientId() {
66+
67+
return clientId;
68+
}
69+
70+
public String getGrantType() {
71+
72+
return grantType;
73+
}
74+
75+
public OIDCConstants.TokenBillingCategory getTokenBillingCategory() {
76+
77+
return tokenBillingCategory;
78+
}
79+
80+
public int getAppResidentTenantId() {
81+
82+
return appResidentTenantId;
83+
}
84+
85+
public String getIssuedTime() {
86+
87+
return issuedTime;
88+
}
89+
90+
public String getAuthorizedOrganizationId() {
91+
92+
return authorizedOrganizationId;
93+
}
94+
95+
/**
96+
* Builder class for TokenIssuanceDO.
97+
*/
98+
public static class Builder {
99+
100+
private String tokenId;
101+
private String tokenType;
102+
private String tenantDomain;
103+
private String clientId;
104+
private String grantType;
105+
private OIDCConstants.TokenBillingCategory tokenBillingCategory;
106+
private int appResidentTenantId;
107+
private String issuedTime;
108+
private String authorizedOrganizationId;
109+
110+
public Builder tokenId(String tokenId) {
111+
112+
this.tokenId = tokenId;
113+
return this;
114+
}
115+
116+
public Builder tokenType(String tokenType) {
117+
118+
this.tokenType = tokenType;
119+
return this;
120+
}
121+
122+
public Builder tenantDomain(String tenantDomain) {
123+
124+
this.tenantDomain = tenantDomain;
125+
return this;
126+
}
127+
128+
public Builder clientId(String clientId) {
129+
130+
this.clientId = clientId;
131+
return this;
132+
}
133+
134+
public Builder grantType(String grantType) {
135+
136+
this.grantType = grantType;
137+
return this;
138+
}
139+
140+
public Builder tokenBillingCategory(OIDCConstants.TokenBillingCategory tokenCategory) {
141+
142+
this.tokenBillingCategory = tokenCategory;
143+
return this;
144+
}
145+
146+
public Builder appResidentTenantId(int appResidentTenantId) {
147+
148+
this.appResidentTenantId = appResidentTenantId;
149+
return this;
150+
}
151+
152+
public Builder issuedTime(String issuedTime) {
153+
154+
this.issuedTime = issuedTime;
155+
return this;
156+
}
157+
158+
public Builder authorizedOrganizationId(String authorizedOrganizationId) {
159+
160+
this.authorizedOrganizationId = authorizedOrganizationId;
161+
return this;
162+
}
163+
164+
public TokenIssuanceDO build() {
165+
166+
return new TokenIssuanceDO(this);
167+
}
168+
}
169+
}

components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCache;
5050
import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCacheEntry;
5151
import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCacheKey;
52+
import org.wso2.carbon.identity.oauth.cache.OAuthCache;
53+
import org.wso2.carbon.identity.oauth.cache.OAuthCacheKey;
5254
import org.wso2.carbon.identity.oauth.common.OAuth2ErrorCodes;
5355
import org.wso2.carbon.identity.oauth.common.OAuthConstants;
5456
import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException;
@@ -62,6 +64,7 @@
6264
import org.wso2.carbon.identity.oauth2.IDTokenValidationFailureException;
6365
import org.wso2.carbon.identity.oauth2.IdentityOAuth2ClientException;
6466
import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
67+
import org.wso2.carbon.identity.oauth2.IdentityOAuth2ServerException;
6568
import org.wso2.carbon.identity.oauth2.OAuth2Constants;
6669
import org.wso2.carbon.identity.oauth2.ResponseHeader;
6770
import org.wso2.carbon.identity.oauth2.bean.OAuthClientAuthnContext;
@@ -75,7 +78,9 @@
7578
import org.wso2.carbon.identity.oauth2.impersonation.services.ImpersonationNotificationMgtService;
7679
import org.wso2.carbon.identity.oauth2.impersonation.services.ImpersonationNotificationMgtServiceImpl;
7780
import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
81+
import org.wso2.carbon.identity.oauth2.model.AccessTokenDO;
7882
import org.wso2.carbon.identity.oauth2.model.RequestParameter;
83+
import org.wso2.carbon.identity.oauth2.model.TokenIssuanceDO;
7984
import org.wso2.carbon.identity.oauth2.rar.util.AuthorizationDetailsUtils;
8085
import org.wso2.carbon.identity.oauth2.rar.validator.AuthorizationDetailsValidator;
8186
import org.wso2.carbon.identity.oauth2.rar.validator.DefaultAuthorizationDetailsValidator;
@@ -84,12 +89,14 @@
8489
import org.wso2.carbon.identity.oauth2.token.handlers.grant.AuthorizationGrantHandler;
8590
import org.wso2.carbon.identity.oauth2.token.handlers.response.AccessTokenResponseHandler;
8691
import org.wso2.carbon.identity.oauth2.util.AuthzUtil;
92+
import org.wso2.carbon.identity.oauth2.util.JWTUtils;
8793
import org.wso2.carbon.identity.oauth2.util.OAuth2TokenUtil;
8894
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
8995
import org.wso2.carbon.identity.oauth2.validators.DefaultOAuth2ScopeValidator;
9096
import org.wso2.carbon.identity.oauth2.validators.JDBCPermissionBasedInternalScopeValidator;
9197
import org.wso2.carbon.identity.oauth2.validators.RoleBasedInternalScopeValidator;
9298
import org.wso2.carbon.identity.openidconnect.IDTokenBuilder;
99+
import org.wso2.carbon.identity.openidconnect.OIDCConstants;
93100
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
94101
import org.wso2.carbon.user.api.UserStoreException;
95102
import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
@@ -98,6 +105,7 @@
98105
import org.wso2.carbon.utils.CarbonUtils;
99106
import org.wso2.carbon.utils.DiagnosticLog;
100107

108+
import java.text.ParseException;
101109
import java.util.ArrayList;
102110
import java.util.Arrays;
103111
import java.util.Collections;
@@ -127,7 +135,7 @@
127135
import static org.wso2.carbon.identity.oauth2.device.constants.Constants.DEVICE_FLOW_GRANT_TYPE;
128136
import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.EXTENDED_REFRESH_TOKEN_DEFAULT_TIME;
129137
import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.INTERNAL_LOGIN_SCOPE;
130-
import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.validateRequestTenantDomain;
138+
import static org.wso2.carbon.identity.openidconnect.OIDCConstants.EXISTING_TOKEN_USED;
131139
import static org.wso2.carbon.identity.openidconnect.OIDCConstants.ID_TOKEN_USER_CLAIMS_PROP_KEY;
132140

133141
/**
@@ -328,7 +336,7 @@ public OAuth2AccessTokenRespDTO issue(OAuth2AccessTokenReqDTO tokenReqDTO)
328336
// Indirectly we can say that the tenantDomain of the SP is the tenantDomain of the user who created SP.
329337
// This is done to avoid having to send the tenantDomain as a query param to the token endpoint
330338
String tenantDomainOfApp = OAuth2Util.getTenantDomainOfOauthApp(oAuthAppDO);
331-
validateRequestTenantDomain(tenantDomainOfApp, tokenReqDTO);
339+
OAuth2Util.validateRequestTenantDomain(tenantDomainOfApp, tokenReqDTO);
332340

333341
tokenReqDTO.setTenantDomain(tenantDomainOfApp);
334342

@@ -1364,6 +1372,11 @@ private void triggerPostListeners(OAuth2AccessTokenReqDTO tokenReqDTO,
13641372

13651373
OAuthEventInterceptor oAuthEventInterceptorProxy = OAuthComponentServiceHolder.getInstance()
13661374
.getOAuthEventInterceptorProxy();
1375+
try {
1376+
triggerPostIssueTokenEvent(tokenReqDTO, tokenRespDTO, tokReqMsgCtx);
1377+
} catch (IdentityOAuth2Exception e) {
1378+
log.error("Error while triggering post issue token event.", e);
1379+
}
13671380

13681381
if (isRefresh) {
13691382
if (oAuthEventInterceptorProxy != null && oAuthEventInterceptorProxy.isEnabled()) {
@@ -1394,6 +1407,64 @@ private void triggerPostListeners(OAuth2AccessTokenReqDTO tokenReqDTO,
13941407
}
13951408
}
13961409

1410+
private static void triggerPostIssueTokenEvent(OAuth2AccessTokenReqDTO tokenReqDTO,
1411+
OAuth2AccessTokenRespDTO tokenRespDTO,
1412+
OAuthTokenReqMessageContext tokReqMsgCtx)
1413+
throws IdentityOAuth2Exception {
1414+
1415+
String cacheKey;
1416+
if (JWTUtils.isJWT(tokenRespDTO.getAccessToken())) {
1417+
Optional<JWTClaimsSet> jwtClaimSet;
1418+
try {
1419+
jwtClaimSet = JWTUtils.getJWTClaimSet(
1420+
JWTUtils.parseJWT(tokenRespDTO.getAccessToken()));
1421+
} catch (ParseException e) {
1422+
throw new IdentityOAuth2ServerException("Error while parsing the JWT access token.", e);
1423+
}
1424+
jwtClaimSet.orElseThrow(() -> new IdentityOAuth2ServerException("Empty JWT claims set found."));
1425+
cacheKey = jwtClaimSet.get().getJWTID();
1426+
} else {
1427+
cacheKey = tokenRespDTO.getAccessToken();
1428+
}
1429+
if (StringUtils.isBlank(cacheKey)) {
1430+
throw new IdentityOAuth2ServerException("Token cache key not found.");
1431+
}
1432+
AccessTokenDO accessTokenDO = (AccessTokenDO) OAuthCache.getInstance().getValueFromCache(
1433+
new OAuthCacheKey(cacheKey));
1434+
if (accessTokenDO == null) {
1435+
throw new IdentityOAuth2ServerException("Access token not found in the cache");
1436+
}
1437+
String tokenType = accessTokenDO.getTokenType();
1438+
int appResidentTenantId = accessTokenDO.getAppResidentTenantId();
1439+
String issuedTime = accessTokenDO.getIssuedTime() != null ?
1440+
accessTokenDO.getIssuedTime().toString() : StringUtils.EMPTY;
1441+
String authorizedOrganizationId = accessTokenDO.getAuthorizedOrganizationId();
1442+
if (!existingTokenUsed(tokReqMsgCtx)) {
1443+
OAuth2TokenUtil.postIssueToken(new TokenIssuanceDO.Builder().
1444+
tokenId(tokenRespDTO.getTokenId()).
1445+
tokenType(tokenType).
1446+
tenantDomain(tokReqMsgCtx.getOauth2AccessTokenReqDTO().getTenantDomain()).
1447+
tokenType(accessTokenDO.getTokenType()).
1448+
clientId(tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId()).
1449+
grantType(tokenReqDTO.getGrantType()).
1450+
tokenBillingCategory(OIDCConstants.TokenBillingCategory.M2M_ACCESS_TOKEN).
1451+
appResidentTenantId(appResidentTenantId).
1452+
issuedTime(issuedTime).
1453+
authorizedOrganizationId(authorizedOrganizationId).
1454+
build()
1455+
);
1456+
}
1457+
}
1458+
1459+
private static Boolean existingTokenUsed(OAuthTokenReqMessageContext tokReqMsgCtx) {
1460+
1461+
Boolean existingTokenUsed = (Boolean) tokReqMsgCtx.getProperty(EXISTING_TOKEN_USED);
1462+
if (existingTokenUsed == null) {
1463+
existingTokenUsed = false;
1464+
}
1465+
return existingTokenUsed;
1466+
}
1467+
13971468
/**
13981469
* Copies the cache entry against the authorization code/device code and adds an entry against the access token.
13991470
* This is done to reuse the calculated user claims for subsequent usages such as user info calls.

0 commit comments

Comments
 (0)