@growth-labs/auth 0.7.3 → 0.7.4

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.
Files changed (37) hide show
  1. package/README.md +20 -15
  2. package/dist/routes/api/code-request.d.ts.map +1 -1
  3. package/dist/routes/api/code-request.js +1 -1
  4. package/dist/routes/api/code-request.js.map +1 -1
  5. package/dist/routes/api/password-reset-confirm.d.ts.map +1 -1
  6. package/dist/routes/api/password-reset-confirm.js +1 -1
  7. package/dist/routes/api/password-reset-confirm.js.map +1 -1
  8. package/dist/routes/api/password-reset-request.d.ts.map +1 -1
  9. package/dist/routes/api/password-reset-request.js +1 -1
  10. package/dist/routes/api/password-reset-request.js.map +1 -1
  11. package/dist/routes/api/shared.d.ts +3 -2
  12. package/dist/routes/api/shared.d.ts.map +1 -1
  13. package/dist/routes/api/shared.js +61 -2
  14. package/dist/routes/api/shared.js.map +1 -1
  15. package/dist/routes/callback.d.ts.map +1 -1
  16. package/dist/routes/callback.js +44 -7
  17. package/dist/routes/callback.js.map +1 -1
  18. package/dist/routes/flow.d.ts +6 -0
  19. package/dist/routes/flow.d.ts.map +1 -1
  20. package/dist/routes/flow.js.map +1 -1
  21. package/dist/utils/analytics.d.ts +17 -0
  22. package/dist/utils/analytics.d.ts.map +1 -1
  23. package/dist/utils/analytics.js +58 -11
  24. package/dist/utils/analytics.js.map +1 -1
  25. package/dist/utils/index.d.ts +1 -1
  26. package/dist/utils/index.d.ts.map +1 -1
  27. package/dist/utils/index.js +1 -1
  28. package/dist/utils/index.js.map +1 -1
  29. package/package.json +2 -2
  30. package/src/routes/api/code-request.ts +1 -0
  31. package/src/routes/api/password-reset-confirm.ts +1 -0
  32. package/src/routes/api/password-reset-request.ts +1 -0
  33. package/src/routes/api/shared.ts +100 -2
  34. package/src/routes/callback.ts +65 -11
  35. package/src/routes/flow.ts +6 -0
  36. package/src/utils/analytics.ts +84 -10
  37. package/src/utils/index.ts +12 -1
package/README.md CHANGED
@@ -114,9 +114,10 @@ consumers can also use it from custom server routes.
114
114
 
115
115
  ## Analytics Events
116
116
 
117
- When `@growth-labs/analytics@0.4.0` or newer is installed, the callback
118
- route emits `signup_completed` or `login_completed` through
119
- `trackServerEvent` from `@growth-labs/analytics/utils`. The payload is:
117
+ When `@growth-labs/analytics@0.4.0` or newer is installed, successful
118
+ callback and JSON verify routes emit `signup_completed` or
119
+ `login_completed` through `trackServerEvent` from
120
+ `@growth-labs/analytics/utils`. The payload is:
120
121
 
121
122
  ```ts
122
123
  {
@@ -132,6 +133,10 @@ can preserve the site's anonymous `visitor_id`, `session_id`, attribution
132
133
  cookies, and `gl_identity_links` row. The identity platform does not need
133
134
  to see site attribution cookies.
134
135
 
136
+ Auth failures emit the operational event `auth_failed` with
137
+ `provider`, `reason`, and `auth_path`. Failure payloads never include raw
138
+ email addresses, passwords, magic codes, or reset tokens.
139
+
135
140
  ## Using `context.locals.user`
136
141
 
137
142
  ```typescript
@@ -184,9 +189,9 @@ internal package-test helper and is not exported from
184
189
  ## OAuth Flow Summary
185
190
 
186
191
  1. User clicks a provider on the local login page → browser hits `/login?provider=...` or a local branded `/password` / `/verify-code` route when that renderer is configured
187
- 2. The local `/login` start route stores PKCE/state/redirect/provider cookies and redirects to `{issuer}/authorize?provider=...`; configured `resource` is sent as the RFC 8707 resource indicator. Consumer-hosted `/password`, `/verify-code`, and `/forgot-password` pages seed root-scoped flow cookies locally, with the PKCE cookie visible to their package-owned POST handlers. The forgot-password route does not set a provider cookie.
188
- 3. For consumer-hosted password or magic-code flows, package-owned POST routes call `{issuerInternal ?? issuer}/api/v1/auth/*` and redirect to `/api/auth/callback?code=...&state=...`; native forms receive `303` redirects, while `Accept: application/json` callers receive `{ ok/error, redirect }`
189
- 4. Issuer authenticates or issues a JSON authorization code → callback exchanges code at `{issuerInternal ?? issuer}/token` via PKCE, sends `Authorization: Basic base64(client_id:client_secret)` when `clientSecret` is configured, includes `resource`, reads `properties.isNewUser` from the JWT payload with fallback to top-level `isNewUser` for legacy issuers, uses the `{cookiePrefix}_provider` auth-flow cookie set at login start for provider attribution, sets session cookies, sets `gl_id` when `GL_IDENTITY_SECRET` is configured, and redirects to the original page
192
+ 2. The local `/login` start route stores PKCE/state/redirect/provider cookies and redirects to `{issuer}/authorize?provider=...`; configured `resource` is sent as the RFC 8707 resource indicator. Consumer-hosted `/password` and `/verify-code` pages seed `{cookiePrefix}_pkce` at both `/` and `callbackPath`: the root-path copy is visible to package-owned POST handlers and JSON verify flows, while the callback-path copy preserves native form handoff through the callback route. The forgot-password route seeds local flow cookies but does not set a provider cookie.
193
+ 3. For consumer-hosted password or magic-code flows, package-owned POST routes call `{issuerInternal ?? issuer}/api/v1/auth/*`; native forms receive `303` redirects through `/api/auth/callback?code=...&state=...`, while `Accept: application/json` verify callers exchange the code in-place, set session cookies, emit the success event, and receive `{ ok, redirect }` for the final destination.
194
+ 4. Issuer authenticates or issues a JSON authorization code → callback exchanges code at `{issuerInternal ?? issuer}/token` via PKCE, sends `Authorization: Basic base64(client_id:client_secret)` when `clientSecret` is configured, includes `resource`, reads `properties.isNewUser` from the JWT payload with fallback to top-level `isNewUser` for legacy issuers, uses the `{cookiePrefix}_provider` auth-flow cookie set at login start for provider attribution, sets session cookies, sets `gl_id` when `GL_IDENTITY_SECRET` is configured, emits success/failure analytics, and redirects to the original page or returns a JSON redirect contract for `Accept: application/json`.
190
195
 
191
196
  ## Signed Identity Cookie
192
197
 
@@ -196,7 +201,7 @@ internal package-test helper and is not exported from
196
201
  - Payload: lowercase hex `sha256(sub + GL_IDENTITY_SECRET)`, where `sub` is the access-token subject. The raw subject is never written into the cookie.
197
202
  - Secret: `GL_IDENTITY_SECRET`, a per-tenant 32-byte standard-base64 value
198
203
  - Attributes: `HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=2592000`
199
- - Issued: callback success after token exchange
204
+ - Issued: callback success or JSON verify success after token exchange
200
205
  - Cleared: `/logout` with `Max-Age=0`
201
206
 
202
207
  Verification helper:
