@yaotoshi/auth-sdk 0.1.1 → 0.2.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.cjs +110 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +110 -53
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -100,24 +100,38 @@ var AuthStorage = class {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
clearAll() {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
try {
|
|
104
|
+
const prefix = this.prefix + "_";
|
|
105
|
+
for (const store of [sessionStorage, localStorage]) {
|
|
106
|
+
const keys = Object.keys(store).filter((k) => k.startsWith(prefix));
|
|
107
|
+
keys.forEach((k) => store.removeItem(k));
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
106
111
|
}
|
|
107
112
|
};
|
|
108
113
|
|
|
109
114
|
// src/client.ts
|
|
110
115
|
var YaotoshiAuth = class {
|
|
111
116
|
constructor(config) {
|
|
117
|
+
this.processing = false;
|
|
112
118
|
this.config = {
|
|
113
119
|
scopes: ["openid", "email"],
|
|
114
120
|
postLogoutRedirectUri: void 0,
|
|
115
121
|
storagePrefix: "yaotoshi_auth",
|
|
122
|
+
apiPathPrefix: "/api/proxy",
|
|
116
123
|
...config
|
|
117
124
|
};
|
|
118
125
|
this.storage = new AuthStorage(this.config.storagePrefix);
|
|
119
126
|
}
|
|
127
|
+
apiUrl(path) {
|
|
128
|
+
const prefix = this.config.apiPathPrefix ?? "/api/proxy";
|
|
129
|
+
return `${this.config.accountsUrl}${prefix}${path}`;
|
|
130
|
+
}
|
|
120
131
|
async login() {
|
|
132
|
+
if (typeof window === "undefined") {
|
|
133
|
+
throw new Error("login() requires a browser environment");
|
|
134
|
+
}
|
|
121
135
|
const codeVerifier = generateCodeVerifier();
|
|
122
136
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
123
137
|
const state = generateState();
|
|
@@ -127,7 +141,7 @@ var YaotoshiAuth = class {
|
|
|
127
141
|
response_type: "code",
|
|
128
142
|
client_id: this.config.clientId,
|
|
129
143
|
redirect_uri: this.config.redirectUri,
|
|
130
|
-
scope:
|
|
144
|
+
scope: this.config.scopes.join(" "),
|
|
131
145
|
state,
|
|
132
146
|
code_challenge: codeChallenge,
|
|
133
147
|
code_challenge_method: "S256"
|
|
@@ -135,75 +149,118 @@ var YaotoshiAuth = class {
|
|
|
135
149
|
window.location.href = `${this.config.accountsUrl}/authorize?${params.toString()}`;
|
|
136
150
|
}
|
|
137
151
|
async handleCallback() {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
152
|
+
if (this.processing) {
|
|
153
|
+
throw new Error("Callback is already being processed");
|
|
154
|
+
}
|
|
155
|
+
this.processing = true;
|
|
156
|
+
try {
|
|
157
|
+
const params = new URLSearchParams(window.location.search);
|
|
158
|
+
const code = params.get("code");
|
|
159
|
+
const state = params.get("state");
|
|
160
|
+
const error = params.get("error");
|
|
161
|
+
if (error) {
|
|
162
|
+
const errorDescription = params.get("error_description");
|
|
163
|
+
throw new Error(`Authorization error: ${error}${errorDescription ? ` \u2014 ${errorDescription}` : ""}`);
|
|
164
|
+
}
|
|
165
|
+
if (!code || !state) {
|
|
166
|
+
throw new Error("Missing code or state in callback");
|
|
167
|
+
}
|
|
168
|
+
const savedState = this.storage.get("state");
|
|
169
|
+
if (state !== savedState) {
|
|
170
|
+
throw new Error("State mismatch \u2014 possible CSRF attack");
|
|
171
|
+
}
|
|
172
|
+
const codeVerifier = this.storage.get("code_verifier");
|
|
173
|
+
if (!codeVerifier) {
|
|
174
|
+
throw new Error("Missing code verifier \u2014 login flow may have been interrupted");
|
|
175
|
+
}
|
|
176
|
+
const tokenResponse = await fetch(this.apiUrl("/token"), {
|
|
177
|
+
method: "POST",
|
|
178
|
+
headers: { "Content-Type": "application/json" },
|
|
179
|
+
credentials: "include",
|
|
180
|
+
body: JSON.stringify({
|
|
181
|
+
grant_type: "authorization_code",
|
|
182
|
+
code,
|
|
183
|
+
client_id: this.config.clientId,
|
|
184
|
+
redirect_uri: this.config.redirectUri,
|
|
185
|
+
code_verifier: codeVerifier
|
|
186
|
+
})
|
|
187
|
+
});
|
|
188
|
+
if (!tokenResponse.ok) {
|
|
189
|
+
const err = await tokenResponse.json().catch(() => ({}));
|
|
190
|
+
const message = Array.isArray(err.message) ? err.message.join(", ") : err.message || "Token exchange failed";
|
|
191
|
+
throw new Error(message);
|
|
192
|
+
}
|
|
193
|
+
const tokenData = await tokenResponse.json();
|
|
194
|
+
this.storage.remove("code_verifier");
|
|
195
|
+
this.storage.remove("state");
|
|
196
|
+
this.storage.setPersistent("access_token", tokenData.access_token);
|
|
197
|
+
this.storage.setPersistent("token_expires_at", String(Date.now() + tokenData.expires_in * 1e3));
|
|
198
|
+
const user = await this.getUser(tokenData.access_token);
|
|
199
|
+
return {
|
|
200
|
+
accessToken: tokenData.access_token,
|
|
201
|
+
scope: tokenData.scope,
|
|
202
|
+
expiresIn: tokenData.expires_in,
|
|
203
|
+
user
|
|
204
|
+
};
|
|
205
|
+
} finally {
|
|
206
|
+
this.processing = false;
|
|
170
207
|
}
|
|
171
|
-
const tokenData = await tokenResponse.json();
|
|
172
|
-
this.storage.remove("code_verifier");
|
|
173
|
-
this.storage.remove("state");
|
|
174
|
-
this.storage.setPersistent("access_token", tokenData.access_token);
|
|
175
|
-
const user = await this.getUser(tokenData.access_token);
|
|
176
|
-
return { accessToken: tokenData.access_token, user };
|
|
177
208
|
}
|
|
178
209
|
async getUser(token) {
|
|
179
210
|
const accessToken = token || this.getAccessToken();
|
|
180
211
|
if (!accessToken) {
|
|
181
212
|
throw new Error("No access token available");
|
|
182
213
|
}
|
|
183
|
-
const response = await fetch(
|
|
184
|
-
headers: { Authorization: `Bearer ${accessToken}` }
|
|
214
|
+
const response = await fetch(this.apiUrl("/me"), {
|
|
215
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
216
|
+
credentials: "include"
|
|
185
217
|
});
|
|
186
218
|
if (!response.ok) {
|
|
187
219
|
if (response.status === 401) {
|
|
188
220
|
this.storage.removePersistent("access_token");
|
|
221
|
+
this.storage.removePersistent("token_expires_at");
|
|
189
222
|
}
|
|
190
223
|
throw new Error("Failed to fetch user info");
|
|
191
224
|
}
|
|
192
|
-
|
|
225
|
+
const data = await response.json();
|
|
226
|
+
if (!data.sub || !data.email) {
|
|
227
|
+
throw new Error("Invalid user info response");
|
|
228
|
+
}
|
|
229
|
+
return data;
|
|
193
230
|
}
|
|
194
|
-
logout() {
|
|
231
|
+
async logout() {
|
|
232
|
+
if (typeof window === "undefined") {
|
|
233
|
+
throw new Error("logout() requires a browser environment");
|
|
234
|
+
}
|
|
195
235
|
const token = this.getAccessToken();
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
236
|
+
try {
|
|
237
|
+
await fetch(this.apiUrl("/logout"), {
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers: { "Content-Type": "application/json" },
|
|
240
|
+
credentials: "include",
|
|
241
|
+
body: JSON.stringify({
|
|
242
|
+
...token && { token },
|
|
243
|
+
...this.config.clientId && { client_id: this.config.clientId },
|
|
244
|
+
...this.config.postLogoutRedirectUri && { post_logout_redirect_uri: this.config.postLogoutRedirectUri }
|
|
245
|
+
})
|
|
246
|
+
});
|
|
247
|
+
} finally {
|
|
248
|
+
this.storage.clearAll();
|
|
249
|
+
}
|
|
200
250
|
if (this.config.postLogoutRedirectUri) {
|
|
201
|
-
|
|
251
|
+
window.location.href = this.config.postLogoutRedirectUri;
|
|
202
252
|
}
|
|
203
|
-
window.location.href = `${this.config.accountsUrl}/api/proxy/logout?${params.toString()}`;
|
|
204
253
|
}
|
|
205
254
|
isAuthenticated() {
|
|
206
|
-
|
|
255
|
+
const token = this.getAccessToken();
|
|
256
|
+
if (!token) return false;
|
|
257
|
+
const expiresAt = this.storage.getPersistent("token_expires_at");
|
|
258
|
+
if (expiresAt && Date.now() > Number(expiresAt)) {
|
|
259
|
+
this.storage.removePersistent("access_token");
|
|
260
|
+
this.storage.removePersistent("token_expires_at");
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
return true;
|
|
207
264
|
}
|
|
208
265
|
getAccessToken() {
|
|
209
266
|
return this.storage.getPersistent("access_token");
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/pkce.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["export { YaotoshiAuth } from './client';\nexport type { YaotoshiAuthConfig, TokenResponse, UserInfo, AuthResult } from './types';\n","function generateRandomBytes(length: number): Uint8Array {\n const array = new Uint8Array(length);\n crypto.getRandomValues(array);\n return array;\n}\n\nfunction base64UrlEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function generateCodeVerifier(): string {\n const bytes = generateRandomBytes(32);\n return base64UrlEncode(bytes);\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(digest);\n}\n\nexport function generateState(): string {\n const bytes = generateRandomBytes(16);\n return base64UrlEncode(bytes);\n}\n","export class AuthStorage {\n private prefix: string;\n\n constructor(prefix = 'yaotoshi_auth') {\n this.prefix = prefix;\n }\n\n private key(name: string): string {\n return `${this.prefix}_${name}`;\n }\n\n get(name: string): string | null {\n try {\n return sessionStorage.getItem(this.key(name));\n } catch {\n return null;\n }\n }\n\n set(name: string, value: string): void {\n try {\n sessionStorage.setItem(this.key(name), value);\n } catch {\n // Storage unavailable\n }\n }\n\n remove(name: string): void {\n try {\n sessionStorage.removeItem(this.key(name));\n } catch {\n // Storage unavailable\n }\n }\n\n getPersistent(name: string): string | null {\n try {\n return localStorage.getItem(this.key(name));\n } catch {\n return null;\n }\n }\n\n setPersistent(name: string, value: string): void {\n try {\n localStorage.setItem(this.key(name), value);\n } catch {\n // Storage unavailable\n }\n }\n\n removePersistent(name: string): void {\n try {\n localStorage.removeItem(this.key(name));\n } catch {\n // Storage unavailable\n }\n }\n\n clearAll(): void {\n this.remove('code_verifier');\n this.remove('state');\n this.removePersistent('access_token');\n }\n}\n","import { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { AuthStorage } from './storage';\nimport type { YaotoshiAuthConfig, TokenResponse, UserInfo, AuthResult } from './types';\n\nexport class YaotoshiAuth {\n private config: Required<Pick<YaotoshiAuthConfig, 'clientId' | 'redirectUri' | 'accountsUrl'>> &\n YaotoshiAuthConfig;\n private storage: AuthStorage;\n\n constructor(config: YaotoshiAuthConfig) {\n this.config = {\n scopes: ['openid', 'email'],\n postLogoutRedirectUri: undefined,\n storagePrefix: 'yaotoshi_auth',\n ...config,\n };\n this.storage = new AuthStorage(this.config.storagePrefix);\n }\n\n async login(): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n this.storage.set('code_verifier', codeVerifier);\n this.storage.set('state', state);\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n scope: (this.config.scopes ?? ['openid', 'email']).join(' '),\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n });\n\n window.location.href = `${this.config.accountsUrl}/authorize?${params.toString()}`;\n }\n\n async handleCallback(): Promise<AuthResult> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(`Authorization error: ${error}`);\n }\n\n if (!code || !state) {\n throw new Error('Missing code or state in callback');\n }\n\n const savedState = this.storage.get('state');\n if (state !== savedState) {\n throw new Error('State mismatch — possible CSRF attack');\n }\n\n const codeVerifier = this.storage.get('code_verifier');\n if (!codeVerifier) {\n throw new Error('Missing code verifier — login flow may have been interrupted');\n }\n\n // Exchange code for token\n const tokenResponse = await fetch(`${this.config.accountsUrl}/api/proxy/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'authorization_code',\n code,\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const err = await tokenResponse.json().catch(() => ({}));\n throw new Error(err.message || 'Token exchange failed');\n }\n\n const tokenData: TokenResponse = await tokenResponse.json();\n\n // Clean up PKCE state\n this.storage.remove('code_verifier');\n this.storage.remove('state');\n\n // Persist the access token\n this.storage.setPersistent('access_token', tokenData.access_token);\n\n // Fetch user info\n const user = await this.getUser(tokenData.access_token);\n\n return { accessToken: tokenData.access_token, user };\n }\n\n async getUser(token?: string): Promise<UserInfo> {\n const accessToken = token || this.getAccessToken();\n if (!accessToken) {\n throw new Error('No access token available');\n }\n\n const response = await fetch(`${this.config.accountsUrl}/api/proxy/me`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n this.storage.removePersistent('access_token');\n }\n throw new Error('Failed to fetch user info');\n }\n\n return response.json();\n }\n\n logout(): void {\n const token = this.getAccessToken();\n this.storage.clearAll();\n\n const params = new URLSearchParams();\n if (token) params.set('token', token);\n if (this.config.clientId) params.set('client_id', this.config.clientId);\n if (this.config.postLogoutRedirectUri) {\n params.set('post_logout_redirect_uri', this.config.postLogoutRedirectUri);\n }\n\n window.location.href = `${this.config.accountsUrl}/api/proxy/logout?${params.toString()}`;\n }\n\n isAuthenticated(): boolean {\n return !!this.getAccessToken();\n }\n\n getAccessToken(): string | null {\n return this.storage.getPersistent('access_token');\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/pkce.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["export { YaotoshiAuth } from './client';\nexport type { YaotoshiAuthConfig, TokenResponse, UserInfo, AuthResult } from './types';\n","function generateRandomBytes(length: number): Uint8Array {\n const array = new Uint8Array(length);\n crypto.getRandomValues(array);\n return array;\n}\n\nfunction base64UrlEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function generateCodeVerifier(): string {\n const bytes = generateRandomBytes(32);\n return base64UrlEncode(bytes);\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(digest);\n}\n\nexport function generateState(): string {\n const bytes = generateRandomBytes(16);\n return base64UrlEncode(bytes);\n}\n","export class AuthStorage {\n private prefix: string;\n\n constructor(prefix = 'yaotoshi_auth') {\n this.prefix = prefix;\n }\n\n private key(name: string): string {\n return `${this.prefix}_${name}`;\n }\n\n get(name: string): string | null {\n try {\n return sessionStorage.getItem(this.key(name));\n } catch {\n return null;\n }\n }\n\n set(name: string, value: string): void {\n try {\n sessionStorage.setItem(this.key(name), value);\n } catch {\n // Storage unavailable\n }\n }\n\n remove(name: string): void {\n try {\n sessionStorage.removeItem(this.key(name));\n } catch {\n // Storage unavailable\n }\n }\n\n getPersistent(name: string): string | null {\n try {\n return localStorage.getItem(this.key(name));\n } catch {\n return null;\n }\n }\n\n setPersistent(name: string, value: string): void {\n try {\n localStorage.setItem(this.key(name), value);\n } catch {\n // Storage unavailable\n }\n }\n\n removePersistent(name: string): void {\n try {\n localStorage.removeItem(this.key(name));\n } catch {\n // Storage unavailable\n }\n }\n\n clearAll(): void {\n try {\n const prefix = this.prefix + '_';\n for (const store of [sessionStorage, localStorage]) {\n const keys = Object.keys(store).filter(k => k.startsWith(prefix));\n keys.forEach(k => store.removeItem(k));\n }\n } catch {\n // Storage unavailable\n }\n }\n}\n","import { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { AuthStorage } from './storage';\nimport type { YaotoshiAuthConfig, TokenResponse, UserInfo, AuthResult } from './types';\n\nexport class YaotoshiAuth {\n private config: Required<Pick<YaotoshiAuthConfig, 'clientId' | 'redirectUri' | 'accountsUrl'>> &\n YaotoshiAuthConfig;\n private storage: AuthStorage;\n private processing = false;\n\n constructor(config: YaotoshiAuthConfig) {\n this.config = {\n scopes: ['openid', 'email'],\n postLogoutRedirectUri: undefined,\n storagePrefix: 'yaotoshi_auth',\n apiPathPrefix: '/api/proxy',\n ...config,\n };\n this.storage = new AuthStorage(this.config.storagePrefix);\n }\n\n private apiUrl(path: string): string {\n const prefix = this.config.apiPathPrefix ?? '/api/proxy';\n return `${this.config.accountsUrl}${prefix}${path}`;\n }\n\n async login(): Promise<void> {\n if (typeof window === 'undefined') {\n throw new Error('login() requires a browser environment');\n }\n\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n this.storage.set('code_verifier', codeVerifier);\n this.storage.set('state', state);\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n scope: this.config.scopes!.join(' '),\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n });\n\n window.location.href = `${this.config.accountsUrl}/authorize?${params.toString()}`;\n }\n\n async handleCallback(): Promise<AuthResult> {\n if (this.processing) {\n throw new Error('Callback is already being processed');\n }\n this.processing = true;\n\n try {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n const errorDescription = params.get('error_description');\n throw new Error(`Authorization error: ${error}${errorDescription ? ` — ${errorDescription}` : ''}`);\n }\n\n if (!code || !state) {\n throw new Error('Missing code or state in callback');\n }\n\n const savedState = this.storage.get('state');\n if (state !== savedState) {\n throw new Error('State mismatch — possible CSRF attack');\n }\n\n const codeVerifier = this.storage.get('code_verifier');\n if (!codeVerifier) {\n throw new Error('Missing code verifier — login flow may have been interrupted');\n }\n\n const tokenResponse = await fetch(this.apiUrl('/token'), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n grant_type: 'authorization_code',\n code,\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const err = await tokenResponse.json().catch(() => ({}));\n const message = Array.isArray(err.message) ? err.message.join(', ') : (err.message || 'Token exchange failed');\n throw new Error(message);\n }\n\n const tokenData: TokenResponse = await tokenResponse.json();\n\n // Clean up PKCE state\n this.storage.remove('code_verifier');\n this.storage.remove('state');\n\n // Persist the access token\n this.storage.setPersistent('access_token', tokenData.access_token);\n this.storage.setPersistent('token_expires_at', String(Date.now() + tokenData.expires_in * 1000));\n\n // Fetch user info\n const user = await this.getUser(tokenData.access_token);\n\n return {\n accessToken: tokenData.access_token,\n scope: tokenData.scope,\n expiresIn: tokenData.expires_in,\n user,\n };\n } finally {\n this.processing = false;\n }\n }\n\n async getUser(token?: string): Promise<UserInfo> {\n const accessToken = token || this.getAccessToken();\n if (!accessToken) {\n throw new Error('No access token available');\n }\n\n const response = await fetch(this.apiUrl('/me'), {\n headers: { Authorization: `Bearer ${accessToken}` },\n credentials: 'include',\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n this.storage.removePersistent('access_token');\n this.storage.removePersistent('token_expires_at');\n }\n throw new Error('Failed to fetch user info');\n }\n\n const data = await response.json();\n if (!data.sub || !data.email) {\n throw new Error('Invalid user info response');\n }\n\n return data;\n }\n\n async logout(): Promise<void> {\n if (typeof window === 'undefined') {\n throw new Error('logout() requires a browser environment');\n }\n\n const token = this.getAccessToken();\n\n try {\n await fetch(this.apiUrl('/logout'), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n ...(token && { token }),\n ...(this.config.clientId && { client_id: this.config.clientId }),\n ...(this.config.postLogoutRedirectUri && { post_logout_redirect_uri: this.config.postLogoutRedirectUri }),\n }),\n });\n } finally {\n // Clear local state regardless of server response\n this.storage.clearAll();\n }\n\n // Redirect after successful logout\n if (this.config.postLogoutRedirectUri) {\n window.location.href = this.config.postLogoutRedirectUri;\n }\n }\n\n isAuthenticated(): boolean {\n const token = this.getAccessToken();\n if (!token) return false;\n\n const expiresAt = this.storage.getPersistent('token_expires_at');\n if (expiresAt && Date.now() > Number(expiresAt)) {\n this.storage.removePersistent('access_token');\n this.storage.removePersistent('token_expires_at');\n return false;\n }\n\n return true;\n }\n\n getAccessToken(): string | null {\n return this.storage.getPersistent('access_token');\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
/** Configuration options for the YaotoshiAuth client. */
|
|
2
2
|
interface YaotoshiAuthConfig {
|
|
3
|
+
/** OAuth client ID */
|
|
3
4
|
clientId: string;
|
|
5
|
+
/** OAuth redirect URI for callback */
|
|
4
6
|
redirectUri: string;
|
|
7
|
+
/** URI to redirect to after logout */
|
|
5
8
|
postLogoutRedirectUri?: string;
|
|
9
|
+
/** Base URL of the accounts service (e.g., "https://accounts.example.com") */
|
|
6
10
|
accountsUrl: string;
|
|
11
|
+
/** OAuth scopes to request (default: ['openid', 'email']) */
|
|
7
12
|
scopes?: string[];
|
|
13
|
+
/** Prefix for storage keys (default: 'yaotoshi_auth') */
|
|
8
14
|
storagePrefix?: string;
|
|
15
|
+
/**
|
|
16
|
+
* API path prefix. Set to '/api/proxy' when using the Next.js proxy (default),
|
|
17
|
+
* or '' when connecting directly to the API.
|
|
18
|
+
*/
|
|
19
|
+
apiPathPrefix?: string;
|
|
9
20
|
}
|
|
10
21
|
interface TokenResponse {
|
|
11
22
|
access_token: string;
|
|
@@ -13,6 +24,7 @@ interface TokenResponse {
|
|
|
13
24
|
expires_in: number;
|
|
14
25
|
scope: string;
|
|
15
26
|
}
|
|
27
|
+
/** User info returned by the /me endpoint. `sub` is the user's unique ID (cuid). */
|
|
16
28
|
interface UserInfo {
|
|
17
29
|
sub: string;
|
|
18
30
|
email: string;
|
|
@@ -20,17 +32,23 @@ interface UserInfo {
|
|
|
20
32
|
}
|
|
21
33
|
interface AuthResult {
|
|
22
34
|
accessToken: string;
|
|
35
|
+
/** Granted scopes (may differ from requested scopes) */
|
|
36
|
+
scope: string;
|
|
37
|
+
/** Token lifetime in seconds */
|
|
38
|
+
expiresIn: number;
|
|
23
39
|
user: UserInfo;
|
|
24
40
|
}
|
|
25
41
|
|
|
26
42
|
declare class YaotoshiAuth {
|
|
27
43
|
private config;
|
|
28
44
|
private storage;
|
|
45
|
+
private processing;
|
|
29
46
|
constructor(config: YaotoshiAuthConfig);
|
|
47
|
+
private apiUrl;
|
|
30
48
|
login(): Promise<void>;
|
|
31
49
|
handleCallback(): Promise<AuthResult>;
|
|
32
50
|
getUser(token?: string): Promise<UserInfo>;
|
|
33
|
-
logout(): void
|
|
51
|
+
logout(): Promise<void>;
|
|
34
52
|
isAuthenticated(): boolean;
|
|
35
53
|
getAccessToken(): string | null;
|
|
36
54
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
/** Configuration options for the YaotoshiAuth client. */
|
|
2
2
|
interface YaotoshiAuthConfig {
|
|
3
|
+
/** OAuth client ID */
|
|
3
4
|
clientId: string;
|
|
5
|
+
/** OAuth redirect URI for callback */
|
|
4
6
|
redirectUri: string;
|
|
7
|
+
/** URI to redirect to after logout */
|
|
5
8
|
postLogoutRedirectUri?: string;
|
|
9
|
+
/** Base URL of the accounts service (e.g., "https://accounts.example.com") */
|
|
6
10
|
accountsUrl: string;
|
|
11
|
+
/** OAuth scopes to request (default: ['openid', 'email']) */
|
|
7
12
|
scopes?: string[];
|
|
13
|
+
/** Prefix for storage keys (default: 'yaotoshi_auth') */
|
|
8
14
|
storagePrefix?: string;
|
|
15
|
+
/**
|
|
16
|
+
* API path prefix. Set to '/api/proxy' when using the Next.js proxy (default),
|
|
17
|
+
* or '' when connecting directly to the API.
|
|
18
|
+
*/
|
|
19
|
+
apiPathPrefix?: string;
|
|
9
20
|
}
|
|
10
21
|
interface TokenResponse {
|
|
11
22
|
access_token: string;
|
|
@@ -13,6 +24,7 @@ interface TokenResponse {
|
|
|
13
24
|
expires_in: number;
|
|
14
25
|
scope: string;
|
|
15
26
|
}
|
|
27
|
+
/** User info returned by the /me endpoint. `sub` is the user's unique ID (cuid). */
|
|
16
28
|
interface UserInfo {
|
|
17
29
|
sub: string;
|
|
18
30
|
email: string;
|
|
@@ -20,17 +32,23 @@ interface UserInfo {
|
|
|
20
32
|
}
|
|
21
33
|
interface AuthResult {
|
|
22
34
|
accessToken: string;
|
|
35
|
+
/** Granted scopes (may differ from requested scopes) */
|
|
36
|
+
scope: string;
|
|
37
|
+
/** Token lifetime in seconds */
|
|
38
|
+
expiresIn: number;
|
|
23
39
|
user: UserInfo;
|
|
24
40
|
}
|
|
25
41
|
|
|
26
42
|
declare class YaotoshiAuth {
|
|
27
43
|
private config;
|
|
28
44
|
private storage;
|
|
45
|
+
private processing;
|
|
29
46
|
constructor(config: YaotoshiAuthConfig);
|
|
47
|
+
private apiUrl;
|
|
30
48
|
login(): Promise<void>;
|
|
31
49
|
handleCallback(): Promise<AuthResult>;
|
|
32
50
|
getUser(token?: string): Promise<UserInfo>;
|
|
33
|
-
logout(): void
|
|
51
|
+
logout(): Promise<void>;
|
|
34
52
|
isAuthenticated(): boolean;
|
|
35
53
|
getAccessToken(): string | null;
|
|
36
54
|
}
|
package/dist/index.js
CHANGED
|
@@ -74,24 +74,38 @@ var AuthStorage = class {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
clearAll() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
try {
|
|
78
|
+
const prefix = this.prefix + "_";
|
|
79
|
+
for (const store of [sessionStorage, localStorage]) {
|
|
80
|
+
const keys = Object.keys(store).filter((k) => k.startsWith(prefix));
|
|
81
|
+
keys.forEach((k) => store.removeItem(k));
|
|
82
|
+
}
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
80
85
|
}
|
|
81
86
|
};
|
|
82
87
|
|
|
83
88
|
// src/client.ts
|
|
84
89
|
var YaotoshiAuth = class {
|
|
85
90
|
constructor(config) {
|
|
91
|
+
this.processing = false;
|
|
86
92
|
this.config = {
|
|
87
93
|
scopes: ["openid", "email"],
|
|
88
94
|
postLogoutRedirectUri: void 0,
|
|
89
95
|
storagePrefix: "yaotoshi_auth",
|
|
96
|
+
apiPathPrefix: "/api/proxy",
|
|
90
97
|
...config
|
|
91
98
|
};
|
|
92
99
|
this.storage = new AuthStorage(this.config.storagePrefix);
|
|
93
100
|
}
|
|
101
|
+
apiUrl(path) {
|
|
102
|
+
const prefix = this.config.apiPathPrefix ?? "/api/proxy";
|
|
103
|
+
return `${this.config.accountsUrl}${prefix}${path}`;
|
|
104
|
+
}
|
|
94
105
|
async login() {
|
|
106
|
+
if (typeof window === "undefined") {
|
|
107
|
+
throw new Error("login() requires a browser environment");
|
|
108
|
+
}
|
|
95
109
|
const codeVerifier = generateCodeVerifier();
|
|
96
110
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
97
111
|
const state = generateState();
|
|
@@ -101,7 +115,7 @@ var YaotoshiAuth = class {
|
|
|
101
115
|
response_type: "code",
|
|
102
116
|
client_id: this.config.clientId,
|
|
103
117
|
redirect_uri: this.config.redirectUri,
|
|
104
|
-
scope:
|
|
118
|
+
scope: this.config.scopes.join(" "),
|
|
105
119
|
state,
|
|
106
120
|
code_challenge: codeChallenge,
|
|
107
121
|
code_challenge_method: "S256"
|
|
@@ -109,75 +123,118 @@ var YaotoshiAuth = class {
|
|
|
109
123
|
window.location.href = `${this.config.accountsUrl}/authorize?${params.toString()}`;
|
|
110
124
|
}
|
|
111
125
|
async handleCallback() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
126
|
+
if (this.processing) {
|
|
127
|
+
throw new Error("Callback is already being processed");
|
|
128
|
+
}
|
|
129
|
+
this.processing = true;
|
|
130
|
+
try {
|
|
131
|
+
const params = new URLSearchParams(window.location.search);
|
|
132
|
+
const code = params.get("code");
|
|
133
|
+
const state = params.get("state");
|
|
134
|
+
const error = params.get("error");
|
|
135
|
+
if (error) {
|
|
136
|
+
const errorDescription = params.get("error_description");
|
|
137
|
+
throw new Error(`Authorization error: ${error}${errorDescription ? ` \u2014 ${errorDescription}` : ""}`);
|
|
138
|
+
}
|
|
139
|
+
if (!code || !state) {
|
|
140
|
+
throw new Error("Missing code or state in callback");
|
|
141
|
+
}
|
|
142
|
+
const savedState = this.storage.get("state");
|
|
143
|
+
if (state !== savedState) {
|
|
144
|
+
throw new Error("State mismatch \u2014 possible CSRF attack");
|
|
145
|
+
}
|
|
146
|
+
const codeVerifier = this.storage.get("code_verifier");
|
|
147
|
+
if (!codeVerifier) {
|
|
148
|
+
throw new Error("Missing code verifier \u2014 login flow may have been interrupted");
|
|
149
|
+
}
|
|
150
|
+
const tokenResponse = await fetch(this.apiUrl("/token"), {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: { "Content-Type": "application/json" },
|
|
153
|
+
credentials: "include",
|
|
154
|
+
body: JSON.stringify({
|
|
155
|
+
grant_type: "authorization_code",
|
|
156
|
+
code,
|
|
157
|
+
client_id: this.config.clientId,
|
|
158
|
+
redirect_uri: this.config.redirectUri,
|
|
159
|
+
code_verifier: codeVerifier
|
|
160
|
+
})
|
|
161
|
+
});
|
|
162
|
+
if (!tokenResponse.ok) {
|
|
163
|
+
const err = await tokenResponse.json().catch(() => ({}));
|
|
164
|
+
const message = Array.isArray(err.message) ? err.message.join(", ") : err.message || "Token exchange failed";
|
|
165
|
+
throw new Error(message);
|
|
166
|
+
}
|
|
167
|
+
const tokenData = await tokenResponse.json();
|
|
168
|
+
this.storage.remove("code_verifier");
|
|
169
|
+
this.storage.remove("state");
|
|
170
|
+
this.storage.setPersistent("access_token", tokenData.access_token);
|
|
171
|
+
this.storage.setPersistent("token_expires_at", String(Date.now() + tokenData.expires_in * 1e3));
|
|
172
|
+
const user = await this.getUser(tokenData.access_token);
|
|
173
|
+
return {
|
|
174
|
+
accessToken: tokenData.access_token,
|
|
175
|
+
scope: tokenData.scope,
|
|
176
|
+
expiresIn: tokenData.expires_in,
|
|
177
|
+
user
|
|
178
|
+
};
|
|
179
|
+
} finally {
|
|
180
|
+
this.processing = false;
|
|
144
181
|
}
|
|
145
|
-
const tokenData = await tokenResponse.json();
|
|
146
|
-
this.storage.remove("code_verifier");
|
|
147
|
-
this.storage.remove("state");
|
|
148
|
-
this.storage.setPersistent("access_token", tokenData.access_token);
|
|
149
|
-
const user = await this.getUser(tokenData.access_token);
|
|
150
|
-
return { accessToken: tokenData.access_token, user };
|
|
151
182
|
}
|
|
152
183
|
async getUser(token) {
|
|
153
184
|
const accessToken = token || this.getAccessToken();
|
|
154
185
|
if (!accessToken) {
|
|
155
186
|
throw new Error("No access token available");
|
|
156
187
|
}
|
|
157
|
-
const response = await fetch(
|
|
158
|
-
headers: { Authorization: `Bearer ${accessToken}` }
|
|
188
|
+
const response = await fetch(this.apiUrl("/me"), {
|
|
189
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
190
|
+
credentials: "include"
|
|
159
191
|
});
|
|
160
192
|
if (!response.ok) {
|
|
161
193
|
if (response.status === 401) {
|
|
162
194
|
this.storage.removePersistent("access_token");
|
|
195
|
+
this.storage.removePersistent("token_expires_at");
|
|
163
196
|
}
|
|
164
197
|
throw new Error("Failed to fetch user info");
|
|
165
198
|
}
|
|
166
|
-
|
|
199
|
+
const data = await response.json();
|
|
200
|
+
if (!data.sub || !data.email) {
|
|
201
|
+
throw new Error("Invalid user info response");
|
|
202
|
+
}
|
|
203
|
+
return data;
|
|
167
204
|
}
|
|
168
|
-
logout() {
|
|
205
|
+
async logout() {
|
|
206
|
+
if (typeof window === "undefined") {
|
|
207
|
+
throw new Error("logout() requires a browser environment");
|
|
208
|
+
}
|
|
169
209
|
const token = this.getAccessToken();
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
210
|
+
try {
|
|
211
|
+
await fetch(this.apiUrl("/logout"), {
|
|
212
|
+
method: "POST",
|
|
213
|
+
headers: { "Content-Type": "application/json" },
|
|
214
|
+
credentials: "include",
|
|
215
|
+
body: JSON.stringify({
|
|
216
|
+
...token && { token },
|
|
217
|
+
...this.config.clientId && { client_id: this.config.clientId },
|
|
218
|
+
...this.config.postLogoutRedirectUri && { post_logout_redirect_uri: this.config.postLogoutRedirectUri }
|
|
219
|
+
})
|
|
220
|
+
});
|
|
221
|
+
} finally {
|
|
222
|
+
this.storage.clearAll();
|
|
223
|
+
}
|
|
174
224
|
if (this.config.postLogoutRedirectUri) {
|
|
175
|
-
|
|
225
|
+
window.location.href = this.config.postLogoutRedirectUri;
|
|
176
226
|
}
|
|
177
|
-
window.location.href = `${this.config.accountsUrl}/api/proxy/logout?${params.toString()}`;
|
|
178
227
|
}
|
|
179
228
|
isAuthenticated() {
|
|
180
|
-
|
|
229
|
+
const token = this.getAccessToken();
|
|
230
|
+
if (!token) return false;
|
|
231
|
+
const expiresAt = this.storage.getPersistent("token_expires_at");
|
|
232
|
+
if (expiresAt && Date.now() > Number(expiresAt)) {
|
|
233
|
+
this.storage.removePersistent("access_token");
|
|
234
|
+
this.storage.removePersistent("token_expires_at");
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
return true;
|
|
181
238
|
}
|
|
182
239
|
getAccessToken() {
|
|
183
240
|
return this.storage.getPersistent("access_token");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pkce.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["function generateRandomBytes(length: number): Uint8Array {\n const array = new Uint8Array(length);\n crypto.getRandomValues(array);\n return array;\n}\n\nfunction base64UrlEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function generateCodeVerifier(): string {\n const bytes = generateRandomBytes(32);\n return base64UrlEncode(bytes);\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(digest);\n}\n\nexport function generateState(): string {\n const bytes = generateRandomBytes(16);\n return base64UrlEncode(bytes);\n}\n","export class AuthStorage {\n private prefix: string;\n\n constructor(prefix = 'yaotoshi_auth') {\n this.prefix = prefix;\n }\n\n private key(name: string): string {\n return `${this.prefix}_${name}`;\n }\n\n get(name: string): string | null {\n try {\n return sessionStorage.getItem(this.key(name));\n } catch {\n return null;\n }\n }\n\n set(name: string, value: string): void {\n try {\n sessionStorage.setItem(this.key(name), value);\n } catch {\n // Storage unavailable\n }\n }\n\n remove(name: string): void {\n try {\n sessionStorage.removeItem(this.key(name));\n } catch {\n // Storage unavailable\n }\n }\n\n getPersistent(name: string): string | null {\n try {\n return localStorage.getItem(this.key(name));\n } catch {\n return null;\n }\n }\n\n setPersistent(name: string, value: string): void {\n try {\n localStorage.setItem(this.key(name), value);\n } catch {\n // Storage unavailable\n }\n }\n\n removePersistent(name: string): void {\n try {\n localStorage.removeItem(this.key(name));\n } catch {\n // Storage unavailable\n }\n }\n\n clearAll(): void {\n this.remove('code_verifier');\n this.remove('state');\n this.removePersistent('access_token');\n }\n}\n","import { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { AuthStorage } from './storage';\nimport type { YaotoshiAuthConfig, TokenResponse, UserInfo, AuthResult } from './types';\n\nexport class YaotoshiAuth {\n private config: Required<Pick<YaotoshiAuthConfig, 'clientId' | 'redirectUri' | 'accountsUrl'>> &\n YaotoshiAuthConfig;\n private storage: AuthStorage;\n\n constructor(config: YaotoshiAuthConfig) {\n this.config = {\n scopes: ['openid', 'email'],\n postLogoutRedirectUri: undefined,\n storagePrefix: 'yaotoshi_auth',\n ...config,\n };\n this.storage = new AuthStorage(this.config.storagePrefix);\n }\n\n async login(): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n this.storage.set('code_verifier', codeVerifier);\n this.storage.set('state', state);\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n scope: (this.config.scopes ?? ['openid', 'email']).join(' '),\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n });\n\n window.location.href = `${this.config.accountsUrl}/authorize?${params.toString()}`;\n }\n\n async handleCallback(): Promise<AuthResult> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(`Authorization error: ${error}`);\n }\n\n if (!code || !state) {\n throw new Error('Missing code or state in callback');\n }\n\n const savedState = this.storage.get('state');\n if (state !== savedState) {\n throw new Error('State mismatch — possible CSRF attack');\n }\n\n const codeVerifier = this.storage.get('code_verifier');\n if (!codeVerifier) {\n throw new Error('Missing code verifier — login flow may have been interrupted');\n }\n\n // Exchange code for token\n const tokenResponse = await fetch(`${this.config.accountsUrl}/api/proxy/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'authorization_code',\n code,\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const err = await tokenResponse.json().catch(() => ({}));\n throw new Error(err.message || 'Token exchange failed');\n }\n\n const tokenData: TokenResponse = await tokenResponse.json();\n\n // Clean up PKCE state\n this.storage.remove('code_verifier');\n this.storage.remove('state');\n\n // Persist the access token\n this.storage.setPersistent('access_token', tokenData.access_token);\n\n // Fetch user info\n const user = await this.getUser(tokenData.access_token);\n\n return { accessToken: tokenData.access_token, user };\n }\n\n async getUser(token?: string): Promise<UserInfo> {\n const accessToken = token || this.getAccessToken();\n if (!accessToken) {\n throw new Error('No access token available');\n }\n\n const response = await fetch(`${this.config.accountsUrl}/api/proxy/me`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n this.storage.removePersistent('access_token');\n }\n throw new Error('Failed to fetch user info');\n }\n\n return response.json();\n }\n\n logout(): void {\n const token = this.getAccessToken();\n this.storage.clearAll();\n\n const params = new URLSearchParams();\n if (token) params.set('token', token);\n if (this.config.clientId) params.set('client_id', this.config.clientId);\n if (this.config.postLogoutRedirectUri) {\n params.set('post_logout_redirect_uri', this.config.postLogoutRedirectUri);\n }\n\n window.location.href = `${this.config.accountsUrl}/api/proxy/logout?${params.toString()}`;\n }\n\n isAuthenticated(): boolean {\n return !!this.getAccessToken();\n }\n\n getAccessToken(): string | null {\n return this.storage.getPersistent('access_token');\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/pkce.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["function generateRandomBytes(length: number): Uint8Array {\n const array = new Uint8Array(length);\n crypto.getRandomValues(array);\n return array;\n}\n\nfunction base64UrlEncode(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function generateCodeVerifier(): string {\n const bytes = generateRandomBytes(32);\n return base64UrlEncode(bytes);\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(digest);\n}\n\nexport function generateState(): string {\n const bytes = generateRandomBytes(16);\n return base64UrlEncode(bytes);\n}\n","export class AuthStorage {\n private prefix: string;\n\n constructor(prefix = 'yaotoshi_auth') {\n this.prefix = prefix;\n }\n\n private key(name: string): string {\n return `${this.prefix}_${name}`;\n }\n\n get(name: string): string | null {\n try {\n return sessionStorage.getItem(this.key(name));\n } catch {\n return null;\n }\n }\n\n set(name: string, value: string): void {\n try {\n sessionStorage.setItem(this.key(name), value);\n } catch {\n // Storage unavailable\n }\n }\n\n remove(name: string): void {\n try {\n sessionStorage.removeItem(this.key(name));\n } catch {\n // Storage unavailable\n }\n }\n\n getPersistent(name: string): string | null {\n try {\n return localStorage.getItem(this.key(name));\n } catch {\n return null;\n }\n }\n\n setPersistent(name: string, value: string): void {\n try {\n localStorage.setItem(this.key(name), value);\n } catch {\n // Storage unavailable\n }\n }\n\n removePersistent(name: string): void {\n try {\n localStorage.removeItem(this.key(name));\n } catch {\n // Storage unavailable\n }\n }\n\n clearAll(): void {\n try {\n const prefix = this.prefix + '_';\n for (const store of [sessionStorage, localStorage]) {\n const keys = Object.keys(store).filter(k => k.startsWith(prefix));\n keys.forEach(k => store.removeItem(k));\n }\n } catch {\n // Storage unavailable\n }\n }\n}\n","import { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { AuthStorage } from './storage';\nimport type { YaotoshiAuthConfig, TokenResponse, UserInfo, AuthResult } from './types';\n\nexport class YaotoshiAuth {\n private config: Required<Pick<YaotoshiAuthConfig, 'clientId' | 'redirectUri' | 'accountsUrl'>> &\n YaotoshiAuthConfig;\n private storage: AuthStorage;\n private processing = false;\n\n constructor(config: YaotoshiAuthConfig) {\n this.config = {\n scopes: ['openid', 'email'],\n postLogoutRedirectUri: undefined,\n storagePrefix: 'yaotoshi_auth',\n apiPathPrefix: '/api/proxy',\n ...config,\n };\n this.storage = new AuthStorage(this.config.storagePrefix);\n }\n\n private apiUrl(path: string): string {\n const prefix = this.config.apiPathPrefix ?? '/api/proxy';\n return `${this.config.accountsUrl}${prefix}${path}`;\n }\n\n async login(): Promise<void> {\n if (typeof window === 'undefined') {\n throw new Error('login() requires a browser environment');\n }\n\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n this.storage.set('code_verifier', codeVerifier);\n this.storage.set('state', state);\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n scope: this.config.scopes!.join(' '),\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n });\n\n window.location.href = `${this.config.accountsUrl}/authorize?${params.toString()}`;\n }\n\n async handleCallback(): Promise<AuthResult> {\n if (this.processing) {\n throw new Error('Callback is already being processed');\n }\n this.processing = true;\n\n try {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n const errorDescription = params.get('error_description');\n throw new Error(`Authorization error: ${error}${errorDescription ? ` — ${errorDescription}` : ''}`);\n }\n\n if (!code || !state) {\n throw new Error('Missing code or state in callback');\n }\n\n const savedState = this.storage.get('state');\n if (state !== savedState) {\n throw new Error('State mismatch — possible CSRF attack');\n }\n\n const codeVerifier = this.storage.get('code_verifier');\n if (!codeVerifier) {\n throw new Error('Missing code verifier — login flow may have been interrupted');\n }\n\n const tokenResponse = await fetch(this.apiUrl('/token'), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n grant_type: 'authorization_code',\n code,\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const err = await tokenResponse.json().catch(() => ({}));\n const message = Array.isArray(err.message) ? err.message.join(', ') : (err.message || 'Token exchange failed');\n throw new Error(message);\n }\n\n const tokenData: TokenResponse = await tokenResponse.json();\n\n // Clean up PKCE state\n this.storage.remove('code_verifier');\n this.storage.remove('state');\n\n // Persist the access token\n this.storage.setPersistent('access_token', tokenData.access_token);\n this.storage.setPersistent('token_expires_at', String(Date.now() + tokenData.expires_in * 1000));\n\n // Fetch user info\n const user = await this.getUser(tokenData.access_token);\n\n return {\n accessToken: tokenData.access_token,\n scope: tokenData.scope,\n expiresIn: tokenData.expires_in,\n user,\n };\n } finally {\n this.processing = false;\n }\n }\n\n async getUser(token?: string): Promise<UserInfo> {\n const accessToken = token || this.getAccessToken();\n if (!accessToken) {\n throw new Error('No access token available');\n }\n\n const response = await fetch(this.apiUrl('/me'), {\n headers: { Authorization: `Bearer ${accessToken}` },\n credentials: 'include',\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n this.storage.removePersistent('access_token');\n this.storage.removePersistent('token_expires_at');\n }\n throw new Error('Failed to fetch user info');\n }\n\n const data = await response.json();\n if (!data.sub || !data.email) {\n throw new Error('Invalid user info response');\n }\n\n return data;\n }\n\n async logout(): Promise<void> {\n if (typeof window === 'undefined') {\n throw new Error('logout() requires a browser environment');\n }\n\n const token = this.getAccessToken();\n\n try {\n await fetch(this.apiUrl('/logout'), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n ...(token && { token }),\n ...(this.config.clientId && { client_id: this.config.clientId }),\n ...(this.config.postLogoutRedirectUri && { post_logout_redirect_uri: this.config.postLogoutRedirectUri }),\n }),\n });\n } finally {\n // Clear local state regardless of server response\n this.storage.clearAll();\n }\n\n // Redirect after successful logout\n if (this.config.postLogoutRedirectUri) {\n window.location.href = this.config.postLogoutRedirectUri;\n }\n }\n\n isAuthenticated(): boolean {\n const token = this.getAccessToken();\n if (!token) return false;\n\n const expiresAt = this.storage.getPersistent('token_expires_at');\n if (expiresAt && Date.now() > Number(expiresAt)) {\n this.storage.removePersistent('access_token');\n this.storage.removePersistent('token_expires_at');\n return false;\n }\n\n return true;\n }\n\n getAccessToken(): string | null {\n return this.storage.getPersistent('access_token');\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaotoshi/auth-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Authentication SDK for Yaotoshi ecosystem apps",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"browser": "./dist/index.js",
|
|
8
|
+
"sideEffects": false,
|
|
7
9
|
"main": "./dist/index.cjs",
|
|
8
10
|
"module": "./dist/index.js",
|
|
9
11
|
"types": "./dist/index.d.ts",
|