(Quick Reference)

Grails OAuth 2 Provider Plugin - Reference Documentation

Authors: Brian Saville, Bobby Vandiver, Roy Willemse

Version: 3.0.0-RC2

1 Introduction to the Spring Security OAuth2 Plugin

The OAuth2 plugin adds OAuth 2.0 support to a Grails application that uses Spring Security. It depends on Spring Security Core plugin.

Under the covers, Spring Security OAuth is used by the plugin to provide OAuth 2.0 services. This documentation specifies a few specific steps you will have to take in order to ensure proper integration with the underlying library.

This plugin provides support for Grails domain classes necessary for providing OAuth 2.0 authorization. The standard grant types described in RFC 6749 are supported by the plugin. Access to protected resources is controlled by a combination of Spring Security Core's methods, i.e. request maps, annotations, intercept maps and careful configuration of the Spring Security filter chains.

1.1 Change Log

  • 3.0.0-RC2
    • Upgrade to Spring Security OAuth 2.0.9-RELEASE
    • Fix issues with Grails 3.1.x (#114, PR #116)
  • 3.0.0-RC1
    • Upgrade to Grails 3.x
    • Make `readAuthentication` in `GormTokenStoreService` null safe (pull request #109)
    • Upgrade to Spring OAuth 2.0.8.RELEASE
  • 2.0-RC5
    • Upgrade to Spring OAuth 2.0.7.RELEASE for compatibility with Spring Security Core RC5 (issue #100)
    • Resolve minor problems affecting stateless access of OAuth 2.0 resources
    • Remove need to include `clientCredentialsAuthenticationProvider` in `grails.plugin.springsecurity.providerNames` list
    • Document using plugin to create only authorization server only or only a resource server (issue #71)
  • 2.0-RC4
    • Fix for Grails 2.5.0 (issue #76)
    • Add support for basic authentication (issue #80)
    • Fix access token header format in the docs (issue #84)
    • Throw exception on validation code save (issue #90)
    • Fixes and enhancements for additional information (issue #87)
    • Add support for unlimited refresh tokens (issue #75)
  • 2.0-RC3
    • Upgrade to Spring OAuth 2.0.6.RELEASE (issue #63)
    • Fix problems with updating access tokens (issues #49, #50, and #68)
    • Add TravisCI build
    • Ensure Set-Cookie header is not set in response
    • Fix handling of scope parameter (issue #64)
  • 2.0-RC2
    • Resolves session vulnerability (issue #42)
    • Upgrade to Spring Security OAuth2 2.0.4.RELEASE
    • Supports authorization auto-approval
    • Minor tweaks to domain models
  • 2.0-RC1
    • Complete overhaul of the plugin
    • Requires/supports Spring Security Core 2.0-RC4
    • Uses Spring Security OAuth2 2.0.2.RELEASE
  • 1.0.5.2
    • Fix #13 - Make clientSecret optional in client configuration structure
  • 1.0.5.1
    • Merge pull request #21 (Burt's cleanup)
    • Use log wrapper instead of log4j
    • Depends on Grails 2.0 or greater (consistent with core plugin)
  • 1.0.5
    • Initial release of plugin compatible with spring security core 2.0-RC2

2 Getting Started

The following assumes that the Spring Security Core plugin has been installed and its required domain class created.

2.1 Install Plugin

Install the OAuth2 plugin by adding a dependency in build.gradle:

dependencies {
    compile 'org.grails.plugins:spring-security-oauth2-provider:3.0.0-SNAPSHOT'
}

This has a dependency on the Spring Security Core plugin, which will be installed if necessary.

2.2 Create Domain Classes

Run the s2-init-oauth2-provider script to generate the required domain classes.

2.3 Secure Authorization and Token Endpoints

Update the Core plugin's rules for the authorization and token endpoints so they are protected by Spring Security. If you're using the Core plugin's staticRules, you'll want to add the following in grails-app/conf/application.groovy:

grails.plugin.springsecurity.controllerAnnotations.staticRules = [
        [pattern: '/oauth/authorize',           access: "isFullyAuthenticated() and (request.getMethod().equals('GET') or request.getMethod().equals('POST'))"],
        [pattern: '/oauth/token',               access: "isFullyAuthenticated() and request.getMethod().equals('POST')"],
        ...

The endpoints are standard Spring MVC controllers in the underlying Spring Security OAuth2 implementations and the URLs must be mapped with .dispatch.

The additional restrictions on the allowed HTTP methods are to ensure compliance with the OAuth 2.0 spec as defined in RFC 6749.

2.4 (Optional) Customize Error and Confirm Access Views

The plugin provides views for the error and confirm access pages. These can be overridden by providing your own grails-app/views/oauth/error.gsp and grails-app/views/oauth/confirm_access.gsp files, respectively.

2.5 Client Registration

At this point your application is a proper OAuth 2.0 provider. You can now register clients in what ever method is appropriate for your application. For example, you can register a client in grails-app/init/Bootstrap.groovy as follows:

def init = { servletContext ->
        new Client(
                clientId: 'my-client',
                authorizedGrantTypes: ['authorization_code', 'refresh_token', 'implicit', 'password', 'client_credentials'],
                authorities: ['ROLE_CLIENT'],
                scopes: ['read', 'write'],
                redirectUris: ['http://myredirect.com']
        ).save(flush: true)
    }

2.6 Controlling Access to Resources

Access to resources is controlled by the Spring Security Core plugin's access control mechanisms. Additionally, the plugin has full support for the OAuth 2.0 SPeL extensions provided by the underlying Spring library. Refer to the methods in OAuth2SecurityExpressionMethods for what is available in the plugin.

Using SPeL is the only tested and confirmed way to enforce OAuth 2.0 specific restrictions on resource access.

The following controller illustrates the use of OAuth 2.0 SPeL:

class SecuredOAuth2ResourcesController {

@Secured(["#oauth2.clientHasRole('ROLE_CLIENT')"]) def clientRoleExpression() { render "client role expression" }

@Secured(["ROLE_CLIENT"]) def clientRole() { render "client role" }

@Secured(["#oauth2.clientHasAnyRole('ROLE_CLIENT', 'ROLE_TRUSTED_CLIENT')"]) def clientHasAnyRole() { render "client has any role" }

@Secured(["#oauth2.isClient()"]) def client() { render "is client" }

@Secured(["#oauth2.isUser()"]) def user() { render "is user" }

@Secured(["#oauth2.denyOAuthClient()"]) def denyClient() { render "no client can see" }

@Secured(["permitAll"]) def anyone() { render "anyone can see" }

def nobody() { render "nobody can see" }

@Secured(["#oauth2.clientHasRole('ROLE_TRUSTED_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('trust')"]) def trustedClient() { render "trusted client" }

@Secured(["hasRole('ROLE_USER') and #oauth2.isUser() and #oauth2.hasScope('trust')"]) def trustedUser() { render "trusted user" }

@Secured(["hasRole('ROLE_USER') or #oauth2.hasScope('read')"]) def userRoleOrReadScope() { render "user role or read scope" } }

The filter chains must be configured to ensure stateless access to the token endpoint and any OAuth 2.0 resources:

grails.plugin.springsecurity.filterChain.chainMap = [
        [pattern: '/oauth/token',               filters: 'JOINED_FILTERS,-oauth2ProviderFilter,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-exceptionTranslationFilter'],
        [pattern: '/securedOAuth2Resources/**', filters: 'JOINED_FILTERS,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-oauth2BasicAuthenticationFilter,-exceptionTranslationFilter'],
        [pattern: '/**',                        filters: 'JOINED_FILTERS,-statelessSecurityContextPersistenceFilter,-oauth2ProviderFilter,-clientCredentialsTokenEndpointFilter,-oauth2BasicAuthenticationFilter,-oauth2ExceptionTranslationFilter']
]

Please consult the section on Filter Chain Configuration for more information.

2.7 Trouble Shooting

If an instance of one of the GORM backed classes that the plugin uses cannot be saved, an OAuth2ValidationException will be thrown. This is a subclass of the standard Grails ValidationException so the plugin consumer has the flexibility to determine how to deal with this type of error. The typical reason for this exception being thrown will likely be related to the max size allotted to the serialized OAuth2Authentication fields. The thrown exception can be inspected for further information about the Errors.

3 Example Flows

The following examples assume you have followed the steps outlined in the Getting Started section for an application named oauth2-test and your grails-app/init/BootStrap.groovy contains the following:

def init = { servletContext ->

Role roleUser = new Role(authority: 'ROLE_USER').save(flush: true)

User user = new User( username: 'my-user', password: 'my-password', enabled: true, accountExpired: false, accountLocked: false, passwordExpired: false ).save(flush: true)

UserRole.create(user, roleUser, true)

new Client( clientId: 'my-client', authorizedGrantTypes: ['authorization_code', 'refresh_token', 'implicit', 'password', 'client_credentials'], authorities: ['ROLE_CLIENT'], scopes: ['read', 'write'], redirectUris: ['http://myredirect.com'] ).save(flush: true) }

After retrieving an access_token via one of the flows, you must include this in the Authorization header when accessing protected resources.

For example, if you receive 7b9a989e-3702-4621-a631-fbd1a996fc94 as the access_token, you will include this in the Authorization header as Bearer 7b9a989e-3702-4621-a631-fbd1a996fc94 when requesting a protected resource.

The examples below are given using CURL tool to make the requests. The plugin is compliant with RFC 6749 when configured properly. Therefore token requests should be made using an HTTP POST and authorization requests should be initiated by the User-Agent with an HTTP GET.

3.1 Authorization Code Grant

The authorization code grant flow is initiated by directing your browser to the authorization endpoint:

http://localhost:8080/oauth2-test/oauth/authorize?response_type=code&client_id=my-client&scope=read

You will be redirected to the login page. After signing in, you will be prompted to confirm the request. Doing so will redirect your browser to the following URL:

http://myredirect.com/?code=139R59

The authorization code included in the query can be exchanged for an access token via the token endpoint:

curl -X POST \
     -d "client_id=my-client" \
     -d "grant_type=authorization_code" \
     -d "code=139R59" http://localhost:8080/oauth2-test/oauth/token

Using HTTP Basic for client authentication:

curl -X POST -u my-client: \
     -d "grant_type=authorization_code" \
     -d "code=139R59" http://localhost:8080/oauth2-test/oauth/token

You'll receive the access_token in the response:

{
    "access_token": "a1ce2915-8d79-4961-8abb-2c6f0fdb4aba",
    "token_type": "bearer",
    "refresh_token": "6540222d-0fb9-4b01-8d45-7be2bdfb68f9",
    "expires_in": 43199,
    "scope": "read"
}

3.2 Implicit Grant

The implicit grant is similar to the authorization code grant and can be initiated by directing your browser to the authorization endpoint:

http://localhost:8080/oauth2-test/oauth/authorize?response_type=token&client_id=my-client&scope=read

Upon confirmation, your browser will be redirected to the following URL:

http://myredirect.com/#access_token=4e22ad4f-08ae-49dc-befb-2c9821af04d1&token_type=bearer&expires_in=43199

The access_token can be extracted from the URL fragment.

3.3 Resource Owner Password Credentials Grant

The resource owner password grant is performed by requesting an access token from the token endpoint:

curl -X POST \
     -d "client_id=my-client" \
     -d "grant_type=password" \
     -d "username=my-user" \
     -d "password=my-password" \
     -d "scope=read" http://localhost:8080/oauth2-test/oauth/token

Using HTTP Basic for client authentication:

curl -X POST -u my-client: \
     -d "grant_type=password" \
     -d "username=my-user" \
     -d "password=my-password" \
     -d "scope=read" http://localhost:8080/oauth2-test/oauth/token

The access_token is included in the response:

{
    "access_token": "1d49fc35-2af6-477e-8fd4-ab0353a4a76f",
    "token_type": "bearer",
    "refresh_token": "4996ba33-be3f-4555-b3e3-0b094a4e60c0",
    "expires_in": 43199,
    "scope": "read"
}

3.4 Client Credentials Grant

The client credentials grant is performed by authenticating the client via the token endpoint:

curl -X POST \
     -d "client_id=my-client" \
     -d "grant_type=client_credentials" \
     -d "scope=read" http://localhost:8080/oauth2-test/oauth/token

Using HTTP Basic for client authentication:

curl -X POST -u my-client: \
     -d "grant_type=client_credentials" \
     -d "scope=read" http://localhost:8080/oauth2-test/oauth/token

The access_token can be extracted from the response:

{
    "access_token": "7b9a989e-3702-4621-a631-fbd1a996fc94",
    "token_type": "bearer",
    "expires_in": 43199,
    "scope": "read"
}

3.5 Refresh Token Grant

The refresh token grant is performed by exchanging a refresh token received during a previous authorization request for an access token from the token endpoint:

curl -X POST \
     -d "client_id=my-client" \
     -d "grant_type=refresh_token" \
     -d "refresh_token=269afd46-0b41-45c2-a920-7d5af8a38d56" \
     -d "scope=read" http://localhost:8080/oauth2-test/oauth/token

Using HTTP Basic for client authentication:

curl -X POST -u my-client: \
     -d "grant_type=refresh_token" \
     -d "refresh_token=269afd46-0b41-45c2-a920-7d5af8a38d56" \
     -d "scope=read" http://localhost:8080/oauth2-test/oauth/token

The above assumes that 269afd46-0b41-45c2-a920-7d5af8a38d56 is the value of the refresh token the client had obtained prior to this request.

The access_token is included in the response:

{
    "access_token": "a3da52c7-4bd2-4d42-a58d-efa64b4de453",
    "token_type": "bearer",
    "refresh_token": "6396c283-47ff-41d2-b887-39bde6af5f1e",
    "expires_in": 43199,
    "scope": "read"
}

4 Required Domain Classes

The plugin uses regular Grails domain classes backed by GORM. There are four required domain classes representing clients, access tokens, refresh tokens and authorization codes that you'll need.

The s2-init-oauth2-provider script will create the domain classes for you in a specified package and update grails-app/conf/application.groovy so the plugin recognizes them. You can customize the generated classes to fit your needs. If you change the default property names, you will need to update grails-app/conf/application.groovy so the plugin is aware of the changes. See the section on domain class properties for more information.

The maxSize constraints in the generated domain classes have been set to reasonable defaults. However, tweaking may be required if you are using longer usernames (email addresses for example), or have many authorities attached to a single user.

The below discussion assumes the s2-init-oauth2-provider script has been run with com.yourapp specified as the package and Client, AccessToken, RefreshToken and AuthorizationCode as the names of your domain classes.

4.1 Client Class

Information from the Grails client domain class will be extracted to create a ClientDetails instance for the underlying Spring Security OAuth 2.0 implementation.

The generated class will look like this:

package com.yourapp

class Client {

private static final String NO_CLIENT_SECRET = ''

transient springSecurityService

String clientId String clientSecret

Integer accessTokenValiditySeconds Integer refreshTokenValiditySeconds

Map<String, Object> additionalInformation

static hasMany = [ authorities: String, authorizedGrantTypes: String, resourceIds: String, scopes: String, autoApproveScopes: String, redirectUris: String ]

static transients = ['springSecurityService']

static constraints = { clientId blank: false, unique: true clientSecret nullable: true

accessTokenValiditySeconds nullable: true refreshTokenValiditySeconds nullable: true

authorities nullable: true authorizedGrantTypes nullable: true

resourceIds nullable: true

scopes nullable: true autoApproveScopes nullable: true

redirectUris nullable: true additionalInformation nullable: true }

def beforeInsert() { encodeClientSecret() }

def beforeUpdate() { if(isDirty('clientSecret')) { encodeClientSecret() } }

protected void encodeClientSecret() { clientSecret = clientSecret ?: NO_CLIENT_SECRET clientSecret = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(clientSecret) : clientSecret } }

The client secret is encoded using the same strategy that is configured by the Core plugin for handling passwords. Optional client secrets are also supported out of the box.

4.2 Access Token Class

This class represents an access token than has been issued to a client on behalf of a user. The authentication object serialized is an instance of OAuth2Authentication from Spring Security OAuth 2.0.

package com.yourapp

class AccessToken {

String authenticationKey byte[] authentication

String username String clientId

String value String tokenType

Date expiration Map<String, Object> additionalInformation

static hasOne = [refreshToken: String] static hasMany = [scope: String]

static constraints = { username nullable: true clientId nullable: false, blank: false value nullable: false, blank: false, unique: true tokenType nullable: false, blank: false expiration nullable: false scope nullable: false refreshToken nullable: true authenticationKey nullable: false, blank: false, unique: true authentication nullable: false, minSize: 1, maxSize: 1024 * 4 additionalInformation nullable: true }

static mapping = { version false scope lazy: false } }

4.3 Refresh Token Class

This class represents a refresh token issued as part of one of the grants that supports issuing a refresh token. The length of time the refresh token is valid is determined by the token services and can be configured. See token services configuration for more. The authentication object serialized is an instance of OAuth2Authentication from Spring Security OAuth 2.0.

package com.yourapp

class RefreshToken {

String value Date expiration byte[] authentication

static constraints = { value nullable: false, blank: false, unique: true expiration nullable: true authentication nullable: false, minSize: 1, maxSize: 1024 * 4 }

static mapping = { version false } }

If a non-expiring refresh token is desired, the client issuing the refresh token should be configured to return a 0 or less for the refresh token validity length in accordance with the behavior of Spring Security OAuth beginning with 2.0.6.RELEASE. A non-expiring GORM refresh token will be stored with a null expiration. When reading a GORM refresh token, if the expiration field is null, an ExpiringOAuth2RefreshToken instance will be created and returned for processing by Spring Security OAuth. Otherwise an instance of OAuth2RefreshToken will be created and used.

4.4 Authorization Code Class

This class represents an authorization code that has been issued via the authorization endpoint as part of an authorization code grant. The authentication object serialized is an instance of OAuth2Authentication from Spring Security OAuth 2.0.

package com.yourapp

class AuthorizationCode {

byte[] authentication String code

static constraints = { code nullable: false, blank: false, unique: true authentication nullable: false, minSize: 1, maxSize: 1024 * 4 }

static mapping = { version false } }

5 Optional Domain Classes

The plugin provides support for using a GORM backed ApprovalStore with the ApprovalStoreUserApprovalHandler provided by the underlying Spring OAuth library. This class is only required if the consuming application is configured to use the UserApprovalSupport.APPROVAL_STORE method of auto-approval.

The s2-init-oauth2-approval script will create the required domain class for you in a specified package and update grails-app/conf/application.groovy so the plugin recognizes it. You can customize the generated class to fit your needs. If you change the default property names, you will need to update grails-app/conf/application.groovy so the plugin is aware of the changes. See the section on domain class properties for more information.

The below discussion assumes the s2-init-oauth2-approval script has been run with com.yourapp specified as the package and Approval as the name of the domain class.

5.1 Approval Class

This class represents a prior scoped approval granted to a client by a user.

package com.yourapp

class Approval {

String username String clientId

String scope boolean approved

Date expiration Date lastModified

static constraints = { username nullable: false, blank: false clientId nullable: false, blank: false scope nullable: false, blank: false expiration nullable: false lastModified nullable: false } }

6 Domain Class Properties

No default class name is assumed for the required domain classes. They must be specified in grails-app/conf/application.groovy. This is done automatically by the s2-init-oauth2-provider script. The following properties exist in the grails.plugin.springsecurity.oauthProvider namespace.

6.1 Client Class Properties

PropertyDefault ValueMeaning
clientLookup.classNamenullClient class name.
clientLookup.clientIdPropertyName'clientId'Client class client ID field.
clientLookup.clientSecretPropertyName'clientSecret'Client class client secret field.
clientLookup.accessTokenValiditySecondsPropertyName'accessTokenValiditySeconds'Client class access token validity length field.
clientLookup.refreshTokenValiditySecondsPropertyName'refreshTokenValiditySeconds'Client class refresh token validity length field.
clientLookup.authoritiesPropertyName'authorities'Client class authorities field.
clientLookup.authorizedGrantTypesPropertyName'authorizedGrantTypes'Client class authorized grant types field.
clientLookup.resourceIdsPropertyName'resourceIds'Client class allowed resource IDs field.
clientLookup.scopesPropertyName'scopes'Client class scopes field.
clientLookup.autoApproveScopesPropertyName'autoApproveScopes'Client class auto-approved scopes field. Including a value of true in the list will auto-approve all scopes for the configured client.
clientLookup.redirectUrisPropertyName'redirectUris'Client class redirect URIs field.
clientLookup.additionalInformationPropertyName'additionalInformation'Client class additional information field.

6.2 Access Token Class Properties

PropertyDefault ValueMeaning
accessTokenLookup.classNamenullAccess token class name.
accessTokenLookup.authenticationKeyPropertyName'authenticationKey'Access token class serialized authentication key used to locate tokens via serialized authentication field.
accessTokenLookup.authenticationPropertyName'authentication'Access token class serialized authentication field.
accessTokenLookup.usernamePropertyName'username'Access token class username field.
accessTokenLookup.clientIdPropertyName'clientId'Access token class client ID field.
accessTokenLookup.valuePropertyName'value'Access token class value field.
accessTokenLookup.tokenTypePropertyName'tokenType'Access token class token type field.
accessTokenLookup.expirationPropertyName'expiration'Access token class expiration field.
accessTokenLookup.refreshTokenPropertyName'refreshToken'Access token class refresh token value field.
accessTokenLookup.scopePropertyName'scope'Access token class scope field.
accessTokenLookup.additionalInformationPropertyName'additionalInformation'Access token class additional information field.

Currently only 'bearer' tokens are supported.

6.3 Refresh Token Class Properties

PropertyDefault ValueMeaning
refreshTokenLookup.classNamenullRefresh token class name.
refreshTokenLookup.authenticationPropertyName'authentication'Refresh token class serialized authentication field.
refreshTokenLookup.valuePropertyName'value'Refresh token class value field.
refreshTokenLookup.expirationPropertyName'expiration'Refresh

6.4 Authorization Code Class Properties

PropertyDefault ValueMeaning
authorizationCodeLookup.classNamenullAuthorization code class name.
authorizationCodeLookup.authenticationPropertyName'authentication'Authorization code class serialized authentication field.
authorizationCodeLookup.codePropertyName'code'Authorization code class code field.

7 Configuration

The plugin is pessimistic by default, locking down as much as possible to guard against accidental security breaches. However, these constraints can be modified if so desired in grails-app/conf/application.groovy. The properties below exist in the grails.plugin.springsecurity.oauthProvider namespace.

7.1 Plugin

The following properties define whether the plugin is active and where the required filters are registered in the Spring Security filter chain:

PropertyDefault ValueMeaning
activetrueWhether the plugin is enabled.
filterStartPositionSecurityFilterPosition.X509_FILTER.orderThe position in the filter chain of the OAuth2AuthenticationProcessingFilter, which handles authentication for resource access by extracting an access token from the incoming request.
clientFilterStartPositionSecurityFilterPosition.DIGEST_AUTH_FILTER.orderThe position in the filter chain of the ClientCredentialsTokenEndpointFilter, which handles client authentication.
statelessFilterStartPositionSecurityFilterPosition.SECURITY_CONTEXT_FILTER.orderThe position in the filter chain of the StatelessSecurityContextPersistenceFilter, which is used to ensure access to OAuth 2.0 resources and the token endpoint are stateless.
exceptionTranslationFilterStartPositionSecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.orderThe position in the filter chain of the ExceptionTranslationFilter configured with a NullRequestCache, which is used to ensure access to OAuth 2.0 resources and the token endpoint are stateless.
basicAuthenticationFilterStartPositionSecurityFilterPosition.BASIC_AUTH_FILTER.orderThe position in the filter chain of the BasicAuthenticationFilter configured with a NullRememberMeServices, which is used to ensure access to OAuth 2.0 resources and the token endpoint are stateless.
registerStatelessFiltertrueWhen this is true, the plugin will register the statelessSecurityContextPersistenceFilter in the filter chain after the securityContextPersistenceFilter provided by the Spring Security Core plugin. See below for additional configuration of the filter chain(s) required to properly secure access to OAuth 2.0 resources.
registerExceptionTranslationFiltertrueWhen this is true, the plugin will register the oauth2ExceptionTranslationFilter in the filter chain after the exceptionTranslationFilter provided by the Spring Security Core plugin. See below for additional configuration of the filter chain(s) required to properly secure access to OAuth 2.0 resources.
registerBasicAuthenticationFiltertrueWhen this is true, the plugin will register the oauth2BasicAuthenticationFilter in the filter chain after the basicAuthenticationFilter provided by the Spring Security Core plugin. See below for additional configuration of the filter chain(s) required to properly secure access to OAuth 2.0 resources.
realmNameGrails OAuth2 RealmRealm name included in the WWW-Authenticate header in a challenge response.

7.2 Endpoint URLs

The endpoint URLs used by the underlying Spring Security OAuth 2.0 implementation can be changed using the following properties:

PropertyDefault ValueMeaning
authorizationEndpointUrl'/oauth/authorize'Authorization endpoint URL.
tokenEndpointUrl'/oauth/token'Token endpoint URL.
userApprovalEndpointUrl'/oauth/confirm_access'URL of the view to display for confirming access to protected resources.
userApprovalParameter'user_oauth_approval'The name of the parameter submitted in the confirmation request. The value of this parameter is used to determine whether a user has confirmed (true) or denied (false) access.
errorEndpointUrl'/oauth/error'URL of the view to display if an error occurs while interacting with the authorization endpoint and the error cannot be returned as part of the query or fragment of the client's redirect URI. This is usually the case when there is a problem with the requesting client's redirect URI.

When changing the URL for the authorizationEndpointUrl or tokenEndpointUrl, you must update Spring Security Core's configuration. Using the staticRules configuration and the default configuration as an example, your grails-app/conf/Config.groovy will look like this:

grails.plugin.springsecurity.controllerAnnotations.staticRules = [
        [pattern: '/oauth/authorize',           access: "isFullyAuthenticated() and (request.getMethod().equals('GET') or request.getMethod().equals('POST'))"],
        [pattern: '/oauth/token',               access: "isFullyAuthenticated() and request.getMethod().equals('POST')"],
        ...

To change the authorizationEndpointUrl to /authorize, you will need to make the following changes in grails-app/conf/application.groovy:

grails.plugin.springsecurity.oauthProvider.authorizationEndpointUrl = '/authorize'

grails.plugin.springsecurity.controllerAnnotations.staticRules = [ [pattern: '/authorize', access: "isFullyAuthenticated() and (request.getMethod().equals('GET') or request.getMethod().equals('POST'))"], [pattern: '/oauth/token', access: "isFullyAuthenticated() and request.getMethod().equals('POST')"], ...

7.3 Token Services

The following properties apply to how tokens are issued and how long they are valid. If a client has defined a specific token validity length, the client's length will take priority over the values configured for the token services.

PropertyDefault ValueMeaning
tokenServices.registerTokenEnhancerstrueWhether registered TokenEnhancer instances should be used.
tokenServices.accessTokenValiditySeconds60 * 60 * 12The length of time that an access token will be valid after it has been issued.
tokenServices.refreshTokenValiditySeconds60 * 60 * 24 * 30The length of time that a refresh token will be valid after it has been issued.
tokenServices.reuseRefreshTokenfalseWhether a new refresh token should be generated if one is currently available.
tokenServices.supportRefreshTokentrueWhether a refresh token can be issued upon request.

7.4 Token Enhancers Configuration

By default, the plugin will register a TokenEnhancerChain with an empty list of TokenEnhancer delegates. When the tokenServices.registerTokenEnhancers option is true, the plugin will detect and use all registered Spring beans implementing the TokenEnhancer interface.

If more control over the ordering of the enhancers in the chain is desired, set the tokenServices.registerTokenEnhancers option to false. The TokenEnhancerChain bean is registered under the name tokenEnhancerChain, so the plugin consumer can get a handle to it for more fine grained configuration.

This bean is aliased under the name tokenEnhancer. This is the bean that is registered with the tokenServices bean. This is done to allow customization of the registered enhancer with minimal effort. Just override the tokenEnhancer bean.

7.5 Supported Grant Types

The following properties determine which of the standard grant types the application can support. Individual clients must declare which of the enabled grant types they support.

PropertyDefault ValueMeaning
grantTypes.authorizationCodetrueWhether the Authorization Code Grant is supported.
grantTypes.implicittrueWhether the Implicit Grant is supported.
grantTypes.clientCredentialstrueWhether the Client Credentials Grant is supported.
grantTypes.passwordtrueWhether the Resource Owner Password Credentials is supported.
grantTypes.refreshTokentrueWhether Refresh Token Grant is supported.

7.6 Additional Authorization Constraints

The plugin enforces the following restrictions on authorization request params:

PropertyDefault ValueMeaning
authorization.requireRegisteredRedirectUritrueWhether a client is required to have registered a redirect URI before performing an authorization request. This addresses RFC 6749 Section 10.6: Authorization Code Redirection URI Manipulation and RFC 6749 Section 10.15: Open Redirectors .
authorization.requireScopetrueWhether the scope for each access token requested is required.

7.7 User Approval Configuration

The plugin provides support for the three UserApprovalHandler implementations provided by the underlying Spring OAuth library. This only applies to the authorization endpoint and allows you to configure the method of auto-approval used by the application. The following properties determine which method of auto-approval to use and how it is configured:

PropertyDefault ValueMeaning
autoEXPLICITDetermines which method of auto-approval to use. The value is determined by grails.plugin.springsecurity.oauthprovider.approval.UserApprovalSupport and must be EXPLICIT, TOKEN_STORE or APPROVAL_STORE.
handleRevocationAsExpiryfalseWhen configured to use an approval store for auto-approval, this determines if approval revocation should expire the corresponding approval instance (true) or delete the approval (false) outright.
approvalValiditySeconds60 * 60 * 24 * 30When configured to use an approval store for auto-approval, the length of time that an approval will be valid after it has been granted.
scopePrefix'scope.'When configured to use an approval store for auto-approval, the prefix added to approved scopes as part of the auto-approval process.

The auto property determines which of the three UserApprovalHandler provided by Spring OAuth will be used.

The default option is to require explicit approval for every authorization and is equivalent to setting auto to EXPLICIT:

grails.plugin.springsecurity.oauthprovider.approval.auto = UserApproval.EXPLICIT

Auto-approval based on previously issued access tokens is supported via the TokenStoreUserApprovalHandler provided by Spring OAuth and can be configured by setting auto to TOKEN_STORE:

grails.plugin.springsecurity.oauthprovider.approval.auto = UserApproval.TOKEN_STORE

Auto-approval based on prior approvals is supported via the ApprovalStoreUserApprovalHandler provided by Spring OAuth and can be configured by setting auto to APPROVAL_STORE:

grails.plugin.springsecurity.oauthprovider.approval.auto = UserApproval.APPROVAL_STORE

The plugin will configure the TokenStoreUserApprovalHandler and ApprovalStoreUserApprovalHandler to use the GORM backed TokenStore and ApprovalStore respectively.

Please consult Spring OAuth directly for more information on the usage of the TokenStore and ApprovalStore methods of auto-approval.

7.8 Default Client Configuration

An application can use the following properties to define the default values that will be used when creating a ClientDetails instance if a client has not specified a value. The default configuration will not allow a client to retrieve an access token unless they have explicitly registered support for the requested grant type.

PropertyDefault ValueMeaning
defaultClientConfig.resourceIds[]Resources the client is authorized to access. This is currently unused as access to resources is controlled by Spring Security Core's rules.
defaultClientConfig.authorizedGrantTypes[]Grant types the client supports.
defaultClientConfig.scope[]Scope to use for each access token request.
defaultClientConfig.autoApproveScopes[]Scopes to auto-approve for authorization requests. Including a value of true in the list will auto-approve all scopes for clients using the default configuration.
defaultClientConfig.registeredRedirectUrinullURI to redirect the user-agent to during an authorization code or implicit grant.
defaultClientConfig.authorities[]Roles and authorities granted to the client.
defaultClientConfig.accessTokenValiditySecondsnullThe length of time that an access token will be valid after it has been issued. This is used instead of the length configured for token services if available.
defaultClientConfig.refreshTokenValiditySecondsnullThe length of time that a refresh token will be valid after it has been issued. This is used instead of the length configured for token services if available.
defaultClientConfig.additionalInformation[:]Additional information about the client. This is not required by OAuth 2.0 but is exposed in the underlying Spring library.

7.9 Filter Chain Configuration

Spring Security Core plugin's securityContextPersistenceFilter stores state in the HTTP session. Access to the token endpoint and OAuth 2.0 resources must be stateless.

By default, the OAuth2 plugin will register the statelessSecurityContextPersistenceFilter in the filter chain after the securityContextPersistenceFilter provided by the Spring Security Core plugin. This is provided as a convenience for the plugin consumer, so they can remove one filter or the other to easily achieve stateful or stateless request handling. See the example below. This automatic filter registration can be disabled by setting the registerStatelessFilter configuration option to false.

The plugin registers an OAuth2AuthenticationProcessingFilter under the bean name oauth2ProviderFilter. This filter provides token authentication using the underlying token store for resource access.

The plugin registers a ClientCredentialsTokenEndpointFilter under the bean name clientCredentialsTokenEndpointFilter. This filter is responsible for authenticating the client specified in any OAuth 2.0 requests. The plugin also registers a BasicAuthenticationFilter under the bean name oauth2BasicAuthenticationFilter. This filter is responsible for authenticating the client via HTTP Basic authentication, which is the recommended method in the OAuth 2.0 specification.

Finally, the plugin registers an ExceptionTranslationFilter under the bean name oauth2ExceptionTranslationFilter. This filter is created with a NullRequestCache instance rather than the HttpSessionRequestCache instance that the Spring Security Core plugin provided ExceptionTranslationFilter is created with. Similar to the statelessSecurityContextPersistenceFilter, this filter is registered automatically by the plugin but can be disabled by setting the registerExceptionTranslationFilter configuration option to false.

The following filter chain configuration is recommended:

grails.plugin.springsecurity.filterChain.chainMap = [
        [pattern: '/oauth/token',               filters: 'JOINED_FILTERS,-oauth2ProviderFilter,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-exceptionTranslationFilter'],
        [pattern: '/securedOAuth2Resources/**', filters: 'JOINED_FILTERS,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-oauth2BasicAuthenticationFilter,-exceptionTranslationFilter'],
        [pattern: '/**',                        filters: 'JOINED_FILTERS,-statelessSecurityContextPersistenceFilter,-oauth2ProviderFilter,-clientCredentialsTokenEndpointFilter,-oauth2BasicAuthenticationFilter,-oauth2ExceptionTranslationFilter']
]

The oauth2ProviderFilter and stateful securityContextPersistenceFilter and exceptionTranslationFilter are removed from the token endpoint's filter chain. With the securityContextPersistenceFilter removed, the statelessSecurityContextPersistenceFilter will be used to ensure access token requests are stateless. Similarly, removing the exceptionTranslationFilter will allow the oauth2ExceptionTranslationFilter to take its place in the filter chain.

The securityContextPersistenceFilter and exceptionTranslationFilter are also removed from the filter chain for OAuth 2.0 resources. However, the oauth2ProviderFilter must not be removed, as this filter is responsible for authenticating the OAuth 2.0 access token included in the request.

It is recommend that filter chain(s) for non-OAuth 2.0 resources have all OAuth 2.0 specific filters removed. These include: statelessSecurityContextPersistenceFilter, oauth2ProviderFilter, clientCredentialsTokenEndpointFilter, basicAuthenticationFilter and oauth2ExceptionTranslationFilter. It is also recommended that any unnecessary filters such as the rememberMeAuthenticationFilter and authenticationProcessingFilter are removed from the filter chains for the token endpoint and OAuth 2.0 resources.

7.10 Domain Class Custom Serialization Configuration

The default behavior of the plugin is to serialize the additionalInformation and scope properties of the Client and AccessToken classes as a Map<String, Object> and Set<String> respectively. This is how the s2-init-oauth2-provider script will generate the domain classes. However, this might not be ideal for all plugin consumers who want more control over the serialization of these fields.

To accommodate these users, it is possible to override the default serialization method on a case-by-case basis. The plugin provides two interfaces to accomplish.

For the additionalInformation fields:

package grails.plugin.springsecurity.oauthprovider.serialization;

import java.util.Map;

public interface OAuth2AdditionalInformationSerializer {

Object serialize(Map<String, Object> additionalInformation);

Map<String, Object> deserialize(Object additionalInformation); }

For the scope field:

package grails.plugin.springsecurity.oauthprovider.serialization;

import java.util.Set;

public interface OAuth2ScopeSerializer {

Object serialize(Set<String> scopes);

Set<String> deserialize(Object scopes); }

By default, the plugin registers implementations that do little more than return the argument provided to each method. The following table shows which plugin provided Spring beans implement these interfaces and how they're used:

Bean NameInterface ImplementedDescription
clientAdditionalInformationSerializerOAuth2AdditionalInformationSerializerHandles deserialization of the additionalInformation property of the Client class into a Map<String, Object>. Only the deserialize method is used by the plugin at this time.
accessTokenAdditionalInformationSerializerOAuth2AdditionalInformationSerializerHandles serialization and deserialization of the additionalInformation property of the AccessToken class into a Map<String, Object>.
accessTokenScopeSerializerOAuth2ScopeSerializerHandles serialization and deserialization of the scope property of the AccessToken class into a Set<String>.

Overriding these beans in resources.groovy will allow the plugin consumer to customize how these fields are serialized. However, this will require the affected domain class to be modified to accommodate the change. For example, let's change the AccessToken to serialized its additionalInformation as JSON String and its scope as white space delimited String.

First, modify the AccessToken class to reflect the change in the storage of these fields:

package test.oauth2

class AccessToken {

String authenticationKey byte[] authentication

String username String clientId

String value String tokenType

Date expiration String additionalInformation

String scope

static hasOne = [refreshToken: String]

static constraints = { username nullable: true clientId nullable: false, blank: false value nullable: false, blank: false, unique: true tokenType nullable: false, blank: false expiration nullable: false scope nullable: false refreshToken nullable: true authenticationKey nullable: false, blank: false, unique: true authentication nullable: false, minSize: 1, maxSize: 1024 * 4 additionalInformation nullable: true }

static mapping = { version false scope lazy: false } }

Next, implement the earlier described interfaces:

package test

import grails.plugin.springsecurity.oauthprovider.serialization.OAuth2ScopeSerializer import org.springframework.security.oauth2.common.util.OAuth2Utils

class WhiteSpaceDelimitedStringScopeSerializer implements OAuth2ScopeSerializer {

@Override Object serialize(Set<String> scopes) { return OAuth2Utils.formatParameterList(scopes) }

@Override Set<String> deserialize(Object scopes) { return OAuth2Utils.parseParameterList(scopes) } }

And:

package test

import grails.plugin.springsecurity.oauthprovider.serialization.OAuth2AdditionalInformationSerializer import groovy.json.JsonOutput import groovy.json.JsonSlurper

class JsonAdditionalInformationSerializer implements OAuth2AdditionalInformationSerializer {

@Override Object serialize(Map<String, Object> additionalInformation) { JsonOutput.toJson(additionalInformation) }

@Override Map<String, Object> deserialize(Object additionalInformation) { new JsonSlurper().parseText(additionalInformation as String) } }

The serialize methods are guaranteed to receive a non-null argument, although they may be provided an empty collection. The deserialize methods are expected to return a non-null value.

Finally, in resources.groovy, override the appropriate beans:

import test.JsonAdditionalInformationSerializer
import test.WhiteSpaceDelimitedStringScopeSerializer

beans = { // Other beans here

accessTokenAdditionalInformationSerializer(JsonAdditionalInformationSerializer) accessTokenScopeSerializer(WhiteSpaceDelimitedStringScopeSerializer) }

8 Standalone Resource Server or Authorization Server

By default, the plugin is configured to assume the role of both the Authorization Server and the Resource Server as defined by RFC 6749. However, it is possible to configure an application to fulfill only one role.

The plugin registers an instance of the Spring OAuth provided OAuth2AuthenticationProcessingFilter under the bean name oauth2ProviderFilter. This filter is responsible for extracting the Bearer token from the Authorization header and confirming its authenticity.

8.1 Authorization Server

To create an application that is only an Authorization Server, it is as simple as configuring the authorization and token endpoints as shown in the Getting Started and Filter Chain Configuration sections and excluding the oauth2ProviderFilter.

So instead of the following filter chain:

grails.plugin.springsecurity.filterChain.chainMap = [
            [pattern: '/oauth/token',               filters: 'JOINED_FILTERS,-oauth2ProviderFilter,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-exceptionTranslationFilter'],
            [pattern: '/securedOAuth2Resources/**', filters: 'JOINED_FILTERS,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-oauth2BasicAuthenticationFilter,-exceptionTranslationFilter'],
            [pattern: '/**',                        filters: 'JOINED_FILTERS,-statelessSecurityContextPersistenceFilter,-oauth2ProviderFilter,-clientCredentialsTokenEndpointFilter,-oauth2BasicAuthenticationFilter,-oauth2ExceptionTranslationFilter']
    ]

You would have something like this:

grails.plugin.springsecurity.filterChain.chainMap = [
           [pattern: '/oauth/token',               filters: 'JOINED_FILTERS,-oauth2ProviderFilter,-securityContextPersistenceFilter,-logoutFilter,-rememberMeAuthenticationFilter,-authenticationProcessingFilter,-exceptionTranslationFilter'],
           [pattern: '/**',                        filters: 'JOINED_FILTERS,-statelessSecurityContextPersistenceFilter,-oauth2ProviderFilter,-clientCredentialsTokenEndpointFilter,-basicAuthenticationFilter,-oauth2ExceptionTranslationFilter']
   ]

Where /** is any Authorization Server specific functionality.

8.2 Resource Server

To create an application that is only a Resource Server is slightly more involved. The plugin uses an implementation of the Spring provided ResourceServerTokenServices interface that uses the currently configured TokenStore to authenticate the presented Bearer token. If the Authorization Server and Resource Server are distinct applications, it is very likely that the Resource Server will need some means to validate the provided Bearer token that depends on your use case. To do this, simply implement the aforementioned ResourceServerTokenServices interface and override the resourceServerTokenServices bean in your resources.groovy.

Next you will need to disable access to the authorization and token endpoints. This can be accomplished by changing access to the appropriate URL. For example, when using static rules to secure your endpoints, you might have the following when the Authorization and Resource Servers are the same application:

grails.plugin.springsecurity.controllerAnnotations.staticRules = [
        [pattern: '/oauth/authorize',           access: "isFullyAuthenticated() and (request.getMethod().equals('GET') or request.getMethod().equals('POST'))"],
        [pattern: '/oauth/token',               access: "isFullyAuthenticated() and request.getMethod().equals('POST')"],
        [pattern: '/',                          access: 'permitAll'],
        [pattern: '/index',                     access: 'permitAll'],
        [pattern: '/index.gsp',                 access: 'permitAll'],
        [pattern: '/**/js/**',                  access: 'permitAll'],
        [pattern: '/**/css/**',                 access: 'permitAll'],
        [pattern: '/**/images/**',              access: 'permitAll'],
        [pattern: '/**/favicon.ico',            access: 'permitAll'],
        [pattern: '/assets/**',                 access: 'permitAll']
]

Splitting out the authorization parts will result in something like this:

grails.plugin.springsecurity.controllerAnnotations.staticRules = [
        [pattern: '/',                          access: 'permitAll'],
        [pattern: '/index',                     access: 'permitAll'],
        [pattern: '/index.gsp',                 access: 'permitAll'],
        [pattern: '/**/js/**',                  access: 'permitAll'],
        [pattern: '/**/css/**',                 access: 'permitAll'],
        [pattern: '/**/images/**',              access: 'permitAll'],
        [pattern: '/**/favicon.ico',            access: 'permitAll'],
        [pattern: '/assets/**',                 access: 'permitAll']
]

Any requests to the authorization or token endpoints will be greeted with a 403 response. You should also remove any filter chain configurations in place for these endpoints as well. Our earlier filter chain would become something like the following:

grails.plugin.springsecurity.filterChain.chainMap = [
           [pattern: '/securedOAuth2Resources/**', filters: 'JOINED_FILTERS,-securityContextPersistenceFilter,-logoutFilter,-rememberMeAuthenticationFilter,-authenticationProcessingFilter,-basicAuthenticationFilter,-exceptionTranslationFilter'],
           [pattern: '/**',                        filters: 'JOINED_FILTERS,-statelessSecurityContextPersistenceFilter,-oauth2ProviderFilter,-clientCredentialsTokenEndpointFilter,-basicAuthenticationFilter,-oauth2ExceptionTranslationFilter']
   ]

Where /** is any Resource Server specific functionality.