Перехватчик не может принять переменную localStorage и передать ее в службуJavascript

Форум по Javascript
Ответить
Anonymous
 Перехватчик не может принять переменную localStorage и передать ее в службу

Сообщение Anonymous »

Проблема
У меня есть объект токена, хранящийся в localStorage:
{
accessToken: string
idToken: string
refreshToken: string
}

Когда я обновляю страницу, у меня создается следующий перехватчик:
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)
})
)
})
)
}

Вот помощник mustSkip для справки:
const AUTH_SKIP_URLS = [
'/auth/login'
]

export function shouldSkip(url: string): boolean {
return AUTH_SKIP_URLS.some(path => url.includes(path))
}

Вместо обращения к RefreshToken он перенаправляется на /login и не достигает конечной точки сеанса/обновления. В идеале он должен извлекать idToken прямо из localStorage, передавать его в службу и обновлять сеанс моего приложения.
Для дальнейшего использования вот мой AuthStore, эффекты, события и редукторы:
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)
}
})

)

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
}
})
)
)
}

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()
}
})

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'])
})
)
})
)
)
}


Подробнее здесь: https://stackoverflow.com/questions/798 ... to-service
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»