@@ -214,19 +219,19 @@ if (result.valid) {
214
219
 
215
220
  ## Analytics Integration
216
221
 
217
- Optional analytics helpers exist for `login_completed` / `signup_completed`,
218
- but the package does not currently guarantee WAE writes by itself. The
219
- event name is derived from the access-token JWT `isNewUser` claim,
220
- preferring `payload.properties.isNewUser` and falling back to top-level
221
- `payload.isNewUser` for legacy issuers. Consumers that need dashboard
222
- events should use the public analytics client/server contract available
223
- in their app.
222
+ Optional analytics helpers exist for `login_completed`,
223
+ `signup_completed`, and `auth_failed`. The success event name is derived
224
+ from the access-token JWT `isNewUser` claim, preferring
225
+ `payload.properties.isNewUser` and falling back to top-level
226
+ `payload.isNewUser` for legacy issuers. The package uses the public
227
+ `@growth-labs/analytics/utils.trackServerEvent` contract when the optional
228
+ analytics peer is installed.
224
229
 
225
230
  ## Key Patterns
226
231
 
227
232
  - Virtual module: `virtual:growth-labs/auth/config` (slash, not hyphen)
228
233
  - Cookie names: `{cookiePrefix}_at`, `{cookiePrefix}_rt`, `{cookiePrefix}_pkce`, `{cookiePrefix}_state`, `{cookiePrefix}_redirect`, `{cookiePrefix}_provider`, `gl_id`
229
- - PKCE flow with `code_verifier` / `code_challenge` (S256); `{cookiePrefix}_pkce` is scoped to `callbackPath` for issuer redirect flows and to `/` for consumer-hosted password/code/forgot flows that post before callback
234
+ - PKCE flow with `code_verifier` / `code_challenge` (S256); `{cookiePrefix}_pkce` is scoped to `callbackPath` for issuer redirect flows and set at both `/` and `callbackPath` for consumer-hosted password/code flows that can either post JSON in-place or hand off to callback
230
235
  - `issuer` is browser-facing; optional `issuerInternal` is used for token exchange, silent refresh, JWT verification/JWKS lookup, and revocation. If omitted, `issuerInternal` falls back to `issuer`.
231
236
  - Confidential OAuth clients use `client_secret_basic` on token exchange and refresh when `clientSecret` is configured. Prefer a Worker secret binding name such as `AUTH_CLIENT_SECRET`; runtime code resolves that binding before calling the issuer.
232
237
  - RFC 8707 `resource` is sent on authorize, token exchange, and refresh when configured
@@ -1 +1 @@
1
- {"version":3,"file":"code-request.d.ts","sourceRoot":"","sources":["../../../src/routes/api/code-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,YAAY,EAKjB,MAAM,aAAa,CAAA;AAEpB,wBAAsB,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsBnE"}
1
+ {"version":3,"file":"code-request.d.ts","sourceRoot":"","sources":["../../../src/routes/api/code-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,YAAY,EAKjB,MAAM,aAAa,CAAA;AAEpB,wBAAsB,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuBnE"}
@@ -11,6 +11,6 @@ export async function POST(context) {
11
11
  const email = await readFormString(form, 'email');
12
12
  const redirect = form.get('redirect')?.toString();
13
13
  const result = await authClient(context).code.request({ email });
14
- return statusRedirect(context, result, options.verifyCodePath, { email, sent: '1', redirect }, options.verifyCodePath, { email, redirect });
14
+ return statusRedirect(context, result, options.verifyCodePath, { email, sent: '1', redirect }, options.verifyCodePath, { email, redirect }, 'email-code');
15
15
  }
16
16
  //# sourceMappingURL=code-request.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"code-request.js","sourceRoot":"","sources":["../../../src/routes/api/code-request.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,SAAS,EAET,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,cAAc,GACd,MAAM,aAAa,CAAA;AAEpB,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAqB;IAC/C,MAAM,aAAa,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;IAClD,IAAI,aAAa;QAAE,OAAO,aAAa,CAAA;IAEvC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAA;IAC3B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAC/C,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAClD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAA;IACjD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IAEhE,OAAO,cAAc,CACpB,OAAO,EACP,MAAM,EACN,OAAO,CAAC,cAAc,EACtB,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,EAC9B,OAAO,CAAC,cAAc,EACtB,EAAE,KAAK,EAAE,QAAQ,EAAE,CACnB,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"code-request.js","sourceRoot":"","sources":["../../../src/routes/api/code-request.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,SAAS,EAET,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,cAAc,GACd,MAAM,aAAa,CAAA;AAEpB,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAqB;IAC/C,MAAM,aAAa,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;IAClD,IAAI,aAAa;QAAE,OAAO,aAAa,CAAA;IAEvC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAA;IAC3B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAC/C,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAClD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAA;IACjD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IAEhE,OAAO,cAAc,CACpB,OAAO,EACP,MAAM,EACN,OAAO,CAAC,cAAc,EACtB,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,EAC9B,OAAO,CAAC,cAAc,EACtB,EAAE,KAAK,EAAE,QAAQ,EAAE,EACnB,YAAY,CACZ,CAAA;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"password-reset-confirm.d.ts","sourceRoot":"","sources":["../../../src/routes/api/password-reset-confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,YAAY,EAKjB,MAAM,aAAa,CAAA;AAEpB,wBAAsB,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyBnE"}
1
+ {"version":3,"file":"password-reset-confirm.d.ts","sourceRoot":"","sources":["../../../src/routes/api/password-reset-confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,YAAY,EAKjB,MAAM,aAAa,CAAA;AAEpB,wBAAsB,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0BnE"}
@@ -14,6 +14,6 @@ export async function POST(context) {
14
14
  token,
15
15
  newPassword: password,
16
16
  });
17
- return statusRedirect(context, result, options.loginPath, { reset: 'success' }, options.resetPasswordPath, { token });
17
+ return statusRedirect(context, result, options.loginPath, { reset: 'success' }, options.resetPasswordPath, { token }, 'password');
18
18
  }
19
19
  //# sourceMappingURL=password-reset-confirm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"password-reset-confirm.js","sourceRoot":"","sources":["../../../src/routes/api/password-reset-confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,SAAS,EAET,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,cAAc,GACd,MAAM,aAAa,CAAA;AAEpB,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAqB;IAC/C,MAAM,aAAa,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;IAClD,IAAI,aAAa;QAAE,OAAO,aAAa,CAAA;IAEvC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAA;IAC3B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAClD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjD,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACxE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;QAC/D,KAAK;QACL,WAAW,EAAE,QAAQ;KACrB,CAAC,CAAA;IAEF,OAAO,cAAc,CACpB,OAAO,EACP,MAAM,EACN,OAAO,CAAC,SAAS,EACjB,EAAE,KAAK,EAAE,SAAS,EAAE,EACpB,OAAO,CAAC,iBAAiB,EACzB,EAAE,KAAK,EAAE,CACT,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"password-reset-confirm.js","sourceRoot":"","sources":["../../../src/routes/api/password-reset-confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,SAAS,EAET,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,cAAc,GACd,MAAM,aAAa,CAAA;AAEpB,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAqB;IAC/C,MAAM,aAAa,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;IAClD,IAAI,aAAa;QAAE,OAAO,aAAa,CAAA;IAEvC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAA;IAC3B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAClD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjD,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACxE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;QAC/D,KAAK;QACL,WAAW,EAAE,QAAQ;KACrB,CAAC,CAAA;IAEF,OAAO,cAAc,CACpB,OAAO,EACP,MAAM,EACN,OAAO,CAAC,SAAS,EACjB,EAAE,KAAK,EAAE,SAAS,EAAE,EACpB,OAAO,CAAC,iBAAiB,EACzB,EAAE,KAAK,EAAE,EACT,UAAU,CACV,CAAA;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"password-reset-request.d.ts","sourceRoot":"","sources":["../../../src/routes/api/password-reset-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,YAAY,EAKjB,MAAM,aAAa,CAAA;AAEpB,wBAAsB,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqBnE"}
1
+ {"version":3,"file":"password-reset-request.d.ts","sourceRoot":"","sources":["../../../src/routes/api/password-reset-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,YAAY,EAKjB,MAAM,aAAa,CAAA;AAEpB,wBAAsB,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsBnE"}
@@ -10,6 +10,6 @@ export async function POST(context) {
10
10
  const form = await readForm(context);
11
11
  const email = await readFormString(form, 'email');
12
12
  const result = await authClient(context).password.reset.request({ email });
13
- return statusRedirect(context, result, options.forgotPasswordPath, { sent: '1' }, options.forgotPasswordPath, { email });
13
+ return statusRedirect(context, result, options.forgotPasswordPath, { sent: '1' }, options.forgotPasswordPath, { email }, 'password');
14
14
  }
15
15
  //# sourceMappingURL=password-reset-request.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"password-reset-request.js","sourceRoot":"","sources":["../../../src/routes/api/password-reset-request.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,SAAS,EAET,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,cAAc,GACd,MAAM,aAAa,CAAA;AAEpB,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAqB;IAC/C,MAAM,aAAa,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;IAClD,IAAI,aAAa;QAAE,OAAO,aAAa,CAAA;IAEvC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAA;IAC3B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAClD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IAE1E,OAAO,cAAc,CACpB,OAAO,EACP,MAAM,EACN,OAAO,CAAC,kBAAkB,EAC1B,EAAE,IAAI,EAAE,GAAG,EAAE,EACb,OAAO,CAAC,kBAAkB,EAC1B,EAAE,KAAK,EAAE,CACT,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"password-reset-request.js","sourceRoot":"","sources":["../../../src/routes/api/password-reset-request.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,SAAS,EAET,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,cAAc,GACd,MAAM,aAAa,CAAA;AAEpB,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAqB;IAC/C,MAAM,aAAa,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;IAClD,IAAI,aAAa;QAAE,OAAO,aAAa,CAAA;IAEvC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAA;IAC3B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAClD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAA;IACpC,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IAE1E,OAAO,cAAc,CACpB,OAAO,EACP,MAAM,EACN,OAAO,CAAC,kBAAkB,EAC1B,EAAE,IAAI,EAAE,GAAG,EAAE,EACb,OAAO,CAAC,kBAAkB,EAC1B,EAAE,KAAK,EAAE,EACT,UAAU,CACV,CAAA;AACF,CAAC"}
@@ -1,12 +1,13 @@
1
1
  import { getConfig } from '../../config-store.js';
2
2
  import { type AuthClientCodeResult, type AuthClientOkResult, authClient } from '../../server.js';
3
+ import { type AuthAnalyticsProvider } from '../../utils/analytics.js';
3
4
  import { type RouteContext, readForm, redirectTo } from '../flow.js';
4
5
  export declare function readFormString(form: FormData, key: string, options?: {
5
6
  trim?: boolean;
6
7
  }): Promise<string>;
7
8
  export declare function rejectCrossSitePost(context: RouteContext): Response | null;
8
- export declare function callbackRedirect(context: RouteContext, result: AuthClientCodeResult, errorPath: string, errorParams: Record<string, string | undefined>, provider?: 'password' | 'email-code'): Response;
9
- export declare function statusRedirect(context: RouteContext, result: AuthClientOkResult, successPath: string, successParams: Record<string, string | undefined>, errorPath: string, errorParams: Record<string, string | undefined>): Response;
9
+ export declare function callbackRedirect(context: RouteContext, result: AuthClientCodeResult, errorPath: string, errorParams: Record<string, string | undefined>, provider?: 'password' | 'email-code'): Promise<Response> | Response;
10
+ export declare function statusRedirect(context: RouteContext, result: AuthClientOkResult, successPath: string, successParams: Record<string, string | undefined>, errorPath: string, errorParams: Record<string, string | undefined>, provider?: AuthAnalyticsProvider): Response;
10
11
  export type { RouteContext };
11
12
  export { authClient, getConfig, readForm, redirectTo };
12
13
  //# sourceMappingURL=shared.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/routes/api/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEhG,OAAO,EAAE,KAAK,YAAY,EAAE,QAAQ,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAA;AAItF,wBAAsB,cAAc,CACnC,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,GAAG,IAAI,CAe1E;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,oBAAoB,EAC5B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,QAAQ,CAAC,EAAE,UAAU,GAAG,YAAY,GAClC,QAAQ,CAgCV;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACjD,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAC7C,QAAQ,CAKV;AAyBD,YAAY,EAAE,YAAY,EAAE,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAA"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/routes/api/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAEjD,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAChG,OAAO,EACN,KAAK,qBAAqB,EAI1B,MAAM,0BAA0B,CAAA;AAWjC,OAAO,EAAE,KAAK,YAAY,EAAE,QAAQ,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAA;AAItF,wBAAsB,cAAc,CACnC,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,GAAG,IAAI,CAe1E;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,oBAAoB,EAC5B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,QAAQ,CAAC,EAAE,UAAU,GAAG,YAAY,GAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAsC9B;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACjD,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC/C,QAAQ,CAAC,EAAE,qBAAqB,GAC9B,QAAQ,CAMV;AAyBD,YAAY,EAAE,YAAY,EAAE,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAA"}
@@ -1,6 +1,11 @@
1
1
  import { getConfig } from '../../config-store.js';
2
+ import { getRuntimeEnv } from '../../runtime.js';
2
3
  import { authClient } from '../../server.js';
3
- import { getCookie, setCookie } from '../../utils/cookies.js';
4
+ import { analyticsAvailable, emitAuthFailureEvent, emitAuthSuccessEvent, } from '../../utils/analytics.js';
5
+ import { clearAuthFlowCookies, getCookie, setCookie, setIdentityCookie, setTokenCookies, } from '../../utils/cookies.js';
6
+ import { createIdentityCookiePayload, signIdentityCookie } from '../../utils/identity-cookie.js';
7
+ import { exchangeCode } from '../../utils/openauth.js';
8
+ import { resolveWaitUntil } from '../../utils/wait-until.js';
4
9
  import { readForm, redirectLocation, redirectTo } from '../flow.js';
5
10
  const CROSS_SITE_POST_MESSAGE = 'Cross-site POST form submissions are forbidden';
6
11
  export async function readFormString(form, key, options = {}) {
@@ -24,13 +29,18 @@ export function rejectCrossSitePost(context) {
24
29
  }
25
30
  export function callbackRedirect(context, result, errorPath, errorParams, provider) {
26
31
  if ('error' in result) {
32
+ queueAuthFailure(context, provider, result.error);
27
33
  return routeResult(context, errorPath, { ...errorParams, error: result.error }, result.error);
28
34
  }
29
35
  const options = getConfig();
30
36
  const state = getCookie(context.request, `${options.cookiePrefix}_state`);
31
37
  if (!state) {
38
+ queueAuthFailure(context, provider, 'missing_auth_flow');
32
39
  return routeResult(context, errorPath, { ...errorParams, error: 'missing_auth_flow' }, 'missing_auth_flow');
33
40
  }
41
+ if (wantsJson(context)) {
42
+ return completeJsonAuth(context, result.code, errorPath, errorParams, provider);
43
+ }
34
44
  const response = mutableHeaders(routeResult(context, options.callbackPath, {
35
45
  code: result.code,
36
46
  state,
@@ -41,8 +51,9 @@ export function callbackRedirect(context, result, errorPath, errorParams, provid
41
51
  }
42
52
  return response;
43
53
  }
44
- export function statusRedirect(context, result, successPath, successParams, errorPath, errorParams) {
54
+ export function statusRedirect(context, result, successPath, successParams, errorPath, errorParams, provider) {
45
55
  if ('error' in result) {
56
+ queueAuthFailure(context, provider, result.error);
46
57
  return routeResult(context, errorPath, { ...errorParams, error: result.error }, result.error);
47
58
  }
48
59
  return routeResult(context, successPath, successParams);
@@ -70,6 +81,54 @@ function mutableHeaders(response) {
70
81
  headers: new Headers(response.headers),
71
82
  });
72
83
  }
84
+ async function completeJsonAuth(context, code, errorPath, errorParams, provider) {
85
+ const options = getConfig();
86
+ const codeVerifier = getCookie(context.request, `${options.cookiePrefix}_pkce`);
87
+ if (!codeVerifier) {
88
+ queueAuthFailure(context, provider, 'missing_auth_flow');
89
+ return routeResult(context, errorPath, { ...errorParams, error: 'missing_auth_flow' }, 'missing_auth_flow');
90
+ }
91
+ const tokenResponse = await exchangeCode(context.url, code, codeVerifier, provider);
92
+ if (!tokenResponse) {
93
+ queueAuthFailure(context, provider, 'token_exchange_failed');
94
+ return routeResult(context, errorPath, { ...errorParams, error: 'auth_failed' }, 'auth_failed');
95
+ }
96
+ const redirectPath = getCookie(context.request, `${options.cookiePrefix}_redirect`) || '/';
97
+ const redirect = new URL(redirectPath, context.url.origin).toString();
98
+ const response = mutableHeaders(Response.json({ ok: true, redirect }));
99
+ setTokenCookies(response, options, tokenResponse.accessToken, tokenResponse.refreshToken);
100
+ clearAuthFlowCookies(response, options);
101
+ await setOptionalIdentityCookie(response, tokenResponse.claims.sub);
102
+ if (analyticsAvailable(context)) {
103
+ resolveWaitUntil(context)(emitAuthSuccessEvent(context, {
104
+ provider: tokenResponse.provider,
105
+ isNewUser: tokenResponse.isNewUser,
106
+ authPath: context.url.pathname,
107
+ identityUserId: tokenResponse.claims.sub,
108
+ }));
109
+ }
110
+ return response;
111
+ }
112
+ function queueAuthFailure(context, provider, reason) {
113
+ if (!analyticsAvailable(context))
114
+ return;
115
+ resolveWaitUntil(context)(emitAuthFailureEvent(context, {
116
+ provider,
117
+ reason,
118
+ authPath: context.url.pathname,
119
+ }));
120
+ }
121
+ async function setOptionalIdentityCookie(response, subject) {
122
+ const secret = getRuntimeEnv().GL_IDENTITY_SECRET;
123
+ if (typeof secret !== 'string' || !secret)
124
+ return;
125
+ try {
126
+ setIdentityCookie(response, await signIdentityCookie(await createIdentityCookiePayload(subject, secret), secret));
127
+ }
128
+ catch {
129
+ // Keep login working if the optional analytics identity secret is misconfigured.
130
+ }
131
+ }
73
132
  function authFlowCookieAttrs() {
74
133
  return {
75
134
  path: '/',
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/routes/api/shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/routes/api/shared.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/routes/callback.ts"],"names":[],"mappings":"AAcA,UAAU,eAAe;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAKhC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;CAC/C;AAOD,wBAAsB,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqFrE"}
1
+ {"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/routes/callback.ts"],"names":[],"mappings":"AAkBA,UAAU,eAAe;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAKhC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;CAC/C;AAoCD,wBAAsB,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0GrE"}
@@ -1,6 +1,6 @@
1
1
  import { getConfig } from '../config-store.js';
2
2
  import { getRuntimeEnv } from '../runtime.js';
3
- import { analyticsAvailable, emitAuthEvent } from '../utils/analytics.js';
3
+ import { analyticsAvailable, emitAuthFailureEvent, emitAuthSuccessEvent, } from '../utils/analytics.js';
4
4
  import { clearAuthFlowCookies, getCookie, setIdentityCookie, setTokenCookies, } from '../utils/cookies.js';
5
5
  import { createIdentityCookiePayload, signIdentityCookie } from '../utils/identity-cookie.js';
6
6
  import { exchangeCode } from '../utils/openauth.js';
@@ -10,33 +10,71 @@ function getIdentitySecret() {
10
10
  const secret = getRuntimeEnv().GL_IDENTITY_SECRET;
11
11
  return typeof secret === 'string' && secret.length > 0 ? secret : null;
12
12
  }
13
+ function wantsJson(request) {
14
+ const accept = request.headers.get('accept') ?? '';
15
+ return accept
16
+ .split(',')
17
+ .some((value) => value.trim().toLowerCase().split(';')[0] === 'application/json');
18
+ }
19
+ function loginErrorResponse(request, origin, loginPath) {
20
+ const redirect = new URL(`${loginPath}?error=auth_failed`, origin).toString();
21
+ if (wantsJson(request)) {
22
+ return Response.json({ error: 'auth_failed', redirect }, { status: 400 });
23
+ }
24
+ return Response.redirect(redirect, 302);
25
+ }
26
+ function queueAuthFailure(context, provider, reason) {
27
+ if (!analyticsAvailable(context))
28
+ return;
29
+ const promise = emitAuthFailureEvent(context, {
30
+ provider,
31
+ reason,
32
+ authPath: context.url.pathname,
33
+ });
34
+ resolveWaitUntil(context)(promise);
35
+ }
13
36
  export async function GET(context) {
14
37
  const options = getConfig();
15
38
  const { url, request } = context;
16
39
  const code = url.searchParams.get('code');
17
40
  const state = url.searchParams.get('state');
41
+ const error = url.searchParams.get('error');
18
42
  // Verify CSRF state
19
43
  const storedState = getCookie(request, `${options.cookiePrefix}_state`);
20
44
  if (!state || !storedState || !verifyState(state, storedState)) {
45
+ queueAuthFailure(context, getCookie(request, `${options.cookiePrefix}_provider`), 'missing_auth_flow');
21
46
  return new Response('Invalid state parameter', { status: 403 });
22
47
  }
23
48
  // Read PKCE verifier
24
49
  const codeVerifier = getCookie(request, `${options.cookiePrefix}_pkce`);
25
50
  if (!codeVerifier) {
51
+ queueAuthFailure(context, getCookie(request, `${options.cookiePrefix}_provider`), 'missing_auth_flow');
26
52
  return new Response('Missing PKCE verifier', { status: 400 });
27
53
  }
28
54
  const provider = getCookie(request, `${options.cookiePrefix}_provider`);
29
55
  const authPath = getCookie(request, `${options.cookiePrefix}_auth_path`) || url.pathname;
56
+ if (error) {
57
+ queueAuthFailure(context, provider, error);
58
+ return loginErrorResponse(request, url.origin, options.loginPath);
59
+ }
60
+ if (!code) {
61
+ queueAuthFailure(context, provider, 'invalid_request');
62
+ return loginErrorResponse(request, url.origin, options.loginPath);
63
+ }
30
64
  // Exchange code for tokens
31
65
  const tokenResponse = await exchangeCode(url, code, codeVerifier, provider === 'google' || provider === 'password' || provider === 'email-code'
32
66
  ? provider
33
67
  : undefined);
34
68
  if (!tokenResponse) {
35
- return Response.redirect(new URL(`${options.loginPath}?error=auth_failed`, url.origin).toString(), 302);
69
+ queueAuthFailure(context, provider, 'token_exchange_failed');
70
+ return loginErrorResponse(request, url.origin, options.loginPath);
36
71
  }
37
72
  // Build redirect response
38
73
  const redirectPath = getCookie(request, `${options.cookiePrefix}_redirect`) || '/';
39
- const response = Response.redirect(new URL(redirectPath, url.origin).toString(), 302);
74
+ const redirect = new URL(redirectPath, url.origin).toString();
75
+ const response = wantsJson(request)
76
+ ? Response.json({ ok: true, redirect })
77
+ : Response.redirect(redirect, 302);
40
78
  // Mutable copy for setting cookies
41
79
  const mutableResponse = new Response(response.body, {
42
80
  status: response.status,
@@ -62,12 +100,11 @@ export async function GET(context) {
62
100
  // the root cause of the missing `login_completed` / `signup_completed`
63
101
  // rows in `gl_conversion_events` after the Fronts cutover.
64
102
  if (analyticsAvailable(context)) {
65
- const eventName = tokenResponse.isNewUser ? 'signup_completed' : 'login_completed';
66
- const promise = emitAuthEvent(context, eventName, {
103
+ const promise = emitAuthSuccessEvent(context, {
67
104
  provider: tokenResponse.provider,
68
105
  isNewUser: tokenResponse.isNewUser,
69
- auth_path: authPath,
70
- identity_user_id: tokenResponse.claims.sub,
106
+ authPath,
107
+ identityUserId: tokenResponse.claims.sub,
71
108
  });
72
109
  resolveWaitUntil(context)(promise);
73
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"callback.js","sourceRoot":"","sources":["../../src/routes/callback.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"callback.js","sourceRoot":"","sources":["../../src/routes/callback.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,12 @@ import type { ResolvedAuthOptions } from '../options.js';
2
2
  export interface RouteContext {
3
3
  request: Request;
4
4
  url: URL;
5
+ locals?: {
6
+ cfContext?: {
7
+ waitUntil?: (promise: Promise<unknown>) => void;
8
+ };
9
+ };
10
+ waitUntil?: (promise: Promise<unknown>) => void;
5
11
  }
6
12
  export declare function withAuthFlowCookies(context: RouteContext, options: ResolvedAuthOptions, response: Response, redirectPath: string, provider?: string): Promise<Response>;
7
13
  export declare function redirectLocation(context: RouteContext, pathname: string, params: Record<string, string | undefined>): string;
@@ -1 +1 @@
1
- {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/routes/flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAIxD,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;CACR;AAED,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC,CAYnB;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACxC,MAAM,CAMR;AAED,wBAAgB,UAAU,CACzB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC1C,MAAM,SAAM,GACV,QAAQ,CAEV;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAEvE"}
1
+ {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/routes/flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAIxD,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;IACR,MAAM,CAAC,EAAE;QACR,SAAS,CAAC,EAAE;YACX,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;SAC/C,CAAA;KACD,CAAA;IACD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;CAC/C;AAED,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC,CAYnB;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACxC,MAAM,CAMR;AAED,wBAAgB,UAAU,CACzB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC1C,MAAM,SAAM,GACV,QAAQ,CAEV;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAEvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/routes/flow.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAOrD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,OAAqB,EACrB,OAA4B,EAC5B,QAAkB,EAClB,YAAoB,EACpB,QAAiB;IAEjB,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;IAC9C,MAAM,OAAO,GAAG,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC3C,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,OAAO,EAAE,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;KACtC,CAAC,CAAA;IACF,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE;QAC/F,QAAQ,EAAE,GAAG;QACb,mBAAmB,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC;KAC3C,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC/B,OAAqB,EACrB,QAAgB,EAChB,MAA0C;IAE1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACpD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK;YAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC/C,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,UAAU,CACzB,OAAqB,EACrB,QAAgB,EAChB,MAA0C,EAC1C,MAAM,GAAG,GAAG;IAEZ,OAAO,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;AAC9E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAqB;IACnD,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAA;AAClC,CAAC"}
1
+ {"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/routes/flow.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAarD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,OAAqB,EACrB,OAA4B,EAC5B,QAAkB,EAClB,YAAoB,EACpB,QAAiB;IAEjB,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;IAC9C,MAAM,OAAO,GAAG,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC3C,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,OAAO,EAAE,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;KACtC,CAAC,CAAA;IACF,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE;QAC/F,QAAQ,EAAE,GAAG;QACb,mBAAmB,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC;KAC3C,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC/B,OAAqB,EACrB,QAAgB,EAChB,MAA0C;IAE1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACpD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK;YAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC/C,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,UAAU,CACzB,OAAqB,EACrB,QAAgB,EAChB,MAA0C,EAC1C,MAAM,GAAG,GAAG;IAEZ,OAAO,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;AAC9E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAqB;IACnD,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAA;AAClC,CAAC"}
@@ -1,4 +1,21 @@
1
+ export type AuthAnalyticsProvider = 'google' | 'email-code' | 'password' | 'unknown';
2
+ export interface AuthSuccessEventInput {
3
+ provider: AuthAnalyticsProvider | string | null | undefined;
4
+ isNewUser: boolean;
5
+ authPath: string;
6
+ identityUserId: string;
7
+ }
8
+ export interface AuthFailureEventInput {
9
+ provider: AuthAnalyticsProvider | string | null | undefined;
10
+ reason: string | null | undefined;
11
+ authPath: string;
12
+ }
1
13
  export declare function emitAuthEvent(context: unknown, eventName: string, data: Record<string, unknown>): Promise<void>;
14
+ export declare function normalizeAuthProvider(provider: AuthAnalyticsProvider | string | null | undefined): AuthAnalyticsProvider;
15
+ export declare function normalizeAuthFailureReason(reason: string | null | undefined): string;
16
+ export declare function authSuccessEventName(isNewUser: boolean): 'signup_completed' | 'login_completed';
17
+ export declare function emitAuthSuccessEvent(context: unknown, input: AuthSuccessEventInput): Promise<void>;
18
+ export declare function emitAuthFailureEvent(context: unknown, input: AuthFailureEventInput): Promise<void>;
2
19
  export declare function analyticsAvailable(context: {
3
20
  locals?: Record<string, unknown>;
4
21
  }): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"AA6BA,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,OAAO,CAGzF;AAED,wBAAsB,cAAc,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAChC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,eAAe,IAAI,IAAI,CAGtC"}
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAA;AAEpF,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC3D,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC3D,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACjC,QAAQ,EAAE,MAAM,CAAA;CAChB;AAiDD,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GACzD,qBAAqB,CAEvB;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGpF;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,kBAAkB,GAAG,iBAAiB,CAE/F;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,OAAO,CAGzF;AAED,wBAAsB,cAAc,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAChC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,eAAe,IAAI,IAAI,CAItC"}
@@ -1,6 +1,21 @@
1
1
  let trackFn = null;
2
2
  let resolved = false;
3
+ let resolvePromise = null;
3
4
  const ANALYTICS_UTILS_MODULE = '@growth-labs/analytics/utils';
5
+ const AUTH_PROVIDERS = new Set(['google', 'email-code', 'password']);
6
+ const AUTH_FAILURE_REASONS = new Set([
7
+ 'auth_failed',
8
+ 'invalid_credentials',
9
+ 'invalid_code',
10
+ 'invalid_request',
11
+ 'invalid_response',
12
+ 'invalid_token',
13
+ 'missing_auth_flow',
14
+ 'oauth_denied',
15
+ 'request_failed',
16
+ 'send_failed',
17
+ 'token_exchange_failed',
18
+ ]);
4
19
  async function importOptionalModule(modulePath) {
5
20
  try {
6
21
  return (await import(/* @vite-ignore */ modulePath));
@@ -12,18 +27,23 @@ async function importOptionalModule(modulePath) {
12
27
  async function resolveAnalytics() {
13
28
  if (resolved)
14
29
  return;
15
- resolved = true;
16
- const mod = await importOptionalModule(ANALYTICS_UTILS_MODULE);
17
- let candidate;
18
- try {
19
- candidate = mod?.trackServerEvent;
20
- }
21
- catch {
22
- candidate = undefined;
23
- }
24
- if (typeof candidate === 'function') {
25
- trackFn = candidate;
30
+ if (!resolvePromise) {
31
+ resolvePromise = (async () => {
32
+ const mod = await importOptionalModule(ANALYTICS_UTILS_MODULE);
33
+ let candidate;
34
+ try {
35
+ candidate = mod?.trackServerEvent;
36
+ }
37
+ catch {
38
+ candidate = undefined;
39
+ }
40
+ if (typeof candidate === 'function') {
41
+ trackFn = candidate;
42
+ }
43
+ resolved = true;
44
+ })();
26
45
  }
46
+ await resolvePromise;
27
47
  }
28
48
  export async function emitAuthEvent(context, eventName, data) {
29
49
  await resolveAnalytics();
@@ -31,6 +51,32 @@ export async function emitAuthEvent(context, eventName, data) {
31
51
  return;
32
52
  return trackFn(context, eventName, data);
33
53
  }
54
+ export function normalizeAuthProvider(provider) {
55
+ return provider && AUTH_PROVIDERS.has(provider) ? provider : 'unknown';
56
+ }
57
+ export function normalizeAuthFailureReason(reason) {
58
+ if (reason === 'access_denied')
59
+ return 'oauth_denied';
60
+ return reason && AUTH_FAILURE_REASONS.has(reason) ? reason : 'auth_failed';
61
+ }
62
+ export function authSuccessEventName(isNewUser) {
63
+ return isNewUser ? 'signup_completed' : 'login_completed';
64
+ }
65
+ export async function emitAuthSuccessEvent(context, input) {
66
+ return emitAuthEvent(context, authSuccessEventName(input.isNewUser), {
67
+ provider: normalizeAuthProvider(input.provider),
68
+ isNewUser: input.isNewUser,
69
+ auth_path: input.authPath,
70
+ identity_user_id: input.identityUserId,
71
+ });
72
+ }
73
+ export async function emitAuthFailureEvent(context, input) {
74
+ return emitAuthEvent(context, 'auth_failed', {
75
+ provider: normalizeAuthProvider(input.provider),
76
+ reason: normalizeAuthFailureReason(input.reason),
77
+ auth_path: input.authPath,
78
+ });
79
+ }
34
80
  export function analyticsAvailable(context) {
35
81
  void context;
36
82
  return true;
@@ -41,5 +87,6 @@ export async function trackAuthEvent(context, eventName, data = {}) {
41
87
  export function _resetAnalytics() {
42
88
  trackFn = null;
43
89
  resolved = false;
90
+ resolvePromise = null;
44
91
  }
45
92
  //# sourceMappingURL=analytics.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"AAEA,IAAI,OAAO,GAAmB,IAAI,CAAA;AAClC,IAAI,QAAQ,GAAG,KAAK,CAAA;AACpB,MAAM,sBAAsB,GAAG,8BAA8B,CAAA;AAE7D,KAAK,UAAU,oBAAoB,CAAC,UAAkB;IACrD,IAAI,CAAC;QACJ,OAAO,CAAC,MAAM,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAA4B,CAAA;IAChF,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;AACF,CAAC;AAED,KAAK,UAAU,gBAAgB;IAC9B,IAAI,QAAQ;QAAE,OAAM;IACpB,QAAQ,GAAG,IAAI,CAAA;IACf,MAAM,GAAG,GAAG,MAAM,oBAAoB,CAAC,sBAAsB,CAAC,CAAA;IAC9D,IAAI,SAAkB,CAAA;IACtB,IAAI,CAAC;QACJ,SAAS,GAAG,GAAG,EAAE,gBAAgB,CAAA;IAClC,CAAC;IAAC,MAAM,CAAC;QACR,SAAS,GAAG,SAAS,CAAA;IACtB,CAAC;IACD,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;QACrC,OAAO,GAAG,SAAoB,CAAA;IAC/B,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,OAAgB,EAChB,SAAiB,EACjB,IAA6B;IAE7B,MAAM,gBAAgB,EAAE,CAAA;IACxB,IAAI,CAAC,OAAO;QAAE,OAAM;IACpB,OAAO,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAA6C;IAC/E,KAAK,OAAO,CAAA;IACZ,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,OAAgB,EAChB,SAAiB,EACjB,OAAgC,EAAE;IAElC,OAAO,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,CAAA;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe;IAC9B,OAAO,GAAG,IAAI,CAAA;IACd,QAAQ,GAAG,KAAK,CAAA;AACjB,CAAC"}
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export { emitAuthEvent, trackAuthEvent } from './analytics.js';
1
+ export { type AuthAnalyticsProvider, type AuthFailureEventInput, type AuthSuccessEventInput, authSuccessEventName, emitAuthEvent, emitAuthFailureEvent, emitAuthSuccessEvent, normalizeAuthFailureReason, normalizeAuthProvider, trackAuthEvent, } from './analytics.js';
2
2
  export { type BridgeMigrationOptions, type BridgeMigrationStatement, buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
3
3
  export { signIdentityCookie, verifyIdentityCookie } from './identity-cookie.js';
4
4
  export { getAuthorizationUrl, getMagicLinkUrl, getPasswordLoginUrl } from './openauth.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC9D,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
@@ -1,4 +1,4 @@
1
- export { emitAuthEvent, trackAuthEvent } from './analytics.js';
1
+ export { authSuccessEventName, emitAuthEvent, emitAuthFailureEvent, emitAuthSuccessEvent, normalizeAuthFailureReason, normalizeAuthProvider, trackAuthEvent, } from './analytics.js';
2
2
  export { buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
3
3
  export { signIdentityCookie, verifyIdentityCookie } from './identity-cookie.js';
4
4
  export { getAuthorizationUrl, getMagicLinkUrl, getPasswordLoginUrl } from './openauth.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC9D,OAAO,EAGN,gCAAgC,EAChC,mCAAmC,EACnC,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAGN,gCAAgC,EAChC,mCAAmC,EACnC,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-labs/auth",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
@@ -62,7 +62,7 @@
62
62
  "typescript": "^5.8.0",
63
63
  "vite": "^7.3.2",
64
64
  "vitest": "^3.0.0",
65
- "@growth-labs/analytics": "0.5.0"
65
+ "@growth-labs/analytics": "0.5.1"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsc",
@@ -29,5 +29,6 @@ export async function POST(context: RouteContext): Promise<Response> {
29
29
  { email, sent: '1', redirect },
30
30
  options.verifyCodePath,
31
31
  { email, redirect },
32
+ 'email-code',
32
33
  )
33
34
  }
@@ -32,5 +32,6 @@ export async function POST(context: RouteContext): Promise<Response> {
32
32
  { reset: 'success' },
33
33
  options.resetPasswordPath,
34
34
  { token },
35
+ 'password',
35
36
  )
36
37
  }
@@ -28,5 +28,6 @@ export async function POST(context: RouteContext): Promise<Response> {
28
28
  { sent: '1' },
29
29
  options.forgotPasswordPath,
30
30
  { email },
31
+ 'password',
31
32
  )
32
33
  }
@@ -1,6 +1,22 @@
1
1
  import { getConfig } from '../../config-store.js'
2
+ import { getRuntimeEnv } from '../../runtime.js'
2
3
  import { type AuthClientCodeResult, type AuthClientOkResult, authClient } from '../../server.js'
3
- import { getCookie, setCookie } from '../../utils/cookies.js'
4
+ import {
5
+ type AuthAnalyticsProvider,
6
+ analyticsAvailable,
7
+ emitAuthFailureEvent,
8
+ emitAuthSuccessEvent,
9
+ } from '../../utils/analytics.js'
10
+ import {
11
+ clearAuthFlowCookies,
12
+ getCookie,
13
+ setCookie,
14
+ setIdentityCookie,
15
+ setTokenCookies,
16
+ } from '../../utils/cookies.js'
17
+ import { createIdentityCookiePayload, signIdentityCookie } from '../../utils/identity-cookie.js'
18
+ import { exchangeCode } from '../../utils/openauth.js'
19
+ import { resolveWaitUntil } from '../../utils/wait-until.js'
4
20
  import { type RouteContext, readForm, redirectLocation, redirectTo } from '../flow.js'
5
21
 
6
22
  const CROSS_SITE_POST_MESSAGE = 'Cross-site POST form submissions are forbidden'
@@ -37,14 +53,16 @@ export function callbackRedirect(
37
53
  errorPath: string,
38
54
  errorParams: Record<string, string | undefined>,
39
55
  provider?: 'password' | 'email-code',
40
- ): Response {
56
+ ): Promise<Response> | Response {
41
57
  if ('error' in result) {
58
+ queueAuthFailure(context, provider, result.error)
42
59
  return routeResult(context, errorPath, { ...errorParams, error: result.error }, result.error)
43
60
  }
44
61
 
45
62
  const options = getConfig()
46
63
  const state = getCookie(context.request, `${options.cookiePrefix}_state`)
47
64
  if (!state) {
65
+ queueAuthFailure(context, provider, 'missing_auth_flow')
48
66
  return routeResult(
49
67
  context,
50
68
  errorPath,
@@ -53,6 +71,10 @@ export function callbackRedirect(
53
71
  )
54
72
  }
55
73
 
74
+ if (wantsJson(context)) {
75
+ return completeJsonAuth(context, result.code, errorPath, errorParams, provider)
76
+ }
77
+
56
78
  const response = mutableHeaders(
57
79
  routeResult(context, options.callbackPath, {
58
80
  code: result.code,
@@ -78,8 +100,10 @@ export function statusRedirect(
78
100
  successParams: Record<string, string | undefined>,
79
101
  errorPath: string,
80
102
  errorParams: Record<string, string | undefined>,
103
+ provider?: AuthAnalyticsProvider,
81
104
  ): Response {
82
105
  if ('error' in result) {
106
+ queueAuthFailure(context, provider, result.error)
83
107
  return routeResult(context, errorPath, { ...errorParams, error: result.error }, result.error)
84
108
  }
85
109
  return routeResult(context, successPath, successParams)
@@ -119,6 +143,80 @@ function mutableHeaders(response: Response): Response {
119
143
  })
120
144
  }
121
145
 
146
+ async function completeJsonAuth(
147
+ context: RouteContext,
148
+ code: string,
149
+ errorPath: string,
150
+ errorParams: Record<string, string | undefined>,
151
+ provider?: 'password' | 'email-code',
152
+ ): Promise<Response> {
153
+ const options = getConfig()
154
+ const codeVerifier = getCookie(context.request, `${options.cookiePrefix}_pkce`)
155
+ if (!codeVerifier) {
156
+ queueAuthFailure(context, provider, 'missing_auth_flow')
157
+ return routeResult(
158
+ context,
159
+ errorPath,
160
+ { ...errorParams, error: 'missing_auth_flow' },
161
+ 'missing_auth_flow',
162
+ )
163
+ }
164
+
165
+ const tokenResponse = await exchangeCode(context.url, code, codeVerifier, provider)
166
+ if (!tokenResponse) {
167
+ queueAuthFailure(context, provider, 'token_exchange_failed')
168
+ return routeResult(context, errorPath, { ...errorParams, error: 'auth_failed' }, 'auth_failed')
169
+ }
170
+
171
+ const redirectPath = getCookie(context.request, `${options.cookiePrefix}_redirect`) || '/'
172
+ const redirect = new URL(redirectPath, context.url.origin).toString()
173
+ const response = mutableHeaders(Response.json({ ok: true, redirect }))
174
+
175
+ setTokenCookies(response, options, tokenResponse.accessToken, tokenResponse.refreshToken)
176
+ clearAuthFlowCookies(response, options)
177
+ await setOptionalIdentityCookie(response, tokenResponse.claims.sub)
178
+ if (analyticsAvailable(context)) {
179
+ resolveWaitUntil(context)(
180
+ emitAuthSuccessEvent(context, {
181
+ provider: tokenResponse.provider,
182
+ isNewUser: tokenResponse.isNewUser,
183
+ authPath: context.url.pathname,
184
+ identityUserId: tokenResponse.claims.sub,
185
+ }),
186
+ )
187
+ }
188
+
189
+ return response
190
+ }
191
+
192
+ function queueAuthFailure(
193
+ context: RouteContext,
194
+ provider: AuthAnalyticsProvider | undefined,
195
+ reason: string,
196
+ ): void {
197
+ if (!analyticsAvailable(context)) return
198
+ resolveWaitUntil(context)(
199
+ emitAuthFailureEvent(context, {
200
+ provider,
201
+ reason,
202
+ authPath: context.url.pathname,
203
+ }),
204
+ )
205
+ }
206
+
207
+ async function setOptionalIdentityCookie(response: Response, subject: string): Promise<void> {
208
+ const secret = getRuntimeEnv().GL_IDENTITY_SECRET
209
+ if (typeof secret !== 'string' || !secret) return
210
+ try {
211
+ setIdentityCookie(
212
+ response,
213
+ await signIdentityCookie(await createIdentityCookiePayload(subject, secret), secret),
214
+ )
215
+ } catch {
216
+ // Keep login working if the optional analytics identity secret is misconfigured.
217
+ }
218
+ }
219
+
122
220
  function authFlowCookieAttrs() {
123
221
  return {
124
222
  path: '/',
@@ -1,6 +1,10 @@
1
1
  import { getConfig } from '../config-store.js'
2
2
  import { getRuntimeEnv } from '../runtime.js'
3
- import { analyticsAvailable, emitAuthEvent } from '../utils/analytics.js'
3
+ import {
4
+ analyticsAvailable,
5
+ emitAuthFailureEvent,
6
+ emitAuthSuccessEvent,
7
+ } from '../utils/analytics.js'
4
8
  import {
5
9
  clearAuthFlowCookies,
6
10
  getCookie,
@@ -28,47 +32,98 @@ function getIdentitySecret(): string | null {
28
32
  return typeof secret === 'string' && secret.length > 0 ? secret : null
29
33
  }
30
34
 
35
+ function wantsJson(request: Request): boolean {
36
+ const accept = request.headers.get('accept') ?? ''
37
+ return accept
38
+ .split(',')
39
+ .some((value) => value.trim().toLowerCase().split(';')[0] === 'application/json')
40
+ }
41
+
42
+ function loginErrorResponse(request: Request, origin: string, loginPath: string): Response {
43
+ const redirect = new URL(`${loginPath}?error=auth_failed`, origin).toString()
44
+ if (wantsJson(request)) {
45
+ return Response.json({ error: 'auth_failed', redirect }, { status: 400 })
46
+ }
47
+ return Response.redirect(redirect, 302)
48
+ }
49
+
50
+ function queueAuthFailure(
51
+ context: CallbackContext,
52
+ provider: string | null,
53
+ reason: string | null,
54
+ ): void {
55
+ if (!analyticsAvailable(context)) return
56
+ const promise = emitAuthFailureEvent(context, {
57
+ provider,
58
+ reason,
59
+ authPath: context.url.pathname,
60
+ })
61
+ resolveWaitUntil(context)(promise)
62
+ }
63
+
31
64
  export async function GET(context: CallbackContext): Promise<Response> {
32
65
  const options = getConfig()
33
66
  const { url, request } = context
34
67
 
35
68
  const code = url.searchParams.get('code')
36
69
  const state = url.searchParams.get('state')
70
+ const error = url.searchParams.get('error')
37
71
 
38
72
  // Verify CSRF state
39
73
  const storedState = getCookie(request, `${options.cookiePrefix}_state`)
40
74
  if (!state || !storedState || !verifyState(state, storedState)) {
75
+ queueAuthFailure(
76
+ context,
77
+ getCookie(request, `${options.cookiePrefix}_provider`),
78
+ 'missing_auth_flow',
79
+ )
41
80
  return new Response('Invalid state parameter', { status: 403 })
42
81
  }
43
82
 
44
83
  // Read PKCE verifier
45
84
  const codeVerifier = getCookie(request, `${options.cookiePrefix}_pkce`)
46
85
  if (!codeVerifier) {
86
+ queueAuthFailure(
87
+ context,
88
+ getCookie(request, `${options.cookiePrefix}_provider`),
89
+ 'missing_auth_flow',
90
+ )
47
91
  return new Response('Missing PKCE verifier', { status: 400 })
48
92
  }
49
93
 
50
94
  const provider = getCookie(request, `${options.cookiePrefix}_provider`)
51
95
  const authPath = getCookie(request, `${options.cookiePrefix}_auth_path`) || url.pathname
52
96
 
97
+ if (error) {
98
+ queueAuthFailure(context, provider, error)
99
+ return loginErrorResponse(request, url.origin, options.loginPath)
100
+ }
101
+
102
+ if (!code) {
103
+ queueAuthFailure(context, provider, 'invalid_request')
104
+ return loginErrorResponse(request, url.origin, options.loginPath)
105
+ }
106
+
53
107
  // Exchange code for tokens
54
108
  const tokenResponse = await exchangeCode(
55
109
  url,
56
- code!,
110
+ code,
57
111
  codeVerifier,
58
112
  provider === 'google' || provider === 'password' || provider === 'email-code'
59
113
  ? provider
60
114
  : undefined,
61
115
  )
62
116
  if (!tokenResponse) {
63
- return Response.redirect(
64
- new URL(`${options.loginPath}?error=auth_failed`, url.origin).toString(),
65
- 302,
66
- )
117
+ queueAuthFailure(context, provider, 'token_exchange_failed')
118
+ return loginErrorResponse(request, url.origin, options.loginPath)
67
119
  }
68
120
 
69
121
  // Build redirect response
70
122
  const redirectPath = getCookie(request, `${options.cookiePrefix}_redirect`) || '/'
71
- const response = Response.redirect(new URL(redirectPath, url.origin).toString(), 302)
123
+ const redirect = new URL(redirectPath, url.origin).toString()
124
+ const response = wantsJson(request)
125
+ ? Response.json({ ok: true, redirect })
126
+ : Response.redirect(redirect, 302)
72
127
 
73
128
  // Mutable copy for setting cookies
74
129
  const mutableResponse = new Response(response.body, {
@@ -102,12 +157,11 @@ export async function GET(context: CallbackContext): Promise<Response> {
102
157
  // the root cause of the missing `login_completed` / `signup_completed`
103
158
  // rows in `gl_conversion_events` after the Fronts cutover.
104
159
  if (analyticsAvailable(context)) {
105
- const eventName = tokenResponse.isNewUser ? 'signup_completed' : 'login_completed'
106
- const promise = emitAuthEvent(context, eventName, {
160
+ const promise = emitAuthSuccessEvent(context, {
107
161
  provider: tokenResponse.provider,
108
162
  isNewUser: tokenResponse.isNewUser,
109
- auth_path: authPath,
110
- identity_user_id: tokenResponse.claims.sub,
163
+ authPath,
164
+ identityUserId: tokenResponse.claims.sub,
111
165
  })
112
166
  resolveWaitUntil(context)(promise)
113
167
  }
@@ -5,6 +5,12 @@ import { createAuthFlow } from '../utils/openauth.js'
5
5
  export interface RouteContext {
6
6
  request: Request
7
7
  url: URL
8
+ locals?: {
9
+ cfContext?: {
10
+ waitUntil?: (promise: Promise<unknown>) => void
11
+ }
12
+ }
13
+ waitUntil?: (promise: Promise<unknown>) => void
8
14
  }
9
15
 
10
16
  export async function withAuthFlowCookies(
@@ -1,8 +1,38 @@
1
1
  type TrackFn = (context: unknown, event: string, props?: Record<string, unknown>) => Promise<void>
2
2
 
3
+ export type AuthAnalyticsProvider = 'google' | 'email-code' | 'password' | 'unknown'
4
+
5
+ export interface AuthSuccessEventInput {
6
+ provider: AuthAnalyticsProvider | string | null | undefined
7
+ isNewUser: boolean
8
+ authPath: string
9
+ identityUserId: string
10
+ }
11
+
12
+ export interface AuthFailureEventInput {
13
+ provider: AuthAnalyticsProvider | string | null | undefined
14
+ reason: string | null | undefined
15
+ authPath: string
16
+ }
17
+
3
18
  let trackFn: TrackFn | null = null
4
19
  let resolved = false
20
+ let resolvePromise: Promise<void> | null = null
5
21
  const ANALYTICS_UTILS_MODULE = '@growth-labs/analytics/utils'
22
+ const AUTH_PROVIDERS = new Set(['google', 'email-code', 'password'])
23
+ const AUTH_FAILURE_REASONS = new Set([
24
+ 'auth_failed',
25
+ 'invalid_credentials',
26
+ 'invalid_code',
27
+ 'invalid_request',
28
+ 'invalid_response',
29
+ 'invalid_token',
30
+ 'missing_auth_flow',
31
+ 'oauth_denied',
32
+ 'request_failed',
33
+ 'send_failed',
34
+ 'token_exchange_failed',
35
+ ])
6
36
 
7
37
  async function importOptionalModule(modulePath: string): Promise<Record<string, unknown> | null> {
8
38
  try {
@@ -14,17 +44,22 @@ async function importOptionalModule(modulePath: string): Promise<Record<string,
14
44
 
15
45
  async function resolveAnalytics(): Promise<void> {
16
46
  if (resolved) return
17
- resolved = true
18
- const mod = await importOptionalModule(ANALYTICS_UTILS_MODULE)
19
- let candidate: unknown
20
- try {
21
- candidate = mod?.trackServerEvent
22
- } catch {
23
- candidate = undefined
24
- }
25
- if (typeof candidate === 'function') {
26
- trackFn = candidate as TrackFn
47
+ if (!resolvePromise) {
48
+ resolvePromise = (async () => {
49
+ const mod = await importOptionalModule(ANALYTICS_UTILS_MODULE)
50
+ let candidate: unknown
51
+ try {
52
+ candidate = mod?.trackServerEvent
53
+ } catch {
54
+ candidate = undefined
55
+ }
56
+ if (typeof candidate === 'function') {
57
+ trackFn = candidate as TrackFn
58
+ }
59
+ resolved = true
60
+ })()
27
61
  }
62
+ await resolvePromise
28
63
  }
29
64
 
30
65
  export async function emitAuthEvent(
@@ -37,6 +72,44 @@ export async function emitAuthEvent(
37
72
  return trackFn(context, eventName, data)
38
73
  }
39
74
 
75
+ export function normalizeAuthProvider(
76
+ provider: AuthAnalyticsProvider | string | null | undefined,
77
+ ): AuthAnalyticsProvider {
78
+ return provider && AUTH_PROVIDERS.has(provider) ? (provider as AuthAnalyticsProvider) : 'unknown'
79
+ }
80
+
81
+ export function normalizeAuthFailureReason(reason: string | null | undefined): string {
82
+ if (reason === 'access_denied') return 'oauth_denied'
83
+ return reason && AUTH_FAILURE_REASONS.has(reason) ? reason : 'auth_failed'
84
+ }
85
+
86
+ export function authSuccessEventName(isNewUser: boolean): 'signup_completed' | 'login_completed' {
87
+ return isNewUser ? 'signup_completed' : 'login_completed'
88
+ }
89
+
90
+ export async function emitAuthSuccessEvent(
91
+ context: unknown,
92
+ input: AuthSuccessEventInput,
93
+ ): Promise<void> {
94
+ return emitAuthEvent(context, authSuccessEventName(input.isNewUser), {
95
+ provider: normalizeAuthProvider(input.provider),
96
+ isNewUser: input.isNewUser,
97
+ auth_path: input.authPath,
98
+ identity_user_id: input.identityUserId,
99
+ })
100
+ }
101
+
102
+ export async function emitAuthFailureEvent(
103
+ context: unknown,
104
+ input: AuthFailureEventInput,
105
+ ): Promise<void> {
106
+ return emitAuthEvent(context, 'auth_failed', {
107
+ provider: normalizeAuthProvider(input.provider),
108
+ reason: normalizeAuthFailureReason(input.reason),
109
+ auth_path: input.authPath,
110
+ })
111
+ }
112
+
40
113
  export function analyticsAvailable(context: { locals?: Record<string, unknown> }): boolean {
41
114
  void context
42
115
  return true
@@ -53,4 +126,5 @@ export async function trackAuthEvent(
53
126
  export function _resetAnalytics(): void {
54
127
  trackFn = null
55
128
  resolved = false
129
+ resolvePromise = null
56
130
  }
@@ -1,4 +1,15 @@
1
- export { emitAuthEvent, trackAuthEvent } from './analytics.js'
1
+ export {
2
+ type AuthAnalyticsProvider,
3
+ type AuthFailureEventInput,
4
+ type AuthSuccessEventInput,
5
+ authSuccessEventName,
6
+ emitAuthEvent,
7
+ emitAuthFailureEvent,
8
+ emitAuthSuccessEvent,
9
+ normalizeAuthFailureReason,
10
+ normalizeAuthProvider,
11
+ trackAuthEvent,
12
+ } from './analytics.js'
2
13
  export {
3
14
  type BridgeMigrationOptions,
4
15
  type BridgeMigrationStatement,