diff --git a/apps/conduit/src/app/app.component.spec.ts b/apps/conduit/src/app/app.component.spec.ts index cc2293be..f0ff7e2e 100644 --- a/apps/conduit/src/app/app.component.spec.ts +++ b/apps/conduit/src/app/app.component.spec.ts @@ -4,7 +4,6 @@ import { AppComponent } from './app.component'; import { RouterTestingModule } from '@angular/router/testing'; import { FooterComponent } from './layout/footer/footer.component'; import { NavbarComponent } from './layout/navbar/navbar.component'; -import { LocalStorageJwtService } from '@realworld/auth/data-access'; describe('AppComponent', () => { let component: AppComponent; @@ -14,7 +13,6 @@ describe('AppComponent', () => { TestBed.configureTestingModule({ imports: [RouterTestingModule], declarations: [AppComponent, FooterComponent, NavbarComponent], - providers: [LocalStorageJwtService], }).compileComponents(); })); diff --git a/apps/conduit/src/app/app.component.ts b/apps/conduit/src/app/app.component.ts index 0d11f7f7..67a56b82 100644 --- a/apps/conduit/src/app/app.component.ts +++ b/apps/conduit/src/app/app.component.ts @@ -1,7 +1,6 @@ -import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AuthStore, LocalStorageJwtService } from '@realworld/auth/data-access'; -import { filter, take } from 'rxjs/operators'; +import { AuthStore } from '@realworld/auth/data-access'; import { FooterComponent } from './layout/footer/footer.component'; import { NavbarComponent } from './layout/navbar/navbar.component'; @@ -11,17 +10,10 @@ import { NavbarComponent } from './layout/navbar/navbar.component'; imports: [FooterComponent, NavbarComponent, RouterModule], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AppComponent implements OnInit { - private readonly localStorageJwtService = inject(LocalStorageJwtService); +export class AppComponent { protected readonly authStore = inject(AuthStore); - ngOnInit() { - this.localStorageJwtService - .getItem() - .pipe( - take(1), - filter((token) => !!token), - ) - .subscribe(() => this.authStore.getUser()); + constructor() { + this.authStore.getUser(); } } diff --git a/apps/conduit/src/app/app.config.ts b/apps/conduit/src/app/app.config.ts index 92be3adb..b67a8e83 100644 --- a/apps/conduit/src/app/app.config.ts +++ b/apps/conduit/src/app/app.config.ts @@ -1,6 +1,6 @@ import { ApplicationConfig, provideExperimentalZonelessChangeDetection } from '@angular/core'; import { provideRouter, withComponentInputBinding, withViewTransitions } from '@angular/router'; -import { authGuard, tokenInterceptor } from '@realworld/auth/data-access'; +import { authGuard } from '@realworld/auth/data-access'; import { errorHandlingInterceptor } from '@realworld/core/error-handler'; import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { API_URL } from '@realworld/core/http-client'; @@ -50,7 +50,7 @@ export const appConfig: ApplicationConfig = { withViewTransitions(), withComponentInputBinding(), ), - provideHttpClient(withInterceptors([errorHandlingInterceptor, tokenInterceptor])), + provideHttpClient(withInterceptors([errorHandlingInterceptor])), { provide: API_URL, useValue: environment.api_url }, ], }; diff --git a/apps/conduit/src/environments/environment.ts b/apps/conduit/src/environments/environment.ts index 14207410..b8140323 100644 --- a/apps/conduit/src/environments/environment.ts +++ b/apps/conduit/src/environments/environment.ts @@ -5,5 +5,5 @@ export const environment = { production: false, - api_url: 'https://real-world-app-39656dff2ddc.herokuapp.com/api', + api_url: 'http://localhost:3000/api', }; diff --git a/libs/auth/data-access/src/auth.store.spec.ts b/libs/auth/data-access/src/auth.store.spec.ts index f4fc1bd3..695f22ce 100644 --- a/libs/auth/data-access/src/auth.store.spec.ts +++ b/libs/auth/data-access/src/auth.store.spec.ts @@ -3,13 +3,12 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { inject, TestBed } from '@angular/core/testing'; import { AuthStore } from './auth.store'; -import { LocalStorageJwtService } from './services/local-storage-jwt.service'; describe('AuthStore', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], - providers: [AuthStore, LocalStorageJwtService, ApiService], + providers: [AuthStore, ApiService], }); }); diff --git a/libs/auth/data-access/src/auth.store.ts b/libs/auth/data-access/src/auth.store.ts index 1ecd2189..d9edfa41 100644 --- a/libs/auth/data-access/src/auth.store.ts +++ b/libs/auth/data-access/src/auth.store.ts @@ -5,7 +5,6 @@ import { inject } from '@angular/core'; import { AuthService } from './services/auth.service'; import { exhaustMap, pipe, switchMap, tap } from 'rxjs'; import { tapResponse } from '@ngrx/operators'; -import { LocalStorageJwtService } from './services/local-storage-jwt.service'; import { Router } from '@angular/router'; import { LoginUser, NewUser, User } from '@realworld/core/api-types'; import { setLoaded, withCallState } from '@realworld/core/data-access'; @@ -15,13 +14,7 @@ export const AuthStore = signalStore( { providedIn: 'root' }, withState(authInitialState), withMethods( - ( - store, - formErrorsStore = inject(FormErrorsStore), - authService = inject(AuthService), - localStorageService = inject(LocalStorageJwtService), - router = inject(Router), - ) => ({ + (store, formErrorsStore = inject(FormErrorsStore), authService = inject(AuthService), router = inject(Router)) => ({ getUser: rxMethod( pipe( switchMap(() => authService.user()), @@ -35,7 +28,6 @@ export const AuthStore = signalStore( tapResponse({ next: ({ user }) => { patchState(store, { user, loggedIn: true }); - localStorageService.setItem(user.token); router.navigateByUrl('/'); }, error: ({ error }) => formErrorsStore.setErrors(error.errors), @@ -51,7 +43,6 @@ export const AuthStore = signalStore( tapResponse({ next: ({ user }) => { patchState(store, { user, loggedIn: true }); - localStorageService.setItem(user.token); router.navigateByUrl('/'); }, error: ({ error }) => formErrorsStore.setErrors(error.errors), @@ -67,7 +58,6 @@ export const AuthStore = signalStore( tapResponse({ next: ({ user }) => { patchState(store, { user }); - localStorageService.setItem(user.token); router.navigate(['profile', user.username]); }, error: ({ error }) => formErrorsStore.setErrors(error.errors), @@ -76,11 +66,21 @@ export const AuthStore = signalStore( ), ), ), - logout: () => { - patchState(store, { user: initialUserValue, loggedIn: false }); - localStorageService.removeItem(); - router.navigateByUrl('login'); - }, + logout: rxMethod( + pipe( + exhaustMap(() => + authService.logout().pipe( + tapResponse({ + next: () => { + patchState(store, { user: initialUserValue, loggedIn: false }); + router.navigateByUrl('login'); + }, + error: ({ error }) => formErrorsStore.setErrors(error.errors), + }), + ), + ), + ), + ), }), ), withCallState({ collection: 'getUser' }), diff --git a/libs/auth/data-access/src/index.ts b/libs/auth/data-access/src/index.ts index 206c4c96..9fc5e7ad 100644 --- a/libs/auth/data-access/src/index.ts +++ b/libs/auth/data-access/src/index.ts @@ -1,5 +1,3 @@ -export * from './services/token-interceptor.service'; -export * from './services/local-storage-jwt.service'; export * from './services/auth-guard'; export { AuthStore } from './auth.store'; diff --git a/libs/auth/data-access/src/services/auth-guard.spec.ts b/libs/auth/data-access/src/services/auth-guard.spec.ts index 0e64e2f2..97b21481 100644 --- a/libs/auth/data-access/src/services/auth-guard.spec.ts +++ b/libs/auth/data-access/src/services/auth-guard.spec.ts @@ -41,7 +41,7 @@ describe('authGuard', () => { }); it('should return login urlTree if the user is not logged in', () => { - jest.spyOn(storage, 'getItem').mockImplementationOnce(() => of(null)); + jest.spyOn(storage, 'getItem').mockImplementationOnce(() => null); const result = TestBed.runInInjectionContext(() => authGuard()); diff --git a/libs/auth/data-access/src/services/auth-guard.ts b/libs/auth/data-access/src/services/auth-guard.ts index 2e65d278..99197542 100644 --- a/libs/auth/data-access/src/services/auth-guard.ts +++ b/libs/auth/data-access/src/services/auth-guard.ts @@ -1,20 +1,14 @@ import { inject } from '@angular/core'; -import { map, Observable, take } from 'rxjs'; import { Router, UrlTree } from '@angular/router'; +import { AuthStore } from '../auth.store'; -import { LocalStorageJwtService } from './local-storage-jwt.service'; - -export const authGuard = (): Observable => { - const storage = inject(LocalStorageJwtService); +export const authGuard = (): boolean | UrlTree => { const router = inject(Router); + const authStore = inject(AuthStore); + + if (!authStore.loggedIn()) { + return router.parseUrl('/login'); + } - return storage.getItem().pipe( - map((token) => { - if (!token) { - return router.parseUrl('/login'); - } - return true; - }), - take(1), - ); + return true; }; diff --git a/libs/auth/data-access/src/services/auth.service.spec.ts b/libs/auth/data-access/src/services/auth.service.spec.ts index 660f8a5d..c96dcd0b 100644 --- a/libs/auth/data-access/src/services/auth.service.spec.ts +++ b/libs/auth/data-access/src/services/auth.service.spec.ts @@ -3,13 +3,12 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { inject, TestBed } from '@angular/core/testing'; import { AuthService } from './auth.service'; -import { LocalStorageJwtService } from './local-storage-jwt.service'; describe('AuthService', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], - providers: [AuthService, LocalStorageJwtService, ApiService], + providers: [AuthService, ApiService], }); }); diff --git a/libs/auth/data-access/src/services/auth.service.ts b/libs/auth/data-access/src/services/auth.service.ts index 965e6d4b..20c9b0cd 100644 --- a/libs/auth/data-access/src/services/auth.service.ts +++ b/libs/auth/data-access/src/services/auth.service.ts @@ -20,6 +20,10 @@ export class AuthService { return this.apiService.post('/users/login', { user: credentials }); } + logout(): Observable<{ message: string }> { + return this.apiService.post<{ message: string }, void>('/users/logout'); + } + register(credentials: NewUser): Observable { return this.apiService.post('/users', { user: credentials }); } diff --git a/libs/auth/data-access/src/services/local-storage-jwt.service.ts b/libs/auth/data-access/src/services/local-storage-jwt.service.ts deleted file mode 100644 index 05a22c53..00000000 --- a/libs/auth/data-access/src/services/local-storage-jwt.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Observable, of } from 'rxjs'; -import { Injectable } from '@angular/core'; - -@Injectable({ providedIn: 'root' }) -export class LocalStorageJwtService { - getItem(): Observable { - const data = localStorage.getItem('jwtToken'); - if (data) { - return of(data); - } - return of(null); - } - - setItem(data: string): Observable { - localStorage.setItem('jwtToken', data); - return of(data); - } - - removeItem(): Observable { - localStorage.removeItem('jwtToken'); - return of(true); - } -} diff --git a/libs/auth/data-access/src/services/token-interceptor.service.spec.ts b/libs/auth/data-access/src/services/token-interceptor.service.spec.ts deleted file mode 100644 index 00f207b4..00000000 --- a/libs/auth/data-access/src/services/token-interceptor.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { inject, TestBed } from '@angular/core/testing'; - -import { LocalStorageJwtService } from './local-storage-jwt.service'; -import { TokenInterceptorService } from './token-interceptor.service'; - -describe('TokenInterceptorService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [TokenInterceptorService, LocalStorageJwtService], - }); - }); - - it('should be created', inject([TokenInterceptorService], (service: TokenInterceptorService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/libs/auth/data-access/src/services/token-interceptor.service.ts b/libs/auth/data-access/src/services/token-interceptor.service.ts deleted file mode 100644 index aa8cf313..00000000 --- a/libs/auth/data-access/src/services/token-interceptor.service.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http'; -import { inject } from '@angular/core'; -import { Observable } from 'rxjs'; -import { LocalStorageJwtService } from './local-storage-jwt.service'; - -export const tokenInterceptor = (request: HttpRequest, next: HttpHandlerFn): Observable> => { - let token: string | null = null; - inject(LocalStorageJwtService) - .getItem() - .subscribe((t) => (token = t)); - - if (token) { - request = request.clone({ - setHeaders: { - Authorization: `Token ${token}`, - }, - }); - } - return next(request); -}; diff --git a/libs/core/http-client/src/lib/api.service.ts b/libs/core/http-client/src/lib/api.service.ts index 7db53f59..0f07c7f6 100644 --- a/libs/core/http-client/src/lib/api.service.ts +++ b/libs/core/http-client/src/lib/api.service.ts @@ -12,22 +12,28 @@ export class ApiService { return this.http.get(`${this.api_url}${url}`, { headers: this.headers, params, + withCredentials: true, }); } post(url: string, data?: D): Observable { - return this.http.post(`${this.api_url}${url}`, JSON.stringify(data), { headers: this.headers }); + return this.http.post(`${this.api_url}${url}`, JSON.stringify(data), { + headers: this.headers, + withCredentials: true, + }); } put(url: string, data: D): Observable { return this.http.put(`${this.api_url}${url}`, JSON.stringify(data), { headers: this.headers, + withCredentials: true, }); } delete(url: string): Observable { return this.http.delete(`${this.api_url}${url}`, { headers: this.headers, + withCredentials: true, }); }