@volcano.dev/sdk 1.2.0-nightly.22454064035.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/LICENSE +22 -0
- package/README.md +73 -0
- package/dist/index.cjs.js +2906 -0
- package/dist/index.esm.js +2897 -0
- package/dist/index.js +2912 -0
- package/dist/next/middleware.esm.js +180 -0
- package/dist/next/middleware.js +186 -0
- package/dist/realtime.cjs.js +1019 -0
- package/dist/realtime.esm.js +1016 -0
- package/dist/realtime.js +1025 -0
- package/package.json +89 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Volcano SDK - Next.js Middleware Helpers
|
|
3
|
+
*
|
|
4
|
+
* Utilities for integrating Volcano authentication with Next.js middleware.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```javascript
|
|
8
|
+
* // middleware.js
|
|
9
|
+
* import { NextResponse } from 'next/server';
|
|
10
|
+
* import { withAuth, createServerClient } from '@volcano.dev/sdk/next/middleware';
|
|
11
|
+
*
|
|
12
|
+
* export async function middleware(request) {
|
|
13
|
+
* const client = createServerClient({
|
|
14
|
+
* anonKey: process.env.VOLCANO_ANON_KEY,
|
|
15
|
+
* apiUrl: process.env.VOLCANO_API_URL,
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* const user = await withAuth(request, client);
|
|
19
|
+
*
|
|
20
|
+
* if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
|
|
21
|
+
* return NextResponse.redirect(new URL('/login', request.url));
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* return NextResponse.next();
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extract auth token from request headers or cookies
|
|
31
|
+
* @param {Request} request - Next.js request object
|
|
32
|
+
* @returns {string|null} The access token or null
|
|
33
|
+
*/
|
|
34
|
+
function getTokenFromRequest(request) {
|
|
35
|
+
// Check Authorization header first
|
|
36
|
+
const authHeader = request.headers.get('authorization');
|
|
37
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
38
|
+
return authHeader.slice(7);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check cookies (for SSR/middleware)
|
|
42
|
+
const cookies = request.cookies;
|
|
43
|
+
if (cookies) {
|
|
44
|
+
const tokenCookie = cookies.get('volcano_access_token');
|
|
45
|
+
if (tokenCookie?.value) {
|
|
46
|
+
return tokenCookie.value;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a server-side Volcano client for middleware/API routes
|
|
55
|
+
* @param {Object} config - Client configuration
|
|
56
|
+
* @param {string} config.anonKey - Your project's anon key
|
|
57
|
+
* @param {string} [config.apiUrl] - API URL (defaults to https://api.volcano.dev)
|
|
58
|
+
* @param {string} [config.accessToken] - Optional pre-set access token
|
|
59
|
+
* @returns {Object} A minimal client for server-side auth validation
|
|
60
|
+
*/
|
|
61
|
+
function createServerClient(config) {
|
|
62
|
+
const apiUrl = config.apiUrl || 'https://api.volcano.dev';
|
|
63
|
+
const anonKey = config.anonKey;
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
/**
|
|
67
|
+
* Validate a token and get user info
|
|
68
|
+
* @param {string} accessToken - The access token to validate
|
|
69
|
+
* @returns {Promise<{user: Object|null, error: Error|null}>}
|
|
70
|
+
*/
|
|
71
|
+
async getUser(accessToken) {
|
|
72
|
+
if (!accessToken) {
|
|
73
|
+
return { user: null, error: new Error('No access token provided') };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const response = await fetch(`${apiUrl}/auth/user`, {
|
|
78
|
+
method: 'GET',
|
|
79
|
+
headers: {
|
|
80
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
81
|
+
'X-Anon-Key': anonKey,
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
const data = await response.json().catch(() => ({}));
|
|
88
|
+
return {
|
|
89
|
+
user: null,
|
|
90
|
+
error: new Error(data.error || `Auth failed: ${response.status}`)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const data = await response.json().catch(() => ({}));
|
|
95
|
+
return { user: data.user || null, error: null };
|
|
96
|
+
} catch (err) {
|
|
97
|
+
return { user: null, error: err };
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Refresh an access token
|
|
103
|
+
* @param {string} refreshToken - The refresh token
|
|
104
|
+
* @returns {Promise<{accessToken: string|null, refreshToken: string|null, error: Error|null}>}
|
|
105
|
+
*/
|
|
106
|
+
async refreshToken(refreshToken) {
|
|
107
|
+
if (!refreshToken) {
|
|
108
|
+
return { accessToken: null, refreshToken: null, error: new Error('No refresh token provided') };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const response = await fetch(`${apiUrl}/auth/refresh`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: {
|
|
115
|
+
'X-Anon-Key': anonKey,
|
|
116
|
+
'Content-Type': 'application/json',
|
|
117
|
+
},
|
|
118
|
+
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
const data = await response.json().catch(() => ({}));
|
|
123
|
+
return {
|
|
124
|
+
accessToken: null,
|
|
125
|
+
refreshToken: null,
|
|
126
|
+
error: new Error(data.error || `Refresh failed: ${response.status}`)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const data = await response.json();
|
|
131
|
+
return {
|
|
132
|
+
accessToken: data.access_token,
|
|
133
|
+
refreshToken: data.refresh_token,
|
|
134
|
+
error: null
|
|
135
|
+
};
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return { accessToken: null, refreshToken: null, error: err };
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Middleware helper to validate auth and get user
|
|
145
|
+
* @param {Request} request - Next.js request object
|
|
146
|
+
* @param {Object} client - Server client created with createServerClient
|
|
147
|
+
* @returns {Promise<Object|null>} The user object or null if not authenticated
|
|
148
|
+
*/
|
|
149
|
+
async function withAuth(request, client) {
|
|
150
|
+
const token = getTokenFromRequest(request);
|
|
151
|
+
if (!token) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const { user, error } = await client.getUser(token);
|
|
156
|
+
if (error) {
|
|
157
|
+
console.warn('Auth validation failed:', error.message);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return user;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Check if running in browser environment
|
|
166
|
+
* @returns {boolean}
|
|
167
|
+
*/
|
|
168
|
+
function isBrowser() {
|
|
169
|
+
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Check if running in server environment (Node.js, Edge Runtime, etc.)
|
|
174
|
+
* @returns {boolean}
|
|
175
|
+
*/
|
|
176
|
+
function isServer() {
|
|
177
|
+
return !isBrowser();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export { createServerClient, getTokenFromRequest, isBrowser, isServer, withAuth };
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Volcano SDK - Next.js Middleware Helpers
|
|
5
|
+
*
|
|
6
|
+
* Utilities for integrating Volcano authentication with Next.js middleware.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```javascript
|
|
10
|
+
* // middleware.js
|
|
11
|
+
* import { NextResponse } from 'next/server';
|
|
12
|
+
* import { withAuth, createServerClient } from '@volcano.dev/sdk/next/middleware';
|
|
13
|
+
*
|
|
14
|
+
* export async function middleware(request) {
|
|
15
|
+
* const client = createServerClient({
|
|
16
|
+
* anonKey: process.env.VOLCANO_ANON_KEY,
|
|
17
|
+
* apiUrl: process.env.VOLCANO_API_URL,
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* const user = await withAuth(request, client);
|
|
21
|
+
*
|
|
22
|
+
* if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
|
|
23
|
+
* return NextResponse.redirect(new URL('/login', request.url));
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* return NextResponse.next();
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Extract auth token from request headers or cookies
|
|
33
|
+
* @param {Request} request - Next.js request object
|
|
34
|
+
* @returns {string|null} The access token or null
|
|
35
|
+
*/
|
|
36
|
+
function getTokenFromRequest(request) {
|
|
37
|
+
// Check Authorization header first
|
|
38
|
+
const authHeader = request.headers.get('authorization');
|
|
39
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
40
|
+
return authHeader.slice(7);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check cookies (for SSR/middleware)
|
|
44
|
+
const cookies = request.cookies;
|
|
45
|
+
if (cookies) {
|
|
46
|
+
const tokenCookie = cookies.get('volcano_access_token');
|
|
47
|
+
if (tokenCookie?.value) {
|
|
48
|
+
return tokenCookie.value;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create a server-side Volcano client for middleware/API routes
|
|
57
|
+
* @param {Object} config - Client configuration
|
|
58
|
+
* @param {string} config.anonKey - Your project's anon key
|
|
59
|
+
* @param {string} [config.apiUrl] - API URL (defaults to https://api.volcano.dev)
|
|
60
|
+
* @param {string} [config.accessToken] - Optional pre-set access token
|
|
61
|
+
* @returns {Object} A minimal client for server-side auth validation
|
|
62
|
+
*/
|
|
63
|
+
function createServerClient(config) {
|
|
64
|
+
const apiUrl = config.apiUrl || 'https://api.volcano.dev';
|
|
65
|
+
const anonKey = config.anonKey;
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
/**
|
|
69
|
+
* Validate a token and get user info
|
|
70
|
+
* @param {string} accessToken - The access token to validate
|
|
71
|
+
* @returns {Promise<{user: Object|null, error: Error|null}>}
|
|
72
|
+
*/
|
|
73
|
+
async getUser(accessToken) {
|
|
74
|
+
if (!accessToken) {
|
|
75
|
+
return { user: null, error: new Error('No access token provided') };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch(`${apiUrl}/auth/user`, {
|
|
80
|
+
method: 'GET',
|
|
81
|
+
headers: {
|
|
82
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
83
|
+
'X-Anon-Key': anonKey,
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const data = await response.json().catch(() => ({}));
|
|
90
|
+
return {
|
|
91
|
+
user: null,
|
|
92
|
+
error: new Error(data.error || `Auth failed: ${response.status}`)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const data = await response.json().catch(() => ({}));
|
|
97
|
+
return { user: data.user || null, error: null };
|
|
98
|
+
} catch (err) {
|
|
99
|
+
return { user: null, error: err };
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Refresh an access token
|
|
105
|
+
* @param {string} refreshToken - The refresh token
|
|
106
|
+
* @returns {Promise<{accessToken: string|null, refreshToken: string|null, error: Error|null}>}
|
|
107
|
+
*/
|
|
108
|
+
async refreshToken(refreshToken) {
|
|
109
|
+
if (!refreshToken) {
|
|
110
|
+
return { accessToken: null, refreshToken: null, error: new Error('No refresh token provided') };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const response = await fetch(`${apiUrl}/auth/refresh`, {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: {
|
|
117
|
+
'X-Anon-Key': anonKey,
|
|
118
|
+
'Content-Type': 'application/json',
|
|
119
|
+
},
|
|
120
|
+
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
const data = await response.json().catch(() => ({}));
|
|
125
|
+
return {
|
|
126
|
+
accessToken: null,
|
|
127
|
+
refreshToken: null,
|
|
128
|
+
error: new Error(data.error || `Refresh failed: ${response.status}`)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const data = await response.json();
|
|
133
|
+
return {
|
|
134
|
+
accessToken: data.access_token,
|
|
135
|
+
refreshToken: data.refresh_token,
|
|
136
|
+
error: null
|
|
137
|
+
};
|
|
138
|
+
} catch (err) {
|
|
139
|
+
return { accessToken: null, refreshToken: null, error: err };
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Middleware helper to validate auth and get user
|
|
147
|
+
* @param {Request} request - Next.js request object
|
|
148
|
+
* @param {Object} client - Server client created with createServerClient
|
|
149
|
+
* @returns {Promise<Object|null>} The user object or null if not authenticated
|
|
150
|
+
*/
|
|
151
|
+
async function withAuth(request, client) {
|
|
152
|
+
const token = getTokenFromRequest(request);
|
|
153
|
+
if (!token) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const { user, error } = await client.getUser(token);
|
|
158
|
+
if (error) {
|
|
159
|
+
console.warn('Auth validation failed:', error.message);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return user;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if running in browser environment
|
|
168
|
+
* @returns {boolean}
|
|
169
|
+
*/
|
|
170
|
+
function isBrowser() {
|
|
171
|
+
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check if running in server environment (Node.js, Edge Runtime, etc.)
|
|
176
|
+
* @returns {boolean}
|
|
177
|
+
*/
|
|
178
|
+
function isServer() {
|
|
179
|
+
return !isBrowser();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
exports.createServerClient = createServerClient;
|
|
183
|
+
exports.getTokenFromRequest = getTokenFromRequest;
|
|
184
|
+
exports.isBrowser = isBrowser;
|
|
185
|
+
exports.isServer = isServer;
|
|
186
|
+
exports.withAuth = withAuth;
|