天天看點

OAuth 2 Developers Guide

The OAuth 2.0 provider mechanism is responsible for exposing OAuth 2.0 protected resources. The configuration involves establishing the OAuth 2.0 clients that can access its protected resources independently or on behalf of a user. The provider does this by managing and verifying the OAuth 2.0 tokens used to access the protected resources. Where applicable, the provider must also supply an interface for the user to confirm that a client can be granted access to the protected resources (i.e. a confirmation page).

The provider role in OAuth 2.0 is actually split between Authorization Service and Resource Service, and while these sometimes reside in the same application, with Spring Security OAuth you have the option to split them across two applications, and also to have multiple Resource Services that share an Authorization Service. The requests for the tokens are handled by Spring MVC controller endpoints, and access to protected resources is handled by standard Spring Security request filters. The following endpoints are required in the Spring Security filter chain in order to implement OAuth 2.0 Authorization Server:

The following filter is required to implement an OAuth 2.0 Resource Server:

As you configure the Authorization Server, you have to consider the grant type that the client is to use to obtain an access token from the end-user (e.g. authorization code, user credentials, refresh token). The configuration of the server is used to provide implementations of the client details service and token services and to enable or disable certain aspects of the mechanism globally. Note, however, that each client can be configured specifically with permissions to be able to use certain authorization mechanisms and access grants. I.e. just because your provider is configured to support the "client credentials" grant type, doesn't mean that a specific client is authorized to use that grant type.

The <code>@EnableAuthorizationServer</code> annotation is used to configure the OAuth 2.0 Authorization Server mechanism, together with any <code>@Beans</code> that implement <code>AuthorizationServerConfigurer</code> (there is a handy adapter implementation with empty methods). The following features are delegated to separate configurers that are created by Spring and passed into the <code>AuthorizationServerConfigurer</code>:

<code>ClientDetailsServiceConfigurer</code>: a configurer that defines the client details service. Client details can be initialized, or you can just refer to an existing store.

<code>AuthorizationServerSecurityConfigurer</code>: defines the security constraints on the token endpoint.

<code>AuthorizationServerEndpointsConfigurer</code>: defines the authorization and token endpoints and the token services.

An important aspect of the provider configuration is the way that an authorization code is supplied to an OAuth client (in the authorization code grant). A authorization code is obtained by the OAuth client by directing the end-user to an authorization page where the user can enter her credentials, resulting in a redirection from the provider authorization server back to the OAuth client with the authorization code. Examples of this are elaborated in the OAuth 2 specification.

In XML there is an <code>&lt;authorization-server/&gt;</code> element that is used in a similar way to configure the OAuth 2.0 Authorization Server.

The <code>ClientDetailsServiceConfigurer</code> (a callback from your <code>AuthorizationServerConfigurer</code>) can be used to define an in-memory or JDBC implementation of the client details service. Important attributes of a client are

<code>clientId</code>: (required) the client id.

<code>secret</code>: (required for trusted clients) the client secret, if any.

<code>scope</code>: The scope to which the client is limited. If scope is undefined or empty (the default) the client is not limited by scope.

<code>authorizedGrantTypes</code>: Grant types that are authorized for the client to use. Default value is empty.

<code>authorities</code>: Authorities that are granted to the client (regular Spring Security authorities).

Client details can be updated in a running application by access the underlying store directly (e.g. database tables in the case of <code>JdbcClientDetailsService</code>) or through the <code>ClientDetailsManager</code> interface (which both implementations of <code>ClientDetailsService</code> also implement).

When an access token is created, the authentication must be stored so that resources accepting the access token can reference it later.

The access token is used to load the authentication that was used to authorize its creation.

The default <code>InMemoryTokenStore</code> is perfectly fine for a single server (i.e. low traffic and no hot swap to a backup server in the case of failure). Most projects can start here, and maybe operate this way in development mode, to make it easy to start a server with no dependencies.

To use JWT tokens you need a <code>JwtTokenStore</code> in your Authorization Server. The Resource Server also needs to be able to decode the tokens so the <code>JwtTokenStore</code> has a dependency on a <code>JwtAccessTokenConverter</code>, and the same implementation is needed by both the Authorization Server and the Resource Server. The tokens are signed by default, and the Resource Server also has to be able to verify the signature, so it either needs the same symmetric (signing) key as the Authorization Server (shared secret, or symmetric key), or it needs the public key (verifier key) that matches the private key (signing key) in the Authorization Server (public-private or asymmetric key). The public key (if available) is exposed by the Authorization Server on the <code>/oauth/token_key</code> endpoint, which is secure by default with access rule "denyAll()". You can open it up by injecting a standard SpEL expression into the <code>AuthorizationServerSecurityConfigurer</code> (e.g. "permitAll()" is probably adequate since it is a public key).

