9.9. OAuth¶
Index
- Overview
- How to use
- How to Use Configuration
- Set-up of Spring Security OAuth
- Implementation of authorization code grant
- Implementation of authorization server
- Creation of setting file (Authorization server)
- Defining authorization server
- Client authentication
- Resource owner authentication
- Authorization for each scope
- Customising scope authorization screen
- Error handling at the time of authorization request
- How to share access token with resource server
- Canceling a token (authorization server)
- Transaction control
- Implementation of resource server
- Implementation of client
- Implementation of authorization server
- Implementation of implicit grant
- Implementation of resource owner password credential grant
- Implementation of client credential grant
- How to extend
- Appendix
9.9.1. Overview¶
This section explains the overview of OAuth 2.0 and the method to implement authorization control function as per the specifications of OAuth 2.0, by using Spring Security OAuth which is one of the Spring project.
Tip
Reference of Spring Security OAuth
Spring Security OAuth also provides the functions which are not introduced in this guideline. Refer to OAuth 2 Developers Guide , for the details of Spring Security OAuth.
9.9.1.1. About OAuth 2.0¶
OAuth 2.0 is the authorization framework where access range can be specified for the resources protected on server, when HTTP service is used in third-party application.
OAuth 2.0 is specified as RFC, and it consists of various related technical specifications.
Important specifications of OAuth 2.0 are as follows.
RFC | Overview | Description |
---|---|---|
RFC 6749
|
Technical specifications describing the most fundamental contents of OAuth 2.0, such as terminology and authorization methods.
|
|
RFC 6750
|
Technical specifications related to method of transferring the “Access token without signature” (Hereafter, referred as access token) within servers,
which is used for authorization control described in RFC 6749.
Information about access token is described later.
|
|
RFC 6819
|
Technical specifications about the security requirements that need to be considered while using OAuth 2.0
In these guidelines, the concrete explanation about the study items is omitted.
|
|
RFC 7519
|
Technical specifications related to “JSON Web Token (JWT)” which is a token including JSON that can be signed.
|
|
RFC 7523
|
Technical specifications related to method of using JWT prescribed in RFC 6819 as an access token to be used for authorization control described in RFC 6749.
|
|
RFC 7009
|
Technical specifications related to additional end point invalidating the token.
|
In conventional client-server type authentication model, the third-party application performs authentication by using user authentication information (user name and password etc.) in order to access the resources of the HTTP service.
In other words, user needs to share the authentication information with third party in order to provide the rights to third party applications for accessing the resources; however it has the risks such as unintended access and information leakage by the user, if there are defects and malicious operations in third party application.
On the other hand, in OAuth 2.0, authentication with HTTP service can be directly performed by the user, and third party applications can access resources without sharing authentication information to third parties by issuing information for authenticated requests called as “access token”.
Moreover, more flexible access control is achieved as compared with conventional client-server type authentication model, by enabling the specification of access range (scope) of resources when access token is issued.
9.9.1.2. Architecture of OAuth 2.0¶
This section explains the roles, scope, authorization grant, and protocol flow defined in OAuth 2.0. In OAuth 2.0, concepts such as scope and authorization grant are defined, and the specifications of authorization are prescribed by using these concepts.
9.9.1.2.1. Roles¶
Following 4 roles are defined in OAuth 2.0.
Role name | Description |
---|---|
Resource owner
|
Role permitting the access to protected resources. User (End user) etc.
|
Resource server
|
Server providing the protected resources.
|
Authorization server
|
Server authenticating resource owner and issuing the access token (Information necessary for client to access the resource server).
|
Client
|
Role getting the authorization of resource owner and making the request for protected resources on behalf of resource owner. Web application etc. Client information is registered in the authorization server in-advance and managed by client ID which is unique in the authorization server.
OAuth 2.0 defines following 2 client types based on the ability to maintain confidentiality of client credentials (client authentication information).
(1) Confidential
Client that can maintain the confidentiality of client credential.
(2) Public
Client that cannot maintain the confidentiality of client credentials and cannot perform secure client authentication by using other means like the client executed on the device of resource owner.
Further, in OAuth 2.0, it is designed as client by considering following examples.
(1) Confidential
(2) Public
|
Note
A user agent refers to a web browser etc. used by the resource owner. In this guideline, resource owner (end user) and user agent are described separately to define the locations where end user operations occur. End user operations occur when the resource owner is clearly defined in the guideline.
9.9.1.2.2. Scope¶
The concept “Scope” is being used as a method of controlling access for resources protected in OAuth 2.0.
In response to the request from client, the authorization server can specify the access rights (read, write permissions etc.) for the protected resources, including the scope in access token based on the policy of authorization server or the instructions of resource owner.
9.9.1.2.3. Protocol flow¶
In OAuth 2.0, resources are accessed in the following flow.
Sr. No. | Description |
---|---|
(1)
|
Authorization is requested for the resource owner. As shown in the above figure, the client directly makes a request to the resource owner,
but it is recommended to request through the authorization server.
Request is made through authorization server for the authorization code grant and
implicit grant among the grant types described later.
|
(2)
|
Client receives an authorization grant (described later) as the credentials representing authorization from resource owner.
|
(3)
|
Client requests for access token by presenting own authentication information and authorization grant given by resource owner
to the authorization server.
|
(4)
|
Authorization server authenticates the client and confirms the validity of authorization grant.
It issues the access token if authorization grant is valid.
|
(5)
|
Client requests to the resource protected on the resource server and authenticates with the issued access token.
|
(6)
|
Resource server checks the validity of access token, and accepts the request and responds to the resource if it is valid.
|
Note
In order to simplify the complicated mechanism of signature and token exchange which was not popular in OAuth 1.0, the request handling access token needs to be made by HTTPS communication in OAuth 2.0. (Using HTTPS communication prevents eavesdropping of access token)
9.9.1.2.4. Authorization grant¶
Authorization grant represents authorization from resource owner and it is used when client fetches the access token. In OAuth 2.0, following 4 grant types are defined, however these can be individually extended such as adding the credential items etc.
Client requests the access token to authorization server from any of grant types and accesses the resource server with the fetched access token. Authorization server must define 1 or more supporting grant types, and determine the grant type to be used among these as per the authorization request from client.
Grant type | Description |
---|---|
Authorization code grant
|
In the flow of authorization code grant, the authorization server issues authorization code to client as an intermediary between client and resource owner, and the client issues the access token by passing the authorization code to authorization server.
It is not necessary to share the credentials of resource owner to the client in order to issue the access token by using the authorization code issued by authorization server.
Authorization code grant is used, when confidential client uses OAuth 2.0 similar to a Web application.
|
Implicit grant
|
In the flow of implicit grant, authorization server acts as an intermediary similar to authorization code grant, however it issues the access token directly instead of an authorization code.
Since it is highly responsive and efficient, it is suitable for clients running on the browsers by using script languages.
However, since the access token is encoded in URL, it may get leaked to a resource owner or other applications on the same device.
Further, since the client is not authenticated, a risk of spoofing attack due to unauthorized usage of access token issued to other clients is also likely.
Since a security risk is likely, it should be used only for public clients which require responsiveness and efficiency.
|
Resource owner password credential grant
|
In the flow of resource owner password credential grant, client uses the authentication information of resource owner as authorization grant and issues the access token directly.
Since the credentials of resource owner must be shared with client, a risk of unauthorized usage or leakage of credentials is likely if client’s reliability is low.
Resource owner password credential grant should be used only when there is high reliability between resource owner and client and when other grant types cannot be used.
|
Client credential grant
|
In the flow of client credential grant, authentication information of client is used as an authorization grant, and access token is issued directly.
It is used when client is a resource owner.
|
Warning
As mentioned in OAuth 2.0 authorization grant, grant types other than authorization code grant have security risks and usage constraints. Therefore, using authorization grant on priority must be considered.
9.9.1.2.4.1. Authorization code grant¶
Flow of authorization code grant is as follows.
Sr. No. | Description |
---|---|
(1)
|
Resource owner accesses the resources which are protected on resource server provided by the client through user agent (web browser etc.).
Client redirects the user agent operated by resource owner to authorization end point on authorization server, in order to fetch authorization from resource owner.
In this case, client includes the “Client ID for own identification”, “Scope requested to the resource as an option”, “Redirect URI for returning the user agent after authorization process performed by authorization server” and “State” in the request parameters.
state is a random value associated with user agent and is used to ensure that series of flows is executed by the same user agent (CSRF countermeasures)
|
(2)
|
User agent accesses the authorization end point of authorization server indicated to the client.
Authorization server authenticates the resource owner through user agent and checks the validity of parameters by comparing it with the registered client information, based on the client ID, scope and redirect URI of request parameter.
After confirmation, resource owner is asked to approve/ deny the access request.
|
(3)
|
Resource owner sends approval / denial of access request to the authorization server.
When the resource owner allows access, the authorization server issues an instruction to redirect the user agent
to the client by using redirect URI included in the request parameter.
At that time, authorization code is assigned as a request parameter of redirect URI.
|
(4)
|
User agent accesses the redirect URI with assigned authorization code.
When processing of client is completed, the response is returned to resource owner.
|
(5)
|
Client sends the authorization code to the token end point on authorization server, in order to request the access token.
Token end point of authorization server authenticates the client and verifies the validity of authorization code, and issues the access token and optional refresh token if it is valid.
Refresh token is used to issue new access token when the access token is disabled or expired.
|
9.9.1.2.4.2. Implicit grant¶
Flow of implicit grant is as follows.
Sr. No. | Description |
---|---|
(1)
|
Resource owner accesses the pages required for resources which are protected on resource server provided by the client through user agent.
Client gives access to authorization end point on authorization server for the user agent of resource owner, in order to fetch authorization from resource owner and issue the access token.
In this case, client includes the “Client ID for own identification”, “Scope requested to the resource as an option”, “Redirect URI for returning the user agent after performing authorization process by authorization server” and “State”, in the request parameters.
state is a random value associated with user agent and is used to ensure that series of flows is executed by the same user agent (CSRF countermeasures)
|
(2)
|
User agent accesses the authorization end point of authorization server indicated to the client.
Authorization server authenticates the resource owner through user agent and checks the validity of parameters by comparing it with the registered client information, based on the client ID, scope and redirect URI of request parameter.
After confirmation, resource owner is asked to approve/ deny the access request.
|
(3)
|
Resource owner sends approval / denial of access request to the authorization server.
When the resource owner allows access, the authorization server issues an instruction to redirect the user agent
to the client by using redirect URI included in the request parameter, and assigns the access token to URL fragment of redirect URI.
Here, “client resource” refers to a static resource which is hosted on Web server etc. separately from client application.
|
(4)
|
User agent sends a request to the client resource in accordance with the redirect instructions.
At that time, information of URL fragment is retained locally and URL fragments are not sent at the time of redirect.
When client resource is accessed, the web page (usually, HTML document including the embedded script) is returned.
User agent executes the script included in web page and extracts the access token from the locally saved URL fragment.
|
(5)
|
User agent passes the access token to client.
|
9.9.1.2.4.3. Resource owner password credential grant¶
Flow of resource owner password credential grant is as follows.
Sr. No. | Description |
---|---|
(1)
|
Resource owner provides the credentials (user name, password) to the client.
|
(2)
|
Client accesses the token end point of authorization server, in order to request the access token.
In this case, client includes the credentials specified from resource owner and the scope requested to resource, in the request parameter.
|
(3)
|
Token end point of authorization server authenticates the client and verifies the credentials of resource owner. If it is valid, it issues the access token.
|
9.9.1.2.4.4. Client credential grant¶
Flow of client credential grant is as follows.
Sr. No. | Description |
---|---|
(1)
|
Client accesses the token end point of authorization server, in order to request the access token.
In this case, client requests for access token including the client’s own credentials.
|
(2)
|
Token end point of authorization server authenticates the client and issues the access token when authentication is successful.
|
9.9.1.2.5. Life cycle of access token¶
Access token is issued by authorization server, by confirming the validity of authorization grant presented by the client. For an issued access token, the scope is assigned based on the policy of authority server or the instructions of resource owner and access is retained for the protected resource. Expiry period is set when the access token is issued, and if it expires, the access rights of protected resource are invalidated.
Flow from issue of access token till its invalidation is as follows.
Sr. No. | Description |
---|---|
(1)
|
Client presents the authorization grant and requests for access token.
|
(2)
|
Authorization server confirms the authorization grant presented by client and issues the access token.
|
(3)
|
Client presents the access token and requests for resource protected on resource server.
|
(4)
|
Resource server verifies the validity of access token presented by client and if it is valid, performs processing for the resource protected on resource server.
|
(5)
|
Client presents the access token (expired), and requests for the resource protected on resource server.
|
(6)
|
Resource server verifies the validity of access token presented by client, and returns error if the access token has expired.
|
Flow of re-issuing the access token as per the refresh token is as follows.
Sr. No. | Description |
---|---|
(1)
|
Client presents the authorization grant and requests for access token.
|
(2)
|
Authorization server confirms the authorization grant presented by client and issues the access token and refresh token.
|
(3)
|
Client presents the access token and requests for resource protected on resource server.
|
(4)
|
Resource server verifies the validity of access token presented by client, and if it is valid, it processes the resource protected on resource server.
|
(5)
|
Client presents the access token (expired), and requests for the resource protected on resource server.
|
(6)
|
Resource server verifies the validity of access token presented by client, and returns error if the access token has expired.
|
(7)
|
When an “access token expired” error is returned from the resource server,
the client requests a new access token by submitting a refresh token (expired).
|
(8)
|
Authorization server verifies the validity of refresh token presented by client and if it is valid, issues the access token and optional refresh token.
|
When the refresh token expires, the authorization grant is submitted again to the authorization server.
Sr. No. | Description |
---|---|
(1)
|
When the client submits an expired access token and a “access token expired” error is returned from resource server,
the client requests a new access token by submitting a refresh token (expired).
|
(2)
|
Authorization server verifies the validity of refresh token submitted by the client and returns an error when the refresh token has expired.
|
(3)
|
When refresh token expired error is returned from authorization server, the client submits the authorization grant again and requests an access token.
|
(4)
|
Authorization server verifies the authorization grant submitted by the client and issues an access token and a refresh token.
|
9.9.1.3. Architecture of Spring Security OAuth¶
Spring Security OAuth is a library that provides functions necessary while building 3 roles such as Authorization Server, Resource Server, and Client as Spring applications among the roles defined in OAuth 2.0. Spring Security OAuth is the technique that works by linking with the functions provided by Spring Framework (Spring MVC) and Spring Security, and it can build the authorization server, resource server and client by appropriate configuration (Bean definition) of default package provided by Spring Security OAuth. Further, many extension points are provided similar to Spring Framework and Spring Security in order to incorporate the requirements that cannot be implemented in default package provided by Spring Security OAuth.
Further, refer to Authentication and Authorization for the details, when the functions provided by Spring Security are to be used for authentication/ authorization of requests within each roles.
Note
Generally, in OAuth 2.0, all applications are not provided by one provider. In many cases, providers provide authorization servers and resource servers, and only the clients that work in coordination with these are implemented. Applications that work in coordination in such cases are not necessarily implemented by using Spring Security OAuth. In the explanation of implementation method, a note will be added as appropriate for the methods to cooperate with applications which are implemented by architecture other than Spring Security OAuth.
Please customize the implementation method introduced in this guideline as appropriate and use it in accordance with the specifications of applications that work in coordination.
When authorization server, resource server, client are built by using Spring Security OAuth, process is performed with the flow given below.
Sr. No. | Description |
---|---|
(1)
|
Resource owner accesses the client through user agent.
Client calls
org.springframework.security.oauth2.client.OAuth2RestTemplate from service.In case of authorization grant accessing the authorization end point, instructions are given to user agent to redirect to authorization end point of authorization server.
|
(2)
|
The user agent accesses authorization endpoint (
org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint ) of authorization server.Authorization endpoint receives the authorization request from the resource owner and issues an authorization code or access token after
displaying a screen asking the resource owner for authorization.
Issued authorization code or access token is passed to the client through user agent by using the redirect.
|
(3)
|
Client accesses
org.springframework.security.oauth2.provider.endpoint.TokenEndpoint which is the token end point of authorization server by using OAuth2RestTemplate .Token end point calls
org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices , validates the authorization grant submitted by the client and issue an access token.Issued access token is passed as a response to the client.
|
(4)
|
Client uses
OAuth2RestTemplate , sets access token received from authorization server in request header to access the resource server.Resource server calls the
org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager and validates access token and information associated with access token via org.springframework.security.oauth2.provider.token.ResourceServerTokenServices .When access token verification is successful, the resource corresponding to request from the client is returned.
|
Note
As mentioned earlier, usage of HTTPS communication at each end point is assumed in OAuth 2.0, however there may be the cases when HTTPS communication is used in SSL accelerator or web server, or when distributed in multiple application servers by using load balancer. When authorization code or redirect URL is embedded in client for linking the access token after authorization by resource owner, the redirect URI indicating the SSL accelerator, Web server and load balancer should be embedded.
Spring (Spring Security OAuth) provides the technique for assembling the redirect URL by using any of the following headers.
- Forwarded header
- X-Forwarded-Host header, X-Forwarded-Port header, X-Forwarded-Proto header
Setting should be done in order to assign the above mentioned headers at SSL accelerator and Web server, load balancer side, so that an appropriate redirect URI can be created in Spring(Spring Security OAuth). If this is not performed, verification of request parameter (redirect URI) performed in the authorization code grant or implicit grant is likely to fail.
Tip
End point provided by Spring Security OAuth is implemented by extending the Spring MVC function. @FrameworkEndpoint
annotation is set in class at end point provided by Spring Security OAuth.
This is because @Controller
annotation does not conflict with the class registered by developer as a component.
Further, end point registered in @FrameworkEndpoint
annotation as component handles the @FrameworkEndpoint
method conflicting with URL as handler method, after reading @RequestMapping
annotation of end point by org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping
which is an extension class of RequestMappingHandlerMapping
.
9.9.1.3.1. Authorization server¶
Authorization server provides functions for authentication of resource owner, obtaining authorization from a resource owner and issuing an access token. Some grant types also provide functions for authentication of resource owner and obtaining authorization from resource owner because it is necessary to ask the resource owner for authorization in order to issue a token.
Authorization server sends client information (information on which client is to be grant authorization, for what scope and which resource) and verifies the access to be authorized and the scope and the resource for the same, and whether the access token can be issued, based on the information.
Note that, it is necessary to register client information in advance, in authorization server, however OAuth 2.0 specifications do not prescribe a registration procedure for client information using authorization server, and Spring Security OAuth also does not provide a registration procedure function for client information. Hence, when you want to provide an interface for registering client information in the application, it must be implemented independently.
9.9.1.3.1.1. Authentication of client¶
Each end point (will be explained later) provides a function which performs authentication of the client included in the request based on the client information managed by authorization server.
9.9.1.3.1.2. Authentication of resource owner¶
When authorization is to be fetched from resource owner (will be explained later), authorization server must also authenticate the resource owner. This function is implemented by using authentication function provided by Spring Security.
For details, refer Authentication.
9.9.1.3.1.3. Fetching authorization from resource owner¶
Spring Security OAuth provides an authorization end point (AuthorizationEndpoint
) by using Spring MVC function.
The client specifies the scope to be used while sending an authorization request. The scope specified by resource owner is authorized or the scope for the client is authorized by authorization server when it matches with the scope assigned to the client, registered in advance, in authorization server. While authorizing the client, the authorization by using Spring Security role explained in section of Authorization can also be used.
Flow for accessing authorization end points is shown below.
Sr. No. | Description |
---|---|
(1)
|
Authorization server process is executed when the authorization end point (
/oauth/authorize ) of authorization server is accessed
by user agent in accordance with the instructions from the client. |
(2)
|
In the
AuthorizationEndpoint which processes URL for accessing (/oauth/authorize ),
call org.springframework.security.oauth2.provider.ClientDetailsService method, and verify request parameter after fetching the client information registered in advance. |
(3)
|
Call
org.springframework.security.oauth2.provider.approval.UserApprovalHandler method and check whether authorization of scope is registered for client |
(4)
|
If the authorization is not registered,
UserApprovalHandler fetches an element of the screen asking for authorization of resource owner (authorization screen)
and forwards to URL (/oauth/confirm_access ) which displays authorization screen.At that time, the scope to be inquired is the intersection of the request parameter and the client information registered in advance.
and is linked by using
@SessionAttributes of Spring MVC. |
(5)
|
In the controller which processes URL for forwarding (
/oauth/confirm_access ),
authorization information is displayed to resource owner via user agent. |
(6)
|
When the authorization is performed by screen operations of resource owner, the authorization end point (
/oauth/authorize ) is accessed again.At this time,
user_oauth_approval is assigned as a request parameter. |
(7)
|
AuthorizationEndpoint assigns user_oauth_approval parameter and if accessed, calls method of UserApprovalHandler and registers authorization information. |
(8)
|
Manage authorization status by
org.springframework.security.oauth2.provider.approval.ApprovalStore in org.springframework.security.oauth2.provider.approval.ApprovalStoreUserApprovalHandler which is package of UserApprovalHandler .When authorization is performed by resource owner, call
ApprovalStore method and register specified information. |
Note
As described earlier, the scope to be inquired is the intersection of scope registered previously in authorization server and scope specified by request parameter at the time of authorization request by client. For example, when the scope of READ and CREATE is specified by request parameters for the client assigning the scopes such as READ, CREATE, DELETE on authorization server, then the scope such as READ, CREATE which is the intersection of (READ,CREATE,DELETE) and (READ,CREATE) can be assigned. When the scope that is not assigned to client on authorization server is specified by request parameter, an error occurs and access is denied.
9.9.1.3.1.3.1. Unauthorized client error¶
As per 4.1.2.1. Error Response of RFC 6749, if validity of redirect URI and client ID cannot be confirmed by request parameter verification, it is considered as an unauthorized client. In this case, the user agent is not redirected to an unauthorized client and the user agent is directed to error screen provided by authorization server thus notifying the resource owner about unauthorized client. In this guideline, the error above is called Unauthorized client error.
When an unauthorized client error occurs at the authorization endpoint, error notification is not sent to the client and transition is done to error screen of authorization server. When an error other than unauthorized client error occurs at the authorization endpoint, an error notification is issued by redirecting to client, from authorization server. Flow when an error occurs at the authorization endpoint is shown below.
Sr. No. | Description |
---|---|
A-1
|
When an unauthorized client error occurs, it is forwarded to URL (
/oauth/error ) which displays an error screen. |
A-2
|
In the controller which processes URL for forwarding (
/oauth/error ), error screen is displayed to resource owner through user agent. |
Sr. No. | Description |
---|---|
B-1
|
When an error other than unauthorized client error occurs, an error notification is redirected to the client from authorization server.
Error information is assigned to redirect URI as request parameter.
|
B-2
|
Error information is passed to
org.springframework.security.oauth2.client.token.AccessTokenProviderChain through OAuth2RestTemplate . |
B-3
|
Restore the passed error information to exception and throw it as an exception.
|
9.9.1.3.1.4. Issue of access token¶
Spring Security OAuth provides token end point (TokenEndpoint
) by using Spring MVC function.
Token end point issues an access token by using org.springframework.security.oauth2.provider.token.DefaultTokenServices
- a default implementation of AuthorizationServerTokenServices
.
While issuing an access token, client information registered in authorization server is fetched via ClientDetailsService
and used to verify whether the access token is to be issued.
Flow while accessing a token end point is shown below.
Sr. No. | Description |
---|---|
(1)
|
Token end point process is executed by accessing the token end point (
/oauth/token ) of authorization server by the client |
(2)
|
Call
ClientDetailsService method and check whether the scope of request parameter is registered in client after fetching the previously registered client information. |
(3)
|
When scope is registered, call
org.springframework.security.oauth2.provider.TokenGranter method and issue access token. |
(4)
|
Call
AuthorizationServerTokenServices method which issues an access token, in AbstractTokenGranter which is an implementation of TokenGranter .AbstractTokenGranter which is the base class of TokenGranter implemented as per grant type, and actual process is assigned to each class. |
(5)
|
Call
ClientDetailsService method in DefaultTokenServices which is an implementation of AuthorizationServerTokenServices and fetch expiry date for the access token to be issued, and expiry and whether a refresh token is issued, for a refresh token. |
(6)
|
Issue an access token based on information fetched from
ClientDetailsService , in DefaultTokenServices .Issued access token is registered in
org.springframework.security.oauth2.provider.token.TokenStore method which manages an access token. |
9.9.1.3.2. Resource server¶
Resource server provides a function to verify the validity of access token itself and accessing resources within the scope retained by access token.
Spring Security OAuth implements the verification function of access token, by using authentication and authorization framework of Spring Security.
Specifically, org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter
provided by Spring Security OAuth is used in Authentication Filter, OAuth2AuthenticationManager
- an implementation class of
AuthenticationManager
is used in authentication process and OAuth2AuthenticationEntryPoint
- an implementation class of org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint
is used in response of authentication error.
Refer to Authenticationfor the details of Spring Security.
Configuration of resource server is as follows.
Sr. No. | Description |
---|---|
(1)
|
When client accesses the resource server at the beginning,
OAuth2AuthenticationProcessingFilter is called.Access token is extracted in
OAuth2AuthenticationProcessingFilter through request. |
(2)
|
After extracting the access token, fetch the authentication information associated with access token by calling
ResourceServerTokenServices method in OAuth2AuthenticationManager which is an implementation of AuthenticationManager .Further, verify the access token when authentication information is fetched.
As a method of fetching authentication information associated with access token, a method of fetching the same by using
TokenStore commonly with the authorization server is listed besides the method of inquiring by HTTP to authorization server.How to fetch the authentication information depends on the implementation of
ResourceServerTokenServices . |
(2’)
|
When an authentication error occurs in
OAuth2AuthenticationProcessingFilter , delegate processing to OAuth2AuthenticationEntryPoint - an implementation of AuthenticationEntryPoint and send an error response. |
(3)
|
When
OAuth2AuthenticationProcessingFilter process is completed, following Security Filter(ExceptionTranslationFilter ) is called.For details of
ExceptionTranslationFilter , refer Response at the time of authorization error. |
(3’)
|
When an exception is captured in
ExceptionTranslationFilter , delegate the process to org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler - an implementation of AccessDeniedHandler and send an error response. |
(4)
|
When authentication and authorization of request is successful, a resource corresponding to a request from the client is returned.
|
9.9.1.3.3. Client¶
The client provides a function to guide (redirect) to the authorization server in order to obtain authorization from resource owner and a function to access resource server by fetching an access token.
Spring Security OAuth provides OAuth2RestTemplate
and org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter
which are used to fetch access token and access a resource server.
OAuth2RestTemplate
is a class that extends RestTemplate
and adds functions for OAuth 2.0, and consists of a mechanism to fetch access tokens again by using refresh tokens and a mechanism to throw exception (UserRedirectRequiredException
) when authorization is required from resource owner for fetching access tokens.
Note that, by registering OAuth2ClientContextFilter
in servlet filter, you can guide (redirect) to authorization server by handling UserRedirectRequiredException
occurred in OAuth2RestTemplate
.
Further, in OAuth2RestTemplate
, access token acquired from authorization server along with grant type specified org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails
is saved in org.springframework.security.oauth2.client.DefaultOAuth2ClientContext
which is an implementation of org.springframework.security.oauth2.client.OAuth2ClientContext
.
DefaultOAuth2ClientContext
is defined as Bean of session scope by default, and access token can be shared among multiple requests.
Configuration while using OAuth2RestTemplate
as client function is given below.
Sr. No, | Description |
---|---|
(1)
|
User agent accesses the
Controller so that Service of client can be called.Servlet function (
OAuth2ClientContextFilter ) for capturing UserRedirectRequiredException that may occur in (5) is applied for access along with access to resource server.User agent can access the authorization end point on authorization server, by applying this servlet function when `` UserRedirectRequiredException`` occurs.
|
(2)
|
Call
OAuth2RestTemplate from Service . |
(3)
|
Fetch access token retained by
OAuth2ClientContext .When a valid access token is fetched, move to process (6).
|
(4)
|
When access token is not saved at first access, or when validity period is lapsed, call
org.springframework.security.oauth2.client.token.AccessTokenProvider and fetch the access token. |
(5)
|
Fetch
AccessTokenProvider the access token for grant type defined in OAuth2ProtectedResourceDetails as detailed information of resource.UserRedirectRequiredException is thrown when fetching of authorization code is not completed in org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider which is an implementation of authorization code grant. |
(6)
|
Specify access token fetched in (3) or (5) and access resource server.
When an exception occurs such as “access token expired” (
org.springframework.security.oauth2.client.http.AccessTokenRequiredException ) while accessing, perform subsequent processes from (4) onwards again after initializing retained access token. |
Note
Since implicit grants are generally adopted by clients implemented in JavaScript etc., a method to implement by using JavaScript is introduced in this guideline as well.
9.9.2. How to use¶
Bean definition example and implementation method required for using Spring Security OAuth is described.
9.9.2.1. How to Use Configuration¶
As shown in “Authorization grant”, flow between authorization server and client varies in OAuth 2.0 depending on the grant type. Therefore, it is necessary to perform implementation in line with the grant type supported by the application.
In this guideline, explanation is given about how to implement authorization server, resource server and client for each grant type.
- How to proceed depending on grant type used
- For implementation method for each grant type, an implementation method for authorization code grant is explained at first and the changes from that of authorization code grant are explained for other grant types. While using any grant type, explanation about authorization code grant must be read first and then the explanation for grant type to be used is read.
- How to proceed further according to the application to be created
- Implementation of authorization server, resource server and client is independent and it is not necessary to understand how to implement all the applications. When you want to implement any one application, only the explanation for the application to be implemented should be read.
9.9.2.2. Set-up of Spring Security OAuth¶
Spring Security OAuth is added as a dependent library in order to use the function provided by Spring Security OAuth.
<!-- (1) -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
Sr. No. | Description |
---|---|
(1)
|
Add Spring Security OAuth as a dependent library in
pom.xml of project using Spring Security OAuth.When resource server, authorization server, client are added as different project, respective description should be added.
|
Note
As given in above setting example, since it is assumed that dependency library version is managed by the parent project “terasoluna- gfw- parent”, it is not necessary to specify the version in pom.xml. The dependent library mentioned above is defined in Spring IO Platform used by terasoluna-gfw-parent.
9.9.2.3. Implementation of authorization code grant¶
How to implement authorization server, resource server and client by using authorization code grant is explained.
9.9.2.3.1. Implementation of authorization server¶
Implementation method of authorization server is described.
Authorization server provides end points for “fetching authorization from resource owners” and “issuing access tokens”, by using the function of Spring Security OAuth. Further, while accessing the above-mentioned end point, it is necessary to authenticate the resource owner or client, and these guidelines are implemented by using the technique for authentication/ authorization of Spring Security.
9.9.2.3.1.1. Creation of setting file (Authorization server)¶
oauth2-auth.xml
is created as configuration file for defining in authorization server.oauth2-auth.xml
performs Bean definition of end points for providing the functions of authentication server, and makes security setting for the end points and sets the grant type supporting the authorization server.oauth2-auth.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/security/oauth2
http://www.springframework.org/schema/security/spring-security-oauth2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
Next, settings are described in web.xml
in order to read the created oauth2-auth.xml
.
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:META-INF/spring/applicationContext.xml
classpath*:META-INF/spring/oauth2-auth.xml <!-- (1) -->
classpath*:META-INF/spring/spring-security.xml
</param-value>
</context-param>
Sr. No. | Description |
---|---|
(1)
|
Specify a Bean definition file of OAuth 2.0. It should be described before
spring-security.xml , by considering the case when URL targeted for access control set in oauth2-auth.xml is included in the URL targeted for access control set in spring-security.xml . |
9.9.2.3.1.2. Defining authorization server¶
Definitions of authorization server are added as below.
oauth2-auth.xml
<oauth2:authorization-server> <!-- (1) -->
<oauth2:authorization-code /> <!-- (2) -->
<oauth2:refresh-token /> <!-- (3) -->
</oauth2:authorization-server>
Sr. No. | Description |
---|---|
(1)
|
Use
<oauth2:authorization-server> tag and define authorization server.By using
<oauth2:authorization-server> tag, authorization end point for authorization and
token end point for issuing access token are registered as components.Following path is set for accessing each component.
|
(2)
|
Use
<oauth2:authorization-code /> tag and support authorization code grant. |
(3)
|
Use
<oauth2:refresh-token /> tag and support refresh token. |
Warning
<oauth2:authorization-code />
tag and <oauth2:refresh-token />
tag must be set in the above sequence.
For details, refer When the multiple grant types are supported in authorization server.
Note
End point registered by using <oauth2:authorization-server>
tag and path for forwarding
can be customized respectively.
For details, refer Customization of end point, Customize forwarding destination.
Note
Since setting file above does not set client-details-service-ref
parameter, errors due to grammatical errors are detected by IDE.
Errors are resolved by adding settings to be described later.
Note
Authorization code is used for only the short period from issuing the authorization code till issuing the access token, hence it is managed in in-memory by default. In case of configuration of multiple authorization servers, it should be managed in DB, in order to share authorization codes among multiple servers. When authorization code is to be managed in DB, the following table, consisting of the column having authorization code as primary key and the column having authentication information, is created. Following example explains the DB definitions when PostgreSQL is used.
BeanID of org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices
managing authorization code in DB is specified in authorization-code-services-ref
of <oauth2:authorization-code>
tag, in the configuration file of authorization server.
Data source to be connected to table for authorization code storage is specified in the constructor of JdbcAuthorizationCodeServices
.
Always refer to Transaction control for the precautions while managing the authorization code permanently in DB.
oauth2-auth.xml
<oauth2:authorization-server>
<oauth2:authorization-code authorization-code-services-ref="authorizationCodeServices"/>
<!-- omitted -->
</oauth2:authorization-server>
<bean id="authorizationCodeServices"
class="org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices">
<constructor-arg ref="dataSource"/>
</bean>
9.9.2.3.1.3. Client authentication¶
Clients that have accessed end points should be authenticated, in order to confirm whether they are registered clients. Client is authenticated by verifying the client ID and password passed by client as parameters based on the client information retained by authorization server. Basic authentication is used for authentication.
Spring Security OAuth provides the implementation class of ClientDetailsService
which is an interface for fetching the client information.
Further, org.springframework.security.oauth2.provider.client.BaseClientDetails
class which is implementation class of org.springframework.security.oauth2.provider.ClientDetails
interface is provided as a class retaining the client information.
BaseClientDetails
provides the basic parameters such as client ID and supporting grant type etc. by using OAuth 2.0, and the parameters can be added by extending BaseClientDetails
.
In this case, extension of BaseClientDetails
and creation of ClientDetailsService
implementation class is performed, and client information wherein client name is added as individual parameter is managed by using DB and the implementation method for authentication is explained.
At first, DB is created as below.
Sr. No. | Description |
---|---|
(1)
|
A table to retain client information. client_id is considered as primary key.
Roles of each column are as below.
|
(2)
|
A table to retain the scope information. It is mapped with the client information by considering client_id as the external key.
Scope used for client authorization is retained in scope column. Records are registered only for the scope of client.
|
(3)
|
A table to retain the resource information. It is mapped with the client information by considering client_id as the external key.
Resource ID used in resource server for identifying whether it is a resource that can be accessed by client is retained in resource_id column.
Access to resource is permitted, only when resource ID defined for the resource retained by resource server is included in resource ID registered here.
Records are registered only for the resource IDs accessible by client.
When not even single resource ID is registered, all resources can be accessed, hence precaution must be taken when it is not registered.
|
(4)
|
A table to retain the grant information. It is mapped with the client information by considering client_id as the external key.
Grant to be used by client is retained in authorized_grant_type column.
Records are registered only for the grant count to be used by client
|
(5)
|
A table to retain the redirect URI information. It is mapped with the client information by considering client_id as the external key.
web_server_redirect_uri column retains the URL that redirects user agent after authorization by resource owner.
Redirect URI to be retained is used in whitelist check of redirect URI declared by the client at the time of authorization request.
Records are registered only for the URLs that can be declared by client.
|
A model retaining the client information is created.
Client.java
public class Client implements Serializable{
private String clientId; // (1)
private String clientSecret; // (2)
private String clientName; // (3)
private Integer accessTokenValidity; // (4)
private Integer refreshTokenValidity; // (5)
// Getters and Setters are omitted
}
Sr.No. | Description |
---|---|
(1)
|
A field to retain the client ID identifying the client.
|
(2)
|
A field to retain the password used for client authentication.
|
(3)
|
An extended field to retain the client name which is not provided in Spring Security OAuth.
Not required, since it is an extended field.
|
(4)
|
A field to retain the validity period [seconds] of access token.
|
(5)
|
A field to retain the validity period [seconds] of refresh token.
|
Note
It is necessary to create a Repository class for handling client information, however the explanation is omitted here. For basic implementation methods, refer Implementation of Repository.
Detailed information of client can be extended easily by creating the class inheriting the BaseClientDetails
class.
OAuthClientDetails.java
public class OAuthClientDetails extends BaseClientDetails{
private Client client;
// Getter and Setter are omitted
}
org.springframework.security.oauth2.provider.ClientDetailsService
is an interface for fetching the detailed client information required in authorization process from the data store.
Creation of ClientDetailsService
implementation class is described below.
OAuthClientDetailsService.java
@Service("clientDetailsService") // (1)
@Transactional
public class OAuthClientDetailsService implements ClientDetailsService {
@Inject
ClientRepository clientRepository;
@Override
public ClientDetails loadClientByClientId(String clientId)
throws ClientRegistrationException {
Client client = clientRepository.findClientByClientId(clientId); // (2)
if (client == null) { // (3)
throw new NoSuchClientException("No client with requested id: " + clientId);
}
// (4)
Set<String> clientScopes = clientRepository.findClientScopesByClientId(clientId);
Set<String> clientResources = clientRepository.findClientResourcesByClientId(clientId);
Set<String> clientGrants = clientRepository.findClientGrantsByClientId(clientId);
Set<String> clientRedirectUris = clientRepository.findClientRedirectUrisByClientId(clientId);
// (5)
OAuthClientDetails clientDetails = new OAuthClientDetails();
clientDetails.setClientId(client.getClientId());
clientDetails.setClientSecret(client.getClientSecret());
clientDetails.setAccessTokenValiditySeconds(client.getAccessTokenValidity());
clientDetails.setRefreshTokenValiditySeconds(client.getRefreshTokenValidity());
clientDetails.setResourceIds(clientResources);
clientDetails.setScope(clientScopes);
clientDetails.setAuthorizedGrantTypes(clientGrants);
clientDetails.setRegisteredRedirectUri(clientRedirectUris);
clientDetails.setClient(client);
return clientDetails;
}
}
Sr. No. | Description |
---|---|
(1)
|
Add
@Service annotation to the class level as service in order to use it as target for component-scan.Specify Bean name as
clientDetailsService . |
(2)
|
Client information is fetched from DB.
|
(3)
|
When the client information is not found, an exception of Spring Security OAuth -
org.springframework.security.oauth2.provider.NoSuchClientException is generated.Note that, this process is also called for fetching client information in authorization end point as well, however, it is handled by authorization end point when
NoSuchClientException occurs,and
org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException - a subclass of org.springframework.security.oauth2.common.exceptions.OAuth2Exception is thrown.For handling methods when
OAuth2Exception occurs in authorization end point, refer Error handling at the time of authorization request. |
(4)
|
Fetch information associated with the client.
If the process efficiency is reduced by calling Repository by dividing it for multiple times, fetch collectively in (2).
|
(5)
|
Set various types of fetched information in
OAuthClientDetails field. |
Add settings necessary for client authentication to oauth2-auth.xml
.
oauth2-auth.xml
<oauth2:authorization-server
client-details-service-ref="clientDetailsService"> <!-- (1) -->
<oauth2:authorization-code />
<oauth2:refresh-token />
</oauth2:authorization-server>
<sec:http pattern="/oauth/*token*/**"
authentication-manager-ref="clientAuthenticationManager"> <!-- (2) -->
<sec:http-basic entry-point-ref="oauthAuthenticationEntryPoint" /> <!-- (3) -->
<sec:csrf disabled="true"/> <!-- (4) -->
<sec:intercept-url pattern="/**" access="isAuthenticated()"/> <!-- (5) -->
<sec:access-denied-handler ref="oauth2AccessDeniedHandler"/> <!-- (6) -->
</sec:http>
<sec:authentication-manager alias="clientAuthenticationManager"> <!-- (7) -->
<sec:authentication-provider user-service-ref="clientDetailsUserService" > <!-- (8) -->
<sec:password-encoder ref="passwordEncoder"/> <!-- (9) -->
</sec:authentication-provider>
</sec:authentication-manager>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="typeName" value="Basic" /> <!-- (10) -->
<property name="realmName" value="Realm" /> <!-- (11) -->
</bean>
<bean id="oauth2AccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" /> <!-- (12) -->
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"> <!-- (13) -->
<constructor-arg ref="clientDetailsService" /> <!-- (14) -->
</bean>
Sr. No. | Description |
---|---|
(1)
|
Specify a Bean of
OAuthClientDetailsService in client-details-service-ref attribute.Bean ID to be specified must match with Bean ID specified by implementation class of
ClientDetailsService . |
(2)
|
Specify a location below
/oauth/*token*/ as a target for access control
as an endpoint URL in order to perform security settings for endpoint related to access token operation.
Endpoint URL defined by Spring Security OAuth and its default value are as below.
Specify a Bean of
AuthenticationManager for client authentication defined in (7), in authentication-manager-ref attribute. |
(3)
|
Apply Basic authentication to client authentication.
For details, refer Basic and Digest Authentication.
|
(4)
|
Invalidate CSRF countermeasures for accessing
/oauth/*token*/** .Spring Security OAuth adopts validity check of the request using state parameter, which is recommended as a CSRF countermeasure of OAuth 2.0.
|
(5)
|
Settings which assign rights of access only to authenticated users, for locations under endpoint.
How to specify access policy for Web resource, refer Authorization.
|
(6)
|
Specify a Bean of
OAuth2AccessDeniedHandler in access-denied-handler . Here, a Bean of oauth2AccessDeniedHandler defined in (12) is specified. |
(7)
|
Define a Bean for
AuthenticationManager for authentication of client.AuthenticationManager used in the authentication of resource owner and BeanID of alias must be specified.For authentication of resource owner, refer Resource owner authentication.
|
(8)
|
Specify a Bean of
org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService defined in (13) in user-service-ref attribute of sec:authentication-provider -ref``. |
(9)
|
Specify a Bean of
PasswordEncoder used in password hashing, which is used in client authentication.For details of password hashing, refer Password hashing.
|
(10)
|
When the client authentication fails, specify the scheme of client authentication submitted to the client in the response header.
|
(11)
|
When client authentication fails, specify the realm of client authentication submitted to the client by response header.
Set when you want to customize realm of client authentication.
When it is not specified, a default value
oauth is set. |
(12)
|
Define
AccessDeniedHandler for OAuth 2.0 provided by Spring Security OAuth.OAuth2AccessDeniedHandler handles exception occuring at the time of authorization error and sends error response. |
(13)
|
Define a Bean for
ClientDetailsUserDetailsService - an implementation class of UserDetailsService interface.UserDetailsService used for resource owner authentication and alias BeanID must be specified. |
(14)
|
Specify a Bean of
OAuthClientDetailsService which fetches client information from database, in constructor argument.BeanID to be specified must match with BeanID specified by the implementation class of
ClientDetailsService . |
9.9.2.3.1.4. Resource owner authentication¶
When an authorization code grant is used for fetching an access token, a resource owner must be authenticated by a method like providing a login screen among others.
For details of Spring Security, refer Authentication and Authorization.
Definition examples of access policies which include authorization endpoint, a forward destination while fetching authorization from resource owner and a forward destination when an exception occurs at authorization endpoint are shown below.
spring-security.xml
<sec:http authentication-manager-ref="authenticationManager"> <!-- (1) -->
<sec:form-login login-page="/login"
authentication-failure-url="/login?error=true"
login-processing-url="/login" />
<sec:logout logout-url="/logout"
logout-success-url="/"
delete-cookies="JSESSIONID" />
<sec:access-denied-handler ref="accessDeniedHandler"/>
<sec:custom-filter ref="userIdMDCPutFilter" after="ANONYMOUS_FILTER"/>
<sec:session-management />
<sec:intercept-url pattern="/login/**" access="permitAll" />
<sec:intercept-url pattern="/oauth/**" access="isAuthenticated()" /> <!-- (2) -->
<!-- omitted -->
</sec:http>
<sec:authentication-manager alias="authenticationManager"> <!-- (3) -->
<sec:authentication-provider
user-service-ref="userDetailsService">
<sec:password-encoder ref="passwordEncoder" />
</sec:authentication-provider>
</sec:authentication-manager>
<bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
Sr. No. | Description |
---|---|
(1)
|
Apply form authentication to authorization server and specify
authenticationManager defined in (3), in authentication-manager-ref attribute.Since
AuthenticationManager is defined in oauth2-auth.xml as well, BeanID of alias must be specified. |
(2)
|
Specify path (
/oauth/ ) consisting of following so as to be accessible by authenticated users.
|
(3)
|
Define a Bean for
authenticationManager for resource owner authentication. |
9.9.2.3.1.5. Authorization for each scope¶
A setup method wherein each scope is authorized individually instead of authorizing the requested scope together while fetching the authorization from resource owner, is explained.
Authorization information must be controlled in a DB for performing perpetual management to prevent loss of authorization information at the time of restarting authorization server or to share authorization information across multiple authorization servers. Following DB is created for storing authorization information for each scope. The example below explains DB definition when PostgreSQL is used.
Sr. No. | Description |
---|---|
(1)
|
A table to retain authorization information. userId, clientId and scope are used as primary keys.
Role of each column is as below.
|
Fetch authorization for each scope of resource owner and apply settings to store the same in DB and control.
Implementation example is as below.
oauth2-auth.xml
<oauth2:authorization-server
client-details-service-ref="clientDetailsService"
user-approval-handler-ref="userApprovalHandler"> <!-- (1) -->
<!-- omitted -->
</oauth2:authorization-server>
<!-- omitted -->
<bean id="userApprovalHandler"
class="org.springframework.security.oauth2.provider.approval.ApprovalStoreUserApprovalHandler"> <!-- (2) -->
<property name="clientDetailsService" ref="clientDetailsService"/>
<property name="approvalStore" ref="approvalStore"/>
<property name="requestFactory" ref="requestFactory"/>
<property name="approvalExpiryInSeconds" value="3200" />
</bean>
<bean id="approvalStore"
class="org.springframework.security.oauth2.provider.approval.JdbcApprovalStore"> <!-- (3) -->
<constructor-arg ref="dataSource"/>
</bean>
<bean id="requestFactory"
class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
<constructor-arg ref="clientDetailsService"/>
</bean>
Sr. No. | Description |
---|---|
(1)
|
Specify a Bean of
ApprovalStoreUserApprovalHandler defined in (2), in user-approval-handler-ref as UserApprovalHandler which performs authorization process of scope. |
(2)
|
Define a Bean for
ApprovalStoreUserApprovalHandler which performs authorization process of scope.Specify a Bean of
org.springframework.security.oauth2.provider.approval.JdbcApprovalStore defined in (3), in approvalStore property which manages authorization results of resource owner.Specify a Bean of
OAuthClientDetailsService in clientDetailsService property which fetches client information to be used in authorization process of scope.Specify a Bean of
org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory in requestFactory property.Bean set in
requestFactory property is not used by ApprovalStoreUserApprovalHandler , however, since an error occurs at the time of Bean generation of ApprovalStoreUserApprovalHandler when the setting is not applied, settings in requestFactory property is necessary.When validity period [Seconds] of authorization information is to be specified, validity period [Seconds] is set in
approvalExpiryInSeconds property. If any setting is not applied, the authorization information will remain valid a month from authorization. |
(3)
|
Define a Bean for
JdbcApprovalStore which manages authorization information in DB.Specify a database in the constructor to connect to the table for storing authorization information.
For setting methods of data source, refer Datasource settings.
Warning For precautions on perpetual management of authorization information in DB, always refer Transaction control. Note When perpetual management is not required to be performed for authorization information and is to be managed by in-memory instead of a DB, a Bean is to be defined for |
9.9.2.3.1.6. Customising scope authorization screen¶
When the scope authorization screen is to be customised, it can be done by creating controller and JSP. An example is explained below wherein the scope authorization screen is customised.
When an endpoint which requests authorization to resource owner is to be called, it is forwarded to /oauth/confirm_access
.
A controller which handles /oauth/confirm_access
is created.
OAuth2ApprovalController.java
@Controller
public class OAuth2ApprovalController {
@RequestMapping("/oauth/confirm_access") // (1)
public String confirmAccess() {
// omitted
return "approval/oauthConfirm";
}
}
Sr. No. | Description |
---|---|
(1)
|
Use
@RequestMapping annotation and perform mapping as a method for accessing /oauth/confirm_access . |
Next, a JSP of scope authorization screen is created.
Since scope for authorization is registered as scopes
key and request parameter is registered as authorizationRequest
,
scope authorization screen is displayed as it is.
oauthConfirm.jsp
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<body>
<div id="wrapper">
<h1>OAuth Approval</h1>
<p>Do you authorize ${f:h(authorizationRequest.clientId)} to access your protected resources?</p> <!-- (1) -->
<form id="confirmationForm" name='confirmationForm' action="${pageContext.request.contextPath}/oauth/authorize" method="post">
<c:forEach var="scope" items="${scopes}"> <!-- (2) -->
<li>
${f:h(scope.key)}
<input type="radio" name="${f:h(scope.key)}" value="true"/>Approve
<input type="radio" name="${f:h(scope.key)}" value="false"/>Deny
</li>
</c:forEach>
<input name="user_oauth_approval" value="true" type="hidden"/> <!-- (3) -->
<sec:csrfInput /> <!-- (4) -->
<label>
<input name="authorize" value="Authorize" type="submit"/>
</label>
</form>
</div>
</body>
Sr. No. | Description |
---|---|
(1)
|
Client ID is displayed on the screen.
Type of
authorizationRequest is org.springframework.security.oauth2.provider.AuthorizationRequest and client ID is output by specifying authorizationRequest.clientId . |
(2)
|
A radio button is output for specifying authorization allowed/denied for each scope. Since target scope is included in
scopes , specify scopes in items .scopes type is LinkedHashMap and retains authorization allowed/denied information for the scope, using scope name as a key. |
(3)
|
Spring Security OAuth assigns
user_oauth_approval to the request parameter by embedding user_oauth_approval as a hidden item.user_oauth_approval assigned to the request parameter can be used for executing a method which performs scope authorization of authorization endpoint. |
(4)
|
Specify
<sec:csrfInput /> element in <form> element of HTML in order to deliver CSRF token. |
9.9.2.3.1.7. Error handling at the time of authorization request¶
When an unauthorized client error (errors related to security such as client not found, redirect URI check error) occurs, OAuth2Exception
provided by Spring Security OAuth is thrown and request is forwarded to /oauth/error
.
Therefore, a controller which handles /oauth/error
must be created.
For details of unauthorized client error, refer Unauthorized client error.
Implementation example of controller is shown below.
OAuth2ErrorController.java
@Controller
public class OAuth2ErrorController {
@RequestMapping("/oauth/error") // (1)
public String handleError() {
// omitted
return "common/error/oauthError";
}
}
Sr. No. | Description |
---|---|
(1)
|
Use
@RequestMapping annotation and perform mapping as a method for accessing /oauth/error . |
Next, a JSP of error screen thus displayed is created.
Since details of exceptions occurred in authorization end point are registered in the error
- attribute name of model, exception details are displayed on the screen using the same.
oauthError.jsp
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OAuth Error!</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/app/css/styles.css">
</head>
<body>
<div id="wrapper">
<h1>OAuth Error!</h1>
<c:if test="${not empty error}">
<p>${f:h(error.oAuth2ErrorCode)}</p> <!-- (1) -->
<p>${f:h(error.message)}</p> <!-- (2) -->
</c:if>
<br>
</div>
</body>
</html>
Sr. No. | Description |
---|---|
(1)
|
Output error code of OAuth 2.0 set in exception.
Error code consists of values like
invalid_request and invalid_client . |
(2)
|
Output error message set in exception.
|
Note
When an error occurred at authorization end point is other than unauthorized client error, error is notified to the client by redirecting to the controller for calling the client. Error information while notifying the error is assigned to redirect URI as request parameter. For error handling, refer Error handling while accessing authorization end point.
Tip
If the application supports Internet Explorer/Microsoft Edge, size of HTML generated as a response to error screen must be taken into consideration.
In Internet Explorer/Microsoft Edge, if the size of HTML sent as a response is less than specified value, a simple message offered by Internet Explorer/Microsoft Edge is displayed instead of error screen provided by the application.
For the reference, refer “Friendly HTTP Error Pages” for detail conditions of Internet Explorer.
9.9.2.3.1.9. Canceling a token (authorization server)¶
How to implement cancellation of issued access token is explained.
Access token can be cancelled by calling revokeToken
method of a class
which implements ConsumerTokenService
interface.
DefaultTokenServices
class implements org.springframework.security.oauth2.provider.token.ConsumerTokenServices
interface.
Authorization information can be deleted as well at the time of cancellation of access token. When authorization request is sent without deleting authorization information after cancellation of access token, authorization information at the time of previous authorization request is reused. Authorization information at the time of previous authorization request can be reused when validity period of authorization information is valid and entire authorization request scope is authorized.
Implementation example is given below.
An interface of service class which cancels a token and implementation class are created.
RevokeTokenService.java
public interface RevokeTokenService {
ResponseEntity<Map<String,String>> revokeToken(String tokenValue, String clientId);
}
RevokeTokenServiceImpl.java
@Service
@Transactional
public class RevokeTokenServiceImpl implements RevokeTokenService {
@Inject
ConsumerTokenServices consumerService; // (1)
@Inject
TokenStore tokenStore; // (2)
@Inject
ApprovalStore approvalStore; // (3)
@Inject
JodaTimeDateFactory dateFactory;
public String revokeToken(String tokenValue, String clientId){ // (4)
// (5)
OAuth2Authentication authentication = tokenStore.readAuthentication(tokenValue);
Map<String,String> map = new HashMap<>();
if (authentication != null) {
if (clientId.equals(authentication.getOAuth2Request().getClientId())) { // (6)
// (7)
Authentication user = authentication.getUserAuthentication();
if (user != null) {
Collection<Approval> approvals = new ArrayList<>();
for (String scope : authentication.getOAuth2Request().getScope()) {
approvals.add(
new Approval(user.getName(), clientId, scope, dateFactory.newDate(), ApprovalStatus.APPROVED));
}
approvalStore.revokeApprovals(approvals);
}
consumerService.revokeToken(tokenValue); // (8)
return ResponseEntity.ok().body(map);
} else {
map.put("error", "invalid_client");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(map);
}
} else {
map.put("error", "invalid_request");
return ResponseEntity.badRequest().body(map);
}
}
}
Sr. No. | Description |
---|---|
(1)
|
Inject a Bean of
ConsumerTokenService interface which cancels access tokens. |
(2)
|
Inject a Bean of
TokenStore used to fetch authentication information while issuing an access token. |
(3)
|
Inject a Bean of
ApprovalStore used to fetch authorization information while issuing an access token.It is not required when authorization information is not to be deleted while cancelling an access token.
|
(4)
|
Receive value of access token to be cancelled and client ID used for checking the client as parameters.
|
(5)
|
Call
readAuthentication method of TokenStore and fetch authentication information while issuing an access token.Token is deleted only when authentication information can be successfully fetched.
|
(6)
|
Fetch client ID used at the time of issuing access token, from authentication information and check whether it matches with client ID of request parameter.
Delete access token only when it matches with client ID at the time of issuing an access token.
|
(7)
|
Fetch authentication information of resource owner at the time of issuing an access token.
When authentication information of resource owner could be fetched, call
revokeApprovals method of TokenStore and delete authorization information.Since authentication information for resource owner does not exist while using client credential grant, the parameters to be passed to
revokeApprovals method cannot be generated.Therefore, authorization information cannot be deleted when it is not possible to fetch authentication information of resource owner.
This process is not required when authorization information is not deleted while cancelling the access token.
|
(8)
|
Call
revokeToken method of ConsumerTokenService and delete access token and refresh token associated with access token. |
A controller which receives a cancellation request of token is created.
TokenRevocationRestController.java
@RestController
@RequestMapping("oauth")
public class TokenRevocationRestController {
@Inject
RevokeTokenService revokeTokenService;
@RequestMapping(value = "tokens/revoke", method = RequestMethod.POST) // (1)
public ResponseEntity<Map<String,String>> revoke(@RequestParam("token") String tokenValue,
@AuthenticationPrincipal UserDetails user){
// (2)
String clientId = user.getUsername();
ResponseEntity<Map<String,String>> result = revokeTokenService.revokeToken(tokenValue, clientId); // (3)
return result;
}
}
Sr. no. | Description |
---|---|
(1)
|
Use
@RequestMapping annotation and map as a method for accessing "/oauth/tokens/revoke" .The path specified here must apply Basic authentication and disable CSRF countermeasures, similar to settings performed in Client authentication.
|
(2)
|
Fetch client ID used while checking the cancellation of token, from authentication information generated in Basic authentication.
|
(3)
|
Call
RevokeTokenService and delete a token.Pass value of access token received as the request parameter and client ID received from authentication information as parameters.
|
Tip
RFC 7009 states that token_type_hint
can be arbitrarily assigned as a request parameter.
token_type_hint
is a hint to determine whether to delete access token or refresh token.
ConsumerTokenService
offered by Spring Security OAuth is not used in the implementation example above in order to delete both access token and refresh token by passing the access token.
Note
The client which requests deletion of token to the authorization server should also delete a token retained by the client after deletion of authorization server. For deletion of token of client server, refer Cancelling a token (Client server).
9.9.2.3.1.10. Transaction control¶
It explains precautions of transaction control in authorization server.
When the information handled by Spring Security OAuth (authorization code, authorization information, token) is to be managed in DB, transaction control must be considered.
@Transactional
annotation is assigned in DefaultTokenServices
which issues access token and refresh token, and transaction control is performed, however, @Transactional
annotation
is not assigned in org.springframework.security.oauth2.provider.code.AuthorizationCodeServices
which handles authorization code and UserApprovalHandler
which handles authorization information
and hence transaction control is not performed.
Hence, when autocommit of connection fetched from DataSource
is disabled, transaction control settings are mandatory since information to be managed is not registered in DB.
Further, even when autocommit is enabled, transaction control must be performed to ensure data consistency.
An example of transaction control when authorization code and authorization information are managed in DB is shown below.
oauth2-auth.xml
<!-- omitted -->
<tx:advice id="oauthTransactionAdvice"> <!-- (1) -->
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="authorizationOperation"
expression="execution(* org.springframework.security.oauth2.provider.code.AuthorizationCodeServices.*(..))"/> <!-- (2) -->
<aop:pointcut id="approvalOperation"
expression="execution(* org.springframework.security.oauth2.provider.approval.UserApprovalHandler.*(..))"/> <!-- (3) -->
<aop:advisor pointcut-ref="authorizationOperation" advice-ref="oauthTransactionAdvice"/>
<aop:advisor pointcut-ref="approvalOperation" advice-ref="oauthTransactionAdvice"/>
</aop:config>
Sr. No. | Description |
---|---|
(1)
|
Set
tx:advice to manage transaction using AOP.Namespace and schema of
tx are added to use this tag. |
(2)
|
Use AOP and set transaction boundary in each method which operates authorization code.
Namespace and schema of
aop are added to use this tag. |
(3)
|
Use AOP and set transaction boundary in each method which operates authorization information.
|
9.9.2.3.2. Implementation of resource server¶
How to implement a resource server is explained.
Resource server provides access token verification and authorization control for resources using Spring Security OAuth function.
Here, how to implement a resource server is explained by using implementation example wherein authorization is set for REST API of TODO resource.
9.9.2.3.2.1. Creating a setup file (resource server)¶
A new Bean definition file for OAuth 2.0 is created while implementing a resource server.
Here, it is oauth2-resource.xml
.
Following settings are added to oauth2-resource.xml
.
oauth2-resource.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/security/oauth2
http://www.springframework.org/schema/security/spring-security-oauth2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<sec:http pattern="/api/v1/todos/**" create-session="stateless"
entry-point-ref="oauth2AuthenticationEntryPoint"> <!-- (1) -->
<sec:access-denied-handler ref="oauth2AccessDeniedHandler"/> <!-- (2) -->
<sec:csrf disabled="true"/> <!-- (3) -->
<sec:custom-filter ref="oauth2AuthenticationFilter"
before="PRE_AUTH_FILTER" /> <!-- (4) -->
<sec:custom-filter ref="userIdMDCPutFilter" after="ANONYMOUS_FILTER"/>
</sec:http>
<bean id="oauth2AccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" /> <!-- (5) -->
<bean id="oauth2AuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" /> <!-- (6) -->
<oauth2:resource-server id="oauth2AuthenticationFilter" resource-id="todoResource"
token-services-ref="tokenServices" entry-point-ref="oauth2AuthenticationEntryPoint" /> <!-- (7) -->
</beans>
Sr. No. | Description |
---|---|
(1)
|
Specify the pattern on path that is the target of authorization control in
pattern attribute.Specify the Bean of
OAuth2AuthenticationEntryPoint in entry-point-ref . Settings are required for definition, however the specified Bean is not used.
Actually, the Bean of OAuth2AuthenticationEntryPoint specified in OAuth2AuthenticationProcessingFilter described later is used. |
(2)
|
Set the Bean of
OAuth2AccessDeniedHandler in access-denied-handler . Here, specify a Bean of oauth2AccessDeniedHandler defined in (5). |
(3)
|
Invalidate CSRF countermeasures in this implementation example.
For CSRF countermeasures, refer CSRF measures.
|
(4)
|
Set
OAuth2AuthenticationProcessingFilter as an authentication filter for resource server for resource server in custom-filter .Specify a Bean of
oauth2AuthenticationFilter defined in (7).Since
OAuth2AuthenticationProcessingFilter is a filter for performing Pre-Authentication by using access token included in request,
set in such a way that OAuth2AuthenticationProcessingFilter process is executed before PRE_AUTH_FILTER by specifying PRE_AUTH_FILTER in before .For Pre-Authentication, refer Pre-Authentication Scenarios.
|
(5)
|
Define
AccessDeniedHandler for resource server provided by Spring Security OAuth.OAuth2AccessDeniedHandler sends the error response by handling the exceptions that occur at the time of authorization error. |
(6)
|
Define the Bean of
OAuth2AuthenticationEntryPoint for sending the error response for OAuth. |
(7)
|
Define the ServletFilter for the resource server provided by Spring Security OAuth.
OAuth2AuthenticationProcessingFilter is registered as Authentication Filter by using <oauth2:resource-server> tag.The string specified in
id attribute is the Bean ID. Here,``oauth2AuthenticationFilter`` is specified.Specify the resource ID provided by server in
resource-id attribute. Here, todoResource is specified.It is verified whether the resource ID specified in
resource-id attribute is included for the resource ID of client information linked to access token.As a result of verification, it is allowed to access the resource only when resource ID is included.
Note that, definition of
resource-id is optional and when it is not defined, resource ID is not verified.Specify the ID of
TokenServices in token-services-ref attribute. TokenServices is described later.Specify the Bean of
OAuth2AuthenticationEntryPoint in entry-point-ref attribute. Here, oauth2AuthenticationEntryPoint is specified. |
Add the settings in web.xml
so as to read a created oauth2-resource.xml
.
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:META-INF/spring/applicationContext.xml
classpath*:META-INF/spring/oauth2-resource.xml <!-- (1) -->
classpath*:META-INF/spring/spring-security.xml
</param-value>
</context-param>
Sr. No. | Description |
---|---|
(1)
|
First read
oauth2-resource.xml considering that the path including the path pattern set in oauth2-resource.xml is set as the access control target in spring-security.xml . |
9.9.2.3.2.2. Setting of accessible scope for the resource¶
In order to define the accessible scope for each resource, add the scope definition and Bean definition for supporting SpEL expression in Bean definition file for OAuth 2.0.
Implementation example is as given below.
oauth2-resource.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/security/oauth2
http://www.springframework.org/schema/security/spring-security-oauth2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<sec:http pattern="/api/v1/todos/**" create-session="stateless"
entry-point-ref="oauth2AuthenticationEntryPoint">
<sec:access-denied-handler ref="oauth2AccessDeniedHandler"/>
<sec:csrf disabled="true"/>
<sec:expression-handler ref="oauth2ExpressionHandler"/> <!-- (1) -->
<sec:intercept-url pattern="/**" method="GET"
access="#oauth2.hasScope('READ')" /> <!-- (2) -->
<sec:intercept-url pattern="/**" method="POST"
access="#oauth2.hasScope('CREATE')" /> <!-- (2) -->
<sec:custom-filter ref="oauth2AuthenticationFilter"
before="PRE_AUTH_FILTER" />
<sec:custom-filter ref="userIdMDCPutFilter" after="ANONYMOUS_FILTER"/>
</sec:http>
<!-- omitted -->
<oauth2:web-expression-handler id="oauth2ExpressionHandler" /> <!-- (3) -->
</beans>
Sr. No. | Description |
---|---|
(1)
|
Specify a Bean of
org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler in expression-handler . |
(2)
|
Define access policy as per the scope for the resource by using
intercept-url .Specify the path pattern of resource to be retained in
pattern attribute. In this implementation example, resource is retained under /api/v1/todos/.Specify the HTTP method of resource in
method attribute.Specify the scope that authorizes the access to resource in
access attribute. Differentiate the setting value by upper-case and lower case characters.Here, it is specified by using SpEL expression.
|
(3)
|
Define the Bean of
OAuth2WebSecurityExpressionHandler .SpeL for performing authorization control provided by Spring Security OAuth is supported by defining this Bean.
Note that, the value specified in
id attribute is the id for this bean. |
The main Expression provided by Spring Security OAuth is introduced.
For details, refer JavaDoc of org.springframework.security.oauth2.provider.expression.OAuth2SecurityExpressionMethods
.
Expression | Description |
---|---|
hasScope(String scope) |
Return
true when the scope authorized by the resource owner and the scope of the argument matches in the client. |
hasAnyScope(String... scopes) |
Return
true when the scope authorized by the resource owner and one of the scopes of the argument matches in the client. |
hasScopeMatching(String scopeRegex) |
Return
true when the scope authorized by the resource owner and the regular expression specified in the argument matches in the client. |
hasAnyScopeMatching(String... scopesRegex) |
Return
true when the scope authorized by the resource owner and one of the regular expressions specified in the argument matches in the client. |
clientHasRole(String role) |
Return
true when the client performs the role specified in the argument. |
clientHasAnyRole(String... roles) |
Return
true when the client performs one of the roles specified in the argument. |
denyOAuthClient |
Deny request in OAuth 2.0. It is used so that only the resource owner can access the resources.
|
isOAuth |
Allow request in OAuth 2.0. It is used so that the client can access the resources.
|
Note
SpEL expression can be used with SpEL provided by Spring Security.
Refer to Built-In Web Expressions for SpEL provided by Spring Security.
9.9.2.3.2.3. How to link access token with authorization server¶
Authorization Server is linked with Resource Server via access token TokenServices
.
Refer to How to share access token with resource server for the methods of linking.
Apply settings by the method of sharing DB.
Refer to How to share access token with resource server for the description of settings as the settings are same as TokenServices
settings in authorization server.
oauth2-resource.xml
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
</bean>
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
<constructor-arg ref="dataSource" />
</bean>
Note
A method which use org.springframework.security.oauth2.provider.token.RemoteTokenServices
provided by Spring Security OAuth and a method which use
JSON Web Token are offered as the methods for linking the authorization server and resource server.
Refer to Linking Authorization Server and Resource Server through HTTP access for how to use RemoteTokenServices
provided by Spring Security OAuth.
9.9.2.3.2.4. Fetching user information¶
In the Resource Server, the authenticated resource owner information can be received by specifying UserDetails
in method argument of Controller class and assigning @AuthenticationPrincipal
annotation similar to fetching method of the authentication information explained in Coordination between authentication process and Spring MVC.
The implementation example is shown below.
@RestController
@RequestMapping("api")
public class TodoRestController {
// omitted
@RequestMapping(value = "todos", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
public Collection<Todo> list(@AuthenticationPrincipal UserDetails user) { // (1)
// omitted
}
}
Sr.No. | Description |
---|---|
(1)
|
Authentication information of resource owner is stored in the argument ``user``
|
When authentication information of the client is to be fetched, specify org.springframework.security.oauth2.provider.OAuth2Authentication
in method argument of Controller class.
An example wherein OAuth2Authentication
is specified in the method argument of Controller class and authentication information of the client and the resource owner is fetched, is given below.
@RestController
@RequestMapping("api")
public class TodoRestController {
// omitted
@RequestMapping(value = "todos", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
public Collection<Todo> list(OAuth2Authentication authentication) { // (1)
String username = authentication.getUserAuthentication().getName(); // (2)
String clientId = authentication.getOAuth2Request().getClientId(); // (3)
// omitted
}
}
Sr.No. | Description |
---|---|
(1)
|
Authentication information of the resource owner and the client is stored in the argument
authentication . |
(2)
|
Fetch resource owner name from
authentication . |
(3)
|
Fetch client ID from
authentication . |
Note
The implementation example is given above wherein OAuth2Authentication
is used in the application layer.
When the implementation is to be done without depending on OAuth2Authentication
, the same function is feasible by implementing HandlerMethodArgumentResolver
.
Refer to Implementing HandlerMethodArgumentResolver for specific implementation method.
9.9.2.3.3. Implementation of client¶
How to implement a client is explained.
How to implement by using OAuth2RestTemplate
is explained here.
In OAuth2RestTemplate
, the functions for fetching access token, sharing access token between multiple requests by OAuth2ClientContext
and error handling when accessing the Resource Server
are implemented as per the grant type by AccessTokenProvider
as independent functions of OAuth 2.0.
In client, OAuth2RestTemplate
is used and resource can be accessed by using OAuth 2.0 function, by defining parameters according to application requirements such as grant type and scope.
Note
When you want to access a resource server which is implemented by architecture other than Spring Security OAuth, it must be checked whether access from REST API is accepted. If API of resource server varies, access must be achieved by means other than OAuth2RestTemplate.
9.9.2.3.3.1. Creation of configuration file (Client)¶
First create oauth2-client.xml
as the configuration file for defining OAuth 2.0.
oauth2-client.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sec="http://www.springframework.org/schema/security"
xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd
">
</beans>
Add the settings to web.xml
so as to read the created oauth2-client.xml
.
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:META-INF/spring/applicationContext.xml
classpath*:META-INF/spring/oauth2-client.xml <!-- (1) -->
classpath*:META-INF/spring/spring-security.xml
</param-value>
</context-param>
<!-- omitted -->
Sr.No. | Description |
---|---|
(1)
|
Set so as to read
oauth2-client.xml . |
9.9.2.3.3.2. Applying OAuth2ClientContextFilter¶
Register OAuth2ClientContextFilter
as a servlet filter.
By registering OAuth2ClientContextFilter
, when an attempt is made to access the Resource Server when authorization cannot be fetched by the resource owner,
a function to capture UserRedirectRequiredException
and redirect to the page for fetching authorization of the resource owner provided by the Authorization Server
can be incorporated.
Add the following settings to oauth2-client.xml
.
oauth2-client.xml
<oauth2:client id="oauth2ClientContextFilter" /> <!-- (1) -->
Sr.No. | Explanation |
---|---|
(1)
|
Bean is defined for
OAuth2ClientContextFilter by using <oauth2:client> tag. The string specified in id attribute is used as Bean ID. |
Add the settings of OAuth2ClientContextFilter
to web.xml
.
web.xml
<filter> <!-- (1) -->
<filter-name>oauth2ClientContextFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>oauth2ClientContextFilter</filter-name>
<url-pattern>/oauth/*</url-pattern> <!-- (2) -->
</filter-mapping>
Sr.No. | Description |
---|---|
(1)
|
Register the Bean wherein BeanID matches with the filter name (value specified in the
<filter-name> attribute) as a servlet filter by using DelegatingFilterProxy .Set the value same as the BeanID specified in
id attribute of <oauth2:client> in the filter name.It is recommended to mention
OAuth2ClientContextFilter at the end of servlet filter definition so as not to perform unintended exception handling of the exception generated in Spring Security OAuth. |
(2)
|
OAuth2ClientContextFilter is applied to the path wherein UserRedirectRequiredException may occur.In the above example,
OAuth2ClientContextFilter is applied to all the requests. |
Note
Since OAuth2ClientContextFilter
generates redirect URI for returning the user agent after the authorization process by the Authorization Server in the pre-process of the filter,
a wide definition such as /*
will result in futile processing in the path where UserRedirectRequiredException
does not occur.
Note
OAuth2ClientContextFilter
stores the URL to be redirected after the Authorization Server gets the authorization
from the resource owner with the attribute name currentUri
in the request scope. Therefore, the attribute name currentUri
cannot be used in the client.
As mentioned above, org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter
is a servlet filter that captures ``UserRedirectRequiredException`` and redirects it to the Authorization Server.
However, if the SystemExceptionResolver
set in the blank project in advance, handles UserRedirectRequiredException
first,
OAuth2ClientContextFilter
will not work as intended.
Therefore, it is necessary to change the setting of spring-mvc.xml` so that the SystemExceptionResolver
does not handle UserRedirectRequiredException
.
Refer to Exception Handling for the description of SystemExceptionResolver
in detail.
Add UserRedirectRequiredException
to spring-mvc.xml
as the exclusion target of SystemExceptionResolver
.
spring-mvc.xml
<bean id="systemExceptionResolver"
class="org.terasoluna.gfw.web.exception.SystemExceptionResolver">
<!-- omitted -->
<property name="excludedExceptions">
<array>
<!-- (1) -->
<value>org.springframework.security.oauth2.client.resource.UserRedirectRequiredException
</value>
</array>
</property>
</bean>
Sr.No. | Description |
---|---|
(1)
|
Exclude
UserRedirectRequiredException from handling target of SystemExceptionResolver . |
9.9.2.3.3.3. Settings of OAuth2RestTemplate¶
How to set OAuth2RestTemplate
is explained.
oauth2-client.xml
<oauth2:resource id="todoAuthCodeGrantResource" client-id="firstSec"
client-secret="firstSecSecret"
type="authorization_code"
scope="READ,WRITE"
access-token-uri="${auth.serverUrl}/oauth/token"
user-authorization-uri="${auth.serverUrl}/oauth/authorize"/> <!-- (1) -->
<oauth2:rest-template id="todoAuthCodeGrantResourceRestTemplate" resource="todoAuthCodeGrantResource" /> <!-- (2) -->
Sr.No. | Description |
---|---|
(1)
|
Define detailed information of the resource to be accessed , to be referred by
OAuth2RestTemplate .Refer to the following table for the setting value of each item.
|
(2)
|
Define
OAuth2RestTemplate .Specify BeanID of
OAuth2RestTemplate in id .Specify
id of Bean defined in (1) for resource . |
¶ Items Description id
Bean ID of resource.client-id
ID for identifying the client in the Authorization Server.client-secret
Password used for authentication of client in the Authorization Server.type
Grant type. Specifyauthorization_code
in case of authorization code grant.scope
Enumerate the scope that requires authorization by separating with commas. Setting value is case-sensitive.Request all scopes set for the client in Authorization Server at the time of omitting.access-token-uri
Endpoint URL of the Authorization Server for requesting the issue of access token.user-authorization-uri
Endpoint URL of the Authorization Server for getting the authorization of resource owner.
Note
In this implementation example, context root of each server set during implementation is represented by following placeholders.
Placeholder key Setting value auth.serverUrl
Context root of authorization server resource.serverUrl
Context root of resource server client.serverUrl
Context root of client
Also, when absolute path of endpoint for each server is to be represented, path is specified below the placeholder to explicitly understand the path such as ${auth.serverUrl}/oauth/token
.
While developing a real application, for example, when the endpoint is in the external system, it is recommended to look at the method to specify the path according to the requirement
such as indicating the whole path by placeholder to deal with changes in endpoint path at the time of changing external system specifications.
Note
In <oauth2:resource>
tag, client-authentication-scheme
attribute is provided as the method to specify
client authentication method at the time of getting access token.
The values that can be specified in client-authentication-scheme
parameter are as follows.
header
: Basic authentication using Authorization header (Default value)query
: Authentication using URL query parameter at the time of requestingform
: Authentication using body parameter at the time of requestingIn this guideline, since Basic authentication is used for authenticating the client, parameters are not specified in the above-mentioned setting example, however, parameters should be specified as per the application requirements.
Note
When issue of access token is requested for authorization server implemented by architecture other than Spring Security OAuth, it must be noted that the request parameter is likely to be different from that given above. In such a case, check architecture specification and set the required request parameters.
9.9.2.3.3.4. Accessing a resource Server¶
How to access the Resource Server using OAuth2RestTemplate
is explained.
Since access to Authorization Server is hidden by OAuth2RestTemplate
and OAuth2ClientContextFilter
,
the process to be performed for REST API provided by the Resource Server is described similar to normal access to REST API without being aware of the Authorization Server at the time of development.
The implementation example of Service class is shown below.
import org.springframework.web.client.RestOperations;
@Service
public class TodoServiceImpl implements TodoService {
@Inject
RestOperations restOperations; // (1)
@Value("${resource.serverUrl}/api/v1/todos")
String url;
@Override
public List<Todo> getTodoList() {
Todo[] todoArray = restOperations.
getForObject(url, Todo[].class); // (2)
return Arrays.asList(todoArray);
}
}
Sr. No. | Description |
---|---|
(1)
|
Inject
RestOperations . |
(2)
|
Access the specified URL with GET method in REST and receive the result in a list.
|
Note
When access token is not issued if OAuth2RestTemplate
is used to access the Resource Server,
it is redirected to the Authorization Server at once.
Thereafter, when the issue of access token is complete, it is redirected to the client and access process to the resource server is called again.
At this time, since redirect to client is performed by GET, the Controller that may be redirected from Authorization Server, must allow GET.
If access to the Resource Server before the redirect is POST, the parameter will be lost in GET after redirect. In that case, a measure like retaining POST parameter in session, should be taken. In this guideline, the explanation of specific handling method is omitted.
Warning
When Spring Security is used, and when you try to fetch an access token by using OAuth2RestTemplate
when user is not authenticated,
org.springframework.security.authentication.InsufficientAuthenticationException
occurs, hence, it must be ensured that the user is
already authenticated on the path by using OAuth2RestTemplate
.
Specifically, it should be confirmed that one of the following is being implemented.
- Ensure that Service method which use
OAuth2RestTemplate
is authenticated by@PreAuthorize
- Ensure that path which use
OAuth2RestTemplate
is authenticated by<sec:intercept-url>
For details, refer Authorization of Web resource and Authorization for the method.
Note that, error does not occur in case of a client credential grant which does not require resource owner authentication since the client becomes a resource owner,
and in case of an implicit grant which does not use OAuth2RestTemplate
.
9.9.2.3.3.5. Cancelling a token (Client server)¶
How to cancel an issued access token is explained.
TokenStore
. a request header is set for Basic authentication, to carry out Basic authentication of client while sending a request to authorization server.OAuth2RestTemplate
.Implementation example is as shown below.
First, a RestTemplate
setting is added to settings file to make a token cancellation request to authorization server.
oauth2-client.xml
<!-- (1) -->
<bean id="revokeRestTemplate" class="org.springframework.web.client.RestTemplate">
<property name="interceptors">
<list>
<ref bean="basicAuthInterceptor" />
</list>
</property>
</bean>
<bean id="basicAuthInterceptor" class="org.springframework.http.client.support.BasicAuthorizationInterceptor">
<constructor-arg index="0" value="${api.auth.username}" />
<constructor-arg index="1" value="${api.auth.password}" />
</bean>
Sr. No. | Description |
---|---|
(1)
|
Define a Bean for
RestTemplate to make a token cancellation request to authorization server.Specify implementation class of
ClientHttpRequestInterceptor in interceptors property to set request header for Basic authentication.For implementation method of implementation class of
ClientHttpRequestInterceptor which sets a request header for Basic authentication, refer Request header setup process for Basic authentication. |
An interface of service class and the implementation class which cancel the token are created.
RevokeTokenClientService.java
public interface RevokeTokenClientService {
String revokeToken();
}
RevokeTokenClientServiceImpl.java
@Service
public class RevokeTokenClientServiceImpl implements RevokeTokenClientService {
@Value("${auth.serverUrl}/api/v1/oth2/tokens/revoke")
String revokeTokenUrl; // (1)
@Inject
@Named("todoAuthCodeGrantResourceRestTemplate")
OAuth2RestOperations oauth2RestOperations; // (2)
@Inject
@Named("revokeRestTemplate")
RestOperations revokeRestOperations; // (3)
@Override
public String revokeToken() {
String tokenValue = getTokenValue(oauth2RestOperations);
String result = "";
if(StringUtils.hasLength(tokenValue)){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> variables = new LinkedMultiValueMap<>();
variables.add("token", tokenValue);
try {
revokeRestOperations.postForObject(url,
new HttpEntity<MultiValueMap<String, String>>(variables, headers),
Void.class); // (4)
result = "success";
initContextToken(oauth2RestOperations); // (5)
} catch (HttpClientErrorException e) {
result = "invalid request";
}
}else{
result ="token not exist";
}
return result;
}
// (6)
private String getTokenValue(OAuth2RestOperations oauth2RestOperations) {
OAuth2AccessToken token = restOperations.getOAuth2ClientContext()
.getAccessToken();
if (token == null) {
return "";
}
return token.getValue();
}
// (7)
private void initContextToken(OAuth2RestOperations oauth2RestOperations) {
oauth2RestOperations.getOAuth2ClientContext().setAccessToken(null);
}
}
Sr. No. | Description |
---|---|
(1)
|
URL to be used while requesting the cancellation of access token to the authorization server.
|
(2)
|
Inject
OAuth2RestTemplate which retains the access token to be cancelled.Since there are multiple implementations of
RestOperations , inject it by specifying Bean name with @Named . |
(3)
|
Inject
RestTemplate for cancelling the access token.Since there are multiple implementations of
RestOperations , inject it by specifying Bean name with @Named . |
(4)
|
In order to cancel the access token in authorization server, it is accessed with method POST in REST.
Set value of access token to be cancelled in the request parameter for passing it to the authorization server.
Access token is fetched by passing
OAuth2RestOperations to getTokenValue method defined in (6). |
(5)
|
Delete access token retained by
OAuth2RestOperations .Access token is deleted by passing
OAuth2RestOperations retaining the access token in initContextToken method defined in (7). |
(6)
|
Method of getting the access token retained by
OAuth2RestOperations .Access token is fetched and returned by calling the
getAccessToken method of OAuth2RestOperations passed as a parameter. |
(7)
|
Method of deleting the access token retained by
OAuth2RestOperations .Delete the access token by passing the null in
setAccessToken method of OAuth2RestOperations passed as a parameter. |
Note
Authorization servers which are implemented with architecture other than Spring Security OAuth may not accept deletion of access tokens by REST API.
In that case, architecture specification of authorization server must be checked and implement appropriately.
9.9.2.3.3.6. Error handling¶
Methods to handle errors which occur while accessing authorization server and resource server using OAuth2RestTemplate
are explained.
9.9.2.3.3.6.1. Error handling while accessing authorization end point¶
Errors which occur at authorization end point are classified as exceptions other than invalid access error and bad client error. For details of bad client error, refer Unauthorized client error. For error handling when an unauthorized client error occurs, refer Error handling at the time of authorization request. This section explains about error handling when errors other than unauthorized client error occur.
Errors notified to the client should be handled as below.
Sr. No. | Errors occurred | Description |
---|---|---|
(1)
|
Authorization for resource owner denied
org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException |
It occurs when the resource owner rejects all the scope specified by the client, on scope authorization screen.
error=access_denied and error_description=User denied access are set in request parameters of redirect URI. |
Warning
Regarding request parameters handled by response at the time of error
As specified in 4.1.2.1. Error Response of RFC 6749,
request parameters error
and error_description
are included in response at the time of error, in OAuth 2.0.
Therefore, when parameters of these names are used at the time of business implementation, it must be noted that parameter names may show conflict in some cases.
A process for handling exceptions thrown by OAuth2RestTemplate
above should be implemented.
In this guideline, error handling is implemented in accordance with the following requirements.
- Determine whether the exception has occurred due to resource owner operations and change error screen
Exceptions occurring in authorization server can be classified into exceptions which are caused by resource owner operations like “authorization denied”, and the exceptions which are caused by application failures like “scope specified by the client does not exist in the client information of authorization server”. Exceptions must be handled appropriately based on their classification.
- For exceptions caused by resource owner operations, transition occurs to “Access denied screen” and reimplementation of operation is suggested
- For exceptions which are not caused by application failures, transition occurs to “system error screen” and review of application settings is suggested
- Redirect to error screen for preventing unnecessary exposure of parameters to URL
Following parameters are assigned to URL parameters in the flow of authorization code grant or implicit grant, however when an error occurs during flow, and forwarding is done for error screen as it is, parameters are exposed in the address bar of the browser. in order to prevent this, it is necessary to redirect to error screen.
- Authorization code (authorization code grant only)
- When an error occurs from issue of authorization code to issue of access token
state
parameter- When an error occurs from authorization request to completed authorization of resource owner
For grant types other than authorization code grant and implicit grant, forwarding is used instead of redirect since the above parameters are not used.
An example is given below wherein UserDeniedAuthorizationException
for authorization denial of resource owner is handled and transition is done to access denied screen.
Note that, handling for transiting to system error screen must also be implemented in the same manner.
For handling of exceptions, refer Errors which occur while accessing authorization server and resource server from the client.
Further, it is assumed in the example that UserDeniedAuthorizationException
is handled commonly throughout the application and
@ControllerAdvice
annotation is used. For details, refer Implementing “@ControllerAdvice”.
Implementation example is shown below.
@ControllerAdvice
public class OAuth2ExceptionHandler {
// omitted
@ExceptionHandler(UserDeniedAuthorizationException.class) // (1)
public String handleUserDeniedAuthorizationException(
UserDeniedAuthorizationException e, RedirectAttributes attributes) {
// omitted
attributes.addFlashAttribute("exception", e);
return "redirect:/oauthAccessDeniedError"; // (2)
}
}
Sr. No. | Description |
---|---|
(1)
|
Handle errors which occur in the controller.
It is executed when
UserDeniedAuthorizationException specified in @ExceptionHandler parameter occur. |
(2)
|
Redirect to hide authorization code and
state parameter.When exception is passed to redirect destination, exception to be passed is set by using
addFlashAttribute method of RedirectAttributes . |
Also, a controller corresponding to path for redirecting /oauthAccessDeniedError
must be defined to
redirect to @ExceptionHandler
of OAuth2ExceptionHandler
.
Definition example of controller is shown below.
OAuth2ErrorController.java
@Controller
public class OAuth2ErrorController {
@RequestMapping("/oauthAccessDeniedError") // (1)
public String handleAccessDeniedError() {
return "common/error/accessDeniedError";
}
}
Sr. No. | Description |
---|---|
(1)
|
Use
@RequestMapping annotation and map as a method for accessing /oauthAccessDeniedError .When
/oauthAccessDeniedError is called, access denied screen is displayed. |
9.9.2.3.3.6.2. Token end point and error handling while accessing resource server¶
In the authorization code grant, all the errors that occur in token end point and resource server must be handled as system errors. For errors occurred, refer Errors which occur while accessing authorization server and resource server from the client. Further, for error handling, refer Error handling while accessing authorization end point.
9.9.2.4. Implementation of implicit grant¶
Implementation methods for authorization server, resource server and client which use implicit grant are explained.
Explanation is given only for the changes from that of authorization code grant. For implementation and explanation other than changes, refer Implementation of authorization code grant.
Changes from that of authorization code grant while implementing each server are shown below.
Server | Changes from that of authorization code grant |
---|---|
Authorization server | Defining authorization server |
Resource server | NIL |
Client | All of Implementation of client |
9.9.2.4.1. Implementation of authorization server¶
oauth2-auth.xml
must be changed from the implementation of authorization code grant.
For implementation other than oauth2-auth.xml
, refer Implementation of authorization server.
9.9.2.4.1.1. Defining authorization server¶
Changes of oauth2-auth.xml
from that of authorization code grant are shown below.
oauth2-auth.xml
<oauth2:authorization-server
client-details-service-ref="clientDetailsService"
user-approval-handler-ref="userApprovalHandler"
token-services-ref="tokenServices">
<oauth2:implicit /> <!-- (1) -->
<oauth2:refresh-token />
</oauth2:authorization-server>
Sr. No. | Description |
---|---|
(1)
|
Use
<oauth2:implicit /> tag and support implicit grant. |
Warning
<oauth2:implicit />
tag and <oauth2:refresh-token />
tag must be set in the above sequence.
For details ,refer When the multiple grant types are supported in authorization server.
9.9.2.4.2. Implementation of resource server¶
Changes are not required from the implementation of authorization code grant.
Implementation should be created by referring Implementation of resource server.
9.9.2.4.3. Implementation of client¶
Generally, clients implemented by JavaScrtipt etc. are adopted in implicit grant.
As libraries other than Java are not provided in Spring Security OAuth, this guideline explains how to implement clients independently using JavaScript.
9.9.2.4.3.1. Accessing a resource server which uses JavaScript¶
In this guideline, a method is explained wherein JSON format data is fetched from resource server by using JavaScript and displayed on the screen as an implementation of client for implicit grant.
Note
jQuery (version 3.1.1) is used as a javaScript library in the implementation example explained below.
JQuery is stored under src/main/webapp/resources/vendor
and created JavaScript file is stored under src/main/webapp/resources/app/js
.
Note
Access token storage destination
When HTML5 compliant browser is used, local storage and session storage can be given as typical examples of storage destinations of access tokens.
Retention period and use for local and session storage are shown below.
Storage destination Retention period Use Local storage Until it is explicitly cleared When only one user is using the terminal browser, and when it is desired to reduce number of access token issues and improve convenience of service Session storage Until tab and browser are closed When multiple users are using terminal browser, when it is desired to prevent unauthorized use of access tokens and strengthen security
An implementation example wherein local storage is adopted is explained.
A JavaScript wherein OAuth 2.0 function is independently implemented by client is created. Implementation example is shown below.
oauth2.js
var oauth2Func = (function(exp, $) {
"use strict";
var
config = {},
DEFAULT_LIFETIME = 3600;
var uuid = function() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == "x" ? r : (r&0x3|0x8);
return v.toString(16);
});
};
var encodeURL= function(url, params) {
var res = url;
var k, i = 0;
for(k in params) {
res += (i++ === 0 ? "?" : "&") + encodeURIComponent(k) + "=" + encodeURIComponent(params[k]);
}
return res;
};
var epoch = function() {
return Math.round(new Date().getTime()/1000.0);
};
var parseQueryString = function (qs) {
var e,
a = /\+/g,
r = /([^&;=]+)=?([^&;]*)/g,
d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
q = qs,
urlParams = {};
while (e = r.exec(q)) {
urlParams[d(e[1])] = d(e[2]);
}
return urlParams;
};
var saveState = function(state, obj) {
localStorage.setItem("state-" + state, JSON.stringify(obj));
};
var getState = function(state) {
var obj = JSON.parse(localStorage.getItem("state-" + state));
localStorage.removeItem("state-" + state);
return obj;
};
var hasScope = function(token, scope) {
if (!token.scopes) return false;
var i;
for(i = 0; i < token.scopes.length; i++) {
if (token.scopes[i] === scope) return true;
}
return false;
};
var filterTokens = function(tokens, scopes) { // (1)
if (!scopes) scopes = [];
var i, j,
result = [],
now = epoch(),
usethis;
for(i = 0; i < tokens.length; i++) {
usethis = true;
if (tokens[i].expires && tokens[i].expires < (now+1)) usethis = false;
for(j = 0; j < scopes.length; j++) {
if (!hasScope(tokens[i], scopes[j])) usethis = false;
}
if (usethis) result.push(tokens[i]);
}
return result;
};
var saveTokens = function(providerId, tokens) {
localStorage.setItem("tokens-" + providerId, JSON.stringify(tokens));
};
var getTokens = function(providerId) {
var tokens = JSON.parse(localStorage.getItem("tokens-" + providerId));
if (!tokens) tokens = [];
return tokens;
};
var wipeTokens = function(providerId) {
localStorage.removeItem("tokens-" + providerId);
};
var saveToken = function(providerId, token) { // (2)
var tokens = getTokens(providerId);
tokens = filterTokens(tokens);
tokens.push(token);
saveTokens(providerId, tokens);
};
var getToken = function(providerId, scopes) {
var tokens = getTokens(providerId);
tokens = filterTokens(tokens, scopes);
if (tokens.length < 1) return null;
return tokens[0];
};
var sendAuthRequest = function(providerId, scopes) { // (3)
if (!config[providerId]) throw "Could not find configuration for provider " + providerId;
var co = config[providerId];
var state = uuid();
var request = {
"response_type": "token"
};
request.state = state;
if (co["redirectUrl"]) {
request["redirect_uri"] = co["redirectUrl"];
}
if (co["clientId"]) {
request["client_id"] = co["clientId"];
}
if (scopes) {
request["scope"] = scopes.join(" ");
}
var authurl = encodeURL(co.authorization, request);
if (window.location.hash) {
request["restoreHash"] = window.location.hash;
}
request["providerId"] = providerId;
if (scopes) {
request["scopes"] = scopes;
}
saveState(state, request);
redirect(authurl);
};
var checkForToken = function(providerId) { // (4)
var h = window.location.hash;
if (h.length < 2) return true;
if (h.indexOf("error") > 0) { // (5)
h = h.substring(1);
var errorinfo = parseQueryString(h);
handleError(providerId, errorinfo);
return false;
}
if (h.indexOf("access_token") === -1) {
return true;
}
h = h.substring(1);
var atoken = parseQueryString(h);
if (!atoken.state) {
return true;
}
var state = getState(atoken.state);
if (!state) throw "Could not retrieve state";
if (!state.providerId) throw "Could not get providerId from state";
if (!config[state.providerId]) throw "Could not retrieve config for this provider.";
var now = epoch();
if (atoken["expires_in"]) {
atoken["expires"] = now + parseInt(atoken["expires_in"]);
} else {
atoken["expires"] = now + DEFAULT_LIFETIME;
}
if (atoken["scope"]) {
atoken["scopes"] = atoken["scope"].split(" ");
} else if (state["scopes"]) {
atoken["scopes"] = state["scopes"];
}
saveToken(state.providerId, atoken);
if (state.restoreHash) {
window.location.hash = state.restoreHash;
} else {
window.location.hash = "";
}
return true;
};
var handleError = function(providerId, cause) { // (6)
if (!config[providerId]) throw "Could not retrieve config for this provider.";
var co = config[providerId];
var errorDetail = cause["error"];
var errorDescription = cause["error_description"];
// redirect error page
if(co["errRedirectUrl"]) {
var request = {};
if (errorDetail) {
request["error"] = errorDetail;
}
if (errorDescription) {
request["error_description"] = errorDescription;
}
redirect(encodeURL(co["errRedirectUrl"], request));
} else {
alert("Access Error. cause: " + errorDetail + "/"
+ errorDescription);
}
};
var redirect = function(url) {
window.location = url;
};
var initialize = function(c) {
config = c;
try {
var key, providerId;
for(key in c) {
providerId = key;
}
return checkForToken(providerId);
} catch(e) {
console.log("Error when retrieving token from hash: " + e);
window.location.hash = "";
return false;
}
};
var clearTokens = function(config) { // (7)
var key;
for(key in config) {
wipeTokens(key);
}
};
var oajax = function(settings) { // (8)
var providerId = settings.providerId;
var scopes = settings.scopes;
var token = getToken(providerId, scopes);
if (!token) {
sendAuthRequest(providerId, scopes);
return;
}
if (!settings.headers) settings.headers = {};
settings.headers["Authorization"] = "Bearer " + token["access_token"];
return $.ajax(settings);
};
var parseFailureJSON = function(providerId, data) { // (9)
var res = data.responseJSON;
var error = res.error;
var errorDescription = res.error_description;
var co = config[providerId];
if (error === "invalid_token" && errorDescription) {
var tokens = getTokens(providerId);
for (var token of tokens) {
if (errorDescription.indexOf(token["access_token"]) >= 0) {
if (errorDescription.indexOf("Invalid access token") < 0) {
// clear expired tokens
wipeTokens(providerId)
// redirect for get access token
if (co["redirectUrl"]) {
redirect(co["redirectUrl"]);
return;
}
}
break;
}
}
}
if (co["errRedirectUrl"]) {
var request = {};
if (error) {
request["error"] = error;
}
if (errorDescription) {
request["error_description"] = errorDescription;
}
redirect(encodeURL(co["errRedirectUrl"], request));
} else {
alert("Access Error. cause: " + error + "/" + errorDescription);
}
};
return {
initialize: function(config) {
return initialize(config);
},
clearTokens: function() {
return clearTokens();
},
oajax: function(settings) {
return oajax(settings);
},
parseFailureJSON : function(providerId, data) {
return parseFailureJSON(providerId, data);
}
};
})(window, jQuery);
Sr.No. | Description |
---|---|
(1)
|
A function (
filterTokens ) which determines and returns access token which can be used by the client.Return the access token which has not expired and which matches with the scope specified by parameters, from the access tokens stored in the local storage.
When the scope is not specified, return an access token which has not expired.
|
(2)
|
A function ((
saveToken ) which stores access token in the local storage.It acts as a key for storing access token array in the local storage, by receiving
providerId of configuration information described later as an argument.Array of access tokens is obtained by adding
token argument to access token obtained from filterTokens . |
(3)
|
A function (
sendAuthRequest ) to request authorization for authorization server.Fetch necessary parameters from configuration information and create a request.
|
(4)
|
A function (
checkForToken ) to fetch access token from authorization response.Verify response of authorization returned from hash of URL, and if it is normal, store the information in local storage by using
saveToken . |
(5)
|
When an error is returned as a result of authorization, call
handleError as a error handling process. |
(6)
|
A function (
handleError ) to process errors at the time of authorization.In this guideline, redirect to redirect destination URL is carried out at the time of error specified in configuration information
as the implementation example of error handling.
|
(7)
|
A function (
clearTokens ) to clear access tokens stored in the local storage. |
(8)
|
A function which requests access to resource for resource server (
oajax ).Fetch access token from local storage and send a request to resource server by using
ajax function of jQuery. |
(9)
|
A function (
parseFailureJSON ) which analyzes JSON returned when an error occurs in ajax function and determines transition destination.Determine whether the access token has expired, delete access token when it is expired and display screen again.
As an example, expiry of access token can be determined under following conditions.
If the token is not expired, it is redirected by assigning
error and error_description to URL for redirect at the time of error.Note About determination criteria Determination criteria shown above is not defined by specifications of Spring Security OAuth.
It is a workaround determination criteria based on current implementation of
If the resource server does not use
|
The implementation example of client is shown below. In order to make URL of authorization server and resource server to be accessed as variable, URL is fetched from property and environment variables in controller and linked to the screen.
TodoController.java
@Controller
public class TodoListController {
@Value("${client.serverUrl}") // (1)
String applicationContextUrl;
@Value("${auth.serverUrl}") // (2)
String authServerUrl;
@Value("${resource.serverUrl}") // (3)
String resourceServerUrl;
@RequestMapping(value = "todo/get", method = RequestMethod.GET)
public String getTodo(Model model) {
model.addAttribute("client.serverUrl", applicationContextUr);
model.addAttribute("auth.serverUrl", authServerUrl);
model.addAttribute("resource.serverUrl", resourceServerUrl);
return "todo/todoList";
}
}
Sr. No. | Description |
---|---|
(1)
|
Fetch root path of client.
|
(2)
|
Fetch root path of authorization server.
|
(3)
|
Fetch root path of resource server.
|
Also, a controller is added which handles URL for redirect at the time of error (/oauth/error
).
Implementation of controller is shown below.
OAuth2ErrorController.java
@Controller
@RequestMapping("/oauth/error")
public class OAuth2ErrorController {
// omitted
@RequestMapping(params = { "error=access_denied",
"error_description=User denied access" }) // (1)
public String handleAccessDeniedError(
@RequestParam("error") String error,
@RequestParam("error_description") String description) {
// omitted
return "common/error/accessDeniedError";
}
@RequestMapping // (2)
public String handleError(
@RequestParam(name = "error", required = false) String error,
@RequestParam(name = "error_description", required = false) String description) {
// omitted
return "common/error/systemError";
}
}
Sr. No. | Description |
---|---|
(1)
|
When the request parameter indicates Access denied for resource owner, move to Access denied screen.
|
(2)
|
Move to System error screen except for (1).
|
Implementation example of client screen (JSP) is shown. In JSP, authorization server and resource server are accessed by using independently implemented JavaScript described earlier.
todoList.jsp
<script type="text/javascript" src="${pageContext.request.contextPath}/resources/vendor/jquery/jquery.js"></script> <!-- (1) -->
<script type="text/javascript" src="${pageContext.request.contextPath}/resources/app/js/oauth2.js"></script> <!-- (1) -->
<script type="text/javascript">
"use strict";
$(document).ready(function() {
var result = oauth2Func.initialize({ // (2)
"todo" : { // (3)
clientId : "client", // (4)
redirectUrl : "${client.serverUrl}/todo/get", // (5)
errRedirectUrl : "${client.serverUrl}/oauth/error", // (6)
authorization : "${auth.serverUrl}/oauth/authorize" // (7)
}
});
if (result) {
oauth2Func.oajax({ // (8)
url : "${resource.serverUrl}/api/v1/todos", // (9)
providerId : "todo", // (10)
scopes : [ "READ" ], // (11)
dataType : "json", // (12)
type : "GET" // (13)
}).done(function(data) { // (14)
$("#message").text(JSON.stringify(data));
}).fail(function(data) { // (15)
oauth2Func.parseFailureJSON("todo", data);
});
}
});
</script>
<div id="wrapper">
<p id="message"></p>
</div>
Sr. No. | Description |
---|---|
(1)
|
Specify the path that stores jQuery and individually implemented JavaScript respectively, mentioned earlier.
|
(2)
|
Define configuration information to be used for authorization request and initialize the same.
|
(3)
|
Specify a unique value as an identifier for distinguishing the configuration information of each client.
In the process of accessing resources described later, manage and fetch configuration information by using this item as a key.
|
(4)
|
Specify ID identifying the client.
|
(5)
|
Specify URL to redirect the client after authenticating the resource owner of authorization server.
|
(6)
|
Specify URL for redirecting when an error is received from authorization server as the authorization response.
These guidelines show the method of redirecting to client screen and displaying the error screen,
as an example of implementation when error is received.
|
(7)
|
Specify authorization end point of authorization server.
|
(8)
|
Access the resource.
|
(9)
|
Specify access destination of resource server.
|
(10)
|
Specify identifier of configuration information to be referred.
|
(11)
|
Specify scope to request authorization. Setting value is classified by upper case character and lower chase character.
|
(12)
|
Specify response format.
|
(13)
|
Access resource server GET method.
|
(14)
|
Specify process when processing is successful. Response after successful processing is stored in
message .In this guideline, response is output as it is.
|
(15)
|
Specify a process to perform at the time of process failure.
|
Warning
In this implementation example, a fixed value is used as a key for storing the access token in the local storage.
When the information is stored in the local storage with a fixed key, as in the implementation example, when multiple users use browser of the same terminal, it may happen that information stored by one user is referred by another user unintentionally.
When it is assumed that multiple users use browser of same terminal, session storage is adopted and it is necessary to implement a system so that unintended and unauthorized use does not occur like storing a unique key for each user etc.
Note
When access token issue is to be requested for the authorization server implemented by architecture other than Spring Security OAuth, it must be noted that the request parameter is likely to be different from the parameter given above. In that case, architecture specification must be checked and required request parameter must be set.
Todo
TBD
When authorization servers and resource server that are not in the same domain are accessed from the clients created in JavaScript, support of
Cross-Origin Resource Sharing
is required on authorization server and resource server.Details will be described in the next versions.
9.9.2.4.3.2. Cancelling a token (client server)¶
Cancellation of issued access token is explained.
For a method to delete access token from authorization server, refer Canceling a token (authorization server).
In the client, an access token stored in the local storage can be deleted by calling clearTokens
of oauth2.js
when the access token becomes unnecessary.
Implementation example is shown below.
$(document).ready(function() {
oauth2Func.clearTokens({ // (1)
"todo" : {} // (2)
});
});
Sr. No. | Description |
---|---|
(1)
|
Call
clearTokens function to delete access tokens from local storage. |
(2)
|
Set a value same as a unique value set in
initialize , as a key to refer access token. |
Warning
Even if the access token is deleted from local storage, the access token can ve fetched again without requesting authorization of resource owner, as explained in Canceling a token (authorization server). It must be noted that merely deleting the access token from the client is inadequate as a security measure.
Warning
JavaScript may not be executed due to termination of browser, depending on the timing of deletion of access token. When the security token must be deleted due to security requirement, considering that the user terminates the browser, it must be noted that it is necessary to delete the access token at the point executing for the use case.
9.9.2.4.3.3. Error handling¶
Methods to handle errors which occur while accessing authorization server and resource server in the implicit grant is explained.
9.9.2.4.3.3.1. Error handling while accessing authorization end point¶
Since errors handled from the errors which occur at authorization end point are same as in authorization code grant, refer Error handling while accessing authorization end point. For error handling, refer Accessing a resource server which uses JavaScript.
9.9.2.4.3.3.2. Error handling while accessing resource server¶
Handling must be done as below for errors which occur while accessing resource server.
Sr. No. | Errors occurred | Description |
---|---|---|
(1)
|
Access token verification error
org.springframework.security.oauth2.common.exceptions.InvalidTokenException |
It occurs when validity of access token received from resource server could not be verified (abnormal system) or when access token has expired (normal system).
Since access token error is a combination of normal system and abnormal system, if this error is received when implicit grant
OAuth2RestTemplate is not being used, it must be determined whether the access token has expired. |
For error handling, refer Accessing a resource server which uses JavaScript.
Note
Handling of errors which occur due to resource owner operations are introduced alone above, however system errors must also be handled. For errors occurred, refer Errors which occur while accessing authorization server and resource server from the client.
9.9.2.5. Implementation of resource owner password credential grant¶
Implementation methods for authorization server, resource server and client which use resource owner password credential grant are explained.
Explanation is given only for the changes from that of authorization code grant. For explanation and implementation for other than changes, refer Implementation of authorization code grant.
Changes from that of authorization code grant while implementing each server are shown below.
Server | Changes from that of authorization code grant |
---|---|
Authorization server | |
Resource server | Nil |
Client | Settings of OAuth2RestTemplate |
9.9.2.5.1. Implementation of authorization server¶
oauth2-auth.xml
must be changed from the implementation of authorization code grant and unnecessary definition must be deleted from DB and Service class.
For implementation of other than oauth2-auth.xml
, refer Implementation of authorization server.
9.9.2.5.1.1. Defining authorization server¶
Changes of oauth2-auth.xml
from that of authorization code grant are shown below.
oauth2-auth.xml
<oauth2:authorization-server
client-details-service-ref="clientDetailsService"
user-approval-handler-ref="userApprovalHandler"
token-services-ref="tokenServices">
<oauth2:refresh-token />
<oauth2:password /> <!-- (1) -->
</oauth2:authorization-server>
Sr.No. | Description |
---|---|
(1)
|
Use
<oauth2:password /> tag and support resource owner password credential grant. |
Warning
<oauth2:password />
tag and <oauth2:refresh-token />
tag must be set in the above sequence.
For details, refer When the multiple grant types are supported in authorization server.
9.9.2.5.1.2. Client authentication¶
Access token is directly issued in the resource owner password credential grant as explained in Authorization grant. Therefore, authorization is not requested to the resource owner and redirection of user agent does not occur.
Table defined in Client authentication of authorization code grant should be deleted since web_server_redirect_uris
is not required.
9.9.2.5.1.3. Cancelling a token (authorization server)¶
In the implementation of authorization code grant, authorization information is canceled in accordance with cancellation of token for authorization server in RevokeTokenServiceImpl.java
.
The process is deleted since cancellation of authorization information is not required in resource owner password credential grant which does not request authorization for resource owner.
RevokeTokenServiceImpl.java
which comments out cancellation of authorization information is shown below.
RevokeTokenClientService.java
@Service
@Transactional
public class RevokeTokenServiceImpl implements RevokeTokenService {
@Inject
ConsumerTokenServices consumerService;
@Inject
TokenStore tokenStore;
@Inject
ApprovalStore approvalStore;
@Inject
JodaTimeDateFactory dateFactory;
public String revokeToken(String tokenValue, String clientId){
OAuth2Authentication authentication = tokenStore.readAuthentication(tokenValue);
if (authentication != null) {
if (clientId.equals(authentication.getOAuth2Request().getClientId())) {
/* Authentication user = authentication.getUserAuthentication();
if (user != null) {
Collection<Approval> approvals = new ArrayList<>();
for (String scope : authentication.getOAuth2Request().getScope()) {
approvals.add(
new Approval(user.getName(), clientId, scope, dateFactory.newDate(), ApprovalStatus.APPROVED));
}
approvalStore.revokeApprovals(approvals);
} */
consumerService.revokeToken(tokenValue);
return "success";
} else {
return "invalid client";
}
} else {
return "invalid token";
}
}
}
9.9.2.5.2. Implementation of resource server¶
Changes are not required from the implementation of authorization code grant.
Implementation should be created by referring Implementation of resource server.
9.9.2.5.3. Implementation of client¶
oauth2-client.xml
must be changed from the implementation of authorization code grant.
Implementation other than oauth2-client.xml
must be created by referring Implementation of client.
9.9.2.5.3.1. OAuth2RestTemplate settings¶
OAuth2RestTemplate
as parameters, however,
when multiple resource owners use the same client, changing setting details for each resource owner must be considered.OAuth2RestTemplate
is set in Bean of Session scope
and information of resource owner is stored in that Bean.<oauth2:resource>
tag normally used as a resource acts as a Bean of Singleton scope, a Bean must be defined independently while changing to Session scope.A setup example of OAuth2RestTemplate
is shown below.
org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails
is defined in Session scope so as to change between settings details of each resource owner and set in OAuth2RestTemplate
.
oauth2-client.xml
<bean id="todoPasswordGrantResource" class="org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails"
scope="session">
<aop:scoped-proxy />
<property name="clientId" value="firstSec" />
<property name="clientSecret" value="firstSecSecret" />
<property name="accessTokenUri" value="${auth.serverUrl}/oauth/token" />
<property name="scope">
<list>
<value>READ</value>
<value>WRITE</value>
</list>
</property>
</bean> <!-- (1) -->
<oauth2:rest-template id="todoPasswordGrantResourceRestTemplate" resource="todoPasswordGrantResource" /> <!-- (2) -->
Sr. No. | Description |
---|---|
(1)
|
Define detail information for the resource to be accessed, referred by
OAuth2RestTemplate .Refer to the table below for setting value of each item.
|
(2)
|
Define
OAuth2RestTemplate .Specify Bean ID of
OAuth2RestTemplate in id .Specify
id of Bean defined in (1), in resource . |
Sr. No. Description class
Specify a Bean which is a resource ofOAuth2RestTemplate
. Here,ResourceOwnerPasswordResourceDetails
is specified.scope
Specify session and use HTTPSession as scope range.<aop:scoped-proxy />
Set the Session scope Bean to inject into a Singleton Bean -OAuth2RestTemplate
.This setting is required because bean of Singleton has a longer life cycle than Session scope Bean.Namespace and schema ofaop
are added in order to use this tag.clientId
property Specify an ID to identify the client in the authorization server forclientId
of Bean.clientSecret
property Set password used for client authentication in the authorization server, forclientSecret
of Bean.accessTokenUri
property Specify the end point of authorization server to request issue of access token.scope
property Set scope list which require authorization forscope
of Bean.Specifyscope
in the list format unlike using<oauth2:resource>
tag.
Note
When the issue of access token is requested for authorization server implemented by architecture other than Spring Security OAuth, it must be noted that the request parameters are likely to be different than those given above. In that case, architecture specifications should be checked and required request parameters must be set.
Note
For acquisition of user name and password of resource owner, it is to be entered by resource owner on the client screen when the access is required and stored in the Bean. In this guideline, explanation about how to specifically fetch user name and password is omitted.
Warning
In the authorization server explained in the guideline, password to be set in ResourceOwnerPasswordResourceDetails
should be in plain text in order to perform comparison verification after hashing the password used for authentication.
Since the risk of handling of password by client in plain text is high, resource owner password credential grant should be used only in limited circumstances
like when the client and resource owner share a high trust relation or when the client is placed in a secure environment etc.
For settings of hashing in the authorization server, refer Client authentication.
9.9.2.5.3.2. Error handling¶
Methods to handle errors which occur while accessing authorization server and resource server in resource server password credential grant are explained.
9.9.2.5.3.2.1. Error handling while accessing token end point and resource server¶
For errors which must be handled, from the errors that occur in resource server, and error handling, refer Token end point and error handling while accessing resource serversince it is same as authorization code grant.
Exceptions which occur at token end point are wrapped in org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException
by OAuth2RestTemplate
of client.
Errors which require handling, from the errors wrapped by OAuth2AccessDeniedException
are shown below.
Sr. No. | Errors occurred | Description |
---|---|---|
(1)
|
Resource owner authentication error
org.springframework.security.oauth2.common.exceptions.InvalidGrantException |
It occurs when there is an error in authentication information of resource submitted while using resource password credential grant.
When an error occurs,
InvalidGrantException is handled as an exception class on the authorization server. |
A process to handle exceptions thrown by OAuth2RestTemplate
above must be implemented.
Implementation example is shown below.
For @ControllerAdvice
annotation used in the implementation example, refer Implementing “@ControllerAdvice”.
@ControllerAdvice
public class OAuth2ExceptionHandler {
// omitted
@ExceptionHandler(OAuth2AccessDeniedException.class) // (1)
public String handleOAuth2AccessDeniedException(OAuth2AccessDeniedException e,
Model model) {
// omitted
// (2)
Throwable cause = e.getCause();
if (cause instanceof InvalidGrantException) {
return handleInvalidGrantException(((InvalidGrantException) cause),
model);
}
model.addAttribute("exception", e);
return "common/error/systemError";
}
private String handleInvalidGrantException(InvalidGrantException e,
Model model) {
model.addAttribute("exception", e);
return "common/error/accessDeniedError";
}
}
Sr. No. | Description |
---|---|
(1)
|
Handle errors which occur in the controller.
It is executed when
OAuth2AccessDeniedException specified in parameters of @ExceptionHandler occurs. |
(2)
|
Determine transition destination screen according to cause of errors occurred.
If the cause of the error is
InvalidGrantException , transition occurs to “Access denied” screen.Transition occurs to “System error screen” for causes other than mentioned above.
|
Note
Handling of only the errors which occur due to resource owner operations is introduced above, however, system errors must also be handled. For errors occurred, refer Errors which occur while accessing authorization server and resource server from the client.
9.9.2.6. Implementation of client credential grant¶
Implementation methods of authorization server, resource server and client which use client credential grant are explained.
Explanation is given only for changes from that of authorization code grant. For implementation and explanation for other than changes, refer Implementation of authorization code grant.
Changes from that of authorization code grant while implementing each server are shown below.
Server | Changes from authorization code grant |
---|---|
Authorization server | |
Resource server | Fetching user information |
Client |
9.9.2.6.1. Implementation of authorization server¶
oauth2-auth.xml
must be changed from implementation of authorization code grant and unnecessary definitions should be deleted from DB and Service class.
Implementation other than oauth2-auth.xml
must be created by referring Implementation of authorization server.
9.9.2.6.1.1. Defining authorization server¶
Changes in oauth2-auth.xml
from that of authorization code grant are shown below.
oauth2-auth.xml
<oauth2:authorization-server
client-details-service-ref="clientDetailsService"
token-endpoint-url="/oauth/token"
token-services-ref="tokenServices">
<oauth2:refresh-token />
<oauth2:client-credentials /> <!-- (1) -->
</oauth2:authorization-server>
Sr. No. | Description |
---|---|
(1)
|
Use
<oauth2:client-credentials /> tag and support client credential grant. |
Warning
<oauth2:client-credentials />
tag and <oauth2:refresh-token />
tag must be set in the sequence given above.
For details, refer When the multiple grant types are supported in authorization server.
9.9.2.6.1.2. Client authentication¶
Access token is directly issued in client credential grant as explained in Authorization grant. Therefore, authorization is not requested to the resource owner and redirection of user agent does not occur.
Table defined in Client authentication of authorization code grant should be deleted since web_server_redirect_uris
is not required.
9.9.2.6.1.3. Cancelling a token (authorization server)¶
In the implementation of authorization code grant, authorization information is cancelled in accordance with cancellation of token for authorization server in RevokeTokenServiceImpl.java
.
The process is deleted since cancellation of authorization information is not required in client credential grant which does not request authorization for resource owner.
RevokeTokenServiceImpl.java
which comments out cancellation of authorization information is shown below.
RevokeTokenServiceImpl.java
@Service
@Transactional
public class RevokeTokenServiceImpl implements RevokeTokenService {
@Inject
ConsumerTokenServices consumerService;
@Inject
TokenStore tokenStore;
@Inject
ApprovalStore approvalStore;
@Inject
JodaTimeDateFactory dateFactory;
public String revokeToken(String tokenValue, String clientId){
OAuth2Authentication authentication = tokenStore.readAuthentication(tokenValue);
if (authentication != null) {
if (clientId.equals(authentication.getOAuth2Request().getClientId())) {
/* Authentication user = authentication.getUserAuthentication();
if (user != null) {
Collection<Approval> approvals = new ArrayList<>();
for (String scope : authentication.getOAuth2Request().getScope()) {
approvals.add(
new Approval(user.getName(), clientId, scope, dateFactory.newDate(), ApprovalStatus.APPROVED));
}
approvalStore.revokeApprovals(approvals);
} */
consumerService.revokeToken(tokenValue);
return "success";
} else {
return "invalid client";
}
} else {
return "invalid token";
}
}
}
9.9.2.6.2. Implementation of resource server¶
Fetching user informationmust be changed from implementation of authorization code grant.
Implementation other than Fetching user informationmust be created by referring Implementation of resource server.
9.9.2.6.2.1. Fetching user information¶
Resource owner information is not stored in Spring Security, for the client credential grant wherein the user authentication is not performed.
Therefore, resource owner information cannot be received even after specifying UserDetails
or OAuth2Authentication
with @AuthenticationPrincipal
annotation as an argument of Controller class, like implementation in authorization code grant.
Alternatively, since client information is retained, String
is specified in the method argument of Controller and client ID can be fetched by assigning @AuthenticationPrincipal
annotation.
Changes from that of authorization code grant are shown below.
@RestController
@RequestMapping("api")
public class TodoRestController {
// omitted
@RequestMapping(value = "todos", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
public Collection<Todo> list(@AuthenticationPrincipal String clientId) { // (1)
// omitted
}
}
Sr. No. | Description |
---|---|
(1)
|
Client ID is stored in argument
clientId . |
9.9.2.6.3. Implementation of client¶
oauth2-client.xml
must be changed from implementation of authorization code grant.
Implementation other than oauth2-client.xml
must be created by referring Implementation of client.
9.9.2.6.3.1. OAuth2RestTemplate settings¶
A setup example of OAuth2RestTemplate
is shown below.
oauth2-client.xml
<oauth2:resource id="todoClientGrantResource" client-id="firstSecClient"
client-secret="firstSecSecret"
type="client_credentials"
access-token-uri="${auth.serverUrl}/oauth/token" /> <!-- (1) -->
<oauth2:rest-template id="todoClientGrantResourceRestTemplate" resource="todoClientGrantResource" />
Sr. No. | Description |
---|---|
(1)
|
Specify grant type in
type attribute. Specify client_credentials in case of client credential grant. |
Note
When the issue of access token is requested for authorization server implemented by architecture other than Spring Security OAuth, it must be noted that the request parameters are likely to be different than those given above. In that case, architecture specifications should be checked and required request parameters must be set.
9.9.2.6.3.2. Error handling¶
9.9.2.6.3.2.1. Error handling while accessing token end point and resource server¶
In the client credential grant, all the errors which occur in token end point and resource server may be handled as system errors. For errors occurred, refer Errors which occur while accessing authorization server and resource server from the client.
9.9.2.6.3.3. Resource owner authentication¶
Since the client acts as a resource owner in the client credential grant, authentication of resource owner as described in Resource owner authentication is not required.
9.9.3. How to extend¶
9.9.3.1. When the multiple grant types are supported in authorization server¶
As introduced in Authorization grant, authorization server can support multiple grant types.
When multiple grant types are to be specified, they are to be set in the order shown in the table below since order of the tags is defined in XML schema.
If these are not set in numerical order, org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException
exception is thrown due to XML interpretation failure at the start of the application.
Sr. No. | Tags |
---|---|
1 | <oauth2:authorization-code /> |
2 | <oauth2:implicit /> |
3 | <oauth2:refresh-token /> |
4 | <oauth2:client-credentials /> |
5 | <oauth2:password /> |
As an example, setup examples while supporting authorization code grant, resource owner password credential grant and a refresh token are shown below.
oauth2-auth.xml
<oauth2:authorization-server>
<oauth2:authorization-code /> <!-- (1) -->
<oauth2:refresh-token /> <!-- (2) -->
<oauth2:password /> <!-- (3) -->
</oauth2:authorization-server>
Sr. No. | Description |
---|---|
(1)
|
Use <oauth2:authorization-code /> tag and support authorization code grant.
|
(2)
|
Use <oauth2:refresh-token /> tag and support refresh token.
|
(3)
|
Use <oauth2:password /> tag and support resource owner password credential grant.
|
9.9.3.2. Linking Authorization Server and Resource Server through HTTP access¶
The Resource Server and Authorization Server can be linked by performing HTTP access from the Resource Server to the check token endpoint of the Authorization Server.
Check token endpoint is the endpoint that receives the value of access token from the Resource Server and verifies the token instead of the Resource Server. Check token endpoint fetches and verifies the access token using ``TokenServices``, and passes the information associated with the access token to the Resource Server when there is no problem in the verification.
Note that, check token end point is a unique function of Spring Security OAuth which is not defined in RFC, however, in this guideline, response is sent in the same format as other end points defined in RFC.
Use RemoteTokenServices
that is the implementation class of `` TokenServices`` for the Resource Server to access the check token endpoint of the Authorization Server.
RemoteTokenServices
performs HTTP access of check token endpoint using RestTemplate
and fetches the information associated with the access token.
Since the access token is verified by the check token endpoint, it is not verified by RemoteTokenServices
.
The implementation example is shown below.
9.9.3.2.1. Setting authorization server¶
First, perform the settings for registering org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint
class as component for verifying token in the Authorization Server.
oauth2-auth.xml
<sec:http pattern="/oauth/*token*/**"
authentication-manager-ref="clientAuthenticationManager"> <!-- (1) -->
<sec:http-basic entry-point-ref="oauthAuthenticationEntryPoint" />
<sec:csrf disabled="true"/>
<sec:intercept-url pattern="/**" access="isAuthenticated()"/>
</sec:http>
<oauth2:authorization-server
client-details-service-ref="clientDetailsService"
user-approval-handler-ref="userApprovalHandler"
token-services-ref="tokenServices"
check-token-enabled="true"
check-token-endpoint-url="/oauth/check-token"> <!-- (2) -->
<oauth2:authorization-code />
<oauth2:implicit />
<oauth2:refresh-token />
<oauth2:client-credentials />
<oauth2:password />
</oauth2:authorization-server>
<!-- omitted -->
Sr.No. | Description |
---|---|
(1)
|
Specify a location below
/oauth/*token*/ for access control, as an endpoint to perform security settings
for check token endpoint. |
(2)
|
CheckTokenEndpoint is registered as a component by specifying true in check-token-enabled attribute of <oauth2:authorization-server> tag./oauth/check_token is set as a check token endpoint. |
Warning
Security measures of check token endpoint
Check token end point of authorization server must be accessed only by resource server and it should not be used by any entity other than resource server. In this guideline, access to check token end point is restricted by Basic authentication, however, if possible, it is recommended to perform higher level access restrictions such as IP restrictions on specific URLs on network.
Note
Use DefaultTokenServices
same when linking TokenServices
through shared DB.
TokenStore
referred by TokenServices
uses the implementation class of the interface as per the application requirements.
9.9.3.2.2. Setting resource server¶
Perform the settings to use RemoteTokenServices
as TokenServices
in the configuration file of the Resource Server.
oauth2-resource.xml
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.RemoteTokenServices">
<property name="checkTokenEndpointUrl" value="${auth.serverUrl}/oauth/check-token" /> <!-- (1) -->
</bean>
Sr. No. | Description |
---|---|
(1)
|
Define Bean for
RemoteTokenServices to enable fetching of information associated with the access token by accessing the check token endpoint of the Authorization Server.Set the URL in
checkTokenEndpointUrl property to access check token endpoint. |
(2)
|
Set ID to identify resource server in authorization server, in
clientId property.Set ID is used as a user name for Basic authentication while accessing check token end point.
Client ID to be set must be registered in the client information of authorization server. For registration of client information, refer Client authentication.
|
(3)
|
Set a password used for authentication of resource server in authorization server, in
clientSecret property.Set password is used as a password for Basic authentication while accessing check token end point.
Password to be set must match password registered in client information of authorization server.
|
Note
When verification error of access token occurs in the check token endpoint, HTTP status code 400(Bad Request) is returned in RemoteTokenServices
.
When HTTP status code 400(Bad Request) is returned in the default implementation of RestTemplate
, error handling is done and HttpClientErrorException
is generated.
RestTemplate
to be used by RemoteTokenServices
by default, is extended so that error handling is not performed when HTTP status code of response is 400 in order to link verification error of the access token to the client server.
When RestTemplate
is injected in RemoteTokenServices
, note that this extension will not be applied.
When RemoteTokenServices
is used in the Resource Server, the username of the resource owner can be fetched by specifying @AuthenticationPrincipal
annotation in the handler method as the argument annotation in String
.
The implementation example is as follows.
@RestController
@RequestMapping("api")
public class TodoRestController {
// omitted
@RequestMapping(value = "todos", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
public Collection<Todo> list(@AuthenticationPrincipal String userName) { // (1)
// omitted
}
}
Sr.No. | Description |
---|---|
(1)
|
Username of resource owner is stored in argument
userName . |
9.9.3.2.3. A method to link individual items to resource server¶
Even in case of the structure where DB is not shared between Authorization Server and Resource Server, there may be a case to link the item corresponding to the user information from the Authorization Server to the Resource Server, however
when RemoteTokenServices
is to be used as TokenServices
by the Resource Server, the information other than the user information cannot be fetched by the annotation @AuthenticationPrincipal
annotation of handler method argument of the Resource Server.
An example of extending org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter
that is a class to be used for linking access token by using RemoteTokenServices
and linking the information other than the username to the Resource Server is shown here.
9.9.3.2.3.1. DefaultAccessTokenConverter means¶
Request authentication information of resource owner and client of the access token value to the Authorization Server from the Resource Server by using RestTemplate
and fetch the result as Map
for linking access token wherein RemoteTokenServices
is used.
DefaultAccessTokenConverter
is used as a convertor that converts the authentication information in Authorization Server to Map
and Map
in Resource Server to the authentication information.
By using this, the parameters linked between the Authorization Server and the Resource Server can be customized by extending ``DefaultAccessTokenConverter`` so as to add the return value from the Authorization Server to the Map
.
In the following explanation, an example of linking independent items related to user information and independent items other than this to each other by customizing ``DefaultAccessTokenConverter`` and its property org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter
in the Authorization Server, is shown.
9.9.3.2.3.2. Implementation of Authorization Server¶
How to implement Authorization Server is explained.
First, extend DefaultUserAuthenticationConverter
for adding independent item of the user information.
CustomUserAuthenticationConverter
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
@Override
public Map<String, ?> convertUserAuthentication(
Authentication authentication) {
Map<String, Object> response = new LinkedHashMap<>();
response.put(USERNAME, authentication.getName());
if (authentication.getAuthorities() != null &&
!authentication.getAuthorities().isEmpty()) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(
authentication.getAuthorities()));
}
response.put("company_id", "COMZZZ"); // (1)
return response;
}
}
Sr.No. | Description |
---|---|
(1)
|
Define the information to be passed to the Resource Server as an independent item
company_id and set to response .The information set in
response , is returned to the Resource Server in JSON format as response BODY at the time of verifying the token of check token endpoint. |
Then, extend DefaultAccessTokenConverter
for adding independent item of other than the user information.
CustomAccessTokenConverter.java
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {
@Override
public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
@SuppressWarnings("unchecked")
Map<String, Object> response = (Map<String, Object>) super.convertAccessToken(token, authentication);
response.put("business_id","BIDXXX"); // (1)
// omitted
return response;
}
}
Sr. No. | Description |
---|---|
(1)
|
Define the information to be passed to the Resource Server as an independent item
business_id and set to response .The information set in
response , is returned to the Resource Server in JSON format as response BODY at the time of verifying the token of check token endpoint. |
Perform the settings of CustomUserAuthenticationConverter
and CustomAccessTokenConverter
created in the configuration file of the Authorization Server.
oauth2-auth.xml
<oauth2:authorization-server
client-details-service-ref="clientDetailsService"
user-approval-handler-ref="userApprovalHandler"
token-services-ref="tokenServices"> <!-- (1) -->
<oauth2:authorization-code />
<oauth2:implicit />
<oauth2:refresh-token />
<oauth2:client-credentials />
<oauth2:password />
</oauth2:authorization-server>
<bean id="checkTokenEndpoint"
class="org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint"> <!-- (2) -->
<constructor-arg ref="tokenServices" />
<property name="accessTokenConverter" ref="accessTokenConverter" />
</bean>
<bean id="accessTokenConverter"
class="com.example.oauth2.auth.converter.CustomAccessTokenConverter"/> <!-- (3) -->
<property name="userTokenConverter">
<bean
class="com.example.oauth2.auth.converter.CustomUserAuthenticationConverter" />
</property>
</bean>
Sr. No. | Description |
---|---|
(1)
|
check-token-enabled attribute of <oauth2:authorization-server> tag is not specified since Bean definition of CheckTokenEndpoint is independently done in (2)./oauth/check_token is set as a token check endpoint. |
(2)
|
Define Bean for
CheckTokenEndpoint .By specifying Bean of
CustomAccessTokenConverter defined in (3) in accessTokenConverter property, independent items added in CustomAccessTokenConverter and CustomUserAuthenticationConverter can be linked to the Resource Server. |
(3)
|
Define Bean for
CustomAccessTokenConverter wherein DefaultAccessTokenConverter is extended.Specify Bean for
CustomUserAuthenticationConverter wherein DefaultUserAuthenticationConverter is extended in userTokenConverter property. |
9.9.3.2.3.3. Implementation of Resource Server¶
Add the function to fetch the information linked from Authorization Server in the Resource Server by @AuthenticationPrincipal
annotation of the handler method argument.
First, create OauthUser
class holding the information to be fetched by @AuthenticationPrincipal
annotation.
ResourceOwner.java
public class OauthUser implements Serializable{
private static final long serialVersionUID = 1L;
private String username;
private String companyId;
private String businessId;
private String clientId;
// omitted
public OauthUser(String username, String companyId, String businessId, String clientId){
this.username = username;
this.companyId = companyId;
this.businessId = businessId;
this.clientId = clientId;
}
// Getters and Setters are omitted
}
org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter
which performs setup for enabling fetching of user information by @AuthenticationPrincipal
annotation is expanded and a function is added to enable fetching of information other than user name.
CustomUserAuthenticationConverter.java
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter{
private Collection<? extends GrantedAuthority> defaultAuthorities; // (1)
public void setDefaultAuthorities(String[] defaultAuthorities) {
this.defaultAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
.arrayToCommaDelimitedString(defaultAuthorities));
}
// (2)
@Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME)) {
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
OauthUser user = new OauthUser(
(String) map.get(USERNAME),
(String) map.get("company_id"),
(String) map.get("business_id"),
(String) map.get("client_id")); // (3)
// omitted
return new UsernamePasswordAuthenticationToken(user, "N/A", authorities); // (4)
}
return null;
}
private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
if (!map.containsKey(AUTHORITIES)) {
return defaultAuthorities;
}
Object authorities = map.get(AUTHORITIES);
if (authorities instanceof String) {
return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
}
if (authorities instanceof Collection) {
return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
.collectionToCommaDelimitedString((Collection<?>) authorities));
}
throw new IllegalArgumentException("Authorities must be either a String or a Collection");
}
}
Sr.No. | Description |
---|---|
(1)
|
Implement
defaultAuthorities used by defaultAuthorities and getAuthorities method since getAuthorities method implemented by DefaultUserAuthenticationConverter is defined by private. |
(2)
|
A method that extracts authentication information from the information linked from authorization server.
|
(3)
|
Set information linked from authorization server in
OauthUser class. |
(4)
|
Information linked from authorization server can be fetched
@AuthenticationPrincipal annotation by setting OauthUser in the first argument of UsernamePasswordAuthenticationToken . |
Configure CustomUserAuthenticationConverter
in setup file of resource server.
oauth2-resource.xml
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.RemoteTokenServices">
<property name="checkTokenEndpointUrl" value="${auth.serverUrl}/oauth/check-token" />
<property name="accessTokenConverter" ref="accessTokenConverter" />
</bean>
<bean id="accessTokenConverter"
class="org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter">
<property name="userTokenConverter">
<bean class="com.example.oauth2.resource.converter.CustomUserAuthenticationConverter"/> <!-- (1) -->
</property>
</bean>
Sr. No. | Description |
---|---|
(1)
|
Define a Bean for
DefaultAccessTokenConverter . Specify CustomUserAuthenticationConverter class in userTokenConverter property to enable fetching of information other than user name by @AuthenticationPrincipal annotation. |
9.9.3.3. Customization of path in authorization server¶
Authorization server can change the path for the end point and forwarding destination when a specific situation occur. This section explains path for authorization server, and how to change the settings of related places.
9.9.3.3.1. Customizable path¶
As explained in How to use, end point in conformance with RFC and Controller for processing a request forwarded in authorization server are registered as components
by performing definition using <oauth2:authorization-server>
tag in the authorization server.
Also, the endpoints published by authorization server to client and resource server can be customized by changing
attribute value of <oauth2:authorization-server> tag.
Below, endpoints registered as components, and default path of forwarding destination and attribute value of <oauth2:authorization-server> tag changed while customizing the endpoint are shown.
Name | Attribute value | Default path | Description |
---|---|---|---|
Authorization endpoint
|
authorization-endpoint-url |
/oauth/authorized |
Endpoint used by client to fetch authorization from resource owner.
|
Token endpoint
|
token-endpoint-url |
/oauth/token |
Endpoint used by client to issue an access token.
|
Check token endpoint
(It is set only for implementation of Linking Authorization Server and Resource Server through HTTP access.)
|
check-token-endpoint-url |
/oauth/check_token |
Endpoint used by resource server to verify access token.
It is used to link access tokens through HTTP access. Further, this endpoint
is not an endpoint defined by RFC, but an endpoint independently expanded by Spring Security OAuth.
|
Name | Attribute value | Default path | Description |
---|---|---|---|
Forward destination for fetching authorization
|
user-approval-page |
/oauth/confirm_access |
Forwarding destination used for returning authorization screen to resource owner.
It is a path used in the authorization server process, and is not published for client or resource server.
|
Forwarding destination when an unauthorized client error occurs
|
error-page |
/oauth/error |
Forward destination to use for notifying an error in the authorization endpoint to resource owner.
It is a path used in the authorization server process, and is not published for client or resource server.
|
These paths can be customized by changing the setting on the authorization server side. A specific setting method is shown below.
9.9.3.3.2. Customization of path¶
9.9.3.3.2.1. Customization of end point¶
As described above, each endpoint can be customized by changing the attribute value of <oauth2:authorization-server> tag.
Implementation example while changing endpoint is shown below.
oauth2-auth.xml
<oauth2:authorization-server
client-details-service-ref="clientDetailsService"
user-approval-handler-ref="userApprovalHandler"
token-services-ref="tokenServices"
token-endpoint-url="/api/token"
authorization-endpoint-url="/api/authorize"
check-token-endpoint-url="/api/check_token"
check-token-enabled="true"> <!-- (1) -->
<!-- omitted -->
</oauth2:authorization-server>
<sec:http pattern="/api/*token*/**"
authentication-manager-ref="clientAuthenticationManager" realm="Realm"> <!-- (2) -->
<!-- omitted -->
</sec:http>
Sr. No. | Description |
---|---|
(1)
|
Set URL in attribute value (
authorization-endpoint-url ,
token-endpoint-url and check-token-endpoint-url or endpoints of <oauth2:authorization-server> tag to be changed |
(2)
|
Change the target for access control so that it includes URL of endpoint related to access token operations.
|
Settings to refer endpoints must be changed in accordance with the endpoints to be changed.
In authorization server, refer endpoint settings of spring-security.xml
of Resource owner authenticationand
oauthConfirm.jsp
of Customising scope authorization screen.
Also, when the client implementation is being performed, oauth2-client.xml
of Settings of OAuth2RestTemplateshould also be referred since
the endpoint of the authorization server is set as the setting of authorization request.
9.9.3.3.2.2. Customize forwarding destination¶
While fetching authorization from resource owner, it is forwarded to /oauth/confirm_access
in the default setting.
Also, when an unauthorized error client error occurs at the authorization endpoint, it is forwarded to /oauth/error
in default setting.
These forward destinations can be changed.
An implementation example while changing the forwarding destination is shown below.
oauth2-auth.xml
<oauth2:authorization-server
client-details-service-ref="clientDetailsService"
user-approval-handler-ref="userApprovalHandler"
token-services-ref="tokenServices"
error-page="forward:/api/error"
user-approval-page="forward:/api/confirm_access"> <!-- (1) -->
<!-- omitted -->
</oauth2:authorization-server>
Sr. No. | Description |
---|---|
(1)
|
Set URL in attribute value for the forwarding destination of
<oauth2:authorization-server> tag (error-page or user-approval-page ).
URL to be set must be assigned a prefix forward: . |
It must be noted that while changing the forward destination, change must be done in the corresponding controller as well. Refer following in the implementation example.
OAuth2ApprovalController.java
of Customising scope authorization screenOAuth2ErrorController.java
of Error handling at the time of authorization request
9.9.4. Appendix¶
9.9.4.1. Errors which occur while accessing authorization server and resource server from the client¶
In this guideline, methods of handling for errors which occur due to resource owner operations are described for the errors which occur while accessing authorization server and resource server from the client. The errors that occur along with errors which are caused by resource owner operations are explained.
9.9.4.1.1. Errors which occur at authorization end point¶
Errors which occur at authorization end point are classified as unauthorized client error and other errors, when an unauthorized client error occurs, error is notified by forwarding to error screen in authorization server. For details of unauthorized error, refer Unauthorized client error. Bad client error which occurs at authorization end point is explained below.
Sr. No. | Errors occurred | Description |
---|---|---|
(1)
|
Client does not exist error
NoSuchClientException |
It occurs when the client who has made user agent access the authorization server is not registered in the client information retained by authorization server.
|
(2)
|
Redirect URI mismatch error
org.springframework.security.oauth2.common.exceptions.RedirectMismatchException |
It occurs when host and root path of redirect URI which is sent as a parameter to authorization server by the client do not match with redirect URI registered in the authorization server.
|
When the error occurred is other than bad client error, error information (error
, error_description
) is notified to the client as a request parameter and restored to the exception in OAuth2RestTemplate
and is thrown.
Errors other than bad client error which occur at authorization end point are explained.
Sr. No. | Errors occurred | Description |
---|---|---|
(1)
|
Scope verification error
org.springframework.security.oauth2.common.exceptions.InvalidScopeException |
It occurs when scope specified by the client does not exist in the client information retained by authorization server.
|
(2)
|
Resource verification error
org.springframework.security.oauth2.common.exceptions.InvalidRequestException |
It occurs when redirect URI which indicates transition destination after authorization is not set or when authorization is done without displaying authorization screen.
|
(3)
|
Response type error
org.springframework.security.oauth2.common.exceptions.UnsupportedResponseTypeException |
It occurs when
response_type parameter specified by the client is not supported by authorization server. |
(4)
|
Grant type error
InvalidGrantException |
It occurs when a grant type that can be used by the client does not exist or when grant types other than authorization code grant and implicit grant which cannot be used by redirect URI are specified.
|
(5)
|
Authorization denied for resource owner
UserDeniedAuthorizationException |
It occurs when resource owner rejects all the scopes specified by client, in the scope authorization screen.
|
(6)
|
Resource owner authentication error
org.springframework.security.authentication.InsufficientAuthenticationException |
It occurs when the resource owner does not authenticate user while accessing authorization end point. It does not occur when appropriate security settings are done on the authorization server.
|
9.9.4.1.2. Errors which occur at token end point¶
When an error occurs at token end point, it is wrapped in OAuth2AccessDeniedException
by OAuth2RestTemplate
of client.
Errors which occur at token end point are explained below.
Sr. No. | Errors occurred | Description |
---|---|---|
(1)
|
Basic authentication error
HttpClientErrorException |
It occurs when Basic authentication of client fails.
|
(2)
|
Client authentication error
InsufficientAuthenticationException |
It occurs when authentication is not done before token end point process. It does not occur when appropriate security settings are done on the authorization server.
|
(3)
|
Scope verification error
InvalidScopeException |
It occurs when the scope specified by the client does not exist in the client information retained by authorization server.
It does not occur if
OAuth2RestTemplate is used since it occurs as an error at authorization end point while using authorization code grant. |
(4)
|
Resource owner authentication error
InvalidGrantException |
It occurs when an error is detected in authentication information of resource owner submitted while using resource owner password credential grant.
|
(5)
|
“Authorization code does not exist” error
InvalidGrantException |
It occurs when authorization code passed to the authorization server from the client does not exist in authorization server, while using authorization code grant.
|
In this guideline, it is assumed that OAuth2RestTemplate
of Spring Security OAuth is used as a client,
OAuth2RestTemplate
consists of a structure so that error does not occur for the settings are managed automatically.
Errors which occur when OAuth2RestTemplate
of Spring Security OAuth is not used are explained below, for developing a client without using Spring Security OAuth.
Sr. No. | Errors occurred | Description |
---|---|---|
(1)
|
Client verification error
org.springframework.security.oauth2.common.exceptions.InvalidClientException |
It occurs when authenticated client and the client set in request parameter do not match.
Basically, it occurs when client ID of client authenticated at token end point and the following client ID do not match.
|
(2)
|
Grant type error
org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException |
It occurs when an error is detected in
grant_type parameter specified in request to token end point.
|
(3)
|
Request verification error
InvalidRequestException |
It occurs when
grant_type parameter does not exist in the request to token end point. |
(4)
|
Redirect URI verification error
RedirectMismatchException |
It occurs when redirect URI used while accessing authorization end point and redirect URI set in the parameter while accessing token end point do not match.
|
9.9.4.1.3. Errors which occur in resource server¶
When an error occurs in the resource server, processing of OAuth2RestTemplate
of the client varies depending on errors occurred.
Errors which occur in resource server are explained below.
Sr. No. | Errors occurred | Description |
---|---|---|
(1)
|
Scope verification error
org.springframework.security.oauth2.common.exceptions.InsufficientScopeException |
It occurs when the scope required for accessing a protected resource does not exist in the scope retained by access token.
|
(2)
|
Resource ID verification error
UserDeniedAuthorizationException |
It occurs when resource ID required for accessing a protected resource does not exist in resource ID of client information associated with access token.
It is wrapped in
OAuth2AccessDeniedException by OAuth2RestTemplate of the client. |
(3)
|
Access token verification error
InvalidTokenException |
It occurs when validity of access token received from resource server could not be verified (abnormal system) or when access token has expired (normal system).
Since access token error is a combination of normal and abnormal systems, if you receive this error when not using implicit grant
OAuth2RestTemplate , it is necessary to determine whether the access token has expired.For basic implementation example, refer Accessing a resource server which uses JavaScript.
Note In this guideline, |
(4)
|
Check token end point Basic authentication error
HttpClientErrorException |
It occurs when Basic authentication fails while accessing check token end point, in Linking Authorization Server and Resource Server through HTTP access.
When error handling is to be performed at the client, error handling must be done at resource server so that resource server returns an appropriate error to the client.
|
9.9.4.2. Regarding Spring Security OAuth extension¶
Extension specifications of OAuth2.0 are listed in 8. Extensibilityof RFC 6749. Applications which provide authorization functions with OAuth 2.0 can be customised based on these.
Although Spring Security OAuth does not explicitly publish how to support extension points specified in RFC 6749 described earlier, following extension points are supported.
- 8.1 Defining Access Token Types
- 8.2 Defining New Endpoint Parameters
- 8.3 Defining New Authorization Grant Types
This section particularly explains main components and process flow of extension points for access token and its response related to 8.2 Defining New Endpoint Parameters.
9.9.4.2.1. Extension of access token request and its response¶
- Any parameter or header can be assigned to a request and its response as an extension point
- for access token request and its response.
However, as described earlier, extension points are not provided in the authorization request. Therefore, for example, when independent parameters are to be added in the authorization request, it must be noted that Spring Security OAuth API itself must be upgraded which requires a fairly large upgrade.
9.9.4.2.1.1. Client¶
RequestEnhancer
interface is provided in the client as an extension
point of access token request.
Main components and related diagrams for extension points are shown below.
Class, interface name | Description |
---|---|
OAuth2RestTemplate |
A class which extends
RestTemplate and adds OAuth 2.0 functions.It consists of OAuth 2.0 specific functions such as fetching access token according to grant type.
|
OAuth2ProtectedResourceDetails |
An interface of detail information to access a resource retained by resource server.
Derived classes are provided to retain parameters corresponding to grant type,
AuthorizationCodeResourceDetails is the implementation class for authorized code grant. |
AccessTokenProvider |
An interface which provides a function to fetch access tokens from authorization server.
Derived classes are provided according to the grant type, and providers for each grant type implements
AccessTokenProvider and inherits OAuth2AccessTokenSupport .In case of authorization code grant,
AuthorizationCodeAccessTokenProvider is the implementation class. |
OAuth2AccessTokenSupport |
A base class which provides a function to fetch access token from authorization server.
It provides a function to send access token request for authorization server, as a common process for grant types.
ClientAuthenticationHandler and RequestEnhancer described later are used for generation of access token request. |
AuthorizationCodeAccessTokenProvider |
A class which provides a function to fetch access token, in authorization code grant.
It provides a function to create an authorization request, as a unique process of authorization code grant.
|
ClientAuthenticationHandler |
An interface which provides a function to set client authentication information in header or form based on
OAuth2ProtectedResourceDetails .
DefaultClientAuthenticationHandler is a default implementation class. |
RequestEnhancer |
An interface which provides a function to set parameters in header or form based on access token request
and
OAuth2ProtectedResourceDetails .
DefaultRequestEnhancer is a default implementation class.One of the extension points in access token request.
Note that, when you want to add unique items to request parameters, only the Bean definition of
DefaultRequestEnhancer used as a default is required to be changed
and it is not necessary to create an implementation class of RequestEnhancer independently.Basically, a Bean definition should be done as shown below.
(1)
DefaultRequestEnhancer Set a key value in parameter which is to be added to
parameterIncludes attribute.(2)
AuthorizationCodeAccessTokenProvider Set
DefaultRequestEnhancer of (1) in tokenRequestEnhancer attribute(3)
OAuth2RestTemplate Set
AuthorizationCodeAccessTokenProvider of (2) in access-token-provider attribute. |
As an example, flow for accessing a client from resource in the authorization code grant is shown below.
Sr. No. | Description |
---|---|
(1)
|
Client side process is started by calling
OAuth2RestTemplate , while accessing a resource retained by the resource server.For flow till issue of authorization code on client side, refer Client.
|
(2)
|
After issuing authorization code,
OAuth2RestTemplate is called once again by redirecting to authorization server.
At that time, authorization code is retained in the session (OAuth2ClientContext ).OAuth2RestTemplate calls AccessTokenProvider corresponding to grant type defined in OAuth2ProtectedResourceDetails .AuthorizationCodeAccessTokenProvider is called as an implementation class of AccessTokenProvider . |
(3)
|
In
AuthorizationCodeAccessTokenProvider , client information from ClientAuthenticationHandler retained by OAuth2AccessTokenSupport , and
request parameter from RequestEnhancer are set in header or form, and access token request is created. |
(4)
|
OAuth2AccessTokenSupport uses the request created in (3) and sends access token request for the authorization server. |
(5)
|
In authorization server, client is authenticated and validity of authorization grant is verified.
When authorization grant is valid, access token is issued.
For flow of issue of access token, refer Issue of access token.
|
(6)
|
AccessTokenProvider returns the fetched access token to OAuth2RestTemplate .OAuth2RestTemplate retains the access token in the session (OAuth2ClientContext ) and uses it to send the request for the resource server. |
9.9.4.2.1.2. Authorization server¶
In authorization server, TokenEnhancer
interface is provided as an extension point
of access token response.
Main components related to extension points, and related diagrams are shown below.
Class, interface name | Description |
---|---|
AbstractEndpoint |
A base class which provides a common function of the end point receiving a request from the client, in the authorization server.
TokenEndpoint is the implementation class. |
TokenEndpoint |
A class which provides a function of endpoint for issuing access token, for a request from the client.
It consists of a function to verify client, and parameters included in the request parameter.
|
ClientDetailsService |
An interface which provides a function to verify client using OAuth 2.0 function.
Derived classes are provided for each medium which manages client information, and
JdbcClientDetailsService and InMemoryClientDetailsService are implementation classes. |
TokenGranter |
An interface which provides a function to issue access token.
Derived classes are provided corresponding to grant type. Provider for each grant always implements
TokenGranter and inherits AbstractTokenGranter .In case of authorization code grant,
AuthorizationCodeTokenGranter is the implementation class. |
AbstractTokenGranter |
A base class which provides a function to issue access token.
It consists of a function which fetches client information from request parameter, and verifies the grant type and calls inherent processing.
|
AuthorizationCodeTokenGranter |
A class which provides a function to issue access token in authorization code grant.
It provides a function to verify authorization code and redirect URI included in access token request, as a process unique to authorization code grant.
|
AuthorizationCodeServices |
An interface which provides a function to issue and manage authorization code, in authorization code grant.
Derived classes are provided for each medium which manages authorization code, and
JdbcAuthorizationServerTokenServices and InMemoryAuthorizationServerTokenServices are implementation classes. |
AuthorizationServerTokenServices |
An interface which provides a function to issue access token and refresh token, required for authorization server.
DefaultTokenServices is the default implementation class. |
TokenEnhancer |
An interface which provides a function to assign additional information to the token generated by
AuthorizationServerTokenServices .By extending this interface, it is possible to assign unique parameters to the information managed as access token, and the information returned to the client.
One of the extension points in access token response.
|
TokenStore |
An interface which provides a function to manage access token.
Derived classes are provided for each medium which manages access token, and
JdbcTokenStore are InMemoryTokenStore implementation classes. |
As an example, flow of accessing token end point of authorization server, in authorization code grant is shown below.
Sr. No. | Description |
---|---|
(1)
|
Process on authorization server side is started by calling
TokenEndpoint . |
(2)
|
ClientDetailsService retained by AbstractEndpoint is called in TokenEndpoint and is included in the access token request.
TokenGranter is called after verifying client information and parameter.AuthorizationCodeTokenGranter is the implementation class of TokenGranter . |
(3)
|
In
TokenGranter , process of AbstractTokenGranter - a base class is carried out at first.In
AbstractTokenGranter , client information specified in request parameter by calling ClientDetailsService is fetched,
and after verification of grant, grant type specific process (AuthorizationCodeTokenGranter ) is called. |
(4)
|
In
AuthorizationCodeTokenGranter , AuthorizationCodeServices is called by specifying authorization code fetched from request parameter, information at the time of authorization request is fetched along with deletion of issued authorization code. |
(5)
|
In
AuthorizationCodeServices , client ID specified as a access token request parameter of authorization code grant, redirect URI and authorization request details fetched in (4) are compared and access token request validity is verified.If there are no problems in the verification results, authentication information is created from request parameter and authentication information of resource owner (user name etc.) fetched while requesting authorization and returned to the caller.
|
(6)
|
A process for fetching access token of
AuthorizationServerTokenServices is called in AbstractTokenGranter .DefaultTokenServices is the implementation class of AuthorizationServerTokenServices . |
(7)
|
Access token is created in
DefaultTokenServices .When
TokenEnhancer is specified, access token can be extended and additional parameters can be added. |
(8)
|
TokenStore is called, access token created in (7) is registered along with authentication information created in (5) and the access token is returned TokenEndpoint for calling.A response is created in
TokenEndpoint based on access token created in (7) and a response is sent for the request. |