# Authentication & Authorization Transition Invariants ## 1. Authentication Rules ### 1.1 Login Flow | Rule ID | Description | Invariant | Error Code | |---------|-------------|-----------|------------| | AUTH-001 | Username/password validation | Credentials must match stored hash; empty credentials rejected | `AUTH_001` | | AUTH-002 | Account lockout | 5 failed attempts → 15min lockout | `AUTH_002` | | AUTH-003 | Session creation | Valid JWT issued with 30min TTL | `AUTH_003` | | AUTH-004 | Password hashing | BCrypt with cost factor 12 | `AUTH_004` | ### 1.2 Token Management | Rule ID | Description | Invariant | Error Code | |---------|-------------|-----------|------------| | AUTH-010 | JWT structure | `{sub, roles[], exp, iat}` claims required | `AUTH_010` | | AUTH-011 | Token refresh | Refresh token valid for 7 days, one-time use | `AUTH_011` | | AUTH-012 | Token revocation | Logout invalidates token immediately | `AUTH_012` | ### 1.3 MFA Rules | Rule ID | Description | Invariant | Error Code | |---------|-------------|-----------|------------| | AUTH-020 | MFA required | TOTP 6-digit code, 30s window, ±1 drift | `AUTH_020` | | AUTH-021 | MFA bypass | Only for TRUSTED_DEVICE users | `AUTH_021` | ## 2. Authorization Rules ### 2.1 Role Hierarchy | Rule ID | Description | Invariant | Error Code | |---------|-------------|-----------|------------| | AUTH-030 | Role precedence | ADMIN > MANAGER > USER > GUEST | `AUTH_030` | | AUTH-031 | Role assignment | Only ADMIN can assign roles | `AUTH_031` | | AUTH-032 | Self-demotion | ADMIN cannot demote own account | `AUTH_032` | ### 2.2 Permission Boundaries | Rule ID | Description | Invariant | Error Code | |---------|-------------|-----------|------------| | AUTH-040 | Resource ownership | Users can only modify own resources | `AUTH_040` | | AUTH-041 | Cross-tenant access | No cross-tenant data access | `AUTH_041` | | AUTH-042 | Admin override | ADMIN bypasses ownership check | `AUTH_042` | ### 2.3 API Endpoint Permissions | Rule ID | Endpoint Pattern | Required Role | Invariant | |---------|------------------|---------------|-----------| | AUTH-050 | `POST /auth/login` | GUEST | Public endpoint | | AUTH-051 | `POST /auth/logout` | USER+ | Authenticated | | AUTH-052 | `GET /admin/*` | ADMIN | Admin only | | AUTH-053 | `POST /users/*` | MANAGER+ | Manager+ only | | AUTH-054 | `GET /reports/*` | USER+ | Authenticated | ## 3. Error Response Format ### 3.1 Standard Error Structure ```json { "code": "AUTH_XXX", "message": "Human-readable message", "timestamp": "ISO-8601", "path": "/original/request/path" } ``` ### 3.2 HTTP Status Mapping | HTTP Status | Condition | |-------------|-----------| | 400 | Invalid request format | | 401 | Missing/invalid credentials | | 403 | Insufficient permissions | | 404 | Resource not found | | 423 | Account locked | | 429 | Rate limit exceeded | | 500 | Internal server error | ## 4. Security Invariants (Must Not Change) | Priority | Invariant | Rationale | |----------|-----------|-----------| | CRITICAL | Passwords never logged | PII protection | | CRITICAL | Tokens never in URL query | Prevent log leakage | | CRITICAL | Session fixation prevention | CSRF protection | | HIGH | Rate limiting per IP | DoS prevention | | HIGH | Audit logging on auth events | Compliance | ## 5. Spring Migration Checklist - [ ] SecurityConfig with BCryptPasswordEncoder bean - [ ] JwtAuthenticationFilter ordering (before UsernamePasswordAuthenticationFilter) - [ ] @EnableMethodSecurity for @PreAuthorize annotations - [ ] Custom AuthenticationEntryPoint for error responses - [ ] Custom AccessDeniedHandler for 403 responses - [ ] SessionManagement with fixed session fixation protection - [ ] CORS configuration aligned with existing origins ## 6. Implementation Reference ### 6.1 SecurityConfig Bean Configuration ```java @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); // AUTH-004 } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .ignoringRequestMatchers("/auth/login", "/auth/refresh") ) .sessionManagement(s -> s .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .sessionFixation().migrateSession() ) .authorizeHttpRequests(auth -> auth .requestMatchers("/auth/login", "/auth/refresh").permitAll() .requestMatchers("/admin/**").hasRole("ADMIN") .requestMatchers("/users/**").hasAnyRole("MANAGER", "ADMIN") .requestMatchers("/reports/**").authenticated() .anyRequest().authenticated() ) .exceptionHandling(ex -> ex .authenticationEntryPoint(new CustomAuthEntryPoint()) .accessDeniedHandler(new CustomAccessDeniedHandler()) ) .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) .build(); } ``` ### 6.2 JWT Claims Structure ```java public record JwtClaims( String subject, // username List roles, // ["ADMIN", "USER"] Date issuedAt, Date expiration // 30min for access, 7days for refresh ) {} ``` ### 6.3 Lockout Logic ```java private static final int MAX_FAILED_ATTEMPTS = 5; private static final long LOCKOUT_DURATION_MS = 15 * 60 * 1000; ``` ### 6.4 Role Hierarchy Levels ```java private static final List ROLE_HIERARCHY = List.of("ADMIN", "MANAGER", "USER", "GUEST"); // Lower index = higher privilege ```