Der Interceptor kann die Variable „localStorage“ nicht aufnehmen und an den Dienst übergebenJavaScript

Javascript-Forum
Anonymous
 Der Interceptor kann die Variable „localStorage“ nicht aufnehmen und an den Dienst übergeben

Post by Anonymous »

Problem
Ich habe ein Token-Objekt in localStorage gespeichert:

Code: Select all

{
accessToken: string
idToken: string
refreshToken: string
}
Wenn ich die Seite aktualisiere, habe ich den folgenden Interceptor erstellt:

Code: Select all

import { HttpErrorResponse, HttpHandlerFn, HttpInterceptorFn, HttpRequest } from "@angular/common/http"
import { inject } from "@angular/core"
import { AuthStore } from "../store/authentication/authentication.store"
import { AuthService } from "../services/auth"
import { catchError, switchMap, throwError } from "rxjs"
import { shouldSkip } from "./helpers/should-skip.helper"

export const authInterceptor: HttpInterceptorFn = (
req: HttpRequest,
next: HttpHandlerFn
) => {
const authStore = inject(AuthStore)
const authService = inject(AuthService)

// Skip login/refresh endpoints
if (shouldSkip(req.url)) {
return next(req)
}
console.log('INTERCEPTING:', req.url)
const accessToken = authStore.idToken()

const authReq = accessToken
? req.clone({
setHeaders: {
Authorization: `Bearer ${accessToken}`
}
})
: req

return next(authReq).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status !== 401) {
return throwError(() => error)
}

// Attempt token refresh
return authService.refreshToken(authStore.refreshToken()).pipe(
switchMap((response: any) => {
// Update store
authStore.setTokens({
accessToken: response.accessToken,
refreshToken: response.refreshToken,
idToken: response.idToken
})

// Retry original request
const retryReq = authReq.clone({
setHeaders: {
Authorization: `Bearer ${response.idToken}`
}
})

return next(retryReq)
}),
catchError(refreshError => {
// Refresh failed → logout
authStore.setTokens({
accessToken: '',
refreshToken: '',
idToken: ''
})

return throwError(() => refreshError)
})
)
})
)
}
Hier ist der ShouldSkip-Helfer als Referenz:

Code: Select all

const AUTH_SKIP_URLS = [
'/auth/login'
]

export function shouldSkip(url: string): boolean {
return AUTH_SKIP_URLS.some(path => url.includes(path))
}
Anstatt das RefreshToken zu treffen, wird es zu /login umgeleitet und trifft nicht auf den Sitzungs-/Refresh-Endpunkt. Im Idealfall sollte das idToken direkt aus localStorage abgerufen und an den Dienst übergeben und meine Anwendungssitzung aktualisiert werden.
Zur weiteren Referenz finden Sie hier meinen AuthStore, die Effekte, Ereignisse und Reduzierer:

Code: Select all

import { patchState, signalStore, withHooks, withMethods, withState } from "@ngrx/signals"
import { withAuthReducer } from "./authentication.reducer"
import { withAuthEffects } from "./authentication.effect"
import { injectDispatch } from "@ngrx/signals/events"
import { authInitEvents } from "./authentication.events"

export interface Auth {
user: string
accessToken: string
refreshToken: string
isAuthenticated: boolean
idToken: string
authLoading: boolean
hasError: boolean
error: string
}

const initialState: Auth = {
user: '',
accessToken: '',
refreshToken: '',
idToken: '',
isAuthenticated: false,
authLoading: false,
hasError: false,
error: ''
}

export const AuthStore = signalStore(
{ providedIn: 'root'},
withState(initialState),
withMethods((store) => ({
setTokens(tokens: { accessToken: string; idToken: string, refreshToken: string }) {
patchState(store, {
accessToken: tokens.accessToken,
idToken: tokens.idToken,
refreshToken: tokens.refreshToken
})
},
})),
withAuthReducer(),
withAuthEffects(),
withHooks({
onInit() {
const dispatch = injectDispatch(authInitEvents)
dispatch.start(true)
}
})

)

Code: Select all

import { signalStoreFeature } from "@ngrx/signals";
import { on, withReducer } from "@ngrx/signals/events";
import { authApiEvents, authPageEvents } from "./authentication.events";

export function withAuthReducer() {
return signalStoreFeature(
withReducer(
on(authPageEvents.login, () => {
return { authLoading: true}
}),
),
withReducer(
on(authApiEvents.logout, () => ({
user: '',
accessToken: '',
idToken: '',
refreshToken: '',
isAuthenticated: false,
authLoading: false,
hasError: false,
error: ''
}))
),
withReducer(
on(authApiEvents.loginSuccess, (event: { payload: {accessToken: string, idToken: string, refreshToken: string}}) =>  {
localStorage.setItem('auth_tokens', JSON.stringify({
accessToken: event.payload.accessToken,
refreshToken: event.payload.refreshToken,
idToken: event.payload.idToken
}))
return {
accessToken: event.payload.accessToken,
refreshToken: event.payload.refreshToken,
isAuthenticated: true,
authLoading: false
}
})
)
)
}

Code: Select all

import { type } from "@ngrx/signals";
import { eventGroup } from "@ngrx/signals/events";

export const authInitEvents = eventGroup({
source: 'Auth Init',
events: {
start: type()
}
})

export const authPageEvents = eventGroup({
source: 'Login Page',
events: {
login: type()
}
})

export const authApiEvents = eventGroup({
source: 'Login API',
events: {
logout: type(),
loginSuccess: type()
}
})

Code: Select all

import { inject } from "@angular/core";
import { signalStoreFeature } from "@ngrx/signals";
import { Events, withEffects } from "@ngrx/signals/events";
import { AuthService } from "../../services/auth";
import { authApiEvents, authInitEvents, authPageEvents } from "./authentication.events";
import { catchError, concatMap, exhaustMap, map, of, tap } from "rxjs";
import { Router } from "@angular/router";

export function withAuthEffects() {
return signalStoreFeature(
withEffects(
(
store: Record,
events = inject(Events),
router = inject(Router),
authService = inject(AuthService)
) => ({
login$: events.on(authPageEvents.login).pipe(
exhaustMap((event) =>
authService.login(event.payload.username, event.payload.password).pipe(
tap(() => {
router.navigate(['/'])}
),
concatMap((response: any) => of(authApiEvents.loginSuccess({
accessToken: response.accessToken,
idToken: response.idToken,
refreshToken: response.refreshToken
})))
)
)
),
hydrate$: events.on(authInitEvents.start).pipe(
exhaustMap(() => {
let tokens = authService.load()
let refreshToken = tokens.refreshToken ?? '' // it should be set here
if(!refreshToken) {
return of(authApiEvents.logout(true))a
}

return authService.refreshToken(refreshToken).pipe(
tap((response: any) => {

authService.refresh(response.refreshToken)
}),
map(({token}) => {

tokens = authService.load()!
// console.log(tokens)
return authApiEvents.loginSuccess({
accessToken: tokens.accessToken,
idToken: tokens.idToken,
refreshToken: tokens.refreshToken
})
}),
catchError(() => of(authApiEvents.logout(true)))
)
})
),
logout$: events.on(authApiEvents.logout).pipe(
tap(() => {
authService.clear()
router.navigate(['/login'])
})
)
})
)
)
}

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post