@adonis-agora/authkit-server 0.55.0 → 0.55.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -44,8 +44,15 @@ export declare function startImpersonation(ctx: HttpContext, params: StartImpers
|
|
|
44
44
|
*/
|
|
45
45
|
export declare function impersonationState(ctx: HttpContext): ImpersonationState;
|
|
46
46
|
/**
|
|
47
|
-
* Encerra a impersonation: restaura `account_user_id = impersonator`,
|
|
48
|
-
*
|
|
49
|
-
*
|
|
47
|
+
* Encerra a impersonation: restaura `account_user_id = impersonator`, remove a
|
|
48
|
+
* key de impersonation e regenera a sessão (anti-fixation). No-op quando não há
|
|
49
|
+
* impersonation ativa.
|
|
50
|
+
*
|
|
51
|
+
* O `admin_access_token` (a credencial do ADMIN usada como `subject_token` do
|
|
52
|
+
* token-exchange) é PRESERVADO: ele é do admin — não do alvo — e o admin segue
|
|
53
|
+
* logado após o stop. Removê-lo forçava um relogin a cada vez que o admin
|
|
54
|
+
* personificava, saía e tentava personificar de novo (No admin access token in
|
|
55
|
+
* session). O token segue curto (TTL do access token do RP) e o logout do app
|
|
56
|
+
* (`ctx.session.clear()` em `AuthRpController.logout`) continua limpando tudo.
|
|
50
57
|
*/
|
|
51
58
|
export declare function stopImpersonation(ctx: HttpContext): Promise<void>;
|
|
@@ -121,9 +121,16 @@ export function impersonationState(ctx) {
|
|
|
121
121
|
return { active: true, targetId, impersonatorId };
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
|
-
* Encerra a impersonation: restaura `account_user_id = impersonator`,
|
|
125
|
-
*
|
|
126
|
-
*
|
|
124
|
+
* Encerra a impersonation: restaura `account_user_id = impersonator`, remove a
|
|
125
|
+
* key de impersonation e regenera a sessão (anti-fixation). No-op quando não há
|
|
126
|
+
* impersonation ativa.
|
|
127
|
+
*
|
|
128
|
+
* O `admin_access_token` (a credencial do ADMIN usada como `subject_token` do
|
|
129
|
+
* token-exchange) é PRESERVADO: ele é do admin — não do alvo — e o admin segue
|
|
130
|
+
* logado após o stop. Removê-lo forçava um relogin a cada vez que o admin
|
|
131
|
+
* personificava, saía e tentava personificar de novo (No admin access token in
|
|
132
|
+
* session). O token segue curto (TTL do access token do RP) e o logout do app
|
|
133
|
+
* (`ctx.session.clear()` em `AuthRpController.logout`) continua limpando tudo.
|
|
127
134
|
*/
|
|
128
135
|
export async function stopImpersonation(ctx) {
|
|
129
136
|
const impersonatorId = ctx.session.get(IMPERSONATOR_SESSION_KEY);
|
|
@@ -139,5 +146,4 @@ export async function stopImpersonation(ctx) {
|
|
|
139
146
|
// confirmação dele, sobre a conta dele, dentro da janela dele.
|
|
140
147
|
ctx.session.put(ACCOUNT_SESSION_KEY, impersonatorId);
|
|
141
148
|
ctx.session.forget(IMPERSONATOR_SESSION_KEY);
|
|
142
|
-
ctx.session.forget(ADMIN_ACCESS_TOKEN_SESSION_KEY);
|
|
143
149
|
}
|
|
@@ -128,6 +128,11 @@ export class OidcService {
|
|
|
128
128
|
findAccount: config.findAccount,
|
|
129
129
|
globalRolesClaim: config.globalRolesClaim,
|
|
130
130
|
resolveTokenRoles: config.resolveTokenRoles,
|
|
131
|
+
// Allowlist de roles que autorizam impersonation — a MESMA de
|
|
132
|
+
// `config.admin.roles` usada pelos guards do console (default
|
|
133
|
+
// `['ADMIN']`). Permite que um host use papel próprio (ex.: `admin`
|
|
134
|
+
// minúsculo num store de roles próprio) sem alterar `globalRoles`.
|
|
135
|
+
adminRoles: config.admin.roles,
|
|
131
136
|
// Resource indicators (RFC 8707) suportados: o `audience` default + cada
|
|
132
137
|
// resource declarado. Usado para validar `audience`/`resource` no pedido de
|
|
133
138
|
// token-exchange — alvos fora desta lista são rejeitados (invalid_target).
|
|
@@ -14,6 +14,11 @@ export interface TokenExchangeDeps {
|
|
|
14
14
|
* `target.globalRoles ?? []` (unchanged behavior). Mirrors the mint-time hook used
|
|
15
15
|
* by the authorization-code flow so impersonated tokens source roles from the same
|
|
16
16
|
* authority (e.g. @adonis-agora/authz) or custom store.
|
|
17
|
+
*
|
|
18
|
+
* The SAME hook also gates the ACTOR (the admin trying to impersonate): its roles
|
|
19
|
+
* are resolved through it too, so an app that keeps roles in its own table
|
|
20
|
+
* (e.g. via `@adonis-agora/authz`) can impersonate without storing roles in the
|
|
21
|
+
* authkit `globalRoles` column. When omitted, falls back to `actor.globalRoles`.
|
|
17
22
|
*/
|
|
18
23
|
resolveTokenRoles?: (account: AuthAccount, context: {
|
|
19
24
|
clientId?: string;
|
|
@@ -23,6 +28,14 @@ export interface TokenExchangeDeps {
|
|
|
23
28
|
orgRole: string;
|
|
24
29
|
} | null;
|
|
25
30
|
}) => string[] | Promise<string[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Allowlist de roles que autorizam a impersonation. Default: `['ADMIN']`.
|
|
33
|
+
* Aceita o papel EXATO (ex.: `'admin'` minúsculo) do app — resolve contra a
|
|
34
|
+
* MESMA fonte de roles do `resolveTokenRoles`/`globalRoles`, não a um papel
|
|
35
|
+
* hardcoded. Mantém `adminRole` como alias singular para back-compat.
|
|
36
|
+
*/
|
|
37
|
+
adminRoles?: string[];
|
|
38
|
+
/** @deprecated Use `adminRoles` (allowlist). Mantido como alias singular. */
|
|
26
39
|
adminRole?: string;
|
|
27
40
|
/**
|
|
28
41
|
* Resource indicators (RFC 8707) suportados pelo provider. Quando o pedido traz
|
|
@@ -16,7 +16,7 @@ function intersectScopes(requested, allowed) {
|
|
|
16
16
|
return out.join(' ');
|
|
17
17
|
}
|
|
18
18
|
export function registerTokenExchange(provider, deps) {
|
|
19
|
-
const
|
|
19
|
+
const adminRoles = deps.adminRoles ?? (deps.adminRole ? [deps.adminRole] : ['ADMIN']);
|
|
20
20
|
const supportedResources = new Set(deps.supportedResources ?? []);
|
|
21
21
|
const handler = async (ctx) => {
|
|
22
22
|
const { params, client } = ctx.oidc;
|
|
@@ -36,7 +36,22 @@ export function registerTokenExchange(provider, deps) {
|
|
|
36
36
|
throw new errors.InvalidGrant('subject_token was not issued to this client');
|
|
37
37
|
}
|
|
38
38
|
const actor = await deps.findAccount(subjectAt.accountId);
|
|
39
|
-
if (!actor
|
|
39
|
+
if (!actor) {
|
|
40
|
+
throw new errors.InvalidGrant('actor not permitted to impersonate');
|
|
41
|
+
}
|
|
42
|
+
// Resolve as roles do ATOR pela MESMA fonte do claim (resolveTokenRoles
|
|
43
|
+
// quando o host configura — ex.: roles em tabela própria via
|
|
44
|
+
// `@adonis-agora/authz`), com fallback pro `globalRoles` do store. Sem isso
|
|
45
|
+
// um host que não grava roles na coluna `globalRoles` do authkit nunca
|
|
46
|
+
// conseguiria impersonar (o gate bateria sempre em `[]`). Mesmo padrão do
|
|
47
|
+
// console (`resolveAccountRoles` em `host/account_roles.ts`).
|
|
48
|
+
const actorRoles = deps.resolveTokenRoles
|
|
49
|
+
? await deps.resolveTokenRoles(actor, {
|
|
50
|
+
clientId: client?.clientId,
|
|
51
|
+
activeOrg: null,
|
|
52
|
+
})
|
|
53
|
+
: (actor.globalRoles ?? []);
|
|
54
|
+
if (!actorRoles.some((r) => adminRoles.includes(r))) {
|
|
40
55
|
throw new errors.InvalidGrant('actor not permitted to impersonate');
|
|
41
56
|
}
|
|
42
57
|
const targetId = params.requested_subject;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/authkit-server",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.2",
|
|
4
4
|
"description": "AdonisJS OIDC/OAuth2 provider (Identity Provider) toolkit: ejectable auth server with sessions, rate-limiting, MFA/TOTP, audit log, federated logout and OpenTelemetry metrics.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dudousxd",
|