@socialproof/mysocial-auth 0.3.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @socialproof/mysocial-auth
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - fixed
8
+
3
9
  ## 0.3.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -68,7 +68,7 @@ await auth.signOut();
68
68
 
69
69
  ### auth.signIn(options?)
70
70
 
71
- - `provider?`: 'google' | 'apple' | 'facebook' | 'twitch'
71
+ - `provider?`: 'google' | 'apple' | 'facebook' | 'twitch' | 'none' (default 'none' = home screen)
72
72
  - `mode?`: 'popup' | 'redirect' (default: 'popup')
73
73
  - Returns: `Promise<Session>` (popup) or never (redirect; page navigates)
74
74
 
@@ -86,8 +86,8 @@ await auth.signOut();
86
86
 
87
87
  ### auth.handleRedirectCallback(url?)
88
88
 
89
- - For redirect mode. Parses URL for code/state/nonce, exchanges for tokens. Omit `url` to use
90
- `window.location.href`.
89
+ - For redirect mode. Parses URL for code/state/nonce, builds session from payload (no exchange).
90
+ Omit `url` to use `window.location.href`.
91
91
 
92
92
  ### auth.onAuthStateChange(callback)
93
93
 
@@ -95,9 +95,10 @@ await auth.signOut();
95
95
 
96
96
  ## Hosted UI Contract (auth.mysocial.network)
97
97
 
98
- The SDK passes `return_origin` and `code_challenge_method: S256` in the login URL params. The auth
99
- frontend generates PKCE (code_challenge/code_verifier) server-side for Google/Apple; the package
100
- does not send them. `return_origin` must match the dApp's origin
98
+ The SDK passes `return_origin`, `code_challenge_method: S256`, and `provider` in the login URL
99
+ params. `provider` is never empty: `'google'`, `'apple'`, `'facebook'`, `'twitch'`, or `'none'`
100
+ (home screen). The auth frontend generates PKCE server-side; the package does not send
101
+ code_challenge. `return_origin` must match the dApp's origin
101
102
  (`window.location.origin`) so the auth server can post the auth result to the correct target.
102
103
  The auth server uses `return_origin` as the `postMessage` targetOrigin when sending the result.
103
104
 
@@ -107,7 +108,7 @@ The popup page must call:
107
108
  window.opener.postMessage(payload, validatedTargetOrigin); // targetOrigin, NOT "*"
108
109
  ```
109
110
 
110
- **Success payload:**
111
+ **Success payload (MYSOCIAL_AUTH_RESULT is the final result; no exchange needed):**
111
112
 
112
113
  ```json
113
114
  {
@@ -116,10 +117,17 @@ window.opener.postMessage(payload, validatedTargetOrigin); // targetOrigin, NOT
116
117
  "state": "...",
117
118
  "nonce": "...",
118
119
  "clientId": "...",
119
- "requestId": "..."
120
+ "requestId": "...",
121
+ "salt": "...",
122
+ "user": {},
123
+ "access_token": "...",
124
+ "refresh_token": "...",
125
+ "expires_at": 0
120
126
  }
121
127
  ```
122
128
 
129
+ `code` is the token (id_token or access_token). Optional: `salt`, `user`, `access_token`, `refresh_token`, `expires_at`.
130
+
123
131
  **Error payload:**
124
132
 
125
133
  ```json
@@ -135,16 +143,13 @@ window.opener.postMessage(payload, validatedTargetOrigin); // targetOrigin, NOT
135
143
  `return_origin` must never be trusted from the query param. The backend must validate it against the
136
144
  allowlist for `client_id`.
137
145
 
138
- **Important:** The package calls your app's backend (`apiBaseUrl`), never the auth frontend. The auth
139
- frontend's `/api/auth/callback` is internal and is not used by this SDK.
146
+ **Important:** The auth frontend and salt service handle the OAuth exchange internally. The package
147
+ does not call `/auth/exchange`. `MYSOCIAL_AUTH_RESULT` is the final result: `code` is the token.
140
148
 
141
149
  ## Backend Contract
142
150
 
143
151
  - `POST ${apiBaseUrl}/auth/request` (optional): `{ client_id, redirect_uri, return_origin }` →
144
152
  `{ request_id }`
145
- - `POST ${apiBaseUrl}/auth/exchange`:
146
- `{ code, redirect_uri, state?, nonce?, request_id? }` (your app's backend; code_verifier not sent—auth frontend handles OAuth/salt internally) →
147
- `{ access_token, refresh_token?, expires_in, user }`
148
153
  - `POST ${apiBaseUrl}/auth/refresh`: `{ refresh_token }` →
149
154
  `{ access_token, refresh_token?, expires_in, user }`
150
155
  - `POST ${apiBaseUrl}/auth/logout`
package/dist/auth.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { REDIRECT_STATE_PREFIX, SESSION_KEY, createStorage, redirectStorage } from "./storage.mjs";
2
2
  import { generateNonce, generateState } from "./pkce.mjs";
3
- import { exchangeCode, fetchRequestId, logout, refreshTokens } from "./exchange.mjs";
3
+ import { fetchRequestId, logout, refreshTokens } from "./exchange.mjs";
4
4
  import { openAuthPopup } from "./popup.mjs";
5
5
  import { mitt } from "@socialproof/utils";
6
6
 
@@ -88,7 +88,7 @@ function createAuth(config) {
88
88
  nonce,
89
89
  return_origin: returnOrigin,
90
90
  mode: "redirect",
91
- provider: provider ?? "",
91
+ provider: provider ?? "none",
92
92
  code_challenge_method: "S256"
93
93
  });
94
94
  if (requestId) params.set("request_id", requestId);
