@adonis-agora/authkit-server 0.72.0 → 0.74.0
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.
- package/README.md +4 -0
- package/build/commands/clients_create.d.ts +2 -0
- package/build/commands/clients_create.js +41 -13
- package/build/commands/commands.json +7 -0
- package/build/index.d.ts +6 -0
- package/build/index.js +7 -0
- package/build/src/commands/import_clients.js +2 -0
- package/build/src/host/access_token_verifier.d.ts +100 -0
- package/build/src/host/access_token_verifier.js +240 -0
- package/build/src/host/account_api/account_orgs_api_controller.js +6 -1
- package/build/src/host/active_org_cookie.d.ts +21 -1
- package/build/src/host/active_org_cookie.js +38 -0
- package/build/src/host/admin_api/admin_orgs_service.d.ts +7 -1
- package/build/src/host/admin_api/admin_orgs_service.js +11 -1
- package/build/src/host/admin_api/api_clients_controller.d.ts +4 -1
- package/build/src/host/admin_api/api_clients_controller.js +25 -2
- package/build/src/host/admin_api/api_orgs_controller.js +20 -20
- package/build/src/host/admin_api/dto.d.ts +1 -0
- package/build/src/host/admin_api/dto.js +1 -0
- package/build/src/host/admin_clients_service.d.ts +9 -0
- package/build/src/host/admin_clients_service.js +21 -0
- package/build/src/host/admin_console/console_clients_controller.d.ts +3 -1
- package/build/src/host/admin_console/console_clients_controller.js +25 -2
- package/build/src/host/admin_console/console_orgs_controller.js +1 -1
- package/build/src/host/admin_sessions_service.d.ts +21 -0
- package/build/src/host/admin_sessions_service.js +49 -0
- package/build/src/host/admin_validators.d.ts +11 -1
- package/build/src/host/admin_validators.js +36 -8
- package/build/src/host/bearer_account.d.ts +9 -0
- package/build/src/host/bearer_account.js +28 -0
- package/build/src/host/client_metadata.d.ts +51 -0
- package/build/src/host/client_metadata.js +98 -0
- package/build/src/host/console_session.d.ts +14 -3
- package/build/src/host/console_session.js +21 -9
- package/build/src/host/controllers/account_orgs_controller.js +3 -0
- package/build/src/host/controllers/interaction_controller.d.ts +18 -1
- package/build/src/host/controllers/interaction_controller.js +55 -14
- package/build/src/host/oidc_bearer_guard.d.ts +113 -0
- package/build/src/host/oidc_bearer_guard.js +223 -0
- package/build/src/host/oidc_rp_guard.d.ts +3 -1
- package/build/src/host/oidc_rp_guard.js +6 -2
- package/build/src/host/org_access_revocation.d.ts +20 -0
- package/build/src/host/org_access_revocation.js +41 -0
- package/build/src/host/ui-dist/assets/{index-Dct63ai-.js → index-CIqtoNcO.js} +1 -1
- package/build/src/host/ui-dist/index.html +1 -1
- package/build/src/provider/build_provider.js +42 -17
- package/build/src/provider/oidc_service.d.ts +8 -0
- package/build/src/provider/oidc_service.js +23 -4
- package/package.json +3 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as oidc from 'oidc-provider';
|
|
2
2
|
import { pickModelAdapterClass } from '../adapters/factory.js';
|
|
3
3
|
import { normalizeActiveOrg, readActiveOrgFromKoaCtx } from '../host/active_org_cookie.js';
|
|
4
|
+
import { assertClientMetadata } from '../host/client_metadata.js';
|
|
4
5
|
import { createDeviceSources } from './device_sources.js';
|
|
5
6
|
import { createLogoutSources } from './logout_sources.js';
|
|
6
7
|
import { registrationPolicyMiddleware } from './registration_policy.js';
|
|
@@ -123,23 +124,39 @@ export function buildProvider(config, options, sessionTtlHolder, tokenTtlHolder)
|
|
|
123
124
|
// `pickModelAdapterClass`, usada também pelos serviços que instanciam
|
|
124
125
|
// adapter na mão. Sem `session.adapter`, as duas classes são a mesma.
|
|
125
126
|
adapter: ((name) => new (pickModelAdapterClass(name, config.AdapterClass, config.SessionAdapterClass))(name)),
|
|
126
|
-
clients: config.clients.map((c) =>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
:
|
|
142
|
-
|
|
127
|
+
clients: config.clients.map((c) => {
|
|
128
|
+
const grants = c.grants ?? ['authorization_code', 'refresh_token'];
|
|
129
|
+
const tokenEndpointAuthMethod = c.tokenEndpointAuthMethod ?? (c.clientSecret ? 'client_secret_basic' : 'none');
|
|
130
|
+
// Clients do `defineConfig` passam pela MESMA validação do admin/CLI/import:
|
|
131
|
+
// um nativo com secret (ou redirect inválido pro tipo) falha no boot, não em runtime.
|
|
132
|
+
assertClientMetadata({
|
|
133
|
+
applicationType: c.applicationType ?? 'web',
|
|
134
|
+
tokenEndpointAuthMethod,
|
|
135
|
+
grantTypes: grants,
|
|
136
|
+
redirectUris: c.redirectUris,
|
|
137
|
+
postLogoutRedirectUris: c.postLogoutRedirectUris ?? [],
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
client_id: c.clientId,
|
|
141
|
+
client_secret: c.clientSecret,
|
|
142
|
+
redirect_uris: c.redirectUris,
|
|
143
|
+
post_logout_redirect_uris: c.postLogoutRedirectUris ?? [],
|
|
144
|
+
grant_types: grants,
|
|
145
|
+
response_types: (c.grants ?? ['authorization_code']).includes('authorization_code')
|
|
146
|
+
? ['code']
|
|
147
|
+
: [],
|
|
148
|
+
token_endpoint_auth_method: tokenEndpointAuthMethod,
|
|
149
|
+
// App nativo (RFC 8252): libera redirect de esquema privado/loopback. Sem a
|
|
150
|
+
// chave, o oidc-provider assume `web` — o comportamento de sempre.
|
|
151
|
+
...(c.applicationType === 'native' ? { application_type: 'native' } : {}),
|
|
152
|
+
// OIDC Back-Channel Logout: só envia as chaves quando o client as declara,
|
|
153
|
+
// p/ não forçar metadata vazio em clients que não usam o recurso.
|
|
154
|
+
...(c.backchannelLogoutUri ? { backchannel_logout_uri: c.backchannelLogoutUri } : {}),
|
|
155
|
+
...(c.backchannelLogoutSessionRequired !== undefined
|
|
156
|
+
? { backchannel_logout_session_required: c.backchannelLogoutSessionRequired }
|
|
157
|
+
: {}),
|
|
158
|
+
};
|
|
159
|
+
}),
|
|
143
160
|
findAccount: options.findAccount,
|
|
144
161
|
// Grant REAPROVEITADO (consent já lembrado): o org capturado no primeiro
|
|
145
162
|
// consent ficaria VELHO se o usuário trocasse de org no console e voltasse a
|
|
@@ -220,6 +237,14 @@ export function buildProvider(config, options, sessionTtlHolder, tokenTtlHolder)
|
|
|
220
237
|
// sobreviva ao authorize e fique disponivel em interactionDetails().params.audience.
|
|
221
238
|
extraParams: ['audience'],
|
|
222
239
|
pkce: { methods: ['S256'], required: () => true },
|
|
240
|
+
// Rotação SEMPRE: todo uso do refresh token emite um novo e invalida o
|
|
241
|
+
// anterior (reuso de um RT já rotacionado revoga o grant inteiro — detecção de
|
|
242
|
+
// replay do oidc-provider). Vale igual para clients públicos (SPA, app nativo
|
|
243
|
+
// RFC 8252), para os quais a rotação é a defesa exigida pelo OAuth 2.0 Security
|
|
244
|
+
// BCP, já que o RT não é amarrado a um secret. O `issueRefreshToken` fica no
|
|
245
|
+
// default do provider: emite RT quando o client tem o grant `refresh_token` e o
|
|
246
|
+
// escopo `offline_access` foi concedido (o que exige `prompt=consent` no
|
|
247
|
+
// authorize — OIDC Core §11), sem distinção entre client público e confidencial.
|
|
223
248
|
rotateRefreshToken: true,
|
|
224
249
|
features: {
|
|
225
250
|
devInteractions: { enabled: false },
|
|
@@ -9,6 +9,14 @@ export declare class OidcService {
|
|
|
9
9
|
get provider(): ReturnType<typeof buildProvider>;
|
|
10
10
|
get callback(): (req: any, res: any) => void;
|
|
11
11
|
get interactions(): InteractionActions;
|
|
12
|
+
/**
|
|
13
|
+
* JWKS PÚBLICO do keystore em uso pelo provider ATUAL (o mesmo que o `jwks_uri`
|
|
14
|
+
* serve). Troca junto com o provider num `reloadKeys`/rotação — quem verifica JWT
|
|
15
|
+
* in-process (ex.: `oidcBearerGuard`) lê daqui a cada verificação.
|
|
16
|
+
*/
|
|
17
|
+
get publicJwks(): {
|
|
18
|
+
keys: Record<string, any>[];
|
|
19
|
+
};
|
|
12
20
|
/** Pathname do issuer sem barra final (ex.: `/oidc`). Vazio quando montado na raiz. */
|
|
13
21
|
readonly mountPath: string;
|
|
14
22
|
readonly recorder: MetricsRecorder;
|
|
@@ -2,9 +2,9 @@ import { timingSafeEqual } from 'node:crypto';
|
|
|
2
2
|
import { NoopRecorder } from '@adonis-agora/authkit-core';
|
|
3
3
|
import Koa from 'koa';
|
|
4
4
|
import mount from 'koa-mount';
|
|
5
|
-
import { normalizeActiveOrg, readActiveOrgFromKoaCtx } from '../host/active_org_cookie.js';
|
|
5
|
+
import { normalizeActiveOrg, readActiveOrgFromKoaCtx, verifyActiveOrgMembership, } from '../host/active_org_cookie.js';
|
|
6
6
|
import { isFirstPartyClient } from '../host/branding.js';
|
|
7
|
-
import { listKeyInfos, signingKeyAgeDays } from '../keys/keystore.js';
|
|
7
|
+
import { listKeyInfos, signingKeyAgeDays, toPublicJwks, } from '../keys/keystore.js';
|
|
8
8
|
import { wireProviderEvents } from '../observability/wire_provider_events.js';
|
|
9
9
|
import { buildProvider } from './build_provider.js';
|
|
10
10
|
import { createInteractionActions } from './interaction_actions.js';
|
|
@@ -13,6 +13,7 @@ export class OidcService {
|
|
|
13
13
|
#provider;
|
|
14
14
|
#callback;
|
|
15
15
|
#interactions;
|
|
16
|
+
#publicJwks;
|
|
16
17
|
#appKey;
|
|
17
18
|
get provider() {
|
|
18
19
|
return this.#provider;
|
|
@@ -23,6 +24,14 @@ export class OidcService {
|
|
|
23
24
|
get interactions() {
|
|
24
25
|
return this.#interactions;
|
|
25
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* JWKS PÚBLICO do keystore em uso pelo provider ATUAL (o mesmo que o `jwks_uri`
|
|
29
|
+
* serve). Troca junto com o provider num `reloadKeys`/rotação — quem verifica JWT
|
|
30
|
+
* in-process (ex.: `oidcBearerGuard`) lê daqui a cada verificação.
|
|
31
|
+
*/
|
|
32
|
+
get publicJwks() {
|
|
33
|
+
return this.#publicJwks;
|
|
34
|
+
}
|
|
26
35
|
/** Pathname do issuer sem barra final (ex.: `/oidc`). Vazio quando montado na raiz. */
|
|
27
36
|
mountPath;
|
|
28
37
|
recorder;
|
|
@@ -87,8 +96,15 @@ export class OidcService {
|
|
|
87
96
|
// — server-a-servidor, SEM cookies — então o cookie abaixo não existe
|
|
88
97
|
// ali. O cookie continua como FALLBACK para fluxos em que o id_token
|
|
89
98
|
// sai no próprio authorize (implicit/hybrid), onde a request é do browser.
|
|
90
|
-
const
|
|
99
|
+
const claimedOrg = normalizeActiveOrg(ctx?.oidc?.entities?.Grant?.activeOrg) ??
|
|
91
100
|
readActiveOrgFromKoaCtx(ctx);
|
|
101
|
+
// O `claimedOrg` é um RETRATO do consent (ou do cookie): o refresh o
|
|
102
|
+
// reemite por até 30 dias. Antes de virar claim, ele é conferido no store
|
|
103
|
+
// — membership e org ATUAIS, papel ATUAL (ver `verifyActiveOrgMembership`).
|
|
104
|
+
// Memoizado por chamada de `findAccount`: `claims()` pode rodar mais de uma
|
|
105
|
+
// vez no mesmo mint (id_token + userinfo) e a pergunta é a mesma.
|
|
106
|
+
let verifiedOrg;
|
|
107
|
+
const currentOrg = () => (verifiedOrg ??= verifyActiveOrgMembership(config.accountStore, user.id, claimedOrg));
|
|
92
108
|
// GATE de least-privilege: roles globais e claims de org só são emitidas
|
|
93
109
|
// para clients FIRST-PARTY. Capturamos o clientId aqui (fora do closure
|
|
94
110
|
// `claims()`, pois o `ctx` da request vive neste escopo).
|
|
@@ -119,10 +135,12 @@ export class OidcService {
|
|
|
119
135
|
};
|
|
120
136
|
// roles/org_* são dados de autorização interna: só para first-party.
|
|
121
137
|
if (firstParty) {
|
|
138
|
+
const activeOrg = await currentOrg();
|
|
122
139
|
base[config.globalRolesClaim] = config.resolveTokenRoles
|
|
123
140
|
? await config.resolveTokenRoles(user, { clientId, activeOrg })
|
|
124
141
|
: (user.globalRoles ?? []);
|
|
125
|
-
// Emite claims de org somente quando há uma org ativa na sessão
|
|
142
|
+
// Emite claims de org somente quando há uma org ativa na sessão E a
|
|
143
|
+
// conta ainda é membro dela (conferido no store, papel atual).
|
|
126
144
|
if (activeOrg) {
|
|
127
145
|
base.org_id = activeOrg.orgId;
|
|
128
146
|
base.org_slug = activeOrg.orgSlug;
|
|
@@ -196,6 +214,7 @@ export class OidcService {
|
|
|
196
214
|
this.#provider = provider;
|
|
197
215
|
this.#callback = callback;
|
|
198
216
|
this.#interactions = interactions;
|
|
217
|
+
this.#publicJwks = toPublicJwks(jwks);
|
|
199
218
|
}
|
|
200
219
|
/**
|
|
201
220
|
* Recarrega as chaves de assinatura AO VIVO: relê o keystore do cofre e reconstrói
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/authkit-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.74.0",
|
|
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",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
}
|
|
103
103
|
},
|
|
104
104
|
"dependencies": {
|
|
105
|
-
"@adonis-agora/authkit-core": "0.
|
|
105
|
+
"@adonis-agora/authkit-core": "0.9.0",
|
|
106
106
|
"@simplewebauthn/browser": "14.0.0",
|
|
107
107
|
"@simplewebauthn/server": "14.0.2",
|
|
108
108
|
"jose": "6.2.12",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"qrcode": "1.5.4"
|
|
114
114
|
},
|
|
115
115
|
"devDependencies": {
|
|
116
|
-
"@adonis-agora/authkit-react": "0.
|
|
116
|
+
"@adonis-agora/authkit-react": "0.23.0",
|
|
117
117
|
"@adonis-agora/durable": "0.40.0",
|
|
118
118
|
"@adonis-agora/telescope": "0.21.1",
|
|
119
119
|
"@adonisjs/ally": "6.3.0",
|