diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/basic.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/basic.adoc index 849fc278b0..844201b557 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/basic.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/basic.adoc @@ -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[] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-resourceserver.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-resourceserver.adoc index 78a36daf68..7137009a72 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-resourceserver.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-resourceserver.adoc @@ -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 <>, 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 <> 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 <> indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. + +image:{icondir}/number_3.png[] Since the user is not authenticated, <> initiates __Start Authentication__. +The configured <> 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 <> diagram. + +image:{icondir}/number_1.png[] When the user submits their bearer token, the `BearerTokenAuthenticationFilter` creates a `BearerTokenAuthenticationToken` which is a type of <> 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 <> or <>. + +image:{icondir}/number_3.png[] If authentication fails, then __Failure__ + +* The <> 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 <> is set on the <>. +* 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-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 <> implementation that leverages a <> and <> to authenticate a JWT. + +Let's take a look at how `JwtAuthenticationProvider` works within Spring Security. +The figure explains details of how the <> in figures from <> works. + +.`JwtAuthenticationProvider` Usage +image::{figures}/jwtauthenticationprovider.png[] + +image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by <>. + +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an <> of type `JwtAuthenticationProvider`. + +[[oauth2resourceserver-jwt-architecture-jwtdecoder]] +image:{icondir}/number_3.png[] `JwtAuthenticationProvider` decodes, verifies, and validates the `Jwt` using a <>. + +[[oauth2resourceserver-jwt-architecture-jwtauthenticationconverter]] +image:{icondir}/number_4.png[] `JwtAuthenticationProvider` then uses the <> to convert the `Jwt` into a `Collection` of granted authorities. + +image:{icondir}/number_5.png[] When authentication is successful, the <> 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 <> 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 <>: .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 <>: .JWT Decoder Configuration ==== @@ -383,7 +453,7 @@ This is handy when deeper configuration, like <> `@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 <>. 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 <> should inspect, like so: .Authorities Claim Configuration ==== @@ -817,7 +887,7 @@ OAuth2TokenValidator 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 <> 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]] +=== 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 <> implementation that leverages a <> to authenticate an opaque token. + +Let's take a look at how `OpaqueTokenAuthenticationProvider` works within Spring Security. +The figure explains details of how the <> in figures from <> works. + +.`OpaqueTokenAuthenticationProvider` Usage +image::{figures}/opaquetokenauthenticationprovider.png[] + +image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by <>. + +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an <> of type `OpaqueTokenAuthenticationProvider`. + +[[oauth2resourceserver-opaque-architecture-introspector]] +image:{icondir}/number_3.png[] `OpaqueTokenAuthenticationProvider` introspects the opaque token and adds granted authorities using an <>. +When authentication is successful, the <> that is returned is of type `BearerTokenAuthentication` and has a principal that is the `OAuth2AuthenticatedPrincipal` returned by the configured <>. +Ultimately, the returned `BearerTokenAuthentication` will be set on the <> 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`, <>: [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 <> 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 <> 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 <> 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 <>: .Introspector Configuration ==== @@ -1322,7 +1416,7 @@ This is handy when deeper configuration, like <> `@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 <> 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 <> 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 <>. 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 <>, 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 <> 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 <> directly with a `JWTClaimSetAwareJWSKeySelector` from Nimbus: [source,java] ---- @@ -1836,7 +1930,7 @@ public class TenantJwtIssuerValidator implements OAuth2TokenValidator { } ---- -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 <>: [source,java] ---- diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/beareraccessdeniedhandler.odg b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/beareraccessdeniedhandler.odg new file mode 100644 index 0000000000..e35bce8c37 Binary files /dev/null and b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/beareraccessdeniedhandler.odg differ diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.odg b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.odg new file mode 100644 index 0000000000..b4b50fb6e5 Binary files /dev/null and b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.odg differ diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.png b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.png new file mode 100644 index 0000000000..a022951a6b Binary files /dev/null and b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.png differ diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.odg b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.odg new file mode 100644 index 0000000000..5dc20b4ba4 Binary files /dev/null and b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.odg differ diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.png b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.png new file mode 100644 index 0000000000..7ea79213ec Binary files /dev/null and b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.png differ diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.odg b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.odg new file mode 100644 index 0000000000..c79b7c3311 Binary files /dev/null and b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.odg differ diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.png b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.png new file mode 100644 index 0000000000..8f0f145085 Binary files /dev/null and b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.png differ diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.odg b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.odg new file mode 100644 index 0000000000..3f47370f14 Binary files /dev/null and b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.odg differ diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.png b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.png new file mode 100644 index 0000000000..6b1c0f7c36 Binary files /dev/null and b/docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.png differ