Only use OIDC token when it's valid
Need to extend the wait logic to the JWT interceptor as well
This commit is contained in:
parent
f868f5df91
commit
63a5750f28
5 changed files with 14 additions and 25 deletions
|
|
@ -3,20 +3,22 @@ import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/c
|
|||
import {Observable, switchMap} from 'rxjs';
|
||||
import { AccountService } from '../_services/account.service';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { OidcService } from '../_services/oidc.service';
|
||||
|
||||
@Injectable()
|
||||
export class JwtInterceptor implements HttpInterceptor {
|
||||
|
||||
constructor(private accountService: AccountService) {}
|
||||
constructor(private accountService: AccountService, private oidcService: OidcService) { }
|
||||
|
||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
return this.accountService.currentUser$.pipe(
|
||||
take(1),
|
||||
switchMap(user => {
|
||||
if (user) {
|
||||
const token = this.oidcService.hasValidToken() ? this.oidcService.token : user.token;
|
||||
request = request.clone({
|
||||
setHeaders: {
|
||||
Authorization: `Bearer ${user.oidcToken ?? user.token}`
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,6 @@ import {Preferences} from './preferences/preferences';
|
|||
// This interface is only used for login and storing/retrieving JWT from local storage
|
||||
export interface User {
|
||||
username: string;
|
||||
// This is set by the oidc service, will always take precedence over the Kavita generated token
|
||||
// When set, the refresh logic for the Kavita token will not run
|
||||
oidcToken: string;
|
||||
token: string;
|
||||
refreshToken: string;
|
||||
roles: string[];
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {takeUntilDestroyed, toSignal} from "@angular/core/rxjs-interop";
|
|||
import {Action} from "./action-factory.service";
|
||||
import {LicenseService} from "./license.service";
|
||||
import {LocalizationService} from "./localization.service";
|
||||
import {OidcService} from "./oidc.service";
|
||||
|
||||
export enum Role {
|
||||
Admin = 'Admin',
|
||||
|
|
@ -46,6 +47,7 @@ export const allRoles = [
|
|||
export class AccountService {
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly oidcService = inject(OidcService);
|
||||
private readonly licenseService = inject(LicenseService);
|
||||
private readonly localizationService = inject(LocalizationService);
|
||||
|
||||
|
|
@ -92,10 +94,6 @@ export class AccountService {
|
|||
});
|
||||
}
|
||||
|
||||
oidcEnabled() {
|
||||
return this.httpClient.get<boolean>(this.baseUrl + "oidc/enabled");
|
||||
}
|
||||
|
||||
canInvokeAction(user: User, action: Action) {
|
||||
const isAdmin = this.hasAdminRole(user);
|
||||
const canDownload = this.hasDownloadRole(user);
|
||||
|
|
@ -219,7 +217,6 @@ export class AccountService {
|
|||
tap((response: User) => {
|
||||
const user = response;
|
||||
if (user) {
|
||||
user.oidcToken = token;
|
||||
this.setCurrentUser(user);
|
||||
}
|
||||
}),
|
||||
|
|
@ -263,7 +260,7 @@ export class AccountService {
|
|||
this.licenseService.hasValidLicense().subscribe();
|
||||
}
|
||||
// oidc handles refreshing itself
|
||||
if (!this.currentUser.oidcToken) {
|
||||
if (!this.oidcService.hasValidToken()) {
|
||||
this.startRefreshTokenTimer();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {DashboardUpdateEvent} from "../_models/events/dashboard-update-event";
|
|||
import {SideNavUpdateEvent} from "../_models/events/sidenav-update-event";
|
||||
import {SiteThemeUpdatedEvent} from "../_models/events/site-theme-updated-event";
|
||||
import {ExternalMatchRateLimitErrorEvent} from "../_models/events/external-match-rate-limit-error-event";
|
||||
import {OidcService} from "./oidc.service";
|
||||
|
||||
export enum EVENTS {
|
||||
UpdateAvailable = 'UpdateAvailable',
|
||||
|
|
@ -146,7 +147,7 @@ export class MessageHubService {
|
|||
*/
|
||||
public onlineUsers$ = this.onlineUsersSource.asObservable();
|
||||
|
||||
constructor() {}
|
||||
constructor(private oidcService: OidcService) {}
|
||||
|
||||
/**
|
||||
* Tests that an event is of the type passed
|
||||
|
|
@ -165,7 +166,7 @@ export class MessageHubService {
|
|||
createHubConnection(user: User) {
|
||||
this.hubConnection = new HubConnectionBuilder()
|
||||
.withUrl(this.hubUrl + 'messages', {
|
||||
accessTokenFactory: () => user.oidcToken ?? user.token
|
||||
accessTokenFactory: () => this.oidcService.hasValidToken() ? this.oidcService.token : user.token
|
||||
})
|
||||
.withAutomaticReconnect()
|
||||
//.withStatefulReconnect() // Requires signalr@8.0
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ export class OidcService {
|
|||
|
||||
private readonly oauth2 = inject(OAuthService);
|
||||
private readonly httpClient = inject(HttpClient);
|
||||
private readonly accountService = inject(AccountService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly toastR = inject(ToastrService);
|
||||
|
||||
|
|
@ -56,17 +55,6 @@ export class OidcService {
|
|||
});
|
||||
}
|
||||
|
||||
this.oauth2.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
|
||||
if (event.type !== "token_refreshed" && event.type != 'token_received') return;
|
||||
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
if (!user) return; // Don't update tokens when we're not logged in. But what's going on?
|
||||
|
||||
// TODO: Do we need to refresh the SignalR connection here?
|
||||
user.oidcToken = this.token;
|
||||
});
|
||||
});
|
||||
|
||||
this.config().subscribe(oidcSetting => {
|
||||
if (!oidcSetting.authority) {
|
||||
this._loaded.set(true);
|
||||
|
|
@ -121,4 +109,8 @@ export class OidcService {
|
|||
return this.oauth2.getAccessToken();
|
||||
}
|
||||
|
||||
hasValidToken() {
|
||||
return this.oauth2.hasValidAccessToken();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue