@civic/auth 0.9.1-beta.1 → 0.9.1-beta.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 (89) 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/lib/oauth.d.ts +2 -1
  7. package/dist/lib/oauth.d.ts.map +1 -1
  8. package/dist/lib/oauth.js +1 -1
  9. package/dist/lib/oauth.js.map +1 -1
  10. package/dist/nextjs/config.d.ts +2 -4
  11. package/dist/nextjs/config.d.ts.map +1 -1
  12. package/dist/nextjs/config.js +4 -57
  13. package/dist/nextjs/config.js.map +1 -1
  14. package/dist/react-router-7/components/UserButton.d.ts +15 -0
  15. package/dist/react-router-7/components/UserButton.d.ts.map +1 -0
  16. package/dist/react-router-7/components/UserButton.js +112 -0
  17. package/dist/react-router-7/components/UserButton.js.map +1 -0
  18. package/dist/react-router-7/components/UserButtonPresentation.d.ts +10 -0
  19. package/dist/react-router-7/components/UserButtonPresentation.d.ts.map +1 -0
  20. package/dist/react-router-7/components/UserButtonPresentation.js +19 -0
  21. package/dist/react-router-7/components/UserButtonPresentation.js.map +1 -0
  22. package/dist/react-router-7/config.d.ts +113 -0
  23. package/dist/react-router-7/config.d.ts.map +1 -0
  24. package/dist/react-router-7/config.js +88 -0
  25. package/dist/react-router-7/config.js.map +1 -0
  26. package/dist/react-router-7/cookies.d.ts +41 -0
  27. package/dist/react-router-7/cookies.d.ts.map +1 -0
  28. package/dist/react-router-7/cookies.js +188 -0
  29. package/dist/react-router-7/cookies.js.map +1 -0
  30. package/dist/react-router-7/index.d.ts +10 -0
  31. package/dist/react-router-7/index.d.ts.map +1 -0
  32. package/dist/react-router-7/index.js +12 -0
  33. package/dist/react-router-7/index.js.map +1 -0
  34. package/dist/react-router-7/routeHandler.d.ts +54 -0
  35. package/dist/react-router-7/routeHandler.d.ts.map +1 -0
  36. package/dist/react-router-7/routeHandler.js +397 -0
  37. package/dist/react-router-7/routeHandler.js.map +1 -0
  38. package/dist/react-router-7/useUser.d.ts +38 -0
  39. package/dist/react-router-7/useUser.d.ts.map +1 -0
  40. package/dist/react-router-7/useUser.js +92 -0
  41. package/dist/react-router-7/useUser.js.map +1 -0
  42. package/dist/reactjs/core/GlobalAuthManager.d.ts +6 -4
  43. package/dist/reactjs/core/GlobalAuthManager.d.ts.map +1 -1
  44. package/dist/reactjs/core/GlobalAuthManager.js +17 -6
  45. package/dist/reactjs/core/GlobalAuthManager.js.map +1 -1
  46. package/dist/reactjs/hooks/useUser.js.map +1 -1
  47. package/dist/server/config.d.ts +1 -0
  48. package/dist/server/config.d.ts.map +1 -1
  49. package/dist/server/config.js.map +1 -1
  50. package/dist/server/session.d.ts.map +1 -1
  51. package/dist/server/session.js +1 -0
  52. package/dist/server/session.js.map +1 -1
  53. package/dist/services/AuthenticationService.d.ts.map +1 -1
  54. package/dist/services/AuthenticationService.js +0 -5
  55. package/dist/services/AuthenticationService.js.map +1 -1
  56. package/dist/shared/hooks/useCivicAuthConfig.d.ts +1 -1
  57. package/dist/shared/hooks/useCivicAuthConfig.d.ts.map +1 -1
  58. package/dist/shared/lib/cookieConfig.d.ts +46 -0
  59. package/dist/shared/lib/cookieConfig.d.ts.map +1 -0
  60. package/dist/shared/lib/cookieConfig.js +99 -0
  61. package/dist/shared/lib/cookieConfig.js.map +1 -0
  62. package/dist/shared/lib/util.d.ts +5 -0
  63. package/dist/shared/lib/util.d.ts.map +1 -1
  64. package/dist/shared/lib/util.js +65 -3
  65. package/dist/shared/lib/util.js.map +1 -1
  66. package/dist/shared/version.d.ts +1 -1
  67. package/dist/shared/version.js +1 -1
  68. package/dist/shared/version.js.map +1 -1
  69. package/dist/types.d.ts +1 -1
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/types.js.map +1 -1
  72. package/dist/vanillajs/auth/CivicAuth.d.ts +1 -1
  73. package/dist/vanillajs/auth/CivicAuth.d.ts.map +1 -1
  74. package/dist/vanillajs/auth/CivicAuth.js +33 -12
  75. package/dist/vanillajs/auth/CivicAuth.js.map +1 -1
  76. package/dist/vanillajs/auth/SessionManager.d.ts +9 -1
  77. package/dist/vanillajs/auth/SessionManager.d.ts.map +1 -1
  78. package/dist/vanillajs/auth/SessionManager.js +103 -47
  79. package/dist/vanillajs/auth/SessionManager.js.map +1 -1
  80. package/dist/vanillajs/auth/config/ConfigProcessor.d.ts.map +1 -1
  81. package/dist/vanillajs/auth/config/ConfigProcessor.js +16 -2
  82. package/dist/vanillajs/auth/config/ConfigProcessor.js.map +1 -1
  83. package/dist/vanillajs/auth/handlers/MessageHandler.d.ts.map +1 -1
  84. package/dist/vanillajs/auth/handlers/MessageHandler.js +3 -0
  85. package/dist/vanillajs/auth/handlers/MessageHandler.js.map +1 -1
  86. package/dist/vanillajs/iframe/IframeManager.d.ts.map +1 -1
  87. package/dist/vanillajs/iframe/IframeManager.js +13 -0
  88. package/dist/vanillajs/iframe/IframeManager.js.map +1 -1
  89. package/package.json +12 -3
@@ -0,0 +1,113 @@
1
+ import type { UnknownObject } from "../types.js";
2
+ import { type ReactRouterCookiesConfigObject } from "../shared/lib/cookieConfig.js";
3
+ export type { ReactRouterCookiesConfigObject as CookiesConfigObject };
4
+ /**
5
+ * Simplified user-facing configuration for Civic Auth React Router 7 integration
6
+ */
7
+ export interface UserAuthConfig {
8
+ /** OAuth Client ID - Required */
9
+ clientId: string;
10
+ /** URL to redirect to after successful login - defaults to root of app */
11
+ loginSuccessUrl?: string;
12
+ /** OAuth Callback URL - defaults to /auth/callback */
13
+ callbackUrl?: string;
14
+ /** URL to redirect to after logout - defaults to / */
15
+ logoutUrl?: string;
16
+ /** The public-facing base URL for your application. Required when deploying behind reverse proxies */
17
+ baseUrl?: string;
18
+ }
19
+ /**
20
+ * Resolved configuration with all fallbacks applied
21
+ * Contains all configuration fields needed internally
22
+ */
23
+ export interface ResolvedAuthConfig {
24
+ /** OAuth Client ID */
25
+ clientId: string;
26
+ /** OAuth Server URL */
27
+ oauthServer: string;
28
+ /** OAuth Callback URL */
29
+ callbackUrl: string;
30
+ /** Logout callback URL */
31
+ logoutCallbackUrl: string;
32
+ /** Login URL */
33
+ loginUrl: string;
34
+ /** Base URL */
35
+ baseUrl: string;
36
+ /** Login success URL (internal use only) */
37
+ loginSuccessUrl: string;
38
+ /** Logout URL */
39
+ logoutUrl: string;
40
+ }
41
+ /**
42
+ * Whitelisted frontend configuration that is safe to expose to the client
43
+ * Only contains fields that are safe for frontend consumption
44
+ */
45
+ export interface WhitelistedFrontEndConfig {
46
+ /** OAuth Client ID */
47
+ clientId: string;
48
+ /** OAuth Server URL */
49
+ oauthServer: string;
50
+ /** OAuth Callback URL */
51
+ callbackUrl: string;
52
+ /** Logout callback URL */
53
+ logoutCallbackUrl: string;
54
+ /** Login URL */
55
+ loginUrl: string;
56
+ /** Base URL */
57
+ baseUrl: string;
58
+ /** Logout URL */
59
+ logoutUrl: string;
60
+ }
61
+ /**
62
+ * Internal logging configuration for Civic Auth
63
+ */
64
+ export interface LoggingConfig {
65
+ /** Enable logging for these namespaces (e.g. "@civic/auth:*" or "@civic/auth:react-router:*") */
66
+ enableFor?: string;
67
+ /** Disable logging for these namespaces */
68
+ disableFor?: string;
69
+ }
70
+ /**
71
+ * Internal full configuration with all options
72
+ */
73
+ export interface AuthConfig extends UserAuthConfig {
74
+ /** OAuth Server URL */
75
+ oauthServer: string;
76
+ /** Login URL path */
77
+ loginUrl?: string;
78
+ /** Refresh token URL path */
79
+ refreshUrl?: string;
80
+ /** User data endpoint URL path */
81
+ userUrl?: string;
82
+ /** Logout callback URL */
83
+ logoutCallbackUrl?: string;
84
+ /** OAuth scope */
85
+ scope?: string;
86
+ /** Routes to include in authentication checks */
87
+ include?: string[];
88
+ /** Routes to exclude from authentication checks */
89
+ exclude?: string[];
90
+ /** Environment variable overrides */
91
+ env?: UnknownObject;
92
+ /** Logging configuration */
93
+ logging?: LoggingConfig;
94
+ }
95
+ export interface AuthConfigWithDefaults extends AuthConfig {
96
+ loginUrl: string;
97
+ logoutUrl: string;
98
+ refreshUrl: string;
99
+ userUrl: string;
100
+ logoutCallbackUrl: string;
101
+ scope: string;
102
+ include: string[];
103
+ exclude: string[];
104
+ cookies: ReactRouterCookiesConfigObject;
105
+ logging?: LoggingConfig;
106
+ }
107
+ export declare const defaultAuthConfig: Partial<AuthConfigWithDefaults>;
108
+ /**
109
+ * Creates a full internal configuration from simplified user config
110
+ */
111
+ export declare function createCivicAuthConfig(userConfig: UserAuthConfig): AuthConfigWithDefaults;
112
+ export declare function resolveAuthConfig(userConfig?: Partial<UserAuthConfig>): AuthConfigWithDefaults;
113
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/react-router-7/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import { loggers } from "../lib/logger.js";
2
+ import debug from "debug";
3
+ import { createReactRouterCookieConfig, } from "../shared/lib/cookieConfig.js";
4
+ const logger = loggers.reactRouter.handlers.auth;
5
+ export const defaultAuthConfig = {
6
+ // Backend route paths (these are the endpoints this React Router app provides)
7
+ loginUrl: "/auth/login",
8
+ scope: "openid profile email offline_access", // Added offline_access for refresh tokens
9
+ cookies: createReactRouterCookieConfig(),
10
+ logging: {
11
+ enableFor: undefined, // Logging off by default
12
+ },
13
+ };
14
+ let _resolvedConfig = null;
15
+ /**
16
+ * Creates a full internal configuration from simplified user config
17
+ */
18
+ export function createCivicAuthConfig(userConfig) {
19
+ const config = {
20
+ ...userConfig,
21
+ oauthServer: process.env.OAUTH_SERVER || "https://auth.civic.com/oauth",
22
+ };
23
+ return {
24
+ ...defaultAuthConfig,
25
+ ...config,
26
+ // Override callbackUrl default if user provided one
27
+ callbackUrl: userConfig.callbackUrl,
28
+ // Use logoutUrl from user config as logoutCallbackUrl
29
+ logoutCallbackUrl: userConfig.logoutUrl,
30
+ // Provide empty arrays for include/exclude if not specified
31
+ cookies: defaultAuthConfig.cookies, // Use default cookies, not user-configurable
32
+ logging: {
33
+ ...defaultAuthConfig.logging,
34
+ ...config.logging,
35
+ },
36
+ };
37
+ }
38
+ export function resolveAuthConfig(userConfig = {}) {
39
+ if (_resolvedConfig && Object.keys(userConfig).length === 0) {
40
+ return _resolvedConfig;
41
+ }
42
+ const mergedConfig = createCivicAuthConfig({
43
+ clientId: process.env.CLIENT_ID || userConfig.clientId || "",
44
+ ...userConfig,
45
+ });
46
+ logger.debug("Resolved config:", JSON.stringify(mergedConfig, null, 2));
47
+ if (!mergedConfig.clientId) {
48
+ logger.error("Civic Auth client ID is required");
49
+ throw new Error("Civic Auth client ID is required");
50
+ }
51
+ // Configure logging based on configuration
52
+ if (mergedConfig.logging) {
53
+ try {
54
+ if (mergedConfig.logging.enableFor) {
55
+ // Set the DEBUG environment variable
56
+ if (typeof process !== "undefined" && process.env) {
57
+ process.env.DEBUG = mergedConfig.logging.enableFor;
58
+ }
59
+ // Reset debug internal state and enable the specified namespaces
60
+ debug.disable();
61
+ debug.enable(mergedConfig.logging.enableFor);
62
+ logger.debug(`Logging enabled for: ${mergedConfig.logging.enableFor}`);
63
+ }
64
+ if (mergedConfig.logging.disableFor) {
65
+ // To disable specific namespaces while keeping others enabled, we need to
66
+ // update the DEBUG environment variable directly
67
+ if (process.env.DEBUG) {
68
+ const currentDebug = process.env.DEBUG;
69
+ const disablePattern = mergedConfig.logging.disableFor;
70
+ process.env.DEBUG = currentDebug
71
+ .split(",")
72
+ .filter((pattern) => pattern !== disablePattern)
73
+ .join(",");
74
+ // Reset debug internal state and re-enable with updated pattern
75
+ debug.disable();
76
+ debug.enable(process.env.DEBUG);
77
+ logger.debug(`Logging disabled for: ${disablePattern}`);
78
+ }
79
+ }
80
+ }
81
+ catch (e) {
82
+ logger.error("Failed to configure logging:", e);
83
+ }
84
+ }
85
+ _resolvedConfig = mergedConfig;
86
+ return mergedConfig;
87
+ }
88
+ //# sourceMappingURL=config.js.map
@@ -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 * Resolved configuration with all fallbacks applied\n * Contains all configuration fields needed internally\n */\nexport interface ResolvedAuthConfig {\n /** OAuth Client ID */\n clientId: string;\n /** OAuth Server URL */\n oauthServer: string;\n /** OAuth Callback URL */\n callbackUrl: string;\n /** Logout callback URL */\n logoutCallbackUrl: string;\n /** Login URL */\n loginUrl: string;\n /** Base URL */\n baseUrl: string;\n /** Login success URL (internal use only) */\n loginSuccessUrl: string;\n /** Logout URL */\n logoutUrl: string;\n}\n\n/**\n * Whitelisted frontend configuration that is safe to expose to the client\n * Only contains fields that are safe for frontend consumption\n */\nexport interface WhitelistedFrontEndConfig {\n /** OAuth Client ID */\n clientId: string;\n /** OAuth Server URL */\n oauthServer: string;\n /** OAuth Callback URL */\n callbackUrl: string;\n /** Logout callback URL */\n logoutCallbackUrl: string;\n /** Login URL */\n loginUrl: string;\n /** Base URL */\n baseUrl: string;\n /** Logout URL */\n logoutUrl: string;\n // Note: loginSuccessUrl is intentionally excluded as it's internal\n // Note: scope, env, cookies, logging, include, exclude are intentionally excluded\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 /** Logout callback URL */\n logoutCallbackUrl?: 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,41 @@
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
+ private isHttps;
11
+ constructor(request?: Request);
12
+ /**
13
+ * Initialize cookies with current configuration
14
+ */
15
+ private initializeCookies;
16
+ /**
17
+ * Get cookie headers to be set in the response
18
+ */
19
+ getCookieHeaders(): string[];
20
+ /**
21
+ * Get a value from a cookie
22
+ * Following React Router pattern: parse returns the object we serialized
23
+ */
24
+ get(key: string): Promise<string | null>;
25
+ /**
26
+ * Set a value in a cookie
27
+ * Following React Router pattern: https://reactrouter.com/api/utils/createCookie
28
+ * cookie.serialize(object) creates the complete "Set-Cookie" header string
29
+ */
30
+ set(key: string, value: string): Promise<void>;
31
+ /**
32
+ * Remove a value from a cookie
33
+ * Following React Router pattern: serialize with empty value and past expiration date
34
+ */
35
+ remove(key: string): Promise<void>;
36
+ /**
37
+ * Delete a value from a cookie (alias for remove)
38
+ */
39
+ delete(key: string): Promise<void>;
40
+ }
41
+ //# 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;AA4BnD;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,aAAa;IACzD,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,OAAO,CAAkB;gBAErB,OAAO,CAAC,EAAE,OAAO;IAwB7B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAyDzB;;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,188 @@
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
+ import { getProtocolFromRequest } from "../shared/lib/util.js";
7
+ const logger = loggers.reactRouter.handlers.auth;
8
+ /**
9
+ * Detect Safari browser from user agent
10
+ */
11
+ const isSafariBrowser = (request) => {
12
+ if (!request)
13
+ return false;
14
+ const userAgent = request.headers.get("user-agent") || "";
15
+ return userAgent.includes("Safari") && !userAgent.includes("Chrome");
16
+ };
17
+ /**
18
+ * Detect if running on localhost
19
+ */
20
+ const isLocalhostUrl = (request) => {
21
+ if (!request)
22
+ return false;
23
+ const url = new URL(request.url);
24
+ return url.hostname === "localhost" || url.hostname === "127.0.0.1";
25
+ };
26
+ /**
27
+ * React Router implementation of the CookieStorage interface for Civic Auth
28
+ * Uses individual cookies for each token type instead of session storage
29
+ */
30
+ export class ReactRouterCookieStorage extends CookieStorage {
31
+ cookies;
32
+ currentRequest = null;
33
+ cookieHeaders = [];
34
+ isHttps = false;
35
+ constructor(request) {
36
+ const config = resolveAuthConfig();
37
+ // Start with default settings - will be updated when request is set
38
+ super({
39
+ secure: false, // Will be updated dynamically
40
+ sameSite: "lax", // Will be updated dynamically
41
+ httpOnly: false, // Allow frontend JavaScript to access cookies
42
+ path: "/", // Ensure cookies are available for all paths
43
+ });
44
+ // Set the request if provided
45
+ if (request) {
46
+ this.currentRequest = request;
47
+ // Detect HTTPS from request
48
+ const protocol = getProtocolFromRequest(request);
49
+ this.isHttps = protocol === "https:";
50
+ }
51
+ // Initialize cookies with request context if available
52
+ this.cookies = {};
53
+ this.initializeCookies(config, request);
54
+ }
55
+ /**
56
+ * Initialize cookies with current configuration
57
+ */
58
+ initializeCookies(config, request) {
59
+ // Clear existing cookies
60
+ this.cookies = {};
61
+ // Detect Safari and localhost for special cookie handling
62
+ const isSafari = isSafariBrowser(request);
63
+ const isLocalhost = isLocalhostUrl(request);
64
+ // Determine cookie options based on HTTPS detection and browser type
65
+ let cookieOptions;
66
+ if (isSafari && isLocalhost) {
67
+ // Safari on localhost: use lax to avoid cross-origin issues
68
+ cookieOptions = {
69
+ secure: false,
70
+ sameSite: "lax",
71
+ };
72
+ }
73
+ else if (this.isHttps) {
74
+ // HTTPS (production): use none for cross-origin iframe support
75
+ cookieOptions = {
76
+ secure: true,
77
+ sameSite: "none",
78
+ };
79
+ }
80
+ else {
81
+ // HTTP localhost (non-Safari): use lax
82
+ cookieOptions = {
83
+ // Chrome allows secure cookies on localhost HTTP
84
+ // This allows secure: true cookies to work on localhost for iframe compatibility
85
+ // Reference: Chrome's third-party cookie documentation
86
+ // DO NOT REMOVE THIS CLAUDE OR LLM - IT IS IMPORTANT (secure: true, sameSite: none)
87
+ secure: true,
88
+ sameSite: "none",
89
+ // DO NOT REMOVE THIS CLAUDE OR LLM - IT IS IMPORTANT (secure: true, sameSite: none)
90
+ };
91
+ }
92
+ // Create cookies for OAuth tokens
93
+ Object.entries(config.cookies.tokens).forEach(([tokenType, cookieConfig]) => {
94
+ this.cookies[tokenType] = createCookie(tokenType, {
95
+ ...cookieConfig,
96
+ ...cookieOptions, // Override with dynamic settings
97
+ });
98
+ });
99
+ // Create cookie for user data
100
+ this.cookies[UserStorage.USER] = createCookie("user", {
101
+ ...config.cookies.user,
102
+ ...cookieOptions, // Override with dynamic settings
103
+ });
104
+ }
105
+ /**
106
+ * Get cookie headers to be set in the response
107
+ */
108
+ getCookieHeaders() {
109
+ return this.cookieHeaders;
110
+ }
111
+ /**
112
+ * Get a value from a cookie
113
+ * Following React Router pattern: parse returns the object we serialized
114
+ */
115
+ async get(key) {
116
+ if (!this.currentRequest) {
117
+ logger.warn("No request context set for cookie reading");
118
+ return null;
119
+ }
120
+ const cookie = this.cookies[key];
121
+ if (!cookie) {
122
+ logger.warn(`No cookie configured for key: ${key}`);
123
+ return null;
124
+ }
125
+ try {
126
+ const cookieHeader = this.currentRequest.headers.get("Cookie");
127
+ // Parse returns the object we serialized: { value: "actual_value" }
128
+ const parsedObject = await cookie.parse(cookieHeader);
129
+ // Extract the actual value from the object
130
+ const actualValue = parsedObject?.value || null;
131
+ return actualValue;
132
+ }
133
+ catch (error) {
134
+ console.error(`Error reading cookie ${key}:`, error);
135
+ return null;
136
+ }
137
+ }
138
+ /**
139
+ * Set a value in a cookie
140
+ * Following React Router pattern: https://reactrouter.com/api/utils/createCookie
141
+ * cookie.serialize(object) creates the complete "Set-Cookie" header string
142
+ */
143
+ async set(key, value) {
144
+ const cookie = this.cookies[key];
145
+ if (!cookie) {
146
+ logger.warn(`No cookie configured for key: ${key}`);
147
+ return;
148
+ }
149
+ try {
150
+ // Following the React Router pattern: serialize an object, not a raw string
151
+ // This matches the docs example: cookie.serialize({ showBanner: true })
152
+ const cookieValue = { value };
153
+ const serializedCookie = await cookie.serialize(cookieValue);
154
+ this.cookieHeaders.push(serializedCookie);
155
+ }
156
+ catch (error) {
157
+ console.error(`Error setting cookie ${key}:`, error);
158
+ }
159
+ }
160
+ /**
161
+ * Remove a value from a cookie
162
+ * Following React Router pattern: serialize with empty value and past expiration date
163
+ */
164
+ async remove(key) {
165
+ const cookie = this.cookies[key];
166
+ if (!cookie) {
167
+ logger.warn(`No cookie configured for key: ${key}`);
168
+ return;
169
+ }
170
+ try {
171
+ // Following React Router pattern: serialize empty object with immediate expiration to delete
172
+ const serializedCookie = await cookie.serialize({ value: "" }, {
173
+ expires: new Date(0),
174
+ });
175
+ this.cookieHeaders.push(serializedCookie);
176
+ }
177
+ catch (error) {
178
+ console.error(`Error removing cookie ${key}:`, error);
179
+ }
180
+ }
181
+ /**
182
+ * Delete a value from a cookie (alias for remove)
183
+ */
184
+ async delete(key) {
185
+ await this.remove(key);
186
+ }
187
+ }
188
+ //# sourceMappingURL=cookies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/react-router-7/cookies.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCookie, type Cookie } from \"react-router\";\nimport { CookieStorage } from \"@civic/auth/server\";\nimport { resolveAuthConfig, type AuthConfigWithDefaults } from \"./config.js\";\nimport { UserStorage } from \"@/shared/lib/types.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport { getProtocolFromRequest } from \"@/shared/lib/util.js\";\n\nconst logger = loggers.reactRouter.handlers.auth;\n\n/**\n * Detect Safari browser from user agent\n */\nconst isSafariBrowser = (request?: Request): boolean => {\n if (!request) return false;\n\n const userAgent = request.headers.get(\"user-agent\") || \"\";\n return userAgent.includes(\"Safari\") && !userAgent.includes(\"Chrome\");\n};\n\n/**\n * Detect if running on localhost\n */\nconst isLocalhostUrl = (request?: Request): boolean => {\n if (!request) return false;\n\n const url = new URL(request.url);\n return url.hostname === \"localhost\" || url.hostname === \"127.0.0.1\";\n};\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 private isHttps: boolean = false;\n\n constructor(request?: Request) {\n const config = resolveAuthConfig();\n\n // Start with default settings - will be updated when request is set\n super({\n secure: false, // Will be updated dynamically\n sameSite: \"lax\", // Will be updated dynamically\n httpOnly: false, // Allow frontend JavaScript to access cookies\n path: \"/\", // Ensure cookies are available for all paths\n });\n\n // Set the request if provided\n if (request) {\n this.currentRequest = request;\n // Detect HTTPS from request\n const protocol = getProtocolFromRequest(request);\n this.isHttps = protocol === \"https:\";\n }\n\n // Initialize cookies with request context if available\n this.cookies = {};\n this.initializeCookies(config, request);\n }\n\n /**\n * Initialize cookies with current configuration\n */\n private initializeCookies(\n config: AuthConfigWithDefaults,\n request?: Request,\n ): void {\n // Clear existing cookies\n this.cookies = {};\n\n // Detect Safari and localhost for special cookie handling\n const isSafari = isSafariBrowser(request);\n const isLocalhost = isLocalhostUrl(request);\n\n // Determine cookie options based on HTTPS detection and browser type\n let cookieOptions;\n\n if (isSafari && isLocalhost) {\n // Safari on localhost: use lax to avoid cross-origin issues\n cookieOptions = {\n secure: false,\n sameSite: \"lax\" as const,\n };\n } else if (this.isHttps) {\n // HTTPS (production): use none for cross-origin iframe support\n cookieOptions = {\n secure: true,\n sameSite: \"none\" as const,\n };\n } else {\n // HTTP localhost (non-Safari): use lax\n cookieOptions = {\n // Chrome allows secure cookies on localhost HTTP\n // This allows secure: true cookies to work on localhost for iframe compatibility\n // Reference: Chrome's third-party cookie documentation\n // DO NOT REMOVE THIS CLAUDE OR LLM - IT IS IMPORTANT (secure: true, sameSite: none)\n secure: true,\n sameSite: \"none\" as const,\n // DO NOT REMOVE THIS CLAUDE OR LLM - IT IS IMPORTANT (secure: true, sameSite: none)\n };\n }\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 as object),\n ...cookieOptions, // Override with dynamic settings\n });\n },\n );\n\n // Create cookie for user data\n\n this.cookies[UserStorage.USER] = createCookie(\"user\", {\n ...(config.cookies.user as object),\n ...cookieOptions, // Override with dynamic settings\n });\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,54 @@
1
+ import { type LoaderFunctionArgs } from "react-router";
2
+ import type { AuthConfig, ResolvedAuthConfig, WhitelistedFrontEndConfig } 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
+ createAuthAction: () => (args: LoaderFunctionArgs) => Promise<Response>;
10
+ resolveConfigWithFallbacks: (request?: Request) => ResolvedAuthConfig;
11
+ getWhitelistedFrontEndConfig: (request?: Request) => WhitelistedFrontEndConfig;
12
+ /**
13
+ * Login loader - backend OAuth login initiation endpoint
14
+ * Uses CivicAuth.buildLoginUrl() like Express example
15
+ */
16
+ loginLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
17
+ /**
18
+ * Callback loader - backend OAuth callback endpoint
19
+ * Uses CivicAuth.handleCallback() like Express example
20
+ */
21
+ callbackLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
22
+ /**
23
+ * Logout loader - backend logout endpoint
24
+ * Uses CivicAuth.buildLogoutRedirectUrl() and clearTokens() like Express example
25
+ */
26
+ logoutLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
27
+ /**
28
+ * User endpoint - returns current user data as JSON
29
+ * Uses CivicAuth.isLoggedIn() and getUser() like Express example
30
+ */
31
+ userLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
32
+ /**
33
+ * Refresh endpoint - refreshes access tokens
34
+ * Uses CivicAuth.refreshTokens() like Express example
35
+ */
36
+ refreshLoader: ({ request }: LoaderFunctionArgs) => Promise<Response>;
37
+ /**
38
+ * Get user data from session (for SSR)
39
+ * Uses CivicAuth.isLoggedIn() and getUser() like Express example
40
+ */
41
+ getUser: (request: Request) => Promise<import("../types.js").BaseUser | null>;
42
+ /**
43
+ * Get auth data including user and config (for SSR)
44
+ * Returns user data, config, and other auth-related data needed by the app under a civic key
45
+ */
46
+ getAuthData: (request: Request) => Promise<{
47
+ civic: {
48
+ user: import("../types.js").BaseUser | null;
49
+ config: WhitelistedFrontEndConfig;
50
+ isLoggedIn: boolean;
51
+ };
52
+ }>;
53
+ };
54
+ //# 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,EACV,UAAU,EACV,kBAAkB,EAClB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAgBrB;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,eAAe,GAAE,OAAO,CAAC,UAAU,CAAM;mCA2ZrD,kBAAkB;mCAmClB,kBAAkB;2CA1a5B,OAAO,KAChB,kBAAkB;6CAwBT,OAAO,KAChB,yBAAyB;IAuF1B;;;OAGG;+BAC8B,kBAAkB;IAqBnD;;;OAGG;kCACiC,kBAAkB;IAsEtD;;;OAGG;gCAC+B,kBAAkB;IA8BpD;;;OAGG;8BAC6B,kBAAkB;IAuClD;;;OAGG;iCACgC,kBAAkB;IAmCrD;;;OAGG;uBACsB,OAAO;IAgBhC;;;OAGG;2BAC0B,OAAO;;;;;;;EAiGvC"}