@civic/auth 0.7.0 → 0.7.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.md +19 -1
- package/dist/nextjs/config.d.ts +1 -0
- package/dist/nextjs/config.d.ts.map +1 -1
- package/dist/nextjs/config.js +1 -1
- package/dist/nextjs/config.js.map +1 -1
- package/dist/nextjs/hooks/useRefresh.d.ts.map +1 -1
- package/dist/nextjs/hooks/useRefresh.js +7 -1
- package/dist/nextjs/hooks/useRefresh.js.map +1 -1
- package/dist/nextjs/routeHandler.d.ts.map +1 -1
- package/dist/nextjs/routeHandler.js +7 -0
- package/dist/nextjs/routeHandler.js.map +1 -1
- package/dist/reactjs/components/SignInButton.d.ts.map +1 -1
- package/dist/reactjs/components/SignInButton.js +8 -3
- package/dist/reactjs/components/SignInButton.js.map +1 -1
- package/dist/reactjs/components/SignOutButton.d.ts.map +1 -1
- package/dist/reactjs/components/SignOutButton.js +3 -1
- package/dist/reactjs/components/SignOutButton.js.map +1 -1
- package/dist/reactjs/components/UserButton.d.ts.map +1 -1
- package/dist/reactjs/components/UserButton.js +11 -6
- package/dist/reactjs/components/UserButton.js.map +1 -1
- package/dist/reactjs/components/index.d.ts +5 -5
- package/dist/reactjs/components/index.d.ts.map +1 -1
- package/dist/reactjs/components/index.js +5 -5
- package/dist/reactjs/components/index.js.map +1 -1
- package/dist/reactjs/core/GlobalAuthManager.d.ts +120 -0
- package/dist/reactjs/core/GlobalAuthManager.d.ts.map +1 -0
- package/dist/reactjs/core/GlobalAuthManager.js +296 -0
- package/dist/reactjs/core/GlobalAuthManager.js.map +1 -0
- package/dist/reactjs/hooks/index.d.ts +2 -2
- package/dist/reactjs/hooks/index.d.ts.map +1 -1
- package/dist/reactjs/hooks/index.js +2 -2
- package/dist/reactjs/hooks/index.js.map +1 -1
- package/dist/reactjs/hooks/useToken.d.ts +13 -0
- package/dist/reactjs/hooks/useToken.d.ts.map +1 -0
- package/dist/reactjs/hooks/useToken.js +48 -0
- package/dist/reactjs/hooks/useToken.js.map +1 -0
- package/dist/reactjs/hooks/useUser.d.ts +20 -2
- package/dist/reactjs/hooks/useUser.d.ts.map +1 -1
- package/dist/reactjs/hooks/useUser.js +163 -7
- package/dist/reactjs/hooks/useUser.js.map +1 -1
- package/dist/reactjs/index.d.ts +6 -2
- package/dist/reactjs/index.d.ts.map +1 -1
- package/dist/reactjs/index.js +7 -1
- package/dist/reactjs/index.js.map +1 -1
- package/dist/reactjs/providers/CivicAuthContext.d.ts +40 -0
- package/dist/reactjs/providers/CivicAuthContext.d.ts.map +1 -0
- package/dist/reactjs/providers/CivicAuthContext.js +303 -0
- package/dist/reactjs/providers/CivicAuthContext.js.map +1 -0
- package/dist/reactjs/providers/CivicAuthProvider.d.ts +20 -4
- package/dist/reactjs/providers/CivicAuthProvider.d.ts.map +1 -1
- package/dist/reactjs/providers/CivicAuthProvider.js +46 -25
- package/dist/reactjs/providers/CivicAuthProvider.js.map +1 -1
- package/dist/reactjs/providers/index.d.ts +2 -2
- package/dist/reactjs/providers/index.d.ts.map +1 -1
- package/dist/reactjs/providers/index.js +4 -2
- package/dist/reactjs/providers/index.js.map +1 -1
- package/dist/server/ServerAuthenticationResolver.d.ts.map +1 -1
- package/dist/server/ServerAuthenticationResolver.js +28 -11
- package/dist/server/ServerAuthenticationResolver.js.map +1 -1
- package/dist/server/config.d.ts +2 -0
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js.map +1 -1
- package/dist/server/login.d.ts +2 -2
- package/dist/server/login.d.ts.map +1 -1
- package/dist/server/login.js +7 -2
- package/dist/server/login.js.map +1 -1
- package/dist/services/AuthenticationService.d.ts +1 -1
- package/dist/services/AuthenticationService.d.ts.map +1 -1
- package/dist/services/AuthenticationService.js +2 -2
- package/dist/services/AuthenticationService.js.map +1 -1
- package/dist/shared/components/CivicAuthIframe.js +1 -1
- package/dist/shared/components/CivicAuthIframe.js.map +1 -1
- package/dist/shared/components/CivicAuthIframeContainer.js +2 -2
- package/dist/shared/components/CivicAuthIframeContainer.js.map +1 -1
- package/dist/shared/hooks/index.d.ts +1 -2
- package/dist/shared/hooks/index.d.ts.map +1 -1
- package/dist/shared/hooks/index.js +1 -2
- package/dist/shared/hooks/index.js.map +1 -1
- package/dist/shared/hooks/useClientTokenExchangeSession.d.ts +7 -0
- package/dist/shared/hooks/useClientTokenExchangeSession.d.ts.map +1 -0
- package/dist/shared/hooks/useClientTokenExchangeSession.js +17 -0
- package/dist/shared/hooks/useClientTokenExchangeSession.js.map +1 -0
- package/dist/shared/lib/BrowserAuthenticationRefresher.js +3 -3
- package/dist/shared/lib/BrowserAuthenticationRefresher.js.map +1 -1
- package/dist/shared/lib/types.d.ts +1 -1
- package/dist/shared/lib/types.js +1 -1
- package/dist/shared/lib/types.js.map +1 -1
- package/dist/shared/lib/util.d.ts +5 -6
- package/dist/shared/lib/util.d.ts.map +1 -1
- package/dist/shared/lib/util.js +66 -75
- package/dist/shared/lib/util.js.map +1 -1
- package/dist/shared/providers/CivicAuthConfigContext.d.ts +2 -2
- package/dist/shared/providers/CivicAuthConfigContext.d.ts.map +1 -1
- package/dist/shared/providers/CivicAuthConfigContext.js +1 -1
- package/dist/shared/providers/CivicAuthConfigContext.js.map +1 -1
- package/dist/shared/providers/TokenProvider.d.ts.map +1 -1
- package/dist/shared/providers/TokenProvider.js +4 -7
- package/dist/shared/providers/TokenProvider.js.map +1 -1
- package/dist/shared/version.d.ts +1 -1
- package/dist/shared/version.d.ts.map +1 -1
- package/dist/shared/version.js +1 -1
- package/dist/shared/version.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +23 -0
- package/dist/utils.js.map +1 -1
- package/dist/vanillajs/auth/CivicAuth.d.ts +12 -0
- package/dist/vanillajs/auth/CivicAuth.d.ts.map +1 -1
- package/dist/vanillajs/auth/CivicAuth.js +88 -5
- package/dist/vanillajs/auth/CivicAuth.js.map +1 -1
- package/dist/vanillajs/auth/SessionManager.d.ts +7 -1
- package/dist/vanillajs/auth/SessionManager.d.ts.map +1 -1
- package/dist/vanillajs/auth/SessionManager.js +34 -3
- package/dist/vanillajs/auth/SessionManager.js.map +1 -1
- package/dist/vanillajs/auth/TokenRefresher.js +2 -2
- package/dist/vanillajs/auth/TokenRefresher.js.map +1 -1
- package/dist/vanillajs/auth/config/ConfigProcessor.d.ts.map +1 -1
- package/dist/vanillajs/auth/config/ConfigProcessor.js +7 -2
- package/dist/vanillajs/auth/config/ConfigProcessor.js.map +1 -1
- package/dist/vanillajs/auth/types/AuthTypes.d.ts +3 -0
- package/dist/vanillajs/auth/types/AuthTypes.d.ts.map +1 -1
- package/dist/vanillajs/auth/types/AuthTypes.js.map +1 -1
- package/dist/vanillajs/index.d.ts +2 -0
- package/dist/vanillajs/index.d.ts.map +1 -1
- package/dist/vanillajs/index.js +2 -0
- package/dist/vanillajs/index.js.map +1 -1
- package/dist/vanillajs/types/index.d.ts +1 -1
- package/dist/vanillajs/types/index.d.ts.map +1 -1
- package/dist/vanillajs/types/index.js.map +1 -1
- package/dist/vanillajs/utils/auth-utils.d.ts +14 -0
- package/dist/vanillajs/utils/auth-utils.d.ts.map +1 -1
- package/dist/vanillajs/utils/auth-utils.js +39 -0
- package/dist/vanillajs/utils/auth-utils.js.map +1 -1
- package/package.json +3 -3
- package/dist/reactjs/hooks/useClientTokenExchangeSession.d.ts +0 -3
- package/dist/reactjs/hooks/useClientTokenExchangeSession.d.ts.map +0 -1
- package/dist/reactjs/hooks/useClientTokenExchangeSession.js +0 -13
- package/dist/reactjs/hooks/useClientTokenExchangeSession.js.map +0 -1
- package/dist/reactjs/providers/AuthProvider.d.ts +0 -10
- package/dist/reactjs/providers/AuthProvider.d.ts.map +0 -1
- package/dist/reactjs/providers/AuthProvider.js +0 -79
- package/dist/reactjs/providers/AuthProvider.js.map +0 -1
- package/dist/reactjs/providers/ClientTokenExchangeSessionProvider.d.ts +0 -17
- package/dist/reactjs/providers/ClientTokenExchangeSessionProvider.d.ts.map +0 -1
- package/dist/reactjs/providers/ClientTokenExchangeSessionProvider.js +0 -190
- package/dist/reactjs/providers/ClientTokenExchangeSessionProvider.js.map +0 -1
|
@@ -10,10 +10,12 @@ const ChevronUp = () => (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", widt
|
|
|
10
10
|
const UserButton = ({ className, wrapperClassName, dropdownButtonClassName, style, wrapperStyle, dropdownButtonStyle, }) => {
|
|
11
11
|
const [isOpen, setIsOpen] = useState(false);
|
|
12
12
|
const [buttonWidth, setButtonWidth] = useState(null);
|
|
13
|
-
const { user, signIn, signOut, authStatus, displayMode } = useUser();
|
|
13
|
+
const { user, signIn, signOut, authStatus, displayMode, isLoading } = useUser();
|
|
14
14
|
const buttonRef = useRef(null);
|
|
15
15
|
const dropdownRef = useRef(null);
|
|
16
16
|
const [userActionStarted, setUserActionStarted] = useState(false);
|
|
17
|
+
// Provide default displayMode if undefined
|
|
18
|
+
const effectiveDisplayMode = displayMode || "iframe";
|
|
17
19
|
useEffect(() => {
|
|
18
20
|
if ([AuthStatus.AUTHENTICATED, AuthStatus.UNAUTHENTICATED].includes(authStatus)) {
|
|
19
21
|
setUserActionStarted(false);
|
|
@@ -89,10 +91,10 @@ const UserButton = ({ className, wrapperClassName, dropdownButtonClassName, styl
|
|
|
89
91
|
height: "100%",
|
|
90
92
|
width: "100%",
|
|
91
93
|
objectFit: "cover",
|
|
92
|
-
}, src: user.picture, alt: user?.name || user?.email }) })) : (_jsx("span", { css: { display: "block" } })), _jsx(ButtonContentOrLoader, { authStatus: authStatus, displayMode:
|
|
94
|
+
}, src: user.picture, alt: user?.name || user?.email }) })) : (_jsx("span", { css: { display: "block" } })), _jsx(ButtonContentOrLoader, { authStatus: authStatus, displayMode: effectiveDisplayMode, userActionStarted: userActionStarted, children: user?.name || user?.email }), _jsx("span", { css: {
|
|
93
95
|
display: "block",
|
|
94
96
|
pointerEvents: "none",
|
|
95
|
-
visibility: shouldShowLoader(authStatus,
|
|
97
|
+
visibility: shouldShowLoader(authStatus, effectiveDisplayMode)
|
|
96
98
|
? "hidden"
|
|
97
99
|
: "visible",
|
|
98
100
|
}, children: isOpen ? _jsx(ChevronUp, {}) : _jsx(ChevronDown, {}) })] }) }), _jsx("div", { ref: dropdownRef, css: isOpen
|
|
@@ -120,7 +122,8 @@ const UserButton = ({ className, wrapperClassName, dropdownButtonClassName, styl
|
|
|
120
122
|
},
|
|
121
123
|
}, onClick: () => {
|
|
122
124
|
setUserActionStarted(true);
|
|
123
|
-
!shouldShowLoader(authStatus,
|
|
125
|
+
!shouldShowLoader(authStatus, effectiveDisplayMode) &&
|
|
126
|
+
handleSignOut();
|
|
124
127
|
}, children: "Logout" }) }) }) })] }));
|
|
125
128
|
}
|
|
126
129
|
return (_jsx("button", { ref: buttonRef, "data-testid": "sign-in-button", css: {
|
|
@@ -136,8 +139,10 @@ const UserButton = ({ className, wrapperClassName, dropdownButtonClassName, styl
|
|
|
136
139
|
},
|
|
137
140
|
}, className: className, style: style, onClick: () => {
|
|
138
141
|
setUserActionStarted(true);
|
|
139
|
-
!shouldShowLoader(authStatus,
|
|
140
|
-
|
|
142
|
+
!shouldShowLoader(authStatus, effectiveDisplayMode) &&
|
|
143
|
+
!isLoading &&
|
|
144
|
+
handleSignIn();
|
|
145
|
+
}, children: _jsx(ButtonContentOrLoader, { authStatus: authStatus, displayMode: effectiveDisplayMode, userActionStarted: userActionStarted, children: "Sign in" }) }));
|
|
141
146
|
};
|
|
142
147
|
export { UserButton };
|
|
143
148
|
//# sourceMappingURL=UserButton.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserButton.js","sourceRoot":"","sources":["../../../src/reactjs/components/UserButton.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport { useUser } from \"@/reactjs/hooks/index.js\";\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n} from \"react\";\nimport { ButtonContentOrLoader } from \"./ButtonContentOrLoader.js\";\nimport { AuthStatus } from \"@/types.js\";\nimport { shouldShowLoader } from \"./utils.js\";\n\nconst ChevronDown = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"lucide lucide-chevron-down\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n);\n\nconst ChevronUp = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"lucide lucide-chevron-up\"\n >\n <path d=\"m18 15-6-6-6 6\" />\n </svg>\n);\n\nconst UserButton = ({\n className,\n wrapperClassName,\n dropdownButtonClassName,\n style,\n wrapperStyle,\n dropdownButtonStyle,\n}: {\n className?: string;\n wrapperClassName?: string;\n dropdownButtonClassName?: string;\n style?: CSSProperties;\n wrapperStyle?: CSSProperties;\n dropdownButtonStyle?: CSSProperties;\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const [buttonWidth, setButtonWidth] = useState<number | null>(null);\n const { user, signIn, signOut, authStatus, displayMode } = useUser();\n const buttonRef = useRef<HTMLButtonElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const [userActionStarted, setUserActionStarted] = useState(false);\n\n useEffect(() => {\n if (\n [AuthStatus.AUTHENTICATED, AuthStatus.UNAUTHENTICATED].includes(\n authStatus,\n )\n ) {\n setUserActionStarted(false);\n }\n }, [authStatus]);\n\n useEffect(() => {\n if (buttonRef.current) {\n setButtonWidth(buttonRef.current.offsetWidth);\n }\n }, [isOpen]);\n\n const handleClickOutside = useCallback((event: MouseEvent) => {\n const target = event.target as HTMLElement;\n\n if (\n buttonRef.current &&\n dropdownRef.current &&\n !buttonRef.current.contains(target) &&\n !dropdownRef.current.contains(target)\n ) {\n setIsOpen(false);\n }\n }, []);\n\n const handleSignOut = useCallback(async () => {\n setIsOpen(false);\n await signOut();\n }, [signOut]);\n\n const handleSignIn = useCallback(async () => {\n setIsOpen(false);\n await signIn();\n }, [signIn]);\n\n const handleEscape = useCallback((event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setIsOpen(false);\n }\n }, []);\n\n useEffect(() => {\n if (isOpen) {\n window.addEventListener(\"click\", handleClickOutside);\n\n window.addEventListener(\"keydown\", handleEscape);\n }\n\n return () => {\n window.removeEventListener(\"click\", handleClickOutside);\n\n window.removeEventListener(\"keydown\", handleEscape);\n };\n }, [handleClickOutside, handleEscape, isOpen]);\n\n if (user) {\n return (\n <div\n css={{ position: \"relative\", width: \"auto\" }}\n className={wrapperClassName}\n style={wrapperStyle}\n id=\"civic-dropdown-container\"\n >\n <button\n ref={buttonRef}\n css={{\n cursor: \"pointer\",\n display: \"flex\",\n minWidth: \"10rem\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: \"0.5rem\",\n borderRadius: \"9999px\",\n border: \"1px solid #6b7280\",\n padding: \"0.75rem 1rem\",\n color: \"#6b7280\",\n transition: \"background-color 0.2s\",\n \"&:hover\": {\n backgroundColor: \"#f3f4f6\",\n },\n }}\n className={className}\n style={style}\n onClick={() => {\n setUserActionStarted(true);\n authStatus !== AuthStatus.SIGNING_OUT &&\n setIsOpen((isOpen) => !isOpen);\n }}\n >\n <>\n {user?.picture ? (\n <span\n css={{\n position: \"relative\",\n display: \"flex\",\n height: \"1.5rem\",\n width: \"1.5rem\",\n flexShrink: 0,\n gap: \"0.5rem\",\n overflow: \"hidden\",\n borderRadius: \"9999px\",\n }}\n >\n <img\n css={{\n height: \"100%\",\n width: \"100%\",\n objectFit: \"cover\",\n }}\n src={user.picture}\n alt={user?.name || user?.email}\n />\n </span>\n ) : (\n <span css={{ display: \"block\" }} />\n )}\n\n <ButtonContentOrLoader\n authStatus={authStatus}\n displayMode={displayMode}\n userActionStarted={userActionStarted}\n >\n {user?.name || user?.email}\n </ButtonContentOrLoader>\n <span\n css={{\n display: \"block\",\n pointerEvents: \"none\",\n visibility: shouldShowLoader(authStatus, displayMode)\n ? \"hidden\"\n : \"visible\",\n }}\n >\n {isOpen ? <ChevronUp /> : <ChevronDown />}\n </span>\n </>\n </button>\n <div\n ref={dropdownRef}\n css={\n isOpen\n ? {\n position: \"absolute\",\n left: 0,\n background: \"transparent\",\n width: buttonWidth || \"auto\",\n marginTop: \"0.5rem\",\n borderRadius: \"0.5rem\",\n boxShadow:\n \"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)\",\n zIndex: 1000,\n }\n : { display: \"none\" }\n }\n >\n <ul css={{ listStyleType: \"none\", margin: 0, padding: 0 }}>\n <li>\n <button\n className={dropdownButtonClassName}\n style={dropdownButtonStyle}\n css={{\n display: \"block\",\n width: \"100%\",\n padding: \"0.5rem 1rem\",\n transition: \"background-color 0.2s\",\n textAlign: \"center\",\n color: \"#6b7280\",\n cursor: \"pointer\",\n borderRadius: \"0.5rem\",\n \"&:hover\": {\n backgroundColor: \"#f3f4f6\",\n },\n }}\n onClick={() => {\n setUserActionStarted(true);\n !shouldShowLoader(authStatus, displayMode) && handleSignOut();\n }}\n >\n Logout\n </button>\n </li>\n </ul>\n </div>\n </div>\n );\n }\n\n return (\n <button\n ref={buttonRef}\n data-testid=\"sign-in-button\"\n css={{\n cursor: \"pointer\",\n borderRadius: \"9999px\",\n border: \"1px solid #6b7280\",\n padding: \"0.75rem 1rem\",\n color: \"#6b7280\",\n transition: \"background-color 0.2s\",\n minWidth: \"9em\", // this stops the button from going too small when in loading mode\n \"&:hover\": {\n backgroundColor: \"#f3f4f6\",\n },\n }}\n className={className}\n style={style}\n onClick={() => {\n setUserActionStarted(true);\n !shouldShowLoader(authStatus, displayMode) && handleSignIn();\n }}\n >\n <ButtonContentOrLoader\n authStatus={authStatus}\n displayMode={displayMode}\n userActionStarted={userActionStarted}\n >\n Sign in\n </ButtonContentOrLoader>\n </button>\n );\n};\n\nexport { UserButton };\n"]}
|
|
1
|
+
{"version":3,"file":"UserButton.js","sourceRoot":"","sources":["../../../src/reactjs/components/UserButton.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport { useUser } from \"@/reactjs/hooks/index.js\";\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n} from \"react\";\nimport { ButtonContentOrLoader } from \"./ButtonContentOrLoader.js\";\nimport { AuthStatus } from \"@/types.js\";\nimport { shouldShowLoader } from \"./utils.js\";\n\nconst ChevronDown = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"lucide lucide-chevron-down\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n);\n\nconst ChevronUp = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"lucide lucide-chevron-up\"\n >\n <path d=\"m18 15-6-6-6 6\" />\n </svg>\n);\n\nconst UserButton = ({\n className,\n wrapperClassName,\n dropdownButtonClassName,\n style,\n wrapperStyle,\n dropdownButtonStyle,\n}: {\n className?: string;\n wrapperClassName?: string;\n dropdownButtonClassName?: string;\n style?: CSSProperties;\n wrapperStyle?: CSSProperties;\n dropdownButtonStyle?: CSSProperties;\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const [buttonWidth, setButtonWidth] = useState<number | null>(null);\n const { user, signIn, signOut, authStatus, displayMode, isLoading } =\n useUser();\n const buttonRef = useRef<HTMLButtonElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const [userActionStarted, setUserActionStarted] = useState(false);\n\n // Provide default displayMode if undefined\n const effectiveDisplayMode = displayMode || \"iframe\";\n\n useEffect(() => {\n if (\n [AuthStatus.AUTHENTICATED, AuthStatus.UNAUTHENTICATED].includes(\n authStatus,\n )\n ) {\n setUserActionStarted(false);\n }\n }, [authStatus]);\n\n useEffect(() => {\n if (buttonRef.current) {\n setButtonWidth(buttonRef.current.offsetWidth);\n }\n }, [isOpen]);\n\n const handleClickOutside = useCallback((event: MouseEvent) => {\n const target = event.target as HTMLElement;\n\n if (\n buttonRef.current &&\n dropdownRef.current &&\n !buttonRef.current.contains(target) &&\n !dropdownRef.current.contains(target)\n ) {\n setIsOpen(false);\n }\n }, []);\n\n const handleSignOut = useCallback(async () => {\n setIsOpen(false);\n await signOut();\n }, [signOut]);\n\n const handleSignIn = useCallback(async () => {\n setIsOpen(false);\n await signIn();\n }, [signIn]);\n\n const handleEscape = useCallback((event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setIsOpen(false);\n }\n }, []);\n\n useEffect(() => {\n if (isOpen) {\n window.addEventListener(\"click\", handleClickOutside);\n\n window.addEventListener(\"keydown\", handleEscape);\n }\n\n return () => {\n window.removeEventListener(\"click\", handleClickOutside);\n\n window.removeEventListener(\"keydown\", handleEscape);\n };\n }, [handleClickOutside, handleEscape, isOpen]);\n\n if (user) {\n return (\n <div\n css={{ position: \"relative\", width: \"auto\" }}\n className={wrapperClassName}\n style={wrapperStyle}\n id=\"civic-dropdown-container\"\n >\n <button\n ref={buttonRef}\n css={{\n cursor: \"pointer\",\n display: \"flex\",\n minWidth: \"10rem\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: \"0.5rem\",\n borderRadius: \"9999px\",\n border: \"1px solid #6b7280\",\n padding: \"0.75rem 1rem\",\n color: \"#6b7280\",\n transition: \"background-color 0.2s\",\n \"&:hover\": {\n backgroundColor: \"#f3f4f6\",\n },\n }}\n className={className}\n style={style}\n onClick={() => {\n setUserActionStarted(true);\n authStatus !== AuthStatus.SIGNING_OUT &&\n setIsOpen((isOpen) => !isOpen);\n }}\n >\n <>\n {user?.picture ? (\n <span\n css={{\n position: \"relative\",\n display: \"flex\",\n height: \"1.5rem\",\n width: \"1.5rem\",\n flexShrink: 0,\n gap: \"0.5rem\",\n overflow: \"hidden\",\n borderRadius: \"9999px\",\n }}\n >\n <img\n css={{\n height: \"100%\",\n width: \"100%\",\n objectFit: \"cover\",\n }}\n src={user.picture}\n alt={user?.name || user?.email}\n />\n </span>\n ) : (\n <span css={{ display: \"block\" }} />\n )}\n\n <ButtonContentOrLoader\n authStatus={authStatus}\n displayMode={effectiveDisplayMode}\n userActionStarted={userActionStarted}\n >\n {user?.name || user?.email}\n </ButtonContentOrLoader>\n <span\n css={{\n display: \"block\",\n pointerEvents: \"none\",\n visibility: shouldShowLoader(authStatus, effectiveDisplayMode)\n ? \"hidden\"\n : \"visible\",\n }}\n >\n {isOpen ? <ChevronUp /> : <ChevronDown />}\n </span>\n </>\n </button>\n <div\n ref={dropdownRef}\n css={\n isOpen\n ? {\n position: \"absolute\",\n left: 0,\n background: \"transparent\",\n width: buttonWidth || \"auto\",\n marginTop: \"0.5rem\",\n borderRadius: \"0.5rem\",\n boxShadow:\n \"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)\",\n zIndex: 1000,\n }\n : { display: \"none\" }\n }\n >\n <ul css={{ listStyleType: \"none\", margin: 0, padding: 0 }}>\n <li>\n <button\n className={dropdownButtonClassName}\n style={dropdownButtonStyle}\n css={{\n display: \"block\",\n width: \"100%\",\n padding: \"0.5rem 1rem\",\n transition: \"background-color 0.2s\",\n textAlign: \"center\",\n color: \"#6b7280\",\n cursor: \"pointer\",\n borderRadius: \"0.5rem\",\n \"&:hover\": {\n backgroundColor: \"#f3f4f6\",\n },\n }}\n onClick={() => {\n setUserActionStarted(true);\n !shouldShowLoader(authStatus, effectiveDisplayMode) &&\n handleSignOut();\n }}\n >\n Logout\n </button>\n </li>\n </ul>\n </div>\n </div>\n );\n }\n\n return (\n <button\n ref={buttonRef}\n data-testid=\"sign-in-button\"\n css={{\n cursor: \"pointer\",\n borderRadius: \"9999px\",\n border: \"1px solid #6b7280\",\n padding: \"0.75rem 1rem\",\n color: \"#6b7280\",\n transition: \"background-color 0.2s\",\n minWidth: \"9em\", // this stops the button from going too small when in loading mode\n \"&:hover\": {\n backgroundColor: \"#f3f4f6\",\n },\n }}\n className={className}\n style={style}\n onClick={() => {\n setUserActionStarted(true);\n !shouldShowLoader(authStatus, effectiveDisplayMode) &&\n !isLoading &&\n handleSignIn();\n }}\n >\n <ButtonContentOrLoader\n authStatus={authStatus}\n displayMode={effectiveDisplayMode}\n userActionStarted={userActionStarted}\n >\n Sign in\n </ButtonContentOrLoader>\n </button>\n );\n};\n\nexport { UserButton };\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export
|
|
1
|
+
export { UserButton } from "./UserButton.js";
|
|
2
|
+
export { SignInButton } from "./SignInButton.js";
|
|
3
|
+
export { SignOutButton } from "./SignOutButton.js";
|
|
4
|
+
export { ButtonContentOrLoader } from "./ButtonContentOrLoader.js";
|
|
5
|
+
export * from "./utils.js";
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/reactjs/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/reactjs/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,cAAc,YAAY,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export
|
|
1
|
+
export { UserButton } from "./UserButton.js";
|
|
2
|
+
export { SignInButton } from "./SignInButton.js";
|
|
3
|
+
export { SignOutButton } from "./SignOutButton.js";
|
|
4
|
+
export { ButtonContentOrLoader } from "./ButtonContentOrLoader.js";
|
|
5
|
+
export * from "./utils.js";
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/reactjs/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/reactjs/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,cAAc,YAAY,CAAC","sourcesContent":["export { UserButton } from \"./UserButton.js\";\nexport { SignInButton } from \"./SignInButton.js\";\nexport { SignOutButton } from \"./SignOutButton.js\";\nexport { ButtonContentOrLoader } from \"./ButtonContentOrLoader.js\";\nexport * from \"./utils.js\";\n"]}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GlobalAuthManager - Singleton Authentication State Manager
|
|
3
|
+
*
|
|
4
|
+
* This module provides a global singleton that manages authentication state across a React application
|
|
5
|
+
* without requiring React Context providers or prop drilling. It acts as a centralized authentication
|
|
6
|
+
* manager that wraps the CivicAuth vanilla JavaScript library and provides a React-friendly interface.
|
|
7
|
+
*
|
|
8
|
+
* Key Features:
|
|
9
|
+
* - Singleton pattern ensures single auth instance across the entire app
|
|
10
|
+
* - Eliminates need for React Context providers and reduces bundle size
|
|
11
|
+
* - Manages authentication state (user, session, loading, errors)
|
|
12
|
+
* - Handles sign-in/sign-out flows with event-driven updates
|
|
13
|
+
* - Provides idempotent initialization (safe to call multiple times)
|
|
14
|
+
* - Supports multiple React hooks subscribing to the same auth state
|
|
15
|
+
* - Automatic session refresh and user data synchronization
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* - Initialize once in your app with authentication configuration
|
|
19
|
+
* - Use React hooks (useCivicAuth, useAuth, etc.) to access auth state
|
|
20
|
+
* - The manager handles all underlying CivicAuth SDK interactions
|
|
21
|
+
* - State updates are automatically propagated to all subscribed components
|
|
22
|
+
*
|
|
23
|
+
* This design pattern is particularly useful for:
|
|
24
|
+
* - Large applications with many components needing auth state
|
|
25
|
+
* - Avoiding provider wrapper hell in React component trees
|
|
26
|
+
* - Ensuring consistent auth state across disconnected component hierarchies
|
|
27
|
+
* - Server-side rendering scenarios where providers might be problematic
|
|
28
|
+
*/
|
|
29
|
+
import type { User, Session } from "../../vanillajs/index.js";
|
|
30
|
+
import type { DisplayMode } from "../../types.js";
|
|
31
|
+
export interface GlobalAuthConfig {
|
|
32
|
+
clientId: string;
|
|
33
|
+
redirectUrl?: string;
|
|
34
|
+
config?: {
|
|
35
|
+
oauthServer?: string;
|
|
36
|
+
};
|
|
37
|
+
displayMode?: DisplayMode;
|
|
38
|
+
iframeMode?: "modal" | "embedded";
|
|
39
|
+
nonce?: string;
|
|
40
|
+
logoutRedirectUrl?: string;
|
|
41
|
+
scopes?: string[];
|
|
42
|
+
authProcessTimeout?: number;
|
|
43
|
+
onSignIn?: (error?: Error) => void;
|
|
44
|
+
onSignOut?: () => void;
|
|
45
|
+
}
|
|
46
|
+
export type AuthStatus = "authenticated" | "unauthenticated" | "authenticating" | "error" | "signing_out";
|
|
47
|
+
export interface GlobalAuthState {
|
|
48
|
+
user: User | null;
|
|
49
|
+
session: Session | null;
|
|
50
|
+
isLoading: boolean;
|
|
51
|
+
authStatus: AuthStatus;
|
|
52
|
+
error: Error | null;
|
|
53
|
+
displayMode?: DisplayMode;
|
|
54
|
+
}
|
|
55
|
+
type StateListener = (state: GlobalAuthState) => void;
|
|
56
|
+
/**
|
|
57
|
+
* Global singleton that manages CivicAuth instance for React hooks
|
|
58
|
+
* Eliminates the need for React providers
|
|
59
|
+
*/
|
|
60
|
+
declare class GlobalAuthManager {
|
|
61
|
+
private static instance;
|
|
62
|
+
private auth;
|
|
63
|
+
private events;
|
|
64
|
+
private config;
|
|
65
|
+
private listeners;
|
|
66
|
+
private callbacks;
|
|
67
|
+
private initializationPromise;
|
|
68
|
+
private state;
|
|
69
|
+
private constructor();
|
|
70
|
+
static getInstance(): GlobalAuthManager;
|
|
71
|
+
/**
|
|
72
|
+
* Initialize auth with config (idempotent)
|
|
73
|
+
* Returns the same promise for concurrent calls with same config
|
|
74
|
+
*/
|
|
75
|
+
initialize(config: GlobalAuthConfig): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Private method that does the actual initialization work
|
|
78
|
+
*/
|
|
79
|
+
private _doInitialize;
|
|
80
|
+
/**
|
|
81
|
+
* Subscribe to state changes
|
|
82
|
+
*/
|
|
83
|
+
subscribe(listener: StateListener): () => void;
|
|
84
|
+
/**
|
|
85
|
+
* Get current state
|
|
86
|
+
*/
|
|
87
|
+
getState(): GlobalAuthState;
|
|
88
|
+
/**
|
|
89
|
+
* Sign in
|
|
90
|
+
*/
|
|
91
|
+
signIn(): Promise<{
|
|
92
|
+
user: User;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Sign out
|
|
96
|
+
*/
|
|
97
|
+
signOut(): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Check if config is the same (for idempotent initialization)
|
|
100
|
+
*/
|
|
101
|
+
private isSameConfig;
|
|
102
|
+
/**
|
|
103
|
+
* Setup event listeners for auth state changes
|
|
104
|
+
*/
|
|
105
|
+
private setupEventListeners;
|
|
106
|
+
/**
|
|
107
|
+
* Refresh user and session data
|
|
108
|
+
*/
|
|
109
|
+
private refreshUserAndSession;
|
|
110
|
+
/**
|
|
111
|
+
* Update state and notify listeners
|
|
112
|
+
*/
|
|
113
|
+
private setState;
|
|
114
|
+
/**
|
|
115
|
+
* Cleanup auth instance
|
|
116
|
+
*/
|
|
117
|
+
private cleanup;
|
|
118
|
+
}
|
|
119
|
+
export { GlobalAuthManager };
|
|
120
|
+
//# sourceMappingURL=GlobalAuthManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GlobalAuthManager.d.ts","sourceRoot":"","sources":["../../../src/reactjs/core/GlobalAuthManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAOH,OAAO,KAAK,EACV,IAAI,EACJ,OAAO,EAER,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAOlD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,MAAM,UAAU,GAClB,eAAe,GACf,iBAAiB,GACjB,gBAAgB,GAChB,OAAO,GACP,aAAa,CAAC;AAElB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAEtD;;;GAGG;AACH,cAAM,iBAAiB;IACrB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAkC;IACzD,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,SAAS,CAGV;IACP,OAAO,CAAC,qBAAqB,CAA8B;IAE3D,OAAO,CAAC,KAAK,CAOX;IAEF,OAAO;IAIP,MAAM,CAAC,WAAW,IAAI,iBAAiB;IAOvC;;;OAGG;IACG,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBzD;;OAEG;YACW,aAAa;IA0E3B;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI;IAK9C;;OAEG;IACH,QAAQ,IAAI,eAAe;IAI3B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC;IAiBvC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAa9B;;OAEG;IACH,OAAO,CAAC,YAAY;IAUpB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwD3B;;OAEG;YACW,qBAAqB;IAiBnC;;OAEG;IACH,OAAO,CAAC,QAAQ;IAKhB;;OAEG;YACW,OAAO;CAStB;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GlobalAuthManager - Singleton Authentication State Manager
|
|
3
|
+
*
|
|
4
|
+
* This module provides a global singleton that manages authentication state across a React application
|
|
5
|
+
* without requiring React Context providers or prop drilling. It acts as a centralized authentication
|
|
6
|
+
* manager that wraps the CivicAuth vanilla JavaScript library and provides a React-friendly interface.
|
|
7
|
+
*
|
|
8
|
+
* Key Features:
|
|
9
|
+
* - Singleton pattern ensures single auth instance across the entire app
|
|
10
|
+
* - Eliminates need for React Context providers and reduces bundle size
|
|
11
|
+
* - Manages authentication state (user, session, loading, errors)
|
|
12
|
+
* - Handles sign-in/sign-out flows with event-driven updates
|
|
13
|
+
* - Provides idempotent initialization (safe to call multiple times)
|
|
14
|
+
* - Supports multiple React hooks subscribing to the same auth state
|
|
15
|
+
* - Automatic session refresh and user data synchronization
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* - Initialize once in your app with authentication configuration
|
|
19
|
+
* - Use React hooks (useCivicAuth, useAuth, etc.) to access auth state
|
|
20
|
+
* - The manager handles all underlying CivicAuth SDK interactions
|
|
21
|
+
* - State updates are automatically propagated to all subscribed components
|
|
22
|
+
*
|
|
23
|
+
* This design pattern is particularly useful for:
|
|
24
|
+
* - Large applications with many components needing auth state
|
|
25
|
+
* - Avoiding provider wrapper hell in React component trees
|
|
26
|
+
* - Ensuring consistent auth state across disconnected component hierarchies
|
|
27
|
+
* - Server-side rendering scenarios where providers might be problematic
|
|
28
|
+
*/
|
|
29
|
+
import { CivicAuth, AuthenticationEvents, AuthEvent, } from "../../vanillajs/index.js";
|
|
30
|
+
/**
|
|
31
|
+
* Global singleton that manages CivicAuth instance for React hooks
|
|
32
|
+
* Eliminates the need for React providers
|
|
33
|
+
*/
|
|
34
|
+
class GlobalAuthManager {
|
|
35
|
+
static instance = null;
|
|
36
|
+
auth = null;
|
|
37
|
+
events = null;
|
|
38
|
+
config = null;
|
|
39
|
+
listeners = new Set();
|
|
40
|
+
callbacks = {};
|
|
41
|
+
initializationPromise = null;
|
|
42
|
+
state = {
|
|
43
|
+
user: null,
|
|
44
|
+
session: null,
|
|
45
|
+
isLoading: false,
|
|
46
|
+
authStatus: "unauthenticated",
|
|
47
|
+
error: null,
|
|
48
|
+
displayMode: undefined,
|
|
49
|
+
};
|
|
50
|
+
constructor() {
|
|
51
|
+
// Private constructor for singleton
|
|
52
|
+
}
|
|
53
|
+
static getInstance() {
|
|
54
|
+
if (!GlobalAuthManager.instance) {
|
|
55
|
+
GlobalAuthManager.instance = new GlobalAuthManager();
|
|
56
|
+
}
|
|
57
|
+
return GlobalAuthManager.instance;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Initialize auth with config (idempotent)
|
|
61
|
+
* Returns the same promise for concurrent calls with same config
|
|
62
|
+
*/
|
|
63
|
+
async initialize(config) {
|
|
64
|
+
// If we have a promise and same config, return existing promise
|
|
65
|
+
if (this.initializationPromise &&
|
|
66
|
+
this.config &&
|
|
67
|
+
this.isSameConfig(config)) {
|
|
68
|
+
return this.initializationPromise;
|
|
69
|
+
}
|
|
70
|
+
// If different config, reset everything
|
|
71
|
+
if (this.config && !this.isSameConfig(config)) {
|
|
72
|
+
this.initializationPromise = null;
|
|
73
|
+
await this.cleanup();
|
|
74
|
+
}
|
|
75
|
+
// Create new initialization promise if needed
|
|
76
|
+
if (!this.initializationPromise) {
|
|
77
|
+
this.initializationPromise = this._doInitialize(config);
|
|
78
|
+
}
|
|
79
|
+
return this.initializationPromise;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Private method that does the actual initialization work
|
|
83
|
+
*/
|
|
84
|
+
async _doInitialize(config) {
|
|
85
|
+
this.config = config;
|
|
86
|
+
this.callbacks = {
|
|
87
|
+
onSignIn: config.onSignIn,
|
|
88
|
+
onSignOut: config.onSignOut,
|
|
89
|
+
};
|
|
90
|
+
this.setState({ isLoading: true, error: null });
|
|
91
|
+
try {
|
|
92
|
+
this.events = new AuthenticationEvents();
|
|
93
|
+
this.setupEventListeners();
|
|
94
|
+
const authConfig = {
|
|
95
|
+
clientId: config.clientId,
|
|
96
|
+
oauthServerBaseUrl: config.config?.oauthServer,
|
|
97
|
+
scopes: config.scopes || [
|
|
98
|
+
"openid",
|
|
99
|
+
"profile",
|
|
100
|
+
"email",
|
|
101
|
+
"offline_access",
|
|
102
|
+
],
|
|
103
|
+
displayMode: config.displayMode || "iframe",
|
|
104
|
+
iframeDisplayMode: config.iframeMode || "modal",
|
|
105
|
+
nonce: config.nonce,
|
|
106
|
+
authProcessTimeout: config.authProcessTimeout || 120000,
|
|
107
|
+
events: this.events,
|
|
108
|
+
logging: {
|
|
109
|
+
enabled: true,
|
|
110
|
+
level: "debug",
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
// Only add redirectUrl if provided
|
|
114
|
+
if (config.redirectUrl) {
|
|
115
|
+
authConfig.redirectUrl = config.redirectUrl;
|
|
116
|
+
}
|
|
117
|
+
// Only add logoutRedirectUrl if provided
|
|
118
|
+
if (config.logoutRedirectUrl) {
|
|
119
|
+
authConfig.logoutRedirectUrl = config.logoutRedirectUrl;
|
|
120
|
+
}
|
|
121
|
+
this.auth = await CivicAuth.create(authConfig);
|
|
122
|
+
// Check initial auth state
|
|
123
|
+
const isAuthenticated = await this.auth.isAuthenticated();
|
|
124
|
+
if (isAuthenticated) {
|
|
125
|
+
await this.refreshUserAndSession();
|
|
126
|
+
this.setState({ authStatus: "authenticated" });
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
this.setState({ authStatus: "unauthenticated" });
|
|
130
|
+
}
|
|
131
|
+
this.setState({
|
|
132
|
+
isLoading: false,
|
|
133
|
+
displayMode: config.displayMode,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
// Reset promise on error so it can be retried
|
|
138
|
+
this.initializationPromise = null;
|
|
139
|
+
const authError = error instanceof Error
|
|
140
|
+
? error
|
|
141
|
+
: new Error("Auth initialization failed");
|
|
142
|
+
this.setState({
|
|
143
|
+
error: authError,
|
|
144
|
+
authStatus: "error",
|
|
145
|
+
isLoading: false,
|
|
146
|
+
});
|
|
147
|
+
throw authError;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Subscribe to state changes
|
|
152
|
+
*/
|
|
153
|
+
subscribe(listener) {
|
|
154
|
+
this.listeners.add(listener);
|
|
155
|
+
return () => this.listeners.delete(listener);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get current state
|
|
159
|
+
*/
|
|
160
|
+
getState() {
|
|
161
|
+
return { ...this.state };
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Sign in
|
|
165
|
+
*/
|
|
166
|
+
async signIn() {
|
|
167
|
+
if (!this.auth) {
|
|
168
|
+
throw new Error("Auth not initialized");
|
|
169
|
+
}
|
|
170
|
+
const { user } = await this.auth.startAuthentication();
|
|
171
|
+
await this.refreshUserAndSession();
|
|
172
|
+
// Ensure we have a user to return
|
|
173
|
+
if (!user) {
|
|
174
|
+
throw new Error("Authentication succeeded but no user was returned");
|
|
175
|
+
}
|
|
176
|
+
// Return the user object
|
|
177
|
+
return { user };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Sign out
|
|
181
|
+
*/
|
|
182
|
+
async signOut() {
|
|
183
|
+
if (!this.auth) {
|
|
184
|
+
throw new Error("Auth not initialized");
|
|
185
|
+
}
|
|
186
|
+
await this.auth.logout();
|
|
187
|
+
this.setState({
|
|
188
|
+
user: null,
|
|
189
|
+
session: null,
|
|
190
|
+
authStatus: "unauthenticated",
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Check if config is the same (for idempotent initialization)
|
|
195
|
+
*/
|
|
196
|
+
isSameConfig(newConfig) {
|
|
197
|
+
if (!this.config)
|
|
198
|
+
return false;
|
|
199
|
+
return (this.config.clientId === newConfig.clientId &&
|
|
200
|
+
this.config.config?.oauthServer === newConfig.config?.oauthServer &&
|
|
201
|
+
this.config.displayMode === newConfig.displayMode &&
|
|
202
|
+
this.config.nonce === newConfig.nonce);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Setup event listeners for auth state changes
|
|
206
|
+
*/
|
|
207
|
+
setupEventListeners() {
|
|
208
|
+
if (!this.events)
|
|
209
|
+
return;
|
|
210
|
+
this.events.on(AuthEvent.SIGN_IN_STARTED, () => {
|
|
211
|
+
this.setState({
|
|
212
|
+
isLoading: true,
|
|
213
|
+
authStatus: "authenticating",
|
|
214
|
+
error: null,
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
this.events.on(AuthEvent.SIGN_IN_COMPLETE, () => {
|
|
218
|
+
this.setState({
|
|
219
|
+
isLoading: false,
|
|
220
|
+
authStatus: "authenticated",
|
|
221
|
+
error: null,
|
|
222
|
+
});
|
|
223
|
+
this.refreshUserAndSession();
|
|
224
|
+
this.callbacks.onSignIn?.();
|
|
225
|
+
});
|
|
226
|
+
this.events.on(AuthEvent.SIGN_IN_ERROR, (event) => {
|
|
227
|
+
const errorDetail = event?.detail || "Authentication failed";
|
|
228
|
+
const authError = new Error(errorDetail);
|
|
229
|
+
this.setState({
|
|
230
|
+
isLoading: false,
|
|
231
|
+
authStatus: "error",
|
|
232
|
+
error: authError,
|
|
233
|
+
});
|
|
234
|
+
this.callbacks.onSignIn?.(authError);
|
|
235
|
+
});
|
|
236
|
+
this.events.on(AuthEvent.SIGN_OUT_STARTED, () => {
|
|
237
|
+
this.setState({
|
|
238
|
+
isLoading: true,
|
|
239
|
+
authStatus: "signing_out",
|
|
240
|
+
error: null,
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
this.events.on(AuthEvent.SIGN_OUT_COMPLETE, () => {
|
|
244
|
+
this.setState({
|
|
245
|
+
isLoading: false,
|
|
246
|
+
authStatus: "unauthenticated",
|
|
247
|
+
user: null,
|
|
248
|
+
session: null,
|
|
249
|
+
error: null,
|
|
250
|
+
});
|
|
251
|
+
this.callbacks.onSignOut?.();
|
|
252
|
+
});
|
|
253
|
+
this.events.on(AuthEvent.USER_SESSION_CHANGED, () => {
|
|
254
|
+
this.refreshUserAndSession();
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Refresh user and session data
|
|
259
|
+
*/
|
|
260
|
+
async refreshUserAndSession() {
|
|
261
|
+
if (!this.auth)
|
|
262
|
+
return;
|
|
263
|
+
try {
|
|
264
|
+
const [session, user] = await Promise.all([
|
|
265
|
+
this.auth.getCurrentSession(),
|
|
266
|
+
this.auth.getCurrentUser(),
|
|
267
|
+
]);
|
|
268
|
+
this.setState({ session, user });
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
const sessionError = error instanceof Error ? error : new Error("Failed to get session");
|
|
272
|
+
this.setState({ error: sessionError });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Update state and notify listeners
|
|
277
|
+
*/
|
|
278
|
+
setState(updates) {
|
|
279
|
+
this.state = { ...this.state, ...updates };
|
|
280
|
+
this.listeners.forEach((listener) => listener(this.state));
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Cleanup auth instance
|
|
284
|
+
*/
|
|
285
|
+
async cleanup() {
|
|
286
|
+
if (this.auth) {
|
|
287
|
+
await this.auth.destroy();
|
|
288
|
+
this.auth = null;
|
|
289
|
+
}
|
|
290
|
+
this.events = null;
|
|
291
|
+
this.config = null;
|
|
292
|
+
this.initializationPromise = null; // Reset promise for clean re-initialization
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
export { GlobalAuthManager };
|
|
296
|
+
//# sourceMappingURL=GlobalAuthManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GlobalAuthManager.js","sourceRoot":"","sources":["../../../src/reactjs/core/GlobalAuthManager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * GlobalAuthManager - Singleton Authentication State Manager\n *\n * This module provides a global singleton that manages authentication state across a React application\n * without requiring React Context providers or prop drilling. It acts as a centralized authentication\n * manager that wraps the CivicAuth vanilla JavaScript library and provides a React-friendly interface.\n *\n * Key Features:\n * - Singleton pattern ensures single auth instance across the entire app\n * - Eliminates need for React Context providers and reduces bundle size\n * - Manages authentication state (user, session, loading, errors)\n * - Handles sign-in/sign-out flows with event-driven updates\n * - Provides idempotent initialization (safe to call multiple times)\n * - Supports multiple React hooks subscribing to the same auth state\n * - Automatic session refresh and user data synchronization\n *\n * Usage:\n * - Initialize once in your app with authentication configuration\n * - Use React hooks (useCivicAuth, useAuth, etc.) to access auth state\n * - The manager handles all underlying CivicAuth SDK interactions\n * - State updates are automatically propagated to all subscribed components\n *\n * This design pattern is particularly useful for:\n * - Large applications with many components needing auth state\n * - Avoiding provider wrapper hell in React component trees\n * - Ensuring consistent auth state across disconnected component hierarchies\n * - Server-side rendering scenarios where providers might be problematic\n */\n\nimport {\n CivicAuth,\n AuthenticationEvents,\n AuthEvent,\n} from \"../../vanillajs/index.js\";\nimport type {\n User,\n Session,\n CivicAuthClientConfig,\n} from \"../../vanillajs/index.js\";\nimport type { DisplayMode } from \"../../types.js\";\n\n// Event payload interfaces\ninterface SignInErrorEvent {\n detail: string;\n}\n\nexport interface GlobalAuthConfig {\n clientId: string;\n redirectUrl?: string;\n config?: {\n oauthServer?: string;\n };\n displayMode?: DisplayMode;\n iframeMode?: \"modal\" | \"embedded\";\n nonce?: string;\n logoutRedirectUrl?: string;\n scopes?: string[];\n authProcessTimeout?: number;\n onSignIn?: (error?: Error) => void;\n onSignOut?: () => void;\n}\n\nexport type AuthStatus =\n | \"authenticated\"\n | \"unauthenticated\"\n | \"authenticating\"\n | \"error\"\n | \"signing_out\";\n\nexport interface GlobalAuthState {\n user: User | null;\n session: Session | null;\n isLoading: boolean;\n authStatus: AuthStatus;\n error: Error | null;\n displayMode?: DisplayMode;\n}\n\ntype StateListener = (state: GlobalAuthState) => void;\n\n/**\n * Global singleton that manages CivicAuth instance for React hooks\n * Eliminates the need for React providers\n */\nclass GlobalAuthManager {\n private static instance: GlobalAuthManager | null = null;\n private auth: CivicAuth | null = null;\n private events: AuthenticationEvents | null = null;\n private config: GlobalAuthConfig | null = null;\n private listeners = new Set<StateListener>();\n private callbacks: {\n onSignIn?: (error?: Error) => void;\n onSignOut?: () => void;\n } = {};\n private initializationPromise: Promise<void> | null = null;\n\n private state: GlobalAuthState = {\n user: null,\n session: null,\n isLoading: false,\n authStatus: \"unauthenticated\",\n error: null,\n displayMode: undefined,\n };\n\n private constructor() {\n // Private constructor for singleton\n }\n\n static getInstance(): GlobalAuthManager {\n if (!GlobalAuthManager.instance) {\n GlobalAuthManager.instance = new GlobalAuthManager();\n }\n return GlobalAuthManager.instance;\n }\n\n /**\n * Initialize auth with config (idempotent)\n * Returns the same promise for concurrent calls with same config\n */\n async initialize(config: GlobalAuthConfig): Promise<void> {\n // If we have a promise and same config, return existing promise\n if (\n this.initializationPromise &&\n this.config &&\n this.isSameConfig(config)\n ) {\n return this.initializationPromise;\n }\n\n // If different config, reset everything\n if (this.config && !this.isSameConfig(config)) {\n this.initializationPromise = null;\n await this.cleanup();\n }\n\n // Create new initialization promise if needed\n if (!this.initializationPromise) {\n this.initializationPromise = this._doInitialize(config);\n }\n\n return this.initializationPromise;\n }\n\n /**\n * Private method that does the actual initialization work\n */\n private async _doInitialize(config: GlobalAuthConfig): Promise<void> {\n this.config = config;\n this.callbacks = {\n onSignIn: config.onSignIn,\n onSignOut: config.onSignOut,\n };\n this.setState({ isLoading: true, error: null });\n\n try {\n this.events = new AuthenticationEvents();\n this.setupEventListeners();\n\n const authConfig: CivicAuthClientConfig = {\n clientId: config.clientId,\n oauthServerBaseUrl: config.config?.oauthServer,\n scopes: config.scopes || [\n \"openid\",\n \"profile\",\n \"email\",\n \"offline_access\",\n ],\n displayMode: config.displayMode || \"iframe\",\n iframeDisplayMode: config.iframeMode || \"modal\",\n nonce: config.nonce,\n authProcessTimeout: config.authProcessTimeout || 120000,\n events: this.events,\n logging: {\n enabled: true,\n level: \"debug\" as const,\n },\n };\n\n // Only add redirectUrl if provided\n if (config.redirectUrl) {\n authConfig.redirectUrl = config.redirectUrl;\n }\n\n // Only add logoutRedirectUrl if provided\n if (config.logoutRedirectUrl) {\n authConfig.logoutRedirectUrl = config.logoutRedirectUrl;\n }\n\n this.auth = await CivicAuth.create(authConfig);\n\n // Check initial auth state\n const isAuthenticated = await this.auth.isAuthenticated();\n if (isAuthenticated) {\n await this.refreshUserAndSession();\n this.setState({ authStatus: \"authenticated\" });\n } else {\n this.setState({ authStatus: \"unauthenticated\" });\n }\n\n this.setState({\n isLoading: false,\n displayMode: config.displayMode,\n });\n } catch (error) {\n // Reset promise on error so it can be retried\n this.initializationPromise = null;\n\n const authError =\n error instanceof Error\n ? error\n : new Error(\"Auth initialization failed\");\n this.setState({\n error: authError,\n authStatus: \"error\",\n isLoading: false,\n });\n throw authError;\n }\n }\n\n /**\n * Subscribe to state changes\n */\n subscribe(listener: StateListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Get current state\n */\n getState(): GlobalAuthState {\n return { ...this.state };\n }\n\n /**\n * Sign in\n */\n async signIn(): Promise<{ user: User }> {\n if (!this.auth) {\n throw new Error(\"Auth not initialized\");\n }\n\n const { user } = await this.auth.startAuthentication();\n await this.refreshUserAndSession();\n\n // Ensure we have a user to return\n if (!user) {\n throw new Error(\"Authentication succeeded but no user was returned\");\n }\n\n // Return the user object\n return { user };\n }\n\n /**\n * Sign out\n */\n async signOut(): Promise<void> {\n if (!this.auth) {\n throw new Error(\"Auth not initialized\");\n }\n\n await this.auth.logout();\n this.setState({\n user: null,\n session: null,\n authStatus: \"unauthenticated\",\n });\n }\n\n /**\n * Check if config is the same (for idempotent initialization)\n */\n private isSameConfig(newConfig: GlobalAuthConfig): boolean {\n if (!this.config) return false;\n return (\n this.config.clientId === newConfig.clientId &&\n this.config.config?.oauthServer === newConfig.config?.oauthServer &&\n this.config.displayMode === newConfig.displayMode &&\n this.config.nonce === newConfig.nonce\n );\n }\n\n /**\n * Setup event listeners for auth state changes\n */\n private setupEventListeners(): void {\n if (!this.events) return;\n\n this.events.on(AuthEvent.SIGN_IN_STARTED, () => {\n this.setState({\n isLoading: true,\n authStatus: \"authenticating\",\n error: null,\n });\n });\n\n this.events.on(AuthEvent.SIGN_IN_COMPLETE, () => {\n this.setState({\n isLoading: false,\n authStatus: \"authenticated\",\n error: null,\n });\n this.refreshUserAndSession();\n this.callbacks.onSignIn?.();\n });\n\n this.events.on(AuthEvent.SIGN_IN_ERROR, (event?: SignInErrorEvent) => {\n const errorDetail = event?.detail || \"Authentication failed\";\n const authError = new Error(errorDetail);\n this.setState({\n isLoading: false,\n authStatus: \"error\",\n error: authError,\n });\n this.callbacks.onSignIn?.(authError);\n });\n\n this.events.on(AuthEvent.SIGN_OUT_STARTED, () => {\n this.setState({\n isLoading: true,\n authStatus: \"signing_out\",\n error: null,\n });\n });\n\n this.events.on(AuthEvent.SIGN_OUT_COMPLETE, () => {\n this.setState({\n isLoading: false,\n authStatus: \"unauthenticated\",\n user: null,\n session: null,\n error: null,\n });\n this.callbacks.onSignOut?.();\n });\n\n this.events.on(AuthEvent.USER_SESSION_CHANGED, () => {\n this.refreshUserAndSession();\n });\n }\n\n /**\n * Refresh user and session data\n */\n private async refreshUserAndSession(): Promise<void> {\n if (!this.auth) return;\n\n try {\n const [session, user] = await Promise.all([\n this.auth.getCurrentSession(),\n this.auth.getCurrentUser(),\n ]);\n\n this.setState({ session, user });\n } catch (error) {\n const sessionError =\n error instanceof Error ? error : new Error(\"Failed to get session\");\n this.setState({ error: sessionError });\n }\n }\n\n /**\n * Update state and notify listeners\n */\n private setState(updates: Partial<GlobalAuthState>): void {\n this.state = { ...this.state, ...updates };\n this.listeners.forEach((listener) => listener(this.state));\n }\n\n /**\n * Cleanup auth instance\n */\n private async cleanup(): Promise<void> {\n if (this.auth) {\n await this.auth.destroy();\n this.auth = null;\n }\n this.events = null;\n this.config = null;\n this.initializationPromise = null; // Reset promise for clean re-initialization\n }\n}\n\nexport { GlobalAuthManager };\n"]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { useUser } from "
|
|
2
|
-
export { useToken } from "
|
|
1
|
+
export { useUser, type UserContextType } from "./useUser.js";
|
|
2
|
+
export { useToken, type TokenContextType } from "./useToken.js";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/reactjs/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/reactjs/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { useUser } from "
|
|
2
|
-
export { useToken } from "
|
|
1
|
+
export { useUser } from "./useUser.js";
|
|
2
|
+
export { useToken } from "./useToken.js";
|
|
3
3
|
//# sourceMappingURL=index.js.map
|