import { useCallback, useEffect, useState } from 'react'
import { Loading, ErrorState } from '../../../components/PageState.jsx'
import ProviderIcon from '../../../components/ProviderIcon.jsx'
import { api } from '../../../api/client.js'
// Link/unlink external SSO identities to this account. Linking redirects through
// the provider's OAuth flow (/auth/sso/:id/link) and returns here with ?linked
// or ?link_error. Only providers that are enabled + valid can be linked.
function LinkedAccounts() {
const [linked, setLinked] = useState(null)
const [available, setAvailable] = useState([])
const [error, setError] = useState('')
const banner = (() => {
const q = new URLSearchParams(window.location.search)
if (q.get('linked')) return { ok: true, text: 'Account linked.' }
if (q.get('link_error') === 'in_use') return { ok: false, text: 'That external account is already linked to another user.' }
if (q.get('link_error')) return { ok: false, text: 'Could not link that account. Please try again.' }
return null
})()
const load = useCallback(async () => {
try {
const [ids, avail] = await Promise.all([
api.admin.linkedIdentities(),
api.authProviders().catch(() => []),
])
setLinked(ids)
setAvailable(Array.isArray(avail) ? avail : [])
} catch {
setError('Could not load linked accounts.')
}
}, [])
useEffect(() => {
load()
}, [load])
const nameFor = (id) => available.find((p) => p.id === id)?.name || id.charAt(0).toUpperCase() + id.slice(1)
const iconFor = (id) => (id === 'google' || id === 'discord' ? id : 'oidc')
async function unlink(provider) {
if (!window.confirm(`Unlink ${nameFor(provider)} from your account?`)) return
try {
await api.admin.unlinkIdentity(provider)
await load()
} catch (err) {
setError(err.message || 'Could not unlink.')
}
}
if (error) return
Link a Google, Discord, or other SSO account so you can sign in with it. SSO can only sign in to an account it is linked to — linking here is what grants that access.
{banner && ({banner.text}
)} {linked.length > 0 && (No SSO providers are enabled. Configure them under Authentication.
)}Add a time-based one-time code (TOTP) from an authenticator app as a second step at login. Optional, and only affects your own account.
1. Scan this QR code with your authenticator app, then enter the current 6-digit code to confirm.
Enter a current code from your authenticator to turn two-factor off.
{msg}
} {error &&{error}
}