@civic/auth 0.9.0-beta.2 → 0.9.1-alpha.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/lib/logger.d.ts +6 -0
  3. package/dist/lib/logger.d.ts.map +1 -1
  4. package/dist/lib/logger.js +7 -0
  5. package/dist/lib/logger.js.map +1 -1
  6. package/dist/nextjs/config.d.ts +2 -4
  7. package/dist/nextjs/config.d.ts.map +1 -1
  8. package/dist/nextjs/config.js +4 -57
  9. package/dist/nextjs/config.js.map +1 -1
  10. package/dist/react-router-7/components/UserButton.d.ts +13 -0
  11. package/dist/react-router-7/components/UserButton.d.ts.map +1 -0
  12. package/dist/react-router-7/components/UserButton.js +108 -0
  13. package/dist/react-router-7/components/UserButton.js.map +1 -0
  14. package/dist/react-router-7/components/UserButtonPresentation.d.ts +10 -0
  15. package/dist/react-router-7/components/UserButtonPresentation.d.ts.map +1 -0
  16. package/dist/react-router-7/components/UserButtonPresentation.js +19 -0
  17. package/dist/react-router-7/components/UserButtonPresentation.js.map +1 -0
  18. package/dist/react-router-7/config.d.ts +69 -0
  19. package/dist/react-router-7/config.d.ts.map +1 -0
  20. package/dist/react-router-7/config.js +88 -0
  21. package/dist/react-router-7/config.js.map +1 -0
  22. package/dist/react-router-7/cookies.d.ts +40 -0
  23. package/dist/react-router-7/cookies.d.ts.map +1 -0
  24. package/dist/react-router-7/cookies.js +125 -0
  25. package/dist/react-router-7/cookies.js.map +1 -0
  26. package/dist/react-router-7/index.d.ts +10 -0
  27. package/dist/react-router-7/index.d.ts.map +1 -0
  28. package/dist/react-router-7/index.js +12 -0
  29. package/dist/react-router-7/index.js.map +1 -0
  30. package/dist/react-router-7/routeHandler.d.ts +51 -0
  31. package/dist/react-router-7/routeHandler.d.ts.map +1 -0
  32. package/dist/react-router-7/routeHandler.js +323 -0
  33. package/dist/react-router-7/routeHandler.js.map +1 -0
  34. package/dist/react-router-7/useUser.d.ts +43 -0
  35. package/dist/react-router-7/useUser.d.ts.map +1 -0
  36. package/dist/react-router-7/useUser.js +92 -0
  37. package/dist/react-router-7/useUser.js.map +1 -0
  38. package/dist/reactjs/core/GlobalAuthManager.d.ts +2 -1
  39. package/dist/reactjs/core/GlobalAuthManager.d.ts.map +1 -1
  40. package/dist/reactjs/core/GlobalAuthManager.js +16 -2
  41. package/dist/reactjs/core/GlobalAuthManager.js.map +1 -1
  42. package/dist/server/session.d.ts.map +1 -1
  43. package/dist/server/session.js +1 -0
  44. package/dist/server/session.js.map +1 -1
  45. package/dist/services/AuthenticationService.d.ts.map +1 -1
  46. package/dist/services/AuthenticationService.js +0 -5
  47. package/dist/services/AuthenticationService.js.map +1 -1
  48. package/dist/services/PKCE.d.ts.map +1 -1
  49. package/dist/services/PKCE.js +4 -1
  50. package/dist/services/PKCE.js.map +1 -1
  51. package/dist/shared/hooks/useCivicAuthConfig.d.ts +1 -1
  52. package/dist/shared/hooks/useCivicAuthConfig.d.ts.map +1 -1
  53. package/dist/shared/lib/cookieConfig.d.ts +46 -0
  54. package/dist/shared/lib/cookieConfig.d.ts.map +1 -0
  55. package/dist/shared/lib/cookieConfig.js +99 -0
  56. package/dist/shared/lib/cookieConfig.js.map +1 -0
  57. package/dist/shared/version.d.ts +1 -1
  58. package/dist/shared/version.d.ts.map +1 -1
  59. package/dist/shared/version.js +1 -1
  60. package/dist/shared/version.js.map +1 -1
  61. package/dist/types.d.ts +1 -1
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/types.js.map +1 -1
  64. package/dist/vanillajs/auth/CivicAuth.d.ts +1 -1
  65. package/dist/vanillajs/auth/CivicAuth.d.ts.map +1 -1
  66. package/dist/vanillajs/auth/CivicAuth.js +1 -0
  67. package/dist/vanillajs/auth/CivicAuth.js.map +1 -1
  68. package/dist/vanillajs/auth/handlers/MessageHandler.d.ts.map +1 -1
  69. package/dist/vanillajs/auth/handlers/MessageHandler.js +3 -0
  70. package/dist/vanillajs/auth/handlers/MessageHandler.js.map +1 -1
  71. package/package.json +13 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/react-router-7/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UnknownObject } from \"@/types.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport debug from \"debug\";\nimport {\n createReactRouterCookieConfig,\n type ReactRouterCookiesConfigObject,\n} from \"@/shared/lib/cookieConfig.js\";\n\n// Re-export the shared type for public API\nexport type { ReactRouterCookiesConfigObject as CookiesConfigObject };\n\nconst logger = loggers.reactRouter.handlers.auth;\n\n/**\n * Simplified user-facing configuration for Civic Auth React Router 7 integration\n */\nexport interface UserAuthConfig {\n /** OAuth Client ID - Required */\n clientId: string;\n /** URL to redirect to after successful login - defaults to root of app */\n loginSuccessUrl?: string;\n /** OAuth Callback URL - defaults to /auth/callback */\n callbackUrl?: string;\n /** URL to redirect to after logout - defaults to / */\n logoutUrl?: string;\n /** The public-facing base URL for your application. Required when deploying behind reverse proxies */\n baseUrl?: string;\n}\n\n/**\n * Internal logging configuration for Civic Auth\n */\nexport interface LoggingConfig {\n /** Enable logging for these namespaces (e.g. \"@civic/auth:*\" or \"@civic/auth:react-router:*\") */\n enableFor?: string;\n /** Disable logging for these namespaces */\n disableFor?: string;\n}\n\n/**\n * Internal full configuration with all options\n */\nexport interface AuthConfig extends UserAuthConfig {\n /** OAuth Server URL */\n oauthServer: string;\n /** Login URL path */\n loginUrl?: string;\n /** Refresh token URL path */\n refreshUrl?: string;\n /** User data endpoint URL path */\n userUrl?: string;\n /** OAuth scope */\n scope?: string;\n /** Routes to include in authentication checks */\n include?: string[];\n /** Routes to exclude from authentication checks */\n exclude?: string[];\n /** Environment variable overrides */\n env?: UnknownObject;\n /** Logging configuration */\n logging?: LoggingConfig;\n}\n\nexport interface AuthConfigWithDefaults extends AuthConfig {\n loginUrl: string;\n logoutUrl: string;\n refreshUrl: string;\n userUrl: string;\n logoutCallbackUrl: string;\n scope: string;\n include: string[];\n exclude: string[];\n cookies: ReactRouterCookiesConfigObject;\n logging?: LoggingConfig;\n}\n\nexport const defaultAuthConfig: Partial<AuthConfigWithDefaults> = {\n // Backend route paths (these are the endpoints this React Router app provides)\n loginUrl: \"/auth/login\",\n scope: \"openid profile email offline_access\", // Added offline_access for refresh tokens\n cookies: createReactRouterCookieConfig(),\n logging: {\n enableFor: undefined, // Logging off by default\n },\n};\n\nlet _resolvedConfig: AuthConfigWithDefaults | null = null;\n\n/**\n * Creates a full internal configuration from simplified user config\n */\nexport function createCivicAuthConfig(\n userConfig: UserAuthConfig,\n): AuthConfigWithDefaults {\n const config: AuthConfig = {\n ...userConfig,\n oauthServer: process.env.OAUTH_SERVER || \"https://auth.civic.com/oauth\",\n };\n\n return {\n ...defaultAuthConfig,\n ...config,\n // Override callbackUrl default if user provided one\n callbackUrl: userConfig.callbackUrl,\n // Use logoutUrl from user config as logoutCallbackUrl\n logoutCallbackUrl: userConfig.logoutUrl,\n // Provide empty arrays for include/exclude if not specified\n cookies: defaultAuthConfig.cookies, // Use default cookies, not user-configurable\n logging: {\n ...defaultAuthConfig.logging,\n ...config.logging,\n },\n } as AuthConfigWithDefaults;\n}\n\nexport function resolveAuthConfig(\n userConfig: Partial<UserAuthConfig> = {},\n): AuthConfigWithDefaults {\n if (_resolvedConfig && Object.keys(userConfig).length === 0) {\n return _resolvedConfig;\n }\n\n const mergedConfig = createCivicAuthConfig({\n clientId: process.env.CLIENT_ID || userConfig.clientId || \"\",\n ...userConfig,\n });\n\n logger.debug(\"Resolved config:\", JSON.stringify(mergedConfig, null, 2));\n\n if (!mergedConfig.clientId) {\n logger.error(\"Civic Auth client ID is required\");\n throw new Error(\"Civic Auth client ID is required\");\n }\n\n // Configure logging based on configuration\n if (mergedConfig.logging) {\n try {\n if (mergedConfig.logging.enableFor) {\n // Set the DEBUG environment variable\n if (typeof process !== \"undefined\" && process.env) {\n process.env.DEBUG = mergedConfig.logging.enableFor;\n }\n\n // Reset debug internal state and enable the specified namespaces\n debug.disable();\n debug.enable(mergedConfig.logging.enableFor);\n\n logger.debug(`Logging enabled for: ${mergedConfig.logging.enableFor}`);\n }\n\n if (mergedConfig.logging.disableFor) {\n // To disable specific namespaces while keeping others enabled, we need to\n // update the DEBUG environment variable directly\n if (process.env.DEBUG) {\n const currentDebug = process.env.DEBUG;\n const disablePattern = mergedConfig.logging.disableFor;\n process.env.DEBUG = currentDebug\n .split(\",\")\n .filter((pattern) => pattern !== disablePattern)\n .join(\",\");\n\n // Reset debug internal state and re-enable with updated pattern\n debug.disable();\n debug.enable(process.env.DEBUG);\n\n logger.debug(`Logging disabled for: ${disablePattern}`);\n }\n }\n } catch (e) {\n logger.error(\"Failed to configure logging:\", e);\n }\n }\n\n _resolvedConfig = mergedConfig;\n return mergedConfig;\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { CookieStorage } from "@civic/auth/server";