@@ -122,6 +122,11 @@ function createAuth(config) {
122
122
  const state = parsed.searchParams.get("state");
123
123
  const nonce = parsed.searchParams.get("nonce");
124
124
  const requestId = parsed.searchParams.get("request_id") ?? void 0;
125
+ const salt = parsed.searchParams.get("salt") ?? void 0;
126
+ const accessToken = parsed.searchParams.get("access_token") ?? void 0;
127
+ const refreshToken = parsed.searchParams.get("refresh_token") ?? void 0;
128
+ const expiresAtParam = parsed.searchParams.get("expires_at");
129
+ const userParam = parsed.searchParams.get("user");
125
130
  if (!code || !state || !nonce) throw new Error("Missing code, state, or nonce in callback URL");
126
131
  const key = `${REDIRECT_STATE_PREFIX}${state}`;
127
132
  const raw = redirectStorage.get(key);
@@ -136,13 +141,17 @@ function createAuth(config) {
136
141
  throw new Error("Request ID mismatch");
137
142
  }
138
143
  redirectStorage.remove(key);
139
- const session = await exchangeCode(config.apiBaseUrl, {
140
- code,
141
- redirect_uri: config.redirectUri,
142
- state,
143
- nonce,
144
- request_id: requestId ?? redirectState.requestId
145
- });
144
+ let user = {};
145
+ if (userParam) try {
146
+ user = JSON.parse(decodeURIComponent(userParam));
147
+ } catch {}
148
+ const session = {
149
+ access_token: accessToken ?? code,
150
+ refresh_token: refreshToken,
151
+ user,
152
+ expires_at: expiresAtParam ? Number(expiresAtParam) : Date.now() + 36e5,
153
+ ...salt && { salt }
154
+ };
146
155
  await saveSession(session);
147
156
  emitter.emit("change", session);
148
157
  return session;
package/dist/auth.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.mjs","names":[],"sources":["../src/auth.ts"],"sourcesContent":["// Copyright (c) The Social Proof Foundation, LLC.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { mitt, type Emitter } from '@socialproof/utils';\nimport type {\n\tMySocialAuthConfig,\n\tSignInOptions,\n\tSession,\n\tAuthStateChangeCallback,\n} from './types.js';\nimport { createStorage, redirectStorage, SESSION_KEY, REDIRECT_STATE_PREFIX } from './storage.js';\nimport { openAuthPopup } from './popup.js';\nimport { exchangeCode, refreshTokens, logout, fetchRequestId } from './exchange.js';\nimport { generateState, generateNonce } from './pkce.js';\n\ntype AuthEvents = { change: Session | null };\n\nexport interface MySocialAuth {\n\tsignIn(options?: SignInOptions): Promise<Session>;\n\tsignOut(): Promise<void>;\n\tgetSession(): Promise<Session | null>;\n\trefresh(): Promise<Session | null>;\n\thandleRedirectCallback(url?: string): Promise<Session>;\n\tonAuthStateChange(callback: AuthStateChangeCallback): () => void;\n}\n\nfunction getReturnOrigin(redirectUri: string): string {\n\ttry {\n\t\tconst u = new URL(redirectUri);\n\t\treturn `${u.protocol}//${u.host}`;\n\t} catch {\n\t\treturn '';\n\t}\n}\n\nexport function createAuth(config: MySocialAuthConfig): MySocialAuth {\n\tconst storage = createStorage(config.storage);\n\tconst emitter: Emitter<AuthEvents> = mitt<AuthEvents>();\n\n\tlet cachedSession: Session | null = null;\n\n\tasync function loadSession(): Promise<Session | null> {\n\t\tconst raw = storage.get(SESSION_KEY);\n\t\tif (!raw) return null;\n\t\ttry {\n\t\t\tconst session = JSON.parse(raw) as Session;\n\t\t\tif (session.expires_at && session.expires_at > Date.now()) {\n\t\t\t\treturn session;\n\t\t\t}\n\t\t\tif (session.refresh_token) {\n\t\t\t\tconst refreshed = await refreshTokens(config.apiBaseUrl, session.refresh_token);\n\t\t\t\tawait saveSession(refreshed);\n\t\t\t\temitter.emit('change', refreshed);\n\t\t\t\treturn refreshed;\n\t\t\t}\n\t\t\tstorage.remove(SESSION_KEY);\n\t\t\tcachedSession = null;\n\t\t\temitter.emit('change', null);\n\t\t\treturn null;\n\t\t} catch {\n\t\t\tstorage.remove(SESSION_KEY);\n\t\t\tcachedSession = null;\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync function saveSession(session: Session): Promise<void> {\n\t\tcachedSession = session;\n\t\tstorage.set(SESSION_KEY, JSON.stringify(session));\n\t}\n\n\tasync function clearSession(): Promise<void> {\n\t\tcachedSession = null;\n\t\tstorage.remove(SESSION_KEY);\n\t}\n\n\treturn {\n\t\tasync signIn(options: SignInOptions = {}) {\n\t\t\tconst mode = options.mode ?? 'popup';\n\t\t\tconst provider = options.provider;\n\n\t\t\tif (mode === 'popup') {\n\t\t\t\tconst session = await openAuthPopup({\n\t\t\t\t\tapiBaseUrl: config.apiBaseUrl,\n\t\t\t\t\tauthOrigin: config.authOrigin,\n\t\t\t\t\tclientId: config.clientId,\n\t\t\t\t\tredirectUri: config.redirectUri,\n\t\t\t\t\tprovider,\n\t\t\t\t\ttimeout: config.popupTimeout ?? 120_000,\n\t\t\t\t\tuseRequestId: config.useRequestId ?? false,\n\t\t\t\t});\n\t\t\t\tawait saveSession(session);\n\t\t\t\temitter.emit('change', session);\n\t\t\t\treturn session;\n\t\t\t}\n\n\t\t\t// Redirect mode\n\t\t\tconst state = generateState();\n\t\t\tconst nonce = generateNonce();\n\t\t\tconst returnOrigin = getReturnOrigin(config.redirectUri);\n\n\t\t\tlet requestId: string | undefined;\n\t\t\tif (config.useRequestId) {\n\t\t\t\trequestId = await fetchRequestId(config.apiBaseUrl, {\n\t\t\t\t\tclient_id: config.clientId,\n\t\t\t\t\tredirect_uri: config.redirectUri,\n\t\t\t\t\treturn_origin: returnOrigin,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst redirectState = {\n\t\t\t\tstate,\n\t\t\t\tnonce,\n\t\t\t\trequestId,\n\t\t\t};\n\t\t\tconst key = `${REDIRECT_STATE_PREFIX}${state}`;\n\t\t\tredirectStorage.set(key, JSON.stringify(redirectState));\n\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\tclient_id: config.clientId,\n\t\t\t\tredirect_uri: config.redirectUri,\n\t\t\t\tstate,\n\t\t\t\tnonce,\n\t\t\t\treturn_origin: returnOrigin,\n\t\t\t\tmode: 'redirect',\n\t\t\t\tprovider: provider ?? '',\n\t\t\t\tcode_challenge_method: 'S256',\n\t\t\t});\n\t\t\tif (requestId) params.set('request_id', requestId);\n\n\t\t\tconst loginUrl = `${config.authOrigin.replace(/\\/$/, '')}/login?${params.toString()}`;\n\t\t\twindow.location.href = loginUrl;\n\n\t\t\treturn new Promise<Session>(() => {}); // Never resolves; page navigates away\n\t\t},\n\n\t\tasync signOut() {\n\t\t\ttry {\n\t\t\t\tawait logout(config.apiBaseUrl);\n\t\t\t} catch {\n\t\t\t\t// Best-effort; clear local state regardless\n\t\t\t}\n\t\t\tawait clearSession();\n\t\t\temitter.emit('change', null);\n\t\t},\n\n\t\tasync getSession() {\n\t\t\tif (cachedSession && cachedSession.expires_at > Date.now()) {\n\t\t\t\treturn cachedSession;\n\t\t\t}\n\t\t\treturn loadSession();\n\t\t},\n\n\t\tasync refresh() {\n\t\t\tconst session = await loadSession();\n\t\t\tif (!session?.refresh_token) return null;\n\t\t\tconst refreshed = await refreshTokens(config.apiBaseUrl, session.refresh_token);\n\t\t\tawait saveSession(refreshed);\n\t\t\temitter.emit('change', refreshed);\n\t\t\treturn refreshed;\n\t\t},\n\n\t\tasync handleRedirectCallback(url?: string) {\n\t\t\tconst targetUrl = url ?? (typeof window !== 'undefined' ? window.location.href : '');\n\t\t\tconst parsed = new URL(targetUrl);\n\t\t\tconst code = parsed.searchParams.get('code');\n\t\t\tconst state = parsed.searchParams.get('state');\n\t\t\tconst nonce = parsed.searchParams.get('nonce');\n\t\t\tconst requestId = parsed.searchParams.get('request_id') ?? undefined;\n\n\t\t\tif (!code || !state || !nonce) {\n\t\t\t\tthrow new Error('Missing code, state, or nonce in callback URL');\n\t\t\t}\n\n\t\t\tconst key = `${REDIRECT_STATE_PREFIX}${state}`;\n\t\t\tconst raw = redirectStorage.get(key);\n\t\t\tif (!raw) {\n\t\t\t\tthrow new Error('No matching redirect state found. Session may have expired.');\n\t\t\t}\n\n\t\t\tconst redirectState = JSON.parse(raw) as {\n\t\t\t\tstate: string;\n\t\t\t\tnonce: string;\n\t\t\t\trequestId?: string;\n\t\t\t};\n\n\t\t\tif (redirectState.state !== state || redirectState.nonce !== nonce) {\n\t\t\t\tredirectStorage.remove(key);\n\t\t\t\tthrow new Error('State or nonce mismatch');\n\t\t\t}\n\t\t\tif (requestId && redirectState.requestId !== requestId) {\n\t\t\t\tredirectStorage.remove(key);\n\t\t\t\tthrow new Error('Request ID mismatch');\n\t\t\t}\n\n\t\t\tredirectStorage.remove(key);\n\n\t\t\tconst session = await exchangeCode(config.apiBaseUrl, {\n\t\t\t\tcode,\n\t\t\t\tredirect_uri: config.redirectUri,\n\t\t\t\tstate,\n\t\t\t\tnonce,\n\t\t\t\trequest_id: requestId ?? redirectState.requestId,\n\t\t\t});\n\n\t\t\tawait saveSession(session);\n\t\t\temitter.emit('change', session);\n\t\t\treturn session;\n\t\t},\n\n\t\tonAuthStateChange(callback: AuthStateChangeCallback) {\n\t\t\temitter.on('change', callback);\n\t\t\treturn () => emitter.off('change', callback);\n\t\t},\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"auth.mjs","names":[],"sources":["../src/auth.ts"],"sourcesContent":["// Copyright (c) The Social Proof Foundation, LLC.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { mitt, type Emitter } from '@socialproof/utils';\nimport type {\n\tMySocialAuthConfig,\n\tSignInOptions,\n\tSession,\n\tAuthStateChangeCallback,\n} from './types.js';\nimport { createStorage, redirectStorage, SESSION_KEY, REDIRECT_STATE_PREFIX } from './storage.js';\nimport { openAuthPopup } from './popup.js';\nimport { refreshTokens, logout, fetchRequestId } from './exchange.js';\nimport { generateState, generateNonce } from './pkce.js';\n\ntype AuthEvents = { change: Session | null };\n\nexport interface MySocialAuth {\n\tsignIn(options?: SignInOptions): Promise<Session>;\n\tsignOut(): Promise<void>;\n\tgetSession(): Promise<Session | null>;\n\trefresh(): Promise<Session | null>;\n\thandleRedirectCallback(url?: string): Promise<Session>;\n\tonAuthStateChange(callback: AuthStateChangeCallback): () => void;\n}\n\nfunction getReturnOrigin(redirectUri: string): string {\n\ttry {\n\t\tconst u = new URL(redirectUri);\n\t\treturn `${u.protocol}//${u.host}`;\n\t} catch {\n\t\treturn '';\n\t}\n}\n\nexport function createAuth(config: MySocialAuthConfig): MySocialAuth {\n\tconst storage = createStorage(config.storage);\n\tconst emitter: Emitter<AuthEvents> = mitt<AuthEvents>();\n\n\tlet cachedSession: Session | null = null;\n\n\tasync function loadSession(): Promise<Session | null> {\n\t\tconst raw = storage.get(SESSION_KEY);\n\t\tif (!raw) return null;\n\t\ttry {\n\t\t\tconst session = JSON.parse(raw) as Session;\n\t\t\tif (session.expires_at && session.expires_at > Date.now()) {\n\t\t\t\treturn session;\n\t\t\t}\n\t\t\tif (session.refresh_token) {\n\t\t\t\tconst refreshed = await refreshTokens(config.apiBaseUrl, session.refresh_token);\n\t\t\t\tawait saveSession(refreshed);\n\t\t\t\temitter.emit('change', refreshed);\n\t\t\t\treturn refreshed;\n\t\t\t}\n\t\t\tstorage.remove(SESSION_KEY);\n\t\t\tcachedSession = null;\n\t\t\temitter.emit('change', null);\n\t\t\treturn null;\n\t\t} catch {\n\t\t\tstorage.remove(SESSION_KEY);\n\t\t\tcachedSession = null;\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync function saveSession(session: Session): Promise<void> {\n\t\tcachedSession = session;\n\t\tstorage.set(SESSION_KEY, JSON.stringify(session));\n\t}\n\n\tasync function clearSession(): Promise<void> {\n\t\tcachedSession = null;\n\t\tstorage.remove(SESSION_KEY);\n\t}\n\n\treturn {\n\t\tasync signIn(options: SignInOptions = {}) {\n\t\t\tconst mode = options.mode ?? 'popup';\n\t\t\tconst provider = options.provider;\n\n\t\t\tif (mode === 'popup') {\n\t\t\t\tconst session = await openAuthPopup({\n\t\t\t\t\tapiBaseUrl: config.apiBaseUrl,\n\t\t\t\t\tauthOrigin: config.authOrigin,\n\t\t\t\t\tclientId: config.clientId,\n\t\t\t\t\tredirectUri: config.redirectUri,\n\t\t\t\t\tprovider,\n\t\t\t\t\ttimeout: config.popupTimeout ?? 120_000,\n\t\t\t\t\tuseRequestId: config.useRequestId ?? false,\n\t\t\t\t});\n\t\t\t\tawait saveSession(session);\n\t\t\t\temitter.emit('change', session);\n\t\t\t\treturn session;\n\t\t\t}\n\n\t\t\t// Redirect mode\n\t\t\tconst state = generateState();\n\t\t\tconst nonce = generateNonce();\n\t\t\tconst returnOrigin = getReturnOrigin(config.redirectUri);\n\n\t\t\tlet requestId: string | undefined;\n\t\t\tif (config.useRequestId) {\n\t\t\t\trequestId = await fetchRequestId(config.apiBaseUrl, {\n\t\t\t\t\tclient_id: config.clientId,\n\t\t\t\t\tredirect_uri: config.redirectUri,\n\t\t\t\t\treturn_origin: returnOrigin,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst redirectState = {\n\t\t\t\tstate,\n\t\t\t\tnonce,\n\t\t\t\trequestId,\n\t\t\t};\n\t\t\tconst key = `${REDIRECT_STATE_PREFIX}${state}`;\n\t\t\tredirectStorage.set(key, JSON.stringify(redirectState));\n\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\tclient_id: config.clientId,\n\t\t\t\tredirect_uri: config.redirectUri,\n\t\t\t\tstate,\n\t\t\t\tnonce,\n\t\t\t\treturn_origin: returnOrigin,\n\t\t\t\tmode: 'redirect',\n\t\t\t\tprovider: provider ?? 'none',\n\t\t\t\tcode_challenge_method: 'S256',\n\t\t\t});\n\t\t\tif (requestId) params.set('request_id', requestId);\n\n\t\t\tconst loginUrl = `${config.authOrigin.replace(/\\/$/, '')}/login?${params.toString()}`;\n\t\t\twindow.location.href = loginUrl;\n\n\t\t\treturn new Promise<Session>(() => {}); // Never resolves; page navigates away\n\t\t},\n\n\t\tasync signOut() {\n\t\t\ttry {\n\t\t\t\tawait logout(config.apiBaseUrl);\n\t\t\t} catch {\n\t\t\t\t// Best-effort; clear local state regardless\n\t\t\t}\n\t\t\tawait clearSession();\n\t\t\temitter.emit('change', null);\n\t\t},\n\n\t\tasync getSession() {\n\t\t\tif (cachedSession && cachedSession.expires_at > Date.now()) {\n\t\t\t\treturn cachedSession;\n\t\t\t}\n\t\t\treturn loadSession();\n\t\t},\n\n\t\tasync refresh() {\n\t\t\tconst session = await loadSession();\n\t\t\tif (!session?.refresh_token) return null;\n\t\t\tconst refreshed = await refreshTokens(config.apiBaseUrl, session.refresh_token);\n\t\t\tawait saveSession(refreshed);\n\t\t\temitter.emit('change', refreshed);\n\t\t\treturn refreshed;\n\t\t},\n\n\t\tasync handleRedirectCallback(url?: string) {\n\t\t\tconst targetUrl = url ?? (typeof window !== 'undefined' ? window.location.href : '');\n\t\t\tconst parsed = new URL(targetUrl);\n\t\t\tconst code = parsed.searchParams.get('code');\n\t\t\tconst state = parsed.searchParams.get('state');\n\t\t\tconst nonce = parsed.searchParams.get('nonce');\n\t\t\tconst requestId = parsed.searchParams.get('request_id') ?? undefined;\n\t\t\tconst salt = parsed.searchParams.get('salt') ?? undefined;\n\t\t\tconst accessToken = parsed.searchParams.get('access_token') ?? undefined;\n\t\t\tconst refreshToken = parsed.searchParams.get('refresh_token') ?? undefined;\n\t\t\tconst expiresAtParam = parsed.searchParams.get('expires_at');\n\t\t\tconst userParam = parsed.searchParams.get('user');\n\n\t\t\tif (!code || !state || !nonce) {\n\t\t\t\tthrow new Error('Missing code, state, or nonce in callback URL');\n\t\t\t}\n\n\t\t\tconst key = `${REDIRECT_STATE_PREFIX}${state}`;\n\t\t\tconst raw = redirectStorage.get(key);\n\t\t\tif (!raw) {\n\t\t\t\tthrow new Error('No matching redirect state found. Session may have expired.');\n\t\t\t}\n\n\t\t\tconst redirectState = JSON.parse(raw) as {\n\t\t\t\tstate: string;\n\t\t\t\tnonce: string;\n\t\t\t\trequestId?: string;\n\t\t\t};\n\n\t\t\tif (redirectState.state !== state || redirectState.nonce !== nonce) {\n\t\t\t\tredirectStorage.remove(key);\n\t\t\t\tthrow new Error('State or nonce mismatch');\n\t\t\t}\n\t\t\tif (requestId && redirectState.requestId !== requestId) {\n\t\t\t\tredirectStorage.remove(key);\n\t\t\t\tthrow new Error('Request ID mismatch');\n\t\t\t}\n\n\t\t\tredirectStorage.remove(key);\n\n\t\t\tlet user: Session['user'] = {};\n\t\t\tif (userParam) {\n\t\t\t\ttry {\n\t\t\t\t\tuser = JSON.parse(decodeURIComponent(userParam)) as Session['user'];\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore invalid user param\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst session: Session = {\n\t\t\t\taccess_token: accessToken ?? code,\n\t\t\t\trefresh_token: refreshToken,\n\t\t\t\tuser,\n\t\t\t\texpires_at: expiresAtParam ? Number(expiresAtParam) : Date.now() + 3600_000,\n\t\t\t\t...(salt && { salt }),\n\t\t\t};\n\n\t\t\tawait saveSession(session);\n\t\t\temitter.emit('change', session);\n\t\t\treturn session;\n\t\t},\n\n\t\tonAuthStateChange(callback: AuthStateChangeCallback) {\n\t\t\temitter.on('change', callback);\n\t\t\treturn () => emitter.off('change', callback);\n\t\t},\n\t};\n}\n"],"mappings":"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"}
package/dist/exchange.mjs CHANGED
@@ -13,27 +13,6 @@ async function fetchRequestId(apiBaseUrl, body) {
13
13
  }
14
14
  return (await res.json()).request_id;
15
15
  }
