From ba9790c77143559ee70cd7bd8e1f55dc5fa11269 Mon Sep 17 00:00:00 2001 From: MaxKey Date: Wed, 15 Feb 2023 10:04:47 +0800 Subject: [PATCH] OpenID Connect metadata --- .../AuthWeChatEnterpriseWebRequestCost.java | 17 ++ .../java/org/maxkey/entity/DbTableColumn.java | 17 ++ .../org/maxkey/entity/DbTableMetaData.java | 17 ++ .../org/maxkey/constants/ContentType.java | 6 +- .../endpoint/AuthorizationEndpoint.java | 42 --- .../provider/endpoint/IntrospectEndpoint.java | 174 +++++++++++ .../provider/endpoint/OauthJwksEndpoint.java | 97 +++++++ .../wellknown/OauthServerConfiguration.java | 261 +++++++++++++++++ .../wellknown/OpenidConfiguration.java | 251 ++++++++++++++++ .../OauthAuthorizationServerEndpoint.java | 270 ++++++++++++++++++ .../endpoint/OpenidConfigurationEndpoint.java | 269 +++++++++++++++++ .../oidc/idtoken/OIDCIdTokenEnhancer.java | 3 +- .../Oauth20AutoConfiguration.java | 3 +- .../synchronizer/jdbc/ColumnFieldMapper.java | 17 ++ 14 files changed, 1399 insertions(+), 45 deletions(-) create mode 100644 maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/IntrospectEndpoint.java create mode 100644 maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/OauthJwksEndpoint.java create mode 100644 maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OauthServerConfiguration.java create mode 100644 maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OpenidConfiguration.java create mode 100644 maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OauthAuthorizationServerEndpoint.java create mode 100644 maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OpenidConfigurationEndpoint.java diff --git a/maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequestCost.java b/maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequestCost.java index daa44b09..1599f1f6 100644 --- a/maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequestCost.java +++ b/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) diff --git a/maxkey-common/src/main/java/org/maxkey/entity/DbTableColumn.java b/maxkey-common/src/main/java/org/maxkey/entity/DbTableColumn.java index 3054c7d4..9c9cce04 100644 --- a/maxkey-common/src/main/java/org/maxkey/entity/DbTableColumn.java +++ b/maxkey-common/src/main/java/org/maxkey/entity/DbTableColumn.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. + */ + + /* * Copyright (c) 2022, MaxKey and/or its affiliates. All rights reserved. * diff --git a/maxkey-common/src/main/java/org/maxkey/entity/DbTableMetaData.java b/maxkey-common/src/main/java/org/maxkey/entity/DbTableMetaData.java index 575ab69c..ee4f27ff 100644 --- a/maxkey-common/src/main/java/org/maxkey/entity/DbTableMetaData.java +++ b/maxkey-common/src/main/java/org/maxkey/entity/DbTableMetaData.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. + */ + + /* * Copyright (c) 2022, MaxKey and/or its affiliates. All rights reserved. * diff --git a/maxkey-core/src/main/java/org/maxkey/constants/ContentType.java b/maxkey-core/src/main/java/org/maxkey/constants/ContentType.java index ba76b2c5..99a263c8 100644 --- a/maxkey-core/src/main/java/org/maxkey/constants/ContentType.java +++ b/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"; + } diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/AuthorizationEndpoint.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/AuthorizationEndpoint.java index 23a8474b..e11e4183 100644 --- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/AuthorizationEndpoint.java +++ b/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 model, diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/IntrospectEndpoint.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/IntrospectEndpoint.java new file mode 100644 index 00000000..d6133cc1 --- /dev/null +++ b/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; + } + + } + +} diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/OauthJwksEndpoint.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/endpoint/OauthJwksEndpoint.java new file mode 100644 index 00000000..40567d69 --- /dev/null +++ b/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(); + } + +} diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OauthServerConfiguration.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OauthServerConfiguration.java new file mode 100644 index 00000000..3be0e8f7 --- /dev/null +++ b/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 code_challenge_methods_supported; + + Set response_types_supported; + Set response_modes_supported; + Set grant_types_supported; + Set subject_types_supported; + Set id_token_signing_alg_values_supported; + + Set scopes_supported; + Set token_endpoint_auth_methods_supported; + Set claims_supported; + String introspection_endpoint; + Set introspection_endpoint_auth_methods_supported; + String revocation_endpoint; + Set revocation_endpoint_auth_methods_supported; + String end_session_endpoint; + boolean request_parameter_supported; + Set request_object_signing_alg_values_supported; + Set backchannel_token_delivery_modes_supported; + Set 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 getCode_challenge_methods_supported() { + return code_challenge_methods_supported; + } + + public void setCode_challenge_methods_supported(Set 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 getResponse_types_supported() { + return response_types_supported; + } + + public void setResponse_types_supported(Set response_types_supported) { + this.response_types_supported = response_types_supported; + } + + public Set getResponse_modes_supported() { + return response_modes_supported; + } + + public void setResponse_modes_supported(Set response_modes_supported) { + this.response_modes_supported = response_modes_supported; + } + + public Set getGrant_types_supported() { + return grant_types_supported; + } + + public void setGrant_types_supported(Set grant_types_supported) { + this.grant_types_supported = grant_types_supported; + } + + public Set getSubject_types_supported() { + return subject_types_supported; + } + + public void setSubject_types_supported(Set subject_types_supported) { + this.subject_types_supported = subject_types_supported; + } + + public Set getId_token_signing_alg_values_supported() { + return id_token_signing_alg_values_supported; + } + + public void setId_token_signing_alg_values_supported(Set id_token_signing_alg_values_supported) { + this.id_token_signing_alg_values_supported = id_token_signing_alg_values_supported; + } + + public Set getScopes_supported() { + return scopes_supported; + } + + public void setScopes_supported(Set scopes_supported) { + this.scopes_supported = scopes_supported; + } + + public Set getToken_endpoint_auth_methods_supported() { + return token_endpoint_auth_methods_supported; + } + + public void setToken_endpoint_auth_methods_supported(Set token_endpoint_auth_methods_supported) { + this.token_endpoint_auth_methods_supported = token_endpoint_auth_methods_supported; + } + + public Set getClaims_supported() { + return claims_supported; + } + + public void setClaims_supported(Set 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 getIntrospection_endpoint_auth_methods_supported() { + return introspection_endpoint_auth_methods_supported; + } + + public void setIntrospection_endpoint_auth_methods_supported( + Set 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 getRevocation_endpoint_auth_methods_supported() { + return revocation_endpoint_auth_methods_supported; + } + + public void setRevocation_endpoint_auth_methods_supported( + Set 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 getRequest_object_signing_alg_values_supported() { + return request_object_signing_alg_values_supported; + } + + public void setRequest_object_signing_alg_values_supported( + Set request_object_signing_alg_values_supported) { + this.request_object_signing_alg_values_supported = request_object_signing_alg_values_supported; + } + + public Set getBackchannel_token_delivery_modes_supported() { + return backchannel_token_delivery_modes_supported; + } + + public void setBackchannel_token_delivery_modes_supported( + Set backchannel_token_delivery_modes_supported) { + this.backchannel_token_delivery_modes_supported = backchannel_token_delivery_modes_supported; + } + + public Set 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 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(); + } + +} diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OpenidConfiguration.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/OpenidConfiguration.java new file mode 100644 index 00000000..f9a9d84c --- /dev/null +++ b/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 response_types_supported; + Set response_modes_supported; + Set grant_types_supported; + Set subject_types_supported; + Set id_token_signing_alg_values_supported; + + Set scopes_supported; + Set token_endpoint_auth_methods_supported; + Set claims_supported; + String introspection_endpoint; + Set introspection_endpoint_auth_methods_supported; + String revocation_endpoint; + Set revocation_endpoint_auth_methods_supported; + String end_session_endpoint; + boolean request_parameter_supported; + Set request_object_signing_alg_values_supported; + Set backchannel_token_delivery_modes_supported; + Set 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 getResponse_types_supported() { + return response_types_supported; + } + + public void setResponse_types_supported(Set response_types_supported) { + this.response_types_supported = response_types_supported; + } + + public Set getResponse_modes_supported() { + return response_modes_supported; + } + + public void setResponse_modes_supported(Set response_modes_supported) { + this.response_modes_supported = response_modes_supported; + } + + public Set getGrant_types_supported() { + return grant_types_supported; + } + + public void setGrant_types_supported(Set grant_types_supported) { + this.grant_types_supported = grant_types_supported; + } + + public Set getSubject_types_supported() { + return subject_types_supported; + } + + public void setSubject_types_supported(Set subject_types_supported) { + this.subject_types_supported = subject_types_supported; + } + + public Set getId_token_signing_alg_values_supported() { + return id_token_signing_alg_values_supported; + } + + public void setId_token_signing_alg_values_supported(Set id_token_signing_alg_values_supported) { + this.id_token_signing_alg_values_supported = id_token_signing_alg_values_supported; + } + + public Set getScopes_supported() { + return scopes_supported; + } + + public void setScopes_supported(Set scopes_supported) { + this.scopes_supported = scopes_supported; + } + + public Set getToken_endpoint_auth_methods_supported() { + return token_endpoint_auth_methods_supported; + } + + public void setToken_endpoint_auth_methods_supported(Set token_endpoint_auth_methods_supported) { + this.token_endpoint_auth_methods_supported = token_endpoint_auth_methods_supported; + } + + public Set getClaims_supported() { + return claims_supported; + } + + public void setClaims_supported(Set 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 getIntrospection_endpoint_auth_methods_supported() { + return introspection_endpoint_auth_methods_supported; + } + + public void setIntrospection_endpoint_auth_methods_supported( + Set 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 getRevocation_endpoint_auth_methods_supported() { + return revocation_endpoint_auth_methods_supported; + } + + public void setRevocation_endpoint_auth_methods_supported( + Set 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 getRequest_object_signing_alg_values_supported() { + return request_object_signing_alg_values_supported; + } + + public void setRequest_object_signing_alg_values_supported( + Set request_object_signing_alg_values_supported) { + this.request_object_signing_alg_values_supported = request_object_signing_alg_values_supported; + } + + public Set getBackchannel_token_delivery_modes_supported() { + return backchannel_token_delivery_modes_supported; + } + + public void setBackchannel_token_delivery_modes_supported( + Set backchannel_token_delivery_modes_supported) { + this.backchannel_token_delivery_modes_supported = backchannel_token_delivery_modes_supported; + } + + public Set 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 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(); + } + +} diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OauthAuthorizationServerEndpoint.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OauthAuthorizationServerEndpoint.java new file mode 100644 index 00000000..ce9d78f6 --- /dev/null +++ b/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 code_challenge_methods_supported = new HashSet(); + 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 introspection_endpoint_auth_methods_supported = new HashSet(); + 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 response_modes_supported = new HashSet(); + 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 id_token_signing_alg_values_supported = new HashSet(); + 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 token_endpoint_auth_methods_supported = new HashSet(); + 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 claims_supported = new HashSet(); + 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 introspection_endpoint_auth_methods_supported = new HashSet(); + 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 response_types_supported = new HashSet(); + 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 response_modes_supported = new HashSet(); + response_modes_supported.add("query"); + response_modes_supported.add("form_post"); + oauthConfig.setResponse_modes_supported(response_modes_supported); + + Set grant_types_supported = new HashSet(); + 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 id_token_signing_alg_values_supported = new HashSet(); + id_token_signing_alg_values_supported.add("RS256"); + oauthConfig.setId_token_signing_alg_values_supported(id_token_signing_alg_values_supported); + + Set scopes_supported = new HashSet(); + 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 token_endpoint_auth_methods_supported = new HashSet(); + 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 claims_supported = new HashSet(); + 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); + } +} diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OpenidConfigurationEndpoint.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/wellknown/endpoint/OpenidConfigurationEndpoint.java new file mode 100644 index 00000000..fccad341 --- /dev/null +++ b/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 introspection_endpoint_auth_methods_supported = new HashSet(); + 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 response_modes_supported = new HashSet(); + 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 id_token_signing_alg_values_supported = new HashSet(); + 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 token_endpoint_auth_methods_supported = new HashSet(); + 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 claims_supported = new HashSet(); + 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 introspection_endpoint_auth_methods_supported = new HashSet(); + 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 response_types_supported = new HashSet(); + 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 response_modes_supported = new HashSet(); + response_modes_supported.add("query"); + response_modes_supported.add("form_post"); + openidConfig.setResponse_modes_supported(response_modes_supported); + + Set grant_types_supported = new HashSet(); + 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 id_token_signing_alg_values_supported = new HashSet(); + id_token_signing_alg_values_supported.add("RS256"); + openidConfig.setId_token_signing_alg_values_supported(id_token_signing_alg_values_supported); + + Set scopes_supported = new HashSet(); + 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 token_endpoint_auth_methods_supported = new HashSet(); + 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 claims_supported = new HashSet(); + 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); + } +} diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oidc/idtoken/OIDCIdTokenEnhancer.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oidc/idtoken/OIDCIdTokenEnhancer.java index 2948e971..6449a033 100644 --- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oidc/idtoken/OIDCIdTokenEnhancer.java +++ b/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); } diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java index c8c1876c..b29a0fe4 100644 --- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java +++ b/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); diff --git a/maxkey-synchronizers/maxkey-synchronizer-jdbc/src/main/java/org/maxkey/synchronizer/jdbc/ColumnFieldMapper.java b/maxkey-synchronizers/maxkey-synchronizer-jdbc/src/main/java/org/maxkey/synchronizer/jdbc/ColumnFieldMapper.java index 8ff13c76..e0baf268 100644 --- a/maxkey-synchronizers/maxkey-synchronizer-jdbc/src/main/java/org/maxkey/synchronizer/jdbc/ColumnFieldMapper.java +++ b/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;