hightjs 0.1.1 → 0.2.1
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/dist/api/http.js +5 -3
- package/dist/auth/client.d.ts +11 -1
- package/dist/auth/client.js +78 -0
- package/dist/auth/core.d.ts +21 -4
- package/dist/auth/core.js +66 -17
- package/dist/auth/example.d.ts +40 -0
- package/dist/auth/example.js +104 -0
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.js +2 -1
- package/dist/auth/providers/credentials.d.ts +68 -0
- package/dist/auth/providers/credentials.js +132 -0
- package/dist/auth/providers/discord.d.ts +67 -0
- package/dist/auth/providers/discord.js +198 -0
- package/dist/auth/providers/index.d.ts +2 -0
- package/dist/auth/providers/index.js +19 -0
- package/dist/auth/providers.d.ts +2 -5
- package/dist/auth/providers.js +6 -12
- package/dist/auth/react.js +20 -13
- package/dist/auth/routes.d.ts +2 -2
- package/dist/auth/routes.js +45 -13
- package/dist/auth/types.d.ts +36 -21
- package/dist/router.js +9 -1
- package/package.json +1 -1
- package/src/api/http.ts +6 -2
- package/src/auth/client.ts +84 -3
- package/src/auth/core.ts +75 -19
- package/src/auth/example.ts +115 -0
- package/src/auth/index.ts +2 -1
- package/src/auth/providers/credentials.ts +158 -0
- package/src/auth/providers/discord.ts +231 -0
- package/src/auth/providers/index.ts +4 -0
- package/src/auth/providers.ts +3 -12
- package/src/auth/react.tsx +21 -13
- package/src/auth/routes.ts +51 -18
- package/src/auth/types.ts +49 -26
- package/src/router.ts +9 -1
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import type {AuthProviderClass, AuthRoute, User} from '../types';
|
|
2
|
+
import {HightJSRequest, HightJSResponse} from '../../api/http';
|
|
3
|
+
|
|
4
|
+
export interface DiscordConfig {
|
|
5
|
+
id?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
clientId: string;
|
|
8
|
+
clientSecret: string;
|
|
9
|
+
callbackUrl?: string;
|
|
10
|
+
successUrl?: string;
|
|
11
|
+
// Escopos OAuth, padrão: ['identify', 'email']
|
|
12
|
+
scope?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Provider para autenticação com Discord OAuth2
|
|
17
|
+
*
|
|
18
|
+
* Este provider permite autenticação usando Discord OAuth2.
|
|
19
|
+
* Automaticamente gerencia o fluxo OAuth completo e rotas necessárias.
|
|
20
|
+
*
|
|
21
|
+
* Exemplo de uso:
|
|
22
|
+
* ```typescript
|
|
23
|
+
* new DiscordProvider({
|
|
24
|
+
* clientId: process.env.DISCORD_CLIENT_ID!,
|
|
25
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET!,
|
|
26
|
+
* callbackUrl: "http://localhost:3000/api/auth/callback/discord"
|
|
27
|
+
* })
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Fluxo de autenticação:
|
|
31
|
+
* 1. GET /api/auth/signin/discord - Gera URL e redireciona para Discord
|
|
32
|
+
* 2. Discord redireciona para /api/auth/callback/discord com código
|
|
33
|
+
* 3. Provider troca código por token e busca dados do usuário
|
|
34
|
+
* 4. Retorna objeto User com dados do Discord
|
|
35
|
+
*/
|
|
36
|
+
export class DiscordProvider implements AuthProviderClass {
|
|
37
|
+
public readonly id: string;
|
|
38
|
+
public readonly name: string;
|
|
39
|
+
public readonly type: string = 'discord';
|
|
40
|
+
|
|
41
|
+
private config: DiscordConfig;
|
|
42
|
+
private readonly defaultScope = ['identify', 'email'];
|
|
43
|
+
|
|
44
|
+
constructor(config: DiscordConfig) {
|
|
45
|
+
this.config = config;
|
|
46
|
+
this.id = config.id || 'discord';
|
|
47
|
+
this.name = config.name || 'Discord';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Método para gerar URL OAuth (usado pelo handleSignIn)
|
|
52
|
+
*/
|
|
53
|
+
handleOauth(credentials: Record<string, string> = {}): string {
|
|
54
|
+
return this.getAuthorizationUrl();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Método principal - agora redireciona para OAuth ou processa callback
|
|
59
|
+
*/
|
|
60
|
+
async handleSignIn(credentials: Record<string, string>): Promise<User | string | null> {
|
|
61
|
+
// Se tem código, é callback - processa autenticação
|
|
62
|
+
if (credentials.code) {
|
|
63
|
+
return await this.processOAuthCallback(credentials);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Se não tem código, é início do OAuth - retorna URL
|
|
67
|
+
return this.handleOauth(credentials);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Processa o callback OAuth (código → usuário)
|
|
72
|
+
*/
|
|
73
|
+
private async processOAuthCallback(credentials: Record<string, string>): Promise<User | null> {
|
|
74
|
+
try {
|
|
75
|
+
const { code } = credentials;
|
|
76
|
+
if (!code) {
|
|
77
|
+
throw new Error('Authorization code not provided');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
// Troca o código por access token
|
|
82
|
+
const tokenResponse = await fetch('https://discord.com/api/oauth2/token', {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: {
|
|
85
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
86
|
+
},
|
|
87
|
+
body: new URLSearchParams({
|
|
88
|
+
client_id: this.config.clientId,
|
|
89
|
+
client_secret: this.config.clientSecret,
|
|
90
|
+
grant_type: 'authorization_code',
|
|
91
|
+
code,
|
|
92
|
+
redirect_uri: this.config.callbackUrl || '',
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (!tokenResponse.ok) {
|
|
97
|
+
const error = await tokenResponse.text();
|
|
98
|
+
// O erro original "Invalid \"code\" in request." acontece aqui.
|
|
99
|
+
throw new Error(`Failed to exchange code for token: ${error}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const tokens = await tokenResponse.json();
|
|
103
|
+
|
|
104
|
+
// Busca dados do usuário
|
|
105
|
+
const userResponse = await fetch('https://discord.com/api/users/@me', {
|
|
106
|
+
headers: {
|
|
107
|
+
'Authorization': `Bearer ${tokens.access_token}`,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (!userResponse.ok) {
|
|
112
|
+
throw new Error('Failed to fetch user data');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const discordUser = await userResponse.json();
|
|
116
|
+
|
|
117
|
+
// Retorna objeto User padronizado
|
|
118
|
+
return {
|
|
119
|
+
id: discordUser.id,
|
|
120
|
+
name: discordUser.global_name || discordUser.username,
|
|
121
|
+
email: discordUser.email,
|
|
122
|
+
image: discordUser.avatar
|
|
123
|
+
? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
|
|
124
|
+
: null,
|
|
125
|
+
username: discordUser.username,
|
|
126
|
+
discriminator: discordUser.discriminator,
|
|
127
|
+
provider: this.id,
|
|
128
|
+
providerId: discordUser.id,
|
|
129
|
+
accessToken: tokens.access_token,
|
|
130
|
+
refreshToken: tokens.refresh_token
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error(`[${this.id} Provider] Error during OAuth callback:`, error);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Método opcional para logout
|
|
141
|
+
*/
|
|
142
|
+
async handleSignOut?(): Promise<void> {
|
|
143
|
+
// Discord OAuth não precisa de logout especial
|
|
144
|
+
// O token será invalidado pelo tempo de vida
|
|
145
|
+
console.log(`[${this.id} Provider] User signed out`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Rotas adicionais específicas do Discord OAuth
|
|
150
|
+
*/
|
|
151
|
+
public additionalRoutes: AuthRoute[] = [
|
|
152
|
+
// Rota de callback do Discord
|
|
153
|
+
{
|
|
154
|
+
method: 'GET',
|
|
155
|
+
path: '/api/auth/callback/discord',
|
|
156
|
+
handler: async (req: HightJSRequest, params: any) => {
|
|
157
|
+
const url = new URL(req.url || '', 'http://localhost');
|
|
158
|
+
const code = url.searchParams.get('code');
|
|
159
|
+
|
|
160
|
+
if (!code) {
|
|
161
|
+
return HightJSResponse.json({ error: 'Authorization code not provided' }, { status: 400 });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
// CORREÇÃO: O fluxo correto é delegar o 'code' para o endpoint de signin
|
|
166
|
+
// principal, que processará o código uma única vez. A implementação anterior
|
|
167
|
+
// usava o código duas vezes, causando o erro 'invalid_grant'.
|
|
168
|
+
const authResponse = await fetch(`${req.headers.origin || 'http://localhost:3000'}/api/auth/signin`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: {
|
|
171
|
+
'Content-Type': 'application/json',
|
|
172
|
+
},
|
|
173
|
+
body: JSON.stringify({
|
|
174
|
+
provider: this.id,
|
|
175
|
+
code,
|
|
176
|
+
})
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (authResponse.ok) {
|
|
180
|
+
// Propaga o cookie de sessão retornado pelo endpoint de signin
|
|
181
|
+
// e redireciona o usuário para a página de sucesso.
|
|
182
|
+
const setCookieHeader = authResponse.headers.get('set-cookie');
|
|
183
|
+
if(this.config.successUrl) {
|
|
184
|
+
return HightJSResponse
|
|
185
|
+
.redirect(this.config.successUrl)
|
|
186
|
+
.header('Set-Cookie', setCookieHeader || '');
|
|
187
|
+
}
|
|
188
|
+
return HightJSResponse.json({ success: true })
|
|
189
|
+
.header('Set-Cookie', setCookieHeader || '');
|
|
190
|
+
} else {
|
|
191
|
+
const errorText = await authResponse.text();
|
|
192
|
+
console.error(`[${this.id} Provider] Session creation failed during callback. Status: ${authResponse.status}, Body: ${errorText}`);
|
|
193
|
+
return HightJSResponse.json({ error: 'Session creation failed' }, { status: 500 });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error(`[${this.id} Provider] Callback handler fetch error:`, error);
|
|
198
|
+
return HightJSResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Gera URL de autorização do Discord
|
|
206
|
+
*/
|
|
207
|
+
getAuthorizationUrl(): string {
|
|
208
|
+
const params = new URLSearchParams({
|
|
209
|
+
client_id: this.config.clientId,
|
|
210
|
+
redirect_uri: this.config.callbackUrl || '',
|
|
211
|
+
response_type: 'code',
|
|
212
|
+
scope: (this.config.scope || this.defaultScope).join(' ')
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
return `https://discord.com/api/oauth2/authorize?${params.toString()}`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Retorna configuração pública do provider
|
|
220
|
+
*/
|
|
221
|
+
getConfig(): any {
|
|
222
|
+
return {
|
|
223
|
+
id: this.id,
|
|
224
|
+
name: this.name,
|
|
225
|
+
type: this.type,
|
|
226
|
+
clientId: this.config.clientId, // Público
|
|
227
|
+
scope: this.config.scope || this.defaultScope,
|
|
228
|
+
callbackUrl: this.config.callbackUrl
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
package/src/auth/providers.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// Exportações dos providers
|
|
2
|
+
export { CredentialsProvider } from './providers/credentials';
|
|
3
|
+
export { DiscordProvider } from './providers/discord';
|
|
2
4
|
|
|
3
|
-
/**
|
|
4
|
-
* Provider para autenticação com credenciais (email/senha)
|
|
5
|
-
*/
|
|
6
|
-
export function CredentialsProvider(config: CredentialsConfig): AuthProvider {
|
|
7
|
-
return {
|
|
8
|
-
id: config.id || 'credentials',
|
|
9
|
-
name: config.name || 'Credentials',
|
|
10
|
-
type: 'credentials',
|
|
11
|
-
authorize: config.authorize
|
|
12
|
-
};
|
|
13
|
-
}
|
package/src/auth/react.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { createContext, useContext, useEffect, useState, useCallback, ReactNode } from 'react';
|
|
2
2
|
import type { Session, SessionContextType, SignInOptions, SignInResult, User } from './types';
|
|
3
|
-
import {router} from "../client";
|
|
3
|
+
import { router } from "../client/clientRouter";
|
|
4
4
|
|
|
5
5
|
const SessionContext = createContext<SessionContextType | undefined>(undefined);
|
|
6
6
|
|
|
@@ -75,23 +75,31 @@ export function SessionProvider({
|
|
|
75
75
|
const data = await response.json();
|
|
76
76
|
|
|
77
77
|
if (response.ok && data.success) {
|
|
78
|
-
//
|
|
78
|
+
// Se é OAuth, redireciona para URL fornecida
|
|
79
|
+
if (data.type === 'oauth' && data.redirectUrl) {
|
|
80
|
+
if (redirect && typeof window !== 'undefined') {
|
|
81
|
+
window.location.href = data.redirectUrl;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
ok: true,
|
|
86
|
+
status: 200,
|
|
87
|
+
url: data.redirectUrl
|
|
88
|
+
};
|
|
89
|
+
}
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
} catch (e) {
|
|
91
|
+
// Se é sessão (credentials), redireciona para callbackUrl
|
|
92
|
+
if (data.type === 'session') {
|
|
93
|
+
if (redirect && typeof window !== 'undefined') {
|
|
84
94
|
window.location.href = callbackUrl || '/';
|
|
85
95
|
}
|
|
86
96
|
|
|
97
|
+
return {
|
|
98
|
+
ok: true,
|
|
99
|
+
status: 200,
|
|
100
|
+
url: callbackUrl || '/'
|
|
101
|
+
};
|
|
87
102
|
}
|
|
88
|
-
await fetchSession();
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
ok: true,
|
|
92
|
-
status: 200,
|
|
93
|
-
url: callbackUrl || '/'
|
|
94
|
-
};
|
|
95
103
|
} else {
|
|
96
104
|
return {
|
|
97
105
|
error: data.error || 'Authentication failed',
|
package/src/auth/routes.ts
CHANGED
|
@@ -13,12 +13,28 @@ export function createAuthRoutes(config: AuthConfig) {
|
|
|
13
13
|
* Uso: /api/auth/[...value].ts
|
|
14
14
|
*/
|
|
15
15
|
return {
|
|
16
|
-
pattern: '/api/auth/[value]',
|
|
16
|
+
pattern: '/api/auth/[...value]',
|
|
17
17
|
|
|
18
18
|
async GET(req: HightJSRequest, params: { [key: string]: string }) {
|
|
19
|
+
|
|
19
20
|
const path = params["value"];
|
|
20
21
|
const route = Array.isArray(path) ? path.join('/') : path || '';
|
|
21
22
|
|
|
23
|
+
// Verifica rotas adicionais dos providers primeiro
|
|
24
|
+
const additionalRoutes = auth.getAllAdditionalRoutes();
|
|
25
|
+
for (const { provider, route: additionalRoute } of additionalRoutes) {
|
|
26
|
+
|
|
27
|
+
if (additionalRoute.method === 'GET' && additionalRoute.path.includes(route)) {
|
|
28
|
+
try {
|
|
29
|
+
return await additionalRoute.handler(req, params);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(`[${provider} Provider] Error in additional route:`, error);
|
|
32
|
+
return HightJSResponse.json({ error: 'Provider route error' }, { status: 500 });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Rotas padrão do sistema
|
|
22
38
|
switch (route) {
|
|
23
39
|
case 'session':
|
|
24
40
|
return await handleSession(req, auth);
|
|
@@ -27,7 +43,7 @@ export function createAuthRoutes(config: AuthConfig) {
|
|
|
27
43
|
return await handleCsrf(req);
|
|
28
44
|
|
|
29
45
|
case 'providers':
|
|
30
|
-
return await handleProviders(
|
|
46
|
+
return await handleProviders(auth);
|
|
31
47
|
|
|
32
48
|
default:
|
|
33
49
|
return HightJSResponse.json({ error: 'Route not found' }, { status: 404 });
|
|
@@ -38,6 +54,20 @@ export function createAuthRoutes(config: AuthConfig) {
|
|
|
38
54
|
const path = params["value"];
|
|
39
55
|
const route = Array.isArray(path) ? path.join('/') : path || '';
|
|
40
56
|
|
|
57
|
+
// Verifica rotas adicionais dos providers primeiro
|
|
58
|
+
const additionalRoutes = auth.getAllAdditionalRoutes();
|
|
59
|
+
for (const { provider, route: additionalRoute } of additionalRoutes) {
|
|
60
|
+
if (additionalRoute.method === 'POST' && additionalRoute.path.includes(route)) {
|
|
61
|
+
try {
|
|
62
|
+
return await additionalRoute.handler(req, params);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(`[${provider} Provider] Error in additional route:`, error);
|
|
65
|
+
return HightJSResponse.json({ error: 'Provider route error' }, { status: 500 });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Rotas padrão do sistema
|
|
41
71
|
switch (route) {
|
|
42
72
|
case 'signin':
|
|
43
73
|
return await handleSignIn(req, auth);
|
|
@@ -50,7 +80,6 @@ export function createAuthRoutes(config: AuthConfig) {
|
|
|
50
80
|
}
|
|
51
81
|
},
|
|
52
82
|
|
|
53
|
-
|
|
54
83
|
// Instância do auth para uso manual
|
|
55
84
|
auth
|
|
56
85
|
};
|
|
@@ -59,7 +88,7 @@ export function createAuthRoutes(config: AuthConfig) {
|
|
|
59
88
|
/**
|
|
60
89
|
* Handler para GET /api/auth/session
|
|
61
90
|
*/
|
|
62
|
-
async function handleSession(req: HightJSRequest, auth:
|
|
91
|
+
async function handleSession(req: HightJSRequest, auth: HWebAuth) {
|
|
63
92
|
const session = await auth.getSession(req);
|
|
64
93
|
|
|
65
94
|
if (!session) {
|
|
@@ -83,14 +112,8 @@ async function handleCsrf(req: HightJSRequest) {
|
|
|
83
112
|
/**
|
|
84
113
|
* Handler para GET /api/auth/providers
|
|
85
114
|
*/
|
|
86
|
-
async function handleProviders(
|
|
87
|
-
const providers =
|
|
88
|
-
.filter(p => p.type === 'credentials') // Apenas credentials
|
|
89
|
-
.map(p => ({
|
|
90
|
-
id: p.id,
|
|
91
|
-
name: p.name,
|
|
92
|
-
type: p.type
|
|
93
|
-
}));
|
|
115
|
+
async function handleProviders(auth: HWebAuth) {
|
|
116
|
+
const providers = auth.getProviders();
|
|
94
117
|
|
|
95
118
|
return HightJSResponse.json({ providers });
|
|
96
119
|
}
|
|
@@ -98,11 +121,10 @@ async function handleProviders(config: AuthConfig) {
|
|
|
98
121
|
/**
|
|
99
122
|
* Handler para POST /api/auth/signin
|
|
100
123
|
*/
|
|
101
|
-
async function handleSignIn(req: HightJSRequest, auth:
|
|
124
|
+
async function handleSignIn(req: HightJSRequest, auth: HWebAuth) {
|
|
102
125
|
try {
|
|
103
126
|
const { provider = 'credentials', ...credentials } = await req.json();
|
|
104
127
|
|
|
105
|
-
// Apenas credentials agora
|
|
106
128
|
const result = await auth.signIn(provider, credentials);
|
|
107
129
|
|
|
108
130
|
if (!result) {
|
|
@@ -112,11 +134,23 @@ async function handleSignIn(req: HightJSRequest, auth: any) {
|
|
|
112
134
|
);
|
|
113
135
|
}
|
|
114
136
|
|
|
137
|
+
// Se tem redirectUrl, é OAuth - retorna URL para redirecionamento
|
|
138
|
+
if ('redirectUrl' in result) {
|
|
139
|
+
return HightJSResponse.json({
|
|
140
|
+
success: true,
|
|
141
|
+
redirectUrl: result.redirectUrl,
|
|
142
|
+
type: 'oauth'
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Se tem session, é credentials - retorna sessão
|
|
115
147
|
return auth.createAuthResponse(result.token, {
|
|
116
148
|
success: true,
|
|
117
|
-
user: result.session.user
|
|
149
|
+
user: result.session.user,
|
|
150
|
+
type: 'session'
|
|
118
151
|
});
|
|
119
152
|
} catch (error) {
|
|
153
|
+
console.error('[hweb-auth] Erro no handleSignIn:', error);
|
|
120
154
|
return HightJSResponse.json(
|
|
121
155
|
{ error: 'Authentication failed' },
|
|
122
156
|
{ status: 500 }
|
|
@@ -127,7 +161,6 @@ async function handleSignIn(req: HightJSRequest, auth: any) {
|
|
|
127
161
|
/**
|
|
128
162
|
* Handler para POST /api/auth/signout
|
|
129
163
|
*/
|
|
130
|
-
async function handleSignOut(req: HightJSRequest, auth:
|
|
131
|
-
return auth.signOut();
|
|
164
|
+
async function handleSignOut(req: HightJSRequest, auth: HWebAuth) {
|
|
165
|
+
return await auth.signOut(req);
|
|
132
166
|
}
|
|
133
|
-
|
package/src/auth/types.ts
CHANGED
|
@@ -7,8 +7,55 @@ export interface Session {
|
|
|
7
7
|
accessToken?: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
// Client-side types
|
|
11
|
+
export interface SignInOptions {
|
|
12
|
+
redirect?: boolean;
|
|
13
|
+
callbackUrl?: string;
|
|
14
|
+
[key: string]: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SignInResult {
|
|
18
|
+
error?: string;
|
|
19
|
+
status?: number;
|
|
20
|
+
ok?: boolean;
|
|
21
|
+
url?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SessionContextType {
|
|
25
|
+
data: Session | null;
|
|
26
|
+
status: 'loading' | 'authenticated' | 'unauthenticated';
|
|
27
|
+
signIn: (provider?: string, options?: SignInOptions) => Promise<SignInResult | undefined>;
|
|
28
|
+
signOut: (options?: { callbackUrl?: string }) => Promise<void>;
|
|
29
|
+
update: () => Promise<Session | null>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AuthRoute {
|
|
33
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
34
|
+
path: string;
|
|
35
|
+
handler: (req: any, params: any) => Promise<any>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface AuthProviderClass {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
type: string;
|
|
42
|
+
|
|
43
|
+
// Para providers OAuth - retorna URL de redirecionamento
|
|
44
|
+
handleOauth?(credentials: Record<string, string>): Promise<string> | string;
|
|
45
|
+
|
|
46
|
+
// Métodos principais
|
|
47
|
+
handleSignIn(credentials: Record<string, string>): Promise<User | string | null>;
|
|
48
|
+
handleSignOut?(): Promise<void>;
|
|
49
|
+
|
|
50
|
+
// Rotas adicionais que o provider pode ter
|
|
51
|
+
additionalRoutes?: AuthRoute[];
|
|
52
|
+
|
|
53
|
+
// Configurações específicas do provider
|
|
54
|
+
getConfig?(): any;
|
|
55
|
+
}
|
|
56
|
+
|
|
10
57
|
export interface AuthConfig {
|
|
11
|
-
providers:
|
|
58
|
+
providers: AuthProviderClass[];
|
|
12
59
|
pages?: {
|
|
13
60
|
signIn?: string;
|
|
14
61
|
signOut?: string;
|
|
@@ -28,6 +75,7 @@ export interface AuthConfig {
|
|
|
28
75
|
debug?: boolean;
|
|
29
76
|
}
|
|
30
77
|
|
|
78
|
+
// Interface legada para compatibilidade
|
|
31
79
|
export interface AuthProvider {
|
|
32
80
|
id: string;
|
|
33
81
|
name: string;
|
|
@@ -35,27 +83,6 @@ export interface AuthProvider {
|
|
|
35
83
|
authorize?: (credentials: Record<string, string>) => Promise<User | null> | User | null;
|
|
36
84
|
}
|
|
37
85
|
|
|
38
|
-
export interface SignInOptions {
|
|
39
|
-
redirect?: boolean;
|
|
40
|
-
callbackUrl?: string;
|
|
41
|
-
[key: string]: any;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface SignInResult {
|
|
45
|
-
error?: string;
|
|
46
|
-
status?: number;
|
|
47
|
-
ok?: boolean;
|
|
48
|
-
url?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface SessionContextType {
|
|
52
|
-
data: Session | null;
|
|
53
|
-
status: 'loading' | 'authenticated' | 'unauthenticated';
|
|
54
|
-
signIn: (provider?: string, options?: SignInOptions) => Promise<SignInResult | undefined>;
|
|
55
|
-
signOut: (options?: { callbackUrl?: string }) => Promise<void>;
|
|
56
|
-
update: () => Promise<Session | null>;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
86
|
// Provider para credenciais
|
|
60
87
|
export interface CredentialsConfig {
|
|
61
88
|
id?: string;
|
|
@@ -67,7 +94,3 @@ export interface CredentialsConfig {
|
|
|
67
94
|
}>;
|
|
68
95
|
authorize: (credentials: Record<string, string>) => Promise<User | null> | User | null;
|
|
69
96
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
package/src/router.ts
CHANGED
|
@@ -341,8 +341,16 @@ export function findMatchingBackendRoute(pathname: string, method: string) {
|
|
|
341
341
|
for (const route of allBackendRoutes) {
|
|
342
342
|
// Verifica se a rota tem um handler para o método HTTP atual
|
|
343
343
|
if (!route.pattern || !route[method.toUpperCase() as keyof BackendRouteConfig]) continue;
|
|
344
|
+
const regexPattern = route.pattern
|
|
345
|
+
// [[...param]] → opcional catch-all
|
|
346
|
+
.replace(/\[\[\.\.\.(\w+)\]\]/g, '(?<$1>.+)?')
|
|
347
|
+
// [...param] → obrigatório catch-all
|
|
348
|
+
.replace(/\[\.\.\.(\w+)\]/g, '(?<$1>.+)')
|
|
349
|
+
// [[param]] → segmento opcional
|
|
350
|
+
.replace(/\[\[(\w+)\]\]/g, '(?<$1>[^/]+)?')
|
|
351
|
+
// [param] → segmento obrigatório
|
|
352
|
+
.replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
|
|
344
353
|
|
|
345
|
-
const regexPattern = route.pattern.replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
|
|
346
354
|
const regex = new RegExp(`^${regexPattern}/?$`);
|
|
347
355
|
const match = pathname.match(regex);
|
|
348
356
|
|