chore(operator): O.9 verification + roll follow-ups into NEXT-STEPS
- Add _verify-token.get.ts to both operator and portal — decodes the access token stored in the nuxt-oidc-auth session and echoes iss/aud/ sub/groups. Used to confirm operator tokens carry aud=dezky-operator and portal tokens carry aud=dezky-portal. Listed in NEXT-STEPS.md as throwaway, to be removed when proper verification surfaces exist. - OPERATOR-PLAN.md O.9 marked done with the actual claims captured + the Mongo-side verification of attach + suspend flows. - NEXT-STEPS.md: replaced the "Operator portal — out-of-band track" section with a "shipped + follow-ups" version. The 9-item follow-up list (impersonation, audit, flags, incidents, support, partner portal, env switcher, on-call, workspace impersonation) is now the authoritative roadmap, not buried inside OPERATOR-PLAN.md.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
// Throwaway verification endpoint for O.9: decodes the access token currently
|
||||
// stored in the operator's nuxt-oidc-auth session and returns the claims we
|
||||
// care about (iss, aud, sub, exp, groups). NEVER returns the raw token. Safe
|
||||
// to leave deployed since it requires a valid operator session and only
|
||||
// echoes claims the user can already see in their JWT.
|
||||
|
||||
import { getUserSession } from 'nuxt-oidc-auth/runtime/server/utils/session.js'
|
||||
|
||||
function decodeJwtClaims(token: string): Record<string, unknown> {
|
||||
const parts = token.split('.')
|
||||
if (parts.length < 2) throw new Error('Not a JWT')
|
||||
const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/')
|
||||
const padded = payload + '='.repeat((4 - (payload.length % 4)) % 4)
|
||||
return JSON.parse(Buffer.from(padded, 'base64').toString('utf8'))
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = await getUserSession(event).catch(() => null)
|
||||
const accessToken = (session as { accessToken?: string } | null)?.accessToken
|
||||
if (!accessToken) throw createError({ statusCode: 401, statusMessage: 'No session' })
|
||||
|
||||
const claims = decodeJwtClaims(accessToken)
|
||||
return {
|
||||
iss: claims.iss,
|
||||
aud: claims.aud,
|
||||
sub: claims.sub,
|
||||
email: claims.email,
|
||||
groups: claims.groups,
|
||||
exp: claims.exp,
|
||||
iat: claims.iat,
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
// Throwaway verification endpoint mirroring the operator one. Decodes the
|
||||
// portal access token from the nuxt-oidc-auth session and echoes the claims
|
||||
// that matter (iss/aud/sub/groups/exp). Useful for confirming that signing
|
||||
// in here yields aud=dezky-portal, distinct from the operator's dezky-operator.
|
||||
|
||||
import { getUserSession } from 'nuxt-oidc-auth/runtime/server/utils/session.js'
|
||||
|
||||
function decodeJwtClaims(token: string): Record<string, unknown> {
|
||||
const parts = token.split('.')
|
||||
if (parts.length < 2) throw new Error('Not a JWT')
|
||||
const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/')
|
||||
const padded = payload + '='.repeat((4 - (payload.length % 4)) % 4)
|
||||
return JSON.parse(Buffer.from(padded, 'base64').toString('utf8'))
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = await getUserSession(event).catch(() => null)
|
||||
const accessToken = (session as { accessToken?: string } | null)?.accessToken
|
||||
if (!accessToken) throw createError({ statusCode: 401, statusMessage: 'No session' })
|
||||
|
||||
const claims = decodeJwtClaims(accessToken)
|
||||
return {
|
||||
iss: claims.iss,
|
||||
aud: claims.aud,
|
||||
sub: claims.sub,
|
||||
email: claims.email,
|
||||
groups: claims.groups,
|
||||
exp: claims.exp,
|
||||
iat: claims.iat,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user