@adonis-agora/authkit-server 0.54.0 → 0.55.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/build/index.d.ts +4 -2
- package/build/index.js +2 -1
- package/build/providers/authkit_server_provider.js +18 -0
- package/build/src/accounts/lucid_account_store.d.ts +35 -0
- package/build/src/accounts/lucid_account_store.js +9 -0
- package/build/src/accounts/lucid_store/core.js +88 -19
- package/build/src/accounts/lucid_store/shared.d.ts +14 -0
- package/build/src/accounts/lucid_store/token_hash.d.ts +79 -0
- package/build/src/accounts/lucid_store/token_hash.js +145 -0
- package/build/src/define_config.d.ts +93 -8
- package/build/src/define_config.js +28 -2
- package/build/src/host/account_api/account_api_controller.js +5 -4
- package/build/src/host/admin_api/admin_users_service.js +2 -1
- package/build/src/host/admin_api/api_orgs_controller.js +2 -1
- package/build/src/host/admin_console/console_impersonation_controller.d.ts +15 -1
- package/build/src/host/admin_console/console_impersonation_controller.js +26 -2
- package/build/src/host/admin_console/console_orgs_controller.js +3 -1
- package/build/src/host/admin_validators.d.ts +3 -3
- package/build/src/host/auth_host_config.d.ts +30 -0
- package/build/src/host/auth_host_config.js +15 -0
- package/build/src/host/config_locks.d.ts +29 -0
- package/build/src/host/config_locks.js +46 -0
- package/build/src/host/console_session.d.ts +38 -2
- package/build/src/host/console_session.js +46 -2
- package/build/src/host/controllers/account_mfa_controller.js +5 -5
- package/build/src/host/controllers/account_orgs_controller.js +2 -1
- package/build/src/host/controllers/account_security_controller.js +6 -5
- package/build/src/host/controllers/interaction_controller.js +122 -26
- package/build/src/host/controllers/registration_controller.js +5 -4
- package/build/src/host/controllers/social_controller.js +37 -0
- package/build/src/host/default_mailer.d.ts +36 -5
- package/build/src/host/default_mailer.js +63 -10
- package/build/src/host/i18n.d.ts +10 -0
- package/build/src/host/i18n.js +16 -0
- package/build/src/host/login_attempt.d.ts +88 -2
- package/build/src/host/login_attempt.js +101 -48
- package/build/src/host/login_notify.js +2 -2
- package/build/src/host/oidc_rp_guard.d.ts +25 -2
- package/build/src/host/oidc_rp_guard.js +55 -9
- package/build/src/host/origin.d.ts +21 -0
- package/build/src/host/origin.js +22 -0
- package/build/src/host/register_auth_host.d.ts +104 -3
- package/build/src/host/register_auth_host.js +222 -26
- package/build/src/host/runtime_settings.d.ts +16 -0
- package/build/src/host/runtime_settings.js +24 -0
- package/build/src/host/runtime_toggles.d.ts +10 -0
- package/build/src/host/runtime_toggles.js +4 -0
- package/build/src/host/security_notice_service.d.ts +4 -2
- package/build/src/host/security_notice_service.js +4 -2
- package/build/src/host/sudo/index.d.ts +8 -0
- package/build/src/host/sudo/index.js +8 -0
- package/build/src/host/sudo/methods/magic_link.d.ts +17 -3
- package/build/src/host/sudo/methods/magic_link.js +37 -13
- package/build/src/host/sudo/runtime.d.ts +44 -4
- package/build/src/host/sudo/runtime.js +90 -6
- package/build/src/host/sudo/satisfiability.d.ts +62 -0
- package/build/src/host/sudo/satisfiability.js +89 -0
- package/build/src/password/common_passwords.js +27 -7
- package/build/src/provider/oidc_service.js +25 -13
- package/build/src/provider/token_exchange.d.ts +12 -1
- package/build/src/provider/token_exchange.js +12 -0
- package/package.json +6 -3
- /package/build/{password → src/password}/common_passwords.txt +0 -0
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Fonte dos dados: compilação de senhas comuns de domínio público.
|
|
12
12
|
* Licença do arquivo de dados: CC0 / Public Domain.
|
|
13
13
|
*/
|
|
14
|
-
import { readFileSync } from 'node:fs';
|
|
14
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
15
15
|
import { dirname, join } from 'node:path';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
/**
|
|
@@ -23,16 +23,36 @@ let _commonPasswordsSet = null;
|
|
|
23
23
|
/**
|
|
24
24
|
* Carrega o arquivo de senhas comuns uma única vez (lazy).
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* Onde o arquivo é esperado: o build script copia `common_passwords.txt`
|
|
27
|
+
* para o mesmo diretório do módulo compilado (`build/src/password/`, já que
|
|
28
|
+
* `tsc` com `rootDir: "./"` preserva o subcaminho `src/`). Em dev via
|
|
29
|
+
* ts-exec (sem build), o módulo roda direto de `src/password/`, onde o
|
|
30
|
+
* `.txt` já mora ao lado do `.ts` no repo.
|
|
31
|
+
*
|
|
32
|
+
* Checamos mais de um candidato — não só o caminho-irmão — para sobreviver
|
|
33
|
+
* a uma mudança futura em `rootDir`/`outDir` ou a uma regressão no script de
|
|
34
|
+
* cópia do build: o caminho-irmão cobre o layout atual (build e dev); os
|
|
35
|
+
* demais são fallbacks para o layout antigo (`build/password/`) e para ler
|
|
36
|
+
* direto de `src/` a partir de um build.
|
|
37
|
+
*
|
|
38
|
+
* FAIL-SAFE: se o arquivo não existir em nenhum candidato, ou não puder ser
|
|
39
|
+
* lido (ex.: bundle sem assets), retorna um Set vazio — a checagem vira
|
|
40
|
+
* no-op sem quebrar o fluxo. Uma senha comum indevidamente aceita é sempre
|
|
41
|
+
* preferível a um login/signup quebrado por um asset ausente.
|
|
28
42
|
*/
|
|
29
43
|
function loadCommonPasswords() {
|
|
30
44
|
if (_commonPasswordsSet !== null)
|
|
31
45
|
return _commonPasswordsSet;
|
|
32
46
|
try {
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
47
|
+
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
48
|
+
const candidates = [
|
|
49
|
+
join(__dir, 'common_passwords.txt'), // caminho-irmão: build/src/password/ (prod) ou src/password/ (dev via ts-exec)
|
|
50
|
+
join(__dir, '..', '..', 'password', 'common_passwords.txt'), // layout antigo: build/password/
|
|
51
|
+
join(__dir, '..', '..', '..', 'src', 'password', 'common_passwords.txt'), // fallback: src/ a partir de um build
|
|
52
|
+
];
|
|
53
|
+
const filePath = candidates.find((p) => existsSync(p));
|
|
54
|
+
if (!filePath)
|
|
55
|
+
throw new Error('common_passwords.txt not found in any candidate path');
|
|
36
56
|
const content = readFileSync(filePath, 'utf-8');
|
|
37
57
|
const entries = content
|
|
38
58
|
.split('\n')
|
|
@@ -41,7 +61,7 @@ function loadCommonPasswords() {
|
|
|
41
61
|
_commonPasswordsSet = new Set(entries);
|
|
42
62
|
}
|
|
43
63
|
catch {
|
|
44
|
-
// Fail-safe: arquivo ausente → Set vazio (no-op check).
|
|
64
|
+
// Fail-safe: arquivo ausente em todos os candidatos → Set vazio (no-op check).
|
|
45
65
|
_commonPasswordsSet = new Set();
|
|
46
66
|
}
|
|
47
67
|
return _commonPasswordsSet;
|
|
@@ -118,19 +118,31 @@ export class OidcService {
|
|
|
118
118
|
},
|
|
119
119
|
}, this.sessionTtlHolder, this.tokenTtlHolder);
|
|
120
120
|
wireProviderEvents(provider, this.recorder);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
config.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
121
|
+
// KILL SWITCH de impersonation (`admin.impersonation`, default `true`).
|
|
122
|
+
// Com a flag em `false` o grant simplesmente NÃO EXISTE no provider — o
|
|
123
|
+
// token endpoint responde `unsupported_grant_type` — em vez de existir e
|
|
124
|
+
// recusar caso a caso. É a mesma flag que fecha o painel do console
|
|
125
|
+
// (`console_impersonation_controller.ts`): uma declaração, duas superfícies.
|
|
126
|
+
if (config.admin.impersonation) {
|
|
127
|
+
registerTokenExchange(provider, {
|
|
128
|
+
findAccount: config.findAccount,
|
|
129
|
+
globalRolesClaim: config.globalRolesClaim,
|
|
130
|
+
resolveTokenRoles: config.resolveTokenRoles,
|
|
131
|
+
// Resource indicators (RFC 8707) suportados: o `audience` default + cada
|
|
132
|
+
// resource declarado. Usado para validar `audience`/`resource` no pedido de
|
|
133
|
+
// token-exchange — alvos fora desta lista são rejeitados (invalid_target).
|
|
134
|
+
supportedResources: [
|
|
135
|
+
config.accessTokens.audience,
|
|
136
|
+
...Object.keys(config.accessTokens.resources),
|
|
137
|
+
],
|
|
138
|
+
// LOAD-BEARING: sem isto o gate de status do alvo dentro de
|
|
139
|
+
// `token_exchange.ts` degrada para "allowed" (o campo é opcional, para não
|
|
140
|
+
// quebrar hosts com stores mínimos) e um admin consegue impersonar uma
|
|
141
|
+
// conta que acabou de desabilitar, recebendo tokens plenamente funcionais.
|
|
142
|
+
accountStore: config.accountStore,
|
|
143
|
+
audit: config.audit,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
134
146
|
// Quando o issuer tem um path (ex.: http://host/oidc), o provider precisa ser
|
|
135
147
|
// MONTADO sob esse path via koa-mount. Isso faz o oidc-provider gerar URLs de
|
|
136
148
|
// discovery e redirects de resume/interaction CORRETAMENTE prefixados (ex.:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthAccount } from '../accounts/account_store.js';
|
|
1
|
+
import type { AccountStore, AuthAccount } from '../accounts/account_store.js';
|
|
2
2
|
import type { AuditSink } from '../audit/audit_sink.js';
|
|
3
3
|
export interface TokenExchangeAccount {
|
|
4
4
|
id: string;
|
|
@@ -32,5 +32,16 @@ export interface TokenExchangeDeps {
|
|
|
32
32
|
supportedResources?: string[];
|
|
33
33
|
/** Sink de auditoria (best-effort). Quando presente, registra `impersonation`. */
|
|
34
34
|
audit?: AuditSink;
|
|
35
|
+
/**
|
|
36
|
+
* AccountStore do host, usado para checar se o alvo da impersonação está
|
|
37
|
+
* desabilitado/expirado (mesmo gate de `attemptPasswordLogin`, via
|
|
38
|
+
* `assertAccountEnabled`). Impersonar uma conta desabilitada mintaria tokens
|
|
39
|
+
* funcionais para uma identidade que deveria estar sem acesso algum.
|
|
40
|
+
*
|
|
41
|
+
* OPCIONAL e capability-probed (via `supportsAccountStatus`, dentro do
|
|
42
|
+
* helper): ausente ou sem a capacidade → degrada para "permitido" (mesma
|
|
43
|
+
* regra de todo o resto da lib — nunca quebra hosts com um store mínimo).
|
|
44
|
+
*/
|
|
45
|
+
accountStore?: AccountStore;
|
|
35
46
|
}
|
|
36
47
|
export declare function registerTokenExchange(provider: any, deps: TokenExchangeDeps): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { errors } from 'oidc-provider';
|
|
2
|
+
import { assertAccountEnabled } from '../host/login_attempt.js';
|
|
2
3
|
const TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange';
|
|
3
4
|
const ACCESS_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:access_token';
|
|
4
5
|
/**
|
|
@@ -46,6 +47,17 @@ export function registerTokenExchange(provider, deps) {
|
|
|
46
47
|
if (!target) {
|
|
47
48
|
throw new errors.InvalidGrant('requested_subject not found');
|
|
48
49
|
}
|
|
50
|
+
// Status do alvo (disabled/expirado): mesmo gate de `attemptPasswordLogin`.
|
|
51
|
+
// Sem isso, impersonar um alvo desabilitado mintava tokens funcionais para
|
|
52
|
+
// uma identidade que o admin acreditava ter revogado. `accountStore` é
|
|
53
|
+
// opcional/capability-probed — sem ele (ou sem a capacidade), permanece
|
|
54
|
+
// "permitido" como antes.
|
|
55
|
+
if (deps.accountStore) {
|
|
56
|
+
const statusGate = await assertAccountEnabled({ accountStore: deps.accountStore, audit: deps.audit }, target.id, { email: target.email ?? '', ip: ctx.req?.socket?.remoteAddress ?? null });
|
|
57
|
+
if (!statusGate.allowed) {
|
|
58
|
+
throw new errors.InvalidGrant('requested_subject is disabled');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
49
61
|
// audience/resource: se o pedido vier com um alvo, ele PRECISA estar entre os
|
|
50
62
|
// resource indicators suportados. Caso contrário rejeitamos (conservador) —
|
|
51
63
|
// nunca embutimos audiência arbitrária no token emitido.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/authkit-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.55.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",
|
|
@@ -124,6 +124,8 @@
|
|
|
124
124
|
"@japa/assert": "4.2.0",
|
|
125
125
|
"@japa/runner": "5.3.0",
|
|
126
126
|
"@poppinss/ts-exec": "1.4.4",
|
|
127
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
128
|
+
"@types/ioredis-mock": "^8.2.7",
|
|
127
129
|
"@types/koa": "^2.15.2",
|
|
128
130
|
"@types/koa-mount": "^4.0.5",
|
|
129
131
|
"@types/luxon": "3.7.1",
|
|
@@ -151,11 +153,12 @@
|
|
|
151
153
|
"@adonis-agora/authkit-react": "0.18.0"
|
|
152
154
|
},
|
|
153
155
|
"scripts": {
|
|
154
|
-
"build": "node scripts/build_host_css.mjs && node scripts/build_webauthn.mjs && node scripts/build_ui.mjs && node -e \"const fs=require('node:fs');for(const d of ['build/stubs','build/host/views'])fs.rmSync(d,{recursive:true,force:true})\" && tsc && node -e \"require('node:fs').cpSync('src/host/assets','build/src/host/assets',{recursive:true})\" && node -e \"require('node:fs').cpSync('stubs','build/stubs',{recursive:true,filter:(s)=>!s.endsWith('.ts')})\" && node -e \"const fs=require('node:fs');if(fs.existsSync('assets'))fs.cpSync('assets','build/assets',{recursive:true})\" && node -e \"require('node:fs').cpSync('src/host/views','build/host/views',{recursive:true})\" && node -e \"const fs=require('node:fs');fs.mkdirSync('build/host/ui',{recursive:true});fs.readdirSync('src/host/ui').filter(f=>f.endsWith('.html')).forEach(f=>fs.copyFileSync('src/host/ui/'+f,'build/host/ui/'+f))\" && node -e \"require('node:fs').copyFileSync('commands/commands.json','build/commands/commands.json')\" && node -e \"const fs=require('node:fs');fs.mkdirSync('build/password',{recursive:true});fs.copyFileSync('src/password/common_passwords.txt','build/password/common_passwords.txt')\"",
|
|
156
|
+
"build": "node scripts/build_host_css.mjs && node scripts/build_webauthn.mjs && node scripts/build_ui.mjs && node -e \"const fs=require('node:fs');for(const d of ['build/stubs','build/host/views'])fs.rmSync(d,{recursive:true,force:true})\" && tsc && node -e \"require('node:fs').cpSync('src/host/assets','build/src/host/assets',{recursive:true})\" && node -e \"require('node:fs').cpSync('stubs','build/stubs',{recursive:true,filter:(s)=>!s.endsWith('.ts')})\" && node -e \"const fs=require('node:fs');if(fs.existsSync('assets'))fs.cpSync('assets','build/assets',{recursive:true})\" && node -e \"require('node:fs').cpSync('src/host/views','build/host/views',{recursive:true})\" && node -e \"const fs=require('node:fs');fs.mkdirSync('build/host/ui',{recursive:true});fs.readdirSync('src/host/ui').filter(f=>f.endsWith('.html')).forEach(f=>fs.copyFileSync('src/host/ui/'+f,'build/host/ui/'+f))\" && node -e \"require('node:fs').copyFileSync('commands/commands.json','build/commands/commands.json')\" && node -e \"const fs=require('node:fs');fs.mkdirSync('build/src/password',{recursive:true});fs.copyFileSync('src/password/common_passwords.txt','build/src/password/common_passwords.txt')\"",
|
|
155
157
|
"build:ui": "node scripts/build_ui.mjs",
|
|
156
158
|
"build:webauthn": "node scripts/build_webauthn.mjs",
|
|
157
159
|
"check:webauthn-bundle": "node scripts/check_webauthn_bundle.mjs",
|
|
158
|
-
"typecheck": "tsc --noEmit && node scripts/typecheck_ui.mjs",
|
|
160
|
+
"typecheck": "tsc --noEmit && node scripts/typecheck_ui.mjs && node scripts/typecheck_tests.mjs",
|
|
161
|
+
"typecheck:tests": "node scripts/typecheck_tests.mjs",
|
|
159
162
|
"test": "node --import=@poppinss/ts-exec bin/test.ts",
|
|
160
163
|
"build:host-css": "node scripts/build_host_css.mjs"
|
|
161
164
|
}
|
|
File without changes
|