Проблема
У меня есть объект токена, хранящийся в 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
Перехватчик не может принять переменную localStorage и передать ее в службу ⇐ Javascript
Форум по Javascript
1768515770
Anonymous
[b]Проблема[/b]
У меня есть объект токена, хранящийся в 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'])
})
)
})
)
)
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79868970/interceptor-failing-to-ingest-localstorage-variable-and-pass-it-to-service[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия