Tweak refresh logic for OIDC

This commit is contained in:
Amelia 2025-07-09 12:42:07 +02:00
parent 061fb222e6
commit e5c716d234
2 changed files with 30 additions and 26 deletions

View file

@ -4,13 +4,20 @@ import {from} from "rxjs";
import {HttpClient} from "@angular/common/http";
import {environment} from "../../environments/environment";
import {OidcPublicConfig} from "../admin/_models/oidc-config";
import {AccountService} from "./account.service";
import {takeUntilDestroyed, toObservable} from "@angular/core/rxjs-interop";
import {take} from "rxjs/operators";
import {ToastrService} from "ngx-toastr";
import {translate} from "@jsverse/transloco";
import {APP_BASE_HREF} from "@angular/common";
import {MessageHubService} from "./message-hub.service";
/**
* Enum mirror of angular-oauth2-oidc events which are used in Kavita
*/
export enum OidcEvents {
/**
* Fired on token refresh, and when the first token is recieved
*/
TokenRefreshed = "token_refreshed"
}
@Injectable({
providedIn: 'root'
@ -19,14 +26,14 @@ 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);
private readonly messageHub = inject(MessageHubService);
protected readonly baseUrl = inject(APP_BASE_HREF);
apiBaseUrl = environment.apiUrl;
public events$ = this.oauth2.events;
/**
* True when the OIDC discovery document has been loaded, and login tried. Or no OIDC has been set up
*/
@ -47,29 +54,19 @@ export class OidcService {
public readonly settings = this._settings.asReadonly();
constructor() {
this.oauth2.setStorage(localStorage);
// log events in dev
if (!environment.production) {
this.oauth2.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(event => {
if (event instanceof OAuthErrorEvent) {
console.error('OAuthErrorEvent Object:', event);
console.error('OAuthErrorEvent:', event);
} else {
console.debug('OAuthEvent Object:', event);
console.debug('OAuthEvent:', event);
}
});
}
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?
user.oidcToken = this.token;
this.messageHub.stopHubConnection();
this.messageHub.createHubConnection(user);
});
});
this.getPublicOidcConfig().subscribe(oidcSetting => {
this._settings.set(oidcSetting);
@ -96,6 +93,10 @@ export class OidcService {
from(this.oauth2.loadDiscoveryDocumentAndTryLogin()).subscribe({
next: _ => {
this._loaded.set(true);
if (!this.oauth2.hasValidAccessToken() && this.oauth2.getRefreshToken()) {
this.oauth2.refreshToken().catch(err => console.error("failed to refresh token on startup", err));
}
},
error: error => {
console.log(error);

View file

@ -25,7 +25,7 @@ import {TranslocoService} from "@jsverse/transloco";
import {VersionService} from "./_services/version.service";
import {LicenseService} from "./_services/license.service";
import {LocalizationService} from "./_services/localization.service";
import {OidcService} from "./_services/oidc.service";
import {OidcEvents, OidcService} from "./_services/oidc.service";
@Component({
selector: 'app-root',
@ -52,7 +52,7 @@ export class AppComponent implements OnInit {
private readonly document = inject(DOCUMENT);
private readonly translocoService = inject(TranslocoService);
private readonly versionService = inject(VersionService); // Needs to be injected to run background job
private readonly oidcService = inject(OidcService); // Needed to auto login
private readonly oidcService = inject(OidcService);
private readonly licenseService = inject(LicenseService);
private readonly localizationService = inject(LocalizationService);
@ -100,11 +100,15 @@ export class AppComponent implements OnInit {
this.localizationService.getLocales().subscribe(); // This will cache the localizations on startup
// Login automatically when a token is available
effect(() => {
const inUse = this.oidcService.inUse();
// Update token, or login when one becomes available
this.oidcService.events$.subscribe(event => {
if (event.type !== OidcEvents.TokenRefreshed) return;
const user = this.accountService.currentUserSignal();
if (!inUse || !this.oidcService.token || user) return;
if (user) {
user.oidcToken = this.oidcService.token;
return;
}
this.accountService.loginByToken(this.oidcService.token).subscribe({
next: () => {
@ -115,7 +119,6 @@ export class AppComponent implements OnInit {
}
});
});
}
@HostListener('window:resize', ['$event'])