JWT, the walking dead, of Spring Security REST for Grails plugin

This post continues with the code generated in the post: how to use a Trait to encapsulate Spring Security core functionality in a Grails 3 app

First lets add the dependency to the Spring Security REST for Grails plugin to build.gradle

buildscript {
    ext {
        grailsVersion = project.grailsVersion
    }
    repositories {
        mavenLocal()
        maven { url "https://repo.grails.org/grails/core" }
    }
    dependencies {
        classpath "org.grails:grails-gradle-plugin:$grailsVersion"
        classpath 'com.bertramlabs.plugins:asset-pipeline-gradle:2.5.0'
        classpath "org.grails.plugins:hibernate:4.3.10.5"
    }
}

plugins {
    id "io.spring.dependency-management" version "0.5.4.RELEASE"
}

version "0.1"
group "myapp"

apply plugin: "spring-boot"
apply plugin: "war"
apply plugin: "asset-pipeline"
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: "org.grails.grails-web"
apply plugin: "org.grails.grails-gsp"

ext {
    grailsVersion = project.grailsVersion
    gradleWrapperVersion = project.gradleWrapperVersion
}

assets {
    minifyJs = true
    minifyCss = true
}

repositories {
    mavenLocal()
    maven { url "https://repo.grails.org/grails/core" }
}

dependencyManagement {
    imports {
        mavenBom "org.grails:grails-bom:$grailsVersion"
    }
    applyMavenExclusions false
}

dependencies {
    compile "org.springframework.boot:spring-boot-starter-logging"
    compile "org.springframework.boot:spring-boot-starter-actuator"
    compile "org.springframework.boot:spring-boot-autoconfigure"
    compile "org.springframework.boot:spring-boot-starter-tomcat"
    compile "org.grails:grails-dependencies"
    compile "org.grails:grails-web-boot"

    compile "org.grails.plugins:hibernate"
    compile "org.grails.plugins:cache"
    compile "org.hibernate:hibernate-ehcache"
    compile "org.grails.plugins:scaffolding"

    runtime "org.grails.plugins:asset-pipeline"

    testCompile "org.grails:grails-plugin-testing"
    testCompile "org.grails.plugins:geb"

    // Note: It is recommended to update to a more robust driver (Chrome, Firefox etc.)
    testRuntime 'org.seleniumhq.selenium:selenium-htmlunit-driver:2.44.0'
    console "org.grails:grails-console"
    compile 'org.grails.plugins:spring-security-core:3.0.3'
    compile "org.grails.plugins:spring-security-rest:2.0.0.M2"
}

task wrapper(type: Wrapper) {
    gradleVersion = gradleWrapperVersion
}

Run the app grails run-app and you will be able to login and get a JWT back as displayed here:

Untitled_Paw_Document

Copy the api/login response to safe place, we will use it later

Now, we are going to start the app without users in the database. By default Grails Datasource behaviour in the Development environment will drop the database when the app its stopped and create it back again when it is started. This is configured in grails-app/init/application.yml

---
grails:
    profile: web
    codegen:
        defaultPackage: myapp
info:
    app:
        name: '@info.app.name@'
        version: '@info.app.version@'
        grailsVersion: '@info.app.grailsVersion@'
spring:
    groovy:
        template:
            check-template-location: false

---
grails:
    mime:
        disable:
            accept:
                header:
                    userAgents:
                        - Gecko
                        - WebKit
                        - Presto
                        - Trident
        types:
            all: '*/*'
            atom: application/atom+xml
            css: text/css
            csv: text/csv
            form: application/x-www-form-urlencoded
            html:
              - text/html
              - application/xhtml+xml
            js: text/javascript
            json:
              - application/json
              - text/json
            multipartForm: multipart/form-data
            pdf: application/pdf
            rss: application/rss+xml
            text: text/plain
            hal:
              - application/hal+json
              - application/hal+xml
            xml:
              - text/xml
              - application/xml
    urlmapping:
        cache:
            maxsize: 1000
    controllers:
        defaultScope: singleton
    converters:
        encoding: UTF-8
    views:
        default:
            codec: html
        gsp:
            encoding: UTF-8
            htmlcodec: xml
            codecs:
                expression: html
                scriptlets: html
                taglib: none
                staticparts: none
---
hibernate:
    cache:
        queries: false
        use_second_level_cache: true
        use_query_cache: false
        region.factory_class: 'org.hibernate.cache.ehcache.EhCacheRegionFactory'

endpoints:
    jmx:
        unique-names: true

dataSource:
    pooled: true
    jmxExport: true
    driverClassName: org.h2.Driver
    username: sa
    password:

environments:
    development:
        dataSource:
            dbCreate: create-drop
            url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    test:
        dataSource:
            dbCreate: update
            url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    production:
        dataSource:
            dbCreate: update
            url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
            properties:
                jmxEnabled: true
                initialSize: 5
                maxActive: 50
                minIdle: 5
                maxIdle: 25
                maxWait: 10000
                maxAge: 600000
                timeBetweenEvictionRunsMillis: 5000
                minEvictableIdleTimeMillis: 60000
                validationQuery: SELECT 1
                validationQueryTimeout: 3
                validationInterval: 15000
                testOnBorrow: true
                testWhileIdle: true
                testOnReturn: false
                jdbcInterceptors: ConnectionState
                defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED

Stop the app.

Edit grails-app/init/BootStrap.groovy


import myapp.*
 
class BootStrap {
 
    def springSecurityService
 
    def init = { servletContext ->
 
//        def userRole = new Role('ROLE_USER').save()
 
//        def me = new User('me@sergiodelamo.com', 'groovycalamari').save()
 
//        UserRole.create me, userRole
 
//        UserRole.withSession {
//            it.flush()
//            it.clear()
//        }
 
    }
 
    def destroy = {
    }
}

Run the app grails run-app

Go to http://localhost:8080/whoAmI in your browser. Try to login. You won’t be able since there is no user in the database.

Now try to hit the endpoint with a HTTP header name:”Authorization” value: the access_token you previously stored in a safe place. You will be logged in and get the email of the user who no longer exists in the database but authenticated correctly once. 😖

Untitled_Paw_Document_and_ShoptimixDevAPI

Note. this is not bug but the correct behaviour. You can’t kill them, they will raise from the death.

Do you like to read about Groovy/Grails development? Yes, then Subscribe to Groovy Calamari a weekly curated email newsletter about the Groovy ecosystem which I write 

2 thoughts on “JWT, the walking dead, of Spring Security REST for Grails plugin

  1. Joe

    I’m curious, what would you recommend to handle this case? It seems as if once a user is deleted he should no longer be able to access the services. If you have access to the user database you could double-check the credentials, but that makes JWT redundant and you may as well have the tokens in the database to begin with. It seems as if this is a major weakness in utilizing JWT as tokens.

    Reply
  2. sdelamo Post author

    it depends of what is your use case. For the more security critical endpoints, you may need an additional double check as you mentioned. For others you may not care. And for the endpoints, which retrieve information related to a user, you will need to be ready for user not found exceptions.

    I think JWT allows security without having to hit a repository (database or similar) which is great for scalability.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *