Skip to content
Early preview. authbase is in active development. The source code and packages are not public yet; these docs describe how it works ahead of the first public release.

Kotlin and JVM SDK

Three modules (JVM 17+). Not on Maven Central yet (they will be at the public release): install them into your local Maven repository and depend on them from your services.

Terminal window
cd sdk/kotlin && ./gradlew publishToMavenLocal
// your service's build.gradle.kts
repositories { mavenLocal(); mavenCentral() }
dependencies {
implementation("io.github.burakmetehan.authbase:authbase-ktor:0.1.0") // Ktor
implementation("io.github.burakmetehan.authbase:authbase-spring-boot-starter:0.1.0") // or Spring Boot
}
Module What
authbase-core Authbase (suspend client for the end-user API) and AccessTokenVerifier
authbase-ktor a Ktor Authentication provider and requireRoles
authbase-spring-boot-starter auto-configured client, Spring Security JwtDecoder, roles → ROLE_…

One per app, with its API key from the environment (never in code):

val authbase = Authbase(url = System.getenv("AUTHBASE_URL"), apiKey = System.getenv("AUTHBASE_API_KEY"))
authbase.users.create(CreateUserRequest(email = email, password = password))
val tokens = authbase.auth.login(LoginRequest(identifier = email, password = password, clientIp = ip))
val next = authbase.auth.refresh(RefreshRequest(refreshToken = tokens.refreshToken))
authbase.users.update(userId) { roles = listOf("admin"); username = null } // sends only these; null clears

Calls are suspend and throw AuthbaseException on a non-2xx answer: status, code (branch on this), detail, requestId, errors (per field, for 422), retryAfter (429/503). An unreachable server is code = "network_error".

Offline, against the app’s JWKS: fetched once, cached for 5 minutes, refetched (at most once a second) for an unknown kid, so a signing-key rotation needs no restart. Only EdDSA, typ: at+jwt, the app’s issuer and the app id as aud are accepted. Ed25519 is verified with the JDK’s own implementation (Java 15+), so no Tink or BouncyCastle is pulled in.

val claims = authbase.verifyAccessToken(token) // learns issuer and app id from the API key, once
val verifier = AccessTokenVerifier(VerifierOptions(issuer = "https://auth.example.com/apps/cashmate", appId = "01a0…"))

A bad token throws AuthbaseTokenException (reason: MISSING, EXPIRED — worth a refresh — or INVALID); an unreachable JWKS throws AuthbaseUnavailableException, an outage rather than the caller’s fault.

install(Authentication) { authbase { client(authbase) } } // or verifier(VerifierOptions(issuer, appId))
routing {
authenticate("authbase") {
get("/me") { call.respond(call.authbase!!.subject) }
requireRoles("admin") { delete("/posts/{id}") { /* … */ } }
}
}

401 (WWW-Authenticate: Bearer, RFC 6750) for a missing or bad token, 403 for missing roles, 503 when the JWKS cannot be fetched; bodies are problem+json.

Add spring-boot-starter-security-oauth2-resource-server and set either the client’s properties or the verifier’s:

authbase.url=https://auth.example.com
authbase.api-key=${AUTHBASE_API_KEY}
# or, to verify without an API key:
# authbase.issuer=https://auth.example.com/apps/cashmate
# authbase.app-id=01a0…
@Bean
fun security(http: HttpSecurity): SecurityFilterChain = http
.authorizeHttpRequests { it.requestMatchers("/admin/**").hasRole("admin"); it.anyRequest().authenticated() }
.oauth2ResourceServer { it.jwt { } }
.build()

The starter supplies the JwtDecoder (authbase’s verifier) and maps the roles claim to ROLE_… authorities; @AuthenticationPrincipal jwt: Jwt gives the claims (jwt.subject is the user id). An Authbase bean is there for calls; its methods are suspend, so Spring MVC controllers that call them need org.jetbrains.kotlinx:kotlinx-coroutines-reactor (Spring runs suspend handlers through Reactor). Define your own bean of any of these types to replace it.

./gradlew build compiles (warnings are errors), tests and packages every module. ModelsMatchSpecTest fails when a model’s fields drift from api/openapi/openapi.yaml. AUTHBASE_TEST_URL=http://localhost:8080 ./gradlew test also runs the integration test against a real instance.