🍻 When response_type=id_token, the resulting Claims are returned in the ID Token.

This commit is contained in:
yadong.zhang 2021-05-06 21:25:21 +08:00
parent 068fc36201
commit 68d2f601bf
4 changed files with 131 additions and 13 deletions

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020-2040, 北京符节科技有限公司 (support@fujieid.com & https://www.fujieid.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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 com.fujieid.jap.ids.model.enums;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* The relationship table between scope and user attributes. For a specific scope, only part of the user attributes can be obtained
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0.0
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims" target="_blank">5.4. Requesting Claims using Scope Values</a>
* @since 1.0.2
*/
public enum ScopeClaimsMapping {
// This scope value requests access to the End-User's default profile Claims,
// which are: name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at.
profile(Arrays.asList("name", "family_name", "given_name", "middle_name", "nickname", "preferred_username", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale", "and updated_at")),
// This scope value requests access to the email and email_verified Claims.
email(Arrays.asList("email", "email_verified")),
// This scope value requests access to the phone_number and phone_number_verified Claims.
phone(Arrays.asList("phone_number", "phone_number_verified")),
// This scope value requests access to the address Claim.
address(Collections.singletonList("address"));
private final List<String> claims;
ScopeClaimsMapping(List<String> claims) {
this.claims = claims;
}
public List<String> getClaims() {
return claims;
}
}

View File

@ -110,7 +110,7 @@ public class IdsAuthorizationProvider {
* @return String
*/
public String generateIdTokenAuthorizationResponse(UserInfo userInfo, IdsRequestParam param, ClientDetail clientDetail) {
String params = "?id_token=" + TokenUtil.createIdToken(clientDetail, userInfo, param.getNonce());
String params = "?id_token=" + TokenUtil.createIdToken(clientDetail, userInfo, param);
return param.getRedirectUri() + params;
}

View File

@ -15,6 +15,7 @@
*/
package com.fujieid.jap.ids.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.fujieid.jap.ids.JapIds;
@ -25,9 +26,7 @@ import com.fujieid.jap.ids.exception.InvalidJwksException;
import com.fujieid.jap.ids.exception.InvalidTokenException;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.model.enums.JwtVerificationType;
import com.fujieid.jap.ids.model.enums.TokenSigningAlg;
import com.fujieid.jap.ids.model.enums.*;
import com.xkcoding.json.JsonUtil;
import com.xkcoding.json.util.StringUtil;
import org.jose4j.jwk.*;
@ -45,7 +44,9 @@ import org.jose4j.keys.resolvers.JwksVerificationKeyResolver;
import org.jose4j.keys.resolvers.VerificationKeyResolver;
import org.jose4j.lang.JoseException;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* simple JSON Web Key generatorhttps://mkjwk.org/?spm=a2c4g.11186623.2.33.4b2040ecxvsKD7
@ -70,6 +71,20 @@ public class JwtUtil {
* @return jwt token
*/
public static String createJwtToken(String clientId, UserInfo userinfo, Long tokenExpireIn, String nonce) {
return createJwtToken(clientId, userinfo, tokenExpireIn, nonce, null, null);
}
/**
* https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples
*
* @param clientId Client Identifier
* @param userinfo User Profile
* @param tokenExpireIn Id Token validity (seconds)
* @param nonce noncestr
* @param scopes scopes
* @return jwt token
*/
public static String createJwtToken(String clientId, UserInfo userinfo, Long tokenExpireIn, String nonce, Set<String> scopes, String responseType) {
String issuer = JapIds.getIdsConfig().getIssuer();
JwtClaims claims = new JwtClaims();
@ -90,12 +105,10 @@ public class JwtUtil {
if (!StringUtil.isEmpty(nonce)) {
claims.setStringClaim(IdsConsts.NONCE, nonce);
}
if (null != userinfo) {
// Time of completion of EU certification. This Claim is required if the RP carries the max_AGE parameter when sending the AuthN request
// Time of completion of EU certification. This Claim is required if the RP carries the max_AGE parameter when sending the AuthN request
// claims.setClaim("auth_time", "auth_time");
// If you include other claim reference: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
claims.setStringClaim("username", userinfo.getUsername());
}
setUserInfoClaim(userinfo, scopes, responseType, claims);
// A JWT is a JWS and/or a JWE with JSON claims as the payload.
// In this example it is a JWS so we create a JsonWebSignature object.
@ -139,6 +152,57 @@ public class JwtUtil {
return idToken;
}
private static void setUserInfoClaim(UserInfo userinfo, Set<String> scopes, String responseType, JwtClaims claims) {
if (null != userinfo) {
// If you include other claim reference: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
claims.setStringClaim("username", userinfo.getUsername());
if (ObjectUtil.isNotNull(scopes) && ResponseType.ID_TOKEN.getType().equalsIgnoreCase(responseType)) {
// This scope value requests access to the End-User's default profile Claims,
// which are: name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at.
Map<String, Object> userInfoMap = JsonUtil.parseKv(JsonUtil.toJsonString(userinfo));
if (scopes.contains("profile")) {
ScopeClaimsMapping scopeClaimsMapping = ScopeClaimsMapping.profile;
List<String> claimList = scopeClaimsMapping.getClaims();
for (String claim : claimList) {
if (userInfoMap.containsKey(claim) && null != userInfoMap.get(claim)) {
claims.setClaim(claim, userInfoMap.get(claim));
}
}
}
// This scope value requests access to the email and email_verified Claims.
if (scopes.contains("email")) {
ScopeClaimsMapping scopeClaimsMapping = ScopeClaimsMapping.email;
List<String> claimList = scopeClaimsMapping.getClaims();
for (String claim : claimList) {
if (userInfoMap.containsKey(claim) && null != userInfoMap.get(claim)) {
claims.setClaim(claim, userInfoMap.get(claim));
}
}
}
// This scope value requests access to the phone_number and phone_number_verified Claims.
if (scopes.contains("phone")) {
ScopeClaimsMapping scopeClaimsMapping = ScopeClaimsMapping.phone;
List<String> claimList = scopeClaimsMapping.getClaims();
for (String claim : claimList) {
if (userInfoMap.containsKey(claim) && null != userInfoMap.get(claim)) {
claims.setClaim(claim, userInfoMap.get(claim));
}
}
}
// This scope value requests access to the address Claim.
if (scopes.contains("address")) {
ScopeClaimsMapping scopeClaimsMapping = ScopeClaimsMapping.address;
List<String> claimList = scopeClaimsMapping.getClaims();
for (String claim : claimList) {
if (userInfoMap.containsKey(claim) && null != userInfoMap.get(claim)) {
claims.setClaim(claim, userInfoMap.get(claim));
}
}
}
}
}
}
public static Map<String, Object> parseJwtToken(String jwtToken) {
JwtConfig jwtConfig = JapIds.getContext().getIdentityService().getJwtConfig(null);
if (null == jwtConfig) {

View File

@ -20,10 +20,7 @@ import cn.hutool.crypto.SecureUtil;
import com.fujieid.jap.core.util.RequestUtil;
import com.fujieid.jap.ids.JapIds;
import com.fujieid.jap.ids.exception.InvalidTokenException;
import com.fujieid.jap.ids.model.AccessToken;
import com.fujieid.jap.ids.model.ClientDetail;
import com.fujieid.jap.ids.model.IdsConsts;
import com.fujieid.jap.ids.model.UserInfo;
import com.fujieid.jap.ids.model.*;
import com.fujieid.jap.ids.model.enums.ErrorResponse;
import com.fujieid.jap.ids.model.enums.TokenAuthMethod;
import com.xkcoding.json.util.StringUtil;
@ -103,6 +100,11 @@ public class TokenUtil {
return JwtUtil.createJwtToken(clientDetail.getClientId(), user, idTokenExpiresIn, nonce);
}
public static String createIdToken(ClientDetail clientDetail, UserInfo user, IdsRequestParam param) {
long idTokenExpiresIn = OauthUtil.getIdTokenExpiresIn(clientDetail.getIdTokenExpiresIn());
return JwtUtil.createJwtToken(clientDetail.getClientId(), user, idTokenExpiresIn, param.getNonce(), OauthUtil.convertStrToList(param.getScope()), param.getResponseType());
}
public static AccessToken createAccessToken(UserInfo user, ClientDetail clientDetail, String grantType, String scope, String nonce) {
String clientId = clientDetail.getClientId();