diff --git a/client/stillbox/src/app/app.component.html b/client/stillbox/src/app/app.component.html index 20725bc..3735317 100644 --- a/client/stillbox/src/app/app.component.html +++ b/client/stillbox/src/app/app.component.html @@ -8,7 +8,7 @@
- @if (auth.loggedIn) { + @if (auth.isAuth()) { @@ -19,7 +19,7 @@
- @if (auth.loggedIn) { + @if (auth.isAuth()) { } @else {
diff --git a/client/stillbox/src/app/app.config.ts b/client/stillbox/src/app/app.config.ts index 0014fed..cb21be6 100644 --- a/client/stillbox/src/app/app.config.ts +++ b/client/stillbox/src/app/app.config.ts @@ -29,7 +29,7 @@ export function authIntercept( next: HttpHandlerFn, ): Observable> { let authSvc: AuthService = inject(AuthService); - if (authSvc.loggedIn) { + if (authSvc.isAuth()) { req = req.clone({ setHeaders: { Authorization: `Bearer ${authSvc.getToken()}`, diff --git a/client/stillbox/src/app/login/auth.service.ts b/client/stillbox/src/app/login/auth.service.ts index 0ec7b97..46fc5df 100644 --- a/client/stillbox/src/app/login/auth.service.ts +++ b/client/stillbox/src/app/login/auth.service.ts @@ -1,56 +1,92 @@ -import { Injectable } from '@angular/core'; +import { + Injectable, + signal, + computed, + effect, + inject, + DestroyRef, +} from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Router } from '@angular/router'; -import { Observable } from 'rxjs'; +import { Observable, Subject } from 'rxjs'; import { tap } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export class Jwt { constructor(public jwt: string) {} } +type AuthState = { + user: string | null; + token: string | null; + is_auth: boolean; +}; + @Injectable({ providedIn: 'root', }) export class AuthService { - loggedIn: boolean = false; + private _accessTokenKey = 'jwt'; + private _storedToken = localStorage.getItem(this._accessTokenKey); + destroyed = inject(DestroyRef); + + private _state = signal({ + user: null, + token: this._storedToken, + is_auth: this._storedToken !== null, + }); + loginFailed = signal(false); + token = computed(() => this._state().token); + isAuth = computed(() => this._state().is_auth); + user = computed(() => this._state().user); constructor( private http: HttpClient, private _router: Router, ) { - let ssJWT = localStorage.getItem('jwt'); - if (ssJWT) { - this.loggedIn = true; - } + effect(() => { + const token = this.token(); + if (token !== null) { + localStorage.setItem(this._accessTokenKey, token); + } else { + localStorage.removeItem(this._accessTokenKey); + } + }); } - login(username: string, password: string): Observable> { + login(username: string, password: string) { return this.http - .post( - '/api/login', - { username: username, password: password }, - { observe: 'response' }, - ) - .pipe( - tap((event) => { - if (event.status == 200) { - localStorage.setItem('jwt', event.body?.jwt.toString() ?? ''); - this.loggedIn = true; - this._router.navigateByUrl('/home'); - } - }), - ); + .post('/api/login', { username: username, password: password }) + .pipe(takeUntilDestroyed(this.destroyed)) + .subscribe({ + next: (res) => { + let state = { + user: username, + token: res.jwt, + is_auth: true, + }; + this._state.set(state); + this.loginFailed.update(() => false); + this._router.navigateByUrl('/'); + }, + error: (err) => { + this.loginFailed.update(() => true); + }, + }); + } + + _clearState() { + this._state.set({}); } logout() { - this.http - .get('/api/logout', { withCredentials: true, observe: 'response' }) - .subscribe((event) => { - if (event.status == 200) { - this.loggedIn = false; - } - }); - localStorage.removeItem('jwt'); - this.loggedIn = false; + this.http.get('/api/logout', { withCredentials: true }).subscribe({ + next: (event) => { + this._clearState(); + }, + error: (err) => { + this._clearState(); + }, + }); this._router.navigateByUrl('/login'); } @@ -60,14 +96,20 @@ export class AuthService { .pipe( tap((event) => { if (event.status == 200) { - localStorage.setItem('jwt', event.body?.jwt.toString() ?? ''); - this.loggedIn = true; + let ost = this._state(); + let tok = event.body?.jwt.toString(); + let state = { + user: ost.user, + token: tok ? tok : null, + is_auth: true, + }; + this._state.set(state); } }), ); } getToken(): string | null { - return localStorage.getItem('jwt'); + return localStorage.getItem(this._accessTokenKey); } } diff --git a/client/stillbox/src/app/login/login.component.html b/client/stillbox/src/app/login/login.component.html index 293daed..1646e47 100644 --- a/client/stillbox/src/app/login/login.component.html +++ b/client/stillbox/src/app/login/login.component.html @@ -28,7 +28,7 @@
- @if (failed) { + @if (failed()) {
Login Failed!
diff --git a/client/stillbox/src/app/login/login.component.ts b/client/stillbox/src/app/login/login.component.ts index 5a8c5bc..a7adc76 100644 --- a/client/stillbox/src/app/login/login.component.ts +++ b/client/stillbox/src/app/login/login.component.ts @@ -1,4 +1,4 @@ -import { Component, inject } from '@angular/core'; +import { Component, inject, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { AuthService } from '../login/auth.service'; import { catchError, of, Subscription } from 'rxjs'; @@ -17,31 +17,9 @@ export class LoginComponent { router: Router = inject(Router); username: string = ''; password: string = ''; - failed: boolean = false; - private subscriptions = new Subscription(); + failed = this.apiService.loginFailed; onSubmit() { - this.failed = false; - this.subscriptions.add( - this.apiService - .login(this.username, this.password) - .pipe( - catchError(() => { - this.failed = true; - return of(null); - }), - ) - .subscribe((event) => { - if (event?.status == 200) { - this.router.navigateByUrl('/'); - } else { - this.failed = true; - } - }), - ); - } - - ngOnDestroy() { - this.subscriptions.unsubscribe(); + this.apiService.login(this.username, this.password); } } diff --git a/client/stillbox/src/app/talkgroups/talkgroups.service.ts b/client/stillbox/src/app/talkgroups/talkgroups.service.ts index 1184952..fee62a3 100644 --- a/client/stillbox/src/app/talkgroups/talkgroups.service.ts +++ b/client/stillbox/src/app/talkgroups/talkgroups.service.ts @@ -51,7 +51,7 @@ export class TalkgroupService { if (sh) { this.shareSvc.getShare(sh).subscribe(this.fetchAll); } else { - if (this.authSvc.loggedIn) { + if (this.authSvc.isAuth()) { this.fetchAll.next(null); } } @@ -59,7 +59,9 @@ export class TalkgroupService { } setShare(share: Share | null) { - this.fetchAll.next(share); + if (!this.authSvc.isAuth() && share !== null) { + this.fetchAll.next(share); + } } ngOnDestroy() {