Authentication & Security
Relevant source files
The following files were used as context for generating this wiki page:
- apps/api/src/main/kotlin/com/briefy/api/infrastructure/security/JwtService.kt
- apps/api/src/main/kotlin/com/briefy/api/config/SecurityConfig.kt
- apps/api/src/main/kotlin/com/briefy/api/application/auth/AuthService.kt
- apps/api/src/main/resources/db/migration/V20260205110000__auth_and_user_ownership.sql
- apps/web/src/routes/signup.tsx
- apps/api/src/test/kotlin/com/briefy/api/api/SourceControllerTest.kt
The Authentication and Security system in Briefy is designed to provide a stateless, secure environment for managing user identities and data ownership. It utilizes a dual-token approach—short-lived JSON Web Tokens (JWT) for access and long-lived opaque tokens for session refresh—ensuring that user sessions are both secure and recoverable without frequent re-authentication.
This system spans both the backend (Spring Boot) and frontend (React), enforcing strict authorization checks at the API level and providing protective route guards in the user interface. Data ownership is enforced at the database level through mandatory user associations for all primary entities like sources, briefings, and topics.
Sources: apps/api/src/main/kotlin/com/briefy/api/config/SecurityConfig.kt, apps/api/src/main/kotlin/com/briefy/api/application/auth/AuthService.kt, apps/api/src/main/resources/db/migration/V20260205110000__auth_and_user_ownership.sql
Security Architecture & Configuration
The backend security is governed by a SecurityConfig class that leverages Spring Security. It is configured to be entirely stateless, disabling CSRF protection and session cookies in favor of token-based authentication.
Authentication Filters
The system employs a specific filter chain to process incoming requests:
- JwtAuthenticationFilter: Intercepts requests to validate the JWT bearer token in the
Authorizationheader. - UsernamePasswordAuthenticationFilter: Standard filter replaced/preceded by the JWT filter.
- RequestMdcFilter: Injected after authentication to populate logging context with user information.
Sources: apps/api/src/main/kotlin/com/briefy/api/config/SecurityConfig.kt:45-56
Endpoint Authorization
Access control is defined using request matchers. While most API endpoints require authentication, specific routes are permit-all for infrastructure and auth management:
| Endpoint Path | Method | Purpose |
|---|---|---|
/api/auth/signup | POST | User registration |
/api/auth/login | POST | User authentication |
/api/auth/refresh | POST | Token renewal |
/api/auth/logout | POST | Session termination |
/api/health, /actuator/** | GET | Health checks |
/api/public/** | ANY | Publicly accessible resources |
Sources: apps/api/src/main/kotlin/com/briefy/api/config/SecurityConfig.kt:35-43
Token Management
The system uses two distinct types of tokens to balance security and user experience.
Access Tokens (JWT)
Access tokens are signed using the HMAC SHA-256 algorithm. They contain claims including the user ID (as the subject), email, and user role. These tokens are short-lived, defaulting to a 15-minute expiration period.
Sources: apps/api/src/main/kotlin/com/briefy/api/infrastructure/security/JwtService.kt:18-36
Refresh Tokens (Opaque)
Refresh tokens are 48-byte opaque strings generated using a secure random generator. Unlike JWTs, they are stored in the database in a hashed format (SHA-256) and are validated against a refresh_sessions table.
The diagram above illustrates the simultaneous issuance of a stateless JWT and a stateful refresh session during the login flow.
Sources: apps/api/src/main/kotlin/com/briefy/api/application/auth/AuthService.kt:98-115
Data Ownership & Multi-tenancy
Data security is enforced by strictly linking all domain entities to a user_id. The database schema was specifically hardened to ensure that data cannot exist without an owner, and indexes are used to prevent unauthorized access or duplicate URL ingestion by the same user.
Ownership Schema
Significant entities like sources, briefings, takeaways, and topics include a mandatory user_id column with a foreign key constraint to the users table.
| Entity | Constraint Type | Purpose |
|---|---|---|
sources | UNIQUE(user_id, url_normalized) | Prevents a user from adding the same URL twice. |
refresh_sessions | REFERENCES users(id) ON DELETE CASCADE | Ensures sessions are purged if a user is deleted. |
topics | INDEX(user_id) | Optimizes topic retrieval for the specific user. |
Sources: apps/api/src/main/resources/db/migration/V20260205110000__auth_and_user_ownership.sql:32-72
Service Level Enforcement
Application services, such as the AuthService, explicitly normalize inputs (e.g., lowercase emails) and verify user status (ensureActive()) before processing requests. Controller tests verify that users cannot access or restore resources they do not own, even if the resource ID is known.
Sources: apps/api/src/application/auth/AuthService.kt:53-56, apps/api/src/test/kotlin/com/briefy/api/api/SourceControllerTest.kt:607-619
Frontend Security Implementation
The web application (React) manages security through route guards and API client integration.
Authentication Guards
Routes that require a logged-in session, such as /settings or /briefings/$briefingId, utilize a beforeLoad hook. This hook executes requireAuth() or redirectAuthenticatedUser() to ensure the user's session state is valid before rendering the component.
Sources: apps/web/src/routes/settings/index.tsx:21-24, apps/web/src/routes/signup.tsx:14-17
Registration and Password Safety
The signup form enforces client-side validation, requiring passwords to be at least 8 characters long before the submission button is enabled. On the backend, passwords are never stored in plain text; they are hashed using the BCrypt algorithm via BCryptPasswordEncoder.
Sources: apps/web/src/routes/signup.tsx:75-78, apps/api/src/main/kotlin/com/briefy/api/config/SecurityConfig.kt:60
Configuration Summary
The security behavior is controlled by several environment variables and properties:
| Property | Default Value | Description |
|---|---|---|
auth.jwt.secret | (Required) | Secret key for signing access tokens. |
auth.jwt.access-token-minutes | 15 | Duration an access token remains valid. |
auth.refresh-token.days | 30 | Duration a refresh session remains valid. |
Sources: apps/api/src/main/kotlin/com/briefy/api/infrastructure/security/JwtService.kt:15-16, apps/api/src/main/kotlin/com/briefy/api/application/auth/AuthService.kt:31
The Authentication & Security system provides a robust foundation for the Briefy platform, ensuring that user identities are verified through standard JWT practices while maintaining a resilient session management system via opaque refresh tokens and strict database-level ownership constraints.