인증/권한 업무 규칙과 전환 불변식 추출 #2

Open
forge-bot wants to merge 9 commits from forge/codex-bais-final3-20260713-192718-BAIS-SPRING-ANA-AUTH-001-attempt-1-run-f0808bb53869 into main
Showing only changes of commit 8fb5989bae - Show all commits

View file

@ -0,0 +1,163 @@
# 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<String> 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<String> ROLE_HIERARCHY =
List.of("ADMIN", "MANAGER", "USER", "GUEST");
// Lower index = higher privilege
```