Add Figures to Resource Server Docs

Fixes gh-8182
This commit is contained in:
Josh Cummings 2020-03-24 15:33:34 -06:00
parent dcacd06360
commit e62b8a7585
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
11 changed files with 118 additions and 23 deletions

View File

@ -23,6 +23,7 @@ The `RequestCache` is typically a `NullRequestCache` that does not save the requ
When a client receives the WWW-Authenticate header it knows it should retry with a username and password.
Below is the flow for the username and password being processed.
[[servlet-authentication-basicauthenticationfilter]]
.Authenticating Username and Password
image::{figures}/basicauthenticationfilter.png[]

View File

@ -1,5 +1,7 @@
[[oauth2resourceserver]]
== OAuth 2.0 Resource Server
:figures: images/servlet/oauth2
:icondir: images/icons
Spring Security supports protecting endpoints using two forms of OAuth 2.0 https://tools.ietf.org/html/rfc6750.html[Bearer Tokens]:
@ -9,12 +11,54 @@ Spring Security supports protecting endpoints using two forms of OAuth 2.0 https
This is handy in circumstances where an application has delegated its authority management to an https://tools.ietf.org/html/rfc6749[authorization server] (for example, Okta or Ping Identity).
This authorization server can be consulted by resource servers to authorize requests.
This section provides details on how Spring Security provides support for OAuth 2.0 https://tools.ietf.org/html/rfc6750.html[Bearer Tokens].
[NOTE]
====
Working samples for both {gh-samples-url}/boot/oauth2resourceserver[JWTs] and {gh-samples-url}/boot/oauth2resourceserver-opaque[Opaque Tokens] are available in the {gh-samples-url}[Spring Security repository].
====
=== Dependencies
Let's take a look at how Bearer Token Authentication works within Spring Security.
First, we see that, like <<servlet-authentication-basic,Basic Authentication>>, the https://tools.ietf.org/html/rfc7235#section-4.1[WWW-Authenticate] header is sent back to an unauthenticated client.
.Sending WWW-Authenticate Header
image::{figures}/bearerauthenticationentrypoint.png[]
The figure above builds off our <<servlet-securityfilterchain,`SecurityFilterChain`>> diagram.
image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized.
image:{icondir}/number_2.png[] Spring Security's <<servlet-authorization-filtersecurityinterceptor,`FilterSecurityInterceptor`>> indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`.
image:{icondir}/number_3.png[] Since the user is not authenticated, <<servlet-exceptiontranslationfilter,`ExceptionTranslationFilter`>> initiates __Start Authentication__.
The configured <<servlet-authentication-authenticationentrypoint,`AuthenticationEntryPoint`>> is an instance of {security-api-url}org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationEntryPoint.html[`BearerTokenAuthenticationEntryPoint`] which sends a WWW-Authenticate header.
The `RequestCache` is typically a `NullRequestCache` that does not save the request since the client is capable of replaying the requests it originally requested.
When a client receives the `WWW-Authenticate: Bearer` header, it knows it should retry with a bearer token.
Below is the flow for the bearer token being processed.
[[oauth2resourceserver-authentication-bearertokenauthenticationfilter]]
.Authenticating Bearer Token
image::{figures}/bearertokenauthenticationfilter.png[]
The figure builds off our <<servlet-securityfilterchain,`SecurityFilterChain`>> diagram.
image:{icondir}/number_1.png[] When the user submits their bearer token, the `BearerTokenAuthenticationFilter` creates a `BearerTokenAuthenticationToken` which is a type of <<servlet-authentication-authentication,`Authentication`>> by extracting the token from the `HttpServletRequest`.
image:{icondir}/number_2.png[] Next, the `HttpServletRequest` is passed to the `AuthenticationManagerResolver`, which selects the `AuthenticationManager`. The `BearerTokenAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated.
The details of what `AuthenticationManager` looks like depends on whether you're configured for <<oauth2resourceserver-jwt-minimalconfiguration,JWT>> or <<oauth2resourceserver-opaque-minimalconfiguration,opaque token>>.
image:{icondir}/number_3.png[] If authentication fails, then __Failure__
* The <<servlet-authentication-securitycontextholder>> is cleared out.
* The `AuthenticationEntryPoint` is invoked to trigger the WWW-Authenticate header to be sent again.
image:{icondir}/number_4.png[] If authentication is successful, then __Success__.
* The <<servlet-authentication-authentication>> is set on the <<servlet-authentication-securitycontextholder>>.
* The `BearerTokenAuthenticationFilter` invokes `FilterChain.doFilter(request,response)` to continue with the rest of the application logic.
=== Dependencies for JWT
Most Resource Server support is collected into `spring-security-oauth2-resource-server`.
However, the support for decoding and verifying JWTs is in `spring-security-oauth2-jose`, meaning that both are necessary in order to have a working resource server that supports JWT-encoded Bearer Tokens.
@ -88,9 +132,35 @@ The resulting `Authentication#getPrincipal`, by default, is a Spring Security `J
From here, consider jumping to:
<<oauth2resourceserver-jwt-jwkseturi,How to Configure without Tying Resource Server startup to an authorization server's availability>>
* <<oauth2resourceserver-jwt-architecture,How JWT Authentication Works>>
* <<oauth2resourceserver-jwt-jwkseturi,How to Configure without tying Resource Server startup to an authorization server's availability>>
* <<oauth2resourceserver-jwt-sansboot,How to Configure without Spring Boot>>
<<oauth2resourceserver-jwt-sansboot,How to Configure without Spring Boot>>
[[oauth2resourceserver-jwt-architecture]]
=== How JWT Authentication Works
Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7519[JWT] Authentication in servlet-based applications, like the one we just saw.
{security-api-url}org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.html[`JwtAuthenticationProvider`] is an <<servlet-authentication-authenticationprovider,`AuthenticationProvider`>> implementation that leverages a <<oauth2resourceserver-jwt-decoder,`JwtDecoder`>> and <<oauth2resourceserver-jwt-authorization-extraction,`JwtAuthenticationConverter`>> to authenticate a JWT.
Let's take a look at how `JwtAuthenticationProvider` works within Spring Security.
The figure explains details of how the <<servlet-authentication-authenticationmanager,`AuthenticationManager`>> in figures from <<oauth2resourceserver-authentication-bearertokenauthenticationfilter,Reading the Bearer Token>> works.
.`JwtAuthenticationProvider` Usage
image::{figures}/jwtauthenticationprovider.png[]
image:{icondir}/number_1.png[] The authentication `Filter` from <<oauth2resourceserver-authentication-bearertokenauthenticationfilter,Reading the Bearer Token>> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by <<servlet-authentication-providermanager,`ProviderManager`>>.
image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an <<servlet-authentication-authenticationprovider>> of type `JwtAuthenticationProvider`.
[[oauth2resourceserver-jwt-architecture-jwtdecoder]]
image:{icondir}/number_3.png[] `JwtAuthenticationProvider` decodes, verifies, and validates the `Jwt` using a <<oauth2resourceserver-jwt-decoder,`JwtDecoder`>>.
[[oauth2resourceserver-jwt-architecture-jwtauthenticationconverter]]
image:{icondir}/number_4.png[] `JwtAuthenticationProvider` then uses the <<oauth2resourceserver-jwt-authorization-extraction,`JwtAuthenticationConverter`>> to convert the `Jwt` into a `Collection` of granted authorities.
image:{icondir}/number_5.png[] When authentication is successful, the <<servlet-authentication-authentication,`Authentication`>> that is returned is of type `JwtAuthenticationToken` and has a principal that is the `Jwt` returned by the configured `JwtDecoder`.
Ultimately, the returned `JwtAuthenticationToken` will be set on the <<servlet-authentication-securitycontextholder,`SecurityContextHolder`>> by the authentication `Filter`.
[[oauth2resourceserver-jwt-jwkseturi]]
=== Specifying the Authorization Server JWK Set Uri Directly
@ -206,8 +276,8 @@ The above requires the scope of `message:read` for any URL that starts with `/me
Methods on the `oauth2ResourceServer` DSL will also override or replace auto configuration.
For example, the second `@Bean` Spring Boot creates is a `JwtDecoder`, which decodes `String` tokens into validated instances of `Jwt`:
[[oauth2resourceserver-jwt-decoder]]
For example, the second `@Bean` Spring Boot creates is a `JwtDecoder`, which <<oauth2resourceserver-jwt-architecture-jwtdecoder,decodes `String` tokens into validated instances of `Jwt`>>:
.JWT Decoder
====
@ -323,7 +393,7 @@ Using `jwkSetUri()` takes precedence over any configuration property.
[[oauth2resourceserver-jwt-decoder-dsl]]
==== Using `decoder()`
More powerful than `jwkSetUri()` is `decoder()`, which will completely replace any Boot auto configuration of `JwtDecoder`:
More powerful than `jwkSetUri()` is `decoder()`, which will completely replace any Boot auto configuration of <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>>:
.JWT Decoder Configuration
====
@ -383,7 +453,7 @@ This is handy when deeper configuration, like <<oauth2resourceserver-jwt-validat
[[oauth2resourceserver-jwt-decoder-bean]]
==== Exposing a `JwtDecoder` `@Bean`
Or, exposing a `JwtDecoder` `@Bean` has the same effect as `decoder()`:
Or, exposing a <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>> `@Bean` has the same effect as `decoder()`:
[source,java]
----
@ -629,11 +699,11 @@ However, there are a number of circumstances where this default is insufficient.
For example, some authorization servers don't use the `scope` attribute, but instead have their own custom attribute.
Or, at other times, the resource server may need to adapt the attribute or a composition of attributes into internalized authorities.
To this end, the DSL exposes `jwtAuthenticationConverter()`, which is responsible for converting a `Jwt` into an `Authentication`.
To this end, the DSL exposes `jwtAuthenticationConverter()`, which is responsible for <<oauth2resourceserver-jwt-architecture-jwtauthenticationconverter,converting a `Jwt` into an `Authentication`>>.
As part of its configuration, we can supply a subsidiary converter to go from `Jwt` to a `Collection` of granted authorities.
Let's say that that your authorization server communicates authorities in a custom claim called `authorities`.
In that case, you can configure the claim that `JwtAuthenticationConverter` should inspect, like so:
In that case, you can configure the claim that <<oauth2resourceserver-jwt-architecture-jwtauthenticationconverter,`JwtAuthenticationConverter`>> should inspect, like so:
.Authorities Claim Configuration
====
@ -817,7 +887,7 @@ OAuth2TokenValidator<Jwt> audienceValidator() {
}
----
Then, to add into a resource server, it's a matter of specifying the `JwtDecoder` instance:
Then, to add into a resource server, it's a matter of specifying the <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>> instance:
[source,java]
----
@ -1017,10 +1087,33 @@ The resulting `Authentication#getPrincipal`, by default, is a Spring Security `{
From here, you may want to jump to:
* <<oauth2resourceserver-opaque-architecture>>
* <<oauth2resourceserver-opaque-attributes,Looking Up Attributes Post-Authentication>>
* <<oauth2resourceserver-opaque-authorization-extraction,Extracting Authorities Manually>>
* <<oauth2resourceserver-opaque-jwt-introspector,Using Introspection with JWTs>>
[[oauth2resourceserver-opaque-architecture]]
=== How Opaque Token Authentication Works
Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7662[opaque token] Authentication in servlet-based applications, like the one we just saw.
{security-api-url}org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.html[`OpaqueTokenAuthenticationProvider`] is an <<servlet-authentication-authenticationprovider,`AuthenticationProvider`>> implementation that leverages a <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>> to authenticate an opaque token.
Let's take a look at how `OpaqueTokenAuthenticationProvider` works within Spring Security.
The figure explains details of how the <<servlet-authentication-authenticationmanager,`AuthenticationManager`>> in figures from <<oauth2resourceserver-authentication-bearertokenauthenticationfilter,Reading the Bearer Token>> works.
.`OpaqueTokenAuthenticationProvider` Usage
image::{figures}/opaquetokenauthenticationprovider.png[]
image:{icondir}/number_1.png[] The authentication `Filter` from <<oauth2resourceserver-authentication-bearertokenauthenticationfilter,Reading the Bearer Token>> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by <<servlet-authentication-providermanager,`ProviderManager`>>.
image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an <<servlet-authentication-authenticationprovider>> of type `OpaqueTokenAuthenticationProvider`.
[[oauth2resourceserver-opaque-architecture-introspector]]
image:{icondir}/number_3.png[] `OpaqueTokenAuthenticationProvider` introspects the opaque token and adds granted authorities using an <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>>.
When authentication is successful, the <<servlet-authentication-authentication,`Authentication`>> that is returned is of type `BearerTokenAuthentication` and has a principal that is the `OAuth2AuthenticatedPrincipal` returned by the configured <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>>.
Ultimately, the returned `BearerTokenAuthentication` will be set on the <<servlet-authentication-securitycontextholder,`SecurityContextHolder`>> by the authentication `Filter`.
[[oauth2resourceserver-opaque-attributes]]
=== Looking Up Attributes Post-Authentication
@ -1149,7 +1242,8 @@ The above requires the scope of `message:read` for any URL that starts with `/me
Methods on the `oauth2ResourceServer` DSL will also override or replace auto configuration.
For example, the second `@Bean` Spring Boot creates is an `OpaqueTokenIntrospector`, which decodes `String` tokens into validated instances of `OAuth2AuthenticatedPrincipal`:
[[oauth2resourceserver-opaque-introspector]]
For example, the second `@Bean` Spring Boot creates is an `OpaqueTokenIntrospector`, <<oauth2resourceserver-opaque-architecture-introspector,which decodes `String` tokens into validated instances of `OAuth2AuthenticatedPrincipal`>>:
[source,java]
----
@ -1159,11 +1253,11 @@ public OpaqueTokenIntrospector introspector() {
}
----
If the application doesn't expose a `OpaqueTokenIntrospector` bean, then Spring Boot will expose the above default one.
If the application doesn't expose a <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> bean, then Spring Boot will expose the above default one.
And its configuration can be overridden using `introspectionUri()` and `introspectionClientCredentials()` or replaced using `introspector()`.
Or, if you're not using Spring Boot at all, then both of these components - the filter chain and a `OpaqueTokenIntrospector` can be specified in XML.
Or, if you're not using Spring Boot at all, then both of these components - the filter chain and a <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> can be specified in XML.
The filter chain is specified like so:
@ -1181,7 +1275,7 @@ The filter chain is specified like so:
----
====
And the `OpaqueTokenIntrospector` like so:
And the <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> like so:
.Opaque Token Introspector
====
@ -1262,7 +1356,7 @@ Using `introspectionUri()` takes precedence over any configuration property.
[[oauth2resourceserver-opaque-introspector-dsl]]
==== Using `introspector()`
More powerful than `introspectionUri()` is `introspector()`, which will completely replace any Boot auto configuration of `OpaqueTokenIntrospector`:
More powerful than `introspectionUri()` is `introspector()`, which will completely replace any Boot auto configuration of <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>>:
.Introspector Configuration
====
@ -1322,7 +1416,7 @@ This is handy when deeper configuration, like <<oauth2resourceserver-opaque-auth
[[oauth2resourceserver-opaque-introspector-bean]]
==== Exposing a `OpaqueTokenIntrospector` `@Bean`
Or, exposing a `OpaqueTokenIntrospector` `@Bean` has the same effect as `introspector()`:
Or, exposing a <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> `@Bean` has the same effect as `introspector()`:
[source,java]
----
@ -1399,7 +1493,7 @@ For example, if the introspection response were:
Then Resource Server would generate an `Authentication` with two authorities, one for `message:read` and the other for `message:write`.
This can, of course, be customized using a custom `OpaqueTokenIntrospector` that takes a look at the attribute set and converts in its own way:
This can, of course, be customized using a custom <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> that takes a look at the attribute set and converts in its own way:
[source,java]
----
@ -1483,7 +1577,7 @@ Any attributes in the corresponding `OAuth2AuthenticatedPrincipal` would be what
But, let's say that, oddly enough, the introspection endpoint only returns whether or not the token is active.
Now what?
In this case, you can create a custom `OpaqueTokenIntrospector` that still hits the endpoint, but then updates the returned principal to have the JWTs claims as the attributes:
In this case, you can create a custom <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> that still hits the endpoint, but then updates the returned principal to have the JWTs claims as the attributes:
[source,java]
----
@ -1528,7 +1622,7 @@ Generally speaking, a Resource Server doesn't care about the underlying user, bu
That said, at times it can be valuable to tie the authorization statement back to a user.
If an application is also using `spring-security-oauth2-client`, having set up the appropriate `ClientRegistrationRepository`, then this is quite simple with a custom `OpaqueTokenIntrospector`.
If an application is also using `spring-security-oauth2-client`, having set up the appropriate `ClientRegistrationRepository`, then this is quite simple with a custom <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>>.
This implementation below does three things:
* Delegates to the introspection endpoint, to affirm the token's validity
@ -1577,7 +1671,7 @@ public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector
}
----
Either way, having created your `OpaqueTokenIntrospector`, you should publish it as a `@Bean` to override the defaults:
Either way, having created your <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>>, you should publish it as a `@Bean` to override the defaults:
[source,java]
----
@ -1732,9 +1826,9 @@ The issuer should be one that the code can verify from a trusted source like a w
===== Parsing the Claim Only Once
You may have observed that this strategy, while simple, comes with the trade-off that the JWT is parsed once by the `AuthenticationManagerResolver` and then again by the `JwtDecoder` later on in the request.
You may have observed that this strategy, while simple, comes with the trade-off that the JWT is parsed once by the `AuthenticationManagerResolver` and then again by the <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>> later on in the request.
This extra parsing can be alleviated by configuring the `JwtDecoder` directly with a `JWTClaimSetAwareJWSKeySelector` from Nimbus:
This extra parsing can be alleviated by configuring the <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>> directly with a `JWTClaimSetAwareJWSKeySelector` from Nimbus:
[source,java]
----
@ -1836,7 +1930,7 @@ public class TenantJwtIssuerValidator implements OAuth2TokenValidator<Jwt> {
}
----
Now that we have a tenant-aware processor and a tenant-aware validator, we can proceed with creating our `JwtDecoder`:
Now that we have a tenant-aware processor and a tenant-aware validator, we can proceed with creating our <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>>:
[source,java]
----

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB