import { Component, inject, signal, OnInit } from '@angular/core'; import { AdminAuthService, type IAuthProvider, type IPlatformSettings, type TAuthProviderStatus, } from '../../../core/services/admin-auth.service'; import { ToastService } from '../../../core/services/toast.service'; @Component({ selector: 'app-auth-providers', standalone: true, template: `

Authentication Providers

Configure OAuth and LDAP authentication

Platform Settings
@if (settings()) {

Local Authentication

Allow email/password login

User Registration

Allow new account creation

Session Duration

{{ formatDuration(settings()!.auth.sessionDurationMinutes) }}

} @else {
}
@if (loading()) {
} @else if (providers().length === 0) {

No providers configured

Add an OAuth or LDAP provider to enable single sign-on

} @else {
@for (provider of providers(); track provider.id) {
@if (provider.type === 'oidc') { } @else { }

{{ provider.displayName }}

{{ provider.status }} @if (settings()?.auth?.defaultProviderId === provider.id) { Default }

{{ provider.name }} ยท {{ provider.type.toUpperCase() }}

@if (provider.type === 'oidc' && provider.oauthConfig) {

{{ provider.oauthConfig.issuer }}

} @if (provider.type === 'ldap' && provider.ldapConfig) {

{{ provider.ldapConfig.serverUrl }}

} @if (provider.lastTestedAt) {
@if (provider.lastTestResult === 'success') { Connection OK } @else { Connection Failed } tested {{ formatDate(provider.lastTestedAt) }}
}
}
} @if (showCreateModal()) { } @if (providerToDelete()) { } @if (showSettingsModal()) { }
`, }) export class AuthProvidersComponent implements OnInit { private adminAuthService = inject(AdminAuthService); private toastService = inject(ToastService); providers = signal([]); settings = signal(null); loading = signal(true); testing = signal(null); deleting = signal(false); savingSettings = signal(false); showCreateModal = signal(false); showSettingsModal = signal(false); providerToDelete = signal(null); selectedProviderForEdit = signal(null); editingSettings = { sessionDurationMinutes: 10080, defaultProviderId: undefined as string | undefined, }; ngOnInit(): void { this.loadData(); } private async loadData(): Promise { this.loading.set(true); try { const [providersRes, settingsRes] = await Promise.all([ this.adminAuthService.listProviders().toPromise(), this.adminAuthService.getSettings().toPromise(), ]); this.providers.set(providersRes?.providers || []); if (settingsRes) { this.settings.set(settingsRes); this.editingSettings = { sessionDurationMinutes: settingsRes.auth.sessionDurationMinutes, defaultProviderId: settingsRes.auth.defaultProviderId, }; } } catch (error) { this.toastService.error('Failed to load authentication settings'); } finally { this.loading.set(false); } } createProvider(type: 'oidc' | 'ldap'): void { this.showCreateModal.set(false); // Navigate to provider form window.location.href = `/admin/auth/providers/new?type=${type}`; } editProvider(provider: IAuthProvider): void { window.location.href = `/admin/auth/providers/${provider.id}`; } async testProvider(provider: IAuthProvider): Promise { this.testing.set(provider.id); try { const result = await this.adminAuthService.testProvider(provider.id).toPromise(); if (result?.success) { this.toastService.success(`Connection successful (${result.latencyMs}ms)`); } else { this.toastService.error(result?.error || 'Connection failed'); } // Reload to get updated test results await this.loadData(); } catch (error) { this.toastService.error('Failed to test provider'); } finally { this.testing.set(null); } } confirmDelete(provider: IAuthProvider): void { this.providerToDelete.set(provider); } async deleteProvider(): Promise { const provider = this.providerToDelete(); if (!provider) return; this.deleting.set(true); try { await this.adminAuthService.deleteProvider(provider.id).toPromise(); this.toastService.success('Provider deleted'); this.providerToDelete.set(null); await this.loadData(); } catch (error) { this.toastService.error('Failed to delete provider'); } finally { this.deleting.set(false); } } async toggleLocalAuth(): Promise { const current = this.settings(); if (!current) return; try { await this.adminAuthService.updateSettings({ auth: { localAuthEnabled: !current.auth.localAuthEnabled }, }).toPromise(); this.toastService.success('Settings updated'); await this.loadData(); } catch (error) { this.toastService.error('Failed to update settings'); } } async toggleRegistration(): Promise { const current = this.settings(); if (!current) return; try { await this.adminAuthService.updateSettings({ auth: { allowUserRegistration: !current.auth.allowUserRegistration }, }).toPromise(); this.toastService.success('Settings updated'); await this.loadData(); } catch (error) { this.toastService.error('Failed to update settings'); } } async saveSettings(): Promise { this.savingSettings.set(true); try { await this.adminAuthService.updateSettings({ auth: { sessionDurationMinutes: this.editingSettings.sessionDurationMinutes, defaultProviderId: this.editingSettings.defaultProviderId, }, }).toPromise(); this.toastService.success('Settings saved'); this.showSettingsModal.set(false); await this.loadData(); } catch (error) { this.toastService.error('Failed to save settings'); } finally { this.savingSettings.set(false); } } getProviderIconClass(type: string): string { return type === 'oidc' ? 'bg-primary/10 text-primary' : 'bg-accent/10 text-accent'; } getStatusBadgeClass(status: TAuthProviderStatus): string { switch (status) { case 'active': return 'badge-accent'; case 'testing': return 'badge-warning'; case 'disabled': return 'badge-secondary'; default: return 'badge-secondary'; } } formatDuration(minutes: number): string { if (minutes < 60) return `${minutes} minutes`; if (minutes < 1440) return `${Math.round(minutes / 60)} hours`; return `${Math.round(minutes / 1440)} days`; } formatDate(dateStr: string): string { const date = new Date(dateStr); const now = new Date(); const diff = now.getTime() - date.getTime(); const minutes = Math.floor(diff / 60000); if (minutes < 1) return 'just now'; if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}h ago`; const days = Math.floor(hours / 24); return `${days}d ago`; } }