hightjs 0.1.1 → 0.2.2

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.
@@ -0,0 +1,68 @@
1
+ import type { AuthProviderClass, User, AuthRoute } from '../types';
2
+ export interface CredentialsConfig {
3
+ id?: string;
4
+ name?: string;
5
+ credentials: Record<string, {
6
+ label: string;
7
+ type: string;
8
+ placeholder?: string;
9
+ }>;
10
+ authorize: (credentials: Record<string, string>) => Promise<User | null> | User | null;
11
+ }
12
+ /**
13
+ * Provider para autenticação com credenciais (email/senha)
14
+ *
15
+ * Este provider permite autenticação usando email/senha ou qualquer outro
16
+ * sistema de credenciais customizado. Você define a função authorize
17
+ * que será chamada para validar as credenciais.
18
+ *
19
+ * Exemplo de uso:
20
+ * ```typescript
21
+ * new CredentialsProvider({
22
+ * name: "Credentials",
23
+ * credentials: {
24
+ * email: { label: "Email", type: "email" },
25
+ * password: { label: "Password", type: "password" }
26
+ * },
27
+ * async authorize(credentials) {
28
+ * // Aqui você faz a validação com seu banco de dados
29
+ * const user = await validateUser(credentials.email, credentials.password);
30
+ * if (user) {
31
+ * return { id: user.id, name: user.name, email: user.email };
32
+ * }
33
+ * return null;
34
+ * }
35
+ * })
36
+ * ```
37
+ */
38
+ export declare class CredentialsProvider implements AuthProviderClass {
39
+ readonly id: string;
40
+ readonly name: string;
41
+ readonly type: string;
42
+ private config;
43
+ constructor(config: CredentialsConfig);
44
+ /**
45
+ * Método principal para autenticar usuário com credenciais
46
+ */
47
+ handleSignIn(credentials: Record<string, string>): Promise<User | null>;
48
+ /**
49
+ * Método opcional para logout (pode ser sobrescrito se necessário)
50
+ */
51
+ handleSignOut?(): Promise<void>;
52
+ /**
53
+ * Rotas adicionais específicas do provider (opcional)
54
+ */
55
+ additionalRoutes?: AuthRoute[];
56
+ /**
57
+ * Retorna configuração pública do provider
58
+ */
59
+ getConfig(): any;
60
+ /**
61
+ * Valida se as credenciais fornecidas são válidas
62
+ */
63
+ validateCredentials(credentials: Record<string, string>): boolean;
64
+ /**
65
+ * Validação simples de email
66
+ */
67
+ private isValidEmail;
68
+ }
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CredentialsProvider = void 0;
4
+ const http_1 = require("../../api/http");
5
+ /**
6
+ * Provider para autenticação com credenciais (email/senha)
7
+ *
8
+ * Este provider permite autenticação usando email/senha ou qualquer outro
9
+ * sistema de credenciais customizado. Você define a função authorize
10
+ * que será chamada para validar as credenciais.
11
+ *
12
+ * Exemplo de uso:
13
+ * ```typescript
14
+ * new CredentialsProvider({
15
+ * name: "Credentials",
16
+ * credentials: {
17
+ * email: { label: "Email", type: "email" },
18
+ * password: { label: "Password", type: "password" }
19
+ * },
20
+ * async authorize(credentials) {
21
+ * // Aqui você faz a validação com seu banco de dados
22
+ * const user = await validateUser(credentials.email, credentials.password);
23
+ * if (user) {
24
+ * return { id: user.id, name: user.name, email: user.email };
25
+ * }
26
+ * return null;
27
+ * }
28
+ * })
29
+ * ```
30
+ */
31
+ class CredentialsProvider {
32
+ constructor(config) {
33
+ this.type = 'credentials';
34
+ /**
35
+ * Rotas adicionais específicas do provider (opcional)
36
+ */
37
+ this.additionalRoutes = [
38
+ {
39
+ method: 'GET',
40
+ path: '/api/auth/credentials/config',
41
+ handler: async (req, params) => {
42
+ // Retorna configuração das credenciais (sem dados sensíveis)
43
+ const safeConfig = {
44
+ id: this.id,
45
+ name: this.name,
46
+ type: this.type,
47
+ credentials: Object.entries(this.config.credentials).reduce((acc, [key, field]) => {
48
+ acc[key] = {
49
+ label: field.label,
50
+ type: field.type,
51
+ placeholder: field.placeholder
52
+ };
53
+ return acc;
54
+ }, {})
55
+ };
56
+ return http_1.HightJSResponse.json({ config: safeConfig });
57
+ }
58
+ }
59
+ ];
60
+ this.config = config;
61
+ this.id = config.id || 'credentials';
62
+ this.name = config.name || 'Credentials';
63
+ }
64
+ /**
65
+ * Método principal para autenticar usuário com credenciais
66
+ */
67
+ async handleSignIn(credentials) {
68
+ try {
69
+ if (!this.config.authorize) {
70
+ throw new Error('Authorize function not provided');
71
+ }
72
+ const user = await this.config.authorize(credentials);
73
+ if (!user) {
74
+ return null;
75
+ }
76
+ // Adiciona informações do provider ao usuário
77
+ return {
78
+ ...user,
79
+ provider: this.id,
80
+ providerId: user.id || user.email || 'unknown'
81
+ };
82
+ }
83
+ catch (error) {
84
+ console.error(`[${this.id} Provider] Error during sign in:`, error);
85
+ return null;
86
+ }
87
+ }
88
+ /**
89
+ * Método opcional para logout (pode ser sobrescrito se necessário)
90
+ */
91
+ async handleSignOut() {
92
+ // Credentials provider não precisa fazer nada específico no logout
93
+ // O core já cuida de limpar cookies e tokens
94
+ console.log(`[${this.id} Provider] User signed out`);
95
+ }
96
+ /**
97
+ * Retorna configuração pública do provider
98
+ */
99
+ getConfig() {
100
+ return {
101
+ id: this.id,
102
+ name: this.name,
103
+ type: this.type,
104
+ credentials: this.config.credentials
105
+ };
106
+ }
107
+ /**
108
+ * Valida se as credenciais fornecidas são válidas
109
+ */
110
+ validateCredentials(credentials) {
111
+ for (const [key, field] of Object.entries(this.config.credentials)) {
112
+ if (!credentials[key]) {
113
+ console.warn(`[${this.id} Provider] Missing required credential: ${key}`);
114
+ return false;
115
+ }
116
+ // Validações básicas por tipo
117
+ if (field.type === 'email' && !this.isValidEmail(credentials[key])) {
118
+ console.warn(`[${this.id} Provider] Invalid email format: ${credentials[key]}`);
119
+ return false;
120
+ }
121
+ }
122
+ return true;
123
+ }
124
+ /**
125
+ * Validação simples de email
126
+ */
127
+ isValidEmail(email) {
128
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
129
+ return emailRegex.test(email);
130
+ }
131
+ }
132
+ exports.CredentialsProvider = CredentialsProvider;
@@ -0,0 +1,67 @@
1
+ import type { AuthProviderClass, AuthRoute, User } from '../types';
2
+ export interface DiscordConfig {
3
+ id?: string;
4
+ name?: string;
5
+ clientId: string;
6
+ clientSecret: string;
7
+ callbackUrl?: string;
8
+ successUrl?: string;
9
+ scope?: string[];
10
+ }
11
+ /**
12
+ * Provider para autenticação com Discord OAuth2
13
+ *
14
+ * Este provider permite autenticação usando Discord OAuth2.
15
+ * Automaticamente gerencia o fluxo OAuth completo e rotas necessárias.
16
+ *
17
+ * Exemplo de uso:
18
+ * ```typescript
19
+ * new DiscordProvider({
20
+ * clientId: process.env.DISCORD_CLIENT_ID!,
21
+ * clientSecret: process.env.DISCORD_CLIENT_SECRET!,
22
+ * callbackUrl: "http://localhost:3000/api/auth/callback/discord"
23
+ * })
24
+ * ```
25
+ *
26
+ * Fluxo de autenticação:
27
+ * 1. GET /api/auth/signin/discord - Gera URL e redireciona para Discord
28
+ * 2. Discord redireciona para /api/auth/callback/discord com código
29
+ * 3. Provider troca código por token e busca dados do usuário
30
+ * 4. Retorna objeto User com dados do Discord
31
+ */
32
+ export declare class DiscordProvider implements AuthProviderClass {
33
+ readonly id: string;
34
+ readonly name: string;
35
+ readonly type: string;
36
+ private config;
37
+ private readonly defaultScope;
38
+ constructor(config: DiscordConfig);
39
+ /**
40
+ * Método para gerar URL OAuth (usado pelo handleSignIn)
41
+ */
42
+ handleOauth(credentials?: Record<string, string>): string;
43
+ /**
44
+ * Método principal - agora redireciona para OAuth ou processa callback
45
+ */
46
+ handleSignIn(credentials: Record<string, string>): Promise<User | string | null>;
47
+ /**
48
+ * Processa o callback OAuth (código → usuário)
49
+ */
50
+ private processOAuthCallback;
51
+ /**
52
+ * Método opcional para logout
53
+ */
54
+ handleSignOut?(): Promise<void>;
55
+ /**
56
+ * Rotas adicionais específicas do Discord OAuth
57
+ */
58
+ additionalRoutes: AuthRoute[];
59
+ /**
60
+ * Gera URL de autorização do Discord
61
+ */
62
+ getAuthorizationUrl(): string;
63
+ /**
64
+ * Retorna configuração pública do provider
65
+ */
66
+ getConfig(): any;
67
+ }
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DiscordProvider = void 0;
4
+ const http_1 = require("../../api/http");
5
+ /**
6
+ * Provider para autenticação com Discord OAuth2
7
+ *
8
+ * Este provider permite autenticação usando Discord OAuth2.
9
+ * Automaticamente gerencia o fluxo OAuth completo e rotas necessárias.
10
+ *
11
+ * Exemplo de uso:
12
+ * ```typescript
13
+ * new DiscordProvider({
14
+ * clientId: process.env.DISCORD_CLIENT_ID!,
15
+ * clientSecret: process.env.DISCORD_CLIENT_SECRET!,
16
+ * callbackUrl: "http://localhost:3000/api/auth/callback/discord"
17
+ * })
18
+ * ```
19
+ *
20
+ * Fluxo de autenticação:
21
+ * 1. GET /api/auth/signin/discord - Gera URL e redireciona para Discord
22
+ * 2. Discord redireciona para /api/auth/callback/discord com código
23
+ * 3. Provider troca código por token e busca dados do usuário
24
+ * 4. Retorna objeto User com dados do Discord
25
+ */
26
+ class DiscordProvider {
27
+ constructor(config) {
28
+ this.type = 'discord';
29
+ this.defaultScope = ['identify', 'email'];
30
+ /**
31
+ * Rotas adicionais específicas do Discord OAuth
32
+ */
33
+ this.additionalRoutes = [
34
+ // Rota de callback do Discord
35
+ {
36
+ method: 'GET',
37
+ path: '/api/auth/callback/discord',
38
+ handler: async (req, params) => {
39
+ const url = new URL(req.url || '', 'http://localhost');
40
+ const code = url.searchParams.get('code');
41
+ if (!code) {
42
+ return http_1.HightJSResponse.json({ error: 'Authorization code not provided' }, { status: 400 });
43
+ }
44
+ try {
45
+ // CORREÇÃO: O fluxo correto é delegar o 'code' para o endpoint de signin
46
+ // principal, que processará o código uma única vez. A implementação anterior
47
+ // usava o código duas vezes, causando o erro 'invalid_grant'.
48
+ const authResponse = await fetch(`${req.headers.origin || 'http://localhost:3000'}/api/auth/signin`, {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ },
53
+ body: JSON.stringify({
54
+ provider: this.id,
55
+ code,
56
+ })
57
+ });
58
+ if (authResponse.ok) {
59
+ // Propaga o cookie de sessão retornado pelo endpoint de signin
60
+ // e redireciona o usuário para a página de sucesso.
61
+ const setCookieHeader = authResponse.headers.get('set-cookie');
62
+ if (this.config.successUrl) {
63
+ return http_1.HightJSResponse
64
+ .redirect(this.config.successUrl)
65
+ .header('Set-Cookie', setCookieHeader || '');
66
+ }
67
+ return http_1.HightJSResponse.json({ success: true })
68
+ .header('Set-Cookie', setCookieHeader || '');
69
+ }
70
+ else {
71
+ const errorText = await authResponse.text();
72
+ console.error(`[${this.id} Provider] Session creation failed during callback. Status: ${authResponse.status}, Body: ${errorText}`);
73
+ return http_1.HightJSResponse.json({ error: 'Session creation failed' }, { status: 500 });
74
+ }
75
+ }
76
+ catch (error) {
77
+ console.error(`[${this.id} Provider] Callback handler fetch error:`, error);
78
+ return http_1.HightJSResponse.json({ error: 'Internal server error' }, { status: 500 });
79
+ }
80
+ }
81
+ }
82
+ ];
83
+ this.config = config;
84
+ this.id = config.id || 'discord';
85
+ this.name = config.name || 'Discord';
86
+ }
87
+ /**
88
+ * Método para gerar URL OAuth (usado pelo handleSignIn)
89
+ */
90
+ handleOauth(credentials = {}) {
91
+ return this.getAuthorizationUrl();
92
+ }
93
+ /**
94
+ * Método principal - agora redireciona para OAuth ou processa callback
95
+ */
96
+ async handleSignIn(credentials) {
97
+ // Se tem código, é callback - processa autenticação
98
+ if (credentials.code) {
99
+ return await this.processOAuthCallback(credentials);
100
+ }
101
+ // Se não tem código, é início do OAuth - retorna URL
102
+ return this.handleOauth(credentials);
103
+ }
104
+ /**
105
+ * Processa o callback OAuth (código → usuário)
106
+ */
107
+ async processOAuthCallback(credentials) {
108
+ try {
109
+ const { code } = credentials;
110
+ if (!code) {
111
+ throw new Error('Authorization code not provided');
112
+ }
113
+ // Troca o código por access token
114
+ const tokenResponse = await fetch('https://discord.com/api/oauth2/token', {
115
+ method: 'POST',
116
+ headers: {
117
+ 'Content-Type': 'application/x-www-form-urlencoded',
118
+ },
119
+ body: new URLSearchParams({
120
+ client_id: this.config.clientId,
121
+ client_secret: this.config.clientSecret,
122
+ grant_type: 'authorization_code',
123
+ code,
124
+ redirect_uri: this.config.callbackUrl || '',
125
+ }),
126
+ });
127
+ if (!tokenResponse.ok) {
128
+ const error = await tokenResponse.text();
129
+ // O erro original "Invalid \"code\" in request." acontece aqui.
130
+ throw new Error(`Failed to exchange code for token: ${error}`);
131
+ }
132
+ const tokens = await tokenResponse.json();
133
+ // Busca dados do usuário
134
+ const userResponse = await fetch('https://discord.com/api/users/@me', {
135
+ headers: {
136
+ 'Authorization': `Bearer ${tokens.access_token}`,
137
+ },
138
+ });
139
+ if (!userResponse.ok) {
140
+ throw new Error('Failed to fetch user data');
141
+ }
142
+ const discordUser = await userResponse.json();
143
+ // Retorna objeto User padronizado
144
+ return {
145
+ id: discordUser.id,
146
+ name: discordUser.global_name || discordUser.username,
147
+ email: discordUser.email,
148
+ image: discordUser.avatar
149
+ ? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
150
+ : null,
151
+ username: discordUser.username,
152
+ discriminator: discordUser.discriminator,
153
+ provider: this.id,
154
+ providerId: discordUser.id,
155
+ accessToken: tokens.access_token,
156
+ refreshToken: tokens.refresh_token
157
+ };
158
+ }
159
+ catch (error) {
160
+ console.error(`[${this.id} Provider] Error during OAuth callback:`, error);
161
+ return null;
162
+ }
163
+ }
164
+ /**
165
+ * Método opcional para logout
166
+ */
167
+ async handleSignOut() {
168
+ // Discord OAuth não precisa de logout especial
169
+ // O token será invalidado pelo tempo de vida
170
+ console.log(`[${this.id} Provider] User signed out`);
171
+ }
172
+ /**
173
+ * Gera URL de autorização do Discord
174
+ */
175
+ getAuthorizationUrl() {
176
+ const params = new URLSearchParams({
177
+ client_id: this.config.clientId,
178
+ redirect_uri: this.config.callbackUrl || '',
179
+ response_type: 'code',
180
+ scope: (this.config.scope || this.defaultScope).join(' ')
181
+ });
182
+ return `https://discord.com/api/oauth2/authorize?${params.toString()}`;
183
+ }
184
+ /**
185
+ * Retorna configuração pública do provider
186
+ */
187
+ getConfig() {
188
+ return {
189
+ id: this.id,
190
+ name: this.name,
191
+ type: this.type,
192
+ clientId: this.config.clientId, // Público
193
+ scope: this.config.scope || this.defaultScope,
194
+ callbackUrl: this.config.callbackUrl
195
+ };
196
+ }
197
+ }
198
+ exports.DiscordProvider = DiscordProvider;
@@ -0,0 +1,2 @@
1
+ export * from './credentials';
2
+ export * from './discord';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // Exportações dos providers
18
+ __exportStar(require("./credentials"), exports);
19
+ __exportStar(require("./discord"), exports);
@@ -1,5 +1,2 @@
1
- import type { AuthProvider, CredentialsConfig } from './types';
2
- /**
3
- * Provider para autenticação com credenciais (email/senha)
4
- */
5
- export declare function CredentialsProvider(config: CredentialsConfig): AuthProvider;
1
+ export { CredentialsProvider } from './providers/credentials';
2
+ export { DiscordProvider } from './providers/discord';
@@ -1,14 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CredentialsProvider = CredentialsProvider;
4
- /**
5
- * Provider para autenticação com credenciais (email/senha)
6
- */
7
- function CredentialsProvider(config) {
8
- return {
9
- id: config.id || 'credentials',
10
- name: config.name || 'Credentials',
11
- type: 'credentials',
12
- authorize: config.authorize
13
- };
14
- }
3
+ exports.DiscordProvider = exports.CredentialsProvider = void 0;
4
+ // Exportações dos providers
5
+ var credentials_1 = require("./providers/credentials");
6
+ Object.defineProperty(exports, "CredentialsProvider", { enumerable: true, get: function () { return credentials_1.CredentialsProvider; } });
7
+ var discord_1 = require("./providers/discord");
8
+ Object.defineProperty(exports, "DiscordProvider", { enumerable: true, get: function () { return discord_1.DiscordProvider; } });
@@ -5,7 +5,7 @@ exports.useSession = useSession;
5
5
  exports.useAuth = useAuth;