To use the <code>JwtTokenStore</code> you need "spring-security-jwt" on your classpath (you can find it in the same github repository as Spring OAuth but with a different release cycle).

The grant types supported by the <code>AuthorizationEndpoint</code> can be configured via the <code>AuthorizationServerEndpointsConfigurer</code>. By default all grant types are supported except password (see below for details of how to switch it on). The following properties affect grant types:

<code>authenticationManager</code>: password grants are switched on by injecting an <code>AuthenticationManager</code>.

<code>userDetailsService</code>: if you inject a <code>UserDetailsService</code> or if one is configured globally anyway (e.g. in a <code>GlobalAuthenticationManagerConfigurer</code>) then a refresh token grant will contain a check on the user details, to ensure that the account is still active

<code>authorizationCodeServices</code>: defines the authorization code services (instance of <code>AuthorizationCodeServices</code>) for the auth code grant.

<code>implicitGrantService</code>: manages state during the imlpicit grant.

<code>tokenGranter</code>: the <code>TokenGranter</code> (taking full control of the granting and ignoring the other properties above)

In XML grant types are included as child elements of the <code>authorization-server</code>.

The <code>AuthorizationServerEndpointsConfigurer</code> has a <code>pathMapping()</code> method. It takes two arguments:

The default (framework implementation) URL path for the endpoint

The custom path required (starting with a "/")

The URL paths provided by the framework are <code>/oauth/authorize</code> (the authorization endpoint), <code>/oauth/token</code> (the token endpoint), <code>/oauth/confirm_access</code> (user posts approval for grants here), <code>/oauth/error</code> (used to render errors in the authorization server), <code>/oauth/check_token</code> (used by Resource Servers to decode access tokens), and <code>/oauth/token_key</code> (exposes public key for token verification if using JWT tokens).

N.B. the Authorization endpoint <code>/oauth/authorize</code> (or its mapped alternative) should be protected using Spring Security so that it is only accessible to authenticated users. For instance using a standard Spring Security <code>WebSecurityConfigurer</code>:

Note: if your Authorization Server is also a Resource Server then there is another security filter chain with lower priority controlling the API resources. Fo those requests to be protected by access tokens you need their paths not to be matched by the ones in the main user-facing filter chain, so be sure to include a request matcher that picks out only non-API resources in the <code>WebSecurityConfigurer</code> above.

The token endpoint is protected for you by default by Spring OAuth in the <code>@Configuration</code> support using HTTP Basic authentication of the client secret. This is not the case in XML (so it should be protected explicitly).

In XML the <code>&lt;authorization-server/&gt;</code> element has some attributes that can be used to change the default endpoint URLs in a similar way. The <code>/check_token</code> endpoint has to be explicitly enabled (with the <code>check-token-enabled</code> attribute).

Most of the Authorization Server endpoints are used primarily by machines, but there are a couple of resource that need a UI and those are the GET for <code>/oauth/confirm_access</code> and the HTML response from <code>/oauth/error</code>. They are provided using whitelabel implementations in the framework, so most real-world instances of the Authorization Server will want to provide their own so they can control the styling and content. All you need to do is provide a Spring MVC controller with <code>@RequestMappings</code> for those endpoints, and the framework defaults will take a lower priority in the dispatcher. In the <code>/oauth/confirm_access</code> endpoint you can expect an <code>AuthorizationRequest</code> bound to the session carrying all the data needed to seek approval from the user (the default implementation is <code>WhitelabelApprovalEndpoint</code> so look there for a starting point to copy). You can grab all the data from that request and render it however you like, and then all the user needs to do is POST back to <code>/oauth/authorize</code> with information about approving or denying the grant. The request parameters are passed directly to a <code>UserApprovalHandler</code> in the <code>AuthorizationEndpoint</code> so you can interpret the data more or less as you please. The default <code>UserApprovalHandler</code> depends on whether or not you have supplied an <code>ApprovalStore</code> in your <code>AuthorizationServerEndpointsConfigurer</code> (in which case it is an <code>ApprovalStoreUserApprovalHandler</code>) or not (in which case it is a <code>TokenStoreUserApprovalHandler</code>). The standard approval handlers accept the following:

<code>TokenStoreUserApprovalHandler</code>: a simple yes/no decision via <code>user_oauth_approval</code> equals to "true" or "false".

