Files
seguidorDeLotes/nuxt4-app/server/api/auth/authentik.get.ts
josedario87 db4a79e617
All checks were successful
build-and-deploy / build (push) Successful in 7s
build-and-deploy / deploy (push) Successful in 3s
Add internal Authentik URL for server-to-server communication
This fixes ETIMEDOUT errors when exchanging OAuth tokens. The container
now uses the Docker internal service name (authentiknucleo-server-1:9000)
for server-to-server API calls while keeping the public URL for browser
redirects.

Changes:
- Add NUXT_OAUTH_AUTHENTIK_SERVER_URL_INTERNAL env var
- Use internal URL for token exchange and userinfo endpoints
- Update docker-compose.yml and Gitea workflow
2025-10-11 18:47:41 -06:00

95 lines
3.0 KiB
TypeScript

import { getQuery } from 'h3'
import { withQuery } from 'ufo'
/**
* OAuth Authentik Login Handler
* Ruta: /api/auth/authentik
*
* Este endpoint inicia el flujo OAuth con Authentik
*/
export default defineEventHandler(async (event) => {
const runtimeConfig = useRuntimeConfig(event)
const query = getQuery(event)
// Configuración de Authentik
const config = {
clientId: runtimeConfig.oauth.authentik.clientId,
clientSecret: runtimeConfig.oauth.authentik.clientSecret,
serverUrl: runtimeConfig.oauth.authentik.serverUrl,
serverUrlInternal: runtimeConfig.oauth.authentik.serverUrlInternal || runtimeConfig.oauth.authentik.serverUrl,
redirectURL: runtimeConfig.oauth.authentik.redirectURL,
scope: ['openid', 'profile', 'email'],
}
console.log('OAuth Authentik - Iniciando flujo:', {
serverUrl: config.serverUrl,
serverUrlInternal: config.serverUrlInternal,
redirectURL: config.redirectURL,
hasCode: !!query.code
})
// Handle OAuth callback
if (query.code) {
try {
// Exchange code for tokens (usar URL interna para comunicación servidor-a-servidor)
const tokenUrl = `${config.serverUrlInternal}/application/o/token/`
const tokenResponse = await $fetch(tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: config.clientId,
client_secret: config.clientSecret,
code: query.code as string,
redirect_uri: config.redirectURL,
}),
})
const tokens = tokenResponse as any
// Get user info (usar URL interna para comunicación servidor-a-servidor)
const userInfoUrl = `${config.serverUrlInternal}/application/o/userinfo/`
const user = await $fetch(userInfoUrl, {
headers: {
Authorization: `Bearer ${tokens.access_token}`,
},
})
// Guardar información del usuario en la sesión
await setUserSession(event, {
user: {
id: (user as any).sub,
email: (user as any).email,
name: (user as any).name || (user as any).preferred_username,
username: (user as any).preferred_username,
picture: (user as any).picture,
groups: (user as any).groups || []
},
loggedInAt: Date.now()
})
// Redirigir al dashboard después del login
return sendRedirect(event, '/')
} catch (error: any) {
console.error('Authentik OAuth error:', error)
return sendRedirect(event, '/?error=auth_failed')
}
}
// Initial redirect to Authentik
const authorizationUrl = withQuery(
`${config.serverUrl}/application/o/authorize/`,
{
client_id: config.clientId,
redirect_uri: config.redirectURL,
response_type: 'code',
scope: config.scope.join(' '),
}
)
console.log('Redirecting to:', authorizationUrl)
return sendRedirect(event, authorizationUrl)
})