@authyon/server 0.2.0-beta.0 → 0.2.0-beta.11
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/LICENSE +21 -0
- package/README.md +321 -2
- package/dist/authorization-CeP_GqT7.d.cts +129 -0
- package/dist/authorization-D_A6L9Ie.d.ts +129 -0
- package/dist/authyonServerClient-D-DxoL30.d.cts +898 -0
- package/dist/authyonServerClient-D-DxoL30.d.ts +898 -0
- package/dist/bff.cjs +1126 -0
- package/dist/bff.d.cts +142 -0
- package/dist/bff.d.ts +142 -0
- package/dist/bff.js +791 -0
- package/dist/browser.cjs +21 -0
- package/dist/browser.d.cts +2 -0
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +4 -0
- package/dist/chunk-56H66KLG.js +323 -0
- package/dist/chunk-77J4NURK.js +13 -0
- package/dist/chunk-NKWQMYB3.js +485 -0
- package/dist/index.cjs +1276 -73
- package/dist/index.d.cts +58 -506
- package/dist/index.d.ts +58 -506
- package/dist/index.js +516 -85
- package/dist/next.cjs +328 -0
- package/dist/next.d.cts +27 -0
- package/dist/next.d.ts +27 -0
- package/dist/next.js +48 -0
- package/package.json +81 -36
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MutualPay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,6 +2,67 @@
|
|
|
2
2
|
|
|
3
3
|
SDK server-side para o [Authyon](https://authyon.com) — administração de ambiente/tenants (OAuth client-credentials) e verificação de access token (publishable key). Nunca importe este pacote em código de browser.
|
|
4
4
|
|
|
5
|
+
## Instalação da beta
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install --save-exact @authyon/server@0.2.0-beta.4
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Durante a beta, `npm install @authyon/server@beta` acompanha o prerelease mais recente. Fixar a versão exata é recomendado para builds reproduzíveis.
|
|
12
|
+
|
|
13
|
+
O pacote possui uma condição de exportação `browser` que falha imediatamente se um bundler tentar incluí-lo no frontend. URLs remotas exigem HTTPS e cada request tem timeout padrão de 15 segundos, configurável por `timeoutMs`.
|
|
14
|
+
|
|
15
|
+
O client aceita o mesmo contrato `HttpAdapter` de `@authyon/auth`, útil para tracing, mocks e stacks HTTP próprias:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createClient, FetchHttpAdapter } from "@authyon/server";
|
|
19
|
+
|
|
20
|
+
const authyon = createClient({
|
|
21
|
+
envKey: process.env.AUTHYON_ENV_KEY,
|
|
22
|
+
httpAdapter: new FetchHttpAdapter(globalThis.fetch),
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Quando houver credenciais, adapter e observabilidade, o builder mantém cada etapa explícita:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
const authyon = new AuthyonServerClientBuilder()
|
|
30
|
+
.withEnvironmentKey(process.env.AUTHYON_ENV_KEY)
|
|
31
|
+
.withEnvironmentCredentials(process.env.AUTHYON_CLIENT_ID, process.env.AUTHYON_CLIENT_SECRET)
|
|
32
|
+
.withHttpLogger(httpLogger)
|
|
33
|
+
.build();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
O client também aceita `httpLogger: { enabled, logger? }`. A configuração é mutável, portanto `enabled` pode ser alterado em runtime. Os eventos não incluem headers, bodies nem valores de query string, e erros do logger nunca interrompem requests.
|
|
37
|
+
|
|
38
|
+
`AuthyonError`, `ErrorCodes` e `error.interpret()` também são exportados por este pacote. Use `category`, `action`, `retryable`, `retryAfter` e `requestId` para implementar middleware e observabilidade sem comparar mensagens humanas.
|
|
39
|
+
|
|
40
|
+
## Sessão web no BFF (código ainda não publicado)
|
|
41
|
+
|
|
42
|
+
O novo entrypoint `@authyon/server/bff` oferece login, 2FA, sessão HttpOnly,
|
|
43
|
+
renovação sob demanda, logout e troca de organização. Os tokens ficam no servidor,
|
|
44
|
+
com adaptador Redis cifrado e compare-and-swap para concorrência entre instâncias.
|
|
45
|
+
Reutiliza os endpoints existentes e o cliente de validação server-side.
|
|
46
|
+
|
|
47
|
+
Veja [contrato e integração](../../docs/bff.md) e [análise/plano](../../docs/bff-session-plan.md).
|
|
48
|
+
O entrypoint padrão e `@authyon/server/next` mantêm seu comportamento anterior;
|
|
49
|
+
a adoção do transporte BFF pelo consumidor é explícita.
|
|
50
|
+
|
|
51
|
+
## Validação local com JWKS
|
|
52
|
+
|
|
53
|
+
Para APIs de alto volume, crie uma vez um verificador com discovery automático:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const verifier = await authyon.createJwksTokenVerifier({
|
|
57
|
+
audience: process.env.AUTHYON_AUDIENCE,
|
|
58
|
+
algorithms: ["RS256"],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const { claims } = await verifier.verifyAccessToken(token);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Ele valida assinatura, algoritmo, issuer, audience, tipo e claims temporais, com cache e rotação de chaves. Use `verification: "jwks"` em `authorizeRequest()` ou `createExpressAuthorizationMiddleware()`. Para ações sensíveis que precisam detectar revogação imediatamente, mantenha o padrão `validate`.
|
|
65
|
+
|
|
5
66
|
## Instalação
|
|
6
67
|
|
|
7
68
|
```bash
|
|
@@ -35,7 +96,7 @@ const { active, sub } = await authyon.introspect(token); // leve
|
|
|
35
96
|
const { user, organization } = await authyon.validate(token); // recomendado — cross-checa revogação
|
|
36
97
|
```
|
|
37
98
|
|
|
38
|
-
Veja [`examples/
|
|
99
|
+
Veja [`examples/tokenVerification.ts`](./examples/tokenVerification.ts) para um middleware completo.
|
|
39
100
|
|
|
40
101
|
## Administração de ambiente (`environment.*`)
|
|
41
102
|
|
|
@@ -43,6 +104,11 @@ Usa as credenciais de ambiente (`clientId`/`clientSecret`) automaticamente — o
|
|
|
43
104
|
|
|
44
105
|
```ts
|
|
45
106
|
const org = await authyon.environment.tenants.create({ name: "Acme", slug: "acme" });
|
|
107
|
+
const tenants = await authyon.environment.tenants.list({
|
|
108
|
+
search: "acme",
|
|
109
|
+
skip: 0,
|
|
110
|
+
take: 20,
|
|
111
|
+
});
|
|
46
112
|
await authyon.environment.tenants.members.add(org.id, userId, ["owner"]);
|
|
47
113
|
await authyon.environment.tenants.members.assignRole(org.id, userId, "billing-admin");
|
|
48
114
|
await authyon.environment.tenants.members.remove(org.id, userId);
|
|
@@ -52,10 +118,79 @@ await authyon.environment.roles.create({ name: "support", permissions: ["tickets
|
|
|
52
118
|
await authyon.environment.audit.list({ take: 50 });
|
|
53
119
|
```
|
|
54
120
|
|
|
55
|
-
Veja [`examples/
|
|
121
|
+
Veja [`examples/organizationMembership.ts`](./examples/organizationMembership.ts) para o fluxo completo, incluindo as rotas de backend que o `@authyon/auth` chamaria.
|
|
56
122
|
|
|
57
123
|
Namespaces disponíveis: `environment.users`, `environment.tenants` (com `.members` e `.roles` aninhados), `environment.roles`, `environment.permissions`, `environment.audit`.
|
|
58
124
|
|
|
125
|
+
## Autorização local
|
|
126
|
+
|
|
127
|
+
`createAuthyonAbility()` transforma o resultado de `validate()`, `introspect()` ou qualquer objeto com `permissions`, `roles` e `scope` em verificações locais no estilo CASL:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { createAuthyonAbility } from "@authyon/server";
|
|
131
|
+
|
|
132
|
+
const identity = await authyon.validate(token);
|
|
133
|
+
const ability = createAuthyonAbility(identity);
|
|
134
|
+
|
|
135
|
+
if (!ability.can("read", "reports")) {
|
|
136
|
+
throw new Error("Forbidden");
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Regras condicionais e de domínio podem ser acrescentadas no segundo argumento. O mecanismo é deny-by-default e não substitui a validação do token nem a autorização aplicada pela API.
|
|
141
|
+
|
|
142
|
+
### Next.js Server Side
|
|
143
|
+
|
|
144
|
+
Em Route Handlers, Server Actions ou SSR, mantenha `@authyon/server` em um módulo marcado com `server-only`:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import "server-only";
|
|
148
|
+
import { createAuthorizationErrorResponse, authorizeRequest, createClient } from "@authyon/server";
|
|
149
|
+
|
|
150
|
+
const authyon = createClient({
|
|
151
|
+
envKey: process.env.AUTHYON_ENV_KEY,
|
|
152
|
+
clientId: process.env.AUTHYON_CLIENT_ID,
|
|
153
|
+
clientSecret: process.env.AUTHYON_CLIENT_SECRET,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
export async function GET(request: Request) {
|
|
157
|
+
try {
|
|
158
|
+
const auth = await authorizeRequest(authyon, request, {
|
|
159
|
+
requirement: { action: "read", subject: "reports" },
|
|
160
|
+
});
|
|
161
|
+
return Response.json({ userId: auth.userId });
|
|
162
|
+
} catch (error) {
|
|
163
|
+
return createAuthorizationErrorResponse(error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Express e frameworks Connect
|
|
169
|
+
|
|
170
|
+
O middleware não adiciona Express como dependência e disponibiliza `request.authyon`:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { createExpressAuthorizationMiddleware } from "@authyon/server";
|
|
174
|
+
|
|
175
|
+
app.get(
|
|
176
|
+
"/reports",
|
|
177
|
+
createExpressAuthorizationMiddleware(authyon, {
|
|
178
|
+
requirement: { action: "read", subject: "reports" },
|
|
179
|
+
}),
|
|
180
|
+
(request, response) => response.json({ userId: request.authyon.userId }),
|
|
181
|
+
);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Use `authorizeToken()` em NestJS, Fastify, Hono, tRPC ou integrações próprias. `passErrorsToNext: true` encaminha erros ao middleware de erros do Express.
|
|
185
|
+
|
|
186
|
+
### IP real do cliente
|
|
187
|
+
|
|
188
|
+
Chamadas server-side propagam `clientIp` em `X-Forwarded-For`. O middleware Express usa o IP do socket por padrão; atrás de um proxy confiável, configure o próprio Express e use `trustProxy: true`. Para Next.js, passe `resolveClientIp` ao `authorizeRequest()` lendo somente headers que sua infraestrutura sobrescreve. Não confie cegamente em `X-Forwarded-For` recebido da internet.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
await authyon.validate(token, { clientIp: "203.0.113.10" });
|
|
192
|
+
```
|
|
193
|
+
|
|
59
194
|
## Administração por tenant (`tenant()`)
|
|
60
195
|
|
|
61
196
|
As credenciais de um tenant são próprias dele — use `authyon.tenant(credentials)` para obter um cliente escopado, que minta e cacheia seu próprio token via `POST /tenant/oauth/token`:
|
|
@@ -63,11 +198,30 @@ As credenciais de um tenant são próprias dele — use `authyon.tenant(credenti
|
|
|
63
198
|
```ts
|
|
64
199
|
const acme = authyon.tenant({ clientId: acmeClientId, clientSecret: acmeClientSecret });
|
|
65
200
|
|
|
201
|
+
const identity = await acme.validate(); // POST /tenant/auth/validate
|
|
66
202
|
await acme.members.list();
|
|
67
203
|
await acme.members.add(userId, ["member"]);
|
|
68
204
|
await acme.roles.create({ name: "viewer", permissions: ["reports:read"] });
|
|
69
205
|
```
|
|
70
206
|
|
|
207
|
+
`validate()` confirma o bearer tenant-client atual e devolve `tenantId`,
|
|
208
|
+
`credentialId`, `clientId` e as permissões vigentes da credencial. A resposta
|
|
209
|
+
vem do backend depois da checagem de revogação, rotação de secret e tenant
|
|
210
|
+
desabilitado; não confie só na claim `scope` quando precisar do estado atual.
|
|
211
|
+
|
|
212
|
+
Em middleware, quando você já recebeu o bearer tenant-client na requisição, não
|
|
213
|
+
use `clientId`/`clientSecret`; valide o token diretamente:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
const validation = await authyon.tenantAuth.validate(tenantBearerToken, {
|
|
217
|
+
clientIp: "203.0.113.10",
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (!validation.valid) {
|
|
221
|
+
// responda 401
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
71
225
|
## Descoberta
|
|
72
226
|
|
|
73
227
|
```ts
|
|
@@ -82,3 +236,168 @@ npm install
|
|
|
82
236
|
npm run build # dist/ (ESM + CJS + .d.ts via tsup)
|
|
83
237
|
npm run typecheck
|
|
84
238
|
```
|
|
239
|
+
|
|
240
|
+
### Emitir uma credencial de integração do tenant
|
|
241
|
+
|
|
242
|
+
Requer o endpoint `POST /env/tenants/{tenantId}/credentials` no backend Authyon
|
|
243
|
+
e uma credencial de ambiente com `authyon:credentials:manage`.
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
const credential = await authyon.environment.tenants.credentials.create(tenantId, {
|
|
247
|
+
description: "ERP financeiro",
|
|
248
|
+
permissions: ["monkeypay:transactions:read"],
|
|
249
|
+
});
|
|
250
|
+
// Entregue clientId e clientSecret ao operador: o segredo só é retornado uma vez.
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
As permissões são explícitas e pertencem à integração; não há concessão automática de acesso total.
|
|
254
|
+
|
|
255
|
+
## Credenciais: paginação, busca e detalhe
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
const page = await authyon.environment.tenants.credentials.listPage(tenantId, {
|
|
259
|
+
search: "transactions",
|
|
260
|
+
skip: 0,
|
|
261
|
+
take: 25,
|
|
262
|
+
});
|
|
263
|
+
const detail = await authyon.environment.tenants.credentials.get(tenantId, credentialId);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
`page` contém `data`, `perPage` (número da página, começando em 1), `pageSize`,
|
|
267
|
+
`total`, `pages`, `hasNext` e `hasPrev`. `take` aceita 1–100 (padrão da API: 25),
|
|
268
|
+
`skip` é um deslocamento a partir de zero e `search` aceita até 200 caracteres.
|
|
269
|
+
A API pesquisa clientId, descrição e permissões antes de paginar.
|
|
270
|
+
|
|
271
|
+
O método existente `credentials.list(tenantId)` continua retornando um array:
|
|
272
|
+
aceita a resposta antiga e percorre todas as páginas da API atual. Prefira
|
|
273
|
+
`listPage` para tabelas ou grandes volumes. A leitura completa detecta páginas
|
|
274
|
+
sobrepostas/sem progresso e tem limite de 1.000 páginas; alterações concorrentes
|
|
275
|
+
podem exigir reiniciar a leitura, pois a API usa paginação por deslocamento.
|
|
276
|
+
|
|
277
|
+
O detalhe contém `permissions`, `createdBy` (`id`, `type`, `displayName`),
|
|
278
|
+
`createdAt`, `updatedAt`, `lastUsedAt`, `secretRotatedAt`, `revokedAt`, `isActive`,
|
|
279
|
+
`expiresAt`, `ageSeconds`, `lifetimeSeconds` e `accessTokenLifetimeSeconds`.
|
|
280
|
+
Listagem e detalhe projetam somente campos permitidos, sem segredo ou hash.
|
|
281
|
+
Registros antigos podem ter autor e atualização nulos. A credencial atual não
|
|
282
|
+
expira automaticamente: `expiresAt` é nulo; `lifetimeSeconds` é nulo enquanto
|
|
283
|
+
ativa e representa sua duração até a revogação quando revogada. A validade do
|
|
284
|
+
access token é separada (atualmente 1.800 segundos).
|
|
285
|
+
|
|
286
|
+
## Scopes e ciclo de vida das credenciais
|
|
287
|
+
|
|
288
|
+
`scopes` é um alias de `permissions` na criação/atualização. Use somente um dos
|
|
289
|
+
campos, com uma lista explícita no formato `namespace:resource:action`. O SDK
|
|
290
|
+
normaliza espaços, caixa e duplicatas; não concede permissões automaticamente.
|
|
291
|
+
A API continua responsável pela autorização. O contrato antigo
|
|
292
|
+
`CreateTenantCredentialInput` e chamadas usando `permissions` são preservados.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
const issued = await authyon.environment.tenants.credentials.create(tenantId, {
|
|
296
|
+
description: "ERP financeiro",
|
|
297
|
+
scopes: ["monkeypay:transactions:read"],
|
|
298
|
+
});
|
|
299
|
+
// Armazene issued.clientSecret no gerenciador de segredos. Não registre a resposta.
|
|
300
|
+
|
|
301
|
+
// Token de um usuário da plataforma. Pode ser um callback assíncrono que
|
|
302
|
+
// consulta a sessão atual a cada operação, sem compartilhar tokens entre usuários.
|
|
303
|
+
const platform = authyon.platform(() => getCurrentPlatformAccessToken());
|
|
304
|
+
const scope = { workspaceId, environmentId, tenantId };
|
|
305
|
+
await platform.credentials.list(scope, { search: "ERP", skip: 0, take: 25 });
|
|
306
|
+
await platform.credentials.get(scope, credentialId);
|
|
307
|
+
await platform.credentials.create(scope, {
|
|
308
|
+
description: "Leitura",
|
|
309
|
+
permissions: ["monkeypay:transactions:read"],
|
|
310
|
+
});
|
|
311
|
+
await platform.credentials.updateScopes(scope, credentialId, ["monkeypay:transactions:read"]);
|
|
312
|
+
await platform.credentials.updatePermissions(scope, credentialId, {
|
|
313
|
+
permissions: ["monkeypay:transactions:read"],
|
|
314
|
+
});
|
|
315
|
+
const rotated = await platform.credentials.rotate(scope, credentialId);
|
|
316
|
+
await platform.credentials.revoke(scope, credentialId);
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Omita `tenantId` de `scope` para gerenciar credenciais do ambiente. Criação e
|
|
320
|
+
rotação retornam o segredo uma única vez. Alterar permissões substitui a lista
|
|
321
|
+
completa. A concessão OAuth continua recebendo apenas `clientId`/`clientSecret`;
|
|
322
|
+
`scopes` não é um parâmetro de redução de acesso na troca de token.
|
|
323
|
+
|
|
324
|
+
Os endpoints `/env` oferecem criação, listagem e detalhe de credenciais do
|
|
325
|
+
tenant. Atualização, rotação e revogação usam os endpoints `/platform` com
|
|
326
|
+
**token de usuário da plataforma**, sem fallback para credenciais de máquina.
|
|
327
|
+
Se a API exigir `auth.step_up_required`, solicite confirmação de senha na sua
|
|
328
|
+
aplicação e chame explicitamente `platform.auth.stepUp(currentPassword)` antes
|
|
329
|
+
de tentar a operação novamente. O SDK não repete mutações automaticamente.
|
|
330
|
+
|
|
331
|
+
## Convites e remoção de acesso
|
|
332
|
+
|
|
333
|
+
### Workspace
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
const team = await platform.workspaces.team(workspaceId);
|
|
337
|
+
const invite = await platform.workspaces.members.invite(workspaceId, {
|
|
338
|
+
email: "pessoa@example.com",
|
|
339
|
+
role: "auditor", // ou "admin", sempre explícito
|
|
340
|
+
});
|
|
341
|
+
// invite.acceptUrl é sensível: entregue somente à pessoa convidada.
|
|
342
|
+
await platform.workspaces.invites.revoke(workspaceId, invite.inviteId);
|
|
343
|
+
await platform.workspaces.members.changeRole(workspaceId, memberId, "auditor");
|
|
344
|
+
await platform.workspaces.members.remove(workspaceId, memberId);
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
`memberId` vem de `team.members`, e não é `userId`. O proprietário tem
|
|
348
|
+
`memberId: null`; a API protege proprietário, acesso próprio e permissões de
|
|
349
|
+
administração. `workspaces.invites.create` é um alias para `members.invite`.
|
|
350
|
+
Convites pendentes ficam em `team.pendingInvites`; revogar o convite invalida o
|
|
351
|
+
link. Para retirar acesso após aceite, remova o membro.
|
|
352
|
+
|
|
353
|
+
A pessoa convidada pode consultar/aceitar o convite por uma rota server-side:
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
const preview = await authyon.workspaceInvites.preview(invitationToken);
|
|
357
|
+
await authyon.workspaceInvites.accept(invitationToken, {
|
|
358
|
+
name: "Pessoa",
|
|
359
|
+
password: registrationPassword,
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Esses métodos não enviam token de usuário nem credenciais de ambiente. Nome e
|
|
364
|
+
senha são necessários quando `preview.requiresRegistration` for verdadeiro;
|
|
365
|
+
para uma conta existente, use `accept(invitationToken)`.
|
|
366
|
+
|
|
367
|
+
### Tenant
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
const user = authyon.user(() => getCurrentUserAccessToken());
|
|
371
|
+
await user.tenants.members.invite(tenantId, {
|
|
372
|
+
email: "pessoa@example.com",
|
|
373
|
+
roles: ["reader"],
|
|
374
|
+
});
|
|
375
|
+
await user.tenants.members.list(tenantId, { skip: 0, take: 25 });
|
|
376
|
+
await user.tenants.members.remove(tenantId, userId);
|
|
377
|
+
|
|
378
|
+
// Administração por máquina: adicione uma conta existente por ID.
|
|
379
|
+
await authyon.environment.tenants.members.add(tenantId, userId, ["reader"]);
|
|
380
|
+
await authyon.environment.tenants.members.remove(tenantId, userId);
|
|
381
|
+
// Os métodos tenant(credentials).members.add/remove existentes continuam disponíveis.
|
|
382
|
+
// Administração com sessão da plataforma:
|
|
383
|
+
await platform.tenants.members.add(scope, userId, ["reader"]);
|
|
384
|
+
await platform.tenants.members.remove(scope, userId);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Configure `envKey` para `user()`. O convite de tenant associa um usuário já
|
|
388
|
+
cadastrado no ambiente pelo e-mail; não cria conta e não envia link por e-mail.
|
|
389
|
+
A API exige associação/permissões do usuário chamador. No browser, os métodos
|
|
390
|
+
existentes `@authyon/auth` → `organization.members.invite/remove` continuam
|
|
391
|
+
atendendo esse fluxo. Nunca importe `@authyon/server` no browser.
|
|
392
|
+
|
|
393
|
+
## Compatibilidade e segurança do transporte
|
|
394
|
+
|
|
395
|
+
Os métodos anteriores permanecem disponíveis. As novas chamadas exigem os
|
|
396
|
+
endpoints atuais da API e a migração `ClientCredentialMetadata` para metadados
|
|
397
|
+
persistidos. O SDK não altera o banco nem publica uma versão automaticamente.
|
|
398
|
+
|
|
399
|
+
O adaptador Fetch agora rejeita redirects para evitar reenviar tokens/segredos;
|
|
400
|
+
configure a URL final HTTPS da API. Adaptadores HTTP personalizados devem manter
|
|
401
|
+
essa política. Os logs internos omitem corpos/headers e ocultam tokens de
|
|
402
|
+
convite presentes no caminho. A auditoria de operações permanece na API; o SDK
|
|
403
|
+
não gera eventos duplicados ao consultar listagens de auditoria.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { x as JwksTokenVerifier, A as AuthyonServerClient } from './authyonServerClient-D-DxoL30.cjs';
|
|
2
|
+
|
|
3
|
+
type AbilitySubject = string | Record<string, unknown>;
|
|
4
|
+
type AbilityConditions = Record<string, unknown>;
|
|
5
|
+
interface AbilityRule {
|
|
6
|
+
action: string | string[];
|
|
7
|
+
subject: string | string[];
|
|
8
|
+
inverted?: boolean;
|
|
9
|
+
conditions?: AbilityConditions;
|
|
10
|
+
fields?: string[];
|
|
11
|
+
reason?: string;
|
|
12
|
+
}
|
|
13
|
+
interface AuthyonPermissionSource {
|
|
14
|
+
permissions?: readonly string[] | null;
|
|
15
|
+
roles?: readonly string[] | null;
|
|
16
|
+
/** OAuth scopes are also accepted and interpreted as Authyon permissions. */
|
|
17
|
+
scope?: string | null;
|
|
18
|
+
}
|
|
19
|
+
/** A declarative group of permissions evaluated against an Authyon permission source. */
|
|
20
|
+
interface PermissionGroup {
|
|
21
|
+
/** Permissions that must all be granted. */
|
|
22
|
+
allOf?: readonly string[];
|
|
23
|
+
/** Permissions where at least one must be granted. */
|
|
24
|
+
anyOf?: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
interface AuthyonAbilityOptions {
|
|
27
|
+
rules?: readonly AbilityRule[];
|
|
28
|
+
/** Rules added when the source contains a matching Authyon role. */
|
|
29
|
+
roleRules?: Readonly<Record<string, readonly AbilityRule[]>>;
|
|
30
|
+
roles?: readonly string[] | null;
|
|
31
|
+
detectSubjectType?: (subject: Record<string, unknown>) => string;
|
|
32
|
+
}
|
|
33
|
+
type AbilityEvent = "updated";
|
|
34
|
+
type AbilityListener = (rules: readonly AbilityRule[]) => void;
|
|
35
|
+
/** Isomorphic, deny-by-default authorization engine backed by Authyon permissions. */
|
|
36
|
+
declare class AuthyonAbility {
|
|
37
|
+
private readonly detectSubjectType;
|
|
38
|
+
private currentRules;
|
|
39
|
+
private readonly listeners;
|
|
40
|
+
constructor(rules?: readonly AbilityRule[], detectSubjectType?: (subject: Record<string, unknown>) => string);
|
|
41
|
+
get rules(): readonly AbilityRule[];
|
|
42
|
+
can(action: string, subject: AbilitySubject, field?: string): boolean;
|
|
43
|
+
cannot(action: string, subject: AbilitySubject, field?: string): boolean;
|
|
44
|
+
rulesFor(action: string, subject: string): readonly AbilityRule[];
|
|
45
|
+
update(rules: readonly AbilityRule[]): void;
|
|
46
|
+
on(event: AbilityEvent, listener: AbilityListener): () => void;
|
|
47
|
+
}
|
|
48
|
+
/** Fluent rule builder with CASL-like `can` and `cannot` methods. */
|
|
49
|
+
declare class AuthyonAbilityBuilder {
|
|
50
|
+
readonly rules: AbilityRule[];
|
|
51
|
+
can(action: string | string[], subject: string | string[], conditions?: AbilityConditions, fields?: string[]): this;
|
|
52
|
+
cannot(action: string | string[], subject: string | string[], conditions?: AbilityConditions, fields?: string[], reason?: string): this;
|
|
53
|
+
build(options?: Pick<AuthyonAbilityOptions, "detectSubjectType">): AuthyonAbility;
|
|
54
|
+
}
|
|
55
|
+
/** Creates an ability from Authyon permissions, OAuth scope and optional role rules. */
|
|
56
|
+
declare function createAuthyonAbility(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AuthyonAbility;
|
|
57
|
+
/** Checks an Authyon permission string using the same wildcard rules as an ability. */
|
|
58
|
+
declare function hasPermission(source: AuthyonPermissionSource | readonly string[], requiredPermission: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Checks a permission group using Authyon's standard permission and wildcard rules.
|
|
61
|
+
* Empty or omitted groups do not add authorization requirements.
|
|
62
|
+
*/
|
|
63
|
+
declare function hasPermissionGroup(source: AuthyonPermissionSource | readonly string[], group: PermissionGroup): boolean;
|
|
64
|
+
/** Converts Authyon's `subject:action` permission strings to authorization rules. */
|
|
65
|
+
declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
|
|
66
|
+
|
|
67
|
+
interface AbilityRequirement {
|
|
68
|
+
action: string;
|
|
69
|
+
subject: AbilitySubject;
|
|
70
|
+
field?: string;
|
|
71
|
+
}
|
|
72
|
+
interface AuthorizationContext {
|
|
73
|
+
token: string;
|
|
74
|
+
userId?: string;
|
|
75
|
+
ability: AuthyonAbility;
|
|
76
|
+
permissions: readonly string[];
|
|
77
|
+
roles: readonly string[];
|
|
78
|
+
}
|
|
79
|
+
interface AuthorizationOptions extends AuthyonAbilityOptions {
|
|
80
|
+
requirement?: AbilityRequirement;
|
|
81
|
+
/** Uses database-backed validation by default; introspection can be selected when desired. */
|
|
82
|
+
verification?: "validate" | "introspect" | "jwks";
|
|
83
|
+
/** Required for local JWKS verification. Create once and reuse its key cache. */
|
|
84
|
+
jwksVerifier?: JwksTokenVerifier;
|
|
85
|
+
/** Real end-user IP, obtained from trusted application infrastructure. */
|
|
86
|
+
clientIp?: string;
|
|
87
|
+
}
|
|
88
|
+
declare class AuthorizationError extends Error {
|
|
89
|
+
readonly status: 401 | 403 | 503;
|
|
90
|
+
readonly code: "authorization.unauthenticated" | "authorization.forbidden" | "authorization.unavailable";
|
|
91
|
+
constructor(status: 401 | 403 | 503, code: "authorization.unauthenticated" | "authorization.forbidden" | "authorization.unavailable", message: string);
|
|
92
|
+
}
|
|
93
|
+
/** Validates a bearer token and creates an Authyon-backed ability for server runtimes. */
|
|
94
|
+
declare function authorizeToken(client: AuthyonServerClient, token: string, options?: AuthorizationOptions): Promise<AuthorizationContext>;
|
|
95
|
+
/** Works with Web Request objects used by Next.js Route Handlers and other Fetch runtimes. */
|
|
96
|
+
declare function authorizeRequest(client: AuthyonServerClient, request: Pick<Request, "headers">, options?: RequestAuthorizationOptions): Promise<AuthorizationContext>;
|
|
97
|
+
interface RequestAuthorizationOptions extends AuthorizationOptions {
|
|
98
|
+
/** Platform-aware resolver. Only read headers that your trusted proxy overwrites. */
|
|
99
|
+
resolveClientIp?: (request: Pick<Request, "headers">) => string | undefined;
|
|
100
|
+
}
|
|
101
|
+
interface ExpressRequestLike {
|
|
102
|
+
headers: Record<string, string | string[] | undefined>;
|
|
103
|
+
ip?: string;
|
|
104
|
+
socket?: {
|
|
105
|
+
remoteAddress?: string;
|
|
106
|
+
};
|
|
107
|
+
authyon?: AuthorizationContext;
|
|
108
|
+
}
|
|
109
|
+
interface ExpressResponseLike {
|
|
110
|
+
status(code: number): ExpressResponseLike;
|
|
111
|
+
json(body: unknown): unknown;
|
|
112
|
+
}
|
|
113
|
+
type ExpressNext = (error?: unknown) => void;
|
|
114
|
+
interface ExpressAuthorizationMiddlewareOptions extends AuthorizationOptions {
|
|
115
|
+
/** Property assigned to the request. Defaults to `authyon`. */
|
|
116
|
+
requestProperty?: string;
|
|
117
|
+
/** Pass authorization errors to Express error middleware instead of writing a JSON response. */
|
|
118
|
+
passErrorsToNext?: boolean;
|
|
119
|
+
/** Trust Express's proxy-aware `request.ip`. Disabled by default. */
|
|
120
|
+
trustProxy?: boolean;
|
|
121
|
+
/** Overrides all built-in IP resolution; return only a trusted, single client IP. */
|
|
122
|
+
resolveClientIp?: (request: ExpressRequestLike) => string | undefined;
|
|
123
|
+
}
|
|
124
|
+
/** Dependency-free middleware compatible with Express and similar Connect frameworks. */
|
|
125
|
+
declare function createExpressAuthorizationMiddleware(client: AuthyonServerClient, options?: ExpressAuthorizationMiddlewareOptions): (request: ExpressRequestLike & Record<string, unknown>, response: ExpressResponseLike, next: ExpressNext) => Promise<void>;
|
|
126
|
+
/** Converts an authorization error into a Fetch Response suitable for Next.js. */
|
|
127
|
+
declare function createAuthorizationErrorResponse(error: unknown): Response;
|
|
128
|
+
|
|
129
|
+
export { type AbilitySubject as A, type ExpressAuthorizationMiddlewareOptions as E, type PermissionGroup as P, type RequestAuthorizationOptions as R, AuthorizationError as a, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRequirement as e, type AbilityRule as f, type AuthorizationContext as g, type AuthorizationOptions as h, AuthyonAbility as i, AuthyonAbilityBuilder as j, type AuthyonAbilityOptions as k, type AuthyonPermissionSource as l, type ExpressNext as m, type ExpressRequestLike as n, type ExpressResponseLike as o, authorizeRequest as p, authorizeToken as q, createAuthorizationErrorResponse as r, createAuthyonAbility as s, createAuthyonRules as t, createExpressAuthorizationMiddleware as u, hasPermission as v, hasPermissionGroup as w };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { x as JwksTokenVerifier, A as AuthyonServerClient } from './authyonServerClient-D-DxoL30.js';
|
|
2
|
+
|
|
3
|
+
type AbilitySubject = string | Record<string, unknown>;
|
|
4
|
+
type AbilityConditions = Record<string, unknown>;
|
|
5
|
+
interface AbilityRule {
|
|
6
|
+
action: string | string[];
|
|
7
|
+
subject: string | string[];
|
|
8
|
+
inverted?: boolean;
|
|
9
|
+
conditions?: AbilityConditions;
|
|
10
|
+
fields?: string[];
|
|
11
|
+
reason?: string;
|
|
12
|
+
}
|
|
13
|
+
interface AuthyonPermissionSource {
|
|
14
|
+
permissions?: readonly string[] | null;
|
|
15
|
+
roles?: readonly string[] | null;
|
|
16
|
+
/** OAuth scopes are also accepted and interpreted as Authyon permissions. */
|
|
17
|
+
scope?: string | null;
|
|
18
|
+
}
|
|
19
|
+
/** A declarative group of permissions evaluated against an Authyon permission source. */
|
|
20
|
+
interface PermissionGroup {
|
|
21
|
+
/** Permissions that must all be granted. */
|
|
22
|
+
allOf?: readonly string[];
|
|
23
|
+
/** Permissions where at least one must be granted. */
|
|
24
|
+
anyOf?: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
interface AuthyonAbilityOptions {
|
|
27
|
+
rules?: readonly AbilityRule[];
|
|
28
|
+
/** Rules added when the source contains a matching Authyon role. */
|
|
29
|
+
roleRules?: Readonly<Record<string, readonly AbilityRule[]>>;
|
|
30
|
+
roles?: readonly string[] | null;
|
|
31
|
+
detectSubjectType?: (subject: Record<string, unknown>) => string;
|
|
32
|
+
}
|
|
33
|
+
type AbilityEvent = "updated";
|
|
34
|
+
type AbilityListener = (rules: readonly AbilityRule[]) => void;
|
|
35
|
+
/** Isomorphic, deny-by-default authorization engine backed by Authyon permissions. */
|
|
36
|
+
declare class AuthyonAbility {
|
|
37
|
+
private readonly detectSubjectType;
|
|
38
|
+
private currentRules;
|
|
39
|
+
private readonly listeners;
|
|
40
|
+
constructor(rules?: readonly AbilityRule[], detectSubjectType?: (subject: Record<string, unknown>) => string);
|
|
41
|
+
get rules(): readonly AbilityRule[];
|
|
42
|
+
can(action: string, subject: AbilitySubject, field?: string): boolean;
|
|
43
|
+
cannot(action: string, subject: AbilitySubject, field?: string): boolean;
|
|
44
|
+
rulesFor(action: string, subject: string): readonly AbilityRule[];
|
|
45
|
+
update(rules: readonly AbilityRule[]): void;
|
|
46
|
+
on(event: AbilityEvent, listener: AbilityListener): () => void;
|
|
47
|
+
}
|
|
48
|
+
/** Fluent rule builder with CASL-like `can` and `cannot` methods. */
|
|
49
|
+
declare class AuthyonAbilityBuilder {
|
|
50
|
+
readonly rules: AbilityRule[];
|
|
51
|
+
can(action: string | string[], subject: string | string[], conditions?: AbilityConditions, fields?: string[]): this;
|
|
52
|
+
cannot(action: string | string[], subject: string | string[], conditions?: AbilityConditions, fields?: string[], reason?: string): this;
|
|
53
|
+
build(options?: Pick<AuthyonAbilityOptions, "detectSubjectType">): AuthyonAbility;
|
|
54
|
+
}
|
|
55
|
+
/** Creates an ability from Authyon permissions, OAuth scope and optional role rules. */
|
|
56
|
+
declare function createAuthyonAbility(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AuthyonAbility;
|
|
57
|
+
/** Checks an Authyon permission string using the same wildcard rules as an ability. */
|
|
58
|
+
declare function hasPermission(source: AuthyonPermissionSource | readonly string[], requiredPermission: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Checks a permission group using Authyon's standard permission and wildcard rules.
|
|
61
|
+
* Empty or omitted groups do not add authorization requirements.
|
|
62
|
+
*/
|
|
63
|
+
declare function hasPermissionGroup(source: AuthyonPermissionSource | readonly string[], group: PermissionGroup): boolean;
|
|
64
|
+
/** Converts Authyon's `subject:action` permission strings to authorization rules. */
|
|
65
|
+
declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
|
|
66
|
+
|
|
67
|
+
interface AbilityRequirement {
|
|
68
|
+
action: string;
|
|
69
|
+
subject: AbilitySubject;
|
|
70
|
+
field?: string;
|
|
71
|
+
}
|
|
72
|
+
interface AuthorizationContext {
|
|
73
|
+
token: string;
|
|
74
|
+
userId?: string;
|
|
75
|
+
ability: AuthyonAbility;
|
|
76
|
+
permissions: readonly string[];
|
|
77
|
+
roles: readonly string[];
|
|
78
|
+
}
|
|
79
|
+
interface AuthorizationOptions extends AuthyonAbilityOptions {
|
|
80
|
+
requirement?: AbilityRequirement;
|
|
81
|
+
/** Uses database-backed validation by default; introspection can be selected when desired. */
|
|
82
|
+
verification?: "validate" | "introspect" | "jwks";
|
|
83
|
+
/** Required for local JWKS verification. Create once and reuse its key cache. */
|
|
84
|
+
jwksVerifier?: JwksTokenVerifier;
|
|
85
|
+
/** Real end-user IP, obtained from trusted application infrastructure. */
|
|
86
|
+
clientIp?: string;
|
|
87
|
+
}
|
|
88
|
+
declare class AuthorizationError extends Error {
|
|
89
|
+
readonly status: 401 | 403 | 503;
|
|
90
|
+
readonly code: "authorization.unauthenticated" | "authorization.forbidden" | "authorization.unavailable";
|
|
91
|
+
constructor(status: 401 | 403 | 503, code: "authorization.unauthenticated" | "authorization.forbidden" | "authorization.unavailable", message: string);
|
|
92
|
+
}
|
|
93
|
+
/** Validates a bearer token and creates an Authyon-backed ability for server runtimes. */
|
|
94
|
+
declare function authorizeToken(client: AuthyonServerClient, token: string, options?: AuthorizationOptions): Promise<AuthorizationContext>;
|
|
95
|
+
/** Works with Web Request objects used by Next.js Route Handlers and other Fetch runtimes. */
|
|
96
|
+
declare function authorizeRequest(client: AuthyonServerClient, request: Pick<Request, "headers">, options?: RequestAuthorizationOptions): Promise<AuthorizationContext>;
|
|
97
|
+
interface RequestAuthorizationOptions extends AuthorizationOptions {
|
|
98
|
+
/** Platform-aware resolver. Only read headers that your trusted proxy overwrites. */
|
|
99
|
+
resolveClientIp?: (request: Pick<Request, "headers">) => string | undefined;
|
|
100
|
+
}
|
|
101
|
+
interface ExpressRequestLike {
|
|
102
|
+
headers: Record<string, string | string[] | undefined>;
|
|
103
|
+
ip?: string;
|
|
104
|
+
socket?: {
|
|
105
|
+
remoteAddress?: string;
|
|
106
|
+
};
|
|
107
|
+
authyon?: AuthorizationContext;
|
|
108
|
+
}
|
|
109
|
+
interface ExpressResponseLike {
|
|
110
|
+
status(code: number): ExpressResponseLike;
|
|
111
|
+
json(body: unknown): unknown;
|
|
112
|
+
}
|
|
113
|
+
type ExpressNext = (error?: unknown) => void;
|
|
114
|
+
interface ExpressAuthorizationMiddlewareOptions extends AuthorizationOptions {
|
|
115
|
+
/** Property assigned to the request. Defaults to `authyon`. */
|
|
116
|
+
requestProperty?: string;
|
|
117
|
+
/** Pass authorization errors to Express error middleware instead of writing a JSON response. */
|
|
118
|
+
passErrorsToNext?: boolean;
|
|
119
|
+
/** Trust Express's proxy-aware `request.ip`. Disabled by default. */
|
|
120
|
+
trustProxy?: boolean;
|
|
121
|
+
/** Overrides all built-in IP resolution; return only a trusted, single client IP. */
|
|
122
|
+
resolveClientIp?: (request: ExpressRequestLike) => string | undefined;
|
|
123
|
+
}
|
|
124
|
+
/** Dependency-free middleware compatible with Express and similar Connect frameworks. */
|
|
125
|
+
declare function createExpressAuthorizationMiddleware(client: AuthyonServerClient, options?: ExpressAuthorizationMiddlewareOptions): (request: ExpressRequestLike & Record<string, unknown>, response: ExpressResponseLike, next: ExpressNext) => Promise<void>;
|
|
126
|
+
/** Converts an authorization error into a Fetch Response suitable for Next.js. */
|
|
127
|
+
declare function createAuthorizationErrorResponse(error: unknown): Response;
|
|
128
|
+
|
|
129
|
+
export { type AbilitySubject as A, type ExpressAuthorizationMiddlewareOptions as E, type PermissionGroup as P, type RequestAuthorizationOptions as R, AuthorizationError as a, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRequirement as e, type AbilityRule as f, type AuthorizationContext as g, type AuthorizationOptions as h, AuthyonAbility as i, AuthyonAbilityBuilder as j, type AuthyonAbilityOptions as k, type AuthyonPermissionSource as l, type ExpressNext as m, type ExpressRequestLike as n, type ExpressResponseLike as o, authorizeRequest as p, authorizeToken as q, createAuthorizationErrorResponse as r, createAuthyonAbility as s, createAuthyonRules as t, createExpressAuthorizationMiddleware as u, hasPermission as v, hasPermissionGroup as w };
|