@adonis-agora/authkit-client 0.10.1 → 0.12.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 +8 -13
- package/build/providers/authkit_client_provider.d.ts +1 -1
- package/build/providers/authkit_client_provider.js +1 -2
- package/build/src/authenticator.d.ts +12 -12
- package/build/src/authenticator.js +14 -31
- package/build/src/define_config.d.ts +2 -4
- package/build/src/define_config.js +1 -8
- package/build/src/middleware/auth_middleware.d.ts +2 -18
- package/build/src/middleware/auth_middleware.js +3 -27
- package/build/src/register_oidc_client.d.ts +5 -6
- package/build/src/register_oidc_client.js +5 -11
- package/build/stubs/config/authkit_client.stub +1 -1
- package/package.json +2 -2
- package/stubs/config/authkit_client.stub +1 -1
package/README.md
CHANGED
|
@@ -26,10 +26,14 @@ router.post('/auth/logout', [OidcSessionController, 'logout'])
|
|
|
26
26
|
```ts
|
|
27
27
|
const identity = await ctx.auth.getIdentity() // claims OIDC validadas (ou null)
|
|
28
28
|
const user = await ctx.auth.getUser() // model do app (via resolveUser)
|
|
29
|
-
ctx.auth.hasGlobalRole('ADMIN') // síncrono, das claims
|
|
30
|
-
await ctx.auth.hasAppRole('COORDINATOR') // via resolveAppRoles
|
|
29
|
+
ctx.auth.hasGlobalRole('ADMIN') // síncrono, das claims (roles do token)
|
|
31
30
|
```
|
|
32
31
|
|
|
32
|
+
> **Autorização por app-role saiu do AuthKit.** O AuthKit só autentica e conhece as `globalRoles` do
|
|
33
|
+
> token. Roles do app (numa tabela do domínio) e permissões são do `@adonis-agora/authz` — configure
|
|
34
|
+
> o seam `resolveRoles` lá. Guard de rota por role vai pra authz/Bouncer, não para o `authkit_middleware`
|
|
35
|
+
> (que só exige login).
|
|
36
|
+
|
|
33
37
|
## Resolução de sessão
|
|
34
38
|
`resolvers.jwt({ tokenSource })` valida o **ID token** (JWT) por JWKS:
|
|
35
39
|
- `tokenSource: 'session'` (default) — lê o token set do `@adonisjs/session`.
|
|
@@ -47,10 +51,6 @@ resolveUser: async (identity) => {
|
|
|
47
51
|
// FK cross-schema: app.users.auth_user_id -> auth.users.id
|
|
48
52
|
return AppUser.query().where('authUserId', identity.userId).firstOrFail()
|
|
49
53
|
},
|
|
50
|
-
resolveAppRoles: async (identity) => {
|
|
51
|
-
const u = await AppUser.findByOrFail('authUserId', identity.userId)
|
|
52
|
-
return u.related('roles').query().then((rs) => rs.map((r) => r.name))
|
|
53
|
-
},
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
### Bancos separados
|
|
@@ -69,13 +69,8 @@ import { createUserinfoResolver } from '@adonis-agora/authkit-client'
|
|
|
69
69
|
resolveUser: createUserinfoResolver({ issuer: env.get('AUTHKIT_ISSUER') }),
|
|
70
70
|
// ou: createUserinfoResolver({ userinfoEndpoint: 'https://idp/me' })
|
|
71
71
|
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
resolveAppRoles: async (identity) => {
|
|
75
|
-
const roles = await AppRole.query().where('authUserId', identity.userId)
|
|
76
|
-
return roles.map((r) => r.name)
|
|
77
|
-
},
|
|
78
|
-
```
|
|
72
|
+
Roles do app (a tabela de roles do próprio domínio) não passam mais pelo AuthKit — configure o seam
|
|
73
|
+
`resolveRoles` do `@adonis-agora/authz`, que as une às `globalRoles` do token para decidir permissão.
|
|
79
74
|
|
|
80
75
|
## Ordem de middleware (importante)
|
|
81
76
|
O middleware do `@adonisjs/session` DEVE rodar ANTES do `authkit_middleware` no stack `router`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ApplicationService } from "@adonisjs/core/types";
|
|
2
1
|
import type { HttpContext } from "@adonisjs/core/http";
|
|
2
|
+
import type { ApplicationService } from "@adonisjs/core/types";
|
|
3
3
|
import { Authenticator } from "../src/authenticator.js";
|
|
4
4
|
import type { ResolvedClientConfig } from "../src/define_config.js";
|
|
5
5
|
/** Deps injetáveis (relógio/fetch) para testar `maybeRefresh` sem rede/tempo real. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { configProvider } from "@adonisjs/core";
|
|
2
2
|
import { RuntimeException } from "@adonisjs/core/exceptions";
|
|
3
3
|
import { Authenticator } from "../src/authenticator.js";
|
|
4
|
-
import { refreshTokens, exchangeToken } from "../src/oidc_login.js";
|
|
5
4
|
import { validateLogoutToken } from "../src/backchannel_logout.js";
|
|
5
|
+
import { exchangeToken, refreshTokens } from "../src/oidc_login.js";
|
|
6
6
|
/** Margem (ms) antes do `expiresAt` em que o access token é renovado proativamente. */
|
|
7
7
|
const REFRESH_SKEW_MS = 60_000;
|
|
8
8
|
export class AuthkitClientManager {
|
|
@@ -161,7 +161,6 @@ export class AuthkitClientManager {
|
|
|
161
161
|
return new Authenticator(ctx, {
|
|
162
162
|
resolver,
|
|
163
163
|
resolveUser: this.config.resolveUser,
|
|
164
|
-
resolveAppRoles: this.config.resolveAppRoles,
|
|
165
164
|
getAccessToken: () => {
|
|
166
165
|
const tokenSet = ctx.session?.get(sessionKey);
|
|
167
166
|
return tokenSet?.accessToken;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { HttpContext } from "@adonisjs/core/http";
|
|
2
1
|
import type { Identity, SessionResolver } from "@adonis-agora/authkit-core";
|
|
3
2
|
import { type MetricsRecorder } from "@adonis-agora/authkit-core";
|
|
3
|
+
import type { HttpContext } from "@adonisjs/core/http";
|
|
4
4
|
/** Contexto opcional repassado a `resolveUser` (extensão backward-compatible). */
|
|
5
5
|
export interface ResolveUserContext {
|
|
6
6
|
accessToken?: string;
|
|
@@ -8,11 +8,16 @@ export interface ResolveUserContext {
|
|
|
8
8
|
export interface AuthenticatorDeps {
|
|
9
9
|
resolver: SessionResolver;
|
|
10
10
|
resolveUser?: (identity: Identity, context: ResolveUserContext) => Promise<unknown>;
|
|
11
|
-
resolveAppRoles?: (identity: Identity) => Promise<string[]>;
|
|
12
11
|
/** lê o access token do token set da sessão (opcional; usado por resolvers userinfo) */
|
|
13
12
|
getAccessToken?: () => string | null | undefined;
|
|
14
13
|
}
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Autenticador do request. O parâmetro `TUser` é o tipo do usuário do app que `resolveUser`
|
|
16
|
+
* devolve — o app o fixa via augmentation de `HttpContext.auth` (`auth: Authenticator<AppUser>`),
|
|
17
|
+
* e aí `getUser()` passa a devolver `AppUser | null` em todo call-site, sem cast. Default `unknown`
|
|
18
|
+
* mantém o comportamento anterior (nada a mudar em quem não augmenta).
|
|
19
|
+
*/
|
|
20
|
+
export declare class Authenticator<TUser = unknown> {
|
|
16
21
|
#private;
|
|
17
22
|
private ctx;
|
|
18
23
|
private deps;
|
|
@@ -22,20 +27,15 @@ export declare class Authenticator {
|
|
|
22
27
|
authenticate(): Promise<Identity>;
|
|
23
28
|
check(): Promise<boolean>;
|
|
24
29
|
hasGlobalRole(role: string): boolean;
|
|
25
|
-
|
|
26
|
-
getUser(): Promise<unknown>;
|
|
27
|
-
/** Resolve (e cacheia) os app roles da identidade corrente. [] sem identidade/resolver. */
|
|
28
|
-
getAppRoles(): Promise<string[]>;
|
|
30
|
+
getUser(): Promise<TUser | null>;
|
|
29
31
|
/**
|
|
30
32
|
* Monta o objeto pronto para compartilhar com o frontend (ex.: Inertia share),
|
|
31
33
|
* casando com o `AuthSharedProps` que o `@adonis-agora/authkit-react` consome.
|
|
32
|
-
* `
|
|
33
|
-
*
|
|
34
|
+
* Retorna `null` quando não há sessão. Autorização (roles de app/permissões)
|
|
35
|
+
* é responsabilidade do `@adonis-agora/authz` — o AuthKit só autentica.
|
|
34
36
|
*/
|
|
35
37
|
toSharedProps(): Promise<{
|
|
36
|
-
user:
|
|
38
|
+
user: TUser | null;
|
|
37
39
|
globalRoles: string[];
|
|
38
|
-
appRoles: string[];
|
|
39
|
-
abilities: string[];
|
|
40
40
|
} | null>;
|
|
41
41
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { AUTHKIT_METRICS, NoopRecorder
|
|
1
|
+
import { AUTHKIT_METRICS, NoopRecorder } from "@adonis-agora/authkit-core";
|
|
2
2
|
import { populateContext } from "./observability/context_bridge.js";
|
|
3
|
+
/**
|
|
4
|
+
* Autenticador do request. O parâmetro `TUser` é o tipo do usuário do app que `resolveUser`
|
|
5
|
+
* devolve — o app o fixa via augmentation de `HttpContext.auth` (`auth: Authenticator<AppUser>`),
|
|
6
|
+
* e aí `getUser()` passa a devolver `AppUser | null` em todo call-site, sem cast. Default `unknown`
|
|
7
|
+
* mantém o comportamento anterior (nada a mudar em quem não augmenta).
|
|
8
|
+
*/
|
|
3
9
|
export class Authenticator {
|
|
4
10
|
ctx;
|
|
5
11
|
deps;
|
|
6
12
|
#identity = null;
|
|
7
13
|
#resolved = false;
|
|
8
|
-
#user;
|
|
14
|
+
#user = null;
|
|
9
15
|
#userResolved = false;
|
|
10
|
-
#appRoles = null;
|
|
11
16
|
#recorder;
|
|
12
17
|
constructor(ctx, deps, recorder = new NoopRecorder()) {
|
|
13
18
|
this.ctx = ctx;
|
|
@@ -51,21 +56,15 @@ export class Authenticator {
|
|
|
51
56
|
hasGlobalRole(role) {
|
|
52
57
|
return this.#identity?.globalRoles.includes(role) ?? false;
|
|
53
58
|
}
|
|
54
|
-
async hasAppRole(role) {
|
|
55
|
-
const identity = await this.getIdentity();
|
|
56
|
-
if (!identity || !this.deps.resolveAppRoles)
|
|
57
|
-
return false;
|
|
58
|
-
if (this.#appRoles === null)
|
|
59
|
-
this.#appRoles = await this.deps.resolveAppRoles(identity);
|
|
60
|
-
return this.#appRoles.includes(role);
|
|
61
|
-
}
|
|
62
59
|
async getUser() {
|
|
63
60
|
if (this.#userResolved)
|
|
64
61
|
return this.#user;
|
|
65
62
|
const identity = await this.getIdentity();
|
|
66
63
|
if (identity && this.deps.resolveUser) {
|
|
67
64
|
const accessToken = this.deps.getAccessToken?.() ?? undefined;
|
|
68
|
-
|
|
65
|
+
// `resolveUser` é tipado `Promise<unknown>` (a lib não conhece o model do app); o `TUser`
|
|
66
|
+
// é a asserção do consumidor, feita UMA vez aqui em vez de em cada call-site de `getUser()`.
|
|
67
|
+
this.#user = (await this.deps.resolveUser(identity, { accessToken }));
|
|
69
68
|
}
|
|
70
69
|
else {
|
|
71
70
|
this.#user = null;
|
|
@@ -73,33 +72,17 @@ export class Authenticator {
|
|
|
73
72
|
this.#userResolved = true;
|
|
74
73
|
return this.#user;
|
|
75
74
|
}
|
|
76
|
-
/** Resolve (e cacheia) os app roles da identidade corrente. [] sem identidade/resolver. */
|
|
77
|
-
async getAppRoles() {
|
|
78
|
-
const identity = await this.getIdentity();
|
|
79
|
-
if (!identity || !this.deps.resolveAppRoles)
|
|
80
|
-
return [];
|
|
81
|
-
if (this.#appRoles === null)
|
|
82
|
-
this.#appRoles = await this.deps.resolveAppRoles(identity);
|
|
83
|
-
return this.#appRoles;
|
|
84
|
-
}
|
|
85
75
|
/**
|
|
86
76
|
* Monta o objeto pronto para compartilhar com o frontend (ex.: Inertia share),
|
|
87
77
|
* casando com o `AuthSharedProps` que o `@adonis-agora/authkit-react` consome.
|
|
88
|
-
* `
|
|
89
|
-
*
|
|
78
|
+
* Retorna `null` quando não há sessão. Autorização (roles de app/permissões)
|
|
79
|
+
* é responsabilidade do `@adonis-agora/authz` — o AuthKit só autentica.
|
|
90
80
|
*/
|
|
91
81
|
async toSharedProps() {
|
|
92
82
|
const identity = await this.getIdentity();
|
|
93
83
|
if (!identity)
|
|
94
84
|
return null;
|
|
95
85
|
const user = await this.getUser();
|
|
96
|
-
|
|
97
|
-
const appRoles = await this.getAppRoles();
|
|
98
|
-
return {
|
|
99
|
-
user,
|
|
100
|
-
globalRoles,
|
|
101
|
-
appRoles,
|
|
102
|
-
abilities: [...appRoles, ...globalRoles],
|
|
103
|
-
};
|
|
86
|
+
return { user, globalRoles: identity.globalRoles ?? [] };
|
|
104
87
|
}
|
|
105
88
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Identity } from "@adonis-agora/authkit-core";
|
|
2
|
-
import { resolvers, type ResolverFactory } from "./resolvers/factory.js";
|
|
3
2
|
import type { SessionIndex } from "./backchannel_logout.js";
|
|
4
|
-
import type { RevocationStore } from "./revocation/revocation_store.js";
|
|
5
3
|
import type { ResiliencePolicy } from "./http/resilient_fetch.js";
|
|
4
|
+
import { type ResolverFactory, resolvers } from "./resolvers/factory.js";
|
|
5
|
+
import type { RevocationStore } from "./revocation/revocation_store.js";
|
|
6
6
|
export { resolvers };
|
|
7
7
|
/** Callback invocado quando um logout_token válido chega via Back-Channel Logout. */
|
|
8
8
|
export type BackchannelLogoutCallback = (event: {
|
|
@@ -30,7 +30,6 @@ export interface ClientConfigInput {
|
|
|
30
30
|
resolveUser?: (identity: Identity, context: {
|
|
31
31
|
accessToken?: string;
|
|
32
32
|
}) => Promise<unknown>;
|
|
33
|
-
resolveAppRoles?: (identity: Identity) => Promise<string[]>;
|
|
34
33
|
sessionKey?: string;
|
|
35
34
|
scopes?: string[];
|
|
36
35
|
globalRolesClaim?: string;
|
|
@@ -66,7 +65,6 @@ export interface ResolvedClientConfig {
|
|
|
66
65
|
resolveUser?: (identity: Identity, context: {
|
|
67
66
|
accessToken?: string;
|
|
68
67
|
}) => Promise<unknown>;
|
|
69
|
-
resolveAppRoles?: (identity: Identity) => Promise<string[]>;
|
|
70
68
|
sessionKey: string;
|
|
71
69
|
scopes: string[];
|
|
72
70
|
globalRolesClaim: string;
|
|
@@ -22,15 +22,8 @@ export function defineConfig(config) {
|
|
|
22
22
|
redirectUri: config.redirectUri,
|
|
23
23
|
resolverFactory: config.resolver,
|
|
24
24
|
resolveUser: config.resolveUser,
|
|
25
|
-
resolveAppRoles: config.resolveAppRoles,
|
|
26
25
|
sessionKey: config.sessionKey ?? "authkit",
|
|
27
|
-
scopes: config.scopes ?? [
|
|
28
|
-
"openid",
|
|
29
|
-
"profile",
|
|
30
|
-
"email",
|
|
31
|
-
"offline_access",
|
|
32
|
-
"roles",
|
|
33
|
-
],
|
|
26
|
+
scopes: config.scopes ?? ["openid", "profile", "email", "offline_access", "roles"],
|
|
34
27
|
globalRolesClaim: config.globalRolesClaim ?? "roles",
|
|
35
28
|
onBackchannelLogout,
|
|
36
29
|
sessionIndex: config.sessionIndex,
|
|
@@ -1,28 +1,12 @@
|
|
|
1
1
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
2
|
import type { NextFn } from '@adonisjs/core/types/http';
|
|
3
3
|
export interface AuthMiddlewareOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Papéis aceitos. Passa se o usuário tiver QUALQUER um deles como role global
|
|
6
|
-
* (claim do IdP) OU como app role (`resolveAppRoles`). Vazio/ausente → só exige login.
|
|
7
|
-
*/
|
|
8
|
-
roles?: string[];
|
|
9
4
|
/** Para onde redirecionar quando não autenticado. Default: `/auth/login`. */
|
|
10
5
|
redirectTo?: string;
|
|
11
|
-
/** Para onde redirecionar quando autenticado mas sem o papel exigido. Default: `/unauthorized`. */
|
|
12
|
-
unauthorizedRedirect?: string;
|
|
13
6
|
}
|
|
14
7
|
/**
|
|
15
|
-
* Middleware de autenticação
|
|
16
|
-
*
|
|
17
|
-
* consumidor reescrevia à mão.
|
|
18
|
-
*
|
|
19
|
-
* ```ts
|
|
20
|
-
* // start/kernel.ts
|
|
21
|
-
* export const middleware = router.named({
|
|
22
|
-
* auth: () => import('@adonis-agora/authkit-client/auth_middleware'),
|
|
23
|
-
* })
|
|
24
|
-
* // uso: .use(middleware.auth({ roles: ['ADVISOR'], redirectTo: '/auth/login' }))
|
|
25
|
-
* ```
|
|
8
|
+
* Middleware de autenticação do AuthKit: exige sessão válida. Autorização por role saiu daqui — use
|
|
9
|
+
* `@adonis-agora/authz` (ou Bouncer) na rota. AuthKit só autentica.
|
|
26
10
|
*/
|
|
27
11
|
export default class AuthkitAuthMiddleware {
|
|
28
12
|
handle(ctx: HttpContext, next: NextFn, options?: AuthMiddlewareOptions): Promise<any>;
|
|
@@ -1,36 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Middleware de autenticação
|
|
3
|
-
*
|
|
4
|
-
* consumidor reescrevia à mão.
|
|
5
|
-
*
|
|
6
|
-
* ```ts
|
|
7
|
-
* // start/kernel.ts
|
|
8
|
-
* export const middleware = router.named({
|
|
9
|
-
* auth: () => import('@adonis-agora/authkit-client/auth_middleware'),
|
|
10
|
-
* })
|
|
11
|
-
* // uso: .use(middleware.auth({ roles: ['ADVISOR'], redirectTo: '/auth/login' }))
|
|
12
|
-
* ```
|
|
2
|
+
* Middleware de autenticação do AuthKit: exige sessão válida. Autorização por role saiu daqui — use
|
|
3
|
+
* `@adonis-agora/authz` (ou Bouncer) na rota. AuthKit só autentica.
|
|
13
4
|
*/
|
|
14
5
|
export default class AuthkitAuthMiddleware {
|
|
15
6
|
async handle(ctx, next, options = {}) {
|
|
16
|
-
const redirectTo = options.redirectTo ?? '/auth/login';
|
|
17
7
|
const ok = await ctx.auth.check();
|
|
18
8
|
if (!ok)
|
|
19
|
-
return ctx.response.redirect(redirectTo);
|
|
20
|
-
if (options.roles?.length) {
|
|
21
|
-
const isGlobal = options.roles.some((r) => ctx.auth.hasGlobalRole(r));
|
|
22
|
-
let allowed = isGlobal;
|
|
23
|
-
if (!allowed) {
|
|
24
|
-
for (const r of options.roles) {
|
|
25
|
-
if (await ctx.auth.hasAppRole(r)) {
|
|
26
|
-
allowed = true;
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (!allowed)
|
|
32
|
-
return ctx.response.redirect(options.unauthorizedRedirect ?? '/unauthorized');
|
|
33
|
-
}
|
|
9
|
+
return ctx.response.redirect(options.redirectTo ?? '/auth/login');
|
|
34
10
|
return next();
|
|
35
11
|
}
|
|
36
12
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type { Router } from '@adonisjs/core/http';
|
|
2
|
-
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
1
|
import type { Identity } from '@adonis-agora/authkit-core';
|
|
2
|
+
import type { HttpContext, Router } from '@adonisjs/core/http';
|
|
4
3
|
/**
|
|
5
4
|
* Mapa role → rota de redirect pós-login. `byGlobalRole` checa as roles globais do
|
|
6
|
-
* IdP (claim
|
|
7
|
-
*
|
|
5
|
+
* IdP (claim do token) — a primeira correspondência vence; sem match → `default`.
|
|
6
|
+
* Autorização por app-role/permissão saiu do AuthKit — use `@adonis-agora/authz`
|
|
7
|
+
* (ex.: no `afterLogin`) se o destino depender disso.
|
|
8
8
|
*/
|
|
9
9
|
export interface PostLoginRedirects {
|
|
10
10
|
byGlobalRole?: Record<string, string>;
|
|
11
|
-
byAppRole?: Record<string, string>;
|
|
12
11
|
default?: string;
|
|
13
12
|
}
|
|
14
13
|
export interface RegisterOidcClientOptions {
|
|
@@ -43,7 +42,7 @@ export interface RegisterOidcClientOptions {
|
|
|
43
42
|
* import { registerOidcClient } from '@adonis-agora/authkit-client'
|
|
44
43
|
* registerOidcClient(router, {
|
|
45
44
|
* loginMiddleware: middleware.guest(),
|
|
46
|
-
* redirects: { byGlobalRole: { ADMIN: '/admin' },
|
|
45
|
+
* redirects: { byGlobalRole: { ADMIN: '/admin' }, default: '/' },
|
|
47
46
|
* })
|
|
48
47
|
* ```
|
|
49
48
|
*/
|
|
@@ -10,7 +10,7 @@ import { buildAuthorizeUrl, buildEndSessionUrl, exchangeCode, generatePkce } fro
|
|
|
10
10
|
* import { registerOidcClient } from '@adonis-agora/authkit-client'
|
|
11
11
|
* registerOidcClient(router, {
|
|
12
12
|
* loginMiddleware: middleware.guest(),
|
|
13
|
-
* redirects: { byGlobalRole: { ADMIN: '/admin' },
|
|
13
|
+
* redirects: { byGlobalRole: { ADMIN: '/admin' }, default: '/' },
|
|
14
14
|
* })
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
@@ -72,8 +72,8 @@ export function registerOidcClient(router, options = {}) {
|
|
|
72
72
|
const hook = await options.afterLogin?.(ctx, identity);
|
|
73
73
|
if (typeof hook === 'string')
|
|
74
74
|
return ctx.response.redirect(hook);
|
|
75
|
-
// 2) Redirect por
|
|
76
|
-
const dest =
|
|
75
|
+
// 2) Redirect por role global (do token).
|
|
76
|
+
const dest = resolveDestination(identity, options.redirects);
|
|
77
77
|
return ctx.response.redirect(dest);
|
|
78
78
|
})
|
|
79
79
|
.as('auth.callback');
|
|
@@ -104,8 +104,8 @@ export function registerOidcClient(router, options = {}) {
|
|
|
104
104
|
.as('auth.backchannel_logout');
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
/** Resolve o destino pós-login a partir do mapa de redirects por
|
|
108
|
-
|
|
107
|
+
/** Resolve o destino pós-login a partir do mapa de redirects por role global (do token). */
|
|
108
|
+
function resolveDestination(identity, redirects) {
|
|
109
109
|
if (!redirects)
|
|
110
110
|
return '/';
|
|
111
111
|
const globalRoles = identity?.globalRoles ?? [];
|
|
@@ -115,11 +115,5 @@ async function resolveDestination(authenticator, identity, redirects) {
|
|
|
115
115
|
return path;
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
-
if (redirects.byAppRole) {
|
|
119
|
-
for (const [role, path] of Object.entries(redirects.byAppRole)) {
|
|
120
|
-
if (await authenticator.hasAppRole(role))
|
|
121
|
-
return path;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
118
|
return redirects.default ?? '/';
|
|
125
119
|
}
|
|
@@ -11,7 +11,7 @@ const authkitClientConfig = defineConfig({
|
|
|
11
11
|
redirectUri: env.get('AUTHKIT_REDIRECT_URI'),
|
|
12
12
|
resolver: resolvers.jwt({ tokenSource: 'session' }),
|
|
13
13
|
// resolveUser: async (identity) => User.find(identity.userId),
|
|
14
|
-
//
|
|
14
|
+
// Roles do app? Configure `resolveRoles` no @adonis-agora/authz (não aqui — o AuthKit só autentica).
|
|
15
15
|
})
|
|
16
16
|
|
|
17
17
|
export default authkitClientConfig
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/authkit-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "AdonisJS OIDC/OAuth2 relying-party (client) toolkit: session-based authentication against an OpenID Connect identity provider, with JWT/PAT user resolvers and OpenTelemetry metrics.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dudousxd",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"ioredis-mock": "^8.9.0",
|
|
83
83
|
"@types/node": "25.9.1",
|
|
84
84
|
"typescript": "6.0.3",
|
|
85
|
-
"@adonis-agora/authkit-server": "0.
|
|
85
|
+
"@adonis-agora/authkit-server": "0.36.0"
|
|
86
86
|
},
|
|
87
87
|
"scripts": {
|
|
88
88
|
"build": "tsc && node -e \"require('node:fs').cpSync('stubs','build/stubs',{recursive:true,filter:(s)=>!s.endsWith('.ts')})\"",
|
|
@@ -11,7 +11,7 @@ const authkitClientConfig = defineConfig({
|
|
|
11
11
|
redirectUri: env.get('AUTHKIT_REDIRECT_URI'),
|
|
12
12
|
resolver: resolvers.jwt({ tokenSource: 'session' }),
|
|
13
13
|
// resolveUser: async (identity) => User.find(identity.userId),
|
|
14
|
-
//
|
|
14
|
+
// Roles do app? Configure `resolveRoles` no @adonis-agora/authz (não aqui — o AuthKit só autentica).
|
|
15
15
|
})
|
|
16
16
|
|
|
17
17
|
export default authkitClientConfig
|