Orazaka
Pages of the documentation

Authentication & sessions

How identity, sessions and access control work, flow by flow.

Complete guide to Orazaka's authentication architecture (local credentials, OAuth2, password recovery).


1. Overview

StrategyModuleConfigurationDescription
Local Credentialskrizaka-users-coreorazaka.identity.auth.localEmail + password hashed with BCrypt
OAuth2 Exchangekrizaka-users-coreorazaka.identity.auth.oauth2.*NextAuth verifies -> Gateway reconciles
Password Resetkrizaka-users-core—Token-based reset with zero-enumeration

Important. The backend never performs OAuth2 redirects or protocol negotiations. This is handled by NextAuth (BFF). The backend only validates tokens.


2. Local Credential Flow

Sequence — text version

Participants

  • Browser (NextAuth)
  • orazaka-web-client
  • orazaka-edge
  • krizaka-users-service
  • krizaka-users-core
  • krizaka-users-persistence
  • PostgreSQL · krizaka_users_db
  1. Step 1Browser (NextAuth) → orazaka-web-client: POST /api/auth/login {email, password}
  2. Step 2orazaka-web-client → orazaka-edge: POST /api/v1/auth/login
  3. Step 3orazaka-edge → krizaka-users-service: route /api/v1/auth
  4. Step 4krizaka-users-service → krizaka-users-core: IdentityService.authenticate(email, password)
  5. Step 5krizaka-users-core → krizaka-users-persistence: find the user by e-mail
  6. Step 6krizaka-users-persistence → PostgreSQL · krizaka_users_db: select the user
  7. Step 7krizaka-users-core → krizaka-users-core: BCrypt.matches — the hash never leaves the core
  8. Step 8krizaka-users-core replies to krizaka-users-service: User domain record
  9. Step 9krizaka-users-service replies to orazaka-web-client: session JWT
  10. Step 10orazaka-web-client replies to Browser (NextAuth): session cookie
  • Endpoint: POST /api/v1/auth/login
  • Payload: {"email": "...", "password": "..."}

3. OAuth2 Token-Exchange Flow

Sequence — text version

Participants

  • Browser (NextAuth)
  • Google / GitHub
  • orazaka-web-client
  • orazaka-edge
  • krizaka-users-service
  • krizaka-users-core
  • krizaka-users-persistence
  1. Step 1Browser (NextAuth) → Google / GitHub: OAuth2 authorization (redirects handled by NextAuth)
  2. Step 2Google / GitHub replies to Browser (NextAuth): ID token
  3. Step 3Browser (NextAuth) → orazaka-web-client: POST /api/auth/callback {provider, idToken}
  4. Step 4orazaka-web-client → orazaka-edge: POST /api/v1/auth/oauth
  5. Step 5orazaka-edge → krizaka-users-service: route /api/v1/auth
  6. Step 6krizaka-users-service → krizaka-users-core: IdentityReconciliationService.reconcile(provider, idToken)
  7. Step 7krizaka-users-core → krizaka-users-core: OAuth2ProviderVerifier — verify the token, extract the profile
  8. Step 8krizaka-users-core → krizaka-users-persistence: find the identity by provider and provider id
  9. Step 9if the user existskrizaka-users-core → krizaka-users-core: reuse the account
  10. Step 10else a new userkrizaka-users-core → krizaka-users-persistence: save the new user and its identity
  11. Step 11krizaka-users-core replies to krizaka-users-service: User domain record
  12. Step 12krizaka-users-service replies to orazaka-web-client: session JWT
  13. Step 13orazaka-web-client replies to Browser (NextAuth): session cookie
  • Endpoint: POST /api/v1/auth/oauth
  • Payload: {"provider": "google", "idToken": "..."}

4. Password Recovery Flow

Sequence — text version

Participants

  • Browser / CLI
  • orazaka-web-client
  • orazaka-edge
  • krizaka-users-service
  • krizaka-users-core
  • krizaka-users-persistence
  1. Request a reset
  2. Step 1Browser / CLI → orazaka-web-client: POST /api/auth/forgot {email}
  3. Step 2orazaka-web-client → orazaka-edge: POST /api/v1/auth/forgot
  4. Step 3orazaka-edge → krizaka-users-service: route /api/v1/auth
  5. Step 4krizaka-users-service → krizaka-users-core: PasswordRecoveryService.requestPasswordReset(email)
  6. Step 5krizaka-users-core → krizaka-users-persistence: find the enabled user by e-mail
  7. Step 6if the user existskrizaka-users-core → krizaka-users-core: generate a 48-byte token, keep only its SHA-256 hash
  8. Step 7if the user existskrizaka-users-core → krizaka-users-persistence: save the reset token (hash, 15 min)
  9. Step 8krizaka-users-service replies to Browser / CLI: 200 OK — always, so an address cannot be probed
  10. Reset the password
  11. Step 9Browser / CLI → orazaka-web-client: POST /api/auth/reset {token, newPassword}
  12. Step 10orazaka-web-client → orazaka-edge: POST /api/v1/auth/reset
  13. Step 11orazaka-edge → krizaka-users-service: route /api/v1/auth
  14. Step 12krizaka-users-service → krizaka-users-core: PasswordRecoveryService.resetPassword(token, newPassword)
  15. Step 13krizaka-users-core → krizaka-users-persistence: find the token by its SHA-256 hash
  16. Step 14if the token is valid and activekrizaka-users-core → krizaka-users-core: BCrypt.encode, outside the transaction
  17. Step 15if the token is valid and activekrizaka-users-core → krizaka-users-persistence: update the password hash, delete the token
  18. Step 16if the token is valid and activekrizaka-users-service replies to Browser / CLI: 200 OK
  19. Step 17else the token is invalid or expiredkrizaka-users-service replies to Browser / CLI: 400 Bad Request
  • Forgot Password: POST /api/v1/auth/forgot. Enforces Zero-Enumeration Invariant: returns 200 OK regardless of whether the email exists.
  • Reset Password: POST /api/v1/auth/reset with payload {"token": "...", "newPassword": "..."}. Returns 400 on invalid/expired token.

5. Security & Invariants

  • Token Cryptography: 48-byte SecureRandom token saved as SHA-256 hash (never plaintext). Expire in 15 minutes. Single-use.
  • Password Hashing: BCrypt is always executed outside @Transactional blocks to prevent pool starvation.
  • Provider Extensibility: Implement OAuth2ProviderVerifier and annotate with @ConditionalOnProperty to support new sign-in options.
  • Database Schemas: Password reset tokens and user password-change tracking are defined in infra/local-db/01-schema.sql. Flyway is disabled (spring.flyway.enabled=false); schemas load via Docker init scripts.
  • Concurrency: Global security context propagation utilizes a DelegatingSecurityContextExecutorService wrapper bean.

Edit this page on GitHub