(Quick Reference)
Spring Security REST Plugin - Reference Documentation
Authors: Alvaro Sanchez-Mariscal
Version: 1.2.5
1 Introduction to the Spring Security REST plugin
The Spring Security REST Grails plugin allows you to use Spring Security for a stateless, token-based, RESTful authentication.
The default behaviour of Spring Security is to store the authenticated principal in the HTTP session. However, in a
RESTful scenario, we need to make sure our server is stateless.
If you are writing an API that will be used by other programs, you can use OAuth for this. But if you are exposing your API
for a front-end Javascript client to implement a
Single Page Interface,
OAuth is not an option, specially if you want to authentication end users against your own user backend (eg: LDAP). In
this case, a token-based authentication may be a more suitable implementation, like the following:
- The client application requests and endpoint that requires authentication, so the server responds with a 401 response.
- The client redirects the user to the login form.
- The user enter credentials, and the client sends a request to the authentication endpoint. The server validates credentials, and if valid, generates, stores and sends back a token to the client.
- The client then stores the token internally. It will be sent on every API method request.
- The client sends again a request to the protected resource, passing the token as an HTTP header.
- The server validates the token, and if valid, executes the actual operation requested.
As per the
REST definition, the client is transferring its
state on every request so the server is truly stateless. The approach to store tokens on the server is just an alternative
to use HTTP basic authentication (
see FAQ) (so credentials are not passed on every request). It also helps to perform the validation
step (#5 in the diagram) faster, because the tokens, and the associated principal information may be cached. Finally,
storing tokens gives you the chance to decide about expiration strategies.
More information about this strategy can be found
on this post by James Ward.
This plugin helps you to wire your existing Spring Security
authentication mechanism, provides you
with ready-to-use
token generation strategies and comes prepackaged with Memcached and GORM support
for
token storage.
Release History
- 17 February 2014
- 10 February 2014
- 4 February 2014
- 31 January 2014
- 15 January 2014
- 14 January 2014
- 13 January 2014
- 12 January 2014
- 10 January 2014
- 31 December 2013
- Initial 1.0.0.RC1 release.
2 Authentication Endpoint
The
authentication filter
uses the default
authenticationManager
bean, which in turn uses all the registered authentication
providers. See the
Spring Security Core guide
for more information about how to define your own providers. Note that you can easily plug any Spring Security sub-plugin
(like the LDAP one) to use a different authentication strategy.
If the authentication is successful, a
token generator is used to generate a token, and a
token storage implementation is used to store the token.
Finally, the JSON response sent back to the client is rendered by a
restAuthenticationTokenJsonRenderer
bean. The plugin
offers you a
default implementation
that renders a response like this:
{
"username": "john.doe",
"token": "1a2b3c4d",
"roles": [
"ADMIN",
"USER"
]
}
If you want your own, simply create a class implementing
RestAuthenticationTokenJsonRenderer
and wire it up in
resources.groovy
with name
restAuthenticationTokenJsonRenderer
.
The principal object stored in the security context, and passed to the JSON renderer, is coming from the configured
authentication providers. In most cases, this will be a UserDetails
object retrieved using the userDetailsService
bean.
If you want to render additional information in your JSON response, you have to:
- Configure an alternative
userDetailsService
bean that retrieves the additional information you want, and put it in a principal object.
- Configure an alternative
restAuthenticationTokenJsonRenderer
that reads that information from the restAuthenticationToken.principal
object.
The following are the
Config.groovy
properties available:
Config key | Default value |
---|
grails.plugin.springsecurity.rest.login.endpointUrl | /login |
grails.plugin.springsecurity.rest.login.failureStatusCode | 403 |
Extracting credentials from the request
The plugin supports 2 ways of extracting the username and password: using request parameters, and using a JSON payload.
For backwards compatibility, request parameters is the default option.
From request parameters
Config key | Default value |
---|
grails.plugin.springsecurity.rest.login.useRequestParamsCredentials | true |
grails.plugin.springsecurity.rest.login.usernameParameter | username |
grails.plugin.springsecurity.rest.login.passwordParameter | password |
From a JSON request
To enable it:
Config key | Default value |
---|
grails.plugin.springsecurity.rest.login.useJsonCredentials | true |
The default implementation expects a request like this:
{
"username": "john.doe",
"password": "dontTellAnybody"
}
If your JSON request format is different, you can plug your own implementation by defining a class which extends
AbstractJsonPayloadCredentialsExtractor
.
The default implementation looks like this:
@Log4j
class DefaultJsonPayloadCredentialsExtractor extends AbstractJsonPayloadCredentialsExtractor { UsernamePasswordAuthenticationToken extractCredentials(HttpServletRequest httpServletRequest) {
def jsonBody = getJsonBody(httpServletRequest) log.debug "Extracted credentials from request params. Username: ${jsonBody.username}, password: ${jsonBody.password?.size()?'[PROTECTED]':'[MISSING]'}" new UsernamePasswordAuthenticationToken(jsonBody.username, jsonBody.password)
}}
Once you are done, register it in
resources.groovy
with the name
credentialsExtractor
.
2.1 Logout Endpoint
The
logout filter
exposes an endpoint for deleting tokens. It will read the token from an HTTP header. If found, will delete it from the
storage, sending a 200 response. Otherwise, it will send a 404 response.
You can configure it in
Config.groovy
using this properties:
Config key | Default value |
---|
grails.plugin.springsecurity.rest.logout.endpointUrl | /logout |
grails.plugin.springsecurity.rest.token.validation.headerName | X-Auth-Token |
3 Token Generation
The plugin comes prepackaged with 2 token generation strategies:
The strategy used is configurable in
Config.groovy
:
Config key | Default value |
---|
grails.plugin.springsecurity.rest.token.generation.useSecureRandom | true |
grails.plugin.springsecurity.rest.token.generation.useUUID | false |
Both of them generate tokens of 32 alphanumeric characters.
That should be enough for most of the human beings. But if you still want to provide your own implementation,
simply write a class implementing
TokenGenerator
and wire it up in
resources.groovy
as
tokenGenerator
.
4 Token Storage
The tokens are stored on the server using a
tokenStorageService
bean. The plugin comes with out-of-the-box support
for Memcached and GORM, but you can use your own strategy implementing the
TokenStorageService
interface.
4.1 Memcached
To use Memcached, simply define the following configuration properties to match your environments accordingly:
Config key | Default value |
---|
grails.plugin.springsecurity.rest.token.storage.useMemcached | false |
grails.plugin.springsecurity.rest.token.storage.memcached.hosts | localhost:11211 |
grails.plugin.springsecurity.rest.token.storage.memcached.username | '' |
grails.plugin.springsecurity.rest.token.storage.memcached.password | '' |
grails.plugin.springsecurity.rest.token.storage.memcached.expiration | 3600 |
For development, if you have Memcached installed locally with the default settings, just define
grails.plugin.springsecurity.rest.token.storage.useMemcached = true
. It should work.
4.2 GORM
The GORM support is still experimental.
To use GORM, those are the relevant configuration properties:
Config key | Default value |
---|
grails.plugin.springsecurity.rest.token.storage.useGorm | false |
grails.plugin.springsecurity.rest.token.storage.gorm.tokenDomainClassName | AuthenticationToken |
grails.plugin.springsecurity.rest.token.storage.gorm.tokenValuePropertyName | tokenValue |
grails.plugin.springsecurity.rest.token.storage.gorm.usernamePropertyName | username |
The domain class should look like this:
class AuthenticationToken { String tokenValue
String username
}
In this case, instead of storing the whole UserDetails
object, only the username is stored. This is because applications
using this strategy will probably have the standard User and Role domain classes. Then, the username is passed to the
default userDetailsService
bean, which in the case of the default Spring Security Core GORM implementation will fetch
the information from the mentioned domain classes.
5 Token Validation Filter
The token validation filter looks for the token in a HTTP header and then tries to validate the token using the configured
token storage implementation.
If the validation is successful, the principal object is stored in the security context. This allows you to use in
your application
@Secured
,
springSecurityService.principal
and so on.
springSecurityService.currentUser
expects a grails.plugin.springsecurity.userdetails.GrailsUser
to perform a DB query.
However, this plugins stores in the security context just a principal Object
, because it does not assume you are using
domain classes to store the users. Use springSecurityService.principal
instead.
Validation Endpoint
There is also an endpoint available that you can call in case you want to know if a given token is valid. It looks for
the token in a HTTP header as well, and if the token is still valid, it renders
its JSON representation.
If the token does not exist, it will render a
grails.plugin.springsecurity.rest.login.failureStatusCode
response
(
403
by default).
The relevant configuration properties are:
Config key | Default value |
---|
grails.plugin.springsecurity.rest.token.validation.headerName | X-Auth-Token |
grails.plugin.springsecurity.rest.token.validation.endpointUrl | /validate |
6 CORS support
This plugin comes pre-installed with the
CORS plugin, which enables
Cross-Origin Resource Sharing. Refer to the
plugin documentation to learn how to configure it.
The CORS plugin activates itself by default. If you don't want it for some environments, you can use
cors.enabled = false
within the appropriate environment block in your
Config.groovy
.
If you don't want CORS support at all, you can skip the plugin by excluding it
when defining this plugin in your
BuildConfig.groovy
:
compile ':spring-security-rest:{{VERSION}}', {
exclude 'cors
}
7 Delegating authentication to OAuth providers
This plugin is meant to be used in applications serving a REST API's to pure Javascript clients. The main authentication
flow of this plugin is to allow you to authenticate your users against any Spring Security-compatible user directory
(like a DB or an LDAP server).
However, there might be situations where you want to delegate the authentication against a third-party provider, like
Google or Facebook. Unfortunately, your pure Javascript front-end application cannot request the providers directly using
OAuth, because then the access keys will be made public.
So is this plugin's responsibility to provide endpoints so your Grails backend acts as a proxy for your front-end client.
The flow is something like the following:
- The client application requests and endpoint that requires authentication, so the server responds with a 401 response (*).
- The client redirects the user to the login form (*).
- This time, instead of using username and password, the user clicks on "Login with Google" button.
- Browser navigates to a Grails URL. Grails will generate a Google Login URL, giving Google a Grails callback URL.
- Browser navigates to Google Login. User logs in, and Google redirects the browser to the Grails callback URL.
- Browser navigates to that Grails callback URL. Then, Grails will use OAuth to fetch user information (like email) from Google. Based on that, will generate a REST API token and fetch and store principal information. The response from Grails will be a front-end URL where the token is a parameter.
- The browser will navigate to that URL, and the Javascript logic will read the token from the URL and store it locally.
- The client sends again a request to the protected resource, passing the token as an HTTP header (*).
The steps flagged with (*) remain unchanged from the
normal flow.
To support OAuth, this plugin uses
Profile & Authentication Client for Java. So you
can use any OAuth (1.0 and 2.0) provider they support. This includes at the time of writing:
- Dropbox.
- Facebook.
- GitHub.
- Google.
- LinkedIn.
- Twitter.
- Windows Live.
- Wordpress.
- Yahoo.
- Paypal.
To start the OAuth authentication flow, from your frontend application, generate a link to
<YOUR_GRAILS_APP>/oauth/authenticate/<provider>
. The user clicking on that link represents step 4 in the previous
diagram.
Note that you can define the frontend callback URL in
Config.groovy
under
grails.plugin.springsecurity.rest.oauth.frontendCallbackUrl
. You need to define a closure that will be called with
the token value as parameter:
grails.plugin.springsecurity.rest.oauth.frontendCallbackUrl = { String tokenValue -> "http://my.frontend-app.com/welcome#token=${tokenValue}" }
You can also define the URL as a
callback
parameter in the original link, eg:
http://your-grails-api.com/oauth/authenticate/google?callback=http://your-frontend-app.com/auth-success.html?token=
In this case, the token will be
concatenated to the end of the URL.
Upon successful OAuth authorisation (after step 6.1 in the above diagram), an
OauthUser
will be stored in the security context. This is done by a bean named
oauthUserDetailsService
. The
default implementation
delegates to the configured
userDetailsService
bean, passing the profile ID as the username:
class DefaultOauthUserDetailsService implements OauthUserDetailsService { @Delegate
UserDetailsService userDetailsService OauthUser loadUserByUserProfile(UserProfile userProfile, Collection<GrantedAuthority> defaultRoles) {
UserDetails userDetails
OauthUser oauthUser try {
userDetails = userDetailsService.loadUserByUsername userProfile.id
userDetails.authorities.addAll defaultRoles
oauthUser = new OauthUser(userDetails.username, userDetails.password, userDetails.authorities, userProfile)
} catch (exception) {
oauthUser = new OauthUser(userProfile.id, 'N/A', defaultRoles, userProfile)
}
return oauthUser
}}
If you want to provide your own implementation, define it in
resources.groovy
with bean name
oauthUserDetailsService
.
Make sure you implements the interface
OauthUserDetailsService
Below are some examples on how to configure it for Google, Facebook and Twitter.
7.1 Google
Define the following block in your
Config.groovy
:
grails {
plugin {
springsecurity { rest { oauth { frontendCallbackUrl = { String tokenValue -> "http://my.frontend-app.com/welcome#token=${tokenValue}" } google { client = org.pac4j.oauth.client.Google2Client
key = 'xxxx.apps.googleusercontent.com'
secret = 'xxx'
scope = org.pac4j.oauth.client.Google2Client.Google2Scope.EMAIL
defaultRoles = ['ROLE_USER', 'ROLE_GOOGLE'] }
}
}
}
}
}
The
scope
can be from any value of the enum org.pac4j.oauth.client.Google2Client.Google2Scope
7.2 Facebook
Define the following block in your
Config.groovy
:
grails {
plugin {
springsecurity { rest { oauth { frontendCallbackUrl = { String tokenValue -> "http://my.frontend-app.com/welcome#token=${tokenValue}" } facebook { client = org.pac4j.oauth.client.FacebookClient
key = 'xxx'
secret = 'yyy'
scope = 'email,user_location'
fields = 'id,name,first_name,middle_name,last_name,username'
defaultRoles = ['ROLE_USER', 'ROLE_FACEBOOK']
}
}
}
}
}
}
The
scope
is a comma-separated list,
without blanks, of Facebook permissions. See the
Facebook documentation for more details.
fields
may contain a comma-separated list,
without blanks, of
user fields.
Both
scope
and
fields
are optional, but it's highly recommendable to fine tune those lists so you don't ask for
information you don't need.
Define the following block in your
Config.groovy
:
grails {
plugin {
springsecurity { rest { oauth { frontendCallbackUrl = { String tokenValue -> "http://my.frontend-app.com/welcome#token=${tokenValue}" } twitter { client = org.pac4j.oauth.client.TwitterClient
key = 'xxx'
secret = 'yyy'
defaultRoles = ['ROLE_USER', 'ROLE_TWITTER']
}
}
}
}
}
}
There is no additional configuration for Twitter.
8 Debugging
If you need debug information, you can specify the following entries in
Config.groovy
:
log4j = {
... debug 'com.odobo',
'grails.app.controllers.com.odobo',
'grails.app.services.com.odobo',
'org.pac4j',
'org.springframework.security' …
}
9 Frequently Asked Questions
Why this token-based implementation? Can't I use HTTP basic authentication?
In theory you can. The only restriction to be truly stateless is to not use HTTP sessions at all. So if you go with
basic authentication, you need to transfer the credentials back and forth every time.
Let's think about that. Keep in mind that your frontend is a pure HTML/Javascript application, consuming a REST API
from the Grails side. So the first time, the Javascript application will make an API query and will receive a 403 response
indicating that authentication is required. Then you present the user a form to enter credentials, you grab them,
encode
them with Base64 and in the next request, you send an HTTP header like
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
.
Now remember you are doing RESTful application, so the session state is maintained in the client. That means that you
would need to store that Base64 encoded string somewhere: cookies? HTML5 local storage? In any case, they are accessible
using browser tools. And that's the point: there is a huge security risk because Base64 it's not encryption, just encoding.
And it can be easily decoded.
You could argue that someone can access the token in the browser. Yes, but having the token will not allow him to obtain
user's credentials. The tokens are just not decodable. And they can be revoked if necessary.
This is more or less the strategy used by OAuth: they use basically tokens in headers. More on this on the next question.
There is also more reasons to be in favour of tokens:
- With basic auth, every single API call would have to check credentials. In the token-based implementation, specially if you use Memcached, the authentication results are cached.
- With basic auth, you are sending the credentials all the time. Ok, you can use SSL, but still I think it's more elegant to use tokens.
Moreover, if you use tokens, you have the chance to implement expiration policies.
A couple of link with further explanations on the token-based flow:
Why can't the API be secured with OAuth
Because to do so, you would need to store consumer key and consumer secret in the browser. Seriously, you don't want
to do that. The problem with OAuth is that it's designed for when the consumer is a server-side application. And it just
does not work well with pure Javascript front-ends. In this scenario, your frontend would be the OAuth consumer and
your Grails backend the OAuth provider
A different story is to delegate the actual authentication to other OAuth providers: this scenario is possible, and
actually
this plugin supports it. In this case, the consumer will be the Grails application, and it's
absolutely fine to store consumer keys on the server, as they are never exposed to the browser.
Why you didn't use any of the existing OAuth plugins? Why pac4j?
I'm aware of plugins like
OAuth and
Spring Security OAuth, but all of them rely on Spring Security Core's
way of using HTTP sessions. So not acceptable.
I chose pac4j because:
- They support major OAuth 2.0 providers out-of-the-box, whereas Scribe does not.
- It's deadly simple and works just fine.
I'm also aware of a pac4j-spring-security module. See my previous response on HTTP sessions.
Dude, this is awesome. How can I compensate you?
I doubt you can :). You may try giving me free beers the next time you see me in a conference. Or you can just express
your gratitude via
Twitter