16
- /** Exchange auth code for tokens */
17
- async function exchangeCode(apiBaseUrl, body) {
18
- const url = `${apiBaseUrl.replace(/\/$/, "")}/auth/exchange`;
19
- const res = await fetch(url, {
20
- method: "POST",
21
- headers: { "Content-Type": "application/json" },
22
- body: JSON.stringify(body)
23
- });
24
- if (!res.ok) {
25
- const text = await res.text();
26
- throw new Error(`Token exchange failed: ${res.status} ${text}`);
27
- }
28
- const data = await res.json();
29
- const expires_at = Date.now() + data.expires_in * 1e3;
30
- return {
31
- access_token: data.access_token,
32
- refresh_token: data.refresh_token,
33
- expires_at,
34
- user: data.user
35
- };
36
- }
37
16
  /** Refresh access token using refresh_token */
38
17
  async function refreshTokens(apiBaseUrl, refreshToken) {
39
18
  const url = `${apiBaseUrl.replace(/\/$/, "")}/auth/refresh`;
@@ -69,5 +48,5 @@ async function logout(apiBaseUrl) {
69
48
  }
70
49
 
71
50
  //#endregion
72
- export { exchangeCode, fetchRequestId, logout, refreshTokens };
51
+ export { fetchRequestId, logout, refreshTokens };
73
52
  //# sourceMappingURL=exchange.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.mjs","names":[],"sources":["../src/exchange.ts"],"sourcesContent":["// Copyright (c) The Social Proof Foundation, LLC.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type {\n\tAuthRequestRequest,\n\tAuthRequestResponse,\n\tExchangeRequest,\n\tExchangeResponse,\n\tSession,\n} from './types.js';\n\n/** Fetch request_id from backend (optional flow). Backend validates return_origin against allowlist. */\nexport async function fetchRequestId(\n\tapiBaseUrl: string,\n\tbody: AuthRequestRequest,\n): Promise<string> {\n\tconst url = `${apiBaseUrl.replace(/\\/$/, '')}/auth/request`;\n\tconst res = await fetch(url, {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify(body),\n\t});\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow new Error(`Auth request failed: ${res.status} ${text}`);\n\t}\n\tconst data = (await res.json()) as AuthRequestResponse;\n\treturn data.request_id;\n}\n\n/** Exchange auth code for tokens */\nexport async function exchangeCode(apiBaseUrl: string, body: ExchangeRequest): Promise<Session> {\n\tconst url = `${apiBaseUrl.replace(/\\/$/, '')}/auth/exchange`;\n\tconst res = await fetch(url, {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify(body),\n\t});\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow new Error(`Token exchange failed: ${res.status} ${text}`);\n\t}\n\tconst data = (await res.json()) as ExchangeResponse;\n\tconst expires_at = Date.now() + data.expires_in * 1000;\n\treturn {\n\t\taccess_token: data.access_token,\n\t\trefresh_token: data.refresh_token,\n\t\texpires_at,\n\t\tuser: data.user,\n\t};\n}\n\n/** Refresh access token using refresh_token */\nexport async function refreshTokens(apiBaseUrl: string, refreshToken: string): Promise<Session> {\n\tconst url = `${apiBaseUrl.replace(/\\/$/, '')}/auth/refresh`;\n\tconst res = await fetch(url, {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({ refresh_token: refreshToken }),\n\t});\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow new Error(`Token refresh failed: ${res.status} ${text}`);\n\t}\n\tconst data = (await res.json()) as ExchangeResponse;\n\tconst expires_at = Date.now() + data.expires_in * 1000;\n\treturn {\n\t\taccess_token: data.access_token,\n\t\trefresh_token: data.refresh_token ?? refreshToken,\n\t\texpires_at,\n\t\tuser: data.user,\n\t};\n}\n\n/** Logout / invalidate session on backend */\nexport async function logout(apiBaseUrl: string): Promise<void> {\n\tconst url = `${apiBaseUrl.replace(/\\/$/, '')}/auth/logout`;\n\tconst res = await fetch(url, {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t});\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow new Error(`Logout failed: ${res.status} ${text}`);\n\t}\n}\n"],"mappings":";;AAYA,eAAsB,eACrB,YACA,MACkB;CAClB,MAAM,MAAM,GAAG,WAAW,QAAQ,OAAO,GAAG,CAAC;CAC7C,MAAM,MAAM,MAAM,MAAM,KAAK;EAC5B,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU,KAAK;EAC1B,CAAC;AACF,KAAI,CAAC,IAAI,IAAI;EACZ,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,QAAM,IAAI,MAAM,wBAAwB,IAAI,OAAO,GAAG,OAAO;;AAG9D,SADc,MAAM,IAAI,MAAM,EAClB;;;AAIb,eAAsB,aAAa,YAAoB,MAAyC;CAC/F,MAAM,MAAM,GAAG,WAAW,QAAQ,OAAO,GAAG,CAAC;CAC7C,MAAM,MAAM,MAAM,MAAM,KAAK;EAC5B,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU,KAAK;EAC1B,CAAC;AACF,KAAI,CAAC,IAAI,IAAI;EACZ,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,QAAM,IAAI,MAAM,0BAA0B,IAAI,OAAO,GAAG,OAAO;;CAEhE,MAAM,OAAQ,MAAM,IAAI,MAAM;CAC9B,MAAM,aAAa,KAAK,KAAK,GAAG,KAAK,aAAa;AAClD,QAAO;EACN,cAAc,KAAK;EACnB,eAAe,KAAK;EACpB;EACA,MAAM,KAAK;EACX;;;AAIF,eAAsB,cAAc,YAAoB,cAAwC;CAC/F,MAAM,MAAM,GAAG,WAAW,QAAQ,OAAO,GAAG,CAAC;CAC7C,MAAM,MAAM,MAAM,MAAM,KAAK;EAC5B,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU,EAAE,eAAe,cAAc,CAAC;EACrD,CAAC;AACF,KAAI,CAAC,IAAI,IAAI;EACZ,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,QAAM,IAAI,MAAM,yBAAyB,IAAI,OAAO,GAAG,OAAO;;CAE/D,MAAM,OAAQ,MAAM,IAAI,MAAM;CAC9B,MAAM,aAAa,KAAK,KAAK,GAAG,KAAK,aAAa;AAClD,QAAO;EACN,cAAc,KAAK;EACnB,eAAe,KAAK,iBAAiB;EACrC;EACA,MAAM,KAAK;EACX;;;AAIF,eAAsB,OAAO,YAAmC;CAC/D,MAAM,MAAM,GAAG,WAAW,QAAQ,OAAO,GAAG,CAAC;CAC7C,MAAM,MAAM,MAAM,MAAM,KAAK;EAC5B,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,CAAC;AACF,KAAI,CAAC,IAAI,IAAI;EACZ,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,QAAM,IAAI,MAAM,kBAAkB,IAAI,OAAO,GAAG,OAAO"}
1
+ {"version":3,"file":"exchange.mjs","names":[],"sources":["../src/exchange.ts"],"sourcesContent":["// Copyright (c) The Social Proof Foundation, LLC.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type {\n\tAuthRequestRequest,\n\tAuthRequestResponse,\n\tExchangeRequest,\n\tExchangeResponse,\n\tSession,\n} from './types.js';\n\n/** Fetch request_id from backend (optional flow). Backend validates return_origin against allowlist. */\nexport async function fetchRequestId(\n\tapiBaseUrl: string,\n\tbody: AuthRequestRequest,\n): Promise<string> {\n\tconst url = `${apiBaseUrl.replace(/\\/$/, '')}/auth/request`;\n\tconst res = await fetch(url, {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify(body),\n\t});\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow new Error(`Auth request failed: ${res.status} ${text}`);\n\t}\n\tconst data = (await res.json()) as AuthRequestResponse;\n\treturn data.request_id;\n}\n\n/** Exchange auth code for tokens */\nexport async function exchangeCode(apiBaseUrl: string, body: ExchangeRequest): Promise<Session> {\n\tconst url = `${apiBaseUrl.replace(/\\/$/, '')}/auth/exchange`;\n\tconst res = await fetch(url, {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify(body),\n\t});\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow new Error(`Token exchange failed: ${res.status} ${text}`);\n\t}\n\tconst data = (await res.json()) as ExchangeResponse;\n\tconst expires_at = Date.now() + data.expires_in * 1000;\n\treturn {\n\t\taccess_token: data.access_token,\n\t\trefresh_token: data.refresh_token,\n\t\texpires_at,\n\t\tuser: data.user,\n\t};\n}\n\n/** Refresh access token using refresh_token */\nexport async function refreshTokens(apiBaseUrl: string, refreshToken: string): Promise<Session> {\n\tconst url = `${apiBaseUrl.replace(/\\/$/, '')}/auth/refresh`;\n\tconst res = await fetch(url, {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({ refresh_token: refreshToken }),\n\t});\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow new Error(`Token refresh failed: ${res.status} ${text}`);\n\t}\n\tconst data = (await res.json()) as ExchangeResponse;\n\tconst expires_at = Date.now() + data.expires_in * 1000;\n\treturn {\n\t\taccess_token: data.access_token,\n\t\trefresh_token: data.refresh_token ?? refreshToken,\n\t\texpires_at,\n\t\tuser: data.user,\n\t};\n}\n\n/** Logout / invalidate session on backend */\nexport async function logout(apiBaseUrl: string): Promise<void> {\n\tconst url = `${apiBaseUrl.replace(/\\/$/, '')}/auth/logout`;\n\tconst res = await fetch(url, {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t});\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow new Error(`Logout failed: ${res.status} ${text}`);\n\t}\n}\n"],"mappings":";;AAYA,eAAsB,eACrB,YACA,MACkB;CAClB,MAAM,MAAM,GAAG,WAAW,QAAQ,OAAO,GAAG,CAAC;CAC7C,MAAM,MAAM,MAAM,MAAM,KAAK;EAC5B,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU,KAAK;EAC1B,CAAC;AACF,KAAI,CAAC,IAAI,IAAI;EACZ,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,QAAM,IAAI,MAAM,wBAAwB,IAAI,OAAO,GAAG,OAAO;;AAG9D,SADc,MAAM,IAAI,MAAM,EAClB;;;AA0Bb,eAAsB,cAAc,YAAoB,cAAwC;CAC/F,MAAM,MAAM,GAAG,WAAW,QAAQ,OAAO,GAAG,CAAC;CAC7C,MAAM,MAAM,MAAM,MAAM,KAAK;EAC5B,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU,EAAE,eAAe,cAAc,CAAC;EACrD,CAAC;AACF,KAAI,CAAC,IAAI,IAAI;EACZ,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,QAAM,IAAI,MAAM,yBAAyB,IAAI,OAAO,GAAG,OAAO;;CAE/D,MAAM,OAAQ,MAAM,IAAI,MAAM;CAC9B,MAAM,aAAa,KAAK,KAAK,GAAG,KAAK,aAAa;AAClD,QAAO;EACN,cAAc,KAAK;EACnB,eAAe,KAAK,iBAAiB;EACrC;EACA,MAAM,KAAK;EACX;;;AAIF,eAAsB,OAAO,YAAmC;CAC/D,MAAM,MAAM,GAAG,WAAW,QAAQ,OAAO,GAAG,CAAC;CAC7C,MAAM,MAAM,MAAM,MAAM,KAAK;EAC5B,QAAQ;EACR,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,CAAC;AACF,KAAI,CAAC,IAAI,IAAI;EACZ,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,QAAM,IAAI,MAAM,kBAAkB,IAAI,OAAO,GAAG,OAAO"}
package/dist/popup.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { AuthTimeoutError, InvalidStateError, PopupBlockedError, PopupClosedError } from "./errors.mjs";
2
2
  import { generateNonce, generateState } from "./pkce.mjs";
3
3
  import { getPopupFeatures } from "./popup-utils.mjs";
4
- import { exchangeCode, fetchRequestId } from "./exchange.mjs";
4
+ import { fetchRequestId } from "./exchange.mjs";
5
5
 
6
6
  //#region src/popup.ts
7
7
  /** Get return_origin from redirectUri (e.g. https://dripdrop.social/auth/callback -> https://dripdrop.social) */
@@ -38,7 +38,7 @@ async function openAuthPopup(options) {
38
38
  nonce,
39
39
  return_origin: returnOrigin,
40
40
  mode: "popup",
41
- provider: provider ?? "",
41
+ provider: provider ?? "none",
42
42
  code_challenge_method: "S256"
43
43
  });
