Bits of cleanup & support custom base urls

This commit is contained in:
Amelia 2025-07-01 21:41:19 +02:00
parent 08914f7546
commit 9979220641
No known key found for this signature in database
GPG key ID: D6D0ECE365407EAA
3 changed files with 52 additions and 26 deletions

View file

@ -1,16 +1,26 @@
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';
import {Injectable} from "@angular/core";
import {Observable, take} from "rxjs";
import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';
import {inject, Injectable} from "@angular/core";
import {catchError, filter, Observable, of, take, timeout} from "rxjs";
import {OidcService} from "../_services/oidc.service";
import {ToastrService} from "ngx-toastr";
@Injectable({
providedIn: 'root'
})
export class OidcResolver implements Resolve<any> {
constructor(private oidcService: OidcService) {}
private oidcService = inject(OidcService);
private toastR = inject(ToastrService);
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
return this.oidcService.loaded$.pipe(take(1));
return this.oidcService.loaded$.pipe(
filter(value => value),
take(1),
timeout(5000),
catchError(err => {
console.log(err);
this.toastR.error("oidc.timeout");
return of(true);
}));
}
}

View file

@ -5,10 +5,11 @@ 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} from "@angular/core/rxjs-interop";
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";
@Injectable({
providedIn: 'root'
@ -21,19 +22,32 @@ export class OidcService {
private readonly destroyRef = inject(DestroyRef);
private readonly toastR = inject(ToastrService);
baseUrl = environment.apiUrl;
protected readonly baseUrl = inject(APP_BASE_HREF);
apiBaseUrl = environment.apiUrl;
private readonly loaded = new BehaviorSubject<boolean>(false);
public readonly loaded$: Observable<boolean> = this.loaded.asObservable();
/**
* True when the OIDC discovery document has been loaded, and login tried. Or no OIDC has been set up
*/
private readonly _loaded = signal(false);
public readonly loaded = this._loaded.asReadonly();
public readonly loaded$ = toObservable(this.loaded);
/**
* OIDC discovery document has been loaded, and login tried and OIDC has been set up
*/
private readonly _ready = signal(false);
public readonly ready = this._ready.asReadonly();
/**
* Public OIDC settings
*/
private readonly _settings = signal<OidcPublicConfig | undefined>(undefined);
public readonly settings = this._settings.asReadonly();
constructor() {
// log events in dev
if (!environment.production) {
this.oauth2.events.subscribe(event => {
this.oauth2.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(event => {
if (event instanceof OAuthErrorEvent) {
console.error('OAuthErrorEvent Object:', event);
} else {
@ -42,9 +56,20 @@ 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.next(true);
this._loaded.set(true);
return
}
@ -53,30 +78,20 @@ export class OidcService {
clientId: oidcSetting.clientId,
// Require https in production unless localhost
requireHttps: environment.production ? 'remoteOnly' : false,
redirectUri: window.location.origin + "/oidc/callback",
postLogoutRedirectUri: window.location.origin + "/login",
redirectUri: window.location.origin + this.baseUrl + "oidc/callback",
postLogoutRedirectUri: window.location.origin + this.baseUrl + "login",
showDebugInformation: !environment.production,
responseType: 'code',
scope: "openid profile email roles offline_access",
// Not all OIDC providers follow this nicely
strictDiscoveryDocumentValidation: false,
});
this._settings.set(oidcSetting);
this.oauth2.setupAutomaticSilentRefresh();
this.oauth2.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
if (event.type !== "token_refreshed") 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;
});
});
from(this.oauth2.loadDiscoveryDocumentAndTryLogin()).subscribe({
next: _ => {
this.loaded.next(true);
this._loaded.set(true);
this._ready.set(true);
},
error: error => {
@ -99,7 +114,7 @@ export class OidcService {
}
config() {
return this.httpClient.get<OidcPublicConfig>(this.baseUrl + "oidc/config");
return this.httpClient.get<OidcPublicConfig>(this.apiBaseUrl + "oidc/config");
}
get token() {

View file

@ -13,6 +13,7 @@
"title": "OpenID Connect Callback",
"login": "Back to login screen",
"error-loading-info": "An error occurred loading OpenID Connect info, contact your administrator",
"timeout": "OIDC resolution has timed out or an error has occurred",
"settings": {
"save": "{{common.save}}",
"notice": "Notice",