Feat: Implement JWT bearer token authentication

Protects image upload, delete, and tag-update endpoints behind
Bearer token auth. Public read endpoints remain open. Angular SPA
gains a login page, auth interceptor, and route guard for /upload.

- JWTAuthProvider (HS256, sub/iat/exp, secrets.compare_digest)
- POST /api/v1/auth/token login endpoint
- require_auth FastAPI dependency on all write routes
- AuthService, LoginComponent, authInterceptor, authGuard
- Detail page hides write controls for unauthenticated visitors
- 43 unit tests passing; integration tests require Docker stack

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-03 19:12:38 +00:00
parent d91a65abe5
commit 5fbbc1e67f
36 changed files with 3998 additions and 42 deletions

View File

@@ -1,12 +1,31 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';
import { Router, RouterOutlet } from '@angular/router';
import { AuthService } from './auth/auth.service';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `<router-outlet />`,
imports: [CommonModule, RouterOutlet],
template: `
<header class="app-header" *ngIf="auth.isAuthenticated()">
<button class="logout-btn" (click)="onLogout()">Sign out</button>
</header>
<router-outlet />
`,
styles: [`
.app-header { display: flex; justify-content: flex-end; padding: 8px 16px; background: #1a1a1a; border-bottom: 1px solid #333; }
.logout-btn { background: none; border: 1px solid #555; color: #aaa; padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 0.9rem; }
.logout-btn:hover { border-color: #aaa; color: #e0e0e0; }
`],
})
export class AppComponent {
title = 'reactbin-ui';
constructor(public auth: AuthService, private router: Router) {}
onLogout(): void {
this.auth.logout();
this.router.navigate(['/login']);
}
}