<code>ApprovalStoreUserApprovalHandler</code>: a set of <code>scope.*</code> parameter keys with "*" equal to the scopes being requested. The value of the parameter can be "true" or "approved" (if the user approved the grant) else the user is deemed to have rejected that scope. A grant is successful if at least one scope is approved.

NOTE: don't forget to include CSRF protection in your form that you render for the user. Spring Security is expecting a request parameter called "_csrf" by default (and it provides the value in a request attribute). See the Spring Security user guide for more information on that, or look at the whitelabel implementation for guidance.

Plain HTTP is fine for testing but an Authorization Server should only be used over SSL in production. You can run the app in a secure container or behind a proxy and it should work fine if you set the proxy and the container up correctly (which is nothing to do with OAuth2). You might also want to secure the endpoints using Spring Security <code>requiresChannel()</code> constraints. For the <code>/authorize</code> endpoint is up to you to do that as part of your normal application security. For the <code>/token</code> endpoint there is a flag in the <code>AuthorizationServerEndpointsConfigurer</code> that you can set using the <code>sslOnly()</code> method. In both cases the secure channel setting is optional but will cause Spring Security to redirect to what it thinks is a secure channel if it detects a request on an insecure channel.

Error handling in an Authorization Server uses standard Spring MVC features, namely <code>@ExceptionHandler</code> methods in the endpoints themselves. Users can also provide a <code>WebResponseExceptionTranslator</code> to the endpoints themselves which is the best way to change the content of the responses as opposed to the way they are rendered. The rendering of exceptions delegates to <code>HttpMesssageConverters</code> (which can be added to the MVC configuration) in the case of token endpoint and to the OAuth error view (<code>/oauth/error</code>) in the case of teh authorization endpoint. The whitelabel error endpoint is provided for HTML responses, but users probably need to provide a custom implementation (e.g. just add a <code>@Controller</code> with <code>@RequestMapping("/oauth/error")</code>).

It is sometimes useful to limit the scope of tokens not only by the scopes assigned to the client, but also according to the user's own permissions. If you use a <code>DefaultOAuth2RequestFactory</code> in your <code>AuthorizationEndpoint</code> you can set a flag <code>checkUserScopes=true</code> to restrict permitted scopes to only those that match the user's roles. You can also inject an <code>OAuth2RequestFactory</code> into the <code>TokenEndpoint</code> but that only works (i.e. with password grants) if you also install a <code>TokenEndpointAuthenticationFilter</code> - you just need to add that filter after the HTTP <code>BasicAuthenticationFilter</code>. Of course, you can also implement your own rules for mapping scopes to roles and install your own version of the <code>OAuth2RequestFactory</code>. The <code>AuthorizationServerEndpointsConfigurer</code> allows you to inject a custom <code>OAuth2RequestFactory</code> so you can use that feature to set up a factory if you use <code>@EnableAuthorizationServer</code>.

A Resource Server (can be the same as the Authorization Server or a separate application) serves resources that are protected by the OAuth2 token. Spring OAuth provides a Spring Security authentication filter that implements this protection. You can switch it on with <code>@EnableResourceServer</code> on an <code>@Configuration</code> class, and configure it (as necessary) using a <code>ResourceServerConfigurer</code>. The following features can be configured:

<code>tokenServices</code>: the bean that defines the token services (instance of <code>ResourceServerTokenServices</code>).

<code>resourceId</code>: the id for the resource (optional, but recommended and will be validated by the auth server if present).

other extension points for the resourecs server (e.g. <code>tokenExtractor</code> for extracting the tokens from incoming requests)

request matchers for protected resources (defaults to all)

access rules for protected resources (defaults to plain "authenticated")

other customizations for the protected resources permitted by the <code>HttpSecurity</code> configurer in Spring Security

The <code>@EnableResourceServer</code> annotation adds a filter of type <code>OAuth2AuthenticationProcessingFilter</code> automatically to the Spring Security filter chain.

In XML there is a <code>&lt;resource-server/&gt;</code> element with an <code>id</code> attribute - this is the bean id for a servlet <code>Filter</code> that can then be added manually to the standard Spring Security chain.

Your <code>ResourceServerTokenServices</code> is the other half of a contract with the Authorization Server. If the Resource Server and Authorization Server are in the same application and you use <code>DefaultTokenServices</code> then you don't have to think too hard about this because it implements all the necessary interfaces so it is automatically consistent. If your Resource Server is a separate application then you have to make sure you match the capabilities of the Authorization Server and provide a <code>ResourceServerTokenServices</code> that knows how to decode the tokens correctly. As with the Authorization Server, you can often use the <code>DefaultTokenServices</code> and the choices are mostly expressed through the <code>TokenStore</code> (backend storage or local encoding). An alternative is the <code>RemoteTokenServices</code> which is a Spring OAuth features (not part of the spec) allowing Resource Servers to decode tokens through an HTTP resource on the Authorization Server (<code>/oauth/check_token</code>). <code>RemoteTokenServices</code> are convenient if there is not a huge volume of traffic in the Resource Servers (every request has to be verified with the Authorization Server), or if you can afford to cache the results. To use the <code>/oauth/check_token</code> endpoint you need to expose it by changing its access rule (default is "denyAll()") in the <code>AuthorizationServerSecurityConfigurer</code>, e.g.

