MaxKey 2 лет назад
Родитель
Сommit
ba9790c771
14 измененных файлов с 1399 добавлено и 45 удалено
  1. 17 0
      maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequestCost.java
  2. 17 0
      maxkey-common/src/main/java/org/maxkey/entity/DbTableColumn.java
  3. 17 0
      maxkey-common/src/main/java/org/maxkey/entity/DbTableMetaData.java
  4. 5 1
      maxkey-core/src/main/java/org/maxkey/constants/ContentType.java
  5. 0 42
      maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/AuthorizationEndpoint.java
  6. 174 0
      maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/IntrospectEndpoint.java
  7. 97 0
      maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/OauthJwksEndpoint.java
  8. 261 0
      maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OauthServerConfiguration.java
  9. 251 0
      maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OpenidConfiguration.java
  10. 270 0
      maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OauthAuthorizationServerEndpoint.java
  11. 269 0
      maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OpenidConfigurationEndpoint.java
  12. 2 1
      maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oidc/idtoken/OIDCIdTokenEnhancer.java
  13. 2 1
      maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java
  14. 17 0
      maxkey-synchronizers/maxkey-synchronizer-jdbc/src/main/java/org/maxkey/synchronizer/jdbc/ColumnFieldMapper.java

+ 17 - 0
maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequestCost.java

@@ -1,3 +1,20 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.
+ */
+ 
+
 //
 // Source code recreated from a .class file by IntelliJ IDEA
 // (powered by FernFlower decompiler)

+ 17 - 0
maxkey-common/src/main/java/org/maxkey/entity/DbTableColumn.java

