@vatts/auth 2.1.2 → 2.1.3-canary.1.0.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/core.js CHANGED
@@ -103,7 +103,8 @@ class VattsAuth {
103
103
  path: '/',
104
104
  httpOnly: true,
105
105
  secure: this.config.secureCookies || false,
106
- sameSite: 'strict'
106
+ sameSite: 'strict',
107
+ domain: this.config.domain || undefined
107
108
  });
108
109
  }
109
110
  /**
@@ -163,7 +164,7 @@ class VattsAuth {
163
164
  sameSite: 'strict', // Prevent CSRF attacks
164
165
  maxAge: (this.config.session?.maxAge || 86400) * 1000,
165
166
  path: '/',
166
- domain: undefined // Let browser set automatically for security
167
+ domain: this.config.domain || undefined // Let browser set automatically for security
167
168
  })
168
169
  // SECURITY: Comprehensive security headers
169
170
  .header('X-Content-Type-Options', 'nosniff')
@@ -55,7 +55,7 @@ export declare class DiscordProvider implements AuthProviderClass {
55
55
  /**
56
56
  * Gera URL de autorização do Discord
57
57
  */
58
- getAuthorizationUrl(): string;
58
+ getAuthorizationUrl(isPopup?: boolean): string;
59
59
  /**
60
60
  * Retorna configuração pública do provider
61
61
  */
@@ -38,7 +38,13 @@ class DiscordProvider {
38
38
  handler: async (req, params) => {
39
39
  const url = new URL(req.url || '', 'http://localhost');
40
40
  const code = url.searchParams.get('code');
41
+ const state = url.searchParams.get('state');
42
+ // Detecta se é modo popup pela state
43
+ const isPopup = state?.includes('popup=true');
41
44
  if (!code) {
45
+ if (isPopup) {
46
+ return vatts_1.VattsResponse.redirect(`/api/auth/popup-callback?success=false&error=Authorization+code+not+provided&provider=discord`);
47
+ }
42
48
  return vatts_1.VattsResponse.json({ error: 'Authorization code not provided' }, { status: 400 });
43
49
  }
44
50
  try {
@@ -56,9 +62,15 @@ class DiscordProvider {
56
62
  })
57
63
  });
58
64
  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
65
  const setCookieHeader = authResponse.headers.get('set-cookie');
66
+ // Se é popup, redireciona para popup-callback
67
+ if (isPopup) {
68
+ const callbackUrl = this.config.successUrl || '/';
69
+ return vatts_1.VattsResponse
70
+ .redirect(`/api/auth/popup-callback?success=true&provider=discord&callbackUrl=${encodeURIComponent(callbackUrl)}`)
71
+ .header('Set-Cookie', setCookieHeader || '');
72
+ }
73
+ // Comportamento normal
62
74
  if (this.config.successUrl) {
63
75
  return vatts_1.VattsResponse
64
76
  .redirect(this.config.successUrl)
@@ -70,11 +82,17 @@ class DiscordProvider {
70
82
  else {
71
83
  const errorText = await authResponse.text();
72
84
  console.error(`[${this.id} Provider] Session creation failed during callback. Status: ${authResponse.status}, Body: ${errorText}`);
85
+ if (isPopup) {
86
+ return vatts_1.VattsResponse.redirect(`/api/auth/popup-callback?success=false&error=Session+creation+failed&provider=discord`);
87
+ }
73
88
  return vatts_1.VattsResponse.json({ error: 'Session creation failed' }, { status: 500 });
74
89
  }
75
90
  }
76
91
  catch (error) {
77
92
  console.error(`[${this.id} Provider] Callback handler fetch error:`, error);
93
+ if (isPopup) {
94
+ return vatts_1.VattsResponse.redirect(`/api/auth/popup-callback?success=false&error=Internal+server+error&provider=discord`);
95
+ }
78
96
  return vatts_1.VattsResponse.json({ error: 'Internal server error' }, { status: 500 });
79
97
  }
80
98
  }