In this example we are configuring both the <code>/oauth/check_token</code> endpoint and the <code>/oauth/token_key</code> endpoint (so trusted resources can obtain the public key for JWT verification). These two endpoints are protected by HTTP Basic authentication using client credentials.

The OAuth 2.0 client mechanism is responsible for access the OAuth 2.0 protected resources of other servers. The configuration involves establishing the relevant protected resources to which users might have access. The client may also need to be supplied with mechanisms for storing authorization codes and access tokens for users.

<code>id</code>: The id of the resource. The id is only used by the client to lookup the resource; it's never used in the OAuth protocol. It's also used as the id of the bean.

<code>clientId</code>: The OAuth client id. This is the id by which the OAuth provider identifies your client.

<code>clientSecret</code>: The secret associated with the resource. By default, no secret is empty.

<code>accessTokenUri</code>: The URI of the provider OAuth endpoint that provides the access token.

<code>scope</code>: Comma-separted list of strings specifying the scope of the access to the resource. By default, no scope will be specified.

<code>clientAuthenticationScheme</code>: The scheme used by your client to authenticate to the access token endpoint. Suggested values: "http_basic" and "form". Default: "http_basic". See section 2.1 of the OAuth 2 spec.

Different grant types have different concrete implementations of <code>OAuth2ProtectedResourceDetails</code> (e.g. <code>ClientCredentialsResource</code> for "client_credentials" grant type). For grant types that require user authorization there is a further property:

<code>userAuthorizationUri</code>: The uri to which the user will be redirected if the user is ever needed to authorize access to the resource. Note that this is not always required, depending on which OAuth 2 profiles are supported.

In XML there is a <code>&lt;resource/&gt;</code> element that can be used to create a bean of type <code>OAuth2ProtectedResourceDetails</code>. It has attributes matching all the properties above.

For the OAuth 2.0 client, configuration is simplified using <code>@EnableOAuth2Client</code>. This does 2 things:

Creates a filter bean (with ID <code>oauth2ClientContextFilter</code>) to store the current request and context. In the case of needing to authenticate during a request it manages the redirection to and from the OAuth authentication uri.

Creates a bean of type <code>AccessTokenRequest</code> in request scope. This can be used by authorization code (or implicit) grant clients to keep state related to individual users from colliding.

The filter has to be wired into the application (e.g. using a Servlet initializer or <code>web.xml</code> configuration for a <code>DelegatingFilterProxy</code> with the same name).

The <code>AccessTokenRequest</code> can be used in an <code>OAuth2RestTemplate</code> like this:

The OAuth2ClientContext is placed (for you) in session scope to keep the state for different users separate. Without that you would have to manage the equivalent data structure yourself on the server, mapping incoming requests to users, and associating each user with a separate instance of the <code>OAuth2ClientContext</code>.

In XML there is a <code>&lt;client/&gt;</code> element with an <code>id</code> attribute - this is the bean id for a servlet <code>Filter</code> that must be mapped as in the <code>@Configuration</code>case to a <code>DelegatingFilterProxy</code> (with the same name).

As a general rule, a web application should not use password grants, so avoid using <code>ResourceOwnerPasswordResourceDetails</code> if you can in favour of <code>AuthorizationCodeResourceDetails</code>. If you desparately need password grants to work from a Java client, then use the same mechanism to configure your <code>OAuth2RestTemplate</code> and add the credentials to the <code>AccessTokenRequest</code> (which is a <code>Map</code> and is ephemeral) not the <code>ResourceOwnerPasswordResourceDetails</code> (which is shared between all access tokens).

To use Facebook as an example, there is a Facebook feature in the <code>tonr2</code> application (you need to change the configuration to add your own, valid, client id and secret - they are easy to generate on the Facebook website).

Facebook token responses also contain a non-compliant JSON entry for the expiry time of the token (they use <code>expires</code> instead of <code>expires_in</code>), so if you want to use the expiry time in your application you will have to decode it manually using a custom <code>OAuth2SerializationService</code>.

http://projects.spring.io/spring-security-oauth/docs/oauth2.html