2
+ /**
3
+ * React Router implementation of the CookieStorage interface for Civic Auth
4
+ * Uses individual cookies for each token type instead of session storage
5
+ */
6
+ export declare class ReactRouterCookieStorage extends CookieStorage {
7
+ private cookies;
8
+ private currentRequest;
9
+ private cookieHeaders;
10
+ constructor();
11
+ /**
12
+ * Set the current request context for reading cookies
13
+ */
14
+ setRequest(request: Request): void;
15
+ /**
16
+ * Get cookie headers to be set in the response
17
+ */
18
+ getCookieHeaders(): string[];
19
+ /**
20
+ * Get a value from a cookie
21
+ * Following React Router pattern: parse returns the object we serialized
22
+ */
23
+ get(key: string): Promise<string | null>;
24
+ /**
25
+ * Set a value in a cookie
26
+ * Following React Router pattern: https://reactrouter.com/api/utils/createCookie
27
+ * cookie.serialize(object) creates the complete "Set-Cookie" header string
28
+ */
29
+ set(key: string, value: string): Promise<void>;
30
+ /**
31
+ * Remove a value from a cookie
32
+ * Following React Router pattern: serialize with empty value and past expiration date
33
+ */
34
+ remove(key: string): Promise<void>;
35
+ /**
36
+ * Delete a value from a cookie (alias for remove)
37
+ */
38
+ delete(key: string): Promise<void>;
39
+ }
40
+ //# sourceMappingURL=cookies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/react-router-7/cookies.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAOnD;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,aAAa;IACzD,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,aAAa,CAAgB;;IA4BrC;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAKlC;;OAEG;IACH,gBAAgB,IAAI,MAAM,EAAE;IAI5B;;;OAGG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA4B9C;;;;OAIG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBpD;;;OAGG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBxC;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGzC"}
@@ -0,0 +1,125 @@
1
+ import { createCookie } from "react-router";
2
+ import { CookieStorage } from "@civic/auth/server";
3
+ import { resolveAuthConfig } from "./config.js";
4
+ import { UserStorage } from "../shared/lib/types.js";
5
+ import { loggers } from "../lib/logger.js";
6
+ const logger = loggers.reactRouter.handlers.auth;
7
+ /**
8
+ * React Router implementation of the CookieStorage interface for Civic Auth
9
+ * Uses individual cookies for each token type instead of session storage
10
+ */
11
+ export class ReactRouterCookieStorage extends CookieStorage {
12
+ cookies;
13
+ currentRequest = null;
14
+ cookieHeaders = [];
15
+ constructor() {
16
+ const config = resolveAuthConfig();
17
+ super({
18
+ secure: false,
19
+ });
20
+ // Create individual cookies for each token type
21
+ this.cookies = {};
22
+ // Create cookies for OAuth tokens
23
+ Object.entries(config.cookies.tokens).forEach(([tokenType, cookieConfig]) => {
24
+ this.cookies[tokenType] = createCookie(tokenType, {
25
+ ...cookieConfig,
26
+ ...this.cookies,
27
+ });
28
+ });
29
+ // Create cookie for user data
30
+ this.cookies[UserStorage.USER] = createCookie("user", {
31
+ ...config.cookies.user,
32
+ ...this.cookies,
33
+ });
34
+ }
35
+ /**
36
+ * Set the current request context for reading cookies
37
+ */
38
+ setRequest(request) {
39
+ this.currentRequest = request;
40
+ this.cookieHeaders = []; // Reset headers for new request
41
+ }
42
+ /**
43
+ * Get cookie headers to be set in the response
44
+ */
45
+ getCookieHeaders() {
46
+ return this.cookieHeaders;
47
+ }
48
+ /**
49
+ * Get a value from a cookie
50
+ * Following React Router pattern: parse returns the object we serialized
51
+ */
52
+ async get(key) {
53
+ if (!this.currentRequest) {
54
+ logger.warn("No request context set for cookie reading");
55
+ return null;
56
+ }
57
+ const cookie = this.cookies[key];
58
+ if (!cookie) {
59
+ logger.warn(`No cookie configured for key: ${key}`);
60
+ return null;
61
+ }
62
+ try {
63
+ const cookieHeader = this.currentRequest.headers.get("Cookie");
64
+ // Parse returns the object we serialized: { value: "actual_value" }
65
+ const parsedObject = await cookie.parse(cookieHeader);
66
+ // Extract the actual value from the object
67
+ const actualValue = parsedObject?.value || null;
68
+ return actualValue;
69
+ }
70
+ catch (error) {
71
+ console.error(`Error reading cookie ${key}:`, error);
72
+ return null;
73
+ }
74
+ }
75
+ /**
76
+ * Set a value in a cookie
77
+ * Following React Router pattern: https://reactrouter.com/api/utils/createCookie
78
+ * cookie.serialize(object) creates the complete "Set-Cookie" header string
79
+ */
80
+ async set(key, value) {
81
+ const cookie = this.cookies[key];
82
+ if (!cookie) {
83
+ logger.warn(`No cookie configured for key: ${key}`);
84
+ return;
85
+ }
86
+ try {
87
+ // Following the React Router pattern: serialize an object, not a raw string
88
+ // This matches the docs example: cookie.serialize({ showBanner: true })
89
+ const cookieValue = { value };
90
+ const serializedCookie = await cookie.serialize(cookieValue);
91
+ this.cookieHeaders.push(serializedCookie);
92
+ }
93
+ catch (error) {
94
+ console.error(`Error setting cookie ${key}:`, error);
95
+ }
96
+ }
97
+ /**
98
+ * Remove a value from a cookie
99
+ * Following React Router pattern: serialize with empty value and past expiration date
100
+ */
101
+ async remove(key) {
102
+ const cookie = this.cookies[key];
103
+ if (!cookie) {
104
+ logger.warn(`No cookie configured for key: ${key}`);
105
+ return;
106
+ }
107
+ try {
108
+ // Following React Router pattern: serialize empty object with immediate expiration to delete
109
+ const serializedCookie = await cookie.serialize({ value: "" }, {
110
+ expires: new Date(0),
111
+ });
112
+ this.cookieHeaders.push(serializedCookie);
113
+ }
114
+ catch (error) {
115
+ console.error(`Error removing cookie ${key}:`, error);
116
+ }
117
+ }
118
+ /**
119
+ * Delete a value from a cookie (alias for remove)
120
+ */
121
+ async delete(key) {
122
+ await this.remove(key);
123
+ }
124
+ }
125
+ //# sourceMappingURL=cookies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/react-router-7/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAe,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC;AAEjD;;;GAGG;AACH,MAAM,OAAO,wBAAyB,SAAQ,aAAa;IACjD,OAAO,CAAyB;IAChC,cAAc,GAAmB,IAAI,CAAC;IACtC,aAAa,GAAa,EAAE,CAAC;IAErC;QACE,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;QACnC,KAAK,CAAC;YACJ,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QAEH,gDAAgD;QAChD,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAElB,kCAAkC;QAClC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAC3C,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,EAAE;YAC5B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,YAAY,CAAC,SAAS,EAAE;gBAChD,GAAG,YAAY;gBACf,GAAG,IAAI,CAAC,OAAO;aAChB,CAAC,CAAC;QACL,CAAC,CACF,CAAC;QAEF,8BAA8B;QAC9B,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE;YACpD,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI;YACtB,GAAG,IAAI,CAAC,OAAO;SAChB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,OAAgB;QACzB,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAC9B,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC,CAAC,gCAAgC;IAC3D,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;YACzD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,iCAAiC,GAAG,EAAE,CAAC,CAAC;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAE/D,oEAAoE;YACpE,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAEtD,2CAA2C;YAC3C,MAAM,WAAW,GAAG,YAAY,EAAE,KAAK,IAAI,IAAI,CAAC;YAEhD,OAAO,WAAW,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,iCAAiC,GAAG,EAAE,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,4EAA4E;YAC5E,wEAAwE;YACxE,MAAM,WAAW,GAAG,EAAE,KAAK,EAAE,CAAC;YAE9B,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;YAE7D,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,iCAAiC,GAAG,EAAE,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,6FAA6F;YAC7F,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,SAAS,CAC7C,EAAE,KAAK,EAAE,EAAE,EAAE,EACb;gBACE,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC;aACrB,CACF,CAAC;YAEF,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,yBAAyB,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;CACF","sourcesContent":["import { createCookie, type Cookie } from \"react-router\";\nimport { CookieStorage } from \"@civic/auth/server\";\nimport { resolveAuthConfig } from \"./config.js\";\nimport { UserStorage } from \"@/shared/lib/types.js\";\nimport { loggers } from \"@/lib/logger.js\";\n\nconst logger = loggers.reactRouter.handlers.auth;\n\n/**\n * React Router implementation of the CookieStorage interface for Civic Auth\n * Uses individual cookies for each token type instead of session storage\n */\nexport class ReactRouterCookieStorage extends CookieStorage {\n private cookies: Record<string, Cookie>;\n private currentRequest: Request | null = null;\n private cookieHeaders: string[] = [];\n\n constructor() {\n const config = resolveAuthConfig();\n super({\n secure: false,\n });\n\n // Create individual cookies for each token type\n this.cookies = {};\n\n // Create cookies for OAuth tokens\n Object.entries(config.cookies.tokens).forEach(\n ([tokenType, cookieConfig]) => {\n this.cookies[tokenType] = createCookie(tokenType, {\n ...cookieConfig,\n ...this.cookies,\n });\n },\n );\n\n // Create cookie for user data\n this.cookies[UserStorage.USER] = createCookie(\"user\", {\n ...config.cookies.user,\n ...this.cookies,\n });\n }\n\n /**\n * Set the current request context for reading cookies\n */\n setRequest(request: Request): void {\n this.currentRequest = request;\n this.cookieHeaders = []; // Reset headers for new request\n }\n\n /**\n * Get cookie headers to be set in the response\n */\n getCookieHeaders(): string[] {\n return this.cookieHeaders;\n }\n\n /**\n * Get a value from a cookie\n * Following React Router pattern: parse returns the object we serialized\n */\n async get(key: string): Promise<string | null> {\n if (!this.currentRequest) {\n logger.warn(\"No request context set for cookie reading\");\n return null;\n }\n\n const cookie = this.cookies[key];\n if (!cookie) {\n logger.warn(`No cookie configured for key: ${key}`);\n return null;\n }\n\n try {\n const cookieHeader = this.currentRequest.headers.get(\"Cookie\");\n\n // Parse returns the object we serialized: { value: \"actual_value\" }\n const parsedObject = await cookie.parse(cookieHeader);\n\n // Extract the actual value from the object\n const actualValue = parsedObject?.value || null;\n\n return actualValue;\n } catch (error) {\n console.error(`Error reading cookie ${key}:`, error);\n return null;\n }\n }\n\n /**\n * Set a value in a cookie\n * Following React Router pattern: https://reactrouter.com/api/utils/createCookie\n * cookie.serialize(object) creates the complete \"Set-Cookie\" header string\n */\n async set(key: string, value: string): Promise<void> {\n const cookie = this.cookies[key];\n if (!cookie) {\n logger.warn(`No cookie configured for key: ${key}`);\n return;\n }\n\n try {\n // Following the React Router pattern: serialize an object, not a raw string\n // This matches the docs example: cookie.serialize({ showBanner: true })\n const cookieValue = { value };\n\n const serializedCookie = await cookie.serialize(cookieValue);\n\n this.cookieHeaders.push(serializedCookie);\n } catch (error) {\n console.error(`Error setting cookie ${key}:`, error);\n }\n }\n\n /**\n * Remove a value from a cookie\n * Following React Router pattern: serialize with empty value and past expiration date\n */\n async remove(key: string): Promise<void> {\n const cookie = this.cookies[key];\n if (!cookie) {\n logger.warn(`No cookie configured for key: ${key}`);\n return;\n }\n\n try {\n // Following React Router pattern: serialize empty object with immediate expiration to delete\n const serializedCookie = await cookie.serialize(\n { value: \"\" },\n {\n expires: new Date(0),\n },\n );\n\n this.cookieHeaders.push(serializedCookie);\n } catch (error) {\n console.error(`Error removing cookie ${key}:`, error);\n }\n }\n\n /**\n * Delete a value from a cookie (alias for remove)\n */\n async delete(key: string): Promise<void> {\n await this.remove(key);\n }\n}\n"]}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * React Router authentication modules for Civic Auth
3
+ */
4
+ export { createRouteHandlers } from "./routeHandler.js";
5
+ export { useUser } from "./useUser.js";
6
+ export { resolveAuthConfig, createCivicAuthConfig, type AuthConfig, type AuthConfigWithDefaults, type LoggingConfig, type CookiesConfigObject, } from "./config.js";
7
+ export { ReactRouterCookieStorage } from "./cookies.js";
8
+ export { UserButton } from "./components/UserButton.js";
9
+ export type { AuthData } from "./useUser.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react-router-7/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,KAAK,mBAAmB,GACzB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAGxD,YAAY,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * React Router authentication modules for Civic Auth
3
+ */
4
+ // Export all components from the React Router package
5
+ export { createRouteHandlers } from "./routeHandler.js";
6
+ // Export hooks - simplified for React Router SSR patterns
7
+ export { useUser } from "./useUser.js";
8
+ // Export configuration and helpers
9
+ export { resolveAuthConfig, createCivicAuthConfig, } from "./config.js";
10
+ export { ReactRouterCookieStorage } from "./cookies.js";
11
+ export { UserButton } from "./components/UserButton.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react-router-7/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,sDAAsD;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,0DAA0D;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,mCAAmC;AACnC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GAKtB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC","sourcesContent":["/**\n * React Router authentication modules for Civic Auth\n */\n\n// Export all components from the React Router package\nexport { createRouteHandlers } from \"./routeHandler.js\";\n\n// Export hooks - simplified for React Router SSR patterns\nexport { useUser } from \"./useUser.js\";\n\n// Export configuration and helpers\nexport {\n resolveAuthConfig,\n createCivicAuthConfig,\n type AuthConfig,\n type AuthConfigWithDefaults,\n type LoggingConfig,\n type CookiesConfigObject,\n} from \"./config.js\";\n\nexport { ReactRouterCookieStorage } from \"./cookies.js\";\nexport { UserButton } from \"./components/UserButton.js\";\n\n// Types - simplified for React Router patterns\nexport type { AuthData } from \"./useUser.js\";\n"]}
@@ -0,0 +1,51 @@
1
+ import { type LoaderFunctionArgs } from "react-router";
2
+ import type { AuthConfig } from "./config.js";
3
+ /**
4
+ * Create auth route handlers for React Router - backend endpoints compatible with VanillaJS frontend integration
5
+ * These routes work similar to the Express example, using server-side CivicAuth SDK
6
+ */
7
+ export declare function createRouteHandlers(configOverrides?: Partial<AuthConfig>): {
8
+ createAuthLoader: () => (args: LoaderFunctionArgs) => Promise<Response>;
9
+ /**
10
+ * Login loader - backend OAuth login initiation endpoint
11
+ * Uses CivicAuth.buildLoginUrl() like Express example
12
+ */
13
+ loginLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
14
+ /**
15
+ * Callback loader - backend OAuth callback endpoint
16
+ * Uses CivicAuth.handleCallback() like Express example
17
+ */
18
+ callbackLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
19
+ /**
20
+ * Logout loader - backend logout endpoint
21
+ * Uses CivicAuth.buildLogoutRedirectUrl() and clearTokens() like Express example
22
+ */
23
+ logoutLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
24
+ /**
25
+ * User endpoint - returns current user data as JSON
26
+ * Uses CivicAuth.isLoggedIn() and getUser() like Express example
27
+ */
28
+ userLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
29
+ /**
30
+ * Refresh endpoint - refreshes access tokens
31
+ * Uses CivicAuth.refreshTokens() like Express example
32
+ */
33
+ refreshLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
34
+ /**
35
+ * Get user data from session (for SSR)
36
+ * Uses CivicAuth.isLoggedIn() and getUser() like Express example
37
+ */
38
+ getUser: (request: Request) => Promise<import("../types.js").BaseUser | null>;
39
+ /**
40
+ * Get auth data including user and config (for SSR)
41
+ * Returns user data, config, and other auth-related data needed by the app under a civic key
42
+ */
43
+ getAuthData: (request: Request) => Promise<{
44
+ civic: {
45
+ user: import("../types.js").BaseUser | null;
46
+ config: Record<string, string>;
47
+ isLoggedIn: boolean;
48
+ };
49
+ }>;
50
+ };
51
+ //# sourceMappingURL=routeHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeHandler.d.ts","sourceRoot":"","sources":["../../src/react-router-7/routeHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAIjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,eAAe,GAAE,OAAO,CAAC,UAAU,CAAM;mCA8WrD,kBAAkB;IA7QtC;;;OAGG;+BAC8B,kBAAkB;IAqBnD;;;OAGG;kCACiC,kBAAkB;IAsEtD;;;OAGG;gCAC+B,kBAAkB;IA0BpD;;;OAGG;8BAC6B,kBAAkB;IAuClD;;;OAGG;iCACgC,kBAAkB;IAmCrD;;;OAGG;uBACsB,OAAO;IAgBhC;;;OAGG;2BAC0B,OAAO;;;;;;;EAkEvC"}
@@ -0,0 +1,323 @@
1
+ import { redirect } from "react-router";
2
+ import { CivicAuth } from "@civic/auth/server";
3
+ import { ReactRouterCookieStorage } from "./cookies.js";
4
+ import { resolveAuthConfig } from "./config.js";
5
+ /**
6
+ * Create auth route handlers for React Router - backend endpoints compatible with VanillaJS frontend integration
7
+ * These routes work similar to the Express example, using server-side CivicAuth SDK
8
+ */
9
+ export function createRouteHandlers(configOverrides = {}) {
10
+ const config = resolveAuthConfig(configOverrides);
11
+ const whitelistedFrontEndConfig = [
12
+ "clientId",
13
+ "oauthServer",
14
+ "callbackUrl",
15
+ "logoutCallbackUrl",
16
+ "loginUrl",
17
+ "baseUrl",
18
+ ].reduce((acc, key) => {
19
+ acc[key] = config[key];
20
+ return acc;
21
+ }, {});
22
+ /**
23
+ * Helper to create CivicAuth instance for a request
24
+ */
25
+ const createCivicAuth = (request) => {
26
+ const cookieStorage = new ReactRouterCookieStorage();
27
+ // Set the request context for reading cookies
28
+ cookieStorage.setRequest(request);
29
+ const host = config.baseUrl ?? new URL(request.url).origin;
30
+ const civicAuth = new CivicAuth(cookieStorage, {
31
+ clientId: config.clientId,
32
+ redirectUrl: config.callbackUrl ?? `${host}/auth/callback`,
33
+ oauthServer: config.oauthServer,
34
+ postLogoutRedirectUrl: config.logoutCallbackUrl ?? `${host}/auth/logout`,
35
+ loginSuccessUrl: `${host}/auth/callback`,
36
+ });
37
+ return {
38
+ civicAuth,
39
+ // Use the original cookieStorage instance that we properly configured with setRequest()
40
+ cookieStorage,
41
+ };
42
+ };
43
+ /**
44
+ * Helper to create response with cookie headers
45
+ * Following React Router pattern: add serialized cookies as "Set-Cookie" headers
46
+ */
47
+ const createResponseWithCookies = (body, init, cookieStorage) => {
48
+ const headers = new Headers(init.headers);
49
+ // Add cookie headers from storage - each one is a complete "Set-Cookie" header from cookie.serialize()
50
+ const cookieHeaders = cookieStorage.getCookieHeaders();
51
+ cookieHeaders.forEach((cookieHeader) => {
52
+ headers.append("Set-Cookie", cookieHeader);
53
+ });
54
+ return new Response(body, {
55
+ ...init,
56
+ headers,
57
+ });
58
+ };
59
+ /**
60
+ * Helper to create redirect with cookie headers
61
+ * Following React Router pattern: add serialized cookies as "Set-Cookie" headers
62
+ */
63
+ const redirectWithCookies = (url, cookieStorage, init) => {
64
+ const headers = new Headers(init?.headers);
65
+ // Add cookie headers from storage - each one is a complete "Set-Cookie" header from cookie.serialize()
66
+ const cookieHeaders = cookieStorage.getCookieHeaders();
67
+ cookieHeaders.forEach((cookieHeader) => {
68
+ headers.append("Set-Cookie", cookieHeader);
69
+ });
70
+ // Set Location header for proper redirect
71
+ headers.set("Location", url);
72
+ return new Response(null, {
73
+ ...init,
74
+ status: 302,
75
+ headers,
76
+ });
77
+ };
78
+ const handlers = {
79
+ /**
80
+ * Login loader - backend OAuth login initiation endpoint
81
+ * Uses CivicAuth.buildLoginUrl() like Express example
82
+ */
83
+ loginLoader: async ({ request }) => {
84
+ const incomingUrl = new URL(request.url);
85
+ const frontendState = incomingUrl.searchParams.get("state");
86
+ // const returnTo = incomingUrl.searchParams.get("returnTo") || "/";
87
+ try {
88
+ const { civicAuth, cookieStorage } = createCivicAuth(request);
89
+ const url = await civicAuth.buildLoginUrl({
90
+ state: frontendState || undefined,
91
+ });
92
+ const response = redirectWithCookies(url.toString(), cookieStorage);
93
+ return response;
94
+ }
95
+ catch (error) {
96
+ console.error("[LOGIN_HANDLER] Backend login error:", error);
97
+ return redirect("/?error=login_failed");
98
+ }
99
+ },
100
+ /**
101
+ * Callback loader - backend OAuth callback endpoint
102
+ * Uses CivicAuth.handleCallback() like Express example
103
+ */
104
+ callbackLoader: async ({ request }) => {
105
+ try {
106
+ const url = new URL(request.url);
107
+ const code = url.searchParams.get("code");
108
+ const state = url.searchParams.get("state");
109
+ const error = url.searchParams.get("error");
110
+ if (error) {
111
+ console.error("OAuth error in callback:", error);
112
+ return redirect("/?error=oauth_error");
113
+ }
114
+ if (!code || !state) {
115
+ throw new Error("Missing code or state parameter");
116
+ }
117
+ const { civicAuth, cookieStorage } = createCivicAuth(request);
118
+ // Convert React Router request to the format expected by handleCallback
119
+ const handleCallbackRequest = {
120
+ headers: Object.fromEntries(request.headers.entries()),
121
+ };
122
+ // For non-iframe requests, use the original handleCallback logic
123
+ const result = await civicAuth.handleCallback({
124
+ code,
125
+ state,
126
+ req: handleCallbackRequest,
127
+ });
128
+ if (result.redirectTo) {
129
+ return redirectWithCookies(result.redirectTo, cookieStorage);
130
+ }
131
+ if (result.content) {
132
+ // Handle both string content and object content
133
+ if (typeof result.content === "string") {
134
+ return createResponseWithCookies(result.content, {
135
+ status: 200,
136
+ headers: {
137
+ "Content-Type": "text/html",
138
+ },
139
+ }, cookieStorage);
140
+ }
141
+ else {
142
+ // Object content (JSON response)
143
+ return createResponseWithCookies(JSON.stringify(result.content), {
144
+ status: 200,
145
+ headers: {
146
+ "Content-Type": "application/json",
147
+ },
148
+ }, cookieStorage);
149
+ }
150
+ }
151
+ // Fallback redirect
152
+ return redirectWithCookies("/", cookieStorage);
153
+ }
154
+ catch (error) {
155
+ console.error("[CALLBACK_HANDLER] OAuth callback error:", error);
156
+ return redirect("/?error=callback_failed");
157
+ }
158
+ },
159
+ /**
160
+ * Logout loader - backend logout endpoint
161
+ * Uses CivicAuth.buildLogoutRedirectUrl() and clearTokens() like Express example
162
+ */
163
+ logoutLoader: async ({ request }) => {
164
+ try {
165
+ const { civicAuth, cookieStorage } = createCivicAuth(request);
166
+ const logoutUrl = await civicAuth.buildLogoutRedirectUrl();
167
+ await civicAuth.clearTokens();
168
+ const url = new URL(logoutUrl.toString());
169
+ // Remove the state parameter to avoid it showing up in the frontend URL
170
+ url.searchParams.delete("state");
171
+ return redirectWithCookies(url.toString(), cookieStorage);
172
+ }
173
+ catch (error) {
174
+ console.error("[LOGOUT_HANDLER] Logout error:", error);
175
+ // If logout URL generation fails, clear tokens and redirect to home (Express pattern)
176
+ try {
177
+ const { civicAuth, cookieStorage } = createCivicAuth(request);
178
+ await civicAuth.clearTokens();
179
+ return redirectWithCookies("/", cookieStorage);
180
+ }
181
+ catch (clearError) {
182
+ console.error("[LOGOUT_HANDLER] Failed to clear tokens:", clearError);
183
+ return redirect("/");
184
+ }
185
+ }
186
+ },
187
+ /**
188
+ * User endpoint - returns current user data as JSON
189
+ * Uses CivicAuth.isLoggedIn() and getUser() like Express example
190
+ */
191
+ userLoader: async ({ request }) => {
192
+ try {
193
+ const { civicAuth, cookieStorage } = createCivicAuth(request);
194
+ const isLoggedIn = await civicAuth.isLoggedIn();
195
+ if (!isLoggedIn) {
196
+ return createResponseWithCookies(JSON.stringify({ error: "Not authenticated" }), {
197
+ status: 401,
198
+ headers: { "Content-Type": "application/json" },
199
+ }, cookieStorage);
200
+ }
201
+ const user = await civicAuth.getUser();
202
+ return createResponseWithCookies(JSON.stringify({ user }), {
203
+ status: 200,
204
+ headers: { "Content-Type": "application/json" },
205
+ }, cookieStorage);
206
+ }
207
+ catch (error) {
208
+ console.error("[USER_HANDLER] User endpoint error:", error);
209
+ return new Response(JSON.stringify({ error: "Internal server error" }), {
210
+ status: 500,
211
+ headers: { "Content-Type": "application/json" },
212
+ });
213
+ }
214
+ },
215
+ /**
216
+ * Refresh endpoint - refreshes access tokens
217
+ * Uses CivicAuth.refreshTokens() like Express example
218
+ */
219
+ refreshLoader: async ({ request }) => {
220
+ try {
221
+ const { civicAuth, cookieStorage } = createCivicAuth(request);
222
+ const isLoggedIn = await civicAuth.isLoggedIn();
223
+ if (!isLoggedIn) {
224
+ return createResponseWithCookies(JSON.stringify({ error: "Not authenticated" }), {
225
+ status: 401,
226
+ headers: { "Content-Type": "application/json" },
227
+ }, cookieStorage);
228
+ }
229
+ await civicAuth.refreshTokens();
230
+ return createResponseWithCookies(JSON.stringify({ success: true, message: "Tokens refreshed" }), {
231
+ status: 200,
232
+ headers: { "Content-Type": "application/json" },
233
+ }, cookieStorage);
234
+ }
235
+ catch (error) {
236
+ console.error("[REFRESH_HANDLER] Token refresh error:", error);
237
+ return new Response(JSON.stringify({ error: "Token refresh failed" }), {
238
+ status: 500,
239
+ headers: { "Content-Type": "application/json" },
240
+ });
241
+ }
242
+ },
243
+ /**
244
+ * Get user data from session (for SSR)
245
+ * Uses CivicAuth.isLoggedIn() and getUser() like Express example
246
+ */
247
+ getUser: async (request) => {
248
+ try {
249
+ const { civicAuth } = createCivicAuth(request);
250
+ const isLoggedIn = await civicAuth.isLoggedIn();
251
+ if (!isLoggedIn) {
252
+ return null;
253
+ }
254
+ return await civicAuth.getUser();
255
+ }
256
+ catch (error) {
257
+ console.error("[GETUSER_HANDLER] getUser error:", error);
258
+ return null;
259
+ }
260
+ },
261
+ /**
262
+ * Get auth data including user and config (for SSR)
263
+ * Returns user data, config, and other auth-related data needed by the app under a civic key
264
+ */
265
+ getAuthData: async (request) => {
266
+ try {
267
+ const { civicAuth } = createCivicAuth(request);
268
+ const isLoggedIn = await civicAuth.isLoggedIn();
269
+ const user = isLoggedIn ? await civicAuth.getUser() : null;
270
+ return {
271
+ civic: {
272
+ user,
273
+ config: whitelistedFrontEndConfig,
274
+ isLoggedIn,
275
+ },
276
+ };
277
+ }
278
+ catch (error) {
279
+ console.error("[GETAUTHDATA_HANDLER] getAuthData error:", error);
280
+ return {
281
+ civic: {
282
+ user: null,
283
+ config: whitelistedFrontEndConfig,
284
+ isLoggedIn: false,
285
+ },
286
+ };
287
+ }
288
+ },
289
+ };
290
+ /**
291
+ * Creates a loader function that handles all auth routes
292
+ * @example
293
+ * // In your auth.$.tsx route file:
294
+ * export const loader = createAuthLoader();
295
+ */
296
+ const createAuthLoader = () => {
297
+ return async (args) => {
298
+ // Get the auth path from the URL
299
+ const authPath = args.params["*"];
300
+ // Route to the appropriate handler
301
+ switch (authPath) {
302
+ case "login":
303
+ return handlers.loginLoader(args);
304
+ case "callback":
305
+ return handlers.callbackLoader(args);
306
+ case "refresh":
307
+ return handlers.refreshLoader(args);
308
+ case "logout":
309
+ return handlers.logoutLoader(args);
310
+ case "user":
311
+ return handlers.userLoader(args);
312
+ default:
313
+ // Return 404 for unknown auth paths
314
+ return new Response("Not Found", { status: 404 });
315
+ }
316
+ };
317
+ };
318
+ return {
319
+ ...handlers,
320
+ createAuthLoader,
321
+ };
322
+ }
323
+ //# sourceMappingURL=routeHandler.js.map