Skip to content
Kona

Authentication

Authentication answers: who is this request? Kona ships session guards for browsers, bearer-token guards for APIs, password hashing, reset brokers, and middleware that gate routes.

AuthServiceProvider registers hashing, an in-memory user provider (swap in CadenceUserProvider for Cadence models), web + api guards, the password broker, personal access tokens, and the Auth facade.

  1. Keep AuthServiceProvider (and SessionServiceProvider for web) on your bootstrap list — kona new does this.
  2. Ensure StartSession runs before auth middleware on HTML routes.
  3. Log in with credentials; protect routes with Authenticate.
auth.setRequest(request)
if (auth.attempt(mapOf("email" to email, "password" to password), remember = true)) {
val intended = Authenticate.pullIntended(request, "/")
return Response.redirect(intended)
}
auth.setRequest(request);
if (auth.attempt(Map.of("email", email, "password", password), true)) {
String intended = Authenticate.pullIntended(request, "/");
return Response.redirect(intended);
}

AuthManager resolves named guards. The Auth object mirrors the manager after the provider boots:

Call Meaning
attempt / login / logout Persist (or clear) the session user
check / guest / id / user Inspect the current user
validate Check credentials without logging in
once / onceUsingId Authenticate for this request only
loginUsingId Log in by primary key
viaRemember True when restored from a remember cookie
guard("api") Switch guard
Auth.setRequest(request)
Auth.attempt(mapOf("email" to "a@b.c", "password" to "secret"))
val user = Auth.user()
  • web (SessionGuard) — stores login_{guard}_id (and password hash) in the request session attribute "session".
  • api (TokenGuard) — reads Authorization: Bearer {id}|{secret} via PersonalAccessTokenManager.

Register custom guards with auth.extend("admin", SessionGuard("admin", users)).

When login / attempt is called with remember = true:

  1. A remember token is stored on the user (Authenticatable.setRememberToken).
  2. A long-lived remember_{guard} cookie ({id}|{token}) is queued on the request.
  3. Middleware that flushes auth cookies (built into Authenticate, RedirectIfAuthenticated, and AuthenticateSession) writes Set-Cookie on the response.

On a later visit with no session id, SessionGuard recalls the user from the cookie and viaRemember() returns true. Logout clears the token and queues a forgetting cookie.

UserProvider.retrieveByToken / updateRememberToken power this; InMemoryUserProvider and CadenceUserProvider both implement them.

For apps whose users live in Cadence models, register CadenceUserProvider instead of (or alongside) the in-memory provider. The model must implement Authenticatable and expose credential columns (typically email + password) plus optional remember_token:

class User : Cadence<User>(), Authenticatable {
override val fillable = setOf("email", "password", "remember_token")
companion object : CadenceCompanion<User>(User::class.java)
override fun getAuthIdentifier(): Any = getAttribute("id")!!
override fun getAuthPassword(): String = getAttribute("password")?.toString() ?: ""
override fun getRememberToken(): String? = getAttribute("remember_token")?.toString()
override fun setRememberToken(token: String?) { setAttribute("remember_token", token) }
}
val users = CadenceUserProvider(User::class.java, Hash.manager().driver())
auth.provider("users", users)
auth.extend("web", SessionGuard("web", users))
CadenceUserProvider<User> users = new CadenceUserProvider<>(User.class, Hash.manager().driver());
auth.provider("users", users);
auth.extend("web", new SessionGuard("web", users));

Credential keys other than the password key become equality filters on the model query. Remember-token updates call save() on the model.

Middleware Role
Authenticate Require login; redirect to /login (or throw 401 when redirectTo = null); stores url.intended on GET
RedirectIfAuthenticated Bounce already-logged-in users away from guest pages
AuthenticateSession Log out when the password hash no longer matches the hash captured at login
router.get("/dashboard", handler).middleware(Authenticate(auth))
router.get("/login", handler).middleware(RedirectIfAuthenticated(auth, "/home"))
router.middleware(AuthenticateSession(auth))

After a successful login, send the user to Authenticate.pullIntended(request, "/").

Hash.make("secret")
Hash.check("secret", hash)
Hash.needsRehash(hash)

Bcrypt is the default (hash.driver=bcrypt, hash.rounds default 10). Passwords longer than 72 bytes are truncated to bcrypt’s limit.

Argon2id is registered as argon2 / argon2id. Switch the default with hash.driver=argon2 (optional hash.argon2.memory, hash.argon2.iterations, hash.argon2.parallelism). Register another Hasher with HashManager.extend when you need a custom algorithm.

PasswordBroker issues reset tokens through a PasswordResetTokenRepository and applies resets through a callback. The default store is in-memory (auth.passwords.driver=array). For durable tokens that survive restarts, set auth.passwords.driver=database (optional auth.passwords.table) and ensure Database is registered before AuthServiceProvider.

val token = broker.createToken(mapOf("email" to email))
val result = broker.reset(mapOf("email" to email, "token" to token, "password" to next)) { user, password ->
// persist Hash.make(password)
}

You can also construct PasswordBroker(..., tokens = DatabasePasswordResetTokenRepository(connection)) explicitly.

For form validation rule lists, use PasswordRules.min(8) / PasswordRules.confirmed() with the HTTP validator (see Validation).

PersonalAccessTokenManager.create(user, name, abilities) returns a plain-text token once (id|secret) and stores a SHA-256 hash. Check abilities with token.can("read") / token.cant("write").

The default repository is in-memory (auth.tokens.driver=array). Set auth.tokens.driver=database (optional auth.tokens.table) for JDBC-backed tokens, or pass DatabasePersonalAccessTokenRepository when constructing the manager.

Authenticate with bearer tokens via TokenGuard or AuthenticateWithToken middleware.