Security model
Sessions
Services are stateless. The access token is a JWT with a short lifetime (handbook target about 15 minutes). The refresh token is stored hashed in auth.refresh_tokens, rotated on every refresh, and the family is revoked if a used token is presented again. Logout revokes the session.
Passwords are hashed with bcrypt or argon2 in the auth implementation. OTP codes are not logged. Private keys are not logged.
Who checks the token
shared-security JwtAuthenticationFilter runs in each service that accepts user traffic. The public key is SHARED_JWT_PUBLIC_KEY. Only auth-service holds the private key.
Admin users are a separate table (auth.admin_users) and admin controllers. They are not a role bit on a normal user.
Service to service
Internal routes use INTERNAL_API_KEY on the server and a per-caller client secret where SPLICK_INTERNAL_CLIENTS_* is configured. Treat a leaked internal key like a production incident: rotate, redeploy, review access logs for /internal/.
Edge
Rate limits on credential routes live in Kong, not only in the app. CORS on messaging is explicit (CORS_ORIGINS). Webhooks on communication-service verify provider signatures.
Secrets
No secrets in git. Local exceptions that already exist in infra folders (APNs keys, OAuth client files, env files) must not be copied into docs, tickets, or screenshots. This site's CI runs scripts/secret-scan.sh.
Account lifecycle
Deletion is a request plus confirmation, with an impact read against feed, then outbox auth.user-deleted so social, feed, expense, and messaging can project the deletion. Anonymize and purge jobs finish the auth-side cleanup.