This plugin is an OAuth2 Provider based on the Spring Security OAuth libraries. It is partially based off of Burt Beckwith's OAuth Provider plugin, which was never officially released.
While this plugin works for certain use cases, not all OAuth2 flows have been tested. In particular, the following works and has been tested:
- The full flow of logging in with both users and clients using tokens and authorization codes
However, the following items have not been tested and may or may not work:
- Grant types besides
authorization_code
andclient_credentials
- Protected resources via Spring OAuth2 ** This is currently done with the Spring Security core methods (ie request maps, annotations, intercept maps)
On install, a view is created at grails-app/views/oauth/confirm.gsp
. This view may be modified as desired, but the
location should match the userApprovalEndpointUrl
setting discussed below.
Clients are configured using the default in memory client details service in an option in Config.groovy or an external configuration
file specified by grails.config.locations
. The following is an example of configuring a simple client with an ID
of myId
and a secret key of mySecret
:
grails.plugins.springsecurity.oauthProvider.clients = [
[
clientId:"myId",
clientSecret:"mySecret"
]
]
Notice that the client configuration consists of a list of maps with each map representing a single configured client.
The properties which can be configured match the properties in the org.springframework.security.oauth2.provider.BaseClientDetails
class. The only difference is the webServerRedirectUri
has been renamed to registeredRedirectUri
in order to be compatible
with newer releases of Spring Security OAuth2. Default values have been configured for each property except for clientId
and
clientSecret
since these are unique for each configured client. These default values are shown in the following code block with
their default values. These default values may be modified by placing a line similar to the following in Config.groovy or an external
configuration file.
grails.plugins.springsecurity.oauthProvider.defaultClientConfig.resourceIds = []
grails.plugins.springsecurity.oauthProvider.defaultClientConfig.authorizedGrantTypes = ["authorization_code", "refresh_token"]
grails.plugins.springsecurity.oauthProvider.defaultClientConfig.scope = []
grails.plugins.springsecurity.oauthProvider.defaultClientConfig.registeredRedirectUri = null
grails.plugins.springsecurity.oauthProvider.defaultClientConfig.authorities = []
For example, with a default configuration option in Config.groovy of:
grails.plugins.springsecurity.oauthProvider.defaultClientConfig.authorizedGrantTypes = ["implicit"]
And a client configuration of:
grails.plugins.springsecurity.oauthProvider.clients = [
[
clientId:"myId",
clientSecret:"mySecret"
]
]
Will result in a client with an ID of myId
and a single authorized grant type of implicit
. However, if the client configuration
was modified to the following:
grails.plugins.springsecurity.oauthProvider.clients = [
[
clientId:"myId",
clientSecret:"mySecret",
authorizedGrantTypes:["authorization_code"]
]
]
Then the resulting configured client myId
would have a single authorized grant type of authorization_code
. In other words,
the default configuration is overridden by the individual client configuration.
Using this method, when the configuration changes, the entire list of clients is reloaded and replaces the old list.
Clients may also be individually registered by using the BaseClientDetails
class in combination with the clientDetailsService
bean in the Bootstrap process. The following is an example of registering a client programmatically (to be run in the BootStrap of your application):
import org.springframework.security.oauth2.provider.BaseClientDetails;
class BootStrap {
def clientDetailsService
def init = { servletContext ->
def client = new BaseClientDetails()
client.clientId = "clientId"
client.clientSecret = "clientSecret"
client.authorizedGrantTypes = ["authorization_code", "refresh_token", "client_credentials", "password", "implicit"]
// Set the full contents of the client details service store to the newly created client
clientDetailsService.clientDetailsStore = [
// Map of client ID to the client details instance
"clientId":client
]
}
The client may login with the URL given in the tokenEndpointUrl
setting (/oauth/token
by default) by using the following syntax.
Notice the grant_type
of client_credentials
and that the client credentials from the example above are used.
http://localhost:8080/app/oauth/token?grant_type=client_credentials&client_id=clientId&client_secret=clientSecret
The response from a login such as this is the following JSON. The access_token
is the important piece here.
{
"access_token": "449acfe6-663f-4fde-b1f8-414c867a4cb5",
"expires_in": 43200,
"refresh_token": "ab12ce7a-de9d-48db-a674-0044897074b0"
}
The following URLs or configuration options show a typical flow authorizing a client for a certain user.
- The client must first be logged in using the URL above.
- Separately, the client must be logged into the application protected by this plugin. Alternatively, they will be logged in
on the next step since the
authorizationEndpointUrl
must be protected with Spring Security Core. One way to accomplish this is to use the static rules in Config.groovy:
grails.plugins.springsecurity.controllerAnnotations.staticRules = [
'/oauth/authorize.dispatch':['ROLE_ADMIN'],
]
** Note that the URL is mapped with .dispatch
at the end. This is essential in order to correctly protect the resource. For
example, a authorizationEndpointUrl
of /custom/authorize-oauth2
would need to be protected with /custom/authorize-oauth2.dispatch
.
- A client attempting to use a service provided by the OAuth protected application is reached by a user. The
client then redirects the user to the
authorizationEndpointUrl
setting (/oauth/authorize
by default). This will actually redirect the user to theuserApprovalEndpointUrl
setting which will present the user with an option to authorize or deny access to the application for the client.
http://localhost:8080/app/oauth/authorize?response_type=code&client_id=clientId&redirect_uri=http://localhost:8080/app/
The user will then be redirected to the redirect_uri
with the code appended as a URL parameter such as:
http://localhost:8080/app/?code=YjZOa8
- The client captures this code and sends it to the application at the
authorizationEndpointUrl
setting.
This will allow the client to access the application as the user. Notice thegrant_type
ofauthorization_code
this time.
http://localhost:8080/app/oauth/authorize?grant_type=authorization_code&client_id=clientId&code=OVD8SZ&redirect_uri=http://localhost:8080/app/
This will then give a token to the client that can be used to access the application as the user (an example needs to go here).
NOTE: The redirect_uri in the code
response and the authorization_code
grant must match! Otherwise, the authorization will fail.
If the instructions above are followed, this plugin will provide access to resources protected with the Secured
annotation or with
static rules defined in Config.groovy. Resources protected with request maps or other spring security configurations should be protected,
but is untested. If you have tested this plugin in these configurations, please let me know and I'll update this section.
By default, in memory implementations of both TokenStore and ClientDetailsService are used, but if you wish for clients and tokens to be persisted to a database, the following options are accepted:
grails.plugins.springsecurity.oauthProvider.clientDetailsServiceClass = org.springframework.security.oauth2.provider.GormClientDetailsService
grails.plugins.springsecurity.oauthProvider.tokenStoreClass = org.springframework.security.oauth2.provider.token.GormTokenStore
NOTE: You will need to setup relevant tables in your database for the OAuthClient, OAuthRefreshToken, and OAuthAccessToken classes, through whatever means are available to you in your app. One such possibility would be "dbm-gorm-diff" as provided by the Grails Database Migration Plugin: http://grails.org/plugin/database-migration
By default, three endpoint URLs have been defined. Note that default URLMappings are provided for the
authorizationEndpointUrl
and the tokenEndpointUrl
. If these are modified, additional URLMappings will have to
be set. Their default values and how they would be set in Config.groovy are shown below:
grails.plugins.springsecurity.oauthProvider.authorizationEndpointUrl = "/oauth/authorize"
grails.plugins.springsecurity.oauthProvider.tokenEndpointUrl = "/oauth/token" // Where the client is authorized
grails.plugins.springsecurity.oauthProvider.userApprovalEndpointUrl = "/oauth/confirm" // Where the user confirms that they approve the client
NOTE: The userApprovalEndpointUrl
never is actually redirected to, but is simply used to specify the location of the view.
For example, a userApprovalEndpointUrl
of /custom/oauth_confirm
would map to the grails-app/views/custom/oauth_confirm.gsp
view.
The grant types for OAuth authentication may be enabled or disabled with simple configuration options. By default all grant types are enabled. Set the option to false to disable it completely, regardless of client configuration.
grails.plugins.springsecurity.oauthProvider.grantTypes.authorizationCode = true
grails.plugins.springsecurity.oauthProvider.grantTypes.implicit = true
grails.plugins.springsecurity.oauthProvider.grantTypes.refreshToken = true
grails.plugins.springsecurity.oauthProvider.grantTypes.clientCredentials = true
grails.plugins.springsecurity.oauthProvider.grantTypes.password = true
Here are some other configuration options that can be set and their default values. Again, these would be placed in Config.groovy:
grails.plugins.springsecurity.oauthProvider.active = true // Set to false to disable the provider, true in all environments but test where false is the default
grails.plugins.springsecurity.oauthProvider.filterStartPosition = SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order // The starting location of the filters registered
grails.plugins.springsecurity.oauthProvider.authorizationCode.approvalParameterName = "user_oauth_approval" // Used on the user confirmation page (see userApprovalEndpointUrl)
grails.plugins.springsecurity.oauthProvider.tokenServices.refreshTokenValiditySeconds = 60 * 10 //default 10 minutes
grails.plugins.springsecurity.oauthProvider.tokenServices.accessTokenValiditySeconds = 60 * 60 * 12 //default 12 hours
grails.plugins.springsecurity.oauthProvider.tokenServices.reuseRefreshToken = true
grails.plugins.springsecurity.oauthProvider.tokenServices.supportRefreshToken = true