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:
@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ImageRecord, ImageService } from '../services/image.service';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-detail',
|
||||
@@ -20,10 +21,10 @@ import { ImageRecord, ImageService } from '../services/image.service';
|
||||
<h3>Tags</h3>
|
||||
<div class="chips">
|
||||
<span *ngFor="let tag of image.tags" class="chip">
|
||||
{{ tag }} <button (click)="removeTag(tag)">×</button>
|
||||
{{ tag }} <button *ngIf="auth.isAuthenticated()" (click)="removeTag(tag)">×</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="add-tag">
|
||||
<div class="add-tag" *ngIf="auth.isAuthenticated()">
|
||||
<input
|
||||
[(ngModel)]="newTagInput"
|
||||
placeholder="Add tag…"
|
||||
@@ -34,7 +35,7 @@ import { ImageRecord, ImageService } from '../services/image.service';
|
||||
<p class="tag-error" *ngIf="tagError">{{ tagError }}</p>
|
||||
</section>
|
||||
|
||||
<button class="delete-btn" (click)="showDeleteDialog = true">Delete Image</button>
|
||||
<button *ngIf="auth.isAuthenticated()" class="delete-btn" (click)="showDeleteDialog = true">Delete Image</button>
|
||||
|
||||
<div class="dialog-overlay" *ngIf="showDeleteDialog">
|
||||
<div class="dialog">
|
||||
@@ -75,6 +76,7 @@ export class DetailComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
public imageService: ImageService,
|
||||
public auth: AuthService,
|
||||
private route: ActivatedRoute,
|
||||
public router: Router,
|
||||
private cdr: ChangeDetectorRef,
|
||||
|
||||
Reference in New Issue
Block a user