@@ -1,4 +1,21 @@
 /*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.
+ */
+ 
+
+/*
  * Copyright (c) 2022, MaxKey and/or its affiliates. All rights reserved.
  *
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.

+ 17 - 0
maxkey-common/src/main/java/org/maxkey/entity/DbTableMetaData.java

@@ -1,4 +1,21 @@
 /*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.
+ */
+ 
+
+/*
  * Copyright (c) 2022, MaxKey and/or its affiliates. All rights reserved.
  *
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.

+ 5 - 1
maxkey-core/src/main/java/org/maxkey/constants/ContentType.java

@@ -1,5 +1,5 @@
 /*
- * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -47,4 +47,8 @@ public class ContentType {
 
     public static final String IMAGE_PNG 				= "image/png";
     
+    public static final String JSON 					= "json";
+    
+    public static final String XML 						= "xml";
+    
 }

+ 0 - 42
maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/AuthorizationEndpoint.java

@@ -23,7 +23,6 @@ import java.util.Set;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.mybatis.jpa.util.JpaWebContext;
 import org.maxkey.authn.annotation.CurrentUser;
 import org.maxkey.authn.web.AuthorizationUtils;
 import org.maxkey.authz.oauth2.common.OAuth2AccessToken;
@@ -46,8 +45,6 @@ import org.maxkey.authz.oauth2.provider.approval.UserApprovalHandler;
 import org.maxkey.authz.oauth2.provider.code.AuthorizationCodeServices;
 import org.maxkey.authz.oauth2.provider.implicit.ImplicitTokenRequest;
 import org.maxkey.authz.oauth2.provider.request.DefaultOAuth2RequestValidator;
-import org.maxkey.constants.ContentType;
-import org.maxkey.crypto.jose.keystore.JWKSetKeyStore;
 import org.maxkey.util.HttpEncoder;
 import org.maxkey.entity.Message;
 import org.maxkey.entity.UserInfo;
@@ -67,11 +64,8 @@ import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.support.SessionStatus;
 import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.View;
-import org.springframework.web.servlet.view.RedirectView;
 import org.springframework.web.util.UriComponents;
 import org.springframework.web.util.UriComponentsBuilder;
 import org.springframework.web.util.UriTemplate;
@@ -297,42 +291,6 @@ public class AuthorizationEndpoint extends AbstractEndpoint {
 		}
 
 	}
-	
-	@Operation(summary = "OAuth JWk 元数据接口", description = "参数mxk_metadata_APPID",method="GET")
-	@RequestMapping(
-			value = "/metadata/oauth/v20/" + WebConstants.MXK_METADATA_PREFIX + "{appid}.{mediaType}",
-			method={RequestMethod.POST, RequestMethod.GET})
-	@ResponseBody
-	public String  metadata(HttpServletRequest request,
-			HttpServletResponse response, 
-			@PathVariable("appid") String appId,
-			@PathVariable("mediaType") String mediaType) {
-		ClientDetails  clientDetails = getClientDetailsService().loadClientByClientId(appId,true);
-		if(clientDetails != null) {
-			String jwkSetString = "";
-			if(!clientDetails.getSignature().equalsIgnoreCase("none")) {
-				jwkSetString = clientDetails.getSignatureKey();
-			}
-			if(!clientDetails.getAlgorithm().equalsIgnoreCase("none")) {
-				if(!StringUtils.hasText(jwkSetString)) {
-					jwkSetString = clientDetails.getAlgorithmKey();
-				}else {
-					jwkSetString = jwkSetString + "," +clientDetails.getAlgorithmKey();
-				}
-			}
-			JWKSetKeyStore jwkSetKeyStore = new JWKSetKeyStore("{\"keys\": [" + jwkSetString + "]}");
-			
-			if(StringUtils.hasText(mediaType) 
-					&& mediaType.equalsIgnoreCase("xml")) {
-				response.setContentType(ContentType.APPLICATION_XML_UTF8);
-			}else {
-				response.setContentType(ContentType.APPLICATION_JSON_UTF8);
-			}
-			return jwkSetKeyStore.toString(mediaType);
-		}
-		
-		return appId + " not exist . \n" + JpaWebContext.version();
-	}
 
 	// We need explicit approval from the user.
 	private ModelAndView getUserApprovalPageResponse(Map<String, Object> model,

+ 174 - 0
maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/IntrospectEndpoint.java

@@ -0,0 +1,174 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.maxkey.authz.oauth2.provider.endpoint;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.maxkey.authn.SignPrincipal;
+import org.maxkey.authz.oauth2.common.OAuth2Constants;
+import org.maxkey.authz.oauth2.common.exceptions.OAuth2Exception;
+import org.maxkey.authz.oauth2.provider.ClientDetailsService;
+import org.maxkey.authz.oauth2.provider.OAuth2Authentication;
+import org.maxkey.authz.oauth2.provider.token.DefaultTokenServices;
+import org.maxkey.util.AuthorizationHeaderCredential;
+import org.maxkey.util.AuthorizationHeaderUtils;
+import org.maxkey.util.JsonUtils;
+import org.maxkey.web.HttpResponseAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+@Tag(name = "2-1-OAuth v2.0 API文档模块")
+@Controller
+public class IntrospectEndpoint {
+	final static Logger _logger = LoggerFactory.getLogger(IntrospectEndpoint.class);	
+	@Autowired
+	@Qualifier("oauth20JdbcClientDetailsService")
+	private ClientDetailsService clientDetailsService;
+	
+	@Autowired
+	@Qualifier("oauth20TokenServices")
+	private DefaultTokenServices oauth20tokenServices;
+	
+	@Autowired
+	ProviderManager oauth20ClientAuthenticationManager;
+
+    @Autowired
+    protected HttpResponseAdapter httpResponseAdapter;
+	
+    @Operation(summary = "OAuth 2.0 令牌验证接口", description = "传递参数token or access_token",method="POST,GET")
+	@RequestMapping(value=OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/introspect", method = {RequestMethod.POST, RequestMethod.GET}) 
+	public void introspect(
+			@RequestParam(value = "token", required = false) String token,
+			@RequestParam(value = "access_token", required = false) String access_token,
+            HttpServletRequest request, HttpServletResponse response) {	  
+    	String  authorization = request.getHeader(AuthorizationHeaderUtils.HEADER_Authorization);
+		AuthorizationHeaderCredential headerCredential = AuthorizationHeaderUtils.resolve(authorization);
+		_logger.debug("Credential {}" , headerCredential);
+		if(StringUtils.isNotBlank(token)) {
+			access_token = token;
+		}
+        if(StringUtils.isBlank(access_token)) {
+        	_logger.error("access_token is null .");
+        }
+        _logger.debug("access_token {}" , access_token);
+	    
+		OAuth2Authentication oAuth2Authentication =null;
+		Introspection introspection = new Introspection(access_token);
+		try{
+			 oAuth2Authentication = oauth20tokenServices.loadAuthentication(access_token);
+			 if(oAuth2Authentication != null && clientAuthenticate(headerCredential)) {   
+				 String client_id = oAuth2Authentication.getOAuth2Request().getClientId();
+				 if(headerCredential.getUsername().equals(client_id)) {
+					 String sub = client_id;
+					//if userAuthentication not null , is password or code , else client_credentials
+					 if(oAuth2Authentication.getUserAuthentication() != null) {
+						 sub = ((SignPrincipal)oAuth2Authentication.getUserAuthentication().getPrincipal()).getUsername();
+					 }
+					 introspection.setSub(sub,true);
+				 }
+			 }
+		}catch(OAuth2Exception e){
+			_logger.error("OAuth2Exception ", e);
+		}
+		
+		httpResponseAdapter.write(response,JsonUtils.gsonToString(introspection),"json"); 
+	}
+	
+    public boolean clientAuthenticate(AuthorizationHeaderCredential headerCredential) {
+    	if(headerCredential != null){
+			UsernamePasswordAuthenticationToken authenticationToken = null;
+			if(headerCredential.getCredentialType().equals(AuthorizationHeaderCredential.Credential.BASIC)) {
+			    if(StringUtils.isNotBlank(headerCredential.getUsername())&&
+			    		StringUtils.isNotBlank(headerCredential.getCredential())
+			    		) {
+			    	UsernamePasswordAuthenticationToken authRequest = 
+							new UsernamePasswordAuthenticationToken(
+									headerCredential.getUsername(),
+									headerCredential.getCredential());
+			    	authenticationToken = (UsernamePasswordAuthenticationToken)oauth20ClientAuthenticationManager.authenticate(authRequest);
+			    }
+			}
+			if(authenticationToken != null && authenticationToken.isAuthenticated()) {
+				return true;
+			}
+		}
+    	return false;
+    }
+    
+	public void setOauth20tokenServices(DefaultTokenServices oauth20tokenServices) {
+		this.oauth20tokenServices = oauth20tokenServices;
+	}
+	
+	public class Introspection {
+		
+		String token;
+		boolean active;
+		String sub;
+
+		public String getToken() {
+			return token;
+		}
+
+		public void setToken(String token) {
+			this.token = token;
+		}
+
+		public boolean isActive() {
+			return active;
+		}
+
+		public void setActive(boolean active) {
+			this.active = active;
+		}
+
+		public String getSub() {
+			return sub;
+		}
+
+		public void setSub(String sub,boolean active) {
+			this.sub = sub;
+			this.active = active;
+		}
+
+		public Introspection(String token) {
+			this.token = token;
+			this.active = false;
+		}
+
+		public Introspection(String token, boolean active, String sub) {
+			this.token = token;
+			this.active = active;
+			this.sub = sub;
+		}
+
+	}
+
+}

+ 97 - 0
maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/OauthJwksEndpoint.java

@@ -0,0 +1,97 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.maxkey.authz.oauth2.provider.endpoint;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.mybatis.jpa.util.JpaWebContext;
+import org.maxkey.authz.oauth2.common.OAuth2Constants;
+import org.maxkey.constants.ContentType;
+import org.maxkey.crypto.jose.keystore.JWKSetKeyStore;
+import org.maxkey.entity.apps.oauth2.provider.ClientDetails;
+import org.maxkey.web.WebConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+@Tag(name = "2-1-OAuth v2.0 API文档模块")
+@Controller
+public class OauthJwksEndpoint extends AbstractEndpoint {
+	final static Logger _logger = LoggerFactory.getLogger(OauthJwksEndpoint.class);
+
+	@Operation(summary = "OAuth JWk 元数据接口", description = "参数mxk_metadata_APPID",method="GET")
+	@RequestMapping(
+			value = OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/jwks",
+			method={RequestMethod.POST, RequestMethod.GET})
+	@ResponseBody
+	public String  keysMetadata(HttpServletRequest request , HttpServletResponse response, 
+			@RequestParam(value = "client_id", required = false) String client_id) {
+		return metadata(request,response,client_id,null);
+	}
+	
+	@Operation(summary = "OAuth JWk 元数据接口", description = "参数mxk_metadata_APPID",method="GET")
+	@RequestMapping(
+			value = "/metadata/oauth/v20/" + WebConstants.MXK_METADATA_PREFIX + "{appid}.{mediaType}",
+			method={RequestMethod.POST, RequestMethod.GET})
+	@ResponseBody
+	public String  metadata(HttpServletRequest request , HttpServletResponse response, 
+			@PathVariable(value="appid", required = false) String appId,
+			@PathVariable(value="mediaType", required = false) String mediaType) {
+		ClientDetails  clientDetails = null;
+		try {
+			clientDetails = getClientDetailsService().loadClientByClientId(appId,true);
+		}catch(Exception e) {
+			_logger.error("getClientDetailsService", e);
+		}
+		if(clientDetails != null) {
+			String jwkSetString = "";
+			if(!clientDetails.getSignature().equalsIgnoreCase("none")) {
+				jwkSetString = clientDetails.getSignatureKey();
+			}
+			if(!clientDetails.getAlgorithm().equalsIgnoreCase("none")) {
+				if(!StringUtils.hasText(jwkSetString)) {
+					jwkSetString = clientDetails.getAlgorithmKey();
+				}else {
+					jwkSetString = jwkSetString + "," +clientDetails.getAlgorithmKey();
+				}
+			}
+			JWKSetKeyStore jwkSetKeyStore = new JWKSetKeyStore("{\"keys\": [" + jwkSetString + "]}");
+			
+			if(StringUtils.hasText(mediaType) 
+					&& mediaType.equalsIgnoreCase(ContentType.XML)) {
+				response.setContentType(ContentType.APPLICATION_XML_UTF8);
+			}else {
+				response.setContentType(ContentType.APPLICATION_JSON_UTF8);
+			}
+			return jwkSetKeyStore.toString(mediaType);
+		}
+		
+		return appId + " not exist . \n" + JpaWebContext.version();
+	}
+
+}

+ 261 - 0
maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OauthServerConfiguration.java

@@ -0,0 +1,261 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.maxkey.authz.oauth2.provider.wellknown;
+
+import java.util.Set;
+
+public class OauthServerConfiguration {
+	String client_id;
+	String issuer;
+	String authorization_endpoint;
+	String token_endpoint;
+	String userinfo_endpoint;
+	String registration_endpoint;
+	String jwks_uri;
+	Set<String> code_challenge_methods_supported;
+	
+	Set<String> response_types_supported;
+	Set<String> response_modes_supported;
+	Set<String> grant_types_supported;
+	Set<String> subject_types_supported;
+	Set<String> id_token_signing_alg_values_supported;
+
+	Set<String> scopes_supported;
+	Set<String> token_endpoint_auth_methods_supported;
+	Set<String> claims_supported;
+	String introspection_endpoint;
+	Set<String> introspection_endpoint_auth_methods_supported;
+	String revocation_endpoint;
+	Set<String> revocation_endpoint_auth_methods_supported;
+	String end_session_endpoint;
+	boolean request_parameter_supported;
+	Set<String> request_object_signing_alg_values_supported;
+	Set<String> backchannel_token_delivery_modes_supported;
+	Set<String> backchannel_authentication_request_signing_alg_values_supported;
+
+	
+	public String getClient_id() {
+		return client_id;
+	}
+
+	public void setClient_id(String client_id) {
+		this.client_id = client_id;
+	}
+
+	public String getIssuer() {
+		return issuer;
+	}
+
+	public void setIssuer(String issuer) {
+		this.issuer = issuer;
+	}
+
+	public String getAuthorization_endpoint() {
+		return authorization_endpoint;
+	}
+
+	public void setAuthorization_endpoint(String authorization_endpoint) {
+		this.authorization_endpoint = authorization_endpoint;
+	}
+
+	public Set<String> getCode_challenge_methods_supported() {
+		return code_challenge_methods_supported;
+	}
+
+	public void setCode_challenge_methods_supported(Set<String> code_challenge_methods_supported) {
+		this.code_challenge_methods_supported = code_challenge_methods_supported;
+	}
+
+	public String getToken_endpoint() {
+		return token_endpoint;
+	}
+
+	public void setToken_endpoint(String token_endpoint) {
+		this.token_endpoint = token_endpoint;
+	}
+
+	public String getUserinfo_endpoint() {
+		return userinfo_endpoint;
+	}
+
+	public void setUserinfo_endpoint(String userinfo_endpoint) {
+		this.userinfo_endpoint = userinfo_endpoint;
+	}
+
+	public String getRegistration_endpoint() {
+		return registration_endpoint;
+	}
+
+	public void setRegistration_endpoint(String registration_endpoint) {
+		this.registration_endpoint = registration_endpoint;
+	}
+
+	public String getJwks_uri() {
+		return jwks_uri;
+	}
+
+	public void setJwks_uri(String jwks_uri) {
+		this.jwks_uri = jwks_uri;
+	}
+
+	public Set<String> getResponse_types_supported() {
+		return response_types_supported;
+	}
+
+	public void setResponse_types_supported(Set<String> response_types_supported) {
+		this.response_types_supported = response_types_supported;
+	}
+
+	public Set<String> getResponse_modes_supported() {
+		return response_modes_supported;
+	}
+
+	public void setResponse_modes_supported(Set<String> response_modes_supported) {
+		this.response_modes_supported = response_modes_supported;
+	}
+
+	public Set<String> getGrant_types_supported() {
+		return grant_types_supported;
+	}
+
+	public void setGrant_types_supported(Set<String> grant_types_supported) {
+		this.grant_types_supported = grant_types_supported;
+	}
+
+	public Set<String> getSubject_types_supported() {
+		return subject_types_supported;
+	}
+
+	public void setSubject_types_supported(Set<String> subject_types_supported) {
+		this.subject_types_supported = subject_types_supported;
+	}
+
+	public Set<String> getId_token_signing_alg_values_supported() {
+		return id_token_signing_alg_values_supported;
+	}
+
+	public void setId_token_signing_alg_values_supported(Set<String> id_token_signing_alg_values_supported) {
+		this.id_token_signing_alg_values_supported = id_token_signing_alg_values_supported;
+	}
+
+	public Set<String> getScopes_supported() {
+		return scopes_supported;
+	}
+
+	public void setScopes_supported(Set<String> scopes_supported) {
+		this.scopes_supported = scopes_supported;
+	}
+
+	public Set<String> getToken_endpoint_auth_methods_supported() {
+		return token_endpoint_auth_methods_supported;
+	}
+
+	public void setToken_endpoint_auth_methods_supported(Set<String> token_endpoint_auth_methods_supported) {
+		this.token_endpoint_auth_methods_supported = token_endpoint_auth_methods_supported;
+	}
+
+	public Set<String> getClaims_supported() {
+		return claims_supported;
+	}
+
+	public void setClaims_supported(Set<String> claims_supported) {
+		this.claims_supported = claims_supported;
+	}
+
+	public String getIntrospection_endpoint() {
+		return introspection_endpoint;
+	}
+
+	public void setIntrospection_endpoint(String introspection_endpoint) {
+		this.introspection_endpoint = introspection_endpoint;
+	}
+
+	public Set<String> getIntrospection_endpoint_auth_methods_supported() {
+		return introspection_endpoint_auth_methods_supported;
+	}
+
+	public void setIntrospection_endpoint_auth_methods_supported(
+			Set<String> introspection_endpoint_auth_methods_supported) {
+		this.introspection_endpoint_auth_methods_supported = introspection_endpoint_auth_methods_supported;
+	}
+
+	public String getRevocation_endpoint() {
+		return revocation_endpoint;
+	}
+
+	public void setRevocation_endpoint(String revocation_endpoint) {
+		this.revocation_endpoint = revocation_endpoint;
+	}
+
+	public Set<String> getRevocation_endpoint_auth_methods_supported() {
+		return revocation_endpoint_auth_methods_supported;
+	}
+
+	public void setRevocation_endpoint_auth_methods_supported(
+			Set<String> revocation_endpoint_auth_methods_supported) {
+		this.revocation_endpoint_auth_methods_supported = revocation_endpoint_auth_methods_supported;
+	}
+
+	public String getEnd_session_endpoint() {
+		return end_session_endpoint;
+	}
+
+	public void setEnd_session_endpoint(String end_session_endpoint) {
+		this.end_session_endpoint = end_session_endpoint;
+	}
+
+	public boolean isRequest_parameter_supported() {
+		return request_parameter_supported;
+	}
+
+	public void setRequest_parameter_supported(boolean request_parameter_supported) {
+		this.request_parameter_supported = request_parameter_supported;
+	}
+
+	public Set<String> getRequest_object_signing_alg_values_supported() {
+		return request_object_signing_alg_values_supported;
+	}
+
+	public void setRequest_object_signing_alg_values_supported(
+			Set<String> request_object_signing_alg_values_supported) {
+		this.request_object_signing_alg_values_supported = request_object_signing_alg_values_supported;
+	}
+
+	public Set<String> getBackchannel_token_delivery_modes_supported() {
+		return backchannel_token_delivery_modes_supported;
+	}
+
+	public void setBackchannel_token_delivery_modes_supported(
+			Set<String> backchannel_token_delivery_modes_supported) {
+		this.backchannel_token_delivery_modes_supported = backchannel_token_delivery_modes_supported;
+	}
+
+	public Set<String> getBackchannel_authentication_request_signing_alg_values_supported() {
+		return backchannel_authentication_request_signing_alg_values_supported;
+	}
+
+	public void setBackchannel_authentication_request_signing_alg_values_supported(
+			Set<String> backchannel_authentication_request_signing_alg_values_supported) {
+		this.backchannel_authentication_request_signing_alg_values_supported = backchannel_authentication_request_signing_alg_values_supported;
+	}
+
+	public OauthServerConfiguration() {
+		super();
+	}
+
+}

+ 251 - 0
maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OpenidConfiguration.java

@@ -0,0 +1,251 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.maxkey.authz.oauth2.provider.wellknown;
+
+import java.util.Set;
+
+public class OpenidConfiguration {
+	String client_id;
+	String issuer;
+	String authorization_endpoint;
+	String token_endpoint;
+	String userinfo_endpoint;
+	String registration_endpoint;
+	String jwks_uri;
+	Set<String> response_types_supported;
+	Set<String> response_modes_supported;
+	Set<String> grant_types_supported;
+	Set<String> subject_types_supported;
+	Set<String> id_token_signing_alg_values_supported;
+
+	Set<String> scopes_supported;
+	Set<String> token_endpoint_auth_methods_supported;
+	Set<String> claims_supported;
+	String introspection_endpoint;
+	Set<String> introspection_endpoint_auth_methods_supported;
+	String revocation_endpoint;
+	Set<String> revocation_endpoint_auth_methods_supported;
+	String end_session_endpoint;
+	boolean request_parameter_supported;
+	Set<String> request_object_signing_alg_values_supported;
+	Set<String> backchannel_token_delivery_modes_supported;
+	Set<String> backchannel_authentication_request_signing_alg_values_supported;
+
+	
+	public String getClient_id() {
+		return client_id;
+	}
+
+	public void setClient_id(String client_id) {
+		this.client_id = client_id;
+	}
+
+	public String getIssuer() {
+		return issuer;
+	}
+
+	public void setIssuer(String issuer) {
+		this.issuer = issuer;
+	}
+
+	public String getAuthorization_endpoint() {
+		return authorization_endpoint;
+	}
+
+	public void setAuthorization_endpoint(String authorization_endpoint) {
+		this.authorization_endpoint = authorization_endpoint;
+	}
+
+	public String getToken_endpoint() {
+		return token_endpoint;
+	}
+
+	public void setToken_endpoint(String token_endpoint) {
+		this.token_endpoint = token_endpoint;
+	}
+
+	public String getUserinfo_endpoint() {
+		return userinfo_endpoint;
+	}
+
+	public void setUserinfo_endpoint(String userinfo_endpoint) {
+		this.userinfo_endpoint = userinfo_endpoint;
+	}
+
+	public String getRegistration_endpoint() {
+		return registration_endpoint;
+	}
+
+	public void setRegistration_endpoint(String registration_endpoint) {
+		this.registration_endpoint = registration_endpoint;
+	}
+
+	public String getJwks_uri() {
+		return jwks_uri;
+	}
+
+	public void setJwks_uri(String jwks_uri) {
+		this.jwks_uri = jwks_uri;
+	}
+
+	public Set<String> getResponse_types_supported() {
+		return response_types_supported;
+	}
+
+	public void setResponse_types_supported(Set<String> response_types_supported) {
+		this.response_types_supported = response_types_supported;
+	}
+
+	public Set<String> getResponse_modes_supported() {
+		return response_modes_supported;
+	}
+
+	public void setResponse_modes_supported(Set<String> response_modes_supported) {
+		this.response_modes_supported = response_modes_supported;
+	}
+
+	public Set<String> getGrant_types_supported() {
+		return grant_types_supported;
+	}
+
+	public void setGrant_types_supported(Set<String> grant_types_supported) {
+		this.grant_types_supported = grant_types_supported;
+	}
+
+	public Set<String> getSubject_types_supported() {
+		return subject_types_supported;
+	}
+
+	public void setSubject_types_supported(Set<String> subject_types_supported) {
+		this.subject_types_supported = subject_types_supported;
+	}
+
+	public Set<String> getId_token_signing_alg_values_supported() {
+		return id_token_signing_alg_values_supported;
+	}
+
+	public void setId_token_signing_alg_values_supported(Set<String> id_token_signing_alg_values_supported) {
+		this.id_token_signing_alg_values_supported = id_token_signing_alg_values_supported;
+	}
+
+	public Set<String> getScopes_supported() {
+		return scopes_supported;
+	}
+
+	public void setScopes_supported(Set<String> scopes_supported) {
+		this.scopes_supported = scopes_supported;
+	}
+
+	public Set<String> getToken_endpoint_auth_methods_supported() {
+		return token_endpoint_auth_methods_supported;
+	}
+
+	public void setToken_endpoint_auth_methods_supported(Set<String> token_endpoint_auth_methods_supported) {
+		this.token_endpoint_auth_methods_supported = token_endpoint_auth_methods_supported;
+	}
+
+	public Set<String> getClaims_supported() {
+		return claims_supported;
+	}
+
+	public void setClaims_supported(Set<String> claims_supported) {
+		this.claims_supported = claims_supported;
+	}
+
+	public String getIntrospection_endpoint() {
+		return introspection_endpoint;
+	}
+
+	public void setIntrospection_endpoint(String introspection_endpoint) {
+		this.introspection_endpoint = introspection_endpoint;
+	}
+
+	public Set<String> getIntrospection_endpoint_auth_methods_supported() {
+		return introspection_endpoint_auth_methods_supported;
+	}
+
+	public void setIntrospection_endpoint_auth_methods_supported(
+			Set<String> introspection_endpoint_auth_methods_supported) {
+		this.introspection_endpoint_auth_methods_supported = introspection_endpoint_auth_methods_supported;
+	}
+
+	public String getRevocation_endpoint() {
+		return revocation_endpoint;
+	}
+
+	public void setRevocation_endpoint(String revocation_endpoint) {
+		this.revocation_endpoint = revocation_endpoint;
+	}
+
+	public Set<String> getRevocation_endpoint_auth_methods_supported() {
+		return revocation_endpoint_auth_methods_supported;
+	}
+
+	public void setRevocation_endpoint_auth_methods_supported(
+			Set<String> revocation_endpoint_auth_methods_supported) {
+		this.revocation_endpoint_auth_methods_supported = revocation_endpoint_auth_methods_supported;
+	}
+
+	public String getEnd_session_endpoint() {
+		return end_session_endpoint;
+	}
+
+	public void setEnd_session_endpoint(String end_session_endpoint) {
+		this.end_session_endpoint = end_session_endpoint;
+	}
+
+	public boolean isRequest_parameter_supported() {
+		return request_parameter_supported;
+	}
+
+	public void setRequest_parameter_supported(boolean request_parameter_supported) {
+		this.request_parameter_supported = request_parameter_supported;
+	}
+
+	public Set<String> getRequest_object_signing_alg_values_supported() {
+		return request_object_signing_alg_values_supported;
+	}
+
+	public void setRequest_object_signing_alg_values_supported(
+			Set<String> request_object_signing_alg_values_supported) {
+		this.request_object_signing_alg_values_supported = request_object_signing_alg_values_supported;
+	}
+
+	public Set<String> getBackchannel_token_delivery_modes_supported() {
+		return backchannel_token_delivery_modes_supported;
+	}
+
+	public void setBackchannel_token_delivery_modes_supported(
+			Set<String> backchannel_token_delivery_modes_supported) {
+		this.backchannel_token_delivery_modes_supported = backchannel_token_delivery_modes_supported;
+	}
+
+	public Set<String> getBackchannel_authentication_request_signing_alg_values_supported() {
+		return backchannel_authentication_request_signing_alg_values_supported;
+	}
+
+	public void setBackchannel_authentication_request_signing_alg_values_supported(
+			Set<String> backchannel_authentication_request_signing_alg_values_supported) {
+		this.backchannel_authentication_request_signing_alg_values_supported = backchannel_authentication_request_signing_alg_values_supported;
+	}
+
+	public OpenidConfiguration() {
+		super();
+	}
+
+}

+ 270 - 0
maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OauthAuthorizationServerEndpoint.java

@@ -0,0 +1,270 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.maxkey.authz.oauth2.provider.wellknown.endpoint;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.maxkey.authz.oauth2.common.OAuth2Constants;
+import org.maxkey.authz.oauth2.provider.endpoint.AbstractEndpoint;
+import org.maxkey.authz.oauth2.provider.wellknown.OauthServerConfiguration;
+import org.maxkey.entity.apps.oauth2.provider.ClientDetails;
+import org.maxkey.pretty.impl.JsonPretty;
+import org.maxkey.web.WebContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.alibaba.cloud.commons.lang.StringUtils;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+@Tag(name = "2-1-OAuth v2.0 API文档模块")
+@Controller
+public class OauthAuthorizationServerEndpoint extends AbstractEndpoint {
+	final static Logger _logger = LoggerFactory.getLogger(OauthAuthorizationServerEndpoint.class);
+	
+	@Operation(summary = "OAuth v2 metadata 元数据接口", description = "参数client_id",method="GET,POST")
+	@RequestMapping(
+			value = {
+					OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/.well-known/oauth-authorization-server"},
+			produces = "application/json",
+			method={RequestMethod.POST, RequestMethod.GET})
+	@ResponseBody
+	public String  configuration(
+			HttpServletRequest request,
+			HttpServletResponse response,
+			@RequestParam(value = "client_id", required = false) String client_id) {
+		return configurationMetadata(request,response, null,client_id);
+	}
+	
+	@Operation(summary = "OAuth v2 metadata 元数据接口", description = "参数client_id",method="GET,POST")
+	@RequestMapping(
+			value = {
+					OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/{instId}/.well-known/oauth-authorization-server"},
+			produces = "application/json",
+			method={RequestMethod.POST, RequestMethod.GET})
+	@ResponseBody
+	public String  configurationMetadata(
+			HttpServletRequest request,
+			HttpServletResponse response, 
+			@PathVariable("instId") String instId,
+			@RequestParam(value = "client_id", required = false) String client_id) {
+		_logger.debug("instId {} , client_id {}" , instId ,client_id);
+		
+		String baseUrl = WebContext.getContextPath(true);
+		
+		ClientDetails  clientDetails = null;
+		
+		if(StringUtils.isNotBlank(client_id)) {
+			try {
+				clientDetails = getClientDetailsService().loadClientByClientId(client_id,true);
+			}catch(Exception e) {
+				_logger.error("getClientDetailsService", e);
+			}
+		}
+		
+		OauthServerConfiguration oauthConfig = new OauthServerConfiguration();
+		oauthConfig.setRequest_parameter_supported(true);
+		oauthConfig.setAuthorization_endpoint(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/authorize");
+		oauthConfig.setToken_endpoint(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/token");
+		oauthConfig.setIntrospection_endpoint(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/introspect");
+		oauthConfig.setUserinfo_endpoint(baseUrl + "/api/oauth/v20/me");
+		oauthConfig.setEnd_session_endpoint(baseUrl + "/force/logout");
+		
+		Set<String>  code_challenge_methods_supported = new HashSet<String>();
+		code_challenge_methods_supported.add("S256");
+		oauthConfig.setCode_challenge_methods_supported(code_challenge_methods_supported);
+		
+		if(clientDetails != null) {
+			oauthConfig.setClient_id(client_id);
+			oauthConfig.setJwks_uri(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/jwks?client_id="+ clientDetails.getClientId());
+
+			Set<String>  introspection_endpoint_auth_methods_supported = new HashSet<String>();
+			introspection_endpoint_auth_methods_supported.add("client_secret_basic");
+			oauthConfig.setIntrospection_endpoint_auth_methods_supported(introspection_endpoint_auth_methods_supported);                                                  
+			                                                  
+			oauthConfig.setIssuer(clientDetails.getIssuer());
+			oauthConfig.setResponse_types_supported(clientDetails.getAuthorizedGrantTypes());
+			
+			Set<String>  response_modes_supported = new HashSet<String>();
+			response_modes_supported.add("query");
+			response_modes_supported.add("form_post");
+			oauthConfig.setResponse_modes_supported(response_modes_supported);
+			
+			oauthConfig.setGrant_types_supported(clientDetails.getAuthorizedGrantTypes());
+			oauthConfig.setClaims_supported(clientDetails.getScope());
+			
+			
+			Set<String>  id_token_signing_alg_values_supported = new HashSet<String>();
+			id_token_signing_alg_values_supported.add(clientDetails.getSignature().toUpperCase());
+			oauthConfig.setId_token_signing_alg_values_supported(id_token_signing_alg_values_supported);
+			
+			oauthConfig.setScopes_supported(clientDetails.getScope());
+			
+			Set<String>  token_endpoint_auth_methods_supported = new HashSet<String>();
+			token_endpoint_auth_methods_supported.add("client_secret_basic");
+			token_endpoint_auth_methods_supported.add("client_secret_post");
+			token_endpoint_auth_methods_supported.add("none");
+			oauthConfig.setToken_endpoint_auth_methods_supported(token_endpoint_auth_methods_supported);
+			
+			Set<String>  claims_supported = new HashSet<String>();
+			claims_supported.add("iss");
+			claims_supported.add("sub");
+			claims_supported.add("aud");
+			claims_supported.add("iat");
+			claims_supported.add("exp");
+			claims_supported.add("jti");
+			claims_supported.add("auth_time");
+			
+			claims_supported.add("institution");
+			claims_supported.add("online_ticket");
+			
+			claims_supported.add("userId");
+			claims_supported.add("user");
+			claims_supported.add("name");
+			claims_supported.add("preferred_username");
+			claims_supported.add("given_name");
+			claims_supported.add("family_name");
+			claims_supported.add("middle_name");
+			claims_supported.add("nickname");
+			claims_supported.add("displayName");
+			claims_supported.add("departmentId");
+			claims_supported.add("department");
+			claims_supported.add("gender");
+			claims_supported.add("zoneinfo");
+			claims_supported.add("locale");
+			claims_supported.add("updated_time");
+			claims_supported.add("birthdate");
+			
+			claims_supported.add("email");
+			claims_supported.add("email_verified");
+			
+			claims_supported.add("phone_number");
+			claims_supported.add("phone_number_verified");
+			
+			claims_supported.add("address");
+			claims_supported.add("country");
+			claims_supported.add("region");
+			claims_supported.add("locality");
+			claims_supported.add("street_address");
+			claims_supported.add("formatted");
+			claims_supported.add("postal_code");
+			
+			oauthConfig.setClaims_supported(claims_supported);
+		}else {
+			oauthConfig.setClient_id(client_id);
+			oauthConfig.setJwks_uri(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/jwks");
+			
+			Set<String>  introspection_endpoint_auth_methods_supported = new HashSet<String>();
+			introspection_endpoint_auth_methods_supported.add("client_secret_basic");
+			oauthConfig.setIntrospection_endpoint_auth_methods_supported(introspection_endpoint_auth_methods_supported);                                                  
+			                                                  
+			oauthConfig.setIssuer(baseUrl + "/maxkey");
+			Set<String>  response_types_supported = new HashSet<String>();
+			response_types_supported.add("code");
+			response_types_supported.add("code id_token");
+			response_types_supported.add("id_token");
+			oauthConfig.setResponse_types_supported(response_types_supported);
+			
+			Set<String>  response_modes_supported = new HashSet<String>();
+			response_modes_supported.add("query");
+			response_modes_supported.add("form_post");
+			oauthConfig.setResponse_modes_supported(response_modes_supported);
+			
+			Set<String>  grant_types_supported = new HashSet<String>();
+			grant_types_supported.add("authorization_code");
+			grant_types_supported.add("refresh_token");
+			grant_types_supported.add("password");
+			grant_types_supported.add("client_credentials");
+			oauthConfig.setGrant_types_supported(grant_types_supported);
+			
+			Set<String>  id_token_signing_alg_values_supported = new HashSet<String>();
+			id_token_signing_alg_values_supported.add("RS256");
+			oauthConfig.setId_token_signing_alg_values_supported(id_token_signing_alg_values_supported);
+			
+			Set<String>  scopes_supported = new HashSet<String>();
+			scopes_supported.add("openid");
+			scopes_supported.add("email");
+			scopes_supported.add("profile");
+			scopes_supported.add("address");
+			scopes_supported.add("phone");
+			oauthConfig.setScopes_supported(scopes_supported);
+			
+			Set<String>  token_endpoint_auth_methods_supported = new HashSet<String>();
+			token_endpoint_auth_methods_supported.add("client_secret_basic");
+			token_endpoint_auth_methods_supported.add("client_secret_post");
+			token_endpoint_auth_methods_supported.add("none");
+			oauthConfig.setToken_endpoint_auth_methods_supported(token_endpoint_auth_methods_supported);
+			
+			Set<String>  claims_supported = new HashSet<String>();
+			claims_supported.add("iss");
+			claims_supported.add("sub");
+			claims_supported.add("aud");
+			claims_supported.add("iat");
+			claims_supported.add("exp");
+			claims_supported.add("jti");
+			claims_supported.add("auth_time");
+			
+			claims_supported.add("institution");
+			claims_supported.add("online_ticket");
+			
+			claims_supported.add("userId");
+			claims_supported.add("user");
+			claims_supported.add("name");
+			claims_supported.add("preferred_username");
+			claims_supported.add("given_name");
+			claims_supported.add("family_name");
+			claims_supported.add("middle_name");
+			claims_supported.add("nickname");
+			claims_supported.add("displayName");
+			claims_supported.add("departmentId");
+			claims_supported.add("department");
+			claims_supported.add("gender");
+			claims_supported.add("zoneinfo");
+			claims_supported.add("locale");
+			claims_supported.add("updated_time");
+			claims_supported.add("birthdate");
+			
+			claims_supported.add("email");
+			claims_supported.add("email_verified");
+			
+			claims_supported.add("phone_number");
+			claims_supported.add("phone_number_verified");
+			
+			claims_supported.add("address");
+			claims_supported.add("country");
+			claims_supported.add("region");
+			claims_supported.add("locality");
+			claims_supported.add("street_address");
+			claims_supported.add("formatted");
+			claims_supported.add("postal_code");
+			
+			oauthConfig.setClaims_supported(claims_supported);
+		}
+		return JsonPretty.getInstance().format(oauthConfig,true);
+	}
+}

+ 269 - 0
maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OpenidConfigurationEndpoint.java

@@ -0,0 +1,269 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.maxkey.authz.oauth2.provider.wellknown.endpoint;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.maxkey.authz.oauth2.common.OAuth2Constants;
+import org.maxkey.authz.oauth2.provider.endpoint.AbstractEndpoint;
+import org.maxkey.authz.oauth2.provider.wellknown.OpenidConfiguration;
+import org.maxkey.entity.apps.oauth2.provider.ClientDetails;
+import org.maxkey.pretty.impl.JsonPretty;
+import org.maxkey.web.WebContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.alibaba.cloud.commons.lang.StringUtils;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+@Tag(name = "2-1-OAuth v2.0 API文档模块")
+@Controller
+public class OpenidConfigurationEndpoint extends AbstractEndpoint {
+	final static Logger _logger = LoggerFactory.getLogger(OpenidConfigurationEndpoint.class);
+	
+
+	@Operation(summary = "OpenID Connect metadata 元数据接口", description = "参数client_id",method="GET,POST")
+	@RequestMapping(
+			value = {
+					OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/.well-known/openid-configuration"},
+			produces = "application/json",
+			method={RequestMethod.POST, RequestMethod.GET})
+	@ResponseBody
+	public String  configuration(
+			HttpServletRequest request,
+			HttpServletResponse response,
+			@RequestParam(value = "client_id", required = false) String client_id) {
+		return configurationMetadata(request,response, null,client_id);
+	}
+	
+	@Operation(summary = "OpenID Connect metadata 元数据接口", description = "参数client_id",method="GET,POST")
+	@RequestMapping(
+			value = {
+					OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/{instId}/.well-known/openid-configuration"},
+			produces = "application/json",
+			method={RequestMethod.POST, RequestMethod.GET})
+	@ResponseBody
+	public String  configurationMetadata(
+			HttpServletRequest request,
+			HttpServletResponse response, 
+			@PathVariable("instId") String instId,
+			@RequestParam(value = "client_id", required = false) String client_id) {
+		_logger.debug("instId {} , client_id {}" , instId ,client_id);
+		
+		String baseUrl = WebContext.getContextPath(true);
+		
+		ClientDetails  clientDetails = null;
+		
+		if(StringUtils.isNotBlank(client_id)) {
+			try {
+				clientDetails = getClientDetailsService().loadClientByClientId(client_id,true);
+			}catch(Exception e) {
+				_logger.error("getClientDetailsService", e);
+			}
+		}
+		
+		OpenidConfiguration openidConfig = new OpenidConfiguration();
+		openidConfig.setRequest_parameter_supported(true);
+		openidConfig.setAuthorization_endpoint(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/authorize");
+		openidConfig.setToken_endpoint(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/token");
+		openidConfig.setIntrospection_endpoint(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/introspect");
+		openidConfig.setUserinfo_endpoint(baseUrl + "/api/connect/v10/userinfo");
+		openidConfig.setEnd_session_endpoint(baseUrl + "/force/logout");
+		
+		if(clientDetails != null) {
+			openidConfig.setClient_id(client_id);
+			openidConfig.setJwks_uri(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/jwks?client_id=" + clientDetails.getClientId());
+	
+			Set<String>  introspection_endpoint_auth_methods_supported = new HashSet<String>();
+			introspection_endpoint_auth_methods_supported.add("client_secret_basic");
+			openidConfig.setIntrospection_endpoint_auth_methods_supported(introspection_endpoint_auth_methods_supported);                                                  
+			                                                  
+			openidConfig.setIssuer(clientDetails.getIssuer());
+			openidConfig.setResponse_types_supported(clientDetails.getAuthorizedGrantTypes());
+			
+			Set<String>  response_modes_supported = new HashSet<String>();
+			response_modes_supported.add("query");
+			response_modes_supported.add("form_post");
+			openidConfig.setResponse_modes_supported(response_modes_supported);
+			
+			openidConfig.setGrant_types_supported(clientDetails.getAuthorizedGrantTypes());
+			openidConfig.setClaims_supported(clientDetails.getScope());
+			
+			
+			Set<String>  id_token_signing_alg_values_supported = new HashSet<String>();
+			id_token_signing_alg_values_supported.add(clientDetails.getSignature().toUpperCase());
+			openidConfig.setId_token_signing_alg_values_supported(id_token_signing_alg_values_supported);
+			
+			openidConfig.setScopes_supported(clientDetails.getScope());
+			
+			Set<String>  token_endpoint_auth_methods_supported = new HashSet<String>();
+			token_endpoint_auth_methods_supported.add("client_secret_basic");
+			token_endpoint_auth_methods_supported.add("client_secret_post");
+			token_endpoint_auth_methods_supported.add("none");
+			openidConfig.setToken_endpoint_auth_methods_supported(token_endpoint_auth_methods_supported);
+			
+			Set<String>  claims_supported = new HashSet<String>();
+			claims_supported.add("iss");
+			claims_supported.add("sub");
+			claims_supported.add("aud");
+			claims_supported.add("iat");
+			claims_supported.add("exp");
+			claims_supported.add("jti");
+			claims_supported.add("auth_time");
+			
+			claims_supported.add("institution");
+			claims_supported.add("online_ticket");
+			
+			claims_supported.add("userId");
+			claims_supported.add("user");
+			claims_supported.add("name");
+			claims_supported.add("preferred_username");
+			claims_supported.add("given_name");
+			claims_supported.add("family_name");
+			claims_supported.add("middle_name");
+			claims_supported.add("nickname");
+			claims_supported.add("displayName");
+			claims_supported.add("departmentId");
+			claims_supported.add("department");
+			claims_supported.add("gender");
+			claims_supported.add("zoneinfo");
+			claims_supported.add("locale");
+			claims_supported.add("updated_time");
+			claims_supported.add("birthdate");
+			
+			claims_supported.add("email");
+			claims_supported.add("email_verified");
+			
+			claims_supported.add("phone_number");
+			claims_supported.add("phone_number_verified");
+			
+			claims_supported.add("address");
+			claims_supported.add("country");
+			claims_supported.add("region");
+			claims_supported.add("locality");
+			claims_supported.add("street_address");
+			claims_supported.add("formatted");
+			claims_supported.add("postal_code");
+			
+			openidConfig.setClaims_supported(claims_supported);
+		}else {
+			openidConfig.setClient_id(client_id);
+			openidConfig.setJwks_uri(baseUrl + OAuth2Constants.ENDPOINT.ENDPOINT_BASE + "/jwks");
+			
+			Set<String>  introspection_endpoint_auth_methods_supported = new HashSet<String>();
+			introspection_endpoint_auth_methods_supported.add("client_secret_basic");
+			openidConfig.setIntrospection_endpoint_auth_methods_supported(introspection_endpoint_auth_methods_supported);                                                  
+			                                                  
+			openidConfig.setIssuer(baseUrl + "/maxkey");
+			Set<String>  response_types_supported = new HashSet<String>();
+			response_types_supported.add("code");
+			response_types_supported.add("code id_token");
+			response_types_supported.add("id_token");
+			openidConfig.setResponse_types_supported(response_types_supported);
+			
+			Set<String>  response_modes_supported = new HashSet<String>();
+			response_modes_supported.add("query");
+			response_modes_supported.add("form_post");
+			openidConfig.setResponse_modes_supported(response_modes_supported);
+			
+			Set<String>  grant_types_supported = new HashSet<String>();
+			grant_types_supported.add("authorization_code");
+			grant_types_supported.add("refresh_token");
+			grant_types_supported.add("password");
+			grant_types_supported.add("client_credentials");
+			openidConfig.setGrant_types_supported(grant_types_supported);
+			
+			Set<String>  id_token_signing_alg_values_supported = new HashSet<String>();
+			id_token_signing_alg_values_supported.add("RS256");
+			openidConfig.setId_token_signing_alg_values_supported(id_token_signing_alg_values_supported);
+			
+			Set<String>  scopes_supported = new HashSet<String>();
+			scopes_supported.add("openid");
+			scopes_supported.add("email");
+			scopes_supported.add("profile");
+			scopes_supported.add("address");
+			scopes_supported.add("phone");
+			openidConfig.setScopes_supported(scopes_supported);
+			
+			Set<String>  token_endpoint_auth_methods_supported = new HashSet<String>();
+			token_endpoint_auth_methods_supported.add("client_secret_basic");
+			token_endpoint_auth_methods_supported.add("client_secret_post");
+			token_endpoint_auth_methods_supported.add("none");
+			openidConfig.setToken_endpoint_auth_methods_supported(token_endpoint_auth_methods_supported);
+			
+			Set<String>  claims_supported = new HashSet<String>();
+			claims_supported.add("iss");
+			claims_supported.add("sub");
+			claims_supported.add("aud");
+			claims_supported.add("iat");
+			claims_supported.add("exp");
+			claims_supported.add("jti");
+			claims_supported.add("auth_time");
+			
+			claims_supported.add("institution");
+			claims_supported.add("online_ticket");
+			
+			claims_supported.add("userId");
+			claims_supported.add("user");
+			claims_supported.add("name");
+			claims_supported.add("preferred_username");
+			claims_supported.add("given_name");
+			claims_supported.add("family_name");
+			claims_supported.add("middle_name");
+			claims_supported.add("nickname");
+			claims_supported.add("displayName");
+			claims_supported.add("departmentId");
+			claims_supported.add("department");
+			claims_supported.add("gender");
+			claims_supported.add("zoneinfo");
+			claims_supported.add("locale");
+			claims_supported.add("updated_time");
+			claims_supported.add("birthdate");
+			
+			claims_supported.add("email");
+			claims_supported.add("email_verified");
+			
+			claims_supported.add("phone_number");
+			claims_supported.add("phone_number_verified");
+			
+			claims_supported.add("address");
+			claims_supported.add("country");
+			claims_supported.add("region");
+			claims_supported.add("locality");
+			claims_supported.add("street_address");
+			claims_supported.add("formatted");
+			claims_supported.add("postal_code");
+			
+			openidConfig.setClaims_supported(claims_supported);
+		}
+		
+		return JsonPretty.getInstance().format(openidConfig,true);
+	}
+}

+ 2 - 1
maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oidc/idtoken/OIDCIdTokenEnhancer.java

@@ -129,7 +129,8 @@ public class OIDCIdTokenEnhancer implements TokenEnhancer {
 				builder.claim("auth_time",  loginDate.getMillis()/1000);
 			}
 			
-			String nonce = (String)request.getExtensions().get("nonce");
+			String nonce = request.getRequestParameters().get("nonce");
+			_logger.debug("getRequestParameters nonce {}",nonce);
 			if (!Strings.isNullOrEmpty(nonce)) {
 				builder.claim("nonce", nonce);
 			}

+ 2 - 1
maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java

@@ -66,7 +66,8 @@ import com.nimbusds.jose.JWEAlgorithm;
 @ComponentScan(basePackages = {
         "org.maxkey.authz.oauth2.provider.endpoint",
         "org.maxkey.authz.oauth2.provider.userinfo.endpoint",
-        "org.maxkey.authz.oauth2.provider.approval.controller"
+        "org.maxkey.authz.oauth2.provider.approval.controller",
+        "org.maxkey.authz.oauth2.provider.wellknown.endpoint"
 })
 public class Oauth20AutoConfiguration implements InitializingBean {
     private static final  Logger _logger = LoggerFactory.getLogger(Oauth20AutoConfiguration.class);

+ 17 - 0
maxkey-synchronizers/maxkey-synchronizer-jdbc/src/main/java/org/maxkey/synchronizer/jdbc/ColumnFieldMapper.java

@@ -1,3 +1,20 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.maxkey.synchronizer.jdbc;
 
 import org.maxkey.entity.DbTableColumn;