@adonis-agora/authkit-client 0.20.5 → 1.0.0-rc.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/CHANGELOG.md +957 -0
- package/README.md +47 -12
- package/build/auth.d.ts +2 -0
- package/build/auth.js +1 -0
- package/build/authkit_context_middleware.d.ts +1 -0
- package/build/authkit_context_middleware.js +2 -0
- package/build/commands/configure.d.ts +10 -0
- package/build/commands/configure.js +57 -2
- package/build/index.d.ts +3 -2
- package/build/index.js +1 -1
- package/build/providers/authkit_client_provider.d.ts +8 -1
- package/build/providers/authkit_client_provider.js +28 -12
- package/build/src/authenticator.d.ts +4 -3
- package/build/src/authenticator.js +6 -3
- package/build/src/host/authkit_client_guard.d.ts +51 -36
- package/build/src/host/authkit_client_guard.js +97 -46
- package/build/src/host/dashboard_guard.d.ts +3 -7
- package/build/src/host/dashboard_guard.js +3 -7
- package/build/src/host/request_authenticator.d.ts +28 -10
- package/build/src/host/request_authenticator.js +5 -3
- package/build/src/middleware/authkit_context_middleware.d.ts +3 -4
- package/build/src/middleware/authkit_context_middleware.js +5 -4
- package/build/src/middleware/backchannel_revocation_middleware.d.ts +1 -1
- package/build/src/middleware/backchannel_revocation_middleware.js +1 -1
- package/build/src/middleware/require_org_middleware.d.ts +3 -2
- package/build/src/middleware/require_org_middleware.js +5 -4
- package/build/src/resolvers/dpop_proof.d.ts +34 -0
- package/build/src/resolvers/dpop_proof.js +92 -0
- package/build/src/resolvers/factory.d.ts +5 -0
- package/build/src/resolvers/factory.js +1 -0
- package/build/src/resolvers/jwt_resolver.d.ts +9 -0
- package/build/src/resolvers/jwt_resolver.js +47 -3
- package/build/stubs/config/auth.stub +30 -0
- package/build/stubs/config/authkit_client.stub +2 -1
- package/build/stubs/middleware/auth_middleware.stub +25 -0
- package/build/stubs/middleware/silent_auth_middleware.stub +16 -0
- package/build/types.d.ts +5 -32
- package/build/types.js +1 -1
- package/package.json +10 -15
- package/skills/authkit-rp-client/SKILL.md +102 -47
- package/stubs/config/auth.stub +30 -0
- package/stubs/config/authkit_client.stub +2 -1
- package/stubs/middleware/auth_middleware.stub +25 -0
- package/stubs/middleware/silent_auth_middleware.stub +16 -0
- package/build/src/host/ctx_auth.d.ts +0 -6
- package/build/src/host/ctx_auth.js +0 -8
- package/build/src/middleware/auth_middleware.d.ts +0 -13
- package/build/src/middleware/auth_middleware.js +0 -13
- package/build/src/middleware/authkit_middleware.d.ts +0 -6
- package/build/src/middleware/authkit_middleware.js +0 -24
- package/build/src/middleware/silent_auth_middleware.d.ts +0 -13
- package/build/src/middleware/silent_auth_middleware.js +0 -15
package/README.md
CHANGED
|
@@ -8,8 +8,16 @@ Authorization Server (`@adonis-agora/authkit-server`), validando o ID token loca
|
|
|
8
8
|
node ace add @adonis-agora/authkit-client
|
|
9
9
|
# ou: pnpm add @adonis-agora/authkit-client && node ace configure @adonis-agora/authkit-client
|
|
10
10
|
```
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
`@adonisjs/auth` é peer obrigatório: `ctx.auth` é o autenticador do `@adonisjs/auth`, apoiado
|
|
12
|
+
na sessão do AuthKit pelo `authkitClientGuard`. O `configure`:
|
|
13
|
+
|
|
14
|
+
- instala `@adonisjs/auth` se o app ainda não depende dele (`--no-install` só imprime o comando);
|
|
15
|
+
- publica `config/authkit_client.ts` e `config/auth.ts` (`authkitClientGuard` +
|
|
16
|
+
`sessionUserProvider` com `--model`, default `#models/user`; um `config/auth.ts` existente não é
|
|
17
|
+
tocado e o guard a adicionar é impresso);
|
|
18
|
+
- publica `app/middleware/auth_middleware.ts` / `silent_auth_middleware.ts` (named `auth` e
|
|
19
|
+
`silentAuth`) e o controller `app/controllers/oidc_session_controller.ts`;
|
|
20
|
+
- registra os providers e, no `router`, `initialize_auth_middleware` + `authkit_context_middleware`.
|
|
13
21
|
|
|
14
22
|
## Rotas de sessão
|
|
15
23
|
|
|
@@ -23,7 +31,6 @@ import { registerOidcClient } from '@adonis-agora/authkit-client'
|
|
|
23
31
|
|
|
24
32
|
registerOidcClient(router, {
|
|
25
33
|
prefix: '/auth', // default
|
|
26
|
-
loginMiddleware: middleware.guest(),
|
|
27
34
|
redirects: { byGlobalRole: { ADMIN: '/admin' }, default: '/' },
|
|
28
35
|
afterLogin: async (ctx, identity) => undefined, // retornar string redireciona
|
|
29
36
|
postLogoutRedirect: '/', // default: origem do redirectUri + '/'
|
|
@@ -51,25 +58,52 @@ router.post('/auth/logout', [OidcSessionController, 'logout'])
|
|
|
51
58
|
> `startSession`/`endSession` são os caminhos que mantêm a invariante.
|
|
52
59
|
|
|
53
60
|
## Uso por request
|
|
54
|
-
`
|
|
61
|
+
Rotas usam `middleware.auth()` / `middleware.silentAuth()` e os controllers leem o usuário em
|
|
62
|
+
`ctx.auth`:
|
|
55
63
|
```ts
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
// app/controllers/dashboard_controller.ts
|
|
65
|
+
export default class DashboardController {
|
|
66
|
+
async handle({ auth, authkit, inertia }: HttpContext) {
|
|
67
|
+
const user = auth.getUserOrFail() // atrás de middleware.auth()
|
|
68
|
+
const isAdmin = authkit.hasGlobalRole('ADMIN') // síncrono, das claims do token
|
|
69
|
+
return inertia.render('dashboard', { user, isAdmin })
|
|
70
|
+
}
|
|
71
|
+
}
|
|
59
72
|
```
|
|
73
|
+
O guard pega o usuário do `resolveUser` (ex.: `lucidMirror`, que cria a linha no primeiro login)
|
|
74
|
+
e cai no provider quando não há `resolveUser`. Sem tabela local de usuários, use
|
|
75
|
+
`authkitIdentityProvider()` como provider: `ctx.auth.user` é a `Identity`.
|
|
76
|
+
|
|
77
|
+
`ctx.authkit` (populado pelo `authkit_context_middleware`) traz o que só o AuthKit sabe — a
|
|
78
|
+
`Identity` (`getIdentity()`), `hasGlobalRole`, a org ativa e `toSharedProps()` para o
|
|
79
|
+
`@adonis-agora/authkit-react` — inclusive em rotas públicas.
|
|
60
80
|
|
|
61
81
|
> **Autorização por app-role saiu do AuthKit.** O AuthKit só autentica e conhece as `globalRoles` do
|
|
62
82
|
> token. Roles do app (numa tabela do domínio) e permissões são do `@adonis-agora/authz` — configure
|
|
63
|
-
> o seam `resolveRoles` lá. Guard de rota por role vai pra authz/Bouncer, não para o `
|
|
83
|
+
> o seam `resolveRoles` lá. Guard de rota por role vai pra authz/Bouncer, não para o `middleware.auth()`
|
|
64
84
|
> (que só exige login).
|
|
65
85
|
|
|
66
86
|
## Resolução de sessão
|
|
67
87
|
`resolvers.jwt({ tokenSource })` valida o **ID token** (JWT) por JWKS:
|
|
68
88
|
- `tokenSource: 'session'` (default) — lê o token set do `@adonisjs/session`.
|
|
69
|
-
- `tokenSource: 'bearer'` — lê do header `Authorization` (SPA/API)
|
|
89
|
+
- `tokenSource: 'bearer'` — lê do header `Authorization` (SPA/API): `Bearer <jwt>` ou
|
|
90
|
+
`DPoP <jwt>` com a prova no header `DPoP` (token com `cnf.jkt` enviado como `Bearer` é recusado).
|
|
91
|
+
|
|
92
|
+
Rotas de API ganham um guard próprio com `resolver`:
|
|
93
|
+
```ts
|
|
94
|
+
// config/auth.ts
|
|
95
|
+
api: authkitClientGuard({
|
|
96
|
+
provider: authkitIdentityProvider(),
|
|
97
|
+
resolver: resolvers.jwt({ tokenSource: 'bearer' }), // ou resolvers.opaque/resolvers.pat
|
|
98
|
+
}),
|
|
99
|
+
```
|
|
100
|
+
```ts
|
|
101
|
+
// start/routes.ts
|
|
102
|
+
router.group(() => { /* … */ }).prefix('/api').use(middleware.auth({ guards: ['api'] }))
|
|
103
|
+
```
|
|
70
104
|
|
|
71
105
|
## Topologias de banco
|
|
72
|
-
A identidade (`ctx.
|
|
106
|
+
A identidade (`ctx.authkit.identity`) sempre vem do **token validado** e independe da
|
|
73
107
|
topologia de banco. O que muda é como `resolveUser` obtém o model do app.
|
|
74
108
|
|
|
75
109
|
### Mesmo banco + schemas
|
|
@@ -102,8 +136,9 @@ Roles do app (a tabela de roles do próprio domínio) não passam mais pelo Auth
|
|
|
102
136
|
`resolveRoles` do `@adonis-agora/authz`, que as une às `globalRoles` do token para decidir permissão.
|
|
103
137
|
|
|
104
138
|
## Ordem de middleware (importante)
|
|
105
|
-
|
|
106
|
-
(o resolver lê o token da session).
|
|
139
|
+
No stack `router`: `@adonisjs/session` → `@adonisjs/auth/initialize_auth_middleware` →
|
|
140
|
+
`authkit_context_middleware` (o resolver lê o token da session). O `configure` já registra nessa
|
|
141
|
+
ordem.
|
|
107
142
|
|
|
108
143
|
## Observabilidade (opcional)
|
|
109
144
|
O `Authenticator` registra métricas de resolução (`authkit.resolve.duration` e
|
package/build/auth.d.ts
ADDED
package/build/auth.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AuthkitClientGuard, authkitClientGuard, authkitIdentityProvider, } from './src/host/authkit_client_guard.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './src/middleware/authkit_context_middleware.js';
|
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
import type Configure from '@adonisjs/core/commands/configure';
|
|
2
|
+
/**
|
|
3
|
+
* `node ace configure @adonis-agora/authkit-client`
|
|
4
|
+
*
|
|
5
|
+
* `ctx.auth` comes from `@adonisjs/auth`, backed by the AuthKit session through
|
|
6
|
+
* `authkitClientGuard`, and `ctx.authkit` carries the AuthKit-only surface.
|
|
7
|
+
* Flags:
|
|
8
|
+
*
|
|
9
|
+
* - `--model=<import>`: the user model the guard's provider loads (default `#models/user`).
|
|
10
|
+
* - `--no-install`: print the install command instead of installing `@adonisjs/auth`.
|
|
11
|
+
*/
|
|
2
12
|
export declare function configure(command: Configure): Promise<void>;
|
|
@@ -1,11 +1,46 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
1
3
|
import { stubsRoot } from '../stubs/main.js';
|
|
4
|
+
const ADONIS_AUTH = '@adonisjs/auth';
|
|
5
|
+
/**
|
|
6
|
+
* `node ace configure @adonis-agora/authkit-client`
|
|
7
|
+
*
|
|
8
|
+
* `ctx.auth` comes from `@adonisjs/auth`, backed by the AuthKit session through
|
|
9
|
+
* `authkitClientGuard`, and `ctx.authkit` carries the AuthKit-only surface.
|
|
10
|
+
* Flags:
|
|
11
|
+
*
|
|
12
|
+
* - `--model=<import>`: the user model the guard's provider loads (default `#models/user`).
|
|
13
|
+
* - `--no-install`: print the install command instead of installing `@adonisjs/auth`.
|
|
14
|
+
*/
|
|
2
15
|
export async function configure(command) {
|
|
16
|
+
const flags = (command.parsedFlags ?? {});
|
|
17
|
+
const modelImport = typeof flags.model === 'string' && flags.model.trim() ? flags.model.trim() : '#models/user';
|
|
3
18
|
const codemods = await command.createCodemods();
|
|
4
19
|
await codemods.makeUsingStub(stubsRoot, 'config/authkit_client.stub', {});
|
|
5
20
|
await codemods.makeUsingStub(stubsRoot, 'controllers/oidc_session_controller.stub', {});
|
|
6
|
-
await codemods.updateRcFile((rc) =>
|
|
21
|
+
await codemods.updateRcFile((rc) => {
|
|
22
|
+
rc.addProvider('@adonisjs/auth/auth_provider');
|
|
23
|
+
rc.addProvider('@adonis-agora/authkit-client/authkit_client_provider');
|
|
24
|
+
});
|
|
25
|
+
await ensureAdonisAuth(command, codemods, flags.install !== false);
|
|
26
|
+
const authConfigPath = command.app.configPath('auth.ts');
|
|
27
|
+
if (existsSync(authConfigPath)) {
|
|
28
|
+
command.logger.warning('config/auth.ts already exists, so it was left as is. Add the AuthKit guard to it:\n' +
|
|
29
|
+
" import { authkitClientGuard } from '@adonis-agora/authkit-client/auth'\n" +
|
|
30
|
+
` web: authkitClientGuard({ provider: sessionUserProvider({ model: () => import('${modelImport}') }) }),`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
await codemods.makeUsingStub(stubsRoot, 'config/auth.stub', { modelImport });
|
|
34
|
+
}
|
|
35
|
+
await codemods.makeUsingStub(stubsRoot, 'middleware/auth_middleware.stub', {});
|
|
36
|
+
await codemods.makeUsingStub(stubsRoot, 'middleware/silent_auth_middleware.stub', {});
|
|
7
37
|
await codemods.registerMiddleware('router', [
|
|
8
|
-
{ path: '@
|
|
38
|
+
{ path: '@adonisjs/auth/initialize_auth_middleware' },
|
|
39
|
+
{ path: '@adonis-agora/authkit-client/authkit_context_middleware' },
|
|
40
|
+
]);
|
|
41
|
+
await codemods.registerMiddleware('named', [
|
|
42
|
+
{ name: 'auth', path: '#middleware/auth_middleware' },
|
|
43
|
+
{ name: 'silentAuth', path: '#middleware/silent_auth_middleware' },
|
|
9
44
|
]);
|
|
10
45
|
await codemods.defineEnvValidations({
|
|
11
46
|
leadingComment: 'Variáveis do @adonis-agora/authkit-client (OIDC client)',
|
|
@@ -22,4 +57,24 @@ export async function configure(command) {
|
|
|
22
57
|
command.logger.info('Registre as rotas de sessão em start/routes.ts com registerOidcClient(router) ' +
|
|
23
58
|
"(import { registerOidcClient } from '@adonis-agora/authkit-client'). " +
|
|
24
59
|
'O app/controllers/oidc_session_controller.ts publicado é só para customizar o fluxo.');
|
|
60
|
+
command.logger.info(`ctx.auth usa o authkitClientGuard (config/auth.ts, model ${modelImport}): proteja rotas com ` +
|
|
61
|
+
'middleware.auth() e leia auth.user. Para criar o usuário local no primeiro login, ' +
|
|
62
|
+
'configure resolveUser: lucidMirror(User, …) em config/authkit_client.ts.');
|
|
63
|
+
}
|
|
64
|
+
/** Installs `@adonisjs/auth` when the app does not depend on it yet. */
|
|
65
|
+
async function ensureAdonisAuth(command, codemods, install) {
|
|
66
|
+
let manifest = {};
|
|
67
|
+
try {
|
|
68
|
+
manifest = JSON.parse(await readFile(command.app.makePath('package.json'), 'utf8'));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// No readable package.json: treat the dependency as missing.
|
|
72
|
+
}
|
|
73
|
+
if (manifest.dependencies?.[ADONIS_AUTH] || manifest.devDependencies?.[ADONIS_AUTH])
|
|
74
|
+
return;
|
|
75
|
+
const packages = [{ name: ADONIS_AUTH, isDevDependency: false }];
|
|
76
|
+
if (install)
|
|
77
|
+
await codemods.installPackages(packages);
|
|
78
|
+
else
|
|
79
|
+
await codemods.listPackagesToInstall(packages);
|
|
25
80
|
}
|
package/build/index.d.ts
CHANGED
|
@@ -13,10 +13,11 @@ export type { DiscoverOptions, OidcEndpoints } from './src/discovery.js';
|
|
|
13
13
|
export { conventionEndpoints, discoverEndpoints, OidcDiscoveryError } from './src/discovery.js';
|
|
14
14
|
export type { CreateDpopProofInput, DpopKeyPair } from './src/dpop.js';
|
|
15
15
|
export { createDpopProof, dpopJwkThumbprint, generateDpopKeyPair, } from './src/dpop.js';
|
|
16
|
-
export type {
|
|
17
|
-
export { AuthkitClientGuard, authkitClientGuard } from './src/host/authkit_client_guard.js';
|
|
16
|
+
export type { IdentityUserProviderContract } from './src/host/authkit_client_guard.js';
|
|
17
|
+
export { AuthkitClientGuard, authkitClientGuard, authkitIdentityProvider, } from './src/host/authkit_client_guard.js';
|
|
18
18
|
export type { DashboardGuardOptions } from './src/host/dashboard_guard.js';
|
|
19
19
|
export { authkitDashboardAuthorize, authkitDashboardMiddleware, isAuthkitAdmin, } from './src/host/dashboard_guard.js';
|
|
20
|
+
export type { AuthkitUser, AuthkitUserType } from './src/host/request_authenticator.js';
|
|
20
21
|
export { getAuthkit } from './src/host/request_authenticator.js';
|
|
21
22
|
export type { ResiliencePolicy } from './src/http/resilient_fetch.js';
|
|
22
23
|
export { resilientFetch } from './src/http/resilient_fetch.js';
|
package/build/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export { BACKCHANNEL_LOGOUT_EVENT, InMemorySessionIndex, InvalidLogoutTokenError
|
|
|
6
6
|
export { defineConfig, resolvers } from './src/define_config.js';
|
|
7
7
|
export { conventionEndpoints, discoverEndpoints, OidcDiscoveryError } from './src/discovery.js';
|
|
8
8
|
export { createDpopProof, dpopJwkThumbprint, generateDpopKeyPair, } from './src/dpop.js';
|
|
9
|
-
export { AuthkitClientGuard, authkitClientGuard } from './src/host/authkit_client_guard.js';
|
|
9
|
+
export { AuthkitClientGuard, authkitClientGuard, authkitIdentityProvider, } from './src/host/authkit_client_guard.js';
|
|
10
10
|
export { authkitDashboardAuthorize, authkitDashboardMiddleware, isAuthkitAdmin, } from './src/host/dashboard_guard.js';
|
|
11
11
|
export { getAuthkit } from './src/host/request_authenticator.js';
|
|
12
12
|
export { resilientFetch } from './src/http/resilient_fetch.js';
|
|
@@ -7,6 +7,7 @@ import { type AuthorizationRequest, type CompleteAuthorizationParams, type Compl
|
|
|
7
7
|
import type { ResolvedClientConfig } from '../src/define_config.js';
|
|
8
8
|
import { type OidcEndpoints } from '../src/discovery.js';
|
|
9
9
|
import { type IdTokenClaims, type ValidateIdTokenOptions } from '../src/id_token.js';
|
|
10
|
+
import type { ResolverFactory } from '../src/resolvers/factory.js';
|
|
10
11
|
import type { TokenSet } from '../src/types.js';
|
|
11
12
|
/** Deps injetáveis (relógio/fetch) para testar `maybeRefresh` sem rede/tempo real. */
|
|
12
13
|
export interface RefreshDeps {
|
|
@@ -161,7 +162,13 @@ export declare class AuthkitClientManager {
|
|
|
161
162
|
* sobrevive à sessão que a parqueou.
|
|
162
163
|
*/
|
|
163
164
|
startSession(ctx: HttpContext, tokenSet: TokenSet): void;
|
|
164
|
-
|
|
165
|
+
/**
|
|
166
|
+
* The request's {@link Authenticator}. `options.resolver` replaces the configured
|
|
167
|
+
* resolver for this authenticator only (e.g. a guard for bearer-token API routes).
|
|
168
|
+
*/
|
|
169
|
+
createAuthenticator(ctx: HttpContext, options?: {
|
|
170
|
+
resolver?: ResolverFactory;
|
|
171
|
+
}): Promise<Authenticator>;
|
|
165
172
|
}
|
|
166
173
|
export default class AuthkitClientProvider {
|
|
167
174
|
protected app: ApplicationService;
|
|
@@ -41,17 +41,29 @@ export class AuthkitClientManager {
|
|
|
41
41
|
this.deps = deps;
|
|
42
42
|
this.#logger = deps.logger ?? { warn: () => { } };
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
#extraResolvers = new WeakMap();
|
|
45
|
+
#buildResolver(factory) {
|
|
46
|
+
return factory.resolver({
|
|
47
|
+
issuer: this.config.issuer,
|
|
48
|
+
clientId: this.config.clientId,
|
|
49
|
+
clientSecret: this.config.clientSecret,
|
|
50
|
+
sessionKey: this.config.sessionKey,
|
|
51
|
+
globalRolesClaim: this.config.globalRolesClaim,
|
|
52
|
+
endpoints: () => this.endpoints(),
|
|
53
|
+
logger: this.#logger,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async #getResolver(factory) {
|
|
57
|
+
if (factory && factory !== this.config.resolverFactory) {
|
|
58
|
+
let resolver = this.#extraResolvers.get(factory);
|
|
59
|
+
if (!resolver) {
|
|
60
|
+
resolver = this.#buildResolver(factory);
|
|
61
|
+
this.#extraResolvers.set(factory, resolver);
|
|
62
|
+
}
|
|
63
|
+
return resolver;
|
|
64
|
+
}
|
|
45
65
|
if (!this.#resolverCache) {
|
|
46
|
-
this.#resolverCache = await this.config.resolverFactory
|
|
47
|
-
issuer: this.config.issuer,
|
|
48
|
-
clientId: this.config.clientId,
|
|
49
|
-
clientSecret: this.config.clientSecret,
|
|
50
|
-
sessionKey: this.config.sessionKey,
|
|
51
|
-
globalRolesClaim: this.config.globalRolesClaim,
|
|
52
|
-
endpoints: () => this.endpoints(),
|
|
53
|
-
logger: this.#logger,
|
|
54
|
-
});
|
|
66
|
+
this.#resolverCache = await this.#buildResolver(this.config.resolverFactory);
|
|
55
67
|
}
|
|
56
68
|
return this.#resolverCache;
|
|
57
69
|
}
|
|
@@ -445,8 +457,12 @@ export class AuthkitClientManager {
|
|
|
445
457
|
session.forget(this.#impersonatorKey);
|
|
446
458
|
session.put(this.config.sessionKey, tokenSet);
|
|
447
459
|
}
|
|
448
|
-
|
|
449
|
-
|
|
460
|
+
/**
|
|
461
|
+
* The request's {@link Authenticator}. `options.resolver` replaces the configured
|
|
462
|
+
* resolver for this authenticator only (e.g. a guard for bearer-token API routes).
|
|
463
|
+
*/
|
|
464
|
+
async createAuthenticator(ctx, options = {}) {
|
|
465
|
+
const resolver = await this.#getResolver(options.resolver);
|
|
450
466
|
const sessionKey = this.config.sessionKey;
|
|
451
467
|
return new Authenticator(ctx, {
|
|
452
468
|
resolver,
|
|
@@ -13,9 +13,8 @@ export interface AuthenticatorDeps {
|
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Autenticador do request. O parâmetro `TUser` é o tipo do usuário do app que `resolveUser`
|
|
16
|
-
* devolve — o app o fixa
|
|
17
|
-
*
|
|
18
|
-
* mantém o comportamento anterior (nada a mudar em quem não augmenta).
|
|
16
|
+
* devolve — o app o fixa pela interface `AuthkitUserType`, e aí `getUser()` passa a devolver
|
|
17
|
+
* `AppUser | null` em todo call-site, sem cast. Default `unknown`.
|
|
19
18
|
*/
|
|
20
19
|
export declare class Authenticator<TUser = unknown> {
|
|
21
20
|
#private;
|
|
@@ -26,6 +25,8 @@ export declare class Authenticator<TUser = unknown> {
|
|
|
26
25
|
get identity(): Identity | null;
|
|
27
26
|
authenticate(): Promise<Identity>;
|
|
28
27
|
check(): Promise<boolean>;
|
|
28
|
+
/** `true` when the app configured `resolveUser` (e.g. `lucidMirror`) for this client. */
|
|
29
|
+
get resolvesUser(): boolean;
|
|
29
30
|
hasGlobalRole(role: string): boolean;
|
|
30
31
|
getUser(): Promise<TUser | null>;
|
|
31
32
|
/**
|
|
@@ -2,9 +2,8 @@ import { AUTHKIT_METRICS, NoopRecorder } from '@adonis-agora/authkit-core';
|
|
|
2
2
|
import { populateContext } from './observability/context_bridge.js';
|
|
3
3
|
/**
|
|
4
4
|
* Autenticador do request. O parâmetro `TUser` é o tipo do usuário do app que `resolveUser`
|
|
5
|
-
* devolve — o app o fixa
|
|
6
|
-
*
|
|
7
|
-
* mantém o comportamento anterior (nada a mudar em quem não augmenta).
|
|
5
|
+
* devolve — o app o fixa pela interface `AuthkitUserType`, e aí `getUser()` passa a devolver
|
|
6
|
+
* `AppUser | null` em todo call-site, sem cast. Default `unknown`.
|
|
8
7
|
*/
|
|
9
8
|
export class Authenticator {
|
|
10
9
|
ctx;
|
|
@@ -53,6 +52,10 @@ export class Authenticator {
|
|
|
53
52
|
async check() {
|
|
54
53
|
return (await this.getIdentity()) !== null;
|
|
55
54
|
}
|
|
55
|
+
/** `true` when the app configured `resolveUser` (e.g. `lucidMirror`) for this client. */
|
|
56
|
+
get resolvesUser() {
|
|
57
|
+
return typeof this.deps.resolveUser === 'function';
|
|
58
|
+
}
|
|
56
59
|
hasGlobalRole(role) {
|
|
57
60
|
return this.#identity?.globalRoles.includes(role) ?? false;
|
|
58
61
|
}
|
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Identity } from '@adonis-agora/authkit-core';
|
|
2
|
+
import { type symbols } from '@adonisjs/auth';
|
|
2
3
|
import type { AuthClientResponse, GuardConfigProvider } from '@adonisjs/auth/types';
|
|
3
|
-
import type { SessionUserProviderContract } from '@adonisjs/auth/types/session';
|
|
4
|
+
import type { SessionGuardUser, SessionUserProviderContract } from '@adonisjs/auth/types/session';
|
|
4
5
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
5
6
|
import type { ConfigProvider } from '@adonisjs/core/types';
|
|
6
7
|
import type { Authenticator } from '../authenticator.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* (ver {@link authkitClientGuard}) para que `getUserOrFail()` — que o contrato
|
|
10
|
-
* exige SÍNCRONO — possa lançá-lo sem um `await import()`.
|
|
11
|
-
*/
|
|
12
|
-
export type UnauthorizedAccessConstructor = new (message: string, options: {
|
|
13
|
-
guardDriverName: string;
|
|
14
|
-
redirectTo?: string;
|
|
15
|
-
}) => Error;
|
|
8
|
+
import type { ResolverFactory } from '../resolvers/factory.js';
|
|
9
|
+
import { type AuthkitUser } from './request_authenticator.js';
|
|
16
10
|
/** O tipo de usuário REAL por trás de um user provider do `@adonisjs/auth`. */
|
|
17
11
|
type RealUser<UserProvider> = UserProvider extends SessionUserProviderContract<infer User> ? User : never;
|
|
18
12
|
/**
|
|
19
|
-
*
|
|
13
|
+
* User provider que pode montar o usuário direto da `Identity` (sem banco). É o
|
|
14
|
+
* que {@link authkitIdentityProvider} devolve; o guard o usa antes de `findById`.
|
|
15
|
+
*/
|
|
16
|
+
export interface IdentityUserProviderContract<User> extends SessionUserProviderContract<User> {
|
|
17
|
+
fromIdentity(identity: Identity): Promise<SessionGuardUser<User> | null>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Guard de `@adonisjs/auth` apoiado na sessão do `@adonis-agora/authkit-client`.
|
|
20
21
|
*
|
|
21
22
|
* A fonte da verdade de "quem está logado" é o `SessionResolver` do authkit
|
|
22
|
-
* (JWT
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* (sessão OIDC, bearer JWT, opaco/introspecção ou PAT, conforme
|
|
24
|
+
* `config/authkit_client.ts` ou a opção `resolver` do guard). O guard não lê a
|
|
25
|
+
* sessão por conta própria.
|
|
26
|
+
*
|
|
27
|
+
* O usuário vem, nesta ordem: do `resolveUser` de `config/authkit_client.ts`
|
|
28
|
+
* (ex.: `lucidMirror`, que cria a linha local no primeiro login; é a MESMA
|
|
29
|
+
* instância de `ctx.authkit.getUser()`), do provider montando o usuário da
|
|
30
|
+
* `Identity` ({@link authkitIdentityProvider}), ou de `provider.findById(sub)`.
|
|
27
31
|
*
|
|
28
32
|
* Consequência: `middleware.auth()`, `ctx.auth.user`, `auth.check()` e o Bouncer
|
|
29
33
|
* passam a funcionar sobre uma sessão do authkit.
|
|
@@ -36,20 +40,21 @@ export declare class AuthkitClientGuard<UserProvider extends SessionUserProvider
|
|
|
36
40
|
authenticationAttempted: boolean;
|
|
37
41
|
isAuthenticated: boolean;
|
|
38
42
|
user?: RealUser<UserProvider>;
|
|
39
|
-
constructor(_name: string, ctx: HttpContext, userProvider: UserProvider,
|
|
43
|
+
constructor(_name: string, ctx: HttpContext, userProvider: UserProvider, options?: {
|
|
44
|
+
resolver?: ResolverFactory;
|
|
45
|
+
});
|
|
40
46
|
/**
|
|
41
|
-
* O {@link Authenticator} do authkit
|
|
47
|
+
* O {@link Authenticator} do authkit que este guard usou (a `Identity` crua,
|
|
42
48
|
* `hasGlobalRole`, `toSharedProps`). Só existe depois de uma tentativa de
|
|
43
49
|
* autenticação; use `ctx.authkit` (via `authkit_context_middleware`) quando
|
|
44
50
|
* precisar dele em rota pública.
|
|
45
51
|
*/
|
|
46
|
-
get authkit(): Authenticator | undefined;
|
|
52
|
+
get authkit(): Authenticator<AuthkitUser> | undefined;
|
|
47
53
|
/**
|
|
48
|
-
* Autentica a request
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* válida mas não existe usuário local correspondente (fail-closed).
|
|
54
|
+
* Autentica a request. Lança `E_UNAUTHORIZED_ACCESS` (o MESMO erro dos guards
|
|
55
|
+
* nativos, então o `middleware.auth()` e o handler de exceção do host tratam
|
|
56
|
+
* igual) quando não há sessão OU quando a sessão é válida mas nenhuma fonte
|
|
57
|
+
* devolve usuário (fail-closed).
|
|
53
58
|
*/
|
|
54
59
|
authenticate(): Promise<RealUser<UserProvider>>;
|
|
55
60
|
/** Igual aos guards nativos: `authenticate()` sem lançar. */
|
|
@@ -71,7 +76,8 @@ export declare class AuthkitClientGuard<UserProvider extends SessionUserProvider
|
|
|
71
76
|
* ```ts
|
|
72
77
|
* import { defineConfig } from '@adonisjs/auth'
|
|
73
78
|
* import { sessionUserProvider } from '@adonisjs/auth/session'
|
|
74
|
-
* import { authkitClientGuard } from '@adonis-agora/authkit-client'
|
|
79
|
+
* import { authkitClientGuard, authkitIdentityProvider } from '@adonis-agora/authkit-client/auth'
|
|
80
|
+
* import { resolvers } from '@adonis-agora/authkit-client'
|
|
75
81
|
*
|
|
76
82
|
* export default defineConfig({
|
|
77
83
|
* default: 'web',
|
|
@@ -79,22 +85,31 @@ export declare class AuthkitClientGuard<UserProvider extends SessionUserProvider
|
|
|
79
85
|
* web: authkitClientGuard({
|
|
80
86
|
* provider: sessionUserProvider({ model: () => import('#models/user') }),
|
|
81
87
|
* }),
|
|
88
|
+
* // API routes authenticated by a bearer access token.
|
|
89
|
+
* api: authkitClientGuard({
|
|
90
|
+
* provider: authkitIdentityProvider(),
|
|
91
|
+
* resolver: resolvers.jwt({ tokenSource: 'bearer' }),
|
|
92
|
+
* }),
|
|
82
93
|
* },
|
|
83
94
|
* })
|
|
84
95
|
* ```
|
|
85
96
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* Precedência com `resolveUser` de `config/authkit_client.ts`: são superfícies
|
|
92
|
-
* SEPARADAS e ambas continuam valendo. `ctx.auth.user` (framework) vem SEMPRE do
|
|
93
|
-
* `provider`; `ctx.authkit.getUser()` (authkit) continua vindo do `resolveUser`.
|
|
94
|
-
* O guard nunca chama `resolveUser`. Com o guard configurado, o normal é apagar
|
|
95
|
-
* o `resolveUser` e deixar só o `provider`.
|
|
97
|
+
* - `provider`: any session user provider. Its `findById` receives `identity.userId`
|
|
98
|
+
* (the `sub` claim). With `resolveUser` in `config/authkit_client.ts`, the guard
|
|
99
|
+
* uses that user first and the provider is the fallback.
|
|
100
|
+
* - `resolver`: replaces the resolver of `config/authkit_client.ts` for this guard
|
|
101
|
+
* only (bearer JWT, opaque, PAT). Without it the guard uses the configured one.
|
|
96
102
|
*/
|
|
97
103
|
export declare function authkitClientGuard<UserProvider extends SessionUserProviderContract<unknown>>(config: {
|
|
98
104
|
provider: UserProvider | ConfigProvider<UserProvider>;
|
|
105
|
+
resolver?: ResolverFactory;
|
|
99
106
|
}): GuardConfigProvider<(ctx: HttpContext) => AuthkitClientGuard<UserProvider>>;
|
|
107
|
+
/**
|
|
108
|
+
* User provider with no database: `ctx.auth.user` is the AuthKit {@link Identity}
|
|
109
|
+
* of the request (`userId`, `email`, `globalRoles`, `profile`, the raw claims).
|
|
110
|
+
* Use it when the app keeps no local user table, or for API guards that only need
|
|
111
|
+
* the token's subject. When `config/authkit_client.ts` sets `resolveUser`, the
|
|
112
|
+
* guard uses that user instead.
|
|
113
|
+
*/
|
|
114
|
+
export declare function authkitIdentityProvider(): IdentityUserProviderContract<Identity>;
|
|
100
115
|
export {};
|