auth: switch to Authentik OAuth for LLM API
Build & Push Portfolio Image / build-push (push) Successful in 3m28s
Build & Push Portfolio Image / build-push (push) Successful in 3m28s
- Add lib/auth.ts: OAuth client with token caching - Use client_credentials grant with portfolio-agent service account - Mount portfolio-agent-oidc secret for credentials - Remove static LLM_API_TOKEN dependency
This commit is contained in:
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Authentik OAuth client for service account authentication.
|
||||
* Uses client_credentials grant with token caching.
|
||||
*/
|
||||
|
||||
interface TokenResponse {
|
||||
access_token: string
|
||||
token_type: string
|
||||
expires_in: number
|
||||
}
|
||||
|
||||
interface CachedToken {
|
||||
token: string
|
||||
expiresAt: number
|
||||
}
|
||||
|
||||
let cachedToken: CachedToken | null = null
|
||||
|
||||
/**
|
||||
* Get a valid access token, fetching a new one if expired.
|
||||
* Tokens are cached with 60s buffer before expiry.
|
||||
*/
|
||||
export async function getAccessToken(): Promise<string> {
|
||||
const now = Date.now()
|
||||
|
||||
// Return cached token if still valid (with 60s buffer)
|
||||
if (cachedToken && cachedToken.expiresAt > now + 60000) {
|
||||
return cachedToken.token
|
||||
}
|
||||
|
||||
const clientId = process.env.AUTHENTIK_CLIENT_ID
|
||||
const clientSecret = process.env.AUTHENTIK_CLIENT_SECRET
|
||||
const tokenUrl = process.env.AUTHENTIK_TOKEN_URL || 'https://authentik.riotpiao.com/application/o/token/'
|
||||
|
||||
if (!clientId || !clientSecret) {
|
||||
throw new Error('AUTHENTIK_CLIENT_ID and AUTHENTIK_CLIENT_SECRET must be set')
|
||||
}
|
||||
|
||||
const response = await fetch(tokenUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
grant_type: 'client_credentials',
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
scope: 'openid roles',
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text()
|
||||
throw new Error(`Failed to get access token: ${response.status} - ${error}`)
|
||||
}
|
||||
|
||||
const data: TokenResponse = await response.json()
|
||||
|
||||
cachedToken = {
|
||||
token: data.access_token,
|
||||
expiresAt: now + (data.expires_in * 1000),
|
||||
}
|
||||
|
||||
return cachedToken.token
|
||||
}
|
||||
Reference in New Issue
Block a user