87 lines
No EOL
2.4 KiB
TypeScript
87 lines
No EOL
2.4 KiB
TypeScript
import { query } from '../../utils/db'
|
|
|
|
function parseJwt(token: string) {
|
|
try {
|
|
const base64Payload = token.split('.')[1]
|
|
const payload = Buffer.from(base64Payload, 'base64').toString('utf8')
|
|
return JSON.parse(payload)
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const config = useRuntimeConfig(event)
|
|
const body = await readBody(event)
|
|
|
|
if (!body?.code || !body?.redirectUri) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: 'Missing code or redirectUri'
|
|
})
|
|
}
|
|
|
|
const tokenUrl = `${config.public.keycloakUrl}/realms/${config.public.keycloakRealm}/protocol/openid-connect/token`
|
|
|
|
const params = new URLSearchParams({
|
|
grant_type: 'authorization_code',
|
|
client_id: config.public.keycloakClientId,
|
|
code: body.code,
|
|
redirect_uri: body.redirectUri
|
|
})
|
|
|
|
if (config.keycloakClientSecret) {
|
|
params.append('client_secret', config.keycloakClientSecret)
|
|
}
|
|
|
|
try {
|
|
const response = await $fetch<any>(tokenUrl, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: params.toString()
|
|
})
|
|
|
|
const decodedToken = parseJwt(response.access_token)
|
|
const realmRoles: string[] = decodedToken?.realm_access?.roles || []
|
|
|
|
if (!realmRoles.includes('portal-access')) {
|
|
throw createError({
|
|
statusCode: 403,
|
|
statusMessage: 'Access Denied'
|
|
})
|
|
}
|
|
|
|
const keycloakId = decodedToken?.sub
|
|
const email = decodedToken?.email
|
|
|
|
let isFirstLogin = false
|
|
|
|
if (keycloakId) {
|
|
try {
|
|
const dbResult = await query(
|
|
`INSERT INTO users (keycloak_id, email)
|
|
VALUES ($1, $2)
|
|
ON CONFLICT (keycloak_id) DO UPDATE SET updated_at = CURRENT_TIMESTAMP
|
|
RETURNING first_login_completed`,
|
|
[keycloakId, email]
|
|
)
|
|
|
|
isFirstLogin = !dbResult.rows[0]?.first_login_completed
|
|
} catch (dbErr) {
|
|
console.error('[DB ERROR in token.post.ts]:', dbErr)
|
|
isFirstLogin = false
|
|
}
|
|
}
|
|
|
|
return {
|
|
...response,
|
|
isFirstLogin
|
|
}
|
|
} catch (error: any) {
|
|
console.error('[AUTH ERROR in token.post.ts]:', error?.data || error)
|
|
throw createError({
|
|
statusCode: error.statusCode || error.response?.status || 401,
|
|
statusMessage: error.data?.message || error.response?._data?.error_description || 'Authentication failed'
|
|
})
|
|
}
|
|
}) |