@smarthivelabs-devs/auth-sdk 0.1.0

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.cjs ADDED
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SmartHiveAuthError: () => SmartHiveAuthError,
24
+ generateCodeChallenge: () => generateCodeChallenge,
25
+ generateCodeVerifier: () => generateCodeVerifier,
26
+ initAuth: () => initAuth,
27
+ smartHiveAuthStorageKeys: () => smartHiveAuthStorageKeys
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+ var SmartHiveAuthError = class extends Error {
31
+ constructor(code, message) {
32
+ super(message);
33
+ this.code = code;
34
+ this.name = "SmartHiveAuthError";
35
+ }
36
+ code;
37
+ };
38
+ async function generateCodeVerifier() {
39
+ const array = new Uint8Array(32);
40
+ crypto.getRandomValues(array);
41
+ return base64urlEncode(array);
42
+ }
43
+ async function generateCodeChallenge(verifier) {
44
+ const encoder = new TextEncoder();
45
+ const data = encoder.encode(verifier);
46
+ const digest = await crypto.subtle.digest("SHA-256", data);
47
+ return base64urlEncode(new Uint8Array(digest));
48
+ }
49
+ function base64urlEncode(buffer) {
50
+ let str = "";
51
+ for (const byte of buffer) str += String.fromCharCode(byte);
52
+ return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
53
+ }
54
+ var smartHiveAuthStorageKeys = {
55
+ session: "smarthive.auth.session",
56
+ pkceVerifier: "smarthive.auth.pkce_verifier",
57
+ pkceState: "smarthive.auth.pkce_state"
58
+ };
59
+ function browserStorage() {
60
+ return {
61
+ getItem: (key) => globalThis.localStorage?.getItem(key) ?? null,
62
+ setItem: (key, value) => globalThis.localStorage?.setItem(key, value),
63
+ removeItem: (key) => globalThis.localStorage?.removeItem(key)
64
+ };
65
+ }
66
+ function sessionStorageAdapter() {
67
+ return {
68
+ getItem: (key) => globalThis.sessionStorage?.getItem(key) ?? null,
69
+ setItem: (key, value) => globalThis.sessionStorage?.setItem(key, value),
70
+ removeItem: (key) => globalThis.sessionStorage?.removeItem(key)
71
+ };
72
+ }
73
+ function normalizeBaseUrl(baseUrl) {
74
+ return baseUrl.replace(/\/$/, "");
75
+ }
76
+ function initAuth(config) {
77
+ const baseUrl = normalizeBaseUrl(config.baseUrl);
78
+ const authBase = normalizeBaseUrl(config.authDomain ?? config.baseUrl);
79
+ const storage = config.storage ?? browserStorage();
80
+ const tempStorage = config.temporaryStorage ?? sessionStorageAdapter();
81
+ async function saveSession(session) {
82
+ if (!session) {
83
+ await storage.removeItem(smartHiveAuthStorageKeys.session);
84
+ return;
85
+ }
86
+ await storage.setItem(smartHiveAuthStorageKeys.session, JSON.stringify(session));
87
+ }
88
+ async function readSession() {
89
+ const raw = await storage.getItem(smartHiveAuthStorageKeys.session);
90
+ return raw ? JSON.parse(raw) : null;
91
+ }
92
+ const client = {
93
+ async initialize() {
94
+ const response = await fetch(
95
+ `${baseUrl}/sdk/config?projectId=${encodeURIComponent(config.projectId)}&publishableKey=${encodeURIComponent(config.publishableKey)}`
96
+ );
97
+ if (!response.ok) throw new SmartHiveAuthError("project_not_found", "Smart Hive Auth project configuration was not found.");
98
+ },
99
+ async login(options) {
100
+ const redirectUri = options?.redirectUri ?? config.redirectUri ?? globalThis.location?.href;
101
+ const state = options?.state ?? crypto.randomUUID();
102
+ const verifier = await generateCodeVerifier();
103
+ const challenge = await generateCodeChallenge(verifier);
104
+ await tempStorage.setItem(smartHiveAuthStorageKeys.pkceVerifier, verifier);
105
+ await tempStorage.setItem(smartHiveAuthStorageKeys.pkceState, state);
106
+ const url = new URL(`${authBase}/api/auth/oauth2/authorize`);
107
+ url.searchParams.set("project_id", config.projectId);
108
+ url.searchParams.set("publishable_key", config.publishableKey);
109
+ url.searchParams.set("response_type", "code");
110
+ url.searchParams.set("redirect_uri", redirectUri);
111
+ url.searchParams.set("state", state);
112
+ url.searchParams.set("code_challenge", challenge);
113
+ url.searchParams.set("code_challenge_method", "S256");
114
+ globalThis.location.assign(url.toString());
115
+ },
116
+ async handleCallback(options) {
117
+ const href = options?.url ?? globalThis.location?.href;
118
+ if (!href) throw new SmartHiveAuthError("callback_failed", "No URL provided for callback handling.");
119
+ const url = new URL(href);
120
+ const code = url.searchParams.get("code");
121
+ const returnedState = url.searchParams.get("state");
122
+ if (!code) throw new SmartHiveAuthError("callback_failed", "No authorization code in callback URL.");
123
+ const storedState = await tempStorage.getItem(smartHiveAuthStorageKeys.pkceState);
124
+ if (!storedState || !returnedState || storedState !== returnedState) {
125
+ throw new SmartHiveAuthError("state_mismatch", "OAuth state mismatch \u2014 possible CSRF attack.");
126
+ }
127
+ const verifier = await tempStorage.getItem(smartHiveAuthStorageKeys.pkceVerifier);
128
+ if (!verifier) {
129
+ throw new SmartHiveAuthError("pkce_missing", "Missing PKCE verifier for authorization code exchange.");
130
+ }
131
+ const redirectUri = config.redirectUri ?? url.origin + url.pathname;
132
+ const body = new URLSearchParams({
133
+ grant_type: "authorization_code",
134
+ code,
135
+ redirect_uri: redirectUri,
136
+ client_id: config.publishableKey
137
+ });
138
+ body.set("code_verifier", verifier);
139
+ const response = await fetch(`${authBase}/api/auth/oauth2/token`, {
140
+ method: "POST",
141
+ headers: { "content-type": "application/x-www-form-urlencoded" },
142
+ body
143
+ });
144
+ if (!response.ok) {
145
+ const err = await response.json().catch(() => ({}));
146
+ throw new SmartHiveAuthError(err.error ?? "token_error", err.error_description ?? "Token exchange failed.");
147
+ }
148
+ const tokenBody = await response.json();
149
+ const session = {
150
+ accessToken: tokenBody.access_token,
151
+ refreshToken: tokenBody.refresh_token,
152
+ expiresAt: tokenBody.expires_in ? Date.now() + tokenBody.expires_in * 1e3 : void 0
153
+ };
154
+ await saveSession(session);
155
+ await tempStorage.removeItem(smartHiveAuthStorageKeys.pkceVerifier);
156
+ await tempStorage.removeItem(smartHiveAuthStorageKeys.pkceState);
157
+ return session;
158
+ },
159
+ async logout() {
160
+ await saveSession(null);
161
+ await fetch(`${baseUrl}/api/auth/sign-out`, { method: "POST", credentials: "include" });
162
+ },
163
+ getSession: readSession,
164
+ async getAccessToken() {
165
+ const session = await readSession();
166
+ if (!session) return null;
167
+ if (session.expiresAt && Date.now() > session.expiresAt - 3e4) {
168
+ return (await client.refreshSession())?.accessToken ?? null;
169
+ }
170
+ return session.accessToken;
171
+ },
172
+ async getAuthorizationHeader() {
173
+ const token = await client.getAccessToken();
174
+ const headers = {};
175
+ if (token) headers.authorization = `Bearer ${token}`;
176
+ return headers;
177
+ },
178
+ async fetch(input, init = {}) {
179
+ const authHeader = await client.getAuthorizationHeader();
180
+ const headers = new Headers(init.headers);
181
+ for (const [key, value] of Object.entries(authHeader)) {
182
+ headers.set(key, value);
183
+ }
184
+ return fetch(input, { ...init, headers });
185
+ },
186
+ async refreshSession() {
187
+ const session = await readSession();
188
+ if (!session?.refreshToken) return session;
189
+ const response = await fetch(`${baseUrl}/api/auth/oauth2/token`, {
190
+ method: "POST",
191
+ headers: { "content-type": "application/x-www-form-urlencoded" },
192
+ body: new URLSearchParams({
193
+ grant_type: "refresh_token",
194
+ refresh_token: session.refreshToken
195
+ })
196
+ });
197
+ if (!response.ok) {
198
+ await saveSession(null);
199
+ return null;
200
+ }
201
+ const body = await response.json();
202
+ const nextSession = {
203
+ ...session,
204
+ accessToken: body.access_token,
205
+ refreshToken: body.refresh_token ?? session.refreshToken,
206
+ expiresAt: body.expires_in ? Date.now() + body.expires_in * 1e3 : session.expiresAt
207
+ };
208
+ await saveSession(nextSession);
209
+ return nextSession;
210
+ },
211
+ async verifyToken(token) {
212
+ if (token.split(".").length !== 3) return false;
213
+ const response = await fetch(`${authBase}/api/auth/oauth2/userinfo`, {
214
+ headers: { authorization: `Bearer ${token}` }
215
+ });
216
+ return response.ok;
217
+ },
218
+ async getUser() {
219
+ const token = await client.getAccessToken();
220
+ if (!token) return null;
221
+ const response = await fetch(`${baseUrl}/api/auth/oauth2/userinfo`, {
222
+ headers: { authorization: `Bearer ${token}` }
223
+ });
224
+ return response.ok ? response.json() : null;
225
+ }
226
+ };
227
+ return client;
228
+ }
229
+ // Annotate the CommonJS export names for ESM import in node:
230
+ 0 && (module.exports = {
231
+ SmartHiveAuthError,
232
+ generateCodeChallenge,
233
+ generateCodeVerifier,
234
+ initAuth,
235
+ smartHiveAuthStorageKeys
236
+ });
237
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface SmartHiveAuthConfig {\n projectId: string;\n publishableKey: string;\n baseUrl: string;\n /** Custom branded auth domain, e.g. \"https://auth.myapp.com\". Used as the entry point for login/token flows. Falls back to baseUrl. */\n authDomain?: string;\n /** Fallback auth domain if authDomain is unreachable, e.g. \"https://auth.smarthivelabs.dev\". */\n fallbackAuthDomain?: string;\n redirectUri?: string;\n storage?: AuthStorage;\n temporaryStorage?: AuthStorage;\n}\n\nexport interface AuthStorage {\n getItem(key: string): string | null | Promise<string | null>;\n setItem(key: string, value: string): void | Promise<void>;\n removeItem(key: string): void | Promise<void>;\n}\n\nexport interface AuthSession {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n user?: unknown;\n}\n\nexport interface SmartHiveAuthClient {\n initialize(): Promise<void>;\n login(options?: { redirectUri?: string; state?: string }): Promise<void>;\n handleCallback(options?: { url?: string }): Promise<AuthSession>;\n logout(): Promise<void>;\n getSession(): Promise<AuthSession | null>;\n getAccessToken(): Promise<string | null>;\n getAuthorizationHeader(): Promise<Record<string, string>>;\n fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;\n refreshSession(): Promise<AuthSession | null>;\n verifyToken(token: string): Promise<boolean>;\n getUser(): Promise<unknown>;\n}\n\nexport class SmartHiveAuthError extends Error {\n constructor(public code: string, message: string) {\n super(message);\n this.name = \"SmartHiveAuthError\";\n }\n}\n\n// ── PKCE helpers (Web Crypto — works in browser + Node 18+) ──────────────────\n\nexport async function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64urlEncode(array);\n}\n\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const digest = await crypto.subtle.digest(\"SHA-256\", data);\n return base64urlEncode(new Uint8Array(digest));\n}\n\nfunction base64urlEncode(buffer: Uint8Array): string {\n let str = \"\";\n for (const byte of buffer) str += String.fromCharCode(byte);\n return btoa(str).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n// ── Storage keys ─────────────────────────────────────────────────────────────\n\nexport const smartHiveAuthStorageKeys = {\n session: \"smarthive.auth.session\",\n pkceVerifier: \"smarthive.auth.pkce_verifier\",\n pkceState: \"smarthive.auth.pkce_state\"\n} as const;\n\nfunction browserStorage(): AuthStorage {\n return {\n getItem: (key) => globalThis.localStorage?.getItem(key) ?? null,\n setItem: (key, value) => globalThis.localStorage?.setItem(key, value),\n removeItem: (key) => globalThis.localStorage?.removeItem(key)\n };\n}\n\nfunction sessionStorageAdapter(): Pick<AuthStorage, \"getItem\" | \"setItem\" | \"removeItem\"> {\n return {\n getItem: (key) => globalThis.sessionStorage?.getItem(key) ?? null,\n setItem: (key, value) => globalThis.sessionStorage?.setItem(key, value),\n removeItem: (key) => globalThis.sessionStorage?.removeItem(key)\n };\n}\n\nfunction normalizeBaseUrl(baseUrl: string) {\n return baseUrl.replace(/\\/$/, \"\");\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport function initAuth(config: SmartHiveAuthConfig): SmartHiveAuthClient {\n const baseUrl = normalizeBaseUrl(config.baseUrl);\n const authBase = normalizeBaseUrl(config.authDomain ?? config.baseUrl);\n const storage = config.storage ?? browserStorage();\n const tempStorage = config.temporaryStorage ?? sessionStorageAdapter();\n\n async function saveSession(session: AuthSession | null) {\n if (!session) { await storage.removeItem(smartHiveAuthStorageKeys.session); return; }\n await storage.setItem(smartHiveAuthStorageKeys.session, JSON.stringify(session));\n }\n\n async function readSession(): Promise<AuthSession | null> {\n const raw = await storage.getItem(smartHiveAuthStorageKeys.session);\n return raw ? JSON.parse(raw) as AuthSession : null;\n }\n\n const client: SmartHiveAuthClient = {\n async initialize() {\n const response = await fetch(\n `${baseUrl}/sdk/config?projectId=${encodeURIComponent(config.projectId)}&publishableKey=${encodeURIComponent(config.publishableKey)}`\n );\n if (!response.ok) throw new SmartHiveAuthError(\"project_not_found\", \"Smart Hive Auth project configuration was not found.\");\n },\n\n async login(options) {\n const redirectUri = options?.redirectUri ?? config.redirectUri ?? globalThis.location?.href;\n const state = options?.state ?? crypto.randomUUID();\n const verifier = await generateCodeVerifier();\n const challenge = await generateCodeChallenge(verifier);\n\n await tempStorage.setItem(smartHiveAuthStorageKeys.pkceVerifier, verifier);\n await tempStorage.setItem(smartHiveAuthStorageKeys.pkceState, state);\n\n const url = new URL(`${authBase}/api/auth/oauth2/authorize`);\n url.searchParams.set(\"project_id\", config.projectId);\n url.searchParams.set(\"publishable_key\", config.publishableKey);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"redirect_uri\", redirectUri);\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"code_challenge\", challenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n globalThis.location.assign(url.toString());\n },\n\n async handleCallback(options) {\n const href = options?.url ?? globalThis.location?.href;\n if (!href) throw new SmartHiveAuthError(\"callback_failed\", \"No URL provided for callback handling.\");\n\n const url = new URL(href);\n const code = url.searchParams.get(\"code\");\n const returnedState = url.searchParams.get(\"state\");\n\n if (!code) throw new SmartHiveAuthError(\"callback_failed\", \"No authorization code in callback URL.\");\n\n const storedState = await tempStorage.getItem(smartHiveAuthStorageKeys.pkceState);\n if (!storedState || !returnedState || storedState !== returnedState) {\n throw new SmartHiveAuthError(\"state_mismatch\", \"OAuth state mismatch — possible CSRF attack.\");\n }\n\n const verifier = await tempStorage.getItem(smartHiveAuthStorageKeys.pkceVerifier);\n if (!verifier) {\n throw new SmartHiveAuthError(\"pkce_missing\", \"Missing PKCE verifier for authorization code exchange.\");\n }\n const redirectUri = config.redirectUri ?? (url.origin + url.pathname);\n\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n code,\n redirect_uri: redirectUri,\n client_id: config.publishableKey\n });\n body.set(\"code_verifier\", verifier);\n\n const response = await fetch(`${authBase}/api/auth/oauth2/token`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body\n });\n\n if (!response.ok) {\n const err = await response.json().catch(() => ({})) as { error?: string; error_description?: string };\n throw new SmartHiveAuthError(err.error ?? \"token_error\", err.error_description ?? \"Token exchange failed.\");\n }\n\n const tokenBody = await response.json() as {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n token_type?: string;\n };\n\n const session: AuthSession = {\n accessToken: tokenBody.access_token,\n refreshToken: tokenBody.refresh_token,\n expiresAt: tokenBody.expires_in ? Date.now() + tokenBody.expires_in * 1000 : undefined\n };\n\n await saveSession(session);\n await tempStorage.removeItem(smartHiveAuthStorageKeys.pkceVerifier);\n await tempStorage.removeItem(smartHiveAuthStorageKeys.pkceState);\n\n return session;\n },\n\n async logout() {\n await saveSession(null);\n await fetch(`${baseUrl}/api/auth/sign-out`, { method: \"POST\", credentials: \"include\" });\n },\n\n getSession: readSession,\n\n async getAccessToken() {\n const session = await readSession();\n if (!session) return null;\n if (session.expiresAt && Date.now() > session.expiresAt - 30000) {\n return (await client.refreshSession())?.accessToken ?? null;\n }\n return session.accessToken;\n },\n\n async getAuthorizationHeader() {\n const token = await client.getAccessToken();\n const headers: Record<string, string> = {};\n if (token) headers.authorization = `Bearer ${token}`;\n return headers;\n },\n\n async fetch(input, init = {}) {\n const authHeader = await client.getAuthorizationHeader();\n const headers = new Headers(init.headers);\n for (const [key, value] of Object.entries(authHeader)) {\n headers.set(key, value);\n }\n return fetch(input, { ...init, headers });\n },\n\n async refreshSession() {\n const session = await readSession();\n if (!session?.refreshToken) return session;\n const response = await fetch(`${baseUrl}/api/auth/oauth2/token`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: session.refreshToken\n })\n });\n if (!response.ok) { await saveSession(null); return null; }\n const body = await response.json() as { access_token: string; refresh_token?: string; expires_in?: number };\n const nextSession: AuthSession = {\n ...session,\n accessToken: body.access_token,\n refreshToken: body.refresh_token ?? session.refreshToken,\n expiresAt: body.expires_in ? Date.now() + body.expires_in * 1000 : session.expiresAt\n };\n await saveSession(nextSession);\n return nextSession;\n },\n\n async verifyToken(token) {\n if (token.split(\".\").length !== 3) return false;\n const response = await fetch(`${authBase}/api/auth/oauth2/userinfo`, {\n headers: { authorization: `Bearer ${token}` }\n });\n return response.ok;\n },\n\n async getUser() {\n const token = await client.getAccessToken();\n if (!token) return null;\n const response = await fetch(`${baseUrl}/api/auth/oauth2/userinfo`, {\n headers: { authorization: `Bearer ${token}` }\n });\n return response.ok ? response.json() : null;\n }\n };\n\n return client;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,55 @@
1
+ interface SmartHiveAuthConfig {
2
+ projectId: string;
3
+ publishableKey: string;
4
+ baseUrl: string;
5
+ /** Custom branded auth domain, e.g. "https://auth.myapp.com". Used as the entry point for login/token flows. Falls back to baseUrl. */
6
+ authDomain?: string;
7
+ /** Fallback auth domain if authDomain is unreachable, e.g. "https://auth.smarthivelabs.dev". */
8
+ fallbackAuthDomain?: string;
9
+ redirectUri?: string;
10
+ storage?: AuthStorage;
11
+ temporaryStorage?: AuthStorage;
12
+ }
13
+ interface AuthStorage {
14
+ getItem(key: string): string | null | Promise<string | null>;
15
+ setItem(key: string, value: string): void | Promise<void>;
16
+ removeItem(key: string): void | Promise<void>;
17
+ }
18
+ interface AuthSession {
19
+ accessToken: string;
20
+ refreshToken?: string;
21
+ expiresAt?: number;
22
+ user?: unknown;
23
+ }
24
+ interface SmartHiveAuthClient {
25
+ initialize(): Promise<void>;
26
+ login(options?: {
27
+ redirectUri?: string;
28
+ state?: string;
29
+ }): Promise<void>;
30
+ handleCallback(options?: {
31
+ url?: string;
32
+ }): Promise<AuthSession>;
33
+ logout(): Promise<void>;
34
+ getSession(): Promise<AuthSession | null>;
35
+ getAccessToken(): Promise<string | null>;
36
+ getAuthorizationHeader(): Promise<Record<string, string>>;
37
+ fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
38
+ refreshSession(): Promise<AuthSession | null>;
39
+ verifyToken(token: string): Promise<boolean>;
40
+ getUser(): Promise<unknown>;
41
+ }
42
+ declare class SmartHiveAuthError extends Error {
43
+ code: string;
44
+ constructor(code: string, message: string);
45
+ }
46
+ declare function generateCodeVerifier(): Promise<string>;
47
+ declare function generateCodeChallenge(verifier: string): Promise<string>;
48
+ declare const smartHiveAuthStorageKeys: {
49
+ readonly session: "smarthive.auth.session";
50
+ readonly pkceVerifier: "smarthive.auth.pkce_verifier";
51
+ readonly pkceState: "smarthive.auth.pkce_state";
52
+ };
53
+ declare function initAuth(config: SmartHiveAuthConfig): SmartHiveAuthClient;
54
+
55
+ export { type AuthSession, type AuthStorage, type SmartHiveAuthClient, type SmartHiveAuthConfig, SmartHiveAuthError, generateCodeChallenge, generateCodeVerifier, initAuth, smartHiveAuthStorageKeys };
@@ -0,0 +1,55 @@
1
+ interface SmartHiveAuthConfig {
2
+ projectId: string;
3
+ publishableKey: string;
4
+ baseUrl: string;
5
+ /** Custom branded auth domain, e.g. "https://auth.myapp.com". Used as the entry point for login/token flows. Falls back to baseUrl. */
6
+ authDomain?: string;
7
+ /** Fallback auth domain if authDomain is unreachable, e.g. "https://auth.smarthivelabs.dev". */
8
+ fallbackAuthDomain?: string;
9
+ redirectUri?: string;
10
+ storage?: AuthStorage;
11
+ temporaryStorage?: AuthStorage;
12
+ }
13
+ interface AuthStorage {
14
+ getItem(key: string): string | null | Promise<string | null>;
15
+ setItem(key: string, value: string): void | Promise<void>;
16
+ removeItem(key: string): void | Promise<void>;
17
+ }
18
+ interface AuthSession {
19
+ accessToken: string;
20
+ refreshToken?: string;
21
+ expiresAt?: number;
22
+ user?: unknown;
23
+ }
24
+ interface SmartHiveAuthClient {
25
+ initialize(): Promise<void>;
26
+ login(options?: {
27
+ redirectUri?: string;
28
+ state?: string;
29
+ }): Promise<void>;
30
+ handleCallback(options?: {
31
+ url?: string;
32
+ }): Promise<AuthSession>;
33
+ logout(): Promise<void>;
34
+ getSession(): Promise<AuthSession | null>;
35
+ getAccessToken(): Promise<string | null>;
36
+ getAuthorizationHeader(): Promise<Record<string, string>>;
37
+ fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
38
+ refreshSession(): Promise<AuthSession | null>;
39
+ verifyToken(token: string): Promise<boolean>;
40
+ getUser(): Promise<unknown>;
41
+ }
42
+ declare class SmartHiveAuthError extends Error {
43
+ code: string;
44
+ constructor(code: string, message: string);
45
+ }
46
+ declare function generateCodeVerifier(): Promise<string>;
47
+ declare function generateCodeChallenge(verifier: string): Promise<string>;
48
+ declare const smartHiveAuthStorageKeys: {
49
+ readonly session: "smarthive.auth.session";
50
+ readonly pkceVerifier: "smarthive.auth.pkce_verifier";
51
+ readonly pkceState: "smarthive.auth.pkce_state";
52
+ };
53
+ declare function initAuth(config: SmartHiveAuthConfig): SmartHiveAuthClient;
54
+
55
+ export { type AuthSession, type AuthStorage, type SmartHiveAuthClient, type SmartHiveAuthConfig, SmartHiveAuthError, generateCodeChallenge, generateCodeVerifier, initAuth, smartHiveAuthStorageKeys };
package/dist/index.js ADDED
@@ -0,0 +1,208 @@
1
+ // src/index.ts
2
+ var SmartHiveAuthError = class extends Error {
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.code = code;
6
+ this.name = "SmartHiveAuthError";
7
+ }
8
+ code;
9
+ };
10
+ async function generateCodeVerifier() {
11
+ const array = new Uint8Array(32);
12
+ crypto.getRandomValues(array);
13
+ return base64urlEncode(array);
14
+ }
15
+ async function generateCodeChallenge(verifier) {
16
+ const encoder = new TextEncoder();
17
+ const data = encoder.encode(verifier);
18
+ const digest = await crypto.subtle.digest("SHA-256", data);
19
+ return base64urlEncode(new Uint8Array(digest));
20
+ }
21
+ function base64urlEncode(buffer) {
22
+ let str = "";
23
+ for (const byte of buffer) str += String.fromCharCode(byte);
24
+ return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
25
+ }
26
+ var smartHiveAuthStorageKeys = {
27
+ session: "smarthive.auth.session",
28
+ pkceVerifier: "smarthive.auth.pkce_verifier",
29
+ pkceState: "smarthive.auth.pkce_state"
30
+ };
31
+ function browserStorage() {
32
+ return {
33
+ getItem: (key) => globalThis.localStorage?.getItem(key) ?? null,
34
+ setItem: (key, value) => globalThis.localStorage?.setItem(key, value),
35
+ removeItem: (key) => globalThis.localStorage?.removeItem(key)
36
+ };
37
+ }
38
+ function sessionStorageAdapter() {
39
+ return {
40
+ getItem: (key) => globalThis.sessionStorage?.getItem(key) ?? null,
41
+ setItem: (key, value) => globalThis.sessionStorage?.setItem(key, value),
42
+ removeItem: (key) => globalThis.sessionStorage?.removeItem(key)
43
+ };
44
+ }
45
+ function normalizeBaseUrl(baseUrl) {
46
+ return baseUrl.replace(/\/$/, "");
47
+ }
48
+ function initAuth(config) {
49
+ const baseUrl = normalizeBaseUrl(config.baseUrl);
50
+ const authBase = normalizeBaseUrl(config.authDomain ?? config.baseUrl);
51
+ const storage = config.storage ?? browserStorage();
52
+ const tempStorage = config.temporaryStorage ?? sessionStorageAdapter();
53
+ async function saveSession(session) {
54
+ if (!session) {
55
+ await storage.removeItem(smartHiveAuthStorageKeys.session);
56
+ return;
57
+ }
58
+ await storage.setItem(smartHiveAuthStorageKeys.session, JSON.stringify(session));
59
+ }
60
+ async function readSession() {
61
+ const raw = await storage.getItem(smartHiveAuthStorageKeys.session);
62
+ return raw ? JSON.parse(raw) : null;
63
+ }
64
+ const client = {
65
+ async initialize() {
66
+ const response = await fetch(
67
+ `${baseUrl}/sdk/config?projectId=${encodeURIComponent(config.projectId)}&publishableKey=${encodeURIComponent(config.publishableKey)}`
68
+ );
69
+ if (!response.ok) throw new SmartHiveAuthError("project_not_found", "Smart Hive Auth project configuration was not found.");
70
+ },
71
+ async login(options) {
72
+ const redirectUri = options?.redirectUri ?? config.redirectUri ?? globalThis.location?.href;
73
+ const state = options?.state ?? crypto.randomUUID();
74
+ const verifier = await generateCodeVerifier();
75
+ const challenge = await generateCodeChallenge(verifier);
76
+ await tempStorage.setItem(smartHiveAuthStorageKeys.pkceVerifier, verifier);
77
+ await tempStorage.setItem(smartHiveAuthStorageKeys.pkceState, state);
78
+ const url = new URL(`${authBase}/api/auth/oauth2/authorize`);
79
+ url.searchParams.set("project_id", config.projectId);
80
+ url.searchParams.set("publishable_key", config.publishableKey);
81
+ url.searchParams.set("response_type", "code");
82
+ url.searchParams.set("redirect_uri", redirectUri);
83
+ url.searchParams.set("state", state);
84
+ url.searchParams.set("code_challenge", challenge);
85
+ url.searchParams.set("code_challenge_method", "S256");
86
+ globalThis.location.assign(url.toString());
87
+ },
88
+ async handleCallback(options) {
89
+ const href = options?.url ?? globalThis.location?.href;
90
+ if (!href) throw new SmartHiveAuthError("callback_failed", "No URL provided for callback handling.");
91
+ const url = new URL(href);
92
+ const code = url.searchParams.get("code");
93
+ const returnedState = url.searchParams.get("state");
94
+ if (!code) throw new SmartHiveAuthError("callback_failed", "No authorization code in callback URL.");
95
+ const storedState = await tempStorage.getItem(smartHiveAuthStorageKeys.pkceState);
96
+ if (!storedState || !returnedState || storedState !== returnedState) {
97
+ throw new SmartHiveAuthError("state_mismatch", "OAuth state mismatch \u2014 possible CSRF attack.");
98
+ }
99
+ const verifier = await tempStorage.getItem(smartHiveAuthStorageKeys.pkceVerifier);
100
+ if (!verifier) {
101
+ throw new SmartHiveAuthError("pkce_missing", "Missing PKCE verifier for authorization code exchange.");
102
+ }
103
+ const redirectUri = config.redirectUri ?? url.origin + url.pathname;
104
+ const body = new URLSearchParams({
105
+ grant_type: "authorization_code",
106
+ code,
107
+ redirect_uri: redirectUri,
108
+ client_id: config.publishableKey
109
+ });
110
+ body.set("code_verifier", verifier);
111
+ const response = await fetch(`${authBase}/api/auth/oauth2/token`, {
112
+ method: "POST",
113
+ headers: { "content-type": "application/x-www-form-urlencoded" },
114
+ body
115
+ });
116
+ if (!response.ok) {
117
+ const err = await response.json().catch(() => ({}));
118
+ throw new SmartHiveAuthError(err.error ?? "token_error", err.error_description ?? "Token exchange failed.");
119
+ }
120
+ const tokenBody = await response.json();
121
+ const session = {
122
+ accessToken: tokenBody.access_token,
123
+ refreshToken: tokenBody.refresh_token,
124
+ expiresAt: tokenBody.expires_in ? Date.now() + tokenBody.expires_in * 1e3 : void 0
125
+ };
126
+ await saveSession(session);
127
+ await tempStorage.removeItem(smartHiveAuthStorageKeys.pkceVerifier);
128
+ await tempStorage.removeItem(smartHiveAuthStorageKeys.pkceState);
129
+ return session;
130
+ },
131
+ async logout() {
132
+ await saveSession(null);
133
+ await fetch(`${baseUrl}/api/auth/sign-out`, { method: "POST", credentials: "include" });
134
+ },
135
+ getSession: readSession,
136
+ async getAccessToken() {
137
+ const session = await readSession();
138
+ if (!session) return null;
139
+ if (session.expiresAt && Date.now() > session.expiresAt - 3e4) {
140
+ return (await client.refreshSession())?.accessToken ?? null;
141
+ }
142
+ return session.accessToken;
143
+ },
144
+ async getAuthorizationHeader() {
145
+ const token = await client.getAccessToken();
146
+ const headers = {};
147
+ if (token) headers.authorization = `Bearer ${token}`;
148
+ return headers;
149
+ },
150
+ async fetch(input, init = {}) {
151
+ const authHeader = await client.getAuthorizationHeader();
152
+ const headers = new Headers(init.headers);
153
+ for (const [key, value] of Object.entries(authHeader)) {
154
+ headers.set(key, value);
155
+ }
156
+ return fetch(input, { ...init, headers });
157
+ },
158
+ async refreshSession() {
159
+ const session = await readSession();
160
+ if (!session?.refreshToken) return session;
161
+ const response = await fetch(`${baseUrl}/api/auth/oauth2/token`, {
162
+ method: "POST",
163
+ headers: { "content-type": "application/x-www-form-urlencoded" },
164
+ body: new URLSearchParams({
165
+ grant_type: "refresh_token",
166
+ refresh_token: session.refreshToken
167
+ })
168
+ });
169
+ if (!response.ok) {
170
+ await saveSession(null);
171
+ return null;
172
+ }
173
+ const body = await response.json();
174
+ const nextSession = {
175
+ ...session,
176
+ accessToken: body.access_token,
177
+ refreshToken: body.refresh_token ?? session.refreshToken,
178
+ expiresAt: body.expires_in ? Date.now() + body.expires_in * 1e3 : session.expiresAt
179
+ };
180
+ await saveSession(nextSession);
181
+ return nextSession;
182
+ },
183
+ async verifyToken(token) {
184
+ if (token.split(".").length !== 3) return false;
185
+ const response = await fetch(`${authBase}/api/auth/oauth2/userinfo`, {
186
+ headers: { authorization: `Bearer ${token}` }
187
+ });
188
+ return response.ok;
189
+ },
190
+ async getUser() {
191
+ const token = await client.getAccessToken();
192
+ if (!token) return null;
193
+ const response = await fetch(`${baseUrl}/api/auth/oauth2/userinfo`, {
194
+ headers: { authorization: `Bearer ${token}` }
195
+ });
196
+ return response.ok ? response.json() : null;
197
+ }
198
+ };
199
+ return client;
200
+ }
201
+ export {
202
+ SmartHiveAuthError,
203
+ generateCodeChallenge,
204
+ generateCodeVerifier,
205
+ initAuth,
206
+ smartHiveAuthStorageKeys
207
+ };
208
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface SmartHiveAuthConfig {\n projectId: string;\n publishableKey: string;\n baseUrl: string;\n /** Custom branded auth domain, e.g. \"https://auth.myapp.com\". Used as the entry point for login/token flows. Falls back to baseUrl. */\n authDomain?: string;\n /** Fallback auth domain if authDomain is unreachable, e.g. \"https://auth.smarthivelabs.dev\". */\n fallbackAuthDomain?: string;\n redirectUri?: string;\n storage?: AuthStorage;\n temporaryStorage?: AuthStorage;\n}\n\nexport interface AuthStorage {\n getItem(key: string): string | null | Promise<string | null>;\n setItem(key: string, value: string): void | Promise<void>;\n removeItem(key: string): void | Promise<void>;\n}\n\nexport interface AuthSession {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n user?: unknown;\n}\n\nexport interface SmartHiveAuthClient {\n initialize(): Promise<void>;\n login(options?: { redirectUri?: string; state?: string }): Promise<void>;\n handleCallback(options?: { url?: string }): Promise<AuthSession>;\n logout(): Promise<void>;\n getSession(): Promise<AuthSession | null>;\n getAccessToken(): Promise<string | null>;\n getAuthorizationHeader(): Promise<Record<string, string>>;\n fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;\n refreshSession(): Promise<AuthSession | null>;\n verifyToken(token: string): Promise<boolean>;\n getUser(): Promise<unknown>;\n}\n\nexport class SmartHiveAuthError extends Error {\n constructor(public code: string, message: string) {\n super(message);\n this.name = \"SmartHiveAuthError\";\n }\n}\n\n// ── PKCE helpers (Web Crypto — works in browser + Node 18+) ──────────────────\n\nexport async function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64urlEncode(array);\n}\n\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const digest = await crypto.subtle.digest(\"SHA-256\", data);\n return base64urlEncode(new Uint8Array(digest));\n}\n\nfunction base64urlEncode(buffer: Uint8Array): string {\n let str = \"\";\n for (const byte of buffer) str += String.fromCharCode(byte);\n return btoa(str).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n// ── Storage keys ─────────────────────────────────────────────────────────────\n\nexport const smartHiveAuthStorageKeys = {\n session: \"smarthive.auth.session\",\n pkceVerifier: \"smarthive.auth.pkce_verifier\",\n pkceState: \"smarthive.auth.pkce_state\"\n} as const;\n\nfunction browserStorage(): AuthStorage {\n return {\n getItem: (key) => globalThis.localStorage?.getItem(key) ?? null,\n setItem: (key, value) => globalThis.localStorage?.setItem(key, value),\n removeItem: (key) => globalThis.localStorage?.removeItem(key)\n };\n}\n\nfunction sessionStorageAdapter(): Pick<AuthStorage, \"getItem\" | \"setItem\" | \"removeItem\"> {\n return {\n getItem: (key) => globalThis.sessionStorage?.getItem(key) ?? null,\n setItem: (key, value) => globalThis.sessionStorage?.setItem(key, value),\n removeItem: (key) => globalThis.sessionStorage?.removeItem(key)\n };\n}\n\nfunction normalizeBaseUrl(baseUrl: string) {\n return baseUrl.replace(/\\/$/, \"\");\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport function initAuth(config: SmartHiveAuthConfig): SmartHiveAuthClient {\n const baseUrl = normalizeBaseUrl(config.baseUrl);\n const authBase = normalizeBaseUrl(config.authDomain ?? config.baseUrl);\n const storage = config.storage ?? browserStorage();\n const tempStorage = config.temporaryStorage ?? sessionStorageAdapter();\n\n async function saveSession(session: AuthSession | null) {\n if (!session) { await storage.removeItem(smartHiveAuthStorageKeys.session); return; }\n await storage.setItem(smartHiveAuthStorageKeys.session, JSON.stringify(session));\n }\n\n async function readSession(): Promise<AuthSession | null> {\n const raw = await storage.getItem(smartHiveAuthStorageKeys.session);\n return raw ? JSON.parse(raw) as AuthSession : null;\n }\n\n const client: SmartHiveAuthClient = {\n async initialize() {\n const response = await fetch(\n `${baseUrl}/sdk/config?projectId=${encodeURIComponent(config.projectId)}&publishableKey=${encodeURIComponent(config.publishableKey)}`\n );\n if (!response.ok) throw new SmartHiveAuthError(\"project_not_found\", \"Smart Hive Auth project configuration was not found.\");\n },\n\n async login(options) {\n const redirectUri = options?.redirectUri ?? config.redirectUri ?? globalThis.location?.href;\n const state = options?.state ?? crypto.randomUUID();\n const verifier = await generateCodeVerifier();\n const challenge = await generateCodeChallenge(verifier);\n\n await tempStorage.setItem(smartHiveAuthStorageKeys.pkceVerifier, verifier);\n await tempStorage.setItem(smartHiveAuthStorageKeys.pkceState, state);\n\n const url = new URL(`${authBase}/api/auth/oauth2/authorize`);\n url.searchParams.set(\"project_id\", config.projectId);\n url.searchParams.set(\"publishable_key\", config.publishableKey);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"redirect_uri\", redirectUri);\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"code_challenge\", challenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n globalThis.location.assign(url.toString());\n },\n\n async handleCallback(options) {\n const href = options?.url ?? globalThis.location?.href;\n if (!href) throw new SmartHiveAuthError(\"callback_failed\", \"No URL provided for callback handling.\");\n\n const url = new URL(href);\n const code = url.searchParams.get(\"code\");\n const returnedState = url.searchParams.get(\"state\");\n\n if (!code) throw new SmartHiveAuthError(\"callback_failed\", \"No authorization code in callback URL.\");\n\n const storedState = await tempStorage.getItem(smartHiveAuthStorageKeys.pkceState);\n if (!storedState || !returnedState || storedState !== returnedState) {\n throw new SmartHiveAuthError(\"state_mismatch\", \"OAuth state mismatch — possible CSRF attack.\");\n }\n\n const verifier = await tempStorage.getItem(smartHiveAuthStorageKeys.pkceVerifier);\n if (!verifier) {\n throw new SmartHiveAuthError(\"pkce_missing\", \"Missing PKCE verifier for authorization code exchange.\");\n }\n const redirectUri = config.redirectUri ?? (url.origin + url.pathname);\n\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n code,\n redirect_uri: redirectUri,\n client_id: config.publishableKey\n });\n body.set(\"code_verifier\", verifier);\n\n const response = await fetch(`${authBase}/api/auth/oauth2/token`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body\n });\n\n if (!response.ok) {\n const err = await response.json().catch(() => ({})) as { error?: string; error_description?: string };\n throw new SmartHiveAuthError(err.error ?? \"token_error\", err.error_description ?? \"Token exchange failed.\");\n }\n\n const tokenBody = await response.json() as {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n token_type?: string;\n };\n\n const session: AuthSession = {\n accessToken: tokenBody.access_token,\n refreshToken: tokenBody.refresh_token,\n expiresAt: tokenBody.expires_in ? Date.now() + tokenBody.expires_in * 1000 : undefined\n };\n\n await saveSession(session);\n await tempStorage.removeItem(smartHiveAuthStorageKeys.pkceVerifier);\n await tempStorage.removeItem(smartHiveAuthStorageKeys.pkceState);\n\n return session;\n },\n\n async logout() {\n await saveSession(null);\n await fetch(`${baseUrl}/api/auth/sign-out`, { method: \"POST\", credentials: \"include\" });\n },\n\n getSession: readSession,\n\n async getAccessToken() {\n const session = await readSession();\n if (!session) return null;\n if (session.expiresAt && Date.now() > session.expiresAt - 30000) {\n return (await client.refreshSession())?.accessToken ?? null;\n }\n return session.accessToken;\n },\n\n async getAuthorizationHeader() {\n const token = await client.getAccessToken();\n const headers: Record<string, string> = {};\n if (token) headers.authorization = `Bearer ${token}`;\n return headers;\n },\n\n async fetch(input, init = {}) {\n const authHeader = await client.getAuthorizationHeader();\n const headers = new Headers(init.headers);\n for (const [key, value] of Object.entries(authHeader)) {\n headers.set(key, value);\n }\n return fetch(input, { ...init, headers });\n },\n\n async refreshSession() {\n const session = await readSession();\n if (!session?.refreshToken) return session;\n const response = await fetch(`${baseUrl}/api/auth/oauth2/token`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: session.refreshToken\n })\n });\n if (!response.ok) { await saveSession(null); return null; }\n const body = await response.json() as { access_token: string; refresh_token?: string; expires_in?: number };\n const nextSession: AuthSession = {\n ...session,\n accessToken: body.access_token,\n refreshToken: body.refresh_token ?? session.refreshToken,\n expiresAt: body.expires_in ? Date.now() + body.expires_in * 1000 : session.expiresAt\n };\n await saveSession(nextSession);\n return nextSession;\n },\n\n async verifyToken(token) {\n if (token.split(\".\").length !== 3) return false;\n const response = await fetch(`${authBase}/api/auth/oauth2/userinfo`, {\n headers: { authorization: `Bearer ${token}` }\n });\n return response.ok;\n },\n\n async getUser() {\n const token = await client.getAccessToken();\n if (!token) return null;\n const response = await fetch(`${baseUrl}/api/auth/oauth2/userinfo`, {\n headers: { authorization: `Bearer ${token}` }\n });\n return response.ok ? response.json() : null;\n }\n };\n\n return client;\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@smarthivelabs-devs/auth-sdk",
3
+ "version": "0.1.0",
4
+ "description": "SmartHive Auth JavaScript/TypeScript SDK — core client for browser, Node.js, and React Native",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/smarthivelabs-devs/smarthive-auth-sdk"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "type": "module",
14
+ "main": "./dist/index.cjs",
15
+ "module": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "require": "./dist/index.cjs"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "devDependencies": {
30
+ "tsup": "^8.3.0",
31
+ "typescript": "^5.7.3"
32
+ },
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "build:watch": "tsup --watch",
36
+ "typecheck": "tsc -p tsconfig.json --noEmit"
37
+ }
38
+ }