- whoami
- Simple Handler Example
- Force SSL
- Ratpack Sessions
- Pac4j and Ratpack
Jeff Beck
Engineer at SmartThings
-note Abstract
So you are all excited about this hot framework Ratpack now you are about ready to launch into production. But you need security, I will go over using pac4j with Ratpack to secure your application. Showing integrations with Twitter, Basic Auth, and others. I will also go over a case study of CellarHQ and their security in Ratpack.
This talk and slides are targeted at Ratpack v0.9.19-SNAPSHOT
Specify a handler at the start of the chain that all request will go through.
handlers{
all {
//All traffic hits this handler first
}
}
-note We can provide a simple handler at the start of the chain that intercepts all traffic and checks for security concerns.
--
all {
if (request.headers['Authorization'] != "Token faketoken") {
response.status(401)
//We must send some response or the request will hang.
response.send()
} else {
//We can choose to do nothing but allow the next handler in the chain to deal with the request.
next()
}
}
-note Here we are actually checking a token now its a simple hard coded value but you could at this point easily do a check against a DB or other datastore.
--
- Identities
- Roles / Authorization
- Mixed Secure / Insecure Content
- Multiple Authentication Options
--
- Stateless MicroService
- Prototyping
all {
if (!checkForSSL) {
redirect(301, request.rawUri)
}
next()
}
-note AWS Headers: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#x-forwarded-headers
--
Using the ELB for SSL termination, we can easily detect if the request was made with https.
request.headers.get('X-Forwarded-Proto') != 'https'
--
The Session
module provides the basics of sessions as well as a in memory data store.
compile ratpack.dependency('session')
--
The SessionModule will make sure every request is set up with a session. Also by default provide an in memory session data store.
bindings {
module SessionModule
}
--
def session = context.get(Session)
//Get Session ID
String sessionId = session.id
//Terminate Session
session.terminate().then()
Code Branch -note
Terminate the session is an Operation so you need to call then() or then {}
An encrypted cookie that stores session data.
Use the ClientSideSessionModule
bindings {
module(ClientSideSessionModule, { config ->
config.setSessionCookieName("s1")
config.setSecretToken("fakeToken")
})
}
--
- No extra infrastructure to support shared sessions.
- Encrypted so the session data can't be messed with.
--
- Max cookie size
- More data transfer every request
- Requires managing a key
You can easily change out the session store by providing an implementation of the SessionStore interface.
protected void configure() {
bind(SessionStore).to(YourSessionStore).in(Singleton);
}
There is a Pac4j class that ties Pac4j into Ratpack well providing some basics you can extend.
--
- No more Authorizer
- No more guice module
build.gradle
compile ratpack.dependency('pac4j')
compile "org.pac4j:pac4j-http:1.7.0"
--
handlers{
all(RatpackPac4j.authenticator(
new BasicAuthClient(
new SimpleTestUsernamePasswordAuthenticator(),
new UsernameProfileCreator())))
prefix("auth"){
//Require all requests past this point to have auth.
all(RatpackPac4j.requireAuth(BasicAuthClient))
get{
render "An authenticated page."
}
}
}
build.gradle
compile ratpack.dependency('pac4j')
compile "org.pac4j:pac4j-oauth:1.7.0"
--
handlers {
all(RatpackPac4j.authenticator(new TwitterClient("key", "secret")))
prefix("auth") {
//Require all requests past this point to have auth.
all(RatpackPac4j.requireAuth(TwitterClient))
get {
render "An authenticated page."
}
}
}
--
Setup all the clients you want.
all(RatpackPac4j.authenticator(
new BasicAuthClient(
new SimpleTestUsernamePasswordAuthenticator(),
new UsernameProfileCreator()),
new TwitterClient("key", "secret")))
--
prefix("auth") {
//Require all requests past this point to have auth.
all({ ctx ->
RatpackPac4j.userProfile(ctx).then { opUp ->
if (opUp.isPresent()) {
ctx.next(single(opUp.get()))
} else {
ctx.redirect(302, "/login")
}
}
})
get { UserProfile userProfile ->
render "An authenticated page. ${userProfile.getId()}"
}
}
--
CellarHQ Open Source
Note A few versions behind but will get updated.