Files
seguidorDeLotes/nuxt4-app/server/utils/oauth-authentik.ts
josedario87 7612487d3f
All checks were successful
build-and-deploy / build (push) Successful in 2m7s
build-and-deploy / deploy (push) Successful in 3s
Add Nuxt 4 app with OAuth/OIDC authentication and PWA support
- Integrated Authentik OAuth/OIDC authentication
- Added PWA functionality with offline support
- Created protected and public API endpoints
- Configured Docker deployment with Traefik
- Added Gitea Actions CI/CD workflow
- Included comprehensive setup documentation
2025-10-11 17:12:05 -06:00

124 lines
3.5 KiB
TypeScript

/**
* Custom OAuth Provider for Authentik
*
* Este archivo extiende nuxt-auth-utils para soportar Authentik como provider OAuth
*/
import type { H3Event } from 'h3'
import { eventHandler, getQuery, sendRedirect } from 'h3'
import { withQuery } from 'ufo'
import { defu } from 'defu'
import { useRuntimeConfig } from '#imports'
export interface OAuthAuthentikConfig {
/**
* Authentik OAuth Client ID
* @default process.env.NUXT_OAUTH_AUTHENTIK_CLIENT_ID
*/
clientId?: string
/**
* Authentik OAuth Client Secret
* @default process.env.NUXT_OAUTH_AUTHENTIK_CLIENT_SECRET
*/
clientSecret?: string
/**
* Authentik Server URL
* @default process.env.NUXT_OAUTH_AUTHENTIK_SERVER_URL
*/
serverUrl?: string
/**
* Redirect URL
* @default process.env.NUXT_OAUTH_AUTHENTIK_REDIRECT_URL
*/
redirectURL?: string
/**
* Require email from user, adds the ['email'] scope if not present
* @default false
*/
emailRequired?: boolean
/**
* Authentik OAuth Scope
* @default ['openid', 'profile', 'email']
*/
scope?: string[]
}
export function oauthAuthentikEventHandler({
config,
onSuccess,
onError,
}: {
config?: OAuthAuthentikConfig
onSuccess: (event: H3Event, result: { user: any; tokens: any }) => Promise<void> | void
onError?: (event: H3Event, error: any) => Promise<void> | void
}) {
return eventHandler(async (event: H3Event) => {
const runtimeConfig = useRuntimeConfig(event)
// Merge config with defaults
const authentikConfig = defu(config, {
clientId: runtimeConfig.oauth.authentik.clientId,
clientSecret: runtimeConfig.oauth.authentik.clientSecret,
serverUrl: runtimeConfig.oauth.authentik.serverUrl,
redirectURL: runtimeConfig.oauth.authentik.redirectURL,
scope: ['openid', 'profile', 'email'],
emailRequired: false
}) as Required<OAuthAuthentikConfig>
const query = getQuery(event)
// Handle callback
if (query.code) {
try {
// Exchange code for tokens
const tokenUrl = `${authentikConfig.serverUrl}/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: authentikConfig.clientId,
client_secret: authentikConfig.clientSecret,
code: query.code as string,
redirect_uri: authentikConfig.redirectURL,
}),
})
const tokens = tokenResponse as any
// Get user info
const userInfoUrl = `${authentikConfig.serverUrl}/application/o/userinfo/`
const user = await $fetch(userInfoUrl, {
headers: {
Authorization: `Bearer ${tokens.access_token}`,
},
})
return onSuccess(event, { user, tokens })
} catch (error: any) {
if (onError) return onError(event, error)
throw error
}
}
// Initial redirect to Authentik
const authorizationUrl = withQuery(
`${authentikConfig.serverUrl}/application/o/authorize/`,
{
client_id: authentikConfig.clientId,
redirect_uri: authentikConfig.redirectURL,
response_type: 'code',
scope: authentikConfig.scope.join(' '),
}
)
return sendRedirect(event, authorizationUrl)
})
}
// Export for use in defineOAuthAuthentikEventHandler
export const oauth = {
authentikEventHandler: oauthAuthentikEventHandler
}