I am learning the OAuth2 authorization code flow.
- I have my own
Authorization Server
(AS) which is OpenAM 7.1. - The
Client
is a simple Spring-Boot web application with a static HTML page, I use Spring-Security to protect the HTML page and control the Oauth2 flow.
I think that my Authorization Server configuration is correct because AS produces the access_token
at the end when I simulate the communication with CURL.
But somehow Spring-Security does not want to accept the issued and validated access token. So I think that my Spring-Security configuration is not correct.
I tried to configure Spring-Security in many different ways, but unfortunately, none of them was working. Maybe I need to implement the steps that I execute with CURL with Spring-Security, but maybe I just missed a configuration line.
This is the last step of my CURL chain where AS gives me the access token (exchange the authorization code for an access token):
url="$authServerHost/openam/oauth2/realms/root/access_token" curl --silent --dump-header - --insecur --request POST --data "grant_type=authorization_code" --data "code=$authorizationCode" --data "client_id=$clientId" --data "client_secret=$clientSecret" --data "redirect_uri=$redirectUri" "$url" -------- response --------- HTTP/1.1 200 X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Cache-Control: no-store Pragma: no-cache Content-Type: application/json;charset=UTF-8 Content-Length: 157 Date: Wed, 29 Sep 2021 17:57:30 GMT { "access_token":"2SiD3moh2iql5j3ocdPOR-W4QRE", "refresh_token":"zWSG-fi1J9hUrY0Tw6GHeXnndgM", "scope":"public_profile", "token_type":"Bearer", "expires_in":3599 }
This is the probe of the token (validate and retrieve information about the token):
url="$authServerHost/openam/oauth2/tokeninfo" curl --silent --dump-header - --insecur --request GET --header "Authorization: Bearer $accessToken" "$url" -------- response --------- HTTP/1.1 200 X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Cache-Control: no-store Pragma: no-cache Content-Type: application/json;charset=UTF-8 Content-Length: 326 Date: Wed, 29 Sep 2021 17:57:30 GMT { "access_token":"2SiD3moh2iql5j3ocdPOR-W4QRE", "grant_type":"authorization_code", "auth_level":0, "auditTrackingId":"45f24ab1-f9a4-43df-bb17-4b4d6c0ffee4-112239", "scope":["public_profile"], "public_profile":"", "realm":"/", "token_type":"Bearer", "expires_in":3599, "authGrantId":"2tg__erKRo_utv4Py_TOt1NOtDo", "client_id":"hello-web" }
Then I try to fetch the protected content with this CURL but Spring-Security redirects me again to the Spring Security’s provider selection page:
url="https://web.example.com:8444/user.html" curl --silent --insecur --dump-header - --request GET --header "Authorization: Bearer $accessToken" "$url" -------- response --------- HTTP/1.1 302 Cache-Control: private X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Strict-Transport-Security: max-age=31536000 ; includeSubDomains X-Frame-Options: DENY Location: https://web.example.com:8444/oauth2/authorization/openam Content-Length: 0 Date: Wed, 29 Sep 2021 18:18:40 GMT
As you can see my web-app with Spring-Security does not accept the valid bearer token and redirects the request to the Authorization Server despite I provided the token.
This is my Spring-Security configuration:
spring: security: oauth2: client: registration: openam: client-id: hello-web client-secret: client-secret authorization-grant-type: authorization_code redirect-uri: https://web.example.com:8444/user.html scope: public_profile provider: openam: token-uri: https://openam.example.com:8443/openam/oauth2/access_token authorization-uri: https://openam.example.com:8443/openam/oauth2/authorize
And the Spring-Security config I use:
@EnableWebSecurity public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(authorizeRequests -> authorizeRequests .antMatchers("/").permitAll() .anyRequest().authenticated() ) .oauth2Login(withDefaults()); } }
What did I miss? Could you please guide me in the right direction?
—-> ADDITIONAL INFO <—-
When I open the protected page https://web.example.com:8444/user.html
in the web browser, then
- I am redirected properly to the Authorization Server login page.
- Then I log in.
- Then the consent approval form appears where I give access to the “public_profile” scope
- Then Spring redirects me again to the login page (step 2).
I think that happens because Spring just does not want to accept the issued access token.
Spring log:
open http://web.example.com:8081/user.html o.s.security.web.FilterChainProxy : Securing GET /user.html s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext o.s.s.w.a.i.FilterSecurityInterceptor : Failed to authorize filter invocation [GET /user.html] with attributes [authenticated] o.s.s.w.s.HttpSessionRequestCache : Saved request https://web.example.com:8444/user.html to session s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using And [Not [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]], Not [And [Or [Ant [pattern='/login'], Ant [pattern='/favicon.ico']], And [Not [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]], MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@6f2273f8, matchingMediaTypes=[application/xhtml+xml, image/*, text/html, text/plain], useEquals=false, ignoredMediaTypes=[*/*]]]]]] s.w.a.DelegatingAuthenticationEntryPoint : Match found! Executing org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint@23fec83b o.s.s.web.DefaultRedirectStrategy : Redirecting to https://web.example.com:8444/oauth2/authorization/openam w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request o.s.security.web.FilterChainProxy : Securing GET /oauth2/authorization/openam s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext o.s.s.web.DefaultRedirectStrategy : Redirecting to https://openam.example.com:8443/openam/oauth2/authorize?response_type=code&client_id=example-web&scope=public_profile&state=HYbQUtdrCuQ5dKUtGI6bBoBbLvScCoELGcundKpNGoI%3D&redirect_uri=https://web.example.com:8444/user.html w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request then the Authorization Server's login page appears then I allow access to the "public_profile" scope then this happens on the Spring side: o.s.security.web.FilterChainProxy : Securing GET /user.html?code=1aC6OC44B03k37pACmmD8n-Ol38&iss=https%3A%2F%2Fopenam.example.com%3A8443%2Fopenam%2Foauth2&state=HYbQUtdrCuQ5dKUtGI6bBoBbLvScCoELGcundKpNGoI%3D&client_id=example-web s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext o.s.s.w.a.i.FilterSecurityInterceptor : Failed to authorize filter invocation [GET /user.html?code=1aC6OC44B03k37pACmmD8n-Ol38&iss=https%3A%2F%2Fopenam.example.com%3A8443%2Fopenam%2Foauth2&state=HYbQUtdrCuQ5dKUtGI6bBoBbLvScCoELGcundKpNGoI%3D&client_id=example-web] with attributes [authenticated] o.s.s.w.s.HttpSessionRequestCache : Saved request https://web.example.com:8444/user.html?code=1aC6OC44B03k37pACmmD8n-Ol38&iss=https%3A%2F%2Fopenam.example.com%3A8443%2Fopenam%2Foauth2&state=HYbQUtdrCuQ5dKUtGI6bBoBbLvScCoELGcundKpNGoI%3D&client_id=example-web to session s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using And [Not [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]], Not [And [Or [Ant [pattern='/login'], Ant [pattern='/favicon.ico']], And [Not [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]], MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@6f2273f8, matchingMediaTypes=[application/xhtml+xml, image/*, text/html, text/plain], useEquals=false, ignoredMediaTypes=[*/*]]]]]] s.w.a.DelegatingAuthenticationEntryPoint : Match found! Executing org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint@23fec83b o.s.s.web.DefaultRedirectStrategy : Redirecting to https://web.example.com:8444/oauth2/authorization/openam w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request o.s.security.web.FilterChainProxy : Securing GET /oauth2/authorization/openam s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext o.s.s.web.DefaultRedirectStrategy : Redirecting to https://openam.example.com:8443/openam/oauth2/authorize?response_type=code&client_id=example-web&scope=public_profile&state=SRHzhN7krlfUhJ50m9lsvaWgLUHglgMOA0wMZHrAmYo%3D&redirect_uri=https://web.example.com:8444/user.html w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
Advertisement
Answer
I notice two issues in the code you have shared.
The first is that you may be confusing an OAuth 2.0 resource server and an OAuth 2.0 client.
The application running on web.example.com:8444 is configured as an OAuth 2.0 client.
However, you are making a request to web.example.com:8444, providing a bearer token and asking for a resource.
The client will not validate the bearer token. In this scenario it seems like you are treating the application as if it were a resource server.
If you are looking to create a resource server application, you can see the full documentation in the Spring Security reference.
The second issue is the behaviour you described when accessing the client in the browser.
The problem here is customising redirect-uri: https://web.example.com:8444/user.html
.
When doing this you override the default redirect URI, which is /login/oauth2/callback/{registrationId}
.
This URI is special because it prompts the OAuth2LoginAuthenticationFilter
to process the request, attempt to authenticate the user and create the OAuth2AuthenticationToken
.
When you customise redirect URI, the OAuth2LoginAuthenticationFilter
is not invoked and the application does not know if the user is authenticated.