Trying to implement the OAuth2 protocol using Spring Authorization Server. Created a simple application with the following configuration.
@SpringBootApplication
class AuthorizationServerApplication
fun main(args: Array<String>) {
runApplication<AuthorizationServerApplication>(*args)
}
@Configuration
@Import(OAuth2AuthorizationServerConfiguration::class)
class AuthorizationServerConfig {
@Bean
fun registeredClientRepository(): RegisteredClientRepository? {
val registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("client")
.clientSecret("{noop}client-secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
.redirectUri("http://example-host:9002/test/admin")
.redirectUri("http://example-host:9002/test")
.scope(OidcScopes.OPENID)
.scope("read")
.build()
return InMemoryRegisteredClientRepository(registeredClient)
}
...
@Bean
fun providerSettings(): ProviderSettings? {
return ProviderSettings.builder()
.issuer("http://example-host:9000")
.build()
}
}
@EnableWebSecurity
class DefaultSecurityConfig {
@Bean
fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain? {
http
.authorizeRequests { authorizeRequests ->
authorizeRequests
.anyRequest().permitAll()
}
// these are disabled so that I won't get any additional issue this needs to be changed
.formLogin().disable()
.csrf().disable()
return http.build()
}
@Bean
fun users(): UserDetailsService? {
val admin: UserDetails = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN")
.authorities("read", "write")
.build()
val user: UserDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.authorities("read")
.build()
return InMemoryUserDetailsManager(admin, user)
}
}
When calling the following endpoint: GET http://example-host:9000/.well-known/oauth-authorization-server
I get back these:
{
"issuer": "http://example-host:9000",
"authorization_endpoint": "http://example-host:9000/oauth2/authorize",
"token_endpoint": "http://example-host:9000/oauth2/token",
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post",
"client_secret_jwt",
"private_key_jwt"
],
"jwks_uri": "http://example-host:9000/oauth2/jwks",
"response_types_supported": [
"code"
],
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token"
],
"revocation_endpoint": "http://example-host:9000/oauth2/revoke",
"revocation_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post",
"client_secret_jwt",
"private_key_jwt"
],
"introspection_endpoint": "http://example-host:9000/oauth2/introspect",
"introspection_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post",
"client_secret_jwt",
"private_key_jwt"
],
"code_challenge_methods_supported": [
"S256"
]
}
I'm trying to go past authentication and trying to follow this documentation. I tried multiple calls one of these is:
curl --location --request POST 'example-host:9000/oauth2/token' \
--header 'Authorization: Basic YWRtaW46cGFzc3dvcmQ=' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client' \
--data-urlencode 'client_secret=client-secret'
I get back 401 most of the time with different messages. Can't really figure out where I can find some documentation with examples as the examples that I was able to find are not really helpful for my usecase. I don't fully get how I would be able to authenticate and use the resource servers' endpoints in case of the client being a front-end application. Maybe I misunderstood something?
Edit: Adding trace logs from authorization server when requesting token endpoint:
curl --location --request POST 'example-host:9000/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client' \
--data-urlencode 'client_secret=client-secret' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'username=user' \
--data-urlencode 'password=password'
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda$746/0x000000080105b608@7a764446, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@841f2ce, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@38eb32b, org.springframework.security.web.context.SecurityContextPersistenceFilter@4232bd1e, org.springframework.security.oauth2.server.authorization.web.ProviderContextFilter@1c463b0b, org.springframework.security.web.header.HeaderWriterFilter@6e87e57e, org.springframework.security.web.csrf.CsrfFilter@3657ca3e, org.springframework.security.web.authentication.logout.LogoutFilter@36c9161d, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter@c85053, org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter@827dabb, org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter@3fce33c2, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter@5f5e39a5, org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter@6f53bec6, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@1be28be5, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@12322dee, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4aba4a37, org.springframework.security.web.session.SessionManagementFilter@3c29c1e5, org.springframework.security.web.access.ExceptionTranslationFilter@5e60456e, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@5771f1b4, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter@1ec08e27, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter@548d0a8d, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter@6c15398a, org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter@6bcb4915]] (1/2)
2022-06-14 16:41:47.154 DEBUG 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Securing POST /oauth2/token
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (2/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking SecurityContextPersistenceFilter (3/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] w.c.HttpSessionSecurityContextRepository : Created SecurityContextImpl [Null authentication]
2022-06-14 16:41:47.154 DEBUG 34744 --- [nio-9000-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking ProviderContextFilter (4/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (5/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (6/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.csrf.CsrfFilter : Did not protect against CSRF since request did not match And [CsrfNotRequired [TRACE, HEAD, GET, OPTIONS], Not [Or [org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda$746/0x000000080105b608@7a764446]]]
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (7/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationEndpointFilter (8/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking OidcProviderConfigurationEndpointFilter (9/22)
2022-06-14 16:41:47.154 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking NimbusJwkSetEndpointFilter (10/22)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationServerMetadataEndpointFilter (11/22)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.security.web.FilterChainProxy : Invoking OAuth2ClientAuthenticationFilter (12/22)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.s.authentication.ProviderManager : Authenticating request with JwtClientAssertionAuthenticationProvider (1/11)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.s.authentication.ProviderManager : Authenticating request with ClientSecretAuthenticationProvider (2/11)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.s.authentication.ProviderManager : Authenticating request with PublicClientAuthenticationProvider (3/11)
2022-06-14 16:41:47.155 TRACE 34744 --- [nio-9000-exec-2] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
2022-06-14 16:41:47.155 DEBUG 34744 --- [nio-9000-exec-2] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2022-06-14 16:41:47.156 DEBUG 34744 --- [nio-9000-exec-2] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2022-06-14 16:41:47.156 DEBUG 34744 --- [nio-9000-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
CodePudding user response:
First, in your case you don't need the Authorization
header in your request for token since you explicitly allowed all requests to pass through via authorizeRequests.anyRequest().permitAll()
.
Second, in your curl
request you didn't specify at least a desired grant type and its parameters.
For example, for the password
grant type the request might look something like this:
curl -L -X POST 'example-host:9000/oauth2/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client' \
--data-urlencode 'client_secret=client-secret' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'username=user' \
--data-urlencode 'password=password'
UPDATE:
Spring authorization server 0.3.0 doesn't support the
password
grant type, exactly as it shows in thegrant_types_supported
section of the.well-known/oauth-authorization-server
endpoint output. There's just no such authentication provider in theorg.springframework.security.oauth2.server.authorization.authentication
package.To make at least the
client_credentials
token request work, add
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
and (in case you'd like to pass the client_id
and client_secret
inside the POST body)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
to your RegisteredClient
in the registeredClientRepository
.
Then this can be tested with
curl -L -X POST 'http://example-host:9000/oauth2/token' -H 'Content-Type: application/x-www-form-urlencoded' -d 'grant_type=client_credentials&client_id=client&client_secret=client-secret'
(be sure to pass the actual client_id
and client_secret
of a RegisteredClient
)
Also, if you import OAuth2AuthorizationServerConfiguration
a default SecurityFilterChain
for the auth server endpoints is created and there's no need to define it manually. On the other hand a SecurityFilterChain
for your app authentication is likely still needed.
- To debug the OAuth authentication process and see the exact exceptions, if any, set some breakpoints in the
org.springframework.security.authentication.ProviderManager#authenticate()
method