6
6
  const jsx_runtime_1 = require("react/jsx-runtime");
7
7
  const react_1 = require("react");
8
- const client_1 = require("../client");
8
+ const clientRouter_1 = require("../client/clientRouter");
9
9
  const SessionContext = (0, react_1.createContext)(undefined);
10
10
  function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0, refetchOnWindowFocus = true }) {
11
11
  const [session, setSession] = (0, react_1.useState)(null);
@@ -57,21 +57,28 @@ function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0
57
57
  });
58
58
  const data = await response.json();
59
59
  if (response.ok && data.success) {
60
- // Atualiza a sessão após login bem-sucedido
61
- if (redirect && typeof window !== 'undefined') {
62
- try {
63
- client_1.router.push(callbackUrl || '/');
60
+ // Se é OAuth, redireciona para URL fornecida
61
+ if (data.type === 'oauth' && data.redirectUrl) {
62
+ if (redirect && typeof window !== 'undefined') {
63
+ window.location.href = data.redirectUrl;
64
64
  }
65
- catch (e) {
65
+ return {
66
+ ok: true,
67
+ status: 200,
68
+ url: data.redirectUrl
69
+ };
70
+ }
71
+ // Se é sessão (credentials), redireciona para callbackUrl
72
+ if (data.type === 'session') {
73
+ if (redirect && typeof window !== 'undefined') {
66
74
  window.location.href = callbackUrl || '/';
67
75
  }
76
+ return {
77
+ ok: true,
78
+ status: 200,
79
+ url: callbackUrl || '/'
80
+ };
68
81
  }
69
- await fetchSession();
70
- return {
71
- ok: true,
72
- status: 200,
73
- url: callbackUrl || '/'
74
- };
75
82
  }
76
83
  else {
77
84
  return {
@@ -101,7 +108,7 @@ function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0
101
108
  setStatus('unauthenticated');
102
109
  if (typeof window !== 'undefined') {
103
110
  try {
104
- client_1.router.push(options.callbackUrl || '/');
111
+ clientRouter_1.router.push(options.callbackUrl || '/');
105
112
  }
106
113
  catch (e) {
107
114
  window.location.href = options.callbackUrl || '/';
@@ -1,4 +1,4 @@
1
- import { HightJSRequest, HightJSResponse } from '../api/http';
1
+ import { HightJSRequest } from '../api/http';
2
2
  import type { AuthConfig } from './types';
3
3
  import { HWebAuth } from './core';
4
4
  /**
@@ -8,7 +8,7 @@ export declare function createAuthRoutes(config: AuthConfig): {
8
8
  pattern: string;
9
9
  GET(req: HightJSRequest, params: {
10
10
  [key: string]: string;
11
- }): Promise<HightJSResponse>;
11
+ }): Promise<any>;
12
12
  POST(req: HightJSRequest, params: {
13
13
  [key: string]: string;
14
14
  }): Promise<any>;