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