Clean up codebase and prepare as template
Remove unused files and code: - Delete test/debug endpoints (test.get.ts, public.get.ts, user.get.ts, debug-config backup) - Remove unused OAuth wrapper (oauth-authentik.ts) - Clean up debug console.log statements - Simplify code comments Fix TypeScript errors: - Add @types/node dependency - Create index.d.ts with User interface extension - Fix UButton color props (red→error, gray→neutral) - Add type assertions in protected.get.ts Update documentation: - Enhance README.md as template documentation - Update SETUP.md with correct API routes (/api/auth/* instead of /auth/*) - Add NUXT_OAUTH_AUTHENTIK_SERVER_URL_INTERNAL documentation - Update endpoint documentation This commit prepares the repository to be used as a template for future Nuxt 4 + Authentik OAuth projects.
This commit is contained in:
100
README.md
100
README.md
@@ -1,3 +1,99 @@
|
||||
# Seguidor de Lotes
|
||||
# Nuxt 4 + Authentik OAuth + PWA Template
|
||||
|
||||
Proyecto reiniciado desde cero.
|
||||
Template de aplicación Nuxt 4 con autenticación OAuth/OIDC usando Authentik y funcionalidad PWA (Progressive Web App).
|
||||
|
||||
## 🚀 Características
|
||||
|
||||
- ✅ **Nuxt 4** - Framework Vue.js de última generación
|
||||
- ✅ **Autenticación OAuth/OIDC** - Integración con Authentik
|
||||
- ✅ **PWA** - Funciona offline con Service Workers
|
||||
- ✅ **Docker** - Despliegue con Docker Compose
|
||||
- ✅ **Traefik** - Configurado para reverse proxy
|
||||
- ✅ **Gitea Actions** - CI/CD automático incluido
|
||||
- ✅ **TypeScript** - Tipado estático
|
||||
- ✅ **Nuxt UI** - Componentes UI modernos
|
||||
|
||||
## 📋 Requisitos Previos
|
||||
|
||||
- Docker y Docker Compose
|
||||
- Authentik corriendo (en la misma red Docker)
|
||||
- Traefik como reverse proxy
|
||||
- Gitea (opcional, para CI/CD)
|
||||
|
||||
## 🛠️ Instalación Rápida
|
||||
|
||||
1. **Clona el repositorio**
|
||||
```bash
|
||||
git clone https://tu-repo.git
|
||||
cd seguidor-lotes
|
||||
```
|
||||
|
||||
2. **Configura variables de entorno** (ver `SETUP.md` para detalles)
|
||||
|
||||
3. **Despliega**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## 📖 Documentación Completa
|
||||
|
||||
Ver [SETUP.md](./SETUP.md) para:
|
||||
- Configuración detallada de Authentik
|
||||
- Variables de entorno requeridas
|
||||
- Configuración de Gitea Actions
|
||||
- Troubleshooting
|
||||
- Personalización
|
||||
|
||||
## 🏗️ Estructura del Proyecto
|
||||
|
||||
```
|
||||
├── .gitea/
|
||||
│ └── workflows/ # CI/CD workflows
|
||||
├── nuxt4-app/
|
||||
│ ├── app/
|
||||
│ │ ├── components/ # Componentes Vue
|
||||
│ │ ├── middleware/ # Middleware de autenticación
|
||||
│ │ └── pages/ # Páginas de la aplicación
|
||||
│ ├── server/
|
||||
│ │ └── api/ # API endpoints
|
||||
│ │ ├── auth/ # Rutas de autenticación OAuth
|
||||
│ │ └── protected.get.ts # Ejemplo de API protegida
|
||||
│ ├── public/ # Assets estáticos y PWA icons
|
||||
│ ├── nuxt.config.ts # Configuración de Nuxt
|
||||
│ └── Dockerfile # Dockerfile para producción
|
||||
├── docker-compose.yml # Configuración de Docker Compose
|
||||
└── SETUP.md # Guía de configuración detallada
|
||||
```
|
||||
|
||||
## 🔐 Autenticación
|
||||
|
||||
El template implementa OAuth 2.0 / OpenID Connect con:
|
||||
- Login con Authentik
|
||||
- Protección de rutas con middleware
|
||||
- Protección de APIs con `requireUserSession`
|
||||
- Información de usuario (email, nombre, grupos)
|
||||
- Logout completo
|
||||
|
||||
## 🎨 Personalización
|
||||
|
||||
1. **Cambiar nombre de la app**: Edita `nuxt4-app/nuxt.config.ts`
|
||||
2. **Añadir páginas protegidas**: Usa `definePageMeta({ middleware: 'auth' })`
|
||||
3. **Crear APIs protegidas**: Usa `requireUserSession(event)` en tus handlers
|
||||
4. **Customizar UI**: Modifica componentes en `app/components/`
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
Problemas comunes y soluciones en [SETUP.md - Sección 10](./SETUP.md#10-troubleshooting)
|
||||
|
||||
## 📝 Licencia
|
||||
|
||||
MIT
|
||||
|
||||
## 🤝 Contribuir
|
||||
|
||||
Este es un template base. Siéntete libre de:
|
||||
1. Fork el proyecto
|
||||
2. Crear una rama para tu feature
|
||||
3. Commit tus cambios
|
||||
4. Push a la rama
|
||||
5. Abrir un Pull Request
|
||||
|
||||
28
SETUP.md
28
SETUP.md
@@ -34,7 +34,7 @@
|
||||
- **Client type**: Confidential
|
||||
- **Client ID**: (se genera automáticamente, guárdalo)
|
||||
- **Client Secret**: (se genera automáticamente, guárdalo)
|
||||
- **Redirect URIs**: `https://app.tudominio.com/auth/authentik`
|
||||
- **Redirect URIs**: `https://app.tudominio.com/api/auth/authentik`
|
||||
- **Scopes**: openid, profile, email
|
||||
- **Subject mode**: Based on the User's UUID
|
||||
- **Include claims in id_token**: ✅ Activado
|
||||
@@ -66,7 +66,8 @@ APP_DOMAIN=app.tudominio.com
|
||||
NUXT_OAUTH_AUTHENTIK_CLIENT_ID=tu-client-id-aqui
|
||||
NUXT_OAUTH_AUTHENTIK_CLIENT_SECRET=tu-client-secret-aqui
|
||||
NUXT_OAUTH_AUTHENTIK_SERVER_URL=https://auth.tudominio.com
|
||||
NUXT_OAUTH_AUTHENTIK_REDIRECT_URL=https://app.tudominio.com/auth/authentik
|
||||
NUXT_OAUTH_AUTHENTIK_REDIRECT_URL=https://app.tudominio.com/api/auth/authentik
|
||||
NUXT_OAUTH_AUTHENTIK_SERVER_URL_INTERNAL=http://nombre-servicio-authentik:9000
|
||||
|
||||
# URL pública de la app
|
||||
NUXT_PUBLIC_APP_URL=https://app.tudominio.com
|
||||
@@ -158,14 +159,8 @@ docker-compose ps
|
||||
### 7.2 Verificar APIs
|
||||
|
||||
```bash
|
||||
# API Pública (sin auth)
|
||||
curl https://app.tudominio.com/api/public
|
||||
|
||||
# API Protegida (requiere auth - debería retornar 401)
|
||||
# API Protegida (requiere auth - debería retornar 401 sin sesión)
|
||||
curl https://app.tudominio.com/api/protected
|
||||
|
||||
# Info de usuario (requiere auth)
|
||||
curl https://app.tudominio.com/api/user
|
||||
```
|
||||
|
||||
### 7.3 Verificar PWA
|
||||
@@ -183,19 +178,18 @@ curl https://app.tudominio.com/api/user
|
||||
### Páginas
|
||||
|
||||
- `/` - Página de inicio (pública)
|
||||
- `/login` - Página de login (solo para usuarios no autenticados)
|
||||
- `/login` - Página de login
|
||||
- `/dashboard` - Dashboard (requiere autenticación)
|
||||
- `/profile` - Perfil (requiere autenticación)
|
||||
|
||||
### API Endpoints
|
||||
|
||||
- `GET /api/public` - Endpoint público
|
||||
- `GET /api/protected` - Endpoint protegido (requiere auth)
|
||||
- `GET /api/user` - Información del usuario autenticado
|
||||
- `GET /api/protected` - Endpoint protegido (requiere autenticación)
|
||||
|
||||
### Auth Endpoints
|
||||
|
||||
- `GET /auth/authentik` - Inicia OAuth flow con Authentik
|
||||
- `GET /auth/logout` - Cierra sesión
|
||||
- `GET /api/auth/authentik` - Inicia OAuth flow con Authentik y maneja callback
|
||||
- `GET /api/auth/logout` - Cierra sesión
|
||||
|
||||
## 9. Desarrollo Local
|
||||
|
||||
@@ -218,8 +212,10 @@ npm run dev
|
||||
```
|
||||
|
||||
**Nota**: Para desarrollo local, necesitas:
|
||||
- Configurar Authentik con redirect URI: `http://localhost:3000/auth/authentik`
|
||||
- Configurar Authentik con redirect URI: `http://localhost:3000/api/auth/authentik`
|
||||
- O usar túnel como ngrok para https
|
||||
- Usar la URL pública de Authentik para `NUXT_OAUTH_AUTHENTIK_SERVER_URL`
|
||||
- No necesitas `NUXT_OAUTH_AUTHENTIK_SERVER_URL_INTERNAL` en desarrollo local
|
||||
|
||||
## 10. Troubleshooting
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ const logout = async () => {
|
||||
</div>
|
||||
|
||||
<UButton
|
||||
color="red"
|
||||
color="error"
|
||||
variant="soft"
|
||||
size="sm"
|
||||
@click="logout"
|
||||
|
||||
@@ -88,7 +88,7 @@ onMounted(() => {
|
||||
<UButton to="/dashboard" color="primary">
|
||||
Ir al Dashboard
|
||||
</UButton>
|
||||
<UButton to="/profile" color="gray">
|
||||
<UButton to="/profile" color="neutral">
|
||||
Ver Perfil
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
18
nuxt4-app/index.d.ts
vendored
Normal file
18
nuxt4-app/index.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// Global type declarations
|
||||
declare module '#auth-utils' {
|
||||
interface User {
|
||||
id?: string
|
||||
email?: string
|
||||
name?: string
|
||||
username?: string
|
||||
picture?: string
|
||||
groups?: string[]
|
||||
}
|
||||
|
||||
interface UserSession {
|
||||
user: User
|
||||
loggedInAt: number
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
20
nuxt4-app/package-lock.json
generated
20
nuxt4-app/package-lock.json
generated
@@ -14,6 +14,9 @@
|
||||
"typescript": "^5.9.3",
|
||||
"vue": "^3.5.22",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.7.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@adonisjs/hash": {
|
||||
@@ -5205,6 +5208,16 @@
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz",
|
||||
"integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-path": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-path/-/parse-path-7.0.3.tgz",
|
||||
@@ -13680,6 +13693,13 @@
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz",
|
||||
"integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unenv": {
|
||||
"version": "2.0.0-rc.21",
|
||||
"resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.21.tgz",
|
||||
|
||||
@@ -17,5 +17,8 @@
|
||||
"typescript": "^5.9.3",
|
||||
"vue": "^3.5.22",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.7.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,12 @@ import { withQuery } from 'ufo'
|
||||
|
||||
/**
|
||||
* OAuth Authentik Login Handler
|
||||
* Ruta: /api/auth/authentik
|
||||
*
|
||||
* Este endpoint inicia el flujo OAuth con Authentik
|
||||
* Handles OAuth flow: initial redirect and callback
|
||||
*/
|
||||
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,
|
||||
@@ -21,13 +18,6 @@ export default defineEventHandler(async (event) => {
|
||||
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 {
|
||||
@@ -70,7 +60,6 @@ export default defineEventHandler(async (event) => {
|
||||
loggedInAt: Date.now()
|
||||
})
|
||||
|
||||
// Redirigir al dashboard después del login
|
||||
return sendRedirect(event, '/')
|
||||
} catch (error: any) {
|
||||
console.error('Authentik OAuth error:', error)
|
||||
@@ -89,6 +78,5 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
)
|
||||
|
||||
console.log('Redirecting to:', authorizationUrl)
|
||||
return sendRedirect(event, authorizationUrl)
|
||||
})
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/**
|
||||
* Logout Handler
|
||||
* Ruta: /api/auth/logout
|
||||
*
|
||||
* Limpia la sesión del usuario y redirige a la página de inicio
|
||||
* Clears user session and redirects to home
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
await clearUserSession(event)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Test endpoint
|
||||
* Ruta: /api/auth/test
|
||||
*/
|
||||
export default defineEventHandler(() => {
|
||||
return { message: 'Auth subfolder works!' }
|
||||
})
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Debug Config Endpoint
|
||||
* Ruta: /api/debug-config
|
||||
*
|
||||
* TEMPORAL: Para verificar que las variables de entorno se estén cargando
|
||||
* BORRAR EN PRODUCCIÓN
|
||||
*/
|
||||
export default defineEventHandler((event) => {
|
||||
const runtimeConfig = useRuntimeConfig(event)
|
||||
|
||||
return {
|
||||
oauth: {
|
||||
authentik: {
|
||||
clientId: runtimeConfig.oauth.authentik.clientId ? 'SET (hidden)' : 'MISSING',
|
||||
clientSecret: runtimeConfig.oauth.authentik.clientSecret ? 'SET (hidden)' : 'MISSING',
|
||||
serverUrl: runtimeConfig.oauth.authentik.serverUrl || 'MISSING',
|
||||
redirectURL: runtimeConfig.oauth.authentik.redirectURL || 'MISSING',
|
||||
}
|
||||
},
|
||||
public: {
|
||||
appUrl: runtimeConfig.public.appUrl || 'MISSING'
|
||||
},
|
||||
env: {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
// Verificar directamente las env vars
|
||||
NUXT_OAUTH_AUTHENTIK_SERVER_URL: process.env.NUXT_OAUTH_AUTHENTIK_SERVER_URL || 'MISSING',
|
||||
NUXT_OAUTH_AUTHENTIK_REDIRECT_URL: process.env.NUXT_OAUTH_AUTHENTIK_REDIRECT_URL || 'MISSING',
|
||||
NUXT_PUBLIC_APP_URL: process.env.NUXT_PUBLIC_APP_URL || 'MISSING',
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,21 +1,16 @@
|
||||
/**
|
||||
* Protected API Endpoint
|
||||
* Ruta: /api/protected
|
||||
*
|
||||
* Endpoint protegido que requiere autenticación
|
||||
* Retorna datos sensibles solo para usuarios autenticados
|
||||
* Requires authentication - returns user-specific data
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
// Verificar autenticación
|
||||
const session = await requireUserSession(event)
|
||||
|
||||
return {
|
||||
message: 'Datos protegidos del usuario',
|
||||
user: session.user.username,
|
||||
user: (session.user as any).username,
|
||||
data: {
|
||||
// Aquí puedes añadir datos específicos del usuario
|
||||
lotes: [],
|
||||
permissions: session.user.groups || []
|
||||
permissions: (session.user as any).groups || []
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Public API Endpoint
|
||||
* Ruta: /api/public
|
||||
*
|
||||
* Endpoint público que no requiere autenticación
|
||||
* Útil para datos que deben estar disponibles offline
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
return {
|
||||
message: 'Este endpoint es público y funciona offline',
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Get Current User API
|
||||
* Ruta: /api/user
|
||||
*
|
||||
* Endpoint protegido que devuelve la información del usuario autenticado
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
// Obtener sesión del usuario (requiere autenticación)
|
||||
const session = await requireUserSession(event)
|
||||
|
||||
return {
|
||||
user: session.user,
|
||||
loggedInAt: session.loggedInAt
|
||||
}
|
||||
})
|
||||
@@ -1,131 +0,0 @@
|
||||
/**
|
||||
* 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)
|
||||
|
||||
// Debug: Log configuration
|
||||
console.log('OAuth Authentik Config:', {
|
||||
clientId: runtimeConfig.oauth.authentik.clientId ? '***' : 'MISSING',
|
||||
clientSecret: runtimeConfig.oauth.authentik.clientSecret ? '***' : 'MISSING',
|
||||
serverUrl: runtimeConfig.oauth.authentik.serverUrl,
|
||||
redirectURL: runtimeConfig.oauth.authentik.redirectURL,
|
||||
})
|
||||
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user