(window.webpackJsonp=window.webpackJsonp||[]).push([[287],{713:function(t,e,n){"use strict";n.r(e);var o=n(56),i=Object(o.a)({},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"advanced-configuration"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#advanced-configuration"}},[t._v("#")]),t._v(" Advanced Configuration")]),t._v(" "),n("p",[n("code",[t._v("HttpSecurity.oauth2Login()")]),t._v(" provides a number of configuration options for customizing OAuth 2.0 Login.\nThe main configuration options are grouped into their protocol endpoint counterparts.")]),t._v(" "),n("p",[t._v("For example, "),n("code",[t._v("oauth2Login().authorizationEndpoint()")]),t._v(" allows configuring the "),n("em",[t._v("Authorization Endpoint")]),t._v(", whereas "),n("code",[t._v("oauth2Login().tokenEndpoint()")]),t._v(" allows configuring the "),n("em",[t._v("Token Endpoint")]),t._v(".")]),t._v(" "),n("p",[t._v("The following code shows an example:")]),t._v(" "),n("p",[t._v("Example 1. Advanced OAuth2 Login Configuration")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login(oauth2 -> oauth2\n\t\t\t .authorizationEndpoint(authorization -> authorization\n\t\t\t ...\n\t\t\t )\n\t\t\t .redirectionEndpoint(redirection -> redirection\n\t\t\t ...\n\t\t\t )\n\t\t\t .tokenEndpoint(token -> token\n\t\t\t ...\n\t\t\t )\n\t\t\t .userInfoEndpoint(userInfo -> userInfo\n\t\t\t ...\n\t\t\t )\n\t\t\t);\n\t}\n}\n")])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n\n override fun configure(http: HttpSecurity) {\n http {\n oauth2Login {\n authorizationEndpoint {\n ...\n }\n redirectionEndpoint {\n ...\n }\n tokenEndpoint {\n ...\n }\n userInfoEndpoint {\n ...\n }\n }\n }\n }\n}\n")])])]),n("p",[t._v("The main goal of the "),n("code",[t._v("oauth2Login()")]),t._v(" DSL was to closely align with the naming, as defined in the specifications.")]),t._v(" "),n("p",[t._v("The OAuth 2.0 Authorization Framework defines the "),n("a",{attrs:{href:"https://tools.ietf.org/html/rfc6749#section-3",target:"_blank",rel:"noopener noreferrer"}},[t._v("Protocol Endpoints"),n("OutboundLink")],1),t._v(" as follows:")]),t._v(" "),n("p",[t._v("The authorization process utilizes two authorization server endpoints (HTTP resources):")]),t._v(" "),n("ul",[n("li",[n("p",[t._v("Authorization Endpoint: Used by the client to obtain authorization from the resource owner via user-agent redirection.")])]),t._v(" "),n("li",[n("p",[t._v("Token Endpoint: Used by the client to exchange an authorization grant for an access token, typically with client authentication.")])])]),t._v(" "),n("p",[t._v("As well as one client endpoint:")]),t._v(" "),n("ul",[n("li",[t._v("Redirection Endpoint: Used by the authorization server to return responses containing authorization credentials to the client via the resource owner user-agent.")])]),t._v(" "),n("p",[t._v("The OpenID Connect Core 1.0 specification defines the "),n("a",{attrs:{href:"https://openid.net/specs/openid-connect-core-1_0.html#UserInfo",target:"_blank",rel:"noopener noreferrer"}},[t._v("UserInfo Endpoint"),n("OutboundLink")],1),t._v(" as follows:")]),t._v(" "),n("p",[t._v("The UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns claims about the authenticated end-user.\nTo obtain the requested claims about the end-user, the client makes a request to the UserInfo Endpoint by using an access token obtained through OpenID Connect Authentication.\nThese claims are normally represented by a JSON object that contains a collection of name-value pairs for the claims.")]),t._v(" "),n("p",[t._v("The following code shows the complete configuration options available for the "),n("code",[t._v("oauth2Login()")]),t._v(" DSL:")]),t._v(" "),n("p",[t._v("Example 2. OAuth2 Login Configuration Options")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login(oauth2 -> oauth2\n\t\t\t .clientRegistrationRepository(this.clientRegistrationRepository())\n\t\t\t .authorizedClientRepository(this.authorizedClientRepository())\n\t\t\t .authorizedClientService(this.authorizedClientService())\n\t\t\t .loginPage("/login")\n\t\t\t .authorizationEndpoint(authorization -> authorization\n\t\t\t .baseUri(this.authorizationRequestBaseUri())\n\t\t\t .authorizationRequestRepository(this.authorizationRequestRepository())\n\t\t\t .authorizationRequestResolver(this.authorizationRequestResolver())\n\t\t\t )\n\t\t\t .redirectionEndpoint(redirection -> redirection\n\t\t\t .baseUri(this.authorizationResponseBaseUri())\n\t\t\t )\n\t\t\t .tokenEndpoint(token -> token\n\t\t\t .accessTokenResponseClient(this.accessTokenResponseClient())\n\t\t\t )\n\t\t\t .userInfoEndpoint(userInfo -> userInfo\n\t\t\t .userAuthoritiesMapper(this.userAuthoritiesMapper())\n\t\t\t .userService(this.oauth2UserService())\n\t\t\t .oidcUserService(this.oidcUserService())\n\t\t\t )\n\t\t\t);\n\t}\n}\n')])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n\n override fun configure(http: HttpSecurity) {\n http {\n oauth2Login {\n clientRegistrationRepository = clientRegistrationRepository()\n authorizedClientRepository = authorizedClientRepository()\n authorizedClientService = authorizedClientService()\n loginPage = "/login"\n authorizationEndpoint {\n baseUri = authorizationRequestBaseUri()\n authorizationRequestRepository = authorizationRequestRepository()\n authorizationRequestResolver = authorizationRequestResolver()\n }\n redirectionEndpoint {\n baseUri = authorizationResponseBaseUri()\n }\n tokenEndpoint {\n accessTokenResponseClient = accessTokenResponseClient()\n }\n userInfoEndpoint {\n userAuthoritiesMapper = userAuthoritiesMapper()\n userService = oauth2UserService()\n oidcUserService = oidcUserService()\n }\n }\n }\n }\n}\n')])])]),n("p",[t._v("In addition to the "),n("code",[t._v("oauth2Login()")]),t._v(" DSL, XML configuration is also supported.")]),t._v(" "),n("p",[t._v("The following code shows the complete configuration options available in the "),n("RouterLink",{attrs:{to:"/appendix/namespace/http.html#nsa-oauth2-login"}},[t._v(" security namespace")]),t._v(":")],1),t._v(" "),n("p",[t._v("Example 3. OAuth2 Login XML Configuration Options")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('\n\t\n\n')])])]),n("p",[t._v("The following sections go into more detail on each of the configuration options available:")]),t._v(" "),n("ul",[n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-login-page"}},[t._v("OAuth 2.0 Login Page")])])]),t._v(" "),n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-redirection-endpoint"}},[t._v("Redirection Endpoint")])])]),t._v(" "),n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-userinfo-endpoint"}},[t._v("UserInfo Endpoint")])])]),t._v(" "),n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-idtoken-verify"}},[t._v("ID Token Signature Verification")])])]),t._v(" "),n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-oidc-logout"}},[t._v("OpenID Connect 1.0 Logout")])])])]),t._v(" "),n("h2",{attrs:{id:"oauth-2-0-login-page"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#oauth-2-0-login-page"}},[t._v("#")]),t._v(" OAuth 2.0 Login Page")]),t._v(" "),n("p",[t._v("By default, the OAuth 2.0 Login Page is auto-generated by the "),n("code",[t._v("DefaultLoginPageGeneratingFilter")]),t._v(".\nThe default login page shows each configured OAuth Client with its "),n("code",[t._v("ClientRegistration.clientName")]),t._v(" as a link, which is capable of initiating the Authorization Request (or OAuth 2.0 Login).")]),t._v(" "),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[t._v("In order for "),n("code",[t._v("DefaultLoginPageGeneratingFilter")]),t._v(" to show links for configured OAuth Clients, the registered "),n("code",[t._v("ClientRegistrationRepository")]),t._v(" needs to also implement "),n("code",[t._v("Iterable")]),t._v("."),n("br"),t._v("See "),n("code",[t._v("InMemoryClientRegistrationRepository")]),t._v(" for reference.")])])]),t._v(" "),n("tbody")]),t._v(" "),n("p",[t._v("The link’s destination for each OAuth Client defaults to the following:")]),t._v(" "),n("p",[n("code",[t._v('OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{registrationId}"')])]),t._v(" "),n("p",[t._v("The following line shows an example:")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('Google\n')])])]),n("p",[t._v("To override the default login page, configure "),n("code",[t._v("oauth2Login().loginPage()")]),t._v(" and (optionally) "),n("code",[t._v("oauth2Login().authorizationEndpoint().baseUri()")]),t._v(".")]),t._v(" "),n("p",[t._v("The following listing shows an example:")]),t._v(" "),n("p",[t._v("Example 4. OAuth2 Login Page Configuration")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login(oauth2 -> oauth2\n\t\t\t .loginPage("/login/oauth2")\n\t\t\t ...\n\t\t\t .authorizationEndpoint(authorization -> authorization\n\t\t\t .baseUri("/login/oauth2/authorization")\n\t\t\t ...\n\t\t\t )\n\t\t\t);\n\t}\n}\n')])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n\n override fun configure(http: HttpSecurity) {\n http {\n oauth2Login {\n loginPage = "/login/oauth2"\n authorizationEndpoint {\n baseUri = "/login/oauth2/authorization"\n }\n }\n }\n }\n}\n')])])]),n("p",[t._v("Xml")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('\n\t\n\n')])])]),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[t._v("You need to provide a "),n("code",[t._v("@Controller")]),t._v(" with a "),n("code",[t._v('@RequestMapping("/login/oauth2")')]),t._v(" that is capable of rendering the custom login page.")])])]),t._v(" "),n("tbody")]),t._v(" "),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[t._v("As noted earlier, configuring "),n("code",[t._v("oauth2Login().authorizationEndpoint().baseUri()")]),t._v(" is optional."),n("br"),t._v("However, if you choose to customize it, ensure the link to each OAuth Client matches the "),n("code",[t._v("authorizationEndpoint().baseUri()")]),t._v("."),n("br"),n("br"),t._v("The following line shows an example:"),n("br"),n("br"),n("code",[t._v('
Google
')])])])]),t._v(" "),n("tbody")]),t._v(" "),n("h2",{attrs:{id:"redirection-endpoint"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#redirection-endpoint"}},[t._v("#")]),t._v(" Redirection Endpoint")]),t._v(" "),n("p",[t._v("The Redirection Endpoint is used by the Authorization Server for returning the Authorization Response (which contains the authorization credentials) to the client via the Resource Owner user-agent.")]),t._v(" "),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[t._v("OAuth 2.0 Login leverages the Authorization Code Grant."),n("br"),t._v("Therefore, the authorization credential is the authorization code.")])])]),t._v(" "),n("tbody")]),t._v(" "),n("p",[t._v("The default Authorization Response "),n("code",[t._v("baseUri")]),t._v(" (redirection endpoint) is "),n("code",[t._v("**/login/oauth2/code/***")]),t._v(", which is defined in "),n("code",[t._v("OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI")]),t._v(".")]),t._v(" "),n("p",[t._v("If you would like to customize the Authorization Response "),n("code",[t._v("baseUri")]),t._v(", configure it as shown in the following example:")]),t._v(" "),n("p",[t._v("Example 5. Redirection Endpoint Configuration")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login(oauth2 -> oauth2\n\t\t\t .redirectionEndpoint(redirection -> redirection\n\t\t\t .baseUri("/login/oauth2/callback/*")\n\t\t\t ...\n\t\t\t )\n\t\t\t);\n\t}\n}\n')])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n\n override fun configure(http: HttpSecurity) {\n http {\n oauth2Login {\n redirectionEndpoint {\n baseUri = "/login/oauth2/callback/*"\n }\n }\n }\n }\n}\n')])])]),n("p",[t._v("Xml")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('\n\t\n\n')])])]),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[t._v("You also need to ensure the "),n("code",[t._v("ClientRegistration.redirectUri")]),t._v(" matches the custom Authorization Response "),n("code",[t._v("baseUri")]),t._v("."),n("br"),n("br"),t._v("The following listing shows an example:"),n("br"),n("br"),t._v("Java"),n("br"),n("br"),n("code",[t._v('
return CommonOAuth2Provider.GOOGLE.getBuilder("google")
\t.clientId("google-client-id")
\t.clientSecret("google-client-secret")
\t.redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}")
\t.build();
')]),n("br"),n("br"),t._v("Kotlin"),n("br"),n("br"),n("code",[t._v('
return CommonOAuth2Provider.GOOGLE.getBuilder("google")
.clientId("google-client-id")
.clientSecret("google-client-secret")
.redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}")
.build()
')])])])]),t._v(" "),n("tbody")]),t._v(" "),n("h2",{attrs:{id:"userinfo-endpoint"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#userinfo-endpoint"}},[t._v("#")]),t._v(" UserInfo Endpoint")]),t._v(" "),n("p",[t._v("The UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections:")]),t._v(" "),n("ul",[n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-map-authorities"}},[t._v("Mapping User Authorities")])])]),t._v(" "),n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-oauth2-user-service"}},[t._v("OAuth 2.0 UserService")])])]),t._v(" "),n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-oidc-user-service"}},[t._v("OpenID Connect 1.0 UserService")])])])]),t._v(" "),n("h3",{attrs:{id:"mapping-user-authorities"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#mapping-user-authorities"}},[t._v("#")]),t._v(" Mapping User Authorities")]),t._v(" "),n("p",[t._v("After the user successfully authenticates with the OAuth 2.0 Provider, the "),n("code",[t._v("OAuth2User.getAuthorities()")]),t._v(" (or "),n("code",[t._v("OidcUser.getAuthorities()")]),t._v(") may be mapped to a new set of "),n("code",[t._v("GrantedAuthority")]),t._v(" instances, which will be supplied to "),n("code",[t._v("OAuth2AuthenticationToken")]),t._v(" when completing the authentication.")]),t._v(" "),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[n("code",[t._v("OAuth2AuthenticationToken.getAuthorities()")]),t._v(" is used for authorizing requests, such as in "),n("code",[t._v("hasRole('USER')")]),t._v(" or "),n("code",[t._v("hasRole('ADMIN')")]),t._v(".")])])]),t._v(" "),n("tbody")]),t._v(" "),n("p",[t._v("There are a couple of options to choose from when mapping user authorities:")]),t._v(" "),n("ul",[n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-map-authorities-grantedauthoritiesmapper"}},[t._v("Using a GrantedAuthoritiesMapper")])])]),t._v(" "),n("li",[n("p",[n("a",{attrs:{href:"#oauth2login-advanced-map-authorities-oauth2userservice"}},[t._v("Delegation-based strategy with OAuth2UserService")])])])]),t._v(" "),n("h4",{attrs:{id:"using-a-grantedauthoritiesmapper"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#using-a-grantedauthoritiesmapper"}},[t._v("#")]),t._v(" Using a GrantedAuthoritiesMapper")]),t._v(" "),n("p",[t._v("Provide an implementation of "),n("code",[t._v("GrantedAuthoritiesMapper")]),t._v(" and configure it as shown in the following example:")]),t._v(" "),n("p",[t._v("Example 6. Granted Authorities Mapper Configuration")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login(oauth2 -> oauth2\n\t\t\t .userInfoEndpoint(userInfo -> userInfo\n\t\t\t .userAuthoritiesMapper(this.userAuthoritiesMapper())\n\t\t\t ...\n\t\t\t )\n\t\t\t);\n\t}\n\n\tprivate GrantedAuthoritiesMapper userAuthoritiesMapper() {\n\t\treturn (authorities) -> {\n\t\t\tSet mappedAuthorities = new HashSet<>();\n\n\t\t\tauthorities.forEach(authority -> {\n\t\t\t\tif (OidcUserAuthority.class.isInstance(authority)) {\n\t\t\t\t\tOidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;\n\n\t\t\t\t\tOidcIdToken idToken = oidcUserAuthority.getIdToken();\n\t\t\t\t\tOidcUserInfo userInfo = oidcUserAuthority.getUserInfo();\n\n\t\t\t\t\t// Map the claims found in idToken and/or userInfo\n\t\t\t\t\t// to one or more GrantedAuthority's and add it to mappedAuthorities\n\n\t\t\t\t} else if (OAuth2UserAuthority.class.isInstance(authority)) {\n\t\t\t\t\tOAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;\n\n\t\t\t\t\tMap userAttributes = oauth2UserAuthority.getAttributes();\n\n\t\t\t\t\t// Map the attributes found in userAttributes\n\t\t\t\t\t// to one or more GrantedAuthority's and add it to mappedAuthorities\n\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn mappedAuthorities;\n\t\t};\n\t}\n}\n")])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n\n override fun configure(http: HttpSecurity) {\n http {\n oauth2Login {\n userInfoEndpoint {\n userAuthoritiesMapper = userAuthoritiesMapper()\n }\n }\n }\n }\n\n private fun userAuthoritiesMapper(): GrantedAuthoritiesMapper = GrantedAuthoritiesMapper { authorities: Collection ->\n val mappedAuthorities = emptySet()\n\n authorities.forEach { authority ->\n if (authority is OidcUserAuthority) {\n val idToken = authority.idToken\n val userInfo = authority.userInfo\n // Map the claims found in idToken and/or userInfo\n // to one or more GrantedAuthority's and add it to mappedAuthorities\n } else if (authority is OAuth2UserAuthority) {\n val userAttributes = authority.attributes\n // Map the attributes found in userAttributes\n // to one or more GrantedAuthority's and add it to mappedAuthorities\n }\n }\n\n mappedAuthorities\n }\n}\n")])])]),n("p",[t._v("Xml")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('\n\t\n\n')])])]),n("p",[t._v("Alternatively, you may register a "),n("code",[t._v("GrantedAuthoritiesMapper")]),t._v(" "),n("code",[t._v("@Bean")]),t._v(" to have it automatically applied to the configuration, as shown in the following example:")]),t._v(" "),n("p",[t._v("Example 7. Granted Authorities Mapper Bean Configuration")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t .oauth2Login(withDefaults());\n\t}\n\n\t@Bean\n\tpublic GrantedAuthoritiesMapper userAuthoritiesMapper() {\n\t\t...\n\t}\n}\n")])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n\n override fun configure(http: HttpSecurity) {\n http {\n oauth2Login { }\n }\n }\n\n @Bean\n fun userAuthoritiesMapper(): GrantedAuthoritiesMapper {\n ...\n }\n}\n")])])]),n("h4",{attrs:{id:"delegation-based-strategy-with-oauth2userservice"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#delegation-based-strategy-with-oauth2userservice"}},[t._v("#")]),t._v(" Delegation-based strategy with OAuth2UserService")]),t._v(" "),n("p",[t._v("This strategy is advanced compared to using a "),n("code",[t._v("GrantedAuthoritiesMapper")]),t._v(", however, it’s also more flexible as it gives you access to the "),n("code",[t._v("OAuth2UserRequest")]),t._v(" and "),n("code",[t._v("OAuth2User")]),t._v(" (when using an OAuth 2.0 UserService) or "),n("code",[t._v("OidcUserRequest")]),t._v(" and "),n("code",[t._v("OidcUser")]),t._v(" (when using an OpenID Connect 1.0 UserService).")]),t._v(" "),n("p",[t._v("The "),n("code",[t._v("OAuth2UserRequest")]),t._v(" (and "),n("code",[t._v("OidcUserRequest")]),t._v(") provides you access to the associated "),n("code",[t._v("OAuth2AccessToken")]),t._v(", which is very useful in the cases where the "),n("em",[t._v("delegator")]),t._v(" needs to fetch authority information from a protected resource before it can map the custom authorities for the user.")]),t._v(" "),n("p",[t._v("The following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService:")]),t._v(" "),n("p",[t._v("Example 8. OAuth2UserService Configuration")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login(oauth2 -> oauth2\n\t\t\t .userInfoEndpoint(userInfo -> userInfo\n\t\t\t .oidcUserService(this.oidcUserService())\n\t\t\t ...\n\t\t\t )\n\t\t\t);\n\t}\n\n\tprivate OAuth2UserService oidcUserService() {\n\t\tfinal OidcUserService delegate = new OidcUserService();\n\n\t\treturn (userRequest) -> {\n\t\t\t// Delegate to the default implementation for loading a user\n\t\t\tOidcUser oidcUser = delegate.loadUser(userRequest);\n\n\t\t\tOAuth2AccessToken accessToken = userRequest.getAccessToken();\n\t\t\tSet mappedAuthorities = new HashSet<>();\n\n\t\t\t// TODO\n\t\t\t// 1) Fetch the authority information from the protected resource using accessToken\n\t\t\t// 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities\n\n\t\t\t// 3) Create a copy of oidcUser but use the mappedAuthorities instead\n\t\t\toidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());\n\n\t\t\treturn oidcUser;\n\t\t};\n\t}\n}\n")])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n\n override fun configure(http: HttpSecurity) {\n http {\n oauth2Login {\n userInfoEndpoint {\n oidcUserService = oidcUserService()\n }\n }\n }\n }\n\n @Bean\n fun oidcUserService(): OAuth2UserService {\n val delegate = OidcUserService()\n\n return OAuth2UserService { userRequest ->\n // Delegate to the default implementation for loading a user\n var oidcUser = delegate.loadUser(userRequest)\n\n val accessToken = userRequest.accessToken\n val mappedAuthorities = HashSet()\n\n // TODO\n // 1) Fetch the authority information from the protected resource using accessToken\n // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities\n // 3) Create a copy of oidcUser but use the mappedAuthorities instead\n oidcUser = DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo)\n\n oidcUser\n }\n }\n}\n")])])]),n("p",[t._v("Xml")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('\n\t\n\n')])])]),n("h3",{attrs:{id:"oauth-2-0-userservice"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#oauth-2-0-userservice"}},[t._v("#")]),t._v(" OAuth 2.0 UserService")]),t._v(" "),n("p",[n("code",[t._v("DefaultOAuth2UserService")]),t._v(" is an implementation of an "),n("code",[t._v("OAuth2UserService")]),t._v(" that supports standard OAuth 2.0 Provider’s.")]),t._v(" "),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[n("code",[t._v("OAuth2UserService")]),t._v(" obtains the user attributes of the end-user (the resource owner) from the UserInfo Endpoint (by using the access token granted to the client during the authorization flow) and returns an "),n("code",[t._v("AuthenticatedPrincipal")]),t._v(" in the form of an "),n("code",[t._v("OAuth2User")]),t._v(".")])])]),t._v(" "),n("tbody")]),t._v(" "),n("p",[n("code",[t._v("DefaultOAuth2UserService")]),t._v(" uses a "),n("code",[t._v("RestOperations")]),t._v(" when requesting the user attributes at the UserInfo Endpoint.")]),t._v(" "),n("p",[t._v("If you need to customize the pre-processing of the UserInfo Request, you can provide "),n("code",[t._v("DefaultOAuth2UserService.setRequestEntityConverter()")]),t._v(" with a custom "),n("code",[t._v("Converter>")]),t._v(".\nThe default implementation "),n("code",[t._v("OAuth2UserRequestEntityConverter")]),t._v(" builds a "),n("code",[t._v("RequestEntity")]),t._v(" representation of a UserInfo Request that sets the "),n("code",[t._v("OAuth2AccessToken")]),t._v(" in the "),n("code",[t._v("Authorization")]),t._v(" header by default.")]),t._v(" "),n("p",[t._v("On the other end, if you need to customize the post-handling of the UserInfo Response, you will need to provide "),n("code",[t._v("DefaultOAuth2UserService.setRestOperations()")]),t._v(" with a custom configured "),n("code",[t._v("RestOperations")]),t._v(".\nThe default "),n("code",[t._v("RestOperations")]),t._v(" is configured as follows:")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("RestTemplate restTemplate = new RestTemplate();\nrestTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());\n")])])]),n("p",[n("code",[t._v("OAuth2ErrorResponseErrorHandler")]),t._v(" is a "),n("code",[t._v("ResponseErrorHandler")]),t._v(" that can handle an OAuth 2.0 Error (400 Bad Request).\nIt uses an "),n("code",[t._v("OAuth2ErrorHttpMessageConverter")]),t._v(" for converting the OAuth 2.0 Error parameters to an "),n("code",[t._v("OAuth2Error")]),t._v(".")]),t._v(" "),n("p",[t._v("Whether you customize "),n("code",[t._v("DefaultOAuth2UserService")]),t._v(" or provide your own implementation of "),n("code",[t._v("OAuth2UserService")]),t._v(", you’ll need to configure it as shown in the following example:")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login(oauth2 -> oauth2\n\t\t\t .userInfoEndpoint(userInfo -> userInfo\n\t\t\t .userService(this.oauth2UserService())\n\t\t\t ...\n\t\t\t )\n\t\t\t);\n\t}\n\n\tprivate OAuth2UserService oauth2UserService() {\n\t\t...\n\t}\n}\n")])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n\n override fun configure(http: HttpSecurity) {\n http {\n oauth2Login {\n userInfoEndpoint {\n userService = oauth2UserService()\n // ...\n }\n }\n }\n }\n\n private fun oauth2UserService(): OAuth2UserService {\n // ...\n }\n}\n")])])]),n("h3",{attrs:{id:"openid-connect-1-0-userservice"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#openid-connect-1-0-userservice"}},[t._v("#")]),t._v(" OpenID Connect 1.0 UserService")]),t._v(" "),n("p",[n("code",[t._v("OidcUserService")]),t._v(" is an implementation of an "),n("code",[t._v("OAuth2UserService")]),t._v(" that supports OpenID Connect 1.0 Provider’s.")]),t._v(" "),n("p",[t._v("The "),n("code",[t._v("OidcUserService")]),t._v(" leverages the "),n("code",[t._v("DefaultOAuth2UserService")]),t._v(" when requesting the user attributes at the UserInfo Endpoint.")]),t._v(" "),n("p",[t._v("If you need to customize the pre-processing of the UserInfo Request and/or the post-handling of the UserInfo Response, you will need to provide "),n("code",[t._v("OidcUserService.setOauth2UserService()")]),t._v(" with a custom configured "),n("code",[t._v("DefaultOAuth2UserService")]),t._v(".")]),t._v(" "),n("p",[t._v("Whether you customize "),n("code",[t._v("OidcUserService")]),t._v(" or provide your own implementation of "),n("code",[t._v("OAuth2UserService")]),t._v(" for OpenID Connect 1.0 Provider’s, you’ll need to configure it as shown in the following example:")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login(oauth2 -> oauth2\n\t\t\t\t.userInfoEndpoint(userInfo -> userInfo\n\t\t\t\t .oidcUserService(this.oidcUserService())\n\t\t\t\t ...\n\t\t\t )\n\t\t\t);\n\t}\n\n\tprivate OAuth2UserService oidcUserService() {\n\t\t...\n\t}\n}\n")])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n\n override fun configure(http: HttpSecurity) {\n http {\n oauth2Login {\n userInfoEndpoint {\n oidcUserService = oidcUserService()\n // ...\n }\n }\n }\n }\n\n private fun oidcUserService(): OAuth2UserService {\n // ...\n }\n}\n")])])]),n("h2",{attrs:{id:"id-token-signature-verification"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#id-token-signature-verification"}},[t._v("#")]),t._v(" ID Token Signature Verification")]),t._v(" "),n("p",[t._v("OpenID Connect 1.0 Authentication introduces the "),n("a",{attrs:{href:"https://openid.net/specs/openid-connect-core-1_0.html#IDToken",target:"_blank",rel:"noopener noreferrer"}},[t._v("ID Token"),n("OutboundLink")],1),t._v(", which is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when used by a Client.")]),t._v(" "),n("p",[t._v("The ID Token is represented as a "),n("a",{attrs:{href:"https://tools.ietf.org/html/rfc7519",target:"_blank",rel:"noopener noreferrer"}},[t._v("JSON Web Token"),n("OutboundLink")],1),t._v(" (JWT) and MUST be signed using "),n("a",{attrs:{href:"https://tools.ietf.org/html/rfc7515",target:"_blank",rel:"noopener noreferrer"}},[t._v("JSON Web Signature"),n("OutboundLink")],1),t._v(" (JWS).")]),t._v(" "),n("p",[t._v("The "),n("code",[t._v("OidcIdTokenDecoderFactory")]),t._v(" provides a "),n("code",[t._v("JwtDecoder")]),t._v(" used for "),n("code",[t._v("OidcIdToken")]),t._v(" signature verification. The default algorithm is "),n("code",[t._v("RS256")]),t._v(" but may be different when assigned during client registration.\nFor these cases, a resolver may be configured to return the expected JWS algorithm assigned for a specific client.")]),t._v(" "),n("p",[t._v("The JWS algorithm resolver is a "),n("code",[t._v("Function")]),t._v(" that accepts a "),n("code",[t._v("ClientRegistration")]),t._v(" and returns the expected "),n("code",[t._v("JwsAlgorithm")]),t._v(" for the client, eg. "),n("code",[t._v("SignatureAlgorithm.RS256")]),t._v(" or "),n("code",[t._v("MacAlgorithm.HS256")])]),t._v(" "),n("p",[t._v("The following code shows how to configure the "),n("code",[t._v("OidcIdTokenDecoderFactory")]),t._v(" "),n("code",[t._v("@Bean")]),t._v(" to default to "),n("code",[t._v("MacAlgorithm.HS256")]),t._v(" for all "),n("code",[t._v("ClientRegistration")]),t._v(":")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@Bean\npublic JwtDecoderFactory idTokenDecoderFactory() {\n\tOidcIdTokenDecoderFactory idTokenDecoderFactory = new OidcIdTokenDecoderFactory();\n\tidTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> MacAlgorithm.HS256);\n\treturn idTokenDecoderFactory;\n}\n")])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("@Bean\nfun idTokenDecoderFactory(): JwtDecoderFactory {\n val idTokenDecoderFactory = OidcIdTokenDecoderFactory()\n idTokenDecoderFactory.setJwsAlgorithmResolver { MacAlgorithm.HS256 }\n return idTokenDecoderFactory\n}\n")])])]),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[t._v("For MAC based algorithms such as "),n("code",[t._v("HS256")]),t._v(", "),n("code",[t._v("HS384")]),t._v(" or "),n("code",[t._v("HS512")]),t._v(", the "),n("code",[t._v("client-secret")]),t._v(" corresponding to the "),n("code",[t._v("client-id")]),t._v(" is used as the symmetric key for signature verification.")])])]),t._v(" "),n("tbody")]),t._v(" "),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[t._v("If more than one "),n("code",[t._v("ClientRegistration")]),t._v(" is configured for OpenID Connect 1.0 Authentication, the JWS algorithm resolver may evaluate the provided "),n("code",[t._v("ClientRegistration")]),t._v(" to determine which algorithm to return.")])])]),t._v(" "),n("tbody")]),t._v(" "),n("h2",{attrs:{id:"openid-connect-1-0-logout"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#openid-connect-1-0-logout"}},[t._v("#")]),t._v(" OpenID Connect 1.0 Logout")]),t._v(" "),n("p",[t._v("OpenID Connect Session Management 1.0 allows the ability to log out the End-User at the Provider using the Client.\nOne of the strategies available is "),n("a",{attrs:{href:"https://openid.net/specs/openid-connect-session-1_0.html#RPLogout",target:"_blank",rel:"noopener noreferrer"}},[t._v("RP-Initiated Logout"),n("OutboundLink")],1),t._v(".")]),t._v(" "),n("p",[t._v("If the OpenID Provider supports both Session Management and "),n("a",{attrs:{href:"https://openid.net/specs/openid-connect-discovery-1_0.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("Discovery"),n("OutboundLink")],1),t._v(", the client may obtain the "),n("code",[t._v("end_session_endpoint")]),t._v(" "),n("code",[t._v("URL")]),t._v(" from the OpenID Provider’s "),n("a",{attrs:{href:"https://openid.net/specs/openid-connect-session-1_0.html#OPMetadata",target:"_blank",rel:"noopener noreferrer"}},[t._v("Discovery Metadata"),n("OutboundLink")],1),t._v(".\nThis can be achieved by configuring the "),n("code",[t._v("ClientRegistration")]),t._v(" with the "),n("code",[t._v("issuer-uri")]),t._v(", as in the following example:")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("spring:\n security:\n oauth2:\n client:\n registration:\n okta:\n client-id: okta-client-id\n client-secret: okta-client-secret\n ...\n provider:\n okta:\n issuer-uri: https://dev-1234.oktapreview.com\n")])])]),n("p",[t._v("…​and the "),n("code",[t._v("OidcClientInitiatedLogoutSuccessHandler")]),t._v(", which implements RP-Initiated Logout, may be configured as follows:")]),t._v(" "),n("p",[t._v("Java")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {\n\n\t@Autowired\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\t@Override\n\tprotected void configure(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests(authorize -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login(withDefaults())\n\t\t\t.logout(logout -> logout\n\t\t\t\t.logoutSuccessHandler(oidcLogoutSuccessHandler())\n\t\t\t);\n\t}\n\n\tprivate LogoutSuccessHandler oidcLogoutSuccessHandler() {\n\t\tOidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler =\n\t\t\t\tnew OidcClientInitiatedLogoutSuccessHandler(this.clientRegistrationRepository);\n\n\t\t// Sets the location that the End-User\'s User Agent will be redirected to\n\t\t// after the logout has been performed at the Provider\n\t\toidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}");\n\n\t\treturn oidcLogoutSuccessHandler;\n\t}\n}\n')])])]),n("p",[t._v("Kotlin")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('@EnableWebSecurity\nclass OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {\n @Autowired\n private lateinit var clientRegistrationRepository: ClientRegistrationRepository\n\n override fun configure(http: HttpSecurity) {\n http {\n authorizeRequests {\n authorize(anyRequest, authenticated)\n }\n oauth2Login { }\n logout {\n logoutSuccessHandler = oidcLogoutSuccessHandler()\n }\n }\n }\n\n private fun oidcLogoutSuccessHandler(): LogoutSuccessHandler {\n val oidcLogoutSuccessHandler = OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository)\n\n // Sets the location that the End-User\'s User Agent will be redirected to\n // after the logout has been performed at the Provider\n oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}")\n return oidcLogoutSuccessHandler\n }\n}\n')])])]),n("table",[n("thead",[n("tr",[n("th"),t._v(" "),n("th",[n("code",[t._v("OidcClientInitiatedLogoutSuccessHandler")]),t._v(" supports the "),n("code",[t._v("{baseUrl}")]),t._v(" placeholder."),n("br"),t._v("If used, the application’s base URL, like "),n("code",[t._v("[https://app.example.org](https://app.example.org)")]),t._v(", will replace it at request time.")])])]),t._v(" "),n("tbody")]),t._v(" "),n("p",[n("RouterLink",{attrs:{to:"/en/spring-security/core.html"}},[t._v("Core Configuration")]),n("RouterLink",{attrs:{to:"/en/client/index.html"}},[t._v("OAuth2 Client")])],1)])}),[],!1,null,null,null);e.default=i.exports}}]);