Krizaka
All building blocks

krizaka-users

Users

Sign-up, login, profiles, API keys and roles as a service — with a typed client for the others.

The problem it removes

Sign-up looks like a weekend. Reset tokens, OAuth and JWTs make it a quarter.

Sign-up looks like a weekend. Then come e-mail verification, reset tokens that must expire and be single-use, Google and GitHub accounts to link, API keys, roles in a JWT — and every other service validating that JWT its own way.

  • Reset links that work twice, never expire, or sit in the database in clear text — readable by anyone with a backup.
  • A users table that grows a column per product feature until another product cannot reuse it.
  • Every service calling the identity service on every request — identity down means everything down.

What it does

  • Register, verify, log in with a password or Google/GitHub, reset — as a service.
  • Session JWTs carry the roles; every other service verifies them locally.
  • A typed UserDirectoryClient with a SERVICE token and a per-entry cache.

A Spring Boot service (or the -core you embed) that registers, verifies, logs in with a password or Google/GitHub, resets passwords, issues API keys and session JWTs carrying roles — and a client that every other service calls with a SERVICE token.

The owl knows every face and keeps no secret in clear: a reset token is single-use, stored as a SHA-256 hash and dead after 15 minutes; a password is a BCrypt hash; a provider key is encrypted with AES-256 before it touches the disk.

The owl's rule

Decisions and trade-offs

  1. We chose

    Other services verify the session JWT locally (krizaka-security), with roles in the token.

    We refused

    Calling the users service (token introspection) on every request.

    Because

    The users service being down does not take the platform down, and a request costs no network hop.

    What it costs you

    A revoked session stays valid until it expires (PT12H by default, IDENTITY_JWT_TTL).

  2. We chose

    A profile is a theme plus attributes your application defines — the answers of an onboarding form whose JSON schema you supply — stored as given and never interpreted.

    We refused

    A fixed profile model with product fields.

    Because

    The service knows users, not your product; Orazaka's own fields moved out of it into its onboarding answers.

    What it costs you

    Your code reads its attributes with its own defaults.

  3. We chose

    A published contract (krizaka-users-api), a client with a per-entry cache, and an embeddable -core.

    We refused

    A library that each application wires to its own tables.

    Because

    One place hashes passwords and signs tokens; a fix lands once.

    What it costs you

    A PostgreSQL database and RabbitMQ for its events (evt.user.registered, evt.password.reset).

In code

javaInvoiceService.java · application.yml
@Service
class InvoiceService {
  private final UserDirectoryClient users; // SERVICE token on every call, per-entry cache

  InvoiceService(UserDirectoryClient users) { this.users = users; }

  String recipient(String userId) {
    return users.getUser(userId).email();
  }

  String plan(String userId) { // attributes are YOUR onboarding answers, stored as given
    return (String) users.getProfile(userId).attributes().getOrDefault("plan", "free");
  }
}

# application.yml
krizaka.users.client:
  base-url: http://users:8083
  service-secret: ${IDENTITY_JWT_SECRET}
  service-name: billing-service
  cache-ttl: PT60S

Don't use it when

  • You already run an identity provider (Keycloak, Auth0, Entra ID): keep it.
  • You need SAML, enterprise SSO or multi-factor authentication: none of them is provided.
  • You need a revocation that takes effect before the token expires.

Where it stands

0.1.0 on Maven Central (api, client, core). 0.2.0 — event schemas checked against their producers — is merged and ships in November.

Published

In progress

  • BOM 0.2.0 manages this block at an unpublished 0.2.0: declare 0.1.0 explicitly until BOM 0.3.0.krizaka-build#11
  • Session tokens signed by this service only, verified with its JWKS by the others — no shared secret.krizaka-platform-kit#11

Adopt it

xmlpom.xml
<!-- BOM 0.2.0 names an unpublished 0.2.0 of this block: declare 0.1.0 until BOM 0.3.0 -->
<dependency>
  <groupId>com.krizaka</groupId>
  <artifactId>krizaka-users-client</artifactId>
  <version>0.1.0</version>
</dependency>

Tell us where it hurts.

A block is right when it survives your code base, not ours. Ask in the block's thread, propose a change as an idea, or report a bug on its repository — every decision on this page is open to a better argument.

The other blocks