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
UserDirectoryClientwith aSERVICEtoken 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.
Decisions and trade-offs
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 (
PT12Hby default,IDENTITY_JWT_TTL).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.
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
@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: PT60SDon'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
<!-- 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
- Platform kitAn event published after the commit is lost on the next crash.
- NotificationsAn e-mail sent inside a transaction that rolls back can't be unsent.
- Billing & creditsDebit after and the work ran on credit nobody had. Debit before and failures get billed.
- Build, BOM & test kitParent POMs drift — and a Spring BOM silently overrides the Boot version you chose.
- Krizaka UIThree products, three token vocabularies, 1,153
light:overrides in one app.