44
44
  if (requestId) params.set("request_id", requestId);
@@ -78,13 +78,13 @@ async function openAuthPopup(options) {
78
78
  try {
79
79
  popup.close();
80
80
  } catch {}
81
- exchangeCode(apiBaseUrl, {
82
- code: msg.code,
83
- redirect_uri: redirectUri,
84
- state,
85
- nonce,
86
- request_id: requestId
87
- }).then(resolve).catch(reject);
81
+ resolve({
82
+ access_token: msg.access_token ?? msg.code,
83
+ refresh_token: msg.refresh_token,
84
+ user: msg.user ?? {},
85
+ expires_at: msg.expires_at ?? Date.now() + 36e5,
86
+ ...msg.salt && { salt: msg.salt }
87
+ });
88
88
  } else if (data.type === "MYSOCIAL_AUTH_ERROR") {
89
89
  const msg = data;
90
90
  cleanup();
@@ -1 +1 @@
1
- {"version":3,"file":"popup.mjs","names":[],"sources":["../src/popup.ts"],"sourcesContent":["// Copyright (c) The Social Proof Foundation, LLC.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { AuthProvider, AuthResultMessage, AuthErrorMessage } from './types.js';\nimport {\n\tAuthTimeoutError,\n\tInvalidStateError,\n\tPopupBlockedError,\n\tPopupClosedError,\n} from './errors.js';\nimport { generateNonce, generateState } from './pkce.js';\nimport { getPopupFeatures } from './popup-utils.js';\nimport { exchangeCode, fetchRequestId } from './exchange.js';\nimport type { Session } from './types.js';\n\n/** Get return_origin from redirectUri (e.g. https://dripdrop.social/auth/callback -> https://dripdrop.social) */\nfunction getReturnOrigin(redirectUri: string): string {\n\ttry {\n\t\tconst u = new URL(redirectUri);\n\t\treturn `${u.protocol}//${u.host}`;\n\t} catch {\n\t\treturn '';\n\t}\n}\n\nexport interface OpenPopupOptions {\n\tapiBaseUrl: string;\n\tauthOrigin: string;\n\tclientId: string;\n\tredirectUri: string;\n\tprovider?: AuthProvider;\n\ttimeout?: number;\n\tuseRequestId?: boolean;\n}\n\n/**\n * Open auth popup and wait for postMessage. Safari: open with about:blank first, then set location.\n * Returns session on success. Rejects on error, timeout, or user close.\n */\nexport async function openAuthPopup(options: OpenPopupOptions): Promise<Session> {\n\tconst {\n\t\tapiBaseUrl,\n\t\tauthOrigin,\n\t\tclientId,\n\t\tredirectUri,\n\t\tprovider,\n\t\ttimeout = 120_000,\n\t\tuseRequestId = false,\n\t} = options;\n\n\tconst state = generateState();\n\tconst nonce = generateNonce();\n\tconst returnOrigin =\n\t\ttypeof window !== 'undefined' ? window.location.origin : getReturnOrigin(redirectUri);\n\n\tlet requestId: string | undefined;\n\tif (useRequestId) {\n\t\trequestId = await fetchRequestId(apiBaseUrl, {\n\t\t\tclient_id: clientId,\n\t\t\tredirect_uri: redirectUri,\n\t\t\treturn_origin: returnOrigin,\n\t\t});\n\t}\n\n\t// Open popup immediately (Safari requires user gesture). Use about:blank, then set location.\n\tconst features = getPopupFeatures(420, 720);\n\tconst popup = window.open('about:blank', '_blank', features);\n\n\tif (!popup || popup.closed) {\n\t\tthrow new PopupBlockedError();\n\t}\n\n\tconst params = new URLSearchParams({\n\t\tclient_id: clientId,\n\t\tredirect_uri: redirectUri,\n\t\tstate,\n\t\tnonce,\n\t\treturn_origin: returnOrigin,\n\t\tmode: 'popup',\n\t\tprovider: provider ?? '',\n\t\tcode_challenge_method: 'S256',\n\t});\n\tif (requestId) params.set('request_id', requestId);\n\n\tconst loginUrl = `${authOrigin.replace(/\\/$/, '')}/login?${params.toString()}`;\n\n\t// Set location after popup is open (Safari-safe)\n\tpopup.location.href = loginUrl;\n\n\treturn new Promise<Session>((resolve, reject) => {\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\t\tlet closedCheckId: ReturnType<typeof setInterval> | null = null;\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\tif (closedCheckId) clearInterval(closedCheckId);\n\t\t\twindow.removeEventListener('message', handler);\n\t\t};\n\n\t\tconst handler = (event: MessageEvent) => {\n\t\t\tif (event.origin !== authOrigin) return;\n\t\t\tif (event.source !== popup) return;\n\n\t\t\tconst data = event.data;\n\t\t\tif (!data || typeof data !== 'object' || !data.type) return;\n\n\t\t\tif (data.type === 'MYSOCIAL_AUTH_RESULT') {\n\t\t\t\tconst msg = data as AuthResultMessage;\n\t\t\t\tif (msg.state !== state || msg.nonce !== nonce) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\treject(new InvalidStateError());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (msg.clientId !== clientId) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\treject(new InvalidStateError());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (requestId && msg.requestId !== requestId) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\treject(new InvalidStateError());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcleanup();\n\t\t\t\ttry {\n\t\t\t\t\tpopup.close();\n\t\t\t\t} catch {\n\t\t\t\t\t// Popup may already be closed by auth server\n\t\t\t\t}\n\n\t\t\t\texchangeCode(apiBaseUrl, {\n\t\t\t\t\tcode: msg.code,\n\t\t\t\t\tredirect_uri: redirectUri,\n\t\t\t\t\tstate,\n\t\t\t\t\tnonce,\n\t\t\t\t\trequest_id: requestId,\n\t\t\t\t})\n\t\t\t\t\t.then(resolve)\n\t\t\t\t\t.catch(reject);\n\t\t\t} else if (data.type === 'MYSOCIAL_AUTH_ERROR') {\n\t\t\t\tconst msg = data as AuthErrorMessage;\n\t\t\t\tcleanup();\n\t\t\t\ttry {\n\t\t\t\t\tpopup.close();\n\t\t\t\t} catch {\n\t\t\t\t\t// Popup may already be closed\n\t\t\t\t}\n\t\t\t\treject(new Error(msg.error ?? 'Authentication failed'));\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener('message', handler);\n\n\t\ttimeoutId = setTimeout(() => {\n\t\t\tcleanup();\n\t\t\ttry {\n\t\t\t\tpopup.close();\n\t\t\t} catch {\n\t\t\t\t// Popup may already be closed\n\t\t\t}\n\t\t\treject(new AuthTimeoutError());\n\t\t}, timeout);\n\n\t\tclosedCheckId = setInterval(() => {\n\t\t\tif (popup.closed) {\n\t\t\t\tcleanup();\n\t\t\t\treject(new PopupClosedError());\n\t\t\t}\n\t\t}, 200);\n\t});\n}\n"],"mappings":";;;;;;;AAgBA,SAAS,gBAAgB,aAA6B;AACrD,KAAI;EACH,MAAM,IAAI,IAAI,IAAI,YAAY;AAC9B,SAAO,GAAG,EAAE,SAAS,IAAI,EAAE;SACpB;AACP,SAAO;;;;;;;AAkBT,eAAsB,cAAc,SAA6C;CAChF,MAAM,EACL,YACA,YACA,UACA,aACA,UACA,UAAU,MACV,eAAe,UACZ;CAEJ,MAAM,QAAQ,eAAe;CAC7B,MAAM,QAAQ,eAAe;CAC7B,MAAM,eACL,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS,gBAAgB,YAAY;CAEtF,IAAI;AACJ,KAAI,aACH,aAAY,MAAM,eAAe,YAAY;EAC5C,WAAW;EACX,cAAc;EACd,eAAe;EACf,CAAC;CAIH,MAAM,WAAW,iBAAiB,KAAK,IAAI;CAC3C,MAAM,QAAQ,OAAO,KAAK,eAAe,UAAU,SAAS;AAE5D,KAAI,CAAC,SAAS,MAAM,OACnB,OAAM,IAAI,mBAAmB;CAG9B,MAAM,SAAS,IAAI,gBAAgB;EAClC,WAAW;EACX,cAAc;EACd;EACA;EACA,eAAe;EACf,MAAM;EACN,UAAU,YAAY;EACtB,uBAAuB;EACvB,CAAC;AACF,KAAI,UAAW,QAAO,IAAI,cAAc,UAAU;CAElD,MAAM,WAAW,GAAG,WAAW,QAAQ,OAAO,GAAG,CAAC,SAAS,OAAO,UAAU;AAG5E,OAAM,SAAS,OAAO;AAEtB,QAAO,IAAI,SAAkB,SAAS,WAAW;EAChD,IAAI,YAAkD;EACtD,IAAI,gBAAuD;EAE3D,MAAM,gBAAgB;AACrB,OAAI,UAAW,cAAa,UAAU;AACtC,OAAI,cAAe,eAAc,cAAc;AAC/C,UAAO,oBAAoB,WAAW,QAAQ;;EAG/C,MAAM,WAAW,UAAwB;AACxC,OAAI,MAAM,WAAW,WAAY;AACjC,OAAI,MAAM,WAAW,MAAO;GAE5B,MAAM,OAAO,MAAM;AACnB,OAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,CAAC,KAAK,KAAM;AAErD,OAAI,KAAK,SAAS,wBAAwB;IACzC,MAAM,MAAM;AACZ,QAAI,IAAI,UAAU,SAAS,IAAI,UAAU,OAAO;AAC/C,cAAS;AACT,YAAO,IAAI,mBAAmB,CAAC;AAC/B;;AAED,QAAI,IAAI,aAAa,UAAU;AAC9B,cAAS;AACT,YAAO,IAAI,mBAAmB,CAAC;AAC/B;;AAED,QAAI,aAAa,IAAI,cAAc,WAAW;AAC7C,cAAS;AACT,YAAO,IAAI,mBAAmB,CAAC;AAC/B;;AAGD,aAAS;AACT,QAAI;AACH,WAAM,OAAO;YACN;AAIR,iBAAa,YAAY;KACxB,MAAM,IAAI;KACV,cAAc;KACd;KACA;KACA,YAAY;KACZ,CAAC,CACA,KAAK,QAAQ,CACb,MAAM,OAAO;cACL,KAAK,SAAS,uBAAuB;IAC/C,MAAM,MAAM;AACZ,aAAS;AACT,QAAI;AACH,WAAM,OAAO;YACN;AAGR,WAAO,IAAI,MAAM,IAAI,SAAS,wBAAwB,CAAC;;;AAIzD,SAAO,iBAAiB,WAAW,QAAQ;AAE3C,cAAY,iBAAiB;AAC5B,YAAS;AACT,OAAI;AACH,UAAM,OAAO;WACN;AAGR,UAAO,IAAI,kBAAkB,CAAC;KAC5B,QAAQ;AAEX,kBAAgB,kBAAkB;AACjC,OAAI,MAAM,QAAQ;AACjB,aAAS;AACT,WAAO,IAAI,kBAAkB,CAAC;;KAE7B,IAAI;GACN"}
1
+ {"version":3,"file":"popup.mjs","names":[],"sources":["../src/popup.ts"],"sourcesContent":["// Copyright (c) The Social Proof Foundation, LLC.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { AuthProvider, AuthResultMessage, AuthErrorMessage } from './types.js';\nimport {\n\tAuthTimeoutError,\n\tInvalidStateError,\n\tPopupBlockedError,\n\tPopupClosedError,\n} from './errors.js';\nimport { generateNonce, generateState } from './pkce.js';\nimport { getPopupFeatures } from './popup-utils.js';\nimport { fetchRequestId } from './exchange.js';\nimport type { Session } from './types.js';\n\n/** Get return_origin from redirectUri (e.g. https://dripdrop.social/auth/callback -> https://dripdrop.social) */\nfunction getReturnOrigin(redirectUri: string): string {\n\ttry {\n\t\tconst u = new URL(redirectUri);\n\t\treturn `${u.protocol}//${u.host}`;\n\t} catch {\n\t\treturn '';\n\t}\n}\n\nexport interface OpenPopupOptions {\n\tapiBaseUrl: string;\n\tauthOrigin: string;\n\tclientId: string;\n\tredirectUri: string;\n\tprovider?: AuthProvider;\n\ttimeout?: number;\n\tuseRequestId?: boolean;\n}\n\n/**\n * Open auth popup and wait for postMessage. Safari: open with about:blank first, then set location.\n * Returns session on success. Rejects on error, timeout, or user close.\n */\nexport async function openAuthPopup(options: OpenPopupOptions): Promise<Session> {\n\tconst {\n\t\tapiBaseUrl,\n\t\tauthOrigin,\n\t\tclientId,\n\t\tredirectUri,\n\t\tprovider,\n\t\ttimeout = 120_000,\n\t\tuseRequestId = false,\n\t} = options;\n\n\tconst state = generateState();\n\tconst nonce = generateNonce();\n\tconst returnOrigin =\n\t\ttypeof window !== 'undefined' ? window.location.origin : getReturnOrigin(redirectUri);\n\n\tlet requestId: string | undefined;\n\tif (useRequestId) {\n\t\trequestId = await fetchRequestId(apiBaseUrl, {\n\t\t\tclient_id: clientId,\n\t\t\tredirect_uri: redirectUri,\n\t\t\treturn_origin: returnOrigin,\n\t\t});\n\t}\n\n\t// Open popup immediately (Safari requires user gesture). Use about:blank, then set location.\n\tconst features = getPopupFeatures(420, 720);\n\tconst popup = window.open('about:blank', '_blank', features);\n\n\tif (!popup || popup.closed) {\n\t\tthrow new PopupBlockedError();\n\t}\n\n\tconst params = new URLSearchParams({\n\t\tclient_id: clientId,\n\t\tredirect_uri: redirectUri,\n\t\tstate,\n\t\tnonce,\n\t\treturn_origin: returnOrigin,\n\t\tmode: 'popup',\n\t\tprovider: provider ?? 'none',\n\t\tcode_challenge_method: 'S256',\n\t});\n\tif (requestId) params.set('request_id', requestId);\n\n\tconst loginUrl = `${authOrigin.replace(/\\/$/, '')}/login?${params.toString()}`;\n\n\t// Set location after popup is open (Safari-safe)\n\tpopup.location.href = loginUrl;\n\n\treturn new Promise<Session>((resolve, reject) => {\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\t\tlet closedCheckId: ReturnType<typeof setInterval> | null = null;\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\tif (closedCheckId) clearInterval(closedCheckId);\n\t\t\twindow.removeEventListener('message', handler);\n\t\t};\n\n\t\tconst handler = (event: MessageEvent) => {\n\t\t\tif (event.origin !== authOrigin) return;\n\t\t\tif (event.source !== popup) return;\n\n\t\t\tconst data = event.data;\n\t\t\tif (!data || typeof data !== 'object' || !data.type) return;\n\n\t\t\tif (data.type === 'MYSOCIAL_AUTH_RESULT') {\n\t\t\t\tconst msg = data as AuthResultMessage;\n\t\t\t\tif (msg.state !== state || msg.nonce !== nonce) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\treject(new InvalidStateError());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (msg.clientId !== clientId) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\treject(new InvalidStateError());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (requestId && msg.requestId !== requestId) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\treject(new InvalidStateError());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcleanup();\n\t\t\t\ttry {\n\t\t\t\t\tpopup.close();\n\t\t\t\t} catch {\n\t\t\t\t\t// Popup may already be closed by auth server\n\t\t\t\t}\n\n\t\t\t\tconst session: Session = {\n\t\t\t\t\taccess_token: msg.access_token ?? msg.code,\n\t\t\t\t\trefresh_token: msg.refresh_token,\n\t\t\t\t\tuser: msg.user ?? {},\n\t\t\t\t\texpires_at: msg.expires_at ?? Date.now() + 3600_000,\n\t\t\t\t\t...(msg.salt && { salt: msg.salt }),\n\t\t\t\t};\n\t\t\t\tresolve(session);\n\t\t\t} else if (data.type === 'MYSOCIAL_AUTH_ERROR') {\n\t\t\t\tconst msg = data as AuthErrorMessage;\n\t\t\t\tcleanup();\n\t\t\t\ttry {\n\t\t\t\t\tpopup.close();\n\t\t\t\t} catch {\n\t\t\t\t\t// Popup may already be closed\n\t\t\t\t}\n\t\t\t\treject(new Error(msg.error ?? 'Authentication failed'));\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener('message', handler);\n\n\t\ttimeoutId = setTimeout(() => {\n\t\t\tcleanup();\n\t\t\ttry {\n\t\t\t\tpopup.close();\n\t\t\t} catch {\n\t\t\t\t// Popup may already be closed\n\t\t\t}\n\t\t\treject(new AuthTimeoutError());\n\t\t}, timeout);\n\n\t\tclosedCheckId = setInterval(() => {\n\t\t\tif (popup.closed) {\n\t\t\t\tcleanup();\n\t\t\t\treject(new PopupClosedError());\n\t\t\t}\n\t\t}, 200);\n\t});\n}\n"],"mappings":";;;;;;;AAgBA,SAAS,gBAAgB,aAA6B;AACrD,KAAI;EACH,MAAM,IAAI,IAAI,IAAI,YAAY;AAC9B,SAAO,GAAG,EAAE,SAAS,IAAI,EAAE;SACpB;AACP,SAAO;;;;;;;AAkBT,eAAsB,cAAc,SAA6C;CAChF,MAAM,EACL,YACA,YACA,UACA,aACA,UACA,UAAU,MACV,eAAe,UACZ;CAEJ,MAAM,QAAQ,eAAe;CAC7B,MAAM,QAAQ,eAAe;CAC7B,MAAM,eACL,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS,gBAAgB,YAAY;CAEtF,IAAI;AACJ,KAAI,aACH,aAAY,MAAM,eAAe,YAAY;EAC5C,WAAW;EACX,cAAc;EACd,eAAe;EACf,CAAC;CAIH,MAAM,WAAW,iBAAiB,KAAK,IAAI;CAC3C,MAAM,QAAQ,OAAO,KAAK,eAAe,UAAU,SAAS;AAE5D,KAAI,CAAC,SAAS,MAAM,OACnB,OAAM,IAAI,mBAAmB;CAG9B,MAAM,SAAS,IAAI,gBAAgB;EAClC,WAAW;EACX,cAAc;EACd;EACA;EACA,eAAe;EACf,MAAM;EACN,UAAU,YAAY;EACtB,uBAAuB;EACvB,CAAC;AACF,KAAI,UAAW,QAAO,IAAI,cAAc,UAAU;CAElD,MAAM,WAAW,GAAG,WAAW,QAAQ,OAAO,GAAG,CAAC,SAAS,OAAO,UAAU;AAG5E,OAAM,SAAS,OAAO;AAEtB,QAAO,IAAI,SAAkB,SAAS,WAAW;EAChD,IAAI,YAAkD;EACtD,IAAI,gBAAuD;EAE3D,MAAM,gBAAgB;AACrB,OAAI,UAAW,cAAa,UAAU;AACtC,OAAI,cAAe,eAAc,cAAc;AAC/C,UAAO,oBAAoB,WAAW,QAAQ;;EAG/C,MAAM,WAAW,UAAwB;AACxC,OAAI,MAAM,WAAW,WAAY;AACjC,OAAI,MAAM,WAAW,MAAO;GAE5B,MAAM,OAAO,MAAM;AACnB,OAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,CAAC,KAAK,KAAM;AAErD,OAAI,KAAK,SAAS,wBAAwB;IACzC,MAAM,MAAM;AACZ,QAAI,IAAI,UAAU,SAAS,IAAI,UAAU,OAAO;AAC/C,cAAS;AACT,YAAO,IAAI,mBAAmB,CAAC;AAC/B;;AAED,QAAI,IAAI,aAAa,UAAU;AAC9B,cAAS;AACT,YAAO,IAAI,mBAAmB,CAAC;AAC/B;;AAED,QAAI,aAAa,IAAI,cAAc,WAAW;AAC7C,cAAS;AACT,YAAO,IAAI,mBAAmB,CAAC;AAC/B;;AAGD,aAAS;AACT,QAAI;AACH,WAAM,OAAO;YACN;AAWR,YAPyB;KACxB,cAAc,IAAI,gBAAgB,IAAI;KACtC,eAAe,IAAI;KACnB,MAAM,IAAI,QAAQ,EAAE;KACpB,YAAY,IAAI,cAAc,KAAK,KAAK,GAAG;KAC3C,GAAI,IAAI,QAAQ,EAAE,MAAM,IAAI,MAAM;KAClC,CACe;cACN,KAAK,SAAS,uBAAuB;IAC/C,MAAM,MAAM;AACZ,aAAS;AACT,QAAI;AACH,WAAM,OAAO;YACN;AAGR,WAAO,IAAI,MAAM,IAAI,SAAS,wBAAwB,CAAC;;;AAIzD,SAAO,iBAAiB,WAAW,QAAQ;AAE3C,cAAY,iBAAiB;AAC5B,YAAS;AACT,OAAI;AACH,UAAM,OAAO;WACN;AAGR,UAAO,IAAI,kBAAkB,CAAC;KAC5B,QAAQ;AAEX,kBAAgB,kBAAkB;AACjC,OAAI,MAAM,QAAQ;AACjB,aAAS;AACT,WAAO,IAAI,kBAAkB,CAAC;;KAE7B,IAAI;GACN"}
package/dist/types.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region src/types.d.ts
2
- /** OAuth provider options for Login with MySocial */
3
- type AuthProvider = 'google' | 'apple' | 'facebook' | 'twitch';
2
+ /** OAuth provider options for Login with MySocial. Use 'none' for home screen (user picks provider). */
3
+ type AuthProvider = 'google' | 'apple' | 'facebook' | 'twitch' | 'none';
4
4
  /** Auth flow mode: popup (opens window) or redirect (navigates away) */
5
5
  type AuthMode = 'popup' | 'redirect';
6
6
  /** User object returned from exchange/refresh */
@@ -16,6 +16,7 @@ interface Session {
16
16
  refresh_token?: string;
17
17
  expires_at: number;
18
18
  user: AuthUser;
19
+ salt?: string;
19
20
  }
20
21
  /** Storage adapter for persisting session (memory, sessionStorage, or custom) */
21
22
  interface StorageAdapter {
@@ -39,6 +40,7 @@ interface MySocialAuthConfig {
39
40
  }
40
41
  /** Sign-in options */
41
42
  interface SignInOptions {
43
+ /** Provider to use. Default 'none' shows auth home screen. */
42
44
  provider?: AuthProvider;
43
45
  mode?: AuthMode;
44
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;KAIY,YAAA;;KAGA,QAAA;;UAGK,QAAA;EAChB,EAAA;EACA,KAAA;EACA,IAAA;EAAA,CACC,GAAA;AAAA;AAJF;AAAA,UAQiB,OAAA;EAChB,YAAA;EACA,aAAA;EACA,UAAA;EACA,IAAA,EAAM,QAAA;AAAA;;UAIU,cAAA;EAChB,GAAA,CAAI,GAAA;EACJ,GAAA,CAAI,GAAA,UAAa,KAAA;EACjB,MAAA,CAAO,GAAA;AAAA;;KAII,aAAA,0BAAuC,cAAA;;UAGlC,kBAAA;EAChB,UAAA;EACA,UAAA;EACA,QAAA;EACA,WAAA;EACA,OAAA,GAAU,aAAA;EAfoB;EAiB9B,YAAA;EAjB8B;EAmB9B,YAAA;AAAA;;UAIgB,aAAA;EAChB,QAAA,GAAW,YAAA;EACX,IAAA,GAAO,QAAA;AAAA;;KAII,uBAAA,IAA2B,OAAA,EAAS,OAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;KAIY,YAAA;;KAGA,QAAA;;UAGK,QAAA;EAChB,EAAA;EACA,KAAA;EACA,IAAA;EAAA,CACC,GAAA;AAAA;AAJF;AAAA,UAQiB,OAAA;EAChB,YAAA;EACA,aAAA;EACA,UAAA;EACA,IAAA,EAAM,QAAA;EACN,IAAA;AAAA;;UAIgB,cAAA;EAChB,GAAA,CAAI,GAAA;EACJ,GAAA,CAAI,GAAA,UAAa,KAAA;EACjB,MAAA,CAAO,GAAA;AAAA;;KAII,aAAA,0BAAuC,cAAA;;UAGlC,kBAAA;EAChB,UAAA;EACA,UAAA;EACA,QAAA;EACA,WAAA;EACA,OAAA,GAAU,aAAA;EAfoB;EAiB9B,YAAA;EAjB8B;EAmB9B,YAAA;AAAA;;UAIgB,aAAA;EArBC;EAuBjB,QAAA,GAAW,YAAA;EACX,IAAA,GAAO,QAAA;AAAA;;KAII,uBAAA,IAA2B,OAAA,EAAS,OAAA"}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@socialproof/mysocial-auth",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "MySocial Auth SDK for Login with MySocial via popup or redirect",
6
6
  "license": "Apache-2.0",
7
- "author": "Mysten Labs <build@socialprooflabs.com>",
7
+ "author": "The Social Proof Foundation, LLC. <build@socialprooflabs.com>",
8
8
  "type": "module",
9
9
  "main": "./dist/index.mjs",
10
10
  "types": "./dist/index.d.mts",
@@ -22,12 +22,12 @@
22
22
  ],
23
23
  "repository": {
24
24
  "type": "git",
25
- "url": "git+https://github.com/MystenLabs/ts-sdks.git"
25
+ "url": "git+https://github.com/the-social-proof-foundation/myso-ts-sdks.git"
26
26
  },
27
27
  "bugs": {
28
- "url": "https://github.com/mystenlabs/ts-sdks/issues"
28
+ "url": "https://github.com/the-social-proof-foundation/myso-ts-sdks/issues"
29
29
  },
30
- "homepage": "https://github.com/MystenLabs/ts-sdks/tree/main/packages/mysocial-auth#readme",
30
+ "homepage": "https://github.com/the-social-proof-foundation/myso-ts-sdks/tree/main/packages/mysocial-auth#readme",
31
31
  "devDependencies": {
32
32
  "@types/node": "^25.0.8",
33
33
  "happy-dom": "^20.1.0",