I have an application for which my users require an access
authority to use the application. As such I did the following:
@Bean
fun filterChain(http: ServerHttpSecurity): SecurityWebFilterChain = http {
csrf { disable() }
formLogin { disable() }
logout { disable() }
httpBasic { disable() }
authorizeExchange {
authorize(anyExchange, hasAuthority("access"))
}
oauth2ResourceServer { jwt {} }
}
Now I would like to add an additional check for a specific claim. I want to verify the contents of the resource_access
claim from within my token and more specifically the presence of a key there.
Basically what I would like is something like this:
authorize(anyExchange, hasAuthority("access") && hasResourceAccess("my-resource"))
I've been trying to find how I can do this without having to redefine everything and in a reactive-supporting way.
A few options that I've tried but haven't been able to see through:
1/ OAuth2TokenValidator
I have tried creating my own but I'm stuck at being able to set it as a JwtValidator
. Looks like I need to define a ReactiveJwtDecoder
but it requires an issuer-uri
that I don't have access to.
@Bean
fun jwtDecoder(): ReactiveJwtDecoder =
(ReactiveJwtDecoders.fromIssuerLocation(issuerUri) as NimbusReactiveJwtDecoder).apply {
setJwtValidator(MyAccessValidator())
}
2/ ReactiveAuthorizationManager<AuthorizationContext>
I could create my own but then I'm not sure how I can combine both the AuthorityReactiveAuthorizationManager
and mine. I guess I could bundle both logics in my own but I'd like a solution where I can reuse what is already existing.
I wanted to use DelegatingReactiveAuthorizationManager
but it seems I cannot because it's a ReactiveAuthorizationManager<ServerWebExchange>
and not a ReactiveAuthorizationManager<AuthorizationContext>
I could rewrite it but I guess there is something that already exists that I just wasn't able to find.
So, digging a bit more I found out the following simple enough solution.
I created my own ReactiveAuthorizationManager
although considering how I used it I could have done with a simple Function
.
fun hasResourceAccess(resource: String) = ReactiveAuthorizationManager<Any?> { authenticationMono, _ ->
authenticationMono
.mapNotNull {
val jwt = it.principal
if (jwt is Jwt) jwt.getClaimAsMap("resource_access")
else null
}
.map { AuthorizationDecision(it.containsKey(resource)) }
.defaultIfEmpty(AuthorizationDecision(false))
}
An I used it this way
@Bean
fun filterChain(http: ServerHttpSecurity): SecurityWebFilterChain = http {
csrf { disable() }
formLogin { disable() }
logout { disable() }
httpBasic { disable() }
authorizeExchange {
authorize(anyExchange) { authenticationMono, authorizationContext ->
Flux
.merge(
hasAuthority("access").check(authenticationMono, authorizationContext),
hasResourceAccess("my-resource").check(authenticationMono, authorizationContext)
)
.all(AuthorizationDecision::isGranted)
.map(::AuthorizationDecision)
}
}
oauth2ResourceServer { jwt {} }
}