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.
@@ -13,17 +13,31 @@ function createAuthRoutes(config) {
13
13
  * Uso: /api/auth/[...value].ts
14
14
  */
15
15
  return {
16
- pattern: '/api/auth/[value]',
16
+ pattern: '/api/auth/[...value]',
17
17
  async GET(req, params) {
18
18
  const path = params["value"];
19
19
  const route = Array.isArray(path) ? path.join('/') : path || '';
20
+ // Verifica rotas adicionais dos providers primeiro
21
+ const additionalRoutes = auth.getAllAdditionalRoutes();
22
+ for (const { provider, route: additionalRoute } of additionalRoutes) {
23
+ if (additionalRoute.method === 'GET' && additionalRoute.path.includes(route)) {
24
+ try {
25
+ return await additionalRoute.handler(req, params);
26
+ }
27
+ catch (error) {
28
+ console.error(`[${provider} Provider] Error in additional route:`, error);
29
+ return http_1.HightJSResponse.json({ error: 'Provider route error' }, { status: 500 });
30
+ }
31
+ }
32
+ }
33
+ // Rotas padrão do sistema
20
34
  switch (route) {
21
35
  case 'session':
22
36
  return await handleSession(req, auth);
23
37
  case 'csrf':
24
38
  return await handleCsrf(req);
25
39
  case 'providers':
26
- return await handleProviders(config);
40
+ return await handleProviders(auth);
27
41
  default:
28
42
  return http_1.HightJSResponse.json({ error: 'Route not found' }, { status: 404 });
29
43
  }
@@ -31,6 +45,20 @@ function createAuthRoutes(config) {
31
45
  async POST(req, params) {
32
46
  const path = params["value"];
33
47
  const route = Array.isArray(path) ? path.join('/') : path || '';
48
+ // Verifica rotas adicionais dos providers primeiro
49
+ const additionalRoutes = auth.getAllAdditionalRoutes();
50
+ for (const { provider, route: additionalRoute } of additionalRoutes) {
51
+ if (additionalRoute.method === 'POST' && additionalRoute.path.includes(route)) {
52
+ try {
53
+ return await additionalRoute.handler(req, params);
54
+ }
55
+ catch (error) {
56
+ console.error(`[${provider} Provider] Error in additional route:`, error);
57
+ return http_1.HightJSResponse.json({ error: 'Provider route error' }, { status: 500 });
58
+ }
59
+ }
60
+ }
61
+ // Rotas padrão do sistema
34
62
  switch (route) {
35
63
  case 'signin':
36
64
  return await handleSignIn(req, auth);
@@ -66,14 +94,8 @@ async function handleCsrf(req) {
66
94
  /**
67
95
  * Handler para GET /api/auth/providers
68
96
  */
69
- async function handleProviders(config) {
70
- const providers = config.providers
71
- .filter(p => p.type === 'credentials') // Apenas credentials
72
- .map(p => ({
73
- id: p.id,
74
- name: p.name,
75
- type: p.type
76
- }));
97
+ async function handleProviders(auth) {
98
+ const providers = auth.getProviders();
77
99
  return http_1.HightJSResponse.json({ providers });
78
100
  }
79
101
  /**
@@ -82,17 +104,27 @@ async function handleProviders(config) {
82
104
  async function handleSignIn(req, auth) {
83
105
  try {
84
106
  const { provider = 'credentials', ...credentials } = await req.json();
85
- // Apenas credentials agora
86
107
  const result = await auth.signIn(provider, credentials);
87
108
  if (!result) {
88
109
  return http_1.HightJSResponse.json({ error: 'Invalid credentials' }, { status: 401 });
89
110
  }
111
+ // Se tem redirectUrl, é OAuth - retorna URL para redirecionamento
112
+ if ('redirectUrl' in result) {
113
+ return http_1.HightJSResponse.json({
114
+ success: true,
115
+ redirectUrl: result.redirectUrl,
116
+ type: 'oauth'
117
+ });
118
+ }
119
+ // Se tem session, é credentials - retorna sessão
90
120
  return auth.createAuthResponse(result.token, {
91
121
  success: true,
92
- user: result.session.user
122
+ user: result.session.user,
123
+ type: 'session'
93
124
  });
94
125
  }
95
126
  catch (error) {
127
+ console.error('[hweb-auth] Erro no handleSignIn:', error);
96
128
  return http_1.HightJSResponse.json({ error: 'Authentication failed' }, { status: 500 });
97
129
  }
98
130
  }
@@ -100,5 +132,5 @@ async function handleSignIn(req, auth) {
100
132
  * Handler para POST /api/auth/signout
101
133
  */
102
134
  async function handleSignOut(req, auth) {
103
- return auth.signOut();
135
+ return await auth.signOut(req);
104
136
  }
@@ -4,8 +4,43 @@ export interface Session {
4
4
  expires: string;
5
5
  accessToken?: string;
6
6
  }
7
+ export interface SignInOptions {
8
+ redirect?: boolean;
9
+ callbackUrl?: string;
10
+ [key: string]: any;
11
+ }
12
+ export interface SignInResult {
13
+ error?: string;
14
+ status?: number;
15
+ ok?: boolean;
16
+ url?: string;
17
+ }
18
+ export interface SessionContextType {
19
+ data: Session | null;
20
+ status: 'loading' | 'authenticated' | 'unauthenticated';
21
+ signIn: (provider?: string, options?: SignInOptions) => Promise<SignInResult | undefined>;
22
+ signOut: (options?: {
23
+ callbackUrl?: string;
24
+ }) => Promise<void>;
25
+ update: () => Promise<Session | null>;
26
+ }
27
+ export interface AuthRoute {
28
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE';
29
+ path: string;
30
+ handler: (req: any, params: any) => Promise<any>;
31
+ }
32
+ export interface AuthProviderClass {
33
+ id: string;
34
+ name: string;
35
+ type: string;
36
+ handleOauth?(credentials: Record<string, string>): Promise<string> | string;
37
+ handleSignIn(credentials: Record<string, string>): Promise<User | string | null>;
38
+ handleSignOut?(): Promise<void>;
39
+ additionalRoutes?: AuthRoute[];
40
+ getConfig?(): any;
41
+ }
7
42
  export interface AuthConfig {
8
- providers: AuthProvider[];
43
+ providers: AuthProviderClass[];
9
44
  pages?: {
10
45
  signIn?: string;
11
46
  signOut?: string;
@@ -30,26 +65,6 @@ export interface AuthProvider {
30
65
  type: 'credentials';
31
66
  authorize?: (credentials: Record<string, string>) => Promise<User | null> | User | null;
32
67
  }
33
- export interface SignInOptions {
34
- redirect?: boolean;
35
- callbackUrl?: string;
36
- [key: string]: any;
37
- }
38
- export interface SignInResult {
39
- error?: string;
40
- status?: number;
41
- ok?: boolean;
42
- url?: string;
43
- }
44
- export interface SessionContextType {
45
- data: Session | null;
46
- status: 'loading' | 'authenticated' | 'unauthenticated';
47
- signIn: (provider?: string, options?: SignInOptions) => Promise<SignInResult | undefined>;
48
- signOut: (options?: {
49
- callbackUrl?: string;
50
- }) => Promise<void>;
51
- update: () => Promise<Session | null>;
52
- }
53
68
  export interface CredentialsConfig {
54
69
  id?: string;
55
70
  name?: string;
package/dist/router.js CHANGED
@@ -309,7 +309,15 @@ function findMatchingBackendRoute(pathname, method) {
309
309
  // Verifica se a rota tem um handler para o método HTTP atual
310
310
  if (!route.pattern || !route[method.toUpperCase()])
311
311
  continue;
312
- const regexPattern = route.pattern.replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
312
+ const regexPattern = route.pattern
313
+ // [[...param]] → opcional catch-all
314
+ .replace(/\[\[\.\.\.(\w+)\]\]/g, '(?<$1>.+)?')
315
+ // [...param] → obrigatório catch-all
316
+ .replace(/\[\.\.\.(\w+)\]/g, '(?<$1>.+)')
317
+ // [[param]] → segmento opcional
318
+ .replace(/\[\[(\w+)\]\]/g, '(?<$1>[^/]+)?')
319
+ // [param] → segmento obrigatório
320
+ .replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
313
321
  const regex = new RegExp(`^${regexPattern}/?$`);
314
322
  const match = pathname.match(regex);
315
323
  if (match) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hightjs",
3
- "version": "0.1.1",
3
+ "version": "0.2.2",
4
4
  "description": "HightJS is a high-level framework for building web applications with ease and speed. It provides a robust set of tools and features to streamline development and enhance productivity.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/api/http.ts CHANGED
@@ -407,12 +407,16 @@ export class HightJSResponse {
407
407
  }
408
408
  });
409
409
 
410
+ // Handle redirects specifically
411
+ if (this._headers['Location']) {
412
+ res.redirect(this._headers['Location']);
413
+ return;
414
+ }
415
+
410
416
  // Envia o corpo se foi definido
411
417
  if (this._sent && this._body !== null) {
412
418
  if (this._headers['Content-Type']?.includes('application/json')) {
413
419
  res.json(JSON.parse(this._body));
414
- } else if (this._headers['Location']) {
415
- res.redirect(this._headers['Location']);
416
420
  } else {
417
421
  res.send(this._body);
418
422
  }
@@ -6,9 +6,6 @@ export function setBasePath(path: string) {
6
6
  basePath = path;
7
7
  }
8
8
 
9
-
10
-
11
-
12
9
  /**
13
10
  * Função para obter a sessão atual (similar ao NextAuth getSession)
14
11
  */
@@ -72,3 +69,87 @@ export async function getProviders(): Promise<any[] | null> {
72
69
  }
73
70
  }
74
71
 
72
+ /**
73
+ * Função para fazer login (similar ao NextAuth signIn)
74
+ */
75
+ export async function signIn(
76
+ provider: string = 'credentials',
77
+ options: SignInOptions = {}
78
+ ): Promise<SignInResult | undefined> {
79
+ try {
80
+ const { redirect = true, callbackUrl, ...credentials } = options;
81
+
82
+ const response = await fetch(`${basePath}/signin`, {
83
+ method: 'POST',
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ },
87
+ credentials: 'include',
88
+ body: JSON.stringify({
89
+ provider,
90
+ ...credentials
91
+ })
92
+ });
93
+
94
+ const data = await response.json();
95
+
96
+ if (response.ok && data.success) {
97
+ // Se é OAuth, redireciona para URL fornecida
98
+ if (data.type === 'oauth' && data.redirectUrl) {
99
+ if (redirect && typeof window !== 'undefined') {
100
+ window.location.href = data.redirectUrl;
101
+ }
102
+
103
+ return {
104
+ ok: true,
105
+ status: 200,
106
+ url: data.redirectUrl
107
+ };
108
+ }
109
+
110
+ // Se é sessão (credentials), redireciona para callbackUrl
111
+ if (data.type === 'session') {
112
+ if (redirect && typeof window !== 'undefined') {
113
+ window.location.href = callbackUrl || '/';
114
+ }
115
+
116
+ return {
117
+ ok: true,
118
+ status: 200,
119
+ url: callbackUrl || '/'
120
+ };
121
+ }
122
+ } else {
123
+ return {
124
+ error: data.error || 'Authentication failed',
125
+ status: response.status,
126
+ ok: false
127
+ };
128
+ }
129
+ } catch (error) {
130
+ console.error('[hweb-auth] Erro no signIn:', error);
131
+ return {
132
+ error: 'Network error',
133
+ status: 500,
134
+ ok: false
135
+ };
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Função para fazer logout (similar ao NextAuth signOut)
141
+ */
142
+ export async function signOut(options: { callbackUrl?: string } = {}): Promise<void> {
143
+ try {
144
+ await fetch(`${basePath}/signout`, {
145
+ method: 'POST',
146
+ credentials: 'include'
147
+ });
148
+
149
+ if (typeof window !== 'undefined') {
150
+ window.location.href = options.callbackUrl || '/';
151
+ }
152
+ } catch (error) {
153
+ console.error('[hweb-auth] Erro no signOut:', error);
154
+ }
155
+ }
package/src/auth/core.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HightJSRequest, HightJSResponse } from '../api/http';
2
- import type { AuthConfig, AuthProvider, User, Session } from './types';
2
+ import type { AuthConfig, AuthProviderClass, User, Session } from './types';
3
3
  import { SessionManager } from './jwt';
4
4
 
5
5
  export class HWebAuth {
@@ -37,38 +37,45 @@ export class HWebAuth {
37
37
  }
38
38
 
39
39
  /**
40
- * Autentica um usuário com credenciais
40
+ * Autentica um usuário usando um provider específico
41
41
  */
42
- async signIn(provider: string, credentials: Record<string, string>): Promise<{ session: Session; token: string } | null> {
43
- const authProvider = this.config.providers.find(p => p.id === provider);
44
- if (!authProvider || authProvider.type !== 'credentials') {
45
- return null;
46
- }
47
-
48
- if (!authProvider.authorize) {
42
+ async signIn(providerId: string, credentials: Record<string, string>): Promise<{ session: Session; token: string } | { redirectUrl: string } | null> {
43
+ const provider = this.config.providers.find(p => p.id === providerId);
44
+ if (!provider) {
45
+ console.error(`[hweb-auth] Provider not found: ${providerId}`);
49
46
  return null;
50
47
  }
51
48
 
52
49
  try {
53
- const user = await authProvider.authorize(credentials);
54
- if (!user) return null;
50
+ // Usa o método handleSignIn do provider
51
+ const result = await provider.handleSignIn(credentials);
52
+
53
+ if (!result) return null;
54
+
55
+ // Se resultado é string, é URL de redirecionamento OAuth
56
+ if (typeof result === 'string') {
57
+ return { redirectUrl: result };
58
+ }
59
+
60
+ // Se resultado é User, cria sessão
61
+ const user = result as User;
55
62
 
56
63
  // Callback de signIn se definido
57
64
  if (this.config.callbacks?.signIn) {
58
- const allowed = await this.config.callbacks.signIn(user, { provider }, {});
65
+ const allowed = await this.config.callbacks.signIn(user, { provider: providerId }, {});
59
66
  if (!allowed) return null;
60
67
  }
61
68
 
62
- const result = this.sessionManager.createSession(user);
69
+ const sessionResult = this.sessionManager.createSession(user);
63
70
 
64
71
  // Callback de sessão se definido
65
72
  if (this.config.callbacks?.session) {
66
- result.session = await this.config.callbacks.session(result.session, user);
73
+ sessionResult.session = await this.config.callbacks.session(sessionResult.session, user);
67
74
  }
68
75
 
69
- return result;
76
+ return sessionResult;
70
77
  } catch (error) {
71
- console.error('[hweb-auth] Erro no signIn:', error);
78
+ console.error(`[hweb-auth] Erro no signIn com provider ${providerId}:`, error);
72
79
  return null;
73
80
  }
74
81
  }
@@ -76,14 +83,28 @@ export class HWebAuth {
76
83
  /**
77
84
  * Faz logout do usuário
78
85
  */
79
- signOut(): HightJSResponse {
86
+ async signOut(req: HightJSRequest): Promise<HightJSResponse> {
87
+ // Busca a sessão atual para saber qual provider usar
88
+ const { session } = await this.middleware(req);
89
+
90
+ if (session?.user?.provider) {
91
+ const provider = this.config.providers.find(p => p.id === session.user.provider);
92
+ if (provider && provider.handleSignOut) {
93
+ try {
94
+ await provider.handleSignOut();
95
+ } catch (error) {
96
+ console.error(`[hweb-auth] Erro no signOut do provider ${provider.id}:`, error);
97
+ }
98
+ }
99
+ }
100
+
80
101
  return HightJSResponse
81
102
  .json({ success: true })
82
103
  .clearCookie('hweb-auth-token', {
83
104
  path: '/',
84
105
  httpOnly: true,
85
- secure: true, // Always use secure cookies
86
- sameSite: 'strict' // Stronger CSRF protection
106
+ secure: true,
107
+ sameSite: 'strict'
87
108
  });
88
109
  }
89
110
 
@@ -103,6 +124,41 @@ export class HWebAuth {
103
124
  return session !== null;
104
125
  }
105
126
 
127
+ /**
128
+ * Retorna todos os providers disponíveis (dados públicos)
129
+ */
130
+ getProviders(): any[] {
131
+ return this.config.providers.map(provider => ({
132
+ id: provider.id,
133
+ name: provider.name,
134
+ type: provider.type,
135
+ config: provider.getConfig ? provider.getConfig() : {}
136
+ }));
137
+ }
138
+
139
+ /**
140
+ * Busca um provider específico
141
+ */
142
+ getProvider(id: string): AuthProviderClass | null {
143
+ return this.config.providers.find(p => p.id === id) || null;
144
+ }
145
+
146
+ /**
147
+ * Retorna todas as rotas adicionais dos providers
148
+ */
149
+ getAllAdditionalRoutes(): Array<{ provider: string; route: any }> {
150
+ const routes: Array<{ provider: string; route: any }> = [];
151
+
152
+ for (const provider of this.config.providers) {
153
+ if (provider.additionalRoutes) {
154
+ for (const route of provider.additionalRoutes) {
155
+ routes.push({ provider: provider.id, route });
156
+ }
157
+ }
158
+ }
159
+
160
+ return routes;
161
+ }
106
162
 
107
163
  /**
108
164
  * Cria resposta com cookie de autenticação - Secure implementation
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Exemplo de como usar os novos providers baseados em classes
3
+ */
4
+
5
+ import { createAuthRoutes } from './routes';
6
+ import { CredentialsProvider, DiscordProvider } from './providers';
7
+ import type { AuthConfig } from './types';
8
+
9
+ // Exemplo de configuração com os novos providers
10
+ const authConfig: AuthConfig = {
11
+ providers: [
12
+ // Provider de credenciais customizado
13
+ new CredentialsProvider({
14
+ name: "Login com Email",
15
+ credentials: {
16
+ email: {
17
+ label: "Email",
18
+ type: "email",
19
+ placeholder: "seu@email.com"
20
+ },
21
+ password: {
22
+ label: "Senha",
23
+ type: "password"
24
+ }
25
+ },
26
+ async authorize(credentials) {
27
+ // Aqui você faz a validação com seu banco de dados
28
+ const { email, password } = credentials;
29
+
30
+ // Exemplo de validação (substitua pela sua lógica)
31
+ if (email === "admin@example.com" && password === "123456") {
32
+ return {
33
+ id: "1",
34
+ name: "Admin User",
35
+ email: email,
36
+ role: "admin"
37
+ };
38
+ }
39
+
40
+ // Retorna null se credenciais inválidas
41
+ return null;
42
+ }
43
+ }),
44
+
45
+ // Provider do Discord
46
+ new DiscordProvider({
47
+ clientId: process.env.DISCORD_CLIENT_ID!,
48
+ clientSecret: process.env.DISCORD_CLIENT_SECRET!,
49
+ callbackUrl: "http://localhost:3000/api/auth/callback/discord"
50
+ })
51
+ ],
52
+
53
+ secret: process.env.HWEB_AUTH_SECRET || "seu-super-secret-aqui-32-chars-min",
54
+
55
+ session: {
56
+ strategy: 'jwt',
57
+ maxAge: 86400 // 24 horas
58
+ },
59
+
60
+ pages: {
61
+ signIn: '/auth/signin',
62
+ signOut: '/auth/signout'
63
+ },
64
+
65
+ callbacks: {
66
+ async signIn(user, account, profile) {
67
+ // Lógica customizada antes do login
68
+ console.log(`Usuário ${user.email} fazendo login via ${account.provider}`);
69
+ return true; // permitir login
70
+ },
71
+
72
+ async session(session, user) {
73
+ // Adicionar dados customizados à sessão
74
+ return {
75
+ ...session,
76
+ user: {
77
+ ...session.user,
78
+ customData: "dados extras"
79
+ }
80
+ };
81
+ }
82
+ }
83
+ };
84
+
85
+ // Criar as rotas de autenticação
86
+ export const authRoutes = createAuthRoutes(authConfig);
87
+
88
+ /**
89
+ * Como usar em suas rotas API:
90
+ *
91
+ * // arquivo: /api/auth/[...value].ts
92
+ * import { authRoutes } from '../../../src/auth/example';
93
+ *
94
+ * export const GET = authRoutes.GET;
95
+ * export const POST = authRoutes.POST;
96
+ */
97
+
98
+ /**
99
+ * Rotas disponíveis automaticamente:
100
+ *
101
+ * Core routes:
102
+ * - GET /api/auth/session - Obter sessão atual
103
+ * - GET /api/auth/providers - Listar providers
104
+ * - GET /api/auth/csrf - Obter token CSRF
105
+ * - POST /api/auth/signin - Login
106
+ * - POST /api/auth/signout - Logout
107
+ *
108
+ * Provider específico (CredentialsProvider):
109
+ * - GET /api/auth/credentials/config - Config do provider
110
+ *
111
+ * Provider específico (DiscordProvider):
112
+ * - GET /api/auth/signin/discord - Iniciar OAuth Discord
113
+ * - GET /api/auth/callback/discord - Callback OAuth Discord
114
+ * - GET /api/auth/discord/config - Config do provider Discord
115
+ */
package/src/auth/index.ts CHANGED
@@ -5,5 +5,6 @@ export * from './core';
5
5
  export * from './routes';
6
6
  export * from './jwt';
7
7
 
8
- export { CredentialsProvider } from './providers';
8
+ export { CredentialsProvider, DiscordProvider } from './providers';
9
9
  export { createAuthRoutes } from './routes';
10
+