@adonis-agora/authkit-client 0.9.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/LICENSE +21 -0
- package/README.md +88 -0
- package/build/commands/configure.d.ts +2 -0
- package/build/commands/configure.js +17 -0
- package/build/index.d.ts +28 -0
- package/build/index.js +15 -0
- package/build/providers/authkit_client_provider.d.ts +61 -0
- package/build/providers/authkit_client_provider.js +187 -0
- package/build/src/authenticator.d.ts +41 -0
- package/build/src/authenticator.js +105 -0
- package/build/src/backchannel_logout.d.ts +77 -0
- package/build/src/backchannel_logout.js +114 -0
- package/build/src/define_config.d.ts +80 -0
- package/build/src/define_config.js +41 -0
- package/build/src/discovery.d.ts +55 -0
- package/build/src/discovery.js +90 -0
- package/build/src/dpop.d.ts +50 -0
- package/build/src/dpop.js +48 -0
- package/build/src/http/resilient_fetch.d.ts +30 -0
- package/build/src/http/resilient_fetch.js +26 -0
- package/build/src/lucid_mirror.d.ts +38 -0
- package/build/src/lucid_mirror.js +32 -0
- package/build/src/middleware/auth_middleware.d.ts +29 -0
- package/build/src/middleware/auth_middleware.js +36 -0
- package/build/src/middleware/authkit_middleware.d.ts +5 -0
- package/build/src/middleware/authkit_middleware.js +9 -0
- package/build/src/middleware/backchannel_revocation_middleware.d.ts +18 -0
- package/build/src/middleware/backchannel_revocation_middleware.js +50 -0
- package/build/src/middleware/silent_auth_middleware.d.ts +13 -0
- package/build/src/middleware/silent_auth_middleware.js +14 -0
- package/build/src/observability/client_metrics.d.ts +10 -0
- package/build/src/observability/client_metrics.js +59 -0
- package/build/src/observability/context_bridge.d.ts +7 -0
- package/build/src/observability/context_bridge.js +49 -0
- package/build/src/oidc_login.d.ts +113 -0
- package/build/src/oidc_login.js +123 -0
- package/build/src/register_oidc_client.d.ts +50 -0
- package/build/src/register_oidc_client.js +125 -0
- package/build/src/resolvers/factory.d.ts +41 -0
- package/build/src/resolvers/factory.js +51 -0
- package/build/src/resolvers/identity.d.ts +30 -0
- package/build/src/resolvers/identity.js +49 -0
- package/build/src/resolvers/jwt_resolver.d.ts +20 -0
- package/build/src/resolvers/jwt_resolver.js +34 -0
- package/build/src/resolvers/opaque_resolver.d.ts +43 -0
- package/build/src/resolvers/opaque_resolver.js +51 -0
- package/build/src/resolvers/pat_resolver.d.ts +18 -0
- package/build/src/resolvers/pat_resolver.js +24 -0
- package/build/src/revocation/revocation_store.d.ts +68 -0
- package/build/src/revocation/revocation_store.js +0 -0
- package/build/src/token_source.d.ts +3 -0
- package/build/src/token_source.js +12 -0
- package/build/src/types.d.ts +6 -0
- package/build/src/types.js +1 -0
- package/build/src/user_resolvers.d.ts +41 -0
- package/build/src/user_resolvers.js +42 -0
- package/build/src/verify_access_token.d.ts +37 -0
- package/build/src/verify_access_token.js +43 -0
- package/build/stubs/.gitkeep +0 -0
- package/build/stubs/config/authkit_client.stub +17 -0
- package/build/stubs/controllers/oidc_session_controller.stub +42 -0
- package/build/stubs/main.d.ts +1 -0
- package/build/stubs/main.js +2 -0
- package/build/types.d.ts +13 -0
- package/build/types.js +1 -0
- package/package.json +92 -0
- package/stubs/.gitkeep +0 -0
- package/stubs/config/authkit_client.stub +17 -0
- package/stubs/controllers/oidc_session_controller.stub +42 -0
- package/stubs/main.ts +2 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converte uma `Identity` (claims OIDC validadas) num objeto de usuário simples.
|
|
3
|
+
* Pode ser usada diretamente como `resolveUser: identityToUser`.
|
|
4
|
+
*/
|
|
5
|
+
export function identityToUser(identity) {
|
|
6
|
+
return {
|
|
7
|
+
id: identity.userId,
|
|
8
|
+
email: identity.email,
|
|
9
|
+
name: identity.profile?.name,
|
|
10
|
+
avatarUrl: identity.profile?.avatarUrl,
|
|
11
|
+
globalRoles: identity.globalRoles,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Fábrica de um `resolveUser` que busca o usuário no endpoint userinfo do IdP
|
|
16
|
+
* usando o access token. Caminho recomendado quando o app precisa de dados
|
|
17
|
+
* além do que o token carrega (topologia de bancos separados).
|
|
18
|
+
*
|
|
19
|
+
* Sem access token (ex.: tokenSource bearer só com ID token), faz fallback
|
|
20
|
+
* para `identityToUser(identity)`.
|
|
21
|
+
*/
|
|
22
|
+
export function createUserinfoResolver(options = {}) {
|
|
23
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
24
|
+
const endpoint = options.userinfoEndpoint ??
|
|
25
|
+
(options.issuer ? `${options.issuer.replace(/\/$/, '')}/me` : undefined);
|
|
26
|
+
return async function resolveUser(identity, context = {}) {
|
|
27
|
+
const accessToken = context.accessToken;
|
|
28
|
+
if (!accessToken || !endpoint) {
|
|
29
|
+
return identityToUser(identity);
|
|
30
|
+
}
|
|
31
|
+
const res = await fetchImpl(endpoint, {
|
|
32
|
+
method: 'GET',
|
|
33
|
+
headers: { authorization: `Bearer ${accessToken}`, accept: 'application/json' },
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
throw new Error(`userinfo falhou: HTTP ${res.status}`);
|
|
37
|
+
}
|
|
38
|
+
const claims = (await res.json());
|
|
39
|
+
// mescla identity (base) com o que veio do userinfo (sobrescreve)
|
|
40
|
+
return { ...identityToUser(identity), ...claims };
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type JWTPayload } from 'jose';
|
|
2
|
+
/**
|
|
3
|
+
* Verificação LOCAL de um JWT Access Token (RFC 9068) emitido pelo AuthKit server
|
|
4
|
+
* com `accessTokens: { format: 'jwt' }`. Valida a assinatura contra o JWKS remoto
|
|
5
|
+
* (cacheado) do IdP — SEM round-trip de introspecção — e impõe as regras do perfil
|
|
6
|
+
* RFC 9068: header `typ: at+jwt`, `iss`/`aud` esperados e claims obrigatórias.
|
|
7
|
+
*
|
|
8
|
+
* Barato e stateless: o `createRemoteJWKSet` busca o jwks_uri uma vez e cacheia as
|
|
9
|
+
* chaves (rotação é tolerada — o jose recarrega ao ver um `kid` novo). Útil para um
|
|
10
|
+
* resource server que recebe o AT no header Authorization e quer validar sem chamar
|
|
11
|
+
* o /introspect a cada request.
|
|
12
|
+
*/
|
|
13
|
+
export interface VerifyJwtAccessTokenOptions {
|
|
14
|
+
/** Issuer esperado (a claim `iss`). */
|
|
15
|
+
issuer: string;
|
|
16
|
+
/** jwks_uri do IdP (do discovery). */
|
|
17
|
+
jwksUri: string;
|
|
18
|
+
/** Audience esperada (a claim `aud`) — a URI/identificador desta API. */
|
|
19
|
+
audience: string | string[];
|
|
20
|
+
/** Algoritmos de assinatura aceitos (defesa contra alg-confusion). Default: asimétricos. */
|
|
21
|
+
algorithms?: string[];
|
|
22
|
+
/** Aceita JWS com qualquer `typ` (desliga a checagem RFC 9068 `at+jwt`). Default: false. */
|
|
23
|
+
allowAnyTyp?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Claims de um JWT AT RFC 9068 validado. */
|
|
26
|
+
export interface JwtAccessTokenClaims extends JWTPayload {
|
|
27
|
+
client_id?: string;
|
|
28
|
+
scope?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Verifica um JWT AT (RFC 9068). Lança (via jose) se a assinatura/claims forem
|
|
32
|
+
* inválidas ou se o `typ` não for `at+jwt` (a menos que `allowAnyTyp`). Retorna as
|
|
33
|
+
* claims validadas no caminho feliz.
|
|
34
|
+
*/
|
|
35
|
+
export declare function verifyJwtAccessToken(token: string, options: VerifyJwtAccessTokenOptions): Promise<JwtAccessTokenClaims>;
|
|
36
|
+
/** Limpa o cache de JWKS remotos (útil em testes). */
|
|
37
|
+
export declare function clearJwksCache(): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
2
|
+
const DEFAULT_ALGS = [
|
|
3
|
+
'RS256',
|
|
4
|
+
'RS384',
|
|
5
|
+
'RS512',
|
|
6
|
+
'PS256',
|
|
7
|
+
'PS384',
|
|
8
|
+
'PS512',
|
|
9
|
+
'ES256',
|
|
10
|
+
'ES384',
|
|
11
|
+
'ES512',
|
|
12
|
+
'EdDSA',
|
|
13
|
+
];
|
|
14
|
+
// Cache de keysets remotos por jwks_uri — evita refazer fetch/parse por request.
|
|
15
|
+
const jwksCache = new Map();
|
|
16
|
+
function getJwks(uri) {
|
|
17
|
+
let set = jwksCache.get(uri);
|
|
18
|
+
if (!set) {
|
|
19
|
+
set = createRemoteJWKSet(new URL(uri));
|
|
20
|
+
jwksCache.set(uri, set);
|
|
21
|
+
}
|
|
22
|
+
return set;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Verifica um JWT AT (RFC 9068). Lança (via jose) se a assinatura/claims forem
|
|
26
|
+
* inválidas ou se o `typ` não for `at+jwt` (a menos que `allowAnyTyp`). Retorna as
|
|
27
|
+
* claims validadas no caminho feliz.
|
|
28
|
+
*/
|
|
29
|
+
export async function verifyJwtAccessToken(token, options) {
|
|
30
|
+
const jwks = getJwks(options.jwksUri);
|
|
31
|
+
const { payload } = await jwtVerify(token, jwks, {
|
|
32
|
+
issuer: options.issuer,
|
|
33
|
+
audience: options.audience,
|
|
34
|
+
algorithms: options.algorithms ?? DEFAULT_ALGS,
|
|
35
|
+
// RFC 9068 §2.1: o header DEVE ser `typ: "at+jwt"`. jose valida quando passamos `typ`.
|
|
36
|
+
...(options.allowAnyTyp ? {} : { typ: 'at+jwt' }),
|
|
37
|
+
});
|
|
38
|
+
return payload;
|
|
39
|
+
}
|
|
40
|
+
/** Limpa o cache de JWKS remotos (útil em testes). */
|
|
41
|
+
export function clearJwksCache() {
|
|
42
|
+
jwksCache.clear();
|
|
43
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.configPath('authkit_client.ts') })
|
|
3
|
+
}}}
|
|
4
|
+
import env from '#start/env'
|
|
5
|
+
import { defineConfig, resolvers } from '@adonis-agora/authkit-client'
|
|
6
|
+
|
|
7
|
+
const authkitClientConfig = defineConfig({
|
|
8
|
+
issuer: env.get('AUTHKIT_ISSUER'),
|
|
9
|
+
clientId: env.get('AUTHKIT_CLIENT_ID'),
|
|
10
|
+
clientSecret: env.get('AUTHKIT_CLIENT_SECRET'),
|
|
11
|
+
redirectUri: env.get('AUTHKIT_REDIRECT_URI'),
|
|
12
|
+
resolver: resolvers.jwt({ tokenSource: 'session' }),
|
|
13
|
+
// resolveUser: async (identity) => User.find(identity.userId),
|
|
14
|
+
// resolveAppRoles: async (identity) => RoleService.appRolesFor(identity.userId),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export default authkitClientConfig
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('app/controllers/oidc_session_controller.ts') })
|
|
3
|
+
}}}
|
|
4
|
+
import type { HttpContext } from '@adonisjs/core/http'
|
|
5
|
+
import { buildAuthorizeUrl, exchangeCode, generatePkce } from '@adonis-agora/authkit-client'
|
|
6
|
+
import { randomUUID } from 'node:crypto'
|
|
7
|
+
|
|
8
|
+
export default class OidcSessionController {
|
|
9
|
+
async login(ctx: HttpContext) {
|
|
10
|
+
const manager = await ctx.containerResolver.make('authkit.client')
|
|
11
|
+
const cfg = manager.clientConfig
|
|
12
|
+
const { verifier, challenge } = await generatePkce()
|
|
13
|
+
const state = randomUUID()
|
|
14
|
+
ctx.session.put('authkit_pkce', { verifier, state })
|
|
15
|
+
const url = buildAuthorizeUrl({
|
|
16
|
+
issuer: cfg.issuer, clientId: cfg.clientId, redirectUri: cfg.redirectUri,
|
|
17
|
+
scopes: cfg.scopes, state, codeChallenge: challenge,
|
|
18
|
+
})
|
|
19
|
+
return ctx.response.redirect(url)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async callback(ctx: HttpContext) {
|
|
23
|
+
const manager = await ctx.containerResolver.make('authkit.client')
|
|
24
|
+
const cfg = manager.clientConfig
|
|
25
|
+
const { code, state } = ctx.request.qs()
|
|
26
|
+
const pkce = ctx.session.get('authkit_pkce')
|
|
27
|
+
if (!pkce || pkce.state !== state) return ctx.response.badRequest({ error: 'state inválido' })
|
|
28
|
+
const tokenSet = await exchangeCode({
|
|
29
|
+
issuer: cfg.issuer, clientId: cfg.clientId, clientSecret: cfg.clientSecret,
|
|
30
|
+
redirectUri: cfg.redirectUri, code, codeVerifier: pkce.verifier,
|
|
31
|
+
})
|
|
32
|
+
ctx.session.put(cfg.sessionKey, tokenSet)
|
|
33
|
+
ctx.session.forget('authkit_pkce')
|
|
34
|
+
return ctx.response.redirect('/')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async logout(ctx: HttpContext) {
|
|
38
|
+
const manager = await ctx.containerResolver.make('authkit.client')
|
|
39
|
+
ctx.session.forget(manager.clientConfig.sessionKey)
|
|
40
|
+
return ctx.response.redirect('/')
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const stubsRoot: string;
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Authenticator } from './src/authenticator.js';
|
|
2
|
+
import type { AuthkitClientManager } from './providers/authkit_client_provider.js';
|
|
3
|
+
declare module '@adonisjs/core/http' {
|
|
4
|
+
interface HttpContext {
|
|
5
|
+
auth: Authenticator;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
declare module '@adonisjs/core/types' {
|
|
9
|
+
interface ContainerBindings {
|
|
10
|
+
'authkit.client': AuthkitClientManager;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export type { Authenticator, AuthkitClientManager };
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@adonis-agora/authkit-client",
|
|
3
|
+
"version": "0.9.0",
|
|
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
|
+
"license": "MIT",
|
|
6
|
+
"author": "dudousxd",
|
|
7
|
+
"homepage": "https://github.com/DavideCarvalho/adonis-authkit/tree/main/packages/authkit-client#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/DavideCarvalho/adonis-authkit.git",
|
|
11
|
+
"directory": "packages/authkit-client"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/DavideCarvalho/adonis-authkit/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"adonisjs",
|
|
18
|
+
"oidc",
|
|
19
|
+
"oauth2",
|
|
20
|
+
"openid-connect",
|
|
21
|
+
"authentication",
|
|
22
|
+
"authkit",
|
|
23
|
+
"relying-party",
|
|
24
|
+
"jwt"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"type": "module",
|
|
30
|
+
"main": "./build/index.js",
|
|
31
|
+
"types": "./build/index.d.ts",
|
|
32
|
+
"files": [
|
|
33
|
+
"build",
|
|
34
|
+
"stubs"
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
".": "./build/index.js",
|
|
38
|
+
"./types": "./build/index.js",
|
|
39
|
+
"./authkit_client_provider": "./build/providers/authkit_client_provider.js",
|
|
40
|
+
"./authkit_middleware": "./build/src/middleware/authkit_middleware.js",
|
|
41
|
+
"./backchannel_revocation_middleware": "./build/src/middleware/backchannel_revocation_middleware.js",
|
|
42
|
+
"./auth_middleware": "./build/src/middleware/auth_middleware.js",
|
|
43
|
+
"./silent_auth_middleware": "./build/src/middleware/silent_auth_middleware.js",
|
|
44
|
+
"./commands": "./build/commands/configure.js"
|
|
45
|
+
},
|
|
46
|
+
"adonisjs": {
|
|
47
|
+
"types": "@adonis-agora/authkit-client",
|
|
48
|
+
"providers": [
|
|
49
|
+
"@adonis-agora/authkit-client/authkit_client_provider"
|
|
50
|
+
],
|
|
51
|
+
"commands": [
|
|
52
|
+
"@adonis-agora/authkit-client/commands"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@adonisjs/core": "7.3.3",
|
|
57
|
+
"@adonisjs/session": "8.1.0",
|
|
58
|
+
"@adonisjs/lucid": "22.4.2",
|
|
59
|
+
"@adonis-agora/resilience": "^0.1.0"
|
|
60
|
+
},
|
|
61
|
+
"peerDependenciesMeta": {
|
|
62
|
+
"@adonisjs/lucid": {
|
|
63
|
+
"optional": true
|
|
64
|
+
},
|
|
65
|
+
"@adonis-agora/resilience": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"jose": "^5.9.0",
|
|
71
|
+
"@adonis-agora/authkit-core": "0.7.0"
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@adonisjs/core": "7.3.3",
|
|
75
|
+
"@adonis-agora/resilience": "^0.1.0",
|
|
76
|
+
"@adonisjs/session": "8.1.0",
|
|
77
|
+
"@adonisjs/lucid": "22.4.2",
|
|
78
|
+
"@adonisjs/tsconfig": "2.0.0",
|
|
79
|
+
"@japa/assert": "4.2.0",
|
|
80
|
+
"@japa/runner": "5.3.0",
|
|
81
|
+
"@poppinss/ts-exec": "1.4.4",
|
|
82
|
+
"ioredis-mock": "^8.9.0",
|
|
83
|
+
"@types/node": "25.9.1",
|
|
84
|
+
"typescript": "6.0.3",
|
|
85
|
+
"@adonis-agora/authkit-server": "0.34.0"
|
|
86
|
+
},
|
|
87
|
+
"scripts": {
|
|
88
|
+
"build": "tsc && node -e \"require('node:fs').cpSync('stubs','build/stubs',{recursive:true,filter:(s)=>!s.endsWith('.ts')})\"",
|
|
89
|
+
"typecheck": "tsc --noEmit",
|
|
90
|
+
"test": "node --import=@poppinss/ts-exec bin/test.ts"
|
|
91
|
+
}
|
|
92
|
+
}
|
package/stubs/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.configPath('authkit_client.ts') })
|
|
3
|
+
}}}
|
|
4
|
+
import env from '#start/env'
|
|
5
|
+
import { defineConfig, resolvers } from '@adonis-agora/authkit-client'
|
|
6
|
+
|
|
7
|
+
const authkitClientConfig = defineConfig({
|
|
8
|
+
issuer: env.get('AUTHKIT_ISSUER'),
|
|
9
|
+
clientId: env.get('AUTHKIT_CLIENT_ID'),
|
|
10
|
+
clientSecret: env.get('AUTHKIT_CLIENT_SECRET'),
|
|
11
|
+
redirectUri: env.get('AUTHKIT_REDIRECT_URI'),
|
|
12
|
+
resolver: resolvers.jwt({ tokenSource: 'session' }),
|
|
13
|
+
// resolveUser: async (identity) => User.find(identity.userId),
|
|
14
|
+
// resolveAppRoles: async (identity) => RoleService.appRolesFor(identity.userId),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export default authkitClientConfig
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.makePath('app/controllers/oidc_session_controller.ts') })
|
|
3
|
+
}}}
|
|
4
|
+
import type { HttpContext } from '@adonisjs/core/http'
|
|
5
|
+
import { buildAuthorizeUrl, exchangeCode, generatePkce } from '@adonis-agora/authkit-client'
|
|
6
|
+
import { randomUUID } from 'node:crypto'
|
|
7
|
+
|
|
8
|
+
export default class OidcSessionController {
|
|
9
|
+
async login(ctx: HttpContext) {
|
|
10
|
+
const manager = await ctx.containerResolver.make('authkit.client')
|
|
11
|
+
const cfg = manager.clientConfig
|
|
12
|
+
const { verifier, challenge } = await generatePkce()
|
|
13
|
+
const state = randomUUID()
|
|
14
|
+
ctx.session.put('authkit_pkce', { verifier, state })
|
|
15
|
+
const url = buildAuthorizeUrl({
|
|
16
|
+
issuer: cfg.issuer, clientId: cfg.clientId, redirectUri: cfg.redirectUri,
|
|
17
|
+
scopes: cfg.scopes, state, codeChallenge: challenge,
|
|
18
|
+
})
|
|
19
|
+
return ctx.response.redirect(url)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async callback(ctx: HttpContext) {
|
|
23
|
+
const manager = await ctx.containerResolver.make('authkit.client')
|
|
24
|
+
const cfg = manager.clientConfig
|
|
25
|
+
const { code, state } = ctx.request.qs()
|
|
26
|
+
const pkce = ctx.session.get('authkit_pkce')
|
|
27
|
+
if (!pkce || pkce.state !== state) return ctx.response.badRequest({ error: 'state inválido' })
|
|
28
|
+
const tokenSet = await exchangeCode({
|
|
29
|
+
issuer: cfg.issuer, clientId: cfg.clientId, clientSecret: cfg.clientSecret,
|
|
30
|
+
redirectUri: cfg.redirectUri, code, codeVerifier: pkce.verifier,
|
|
31
|
+
})
|
|
32
|
+
ctx.session.put(cfg.sessionKey, tokenSet)
|
|
33
|
+
ctx.session.forget('authkit_pkce')
|
|
34
|
+
return ctx.response.redirect('/')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async logout(ctx: HttpContext) {
|
|
38
|
+
const manager = await ctx.containerResolver.make('authkit.client')
|
|
39
|
+
ctx.session.forget(manager.clientConfig.sessionKey)
|
|
40
|
+
return ctx.response.redirect('/')
|
|
41
|
+
}
|
|
42
|
+
}
|
package/stubs/main.ts
ADDED