@@ -88,7 +106,7 @@ class DiscordProvider {
88
106
  * Método para gerar URL OAuth (usado pelo handleSignIn)
89
107
  */
90
108
  handleOauth(credentials = {}) {
91
- return this.getAuthorizationUrl();
109
+ return this.getAuthorizationUrl(credentials.popup === 'true');
92
110
  }
93
111
  /**
94
112
  * Método principal - agora redireciona para OAuth ou processa callback
@@ -164,13 +182,17 @@ class DiscordProvider {
164
182
  /**
165
183
  * Gera URL de autorização do Discord
166
184
  */
167
- getAuthorizationUrl() {
185
+ getAuthorizationUrl(isPopup = false) {
168
186
  const params = new URLSearchParams({
169
187
  client_id: this.config.clientId,
170
188
  redirect_uri: this.config.callbackUrl || '',
171
189
  response_type: 'code',
172
190
  scope: (this.config.scope || this.defaultScope).join(' ')
173
191
  });
192
+ // Adiciona state para indicar modo popup
193
+ if (isPopup) {
194
+ params.set('state', `popup=true&timestamp=${Date.now()}`);
195
+ }
174
196
  return `https://discord.com/api/oauth2/authorize?${params.toString()}`;
175
197
  }
176
198
  /**
@@ -55,7 +55,7 @@ export declare class GithubProvider implements AuthProviderClass {
55
55
  /**
56
56
  * Gera a URL de autorização do GitHub
57
57
  */
58
- getAuthorizationUrl(): string;
58
+ getAuthorizationUrl(isPopup?: boolean): string;
59
59
  /**
60
60
  * Retorna a configuração pública do provider
61
61
  */
@@ -41,7 +41,13 @@ class GithubProvider {
41
41
  handler: async (req, params) => {
42
42
  const url = new URL(req.url || '', 'http://localhost');
43
43
  const code = url.searchParams.get('code');
44
+ const state = url.searchParams.get('state');
45
+ // Detecta se é modo popup pela state
46
+ const isPopup = state?.includes('popup=true');
44
47
  if (!code) {
48
+ if (isPopup) {
49
+ return vatts_1.VattsResponse.redirect(`/api/auth/popup-callback?success=false&error=Authorization+code+not+provided&provider=github`);
50
+ }
45
51
  return vatts_1.VattsResponse.json({ error: 'Authorization code not provided' }, { status: 400 });
46
52
  }
47
53
  try {
@@ -57,8 +63,15 @@ class GithubProvider {
57
63
  })
58
64
  });
59
65
  if (authResponse.ok) {
60
- // Propaga o cookie de sessão e redireciona para a URL de sucesso
61
66
  const setCookieHeader = authResponse.headers.get('set-cookie');
67
+ // Se é popup, redireciona para popup-callback
68
+ if (isPopup) {
69
+ const callbackUrl = this.config.successUrl || '/';
70
+ return vatts_1.VattsResponse
71
+ .redirect(`/api/auth/popup-callback?success=true&provider=github&callbackUrl=${encodeURIComponent(callbackUrl)}`)
72
+ .header('Set-Cookie', setCookieHeader || '');
73
+ }
74
+ // Comportamento normal
62
75
  if (this.config.successUrl) {
63
76
  return vatts_1.VattsResponse
64
77
  .redirect(this.config.successUrl)
@@ -70,11 +83,17 @@ class GithubProvider {
70
83
  else {
71
84
  const errorText = await authResponse.text();
72
85
  console.error(`[${this.id} Provider] Session creation failed during callback. Status: ${authResponse.status}, Body: ${errorText}`);
86
+ if (isPopup) {
87
+ return vatts_1.VattsResponse.redirect(`/api/auth/popup-callback?success=false&error=Session+creation+failed&provider=github`);
88
+ }
73
89
  return vatts_1.VattsResponse.json({ error: 'Session creation failed' }, { status: 500 });
74
90
  }
75
91
  }
76
92
  catch (error) {
77
93
  console.error(`[${this.id} Provider] Callback handler fetch error:`, error);
94
+ if (isPopup) {
95
+ return vatts_1.VattsResponse.redirect(`/api/auth/popup-callback?success=false&error=Internal+server+error&provider=github`);
96
+ }
78
97
  return vatts_1.VattsResponse.json({ error: 'Internal server error' }, { status: 500 });
79
98
  }
80
99
  }
@@ -88,7 +107,7 @@ class GithubProvider {
88
107
  * Método para gerar URL OAuth (usado pelo handleSignIn)
89
108
  */
90
109
  handleOauth(credentials = {}) {
91
- return this.getAuthorizationUrl();
110
+ return this.getAuthorizationUrl(credentials.popup === 'true');
92
111
  }
93
112
  /**
94
113
  * Método principal - redireciona para OAuth ou processa o callback
@@ -191,7 +210,7 @@ class GithubProvider {
191
210
  /**
192
211
  * Gera a URL de autorização do GitHub
193
212
  */
194
- getAuthorizationUrl() {
213
+ getAuthorizationUrl(isPopup = false) {
195
214
  const params = new URLSearchParams({
196
215
  client_id: this.config.clientId,
197
216
  redirect_uri: this.config.callbackUrl || '',
@@ -199,6 +218,10 @@ class GithubProvider {
199
218
  // GitHub não usa 'response_type=code' explicitamente na URL padrão, mas aceita.
200
219
  // O padrão é web application flow.
201
220
  });
221
+ // Adiciona state para indicar modo popup
222
+ if (isPopup) {
223
+ params.set('state', `popup=true&timestamp=${Date.now()}`);
224
+ }
202
225
  return `https://github.com/login/oauth/authorize?${params.toString()}`;
203
226
  }
204
227
  /**
@@ -55,7 +55,7 @@ export declare class GoogleProvider implements AuthProviderClass {
55
55
  /**
56
56
  * Gera a URL de autorização do Google
57
57
  */
58
- getAuthorizationUrl(): string;
58
+ getAuthorizationUrl(isPopup?: boolean): string;
59
59
  /**
60
60
  * Retorna a configuração pública do provider
61
61
  */
@@ -42,7 +42,13 @@ class GoogleProvider {
42
42
  handler: async (req, params) => {
43
43
  const url = new URL(req.url || '', 'http://localhost');
44
44
  const code = url.searchParams.get('code');
45
+ const state = url.searchParams.get('state');
46
+ // Detecta se é modo popup pela state
47
+ const isPopup = state?.includes('popup=true');
45
48
  if (!code) {
49
+ if (isPopup) {
50
+ return vatts_1.VattsResponse.redirect(`/api/auth/popup-callback?success=false&error=Authorization+code+not+provided&provider=google`);
51
+ }
46
52
  return vatts_1.VattsResponse.json({ error: 'Authorization code not provided' }, { status: 400 });
47
53
  }
48
54
  try {
@@ -58,8 +64,15 @@ class GoogleProvider {
58
64
  })
59
65
  });
60
66
  if (authResponse.ok) {
61
- // Propaga o cookie de sessão e redireciona para a URL de sucesso
62
67
  const setCookieHeader = authResponse.headers.get('set-cookie');
68
+ // Se é popup, redireciona para popup-callback
69
+ if (isPopup) {
70
+ const callbackUrl = this.config.successUrl || '/';
71
+ return vatts_1.VattsResponse
72
+ .redirect(`/api/auth/popup-callback?success=true&provider=google&callbackUrl=${encodeURIComponent(callbackUrl)}`)
73
+ .header('Set-Cookie', setCookieHeader || '');
74
+ }
75
+ // Comportamento normal
63
76
  if (this.config.successUrl) {
64
77
  return vatts_1.VattsResponse
65
78
  .redirect(this.config.successUrl)
@@ -71,11 +84,17 @@ class GoogleProvider {
71
84
  else {
72
85
  const errorText = await authResponse.text();
73
86
  console.error(`[${this.id} Provider] Session creation failed during callback. Status: ${authResponse.status}, Body: ${errorText}`);
87
+ if (isPopup) {
88
+ return vatts_1.VattsResponse.redirect(`/api/auth/popup-callback?success=false&error=Session+creation+failed&provider=google`);
89
+ }
74
90
  return vatts_1.VattsResponse.json({ error: 'Session creation failed' }, { status: 500 });
75
91
  }
76
92
  }
77
93
  catch (error) {
78
94
  console.error(`[${this.id} Provider] Callback handler fetch error:`, error);
95
+ if (isPopup) {
96
+ return vatts_1.VattsResponse.redirect(`/api/auth/popup-callback?success=false&error=Internal+server+error&provider=google`);
97
+ }
79
98
  return vatts_1.VattsResponse.json({ error: 'Internal server error' }, { status: 500 });
80
99
  }
81
100
  }
@@ -89,7 +108,7 @@ class GoogleProvider {
89
108
  * Método para gerar URL OAuth (usado pelo handleSignIn)
90
109
  */
91
110
  handleOauth(credentials = {}) {
92
- return this.getAuthorizationUrl();
111
+ return this.getAuthorizationUrl(credentials.popup === 'true');
93
112
  }
94
113
  /**
95
114
  * Método principal - redireciona para OAuth ou processa o callback
@@ -160,13 +179,17 @@ class GoogleProvider {
160
179
  /**
161
180
  * Gera a URL de autorização do Google
162
181
  */
163
- getAuthorizationUrl() {
182
+ getAuthorizationUrl(isPopup = false) {
164
183
  const params = new URLSearchParams({
165
184
  client_id: this.config.clientId,
166
185
  redirect_uri: this.config.callbackUrl || '',
167
186
  response_type: 'code',
168
187
  scope: (this.config.scope || this.defaultScope).join(' ')
169
188
  });
189
+ // Adiciona state para indicar modo popup
190
+ if (isPopup) {
191
+ params.set('state', `popup=true&timestamp=${Date.now()}`);
192
+ }
170
193
  return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
171
194
  }
172
195
  /**
@@ -1,5 +1,4 @@
1
1
  export * from './react';
2
- export * from '../client';
3
2
  export * from './components';
4
3
  export { useSession, useAuth, SessionProvider } from './react';
5
4
  export { AuthGuard, GuestOnly } from './components';
@@ -33,7 +33,6 @@ exports.GuestOnly = exports.AuthGuard = exports.SessionProvider = exports.useAut
33
33
  */
34
34
  // Exportações do frontend
35
35
  __exportStar(require("./react"), exports);
36
- __exportStar(require("../client"), exports);
37
36
  __exportStar(require("./components"), exports);
38
37
  // Re-exports das funções mais usadas para conveniência
39
38
  var react_1 = require("./react");
@@ -23,6 +23,67 @@ const jsx_runtime_1 = require("react/jsx-runtime");
23
23
  const react_1 = require("react");
24
24
  const react_2 = require("vatts/react");
25
25
  const SessionContext = (0, react_1.createContext)(undefined);
26
+ /**
27
+ * Abre OAuth em popup e aguarda o resultado
28
+ */
29
+ function openOAuthPopup(url, provider, fetchSession, redirect) {
30
+ return new Promise((resolve, reject) => {
31
+ const width = 600;
32
+ const height = 700;
33
+ const left = window.screenX + (window.outerWidth - width) / 2;
34
+ const top = window.screenY + (window.outerHeight - height) / 2;
35
+ const popup = window.open(url, `oauth-${provider}`, `width=${width},height=${height},left=${left},top=${top},toolbar=no,location=no,status=no,menubar=no,scrollbars=yes`);
36
+ if (!popup) {
37
+ resolve({
38
+ error: 'Popup blocked',
39
+ status: 400,
40
+ ok: false
41
+ });
42
+ return;
43
+ }
44
+ // Verifica se o popup foi fechado manualmente
45
+ const checkPopupClosed = setInterval(() => {
46
+ if (popup.closed) {
47
+ clearInterval(checkPopupClosed);
48
+ window.removeEventListener('message', handleMessage);
49
+ resolve({
50
+ error: 'Popup closed',
51
+ status: 400,
52
+ ok: false
53
+ });
54
+ }
55
+ }, 500);
56
+ // Listener para mensagens do popup
57
+ const handleMessage = async (event) => {
58
+ if (event.data?.type === 'oauth-success' && event.data?.provider === provider) {
59
+ clearInterval(checkPopupClosed);
60
+ window.removeEventListener('message', handleMessage);
61
+ popup.close();
62
+ // Atualiza a sessão
63
+ await fetchSession();
64
+ if (redirect && typeof window !== 'undefined') {
65
+ window.location.href = event.data.callbackUrl || '/';
66
+ }
67
+ resolve({
68
+ ok: true,
69
+ status: 200,
70
+ url: event.data.callbackUrl || '/'
71
+ });
72
+ }
73
+ else if (event.data?.type === 'oauth-error' && event.data?.provider === provider) {
74
+ clearInterval(checkPopupClosed);
75
+ window.removeEventListener('message', handleMessage);
76
+ popup.close();
77
+ resolve({
78
+ error: event.data.error || 'Authentication failed',
79
+ status: 401,
80
+ ok: false
81
+ });
82
+ }
83
+ };
84
+ window.addEventListener('message', handleMessage);
85
+ });
86
+ }
26
87
  function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0, refetchOnWindowFocus = true }) {
27
88
  const [session, setSession] = (0, react_1.useState)(null);
28
89
  const [status, setStatus] = (0, react_1.useState)('loading');
@@ -59,7 +120,7 @@ function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0
59
120
  // SignIn function
60
121
  const signIn = (0, react_1.useCallback)(async (provider = 'credentials', options = {}) => {
61
122
  try {
62
- const { redirect = true, callbackUrl, ...credentials } = options;
123
+ const { redirect = true, callbackUrl, popup = false, ...credentials } = options;
63
124
  const response = await fetch(`${basePath}/signin`, {
64
125
  method: 'POST',
65
126
  headers: {
@@ -68,15 +129,19 @@ function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0
68
129
  credentials: 'include',
69
130
  body: JSON.stringify({
70
131
  provider,
71
- ...credentials
132
+ ...credentials,
133
+ popup
72
134
  })
73
135
  });
74
136
  const data = await response.json();
75
137
  if (response.ok && data.success) {
76
- await fetchSession();
77
- // Se é OAuth, redireciona para URL fornecida
138
+ // Se é OAuth, redireciona para URL fornecida ou abre popup
78
139
  if (data.type === 'oauth' && data.redirectUrl) {
79
- if (redirect && typeof window !== 'undefined') {
140
+ if (popup && typeof window !== 'undefined') {
141
+ // Abre em popup
142
+ return await openOAuthPopup(data.redirectUrl, provider, fetchSession, redirect);
143
+ }
144
+ else if (redirect && typeof window !== 'undefined') {
80
145
  window.location.href = data.redirectUrl;
81
146
  }
82
147
  return {
@@ -85,7 +150,8 @@ function SessionProvider({ children, basePath = '/api/auth', refetchInterval = 0
85
150
  url: data.redirectUrl
86
151
  };
87
152
  }
88
- // Se é sessão (credentials), redireciona para callbackUrl
153
+ // Se é sessão (credentials), atualiza e redireciona
154
+ await fetchSession();
89
155
  if (data.type === 'session') {
90
156
  if (redirect && typeof window !== 'undefined') {
91
157
  window.location.href = callbackUrl || '/';
package/dist/routes.js CHANGED
@@ -54,6 +54,8 @@ function createAuthRoutes(config) {
54
54
  return await handleCsrf(req);
55
55
  case 'providers':
56
56
  return await handleProviders(auth);
57
+ case 'popup-callback':
58
+ return handlePopupCallback(req);
57
59
  default:
58
60
  return vatts_1.VattsResponse.json({ error: 'Route not found' }, { status: 404 });
59
61
  }
@@ -120,8 +122,12 @@ async function handleProviders(auth) {
120
122
  */
121
123
  async function handleSignIn(req, auth) {
122
124
  try {
123
- const { provider = 'credentials', ...credentials } = await req.json();
124
- const result = await auth.signIn(provider, credentials);
125
+ const { provider = 'credentials', popup, ...credentials } = await req.json();
126
+ // Se popup está definido, passa para os credentials
127
+ const credentialsWithPopup = popup !== undefined
128
+ ? { ...credentials, popup: String(popup) }
129
+ : credentials;
130
+ const result = await auth.signIn(provider, credentialsWithPopup);
125
131
  if (!result) {
126
132
  return vatts_1.VattsResponse.json({ error: 'Invalid credentials' }, { status: 401 });
127
133
  }
@@ -145,6 +151,89 @@ async function handleSignIn(req, auth) {
145
151
  return vatts_1.VattsResponse.json({ error: 'Authentication failed' }, { status: 500 });
146
152
  }
147
153
  }
154
+ /**
155
+ * Handler para GET /api/auth/popup-callback
156
+ * Retorna uma página HTML que envia mensagem para a janela pai e fecha o popup
157
+ */
158
+ function handlePopupCallback(req) {
159
+ const url = new URL(req.url, 'http://localhost');
160
+ const success = url.searchParams.get('success') === 'true';
161
+ const error = url.searchParams.get('error');
162
+ const provider = url.searchParams.get('provider') || 'unknown';
163
+ const callbackUrl = url.searchParams.get('callbackUrl') || '/';
164
+ const html = `
165
+ <!DOCTYPE html>
166
+ <html>
167
+ <head>
168
+ <meta charset="UTF-8">
169
+ <title>Authenticating...</title>
170
+ <style>
171
+ body {
172
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
173
+ display: flex;
174
+ justify-content: center;
175
+ align-items: center;
176
+ height: 100vh;
177
+ margin: 0;
178
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
179
+ color: white;
180
+ }
181
+ .container {
182
+ text-align: center;
183
+ }
184
+ .spinner {
185
+ border: 4px solid rgba(255,255,255,0.3);
186
+ border-radius: 50%;
187
+ border-top: 4px solid white;
188
+ width: 40px;
189
+ height: 40px;
190
+ animation: spin 1s linear infinite;
191
+ margin: 0 auto 20px;
192
+ }
193
+ @keyframes spin {
194
+ 0% { transform: rotate(0deg); }
195
+ 100% { transform: rotate(360deg); }
196
+ }
197
+ h2 {
198
+ margin: 0;
199
+ font-size: 24px;
200
+ }
201
+ p {
202
+ margin: 10px 0 0;
203
+ opacity: 0.9;
204
+ }
205
+ </style>
206
+ </head>
207
+ <body>
208
+ <div class="container">
209
+ <div class="spinner"></div>
210
+ <h2>${success ? '✓ Autenticação bem-sucedida' : '✗ Erro na autenticação'}</h2>
211
+ <p>${success ? 'Fechando janela...' : (error || 'Algo deu errado')}</p>
212
+ </div>
213
+ <script>
214
+ (function() {
215
+ try {
216
+ if (window.opener) {
217
+ console.log('Enviando mensagem para janela pai:')
218
+ window.opener.postMessage({
219
+ type: ${success ? "'oauth-success'" : "'oauth-error'"},
220
+ provider: "${provider}",
221
+ ${success ? `callbackUrl: "${callbackUrl}"` : `error: "${error || 'Authentication failed'}"`}
222
+ }, window.location.origin);
223
+ }
224
+ setTimeout(() => {
225
+ window.close();
226
+ }, 1000);
227
+ } catch (e) {
228
+ console.error('Error communicating with parent window:', e);
229
+ }
230
+ })();
231
+ </script>
232
+ </body>
233
+ </html>
234
+ `;
235
+ return vatts_1.VattsResponse.html(html);
236
+ }
148
237
  /**
149
238
  * Handler para POST /api/auth/signout
150
239
  */
package/dist/types.d.ts CHANGED
@@ -7,6 +7,7 @@ export interface Session {
7
7
  export interface SignInOptions {
8
8
  redirect?: boolean;
9
9
  callbackUrl?: string;
10
+ popup?: boolean;
10
11
  [key: string]: any;
11
12
  }
12
13
  export interface SignInResult {
@@ -63,6 +64,7 @@ export interface AuthConfig {
63
64
  secret?: string;
64
65
  debug?: boolean;
65
66
  secureCookies?: boolean;
67
+ domain?: string;
66
68
  }
67
69
  export interface CredentialsConfig {
68
70
  id?: string;
@@ -24,6 +24,67 @@ const vue_1 = require("vue");
24
24
  const vue_2 = require("vatts/vue");
25
25
  // Chave de injeção para o TypeScript
26
26
  exports.SessionKey = Symbol('SessionKey');
27
+ /**
28
+ * Abre OAuth em popup e aguarda o resultado
29
+ */
30
+ function openOAuthPopup(url, provider, fetchSession, redirect) {
31
+ return new Promise((resolve, reject) => {
32
+ const width = 600;
33
+ const height = 700;
34
+ const left = window.screenX + (window.outerWidth - width) / 2;
35
+ const top = window.screenY + (window.outerHeight - height) / 2;
36
+ const popup = window.open(url, `oauth-${provider}`, `width=${width},height=${height},left=${left},top=${top},toolbar=no,location=no,status=no,menubar=no,scrollbars=yes`);
37
+ if (!popup) {
38
+ resolve({
39
+ error: 'Popup blocked',
40
+ status: 400,
41
+ ok: false
42
+ });
43
+ return;
44
+ }
45
+ // Verifica se o popup foi fechado manualmente
46
+ const checkPopupClosed = setInterval(() => {
47
+ if (popup.closed) {
48
+ clearInterval(checkPopupClosed);
49
+ window.removeEventListener('message', handleMessage);
50
+ resolve({
51
+ error: 'Popup closed',
52
+ status: 400,
53
+ ok: false
54
+ });
55
+ }
56
+ }, 500);
57
+ // Listener para mensagens do popup
58
+ const handleMessage = async (event) => {
59
+ if (event.data?.type === 'oauth-success' && event.data?.provider === provider) {
60
+ clearInterval(checkPopupClosed);
61
+ window.removeEventListener('message', handleMessage);
62
+ popup.close();
63
+ // Atualiza a sessão
64
+ await fetchSession();
65
+ if (redirect && typeof window !== 'undefined') {
66
+ window.location.href = event.data.callbackUrl || '/';
67
+ }
68
+ resolve({
69
+ ok: true,
70
+ status: 200,
71
+ url: event.data.callbackUrl || '/'
72
+ });
73
+ }
74
+ else if (event.data?.type === 'oauth-error' && event.data?.provider === provider) {
75
+ clearInterval(checkPopupClosed);
76
+ window.removeEventListener('message', handleMessage);
77
+ popup.close();
78
+ resolve({
79
+ error: event.data.error || 'Authentication failed',
80
+ status: 401,
81
+ ok: false
82
+ });
83
+ }
84
+ };
85
+ window.addEventListener('message', handleMessage);
86
+ });
87
+ }
27
88
  /**
28
89
  * Composable que contém toda a lógica do SessionProvider.
29
90
  * Deve ser chamado dentro do setup do componente.
@@ -66,7 +127,7 @@ function useSessionProviderLogic(props) {
66
127
  // SignIn function
67
128
  const signIn = async (provider = 'credentials', options = {}) => {
68
129
  try {
69
- const { redirect = true, callbackUrl, ...credentials } = options;
130
+ const { redirect = true, callbackUrl, popup = false, ...credentials } = options;
70
131
  const response = await fetch(`${props.basePath}/signin`, {
71
132
  method: 'POST',
72
133
  headers: {
@@ -80,13 +141,18 @@ function useSessionProviderLogic(props) {
80
141
  });
81
142
  const data = await response.json();
82
143
  if (response.ok && data.success) {
83
- await fetchSession();
84
144
  if (data.type === 'oauth' && data.redirectUrl) {
85
- if (redirect && typeof window !== 'undefined') {
145
+ if (popup && typeof window !== 'undefined') {
146
+ // Abre em popup
147
+ return await openOAuthPopup(data.redirectUrl, provider, fetchSession, redirect);
148
+ }
149
+ else if (redirect && typeof window !== 'undefined') {
86
150
  window.location.href = data.redirectUrl;
87
151
  }
88
152
  return { ok: true, status: 200, url: data.redirectUrl };
89
153
  }
154
+ // Se é sessão (credentials), atualiza e redireciona
155
+ await fetchSession();
90
156
  if (data.type === 'session') {
91
157
  const finalUrl = callbackUrl || '/';
92
158
  if (redirect && typeof window !== 'undefined') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vatts/auth",
3
- "version": "2.1.2",
3
+ "version": "2.1.3-canary.1.0.1",
4
4
  "description": "Authentication package for Vatts.js framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -50,7 +50,7 @@
50
50
  "react": "^19.2.0",
51
51
  "react-dom": "^19.2.0",
52
52
  "vue": "^3.5.27",
53
- "vatts": "^2.1.2"
53
+ "vatts": "^2.1.3-canary.1.0.1"
54
54
  },
55
55
  "peerDependenciesMeta": {
56
56
  "react-dom": {
package/dist/client.d.ts DELETED
@@ -1,24 +0,0 @@
1
- import type { SignInOptions, SignInResult, Session } from './types';
2
- export declare function setBasePath(path: string): void;
3
- /**
4
- * Função para obter a sessão atual (similar ao NextAuth getSession)
5
- */
6
- export declare function getSession(): Promise<Session | null>;
7
- /**
8
- * Função para obter token CSRF
9
- */
10
- export declare function getCsrfToken(): Promise<string | null>;
11
- /**
12
- * Função para obter providers disponíveis
13
- */
14
- export declare function getProviders(): Promise<any[] | null>;
15
- /**
16
- * Função para fazer login (similar ao NextAuth signIn)
17
- */
18
- export declare function signIn(provider?: string, options?: SignInOptions): Promise<SignInResult | undefined>;
19
- /**
20
- * Função para fazer logout (similar ao NextAuth signOut)
21
- */
22
- export declare function signOut(options?: {
23
- callbackUrl?: string;
24
- }): Promise<void>;
package/dist/client.js DELETED
@@ -1,146 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setBasePath = setBasePath;
4
- exports.getSession = getSession;
5
- exports.getCsrfToken = getCsrfToken;
6
- exports.getProviders = getProviders;
7
- exports.signIn = signIn;
8
- exports.signOut = signOut;
9
- // Configuração global do client
10
- let basePath = '/api/auth';
11
- function setBasePath(path) {
12
- basePath = path;
13
- }
14
- /**
15
- * Função para obter a sessão atual (similar ao NextAuth getSession)
16
- */
17
- async function getSession() {
18
- try {
19
- const response = await fetch(`${basePath}/session`, {
20
- credentials: 'include'
21
- });
22
- if (!response.ok) {
23
- return null;
24
- }
25
- const data = await response.json();
26
- return data.session || null;
27
- }
28
- catch (error) {
29
- console.error('[vatts-auth] Error fetching session:', error);
30
- return null;
31
- }
32
- }
33
- /**
34
- * Função para obter token CSRF
35
- */
36
- async function getCsrfToken() {
37
- try {
38
- const response = await fetch(`${basePath}/csrf`, {
39
- credentials: 'include'
40
- });
41
- if (!response.ok) {
42
- return null;
43
- }
44
- const data = await response.json();
45
- return data.csrfToken || null;
46
- }
47
- catch (error) {
48
- console.error('[vatts-auth] Error fetching CSRF token:', error);
49
- return null;
50
- }
51
- }
52
- /**
53
- * Função para obter providers disponíveis
54
- */
55
- async function getProviders() {
56
- try {
57
- const response = await fetch(`${basePath}/providers`, {
58
- credentials: 'include'
59
- });
60
- if (!response.ok) {
61
- return null;
62
- }
63
- const data = await response.json();
64
- return data.providers || [];
65
- }
66
- catch (error) {
67
- console.error('[vatts-auth] Error searching for providers:', error);
68
- return null;
69
- }
70
- }
71
- /**
72
- * Função para fazer login (similar ao NextAuth signIn)
73
- */
74
- async function signIn(provider = 'credentials', options = {}) {
75
- try {
76
- const { redirect = true, callbackUrl, ...credentials } = options;
77
- const response = await fetch(`${basePath}/signin`, {
78
- method: 'POST',
79
- headers: {
80
- 'Content-Type': 'application/json',
81
- },
82
- credentials: 'include',
83
- body: JSON.stringify({
84
- provider,
85
- ...credentials
86
- })
87
- });
88
- const data = await response.json();
89
- if (response.ok && data.success) {
90
- // Se é OAuth, redireciona para URL fornecida
91
- if (data.type === 'oauth' && data.redirectUrl) {
92
- if (redirect && typeof window !== 'undefined') {
93
- window.location.href = data.redirectUrl;
94
- }
95
- return {
96
- ok: true,
97
- status: 200,
98
- url: data.redirectUrl
99
- };
100
- }
101
- // Se é sessão (credentials), redireciona para callbackUrl
102
- if (data.type === 'session') {
103
- if (redirect && typeof window !== 'undefined') {
104
- window.location.href = callbackUrl || '/';
105
- }
106
- return {
107
- ok: true,
108
- status: 200,
109
- url: callbackUrl || '/'
110
- };
111
- }
112
- }
113
- else {
114
- return {
115
- error: data.error || 'Authentication failed',
116
- status: response.status,
117
- ok: false
118
- };
119
- }
120
- }
121
- catch (error) {
122
- console.error('[vatts-auth] Error on signIn:', error);
123
- return {
124
- error: 'Network error',
125
- status: 500,
126
- ok: false
127
- };
128
- }
129
- }
130
- /**
131
- * Função para fazer logout (similar ao NextAuth signOut)
132
- */
133
- async function signOut(options = {}) {
134
- try {
135
- await fetch(`${basePath}/signout`, {
136
- method: 'POST',
137
- credentials: 'include'
138
- });
139
- if (typeof window !== 'undefined') {
140
- window.location.href = options.callbackUrl || '/';
141
- }
142
- }
143
- catch (error) {
144
- console.error('[vatts-auth] Error on signOut:', error);
145
- }
146
- }