@githat/nextjs 0.3.0 → 0.3.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/index.d.mts +48 -2
- package/dist/index.d.ts +48 -2
- package/dist/index.js +134 -68
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +135 -69
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.d.mts +39 -2
- package/dist/middleware.d.ts +39 -2
- package/dist/middleware.js +82 -5
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +72 -5
- package/dist/middleware.mjs.map +1 -1
- package/dist/server.d.mts +115 -0
- package/dist/server.d.ts +115 -0
- package/dist/server.js +186 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +147 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +9 -1
package/dist/index.d.mts
CHANGED
|
@@ -22,6 +22,17 @@ interface GitHatConfig {
|
|
|
22
22
|
signUpUrl?: string;
|
|
23
23
|
afterSignInUrl?: string;
|
|
24
24
|
afterSignOutUrl?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Token storage mode:
|
|
27
|
+
* - 'localStorage' (default): Tokens stored in browser localStorage
|
|
28
|
+
* - 'cookie': Tokens stored in httpOnly cookies (more secure, XSS-resistant)
|
|
29
|
+
*
|
|
30
|
+
* When using 'cookie' mode:
|
|
31
|
+
* - Login/refresh automatically set httpOnly cookies
|
|
32
|
+
* - SDK reads auth state from cookies (server-side)
|
|
33
|
+
* - Better for apps with server-side rendering
|
|
34
|
+
*/
|
|
35
|
+
tokenStorage?: 'localStorage' | 'cookie';
|
|
25
36
|
}
|
|
26
37
|
interface AuthState {
|
|
27
38
|
user: GitHatUser | null;
|
|
@@ -56,9 +67,12 @@ interface GitHatProviderProps {
|
|
|
56
67
|
}
|
|
57
68
|
declare function GitHatProvider({ config: rawConfig, children }: GitHatProviderProps): react_jsx_runtime.JSX.Element;
|
|
58
69
|
|
|
70
|
+
interface OrgMetadata$1 {
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
}
|
|
59
73
|
declare function useAuth(): GitHatContextValue;
|
|
60
74
|
declare function useGitHat(): {
|
|
61
|
-
fetch: <T =
|
|
75
|
+
fetch: <T = unknown>(endpoint: string, fetchOptions?: RequestInit) => Promise<T>;
|
|
62
76
|
getUserOrgs: () => Promise<{
|
|
63
77
|
orgs: GitHatOrg[];
|
|
64
78
|
}>;
|
|
@@ -68,6 +82,8 @@ declare function useGitHat(): {
|
|
|
68
82
|
verifyAgent: (wallet: string) => Promise<{
|
|
69
83
|
verified: boolean;
|
|
70
84
|
}>;
|
|
85
|
+
getOrgMetadata: () => Promise<OrgMetadata$1>;
|
|
86
|
+
updateOrgMetadata: (updates: OrgMetadata$1) => Promise<OrgMetadata$1>;
|
|
71
87
|
};
|
|
72
88
|
|
|
73
89
|
interface DataItem {
|
|
@@ -210,4 +226,34 @@ interface ProtectedRouteProps {
|
|
|
210
226
|
}
|
|
211
227
|
declare function ProtectedRoute({ children, fallback }: ProtectedRouteProps): react_jsx_runtime.JSX.Element | null;
|
|
212
228
|
|
|
213
|
-
|
|
229
|
+
/**
|
|
230
|
+
* @githat/nextjs/server
|
|
231
|
+
*
|
|
232
|
+
* Server-side utilities for token verification in Next.js API routes and middleware.
|
|
233
|
+
* This module runs on the server only — do not import in client components.
|
|
234
|
+
*/
|
|
235
|
+
interface AuthPayload {
|
|
236
|
+
userId: string;
|
|
237
|
+
email: string;
|
|
238
|
+
orgId: string | null;
|
|
239
|
+
orgSlug: string | null;
|
|
240
|
+
role: 'owner' | 'admin' | 'member' | null;
|
|
241
|
+
tier: 'free' | 'basic' | 'pro' | 'enterprise' | null;
|
|
242
|
+
}
|
|
243
|
+
interface VerifyOptions {
|
|
244
|
+
/**
|
|
245
|
+
* Secret key for local JWT verification. If provided, tokens are verified
|
|
246
|
+
* locally without making an API call (~1ms vs ~50-100ms).
|
|
247
|
+
* Must match the JWT_SECRET used by the GitHat backend.
|
|
248
|
+
*/
|
|
249
|
+
secretKey?: string;
|
|
250
|
+
/**
|
|
251
|
+
* API URL for remote token verification. Defaults to https://api.githat.io
|
|
252
|
+
*/
|
|
253
|
+
apiUrl?: string;
|
|
254
|
+
}
|
|
255
|
+
interface OrgMetadata {
|
|
256
|
+
[key: string]: unknown;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export { type AuthActions, type AuthPayload, type AuthState, type BatchOperation, type BatchResult, type DataItem, type DeleteResult, type GitHatConfig, type GitHatContextValue, type GitHatOrg, GitHatProvider, type GitHatUser, type OrgMetadata, OrgSwitcher, ProtectedRoute, type PutResult, type QueryOptions, type QueryResult, SignInButton, SignInForm, SignUpButton, type SignUpData, SignUpForm, type SignUpResult, UserButton, VerifiedBadge, type VerifyOptions, useAuth, useData, useGitHat };
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,17 @@ interface GitHatConfig {
|
|
|
22
22
|
signUpUrl?: string;
|
|
23
23
|
afterSignInUrl?: string;
|
|
24
24
|
afterSignOutUrl?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Token storage mode:
|
|
27
|
+
* - 'localStorage' (default): Tokens stored in browser localStorage
|
|
28
|
+
* - 'cookie': Tokens stored in httpOnly cookies (more secure, XSS-resistant)
|
|
29
|
+
*
|
|
30
|
+
* When using 'cookie' mode:
|
|
31
|
+
* - Login/refresh automatically set httpOnly cookies
|
|
32
|
+
* - SDK reads auth state from cookies (server-side)
|
|
33
|
+
* - Better for apps with server-side rendering
|
|
34
|
+
*/
|
|
35
|
+
tokenStorage?: 'localStorage' | 'cookie';
|
|
25
36
|
}
|
|
26
37
|
interface AuthState {
|
|
27
38
|
user: GitHatUser | null;
|
|
@@ -56,9 +67,12 @@ interface GitHatProviderProps {
|
|
|
56
67
|
}
|
|
57
68
|
declare function GitHatProvider({ config: rawConfig, children }: GitHatProviderProps): react_jsx_runtime.JSX.Element;
|
|
58
69
|
|
|
70
|
+
interface OrgMetadata$1 {
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
}
|
|
59
73
|
declare function useAuth(): GitHatContextValue;
|
|
60
74
|
declare function useGitHat(): {
|
|
61
|
-
fetch: <T =
|
|
75
|
+
fetch: <T = unknown>(endpoint: string, fetchOptions?: RequestInit) => Promise<T>;
|
|
62
76
|
getUserOrgs: () => Promise<{
|
|
63
77
|
orgs: GitHatOrg[];
|
|
64
78
|
}>;
|
|
@@ -68,6 +82,8 @@ declare function useGitHat(): {
|
|
|
68
82
|
verifyAgent: (wallet: string) => Promise<{
|
|
69
83
|
verified: boolean;
|
|
70
84
|
}>;
|
|
85
|
+
getOrgMetadata: () => Promise<OrgMetadata$1>;
|
|
86
|
+
updateOrgMetadata: (updates: OrgMetadata$1) => Promise<OrgMetadata$1>;
|
|
71
87
|
};
|
|
72
88
|
|
|
73
89
|
interface DataItem {
|
|
@@ -210,4 +226,34 @@ interface ProtectedRouteProps {
|
|
|
210
226
|
}
|
|
211
227
|
declare function ProtectedRoute({ children, fallback }: ProtectedRouteProps): react_jsx_runtime.JSX.Element | null;
|
|
212
228
|
|
|
213
|
-
|
|
229
|
+
/**
|
|
230
|
+
* @githat/nextjs/server
|
|
231
|
+
*
|
|
232
|
+
* Server-side utilities for token verification in Next.js API routes and middleware.
|
|
233
|
+
* This module runs on the server only — do not import in client components.
|
|
234
|
+
*/
|
|
235
|
+
interface AuthPayload {
|
|
236
|
+
userId: string;
|
|
237
|
+
email: string;
|
|
238
|
+
orgId: string | null;
|
|
239
|
+
orgSlug: string | null;
|
|
240
|
+
role: 'owner' | 'admin' | 'member' | null;
|
|
241
|
+
tier: 'free' | 'basic' | 'pro' | 'enterprise' | null;
|
|
242
|
+
}
|
|
243
|
+
interface VerifyOptions {
|
|
244
|
+
/**
|
|
245
|
+
* Secret key for local JWT verification. If provided, tokens are verified
|
|
246
|
+
* locally without making an API call (~1ms vs ~50-100ms).
|
|
247
|
+
* Must match the JWT_SECRET used by the GitHat backend.
|
|
248
|
+
*/
|
|
249
|
+
secretKey?: string;
|
|
250
|
+
/**
|
|
251
|
+
* API URL for remote token verification. Defaults to https://api.githat.io
|
|
252
|
+
*/
|
|
253
|
+
apiUrl?: string;
|
|
254
|
+
}
|
|
255
|
+
interface OrgMetadata {
|
|
256
|
+
[key: string]: unknown;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export { type AuthActions, type AuthPayload, type AuthState, type BatchOperation, type BatchResult, type DataItem, type DeleteResult, type GitHatConfig, type GitHatContextValue, type GitHatOrg, GitHatProvider, type GitHatUser, type OrgMetadata, OrgSwitcher, ProtectedRoute, type PutResult, type QueryOptions, type QueryResult, SignInButton, SignInForm, SignUpButton, type SignUpData, SignUpForm, type SignUpResult, UserButton, VerifiedBadge, type VerifyOptions, useAuth, useData, useGitHat };
|
package/dist/index.js
CHANGED
|
@@ -54,15 +54,16 @@ function resolveConfig(config) {
|
|
|
54
54
|
signInUrl: config.signInUrl || "/sign-in",
|
|
55
55
|
signUpUrl: config.signUpUrl || "/sign-up",
|
|
56
56
|
afterSignInUrl: config.afterSignInUrl || "/dashboard",
|
|
57
|
-
afterSignOutUrl: config.afterSignOutUrl || "/"
|
|
57
|
+
afterSignOutUrl: config.afterSignOutUrl || "/",
|
|
58
|
+
tokenStorage: config.tokenStorage || "localStorage"
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
// src/client.ts
|
|
62
63
|
var _refreshPromise = null;
|
|
63
|
-
async function refreshTokens(apiUrl, appKey) {
|
|
64
|
-
const refreshToken = typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.refreshToken) : null;
|
|
65
|
-
if (!refreshToken) return false;
|
|
64
|
+
async function refreshTokens(apiUrl, appKey, useCookies) {
|
|
65
|
+
const refreshToken = typeof window !== "undefined" && !useCookies ? localStorage.getItem(TOKEN_KEYS.refreshToken) : null;
|
|
66
|
+
if (!useCookies && !refreshToken) return false;
|
|
66
67
|
let orgId = null;
|
|
67
68
|
try {
|
|
68
69
|
const orgStr = localStorage.getItem(TOKEN_KEYS.org);
|
|
@@ -70,18 +71,22 @@ async function refreshTokens(apiUrl, appKey) {
|
|
|
70
71
|
} catch {
|
|
71
72
|
}
|
|
72
73
|
try {
|
|
73
|
-
const
|
|
74
|
+
const refreshUrl = useCookies ? `${apiUrl}/auth/refresh?setCookie=true` : `${apiUrl}/auth/refresh`;
|
|
75
|
+
const res = await fetch(refreshUrl, {
|
|
74
76
|
method: "POST",
|
|
75
77
|
headers: {
|
|
76
78
|
"Content-Type": "application/json",
|
|
77
79
|
"X-GitHat-App-Key": appKey
|
|
78
80
|
},
|
|
79
|
-
|
|
81
|
+
credentials: useCookies ? "include" : "same-origin",
|
|
82
|
+
body: JSON.stringify(useCookies ? { orgId } : { refreshToken, orgId })
|
|
80
83
|
});
|
|
81
84
|
if (!res.ok) return false;
|
|
82
85
|
const data = await res.json();
|
|
83
|
-
if (
|
|
84
|
-
|
|
86
|
+
if (!useCookies) {
|
|
87
|
+
if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
|
|
88
|
+
if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
|
|
89
|
+
}
|
|
85
90
|
if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
|
|
86
91
|
return true;
|
|
87
92
|
} catch {
|
|
@@ -91,23 +96,30 @@ async function refreshTokens(apiUrl, appKey) {
|
|
|
91
96
|
function clearAuth() {
|
|
92
97
|
if (typeof window === "undefined") return;
|
|
93
98
|
Object.values(TOKEN_KEYS).forEach((key) => localStorage.removeItem(key));
|
|
94
|
-
window.dispatchEvent(
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
window.dispatchEvent(
|
|
100
|
+
new CustomEvent("githat:auth-changed", {
|
|
101
|
+
detail: { user: null, org: null, signedIn: false }
|
|
102
|
+
})
|
|
103
|
+
);
|
|
97
104
|
}
|
|
98
|
-
function createClient(apiUrl, appKey) {
|
|
99
|
-
|
|
105
|
+
function createClient(apiUrl, appKey, options = {}) {
|
|
106
|
+
const { useCookies = false } = options;
|
|
107
|
+
async function fetchApi(endpoint, fetchOptions = {}) {
|
|
100
108
|
const url = `${apiUrl}${endpoint}`;
|
|
101
|
-
const token = typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
|
|
109
|
+
const token = typeof window !== "undefined" && !useCookies ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
|
|
102
110
|
const headers = {
|
|
103
111
|
"Content-Type": "application/json",
|
|
104
112
|
"X-GitHat-App-Key": appKey,
|
|
105
113
|
...token && { Authorization: `Bearer ${token}` },
|
|
106
|
-
...
|
|
114
|
+
...fetchOptions.headers
|
|
107
115
|
};
|
|
108
116
|
let response;
|
|
109
117
|
try {
|
|
110
|
-
response = await fetch(url, {
|
|
118
|
+
response = await fetch(url, {
|
|
119
|
+
...fetchOptions,
|
|
120
|
+
headers,
|
|
121
|
+
credentials: useCookies ? "include" : "same-origin"
|
|
122
|
+
});
|
|
111
123
|
} catch (networkError) {
|
|
112
124
|
if (networkError instanceof TypeError) {
|
|
113
125
|
const isMissingKey = !appKey || !appKey.startsWith("pk_live_");
|
|
@@ -117,27 +129,26 @@ function createClient(apiUrl, appKey) {
|
|
|
117
129
|
"Missing GitHat API key. Add NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY to .env.local"
|
|
118
130
|
);
|
|
119
131
|
}
|
|
120
|
-
throw new Error(
|
|
121
|
-
"Unable to connect to GitHat API. Check your network connection."
|
|
122
|
-
);
|
|
132
|
+
throw new Error("Unable to connect to GitHat API. Check your network connection.");
|
|
123
133
|
}
|
|
124
134
|
throw networkError;
|
|
125
135
|
}
|
|
126
136
|
if (response.status === 401) {
|
|
127
137
|
if (!_refreshPromise) {
|
|
128
|
-
_refreshPromise = refreshTokens(apiUrl, appKey).finally(() => {
|
|
138
|
+
_refreshPromise = refreshTokens(apiUrl, appKey, useCookies).finally(() => {
|
|
129
139
|
_refreshPromise = null;
|
|
130
140
|
});
|
|
131
141
|
}
|
|
132
142
|
const refreshed = await _refreshPromise;
|
|
133
143
|
if (refreshed) {
|
|
134
|
-
const newToken = localStorage.getItem(TOKEN_KEYS.accessToken);
|
|
144
|
+
const newToken = !useCookies && typeof window !== "undefined" ? localStorage.getItem(TOKEN_KEYS.accessToken) : null;
|
|
135
145
|
const retryResponse = await fetch(url, {
|
|
136
|
-
...
|
|
146
|
+
...fetchOptions,
|
|
137
147
|
headers: {
|
|
138
148
|
...headers,
|
|
139
149
|
...newToken && { Authorization: `Bearer ${newToken}` }
|
|
140
|
-
}
|
|
150
|
+
},
|
|
151
|
+
credentials: useCookies ? "include" : "same-origin"
|
|
141
152
|
});
|
|
142
153
|
const retryData = await retryResponse.json();
|
|
143
154
|
if (!retryResponse.ok) throw new Error(retryData.error || "Request failed");
|
|
@@ -158,38 +169,57 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
158
169
|
var GitHatContext = (0, import_react.createContext)(null);
|
|
159
170
|
function GitHatProvider({ config: rawConfig, children }) {
|
|
160
171
|
const config = (0, import_react.useMemo)(() => resolveConfig(rawConfig), [rawConfig]);
|
|
161
|
-
const
|
|
172
|
+
const useCookies = config.tokenStorage === "cookie";
|
|
173
|
+
const clientRef = (0, import_react.useRef)(createClient(config.apiUrl, config.publishableKey, { useCookies }));
|
|
162
174
|
const [user, setUser] = (0, import_react.useState)(null);
|
|
163
175
|
const [org, setOrg] = (0, import_react.useState)(null);
|
|
164
176
|
const [isSignedIn, setIsSignedIn] = (0, import_react.useState)(false);
|
|
165
177
|
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
166
178
|
const [authError, setAuthError] = (0, import_react.useState)(null);
|
|
167
179
|
(0, import_react.useEffect)(() => {
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
+
const validateSession = async () => {
|
|
181
|
+
try {
|
|
182
|
+
if (!useCookies) {
|
|
183
|
+
const token = localStorage.getItem(TOKEN_KEYS.accessToken);
|
|
184
|
+
const storedUser = localStorage.getItem(TOKEN_KEYS.user);
|
|
185
|
+
if (!token || !storedUser) {
|
|
186
|
+
setIsLoading(false);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const data = await clientRef.current.fetchApi("/auth/me");
|
|
191
|
+
if (data.user) {
|
|
192
|
+
setUser(data.user);
|
|
193
|
+
setOrg(data.currentOrg || null);
|
|
194
|
+
setIsSignedIn(true);
|
|
195
|
+
setAuthError(null);
|
|
196
|
+
if (!useCookies) {
|
|
197
|
+
localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
|
|
198
|
+
if (data.currentOrg) {
|
|
199
|
+
localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.currentOrg));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch (err) {
|
|
204
|
+
const error = err;
|
|
205
|
+
if (error.message === "Session expired") {
|
|
180
206
|
clientRef.current.clearAuth();
|
|
181
|
-
} else {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
207
|
+
} else if (!useCookies) {
|
|
208
|
+
const storedUser = localStorage.getItem(TOKEN_KEYS.user);
|
|
209
|
+
if (storedUser) {
|
|
210
|
+
try {
|
|
211
|
+
setUser(JSON.parse(storedUser));
|
|
212
|
+
setIsSignedIn(true);
|
|
213
|
+
} catch {
|
|
214
|
+
}
|
|
185
215
|
}
|
|
186
|
-
setAuthError(
|
|
216
|
+
setAuthError(error.message || "Failed to verify session");
|
|
187
217
|
}
|
|
188
|
-
}
|
|
189
|
-
} else {
|
|
218
|
+
}
|
|
190
219
|
setIsLoading(false);
|
|
191
|
-
}
|
|
192
|
-
|
|
220
|
+
};
|
|
221
|
+
validateSession();
|
|
222
|
+
}, [useCookies]);
|
|
193
223
|
(0, import_react.useEffect)(() => {
|
|
194
224
|
const handleAuthChanged = (e) => {
|
|
195
225
|
const detail = e.detail;
|
|
@@ -207,30 +237,36 @@ function GitHatProvider({ config: rawConfig, children }) {
|
|
|
207
237
|
return () => window.removeEventListener("githat:auth-changed", handleAuthChanged);
|
|
208
238
|
}, []);
|
|
209
239
|
const signIn = (0, import_react.useCallback)(async (email, password) => {
|
|
210
|
-
const
|
|
240
|
+
const loginUrl = useCookies ? "/auth/login?setCookie=true" : "/auth/login";
|
|
241
|
+
const data = await clientRef.current.fetchApi(loginUrl, {
|
|
211
242
|
method: "POST",
|
|
212
243
|
body: JSON.stringify({ email, password })
|
|
213
244
|
});
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
245
|
+
if (!useCookies && data.accessToken && data.refreshToken) {
|
|
246
|
+
localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
|
|
247
|
+
localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
|
|
248
|
+
localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
|
|
249
|
+
if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
|
|
250
|
+
}
|
|
218
251
|
setUser(data.user);
|
|
219
252
|
setOrg(data.org || null);
|
|
220
253
|
setIsSignedIn(true);
|
|
221
254
|
window.dispatchEvent(new CustomEvent("githat:auth-changed", {
|
|
222
255
|
detail: { user: data.user, org: data.org, signedIn: true }
|
|
223
256
|
}));
|
|
224
|
-
}, []);
|
|
257
|
+
}, [useCookies]);
|
|
225
258
|
const signUp = (0, import_react.useCallback)(async (signUpData) => {
|
|
226
|
-
const
|
|
259
|
+
const registerUrl = useCookies ? "/auth/register?setCookie=true" : "/auth/register";
|
|
260
|
+
const data = await clientRef.current.fetchApi(registerUrl, {
|
|
227
261
|
method: "POST",
|
|
228
262
|
body: JSON.stringify(signUpData)
|
|
229
263
|
});
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
264
|
+
if (!useCookies && data.accessToken && data.refreshToken) {
|
|
265
|
+
localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
|
|
266
|
+
localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
|
|
267
|
+
localStorage.setItem(TOKEN_KEYS.user, JSON.stringify(data.user));
|
|
268
|
+
if (data.org) localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
|
|
269
|
+
}
|
|
234
270
|
setUser(data.user);
|
|
235
271
|
setOrg(data.org || null);
|
|
236
272
|
setIsSignedIn(true);
|
|
@@ -238,10 +274,11 @@ function GitHatProvider({ config: rawConfig, children }) {
|
|
|
238
274
|
detail: { user: data.user, org: data.org, signedIn: true }
|
|
239
275
|
}));
|
|
240
276
|
return { requiresVerification: !data.user.emailVerified, email: signUpData.email };
|
|
241
|
-
}, []);
|
|
277
|
+
}, [useCookies]);
|
|
242
278
|
const signOut = (0, import_react.useCallback)(async () => {
|
|
243
279
|
try {
|
|
244
|
-
|
|
280
|
+
const logoutUrl = useCookies ? "/auth/logout?setCookie=true" : "/auth/logout";
|
|
281
|
+
await clientRef.current.fetchApi(logoutUrl, { method: "POST" });
|
|
245
282
|
} catch {
|
|
246
283
|
}
|
|
247
284
|
clientRef.current.clearAuth();
|
|
@@ -251,22 +288,24 @@ function GitHatProvider({ config: rawConfig, children }) {
|
|
|
251
288
|
if (typeof window !== "undefined" && config.afterSignOutUrl) {
|
|
252
289
|
window.location.href = config.afterSignOutUrl;
|
|
253
290
|
}
|
|
254
|
-
}, [config.afterSignOutUrl]);
|
|
291
|
+
}, [config.afterSignOutUrl, useCookies]);
|
|
255
292
|
const switchOrg = (0, import_react.useCallback)(async (orgId) => {
|
|
256
293
|
try {
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
294
|
+
const switchUrl = useCookies ? `/user/orgs/${orgId}/switch?setCookie=true` : `/user/orgs/${orgId}/switch`;
|
|
295
|
+
const data = await clientRef.current.fetchApi(switchUrl, { method: "POST" });
|
|
296
|
+
if (!useCookies) {
|
|
297
|
+
if (data.accessToken) localStorage.setItem(TOKEN_KEYS.accessToken, data.accessToken);
|
|
298
|
+
if (data.refreshToken) localStorage.setItem(TOKEN_KEYS.refreshToken, data.refreshToken);
|
|
299
|
+
localStorage.setItem(TOKEN_KEYS.org, JSON.stringify(data.org));
|
|
300
|
+
}
|
|
301
|
+
setOrg(data.org);
|
|
263
302
|
window.dispatchEvent(new CustomEvent("githat:auth-changed", {
|
|
264
|
-
detail: { user, org:
|
|
303
|
+
detail: { user, org: data.org, signedIn: true }
|
|
265
304
|
}));
|
|
266
305
|
} catch (e) {
|
|
267
306
|
console.error("Org switch failed:", e);
|
|
268
307
|
}
|
|
269
|
-
}, [user]);
|
|
308
|
+
}, [user, useCookies]);
|
|
270
309
|
const value = (0, import_react.useMemo)(() => ({
|
|
271
310
|
user,
|
|
272
311
|
org,
|
|
@@ -295,11 +334,38 @@ function useGitHat() {
|
|
|
295
334
|
() => createClient(ctx.config.apiUrl, ctx.config.publishableKey),
|
|
296
335
|
[ctx.config.apiUrl, ctx.config.publishableKey]
|
|
297
336
|
);
|
|
337
|
+
const getOrgMetadata = (0, import_react2.useCallback)(async () => {
|
|
338
|
+
if (!ctx.org?.id) {
|
|
339
|
+
throw new Error("No active organization");
|
|
340
|
+
}
|
|
341
|
+
const response = await client.fetchApi(
|
|
342
|
+
`/orgs/${ctx.org.id}/metadata`
|
|
343
|
+
);
|
|
344
|
+
return response.metadata || {};
|
|
345
|
+
}, [client, ctx.org?.id]);
|
|
346
|
+
const updateOrgMetadata = (0, import_react2.useCallback)(
|
|
347
|
+
async (updates) => {
|
|
348
|
+
if (!ctx.org?.id) {
|
|
349
|
+
throw new Error("No active organization");
|
|
350
|
+
}
|
|
351
|
+
const response = await client.fetchApi(
|
|
352
|
+
`/orgs/${ctx.org.id}/metadata`,
|
|
353
|
+
{
|
|
354
|
+
method: "PATCH",
|
|
355
|
+
body: JSON.stringify(updates)
|
|
356
|
+
}
|
|
357
|
+
);
|
|
358
|
+
return response.metadata || {};
|
|
359
|
+
},
|
|
360
|
+
[client, ctx.org?.id]
|
|
361
|
+
);
|
|
298
362
|
return {
|
|
299
363
|
fetch: client.fetchApi,
|
|
300
364
|
getUserOrgs: () => client.fetchApi("/user/orgs"),
|
|
301
365
|
verifyMCP: (domain) => client.fetchApi(`/verify/mcp/${domain}`),
|
|
302
|
-
verifyAgent: (wallet) => client.fetchApi(`/verify/agent/${wallet}`)
|
|
366
|
+
verifyAgent: (wallet) => client.fetchApi(`/verify/agent/${wallet}`),
|
|
367
|
+
getOrgMetadata,
|
|
368
|
+
updateOrgMetadata
|
|
303
369
|
};
|
|
304
370
|
}
|
|
305
371
|
|