feat: dynamic auth providers + Auth0 support

- Add /api/1/auth/providers endpoint that returns only configured providers
- Frontend fetches available providers dynamically instead of hardcoding
- Only providers with credentials set (KEY + SECRET) appear as login options
- Add Auth0 as a supported authentication provider
- Add python-jose[cryptography] dependency for Auth0 JWT verification
- Show helpful message when no providers are configured

This makes the login page modular - administrators only see buttons for
providers they've actually configured, avoiding confusion from dead buttons.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Brian Olson
2025-12-31 15:57:48 -05:00
parent 16847cea93
commit 3aa321aff8
8 changed files with 135 additions and 12 deletions

View File

@@ -1,9 +1,12 @@
.login-view(*ngIf='ready')
.buttons
.buttons(*ngIf='providers.length')
a.btn(
*ngFor='let provider of providers',
[class]='provider.cls',
href='{{commonService.backendURL}}/api/1/auth/social/login/{{provider.id}}'
)
fa-icon([icon]='provider.icon', [fixedWidth]='true')
fa-icon([icon]='provider.faIcon', [fixedWidth]='true')
span Log in with {{provider.name}}
.no-providers(*ngIf='!providers.length')
p No authentication providers configured.
p.text-muted Contact your administrator.

View File

@@ -23,3 +23,12 @@
margin: auto;
}
}
.no-providers {
text-align: center;
color: #6c757d;
p {
margin: 0.5rem 0;
}
}

View File

@@ -1,7 +1,25 @@
import { Component } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { LoginService, CommonService } from 'src/common'
import { faGithub, faGitlab, faGoogle, faMicrosoft } from '@fortawesome/free-brands-svg-icons'
import { faGithub, faGitlab, faGoogle, faMicrosoft, IconDefinition } from '@fortawesome/free-brands-svg-icons'
import { faKey } from '@fortawesome/free-solid-svg-icons'
interface Provider {
id: string
name: string
icon: string
cls: string
}
// Map icon names from backend to FontAwesome icons
const iconMap: Record<string, IconDefinition> = {
github: faGithub,
gitlab: faGitlab,
google: faGoogle,
microsoft: faMicrosoft,
key: faKey, // Used for Auth0 and other providers without brand icons
}
@Component({
selector: 'login',
@@ -11,20 +29,27 @@ import { faGithub, faGitlab, faGoogle, faMicrosoft } from '@fortawesome/free-bra
export class LoginComponent {
loggedIn: any
ready = false
providers = [
{ name: 'GitHub', icon: faGithub, cls: 'btn-primary', id: 'github' },
{ name: 'GitLab', icon: faGitlab, cls: 'btn-warning', id: 'gitlab' },
{ name: 'Google', icon: faGoogle, cls: 'btn-secondary', id: 'google-oauth2' },
{ name: 'Microsoft', icon: faMicrosoft, cls: 'btn-light', id: 'microsoft-graph' },
]
providers: Array<Provider & { faIcon: IconDefinition }> = []
constructor (
private http: HttpClient,
private loginService: LoginService,
public commonService: CommonService,
) { }
async ngOnInit () {
// Fetch available providers from backend
try {
const providers = await this.http.get<Provider[]>('/api/1/auth/providers').toPromise()
this.providers = (providers || []).map(p => ({
...p,
faIcon: iconMap[p.icon] || faKey,
}))
} catch {
// Fallback to empty list if API fails
this.providers = []
}
await this.loginService.ready$.toPromise()
this.loggedIn = !!this.loginService.user
this.ready = true