Skip to content

tucanoo/grails_passkey_tutorial

Repository files navigation

Grails 7 Passkeys Tutorial - Simple CRM

A companion project for the Tucanoo tutorial article on adding passkeys (WebAuthn) to a Grails application.

This repository demonstrates a practical migration path:

  • Existing Grails CRM app
  • Spring Security login
  • Add passkey enrollment and passkey sign-in
  • Prompt users after password login to create their first passkey

Background

This project builds on the original Tucanoo Grails CRM tutorial but now uses Grails 7:

Prerequisites

  • Java 25+ (java -version)

No separate Gradle installation is required. The Gradle wrapper is included.

Quick Start

git clone https://github.com/tucanoo/crm_grails.git
cd crm_grails
./gradlew bootRun

Windows PowerShell:

git clone https://github.com/tucanoo/crm_grails.git
cd crm_grails
.\gradlew.bat bootRun

Open http://localhost:8080 and sign in with:

  • Username: user
  • Password: changeme

Tutorial Flow To Try

  1. Sign in with username/password.
  2. You are redirected to /dashboard/index.
  3. If no passkeys exist for your account, a banner appears.
  4. Click Create passkey and complete browser/device verification.
  5. Log out.
  6. On the login page, click Sign in with passkey.

What This Project Demonstrates

Feature Where to look
Password login with in-memory user src/main/groovy/com/tucanoo/security/InMemoryUserDetailsService.groovy
Passkey registration API (registerOptions, registerComplete) grails-app/controllers/com/tucanoo/security/passkey/PasskeyController.groovy
Passkey authentication API (authOptions, authComplete) grails-app/controllers/com/tucanoo/security/passkey/PasskeyController.groovy
WebAuthn server orchestration grails-app/services/com/tucanoo/security/passkey/PasskeyService.groovy
WebAuthn credential repository adapter src/main/groovy/com/tucanoo/security/passkey/PasskeyCredentialRepository.groovy
One-time challenge cache src/main/groovy/com/tucanoo/security/passkey/PasskeyChallengeCache.groovy
RelyingParty bean configuration src/main/groovy/com/tucanoo/security/passkey/PasskeyConfiguration.groovy and grails-app/conf/spring/resources.groovy
Login page passkey button and browser WebAuthn JS grails-app/views/login/auth.gsp
Post-login passkey setup banner grails-app/controllers/com/tucanoo/DashboardController.groovy and grails-app/views/dashboard/index.gsp
Persisted passkey credentials grails-app/domain/com/tucanoo/security/PasskeyCredential.groovy

Configuration

Main settings live in grails-app/conf/application.yml.

Authentication

app:
  security:
    username: user
    password: changeme
    role: CRM_USER

Passkeys

passkeys:
  enabled: true
  rpId: localhost
  rpName: Simple CRM
  origins:
    - http://localhost:8080
    - http://127.0.0.1:8080
  attestation: none
  allowUsernameless: false
  challengeTtlSeconds: 300
  maxCredentialsPerUser: 5

Notes:

  • rpId and origins must match where your app is served.
  • Localhost is allowed as a secure context for WebAuthn in modern browsers.
  • If you deploy to another host/domain, update both rpId and origins.

Tech Stack

  • Grails 7.0.7
  • Spring Security Core plugin (org.apache.grails:grails-spring-security:7.0.0)
  • Yubico WebAuthn server (com.yubico:webauthn-server-core:2.8.1)
  • GSP + asset-pipeline
  • H2 database

Project Structure

grails-app/
  conf/
    application.yml
    spring/resources.groovy
  controllers/com/tucanoo/
    DashboardController.groovy
    crm/CustomerController.groovy
    security/passkey/PasskeyController.groovy
  domain/com/tucanoo/
    crm/Customer.groovy
    security/PasskeyCredential.groovy
  services/com/tucanoo/
    crm/CustomerService.groovy
    security/passkey/PasskeyService.groovy
  views/
    login/auth.gsp
    dashboard/index.gsp
    customer/*.gsp

src/main/groovy/com/tucanoo/security/
  InMemoryUserDetailsService.groovy
  passkey/
    PasskeyConfiguration.groovy
    PasskeyCredentialRepository.groovy
    PasskeyChallengeCache.groovy

Run Tests

./gradlew test integrationTest

Windows PowerShell:

.\gradlew.bat test integrationTest

Troubleshooting Passkeys

  • Passkeys disabled: set passkeys.enabled: true.
  • Origin not allowed: add your exact scheme/host/port to passkeys.origins.
  • Browser says passkeys unsupported: test on a current browser with WebAuthn support.
  • No banner after login: confirm user has no rows in passkey_credential with revoked = false.

References

About

Grails 7 Simple CRM tutorial project showing how to add WebAuthn passkeys (enrollment prompts, passwordless sign-in) on top of the original Grails CRM app.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors