@replyke/react-js
Version:
Replyke: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.
104 lines • 5.07 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = require("react");
const core_1 = require("@replyke/core");
const BASE_URL = "https://api.replyke.com/v7";
/**
* Web-only hook for OAuth sign-in and identity linking.
* Uses window.location for redirect-based OAuth flow.
*
* Usage (sign-in):
* const { initiateOAuth, handleOAuthCallback } = useOAuthSignIn();
* await initiateOAuth("google", "https://myapp.com/auth/callback");
*
* Usage (link provider to current user):
* const { linkOAuthProvider, handleOAuthCallback } = useOAuthSignIn();
* await linkOAuthProvider("github", "https://myapp.com/settings");
*
* On the callback page (component mount):
* useEffect(() => { handleOAuthCallback(); }, []);
*/
function useOAuthSignIn() {
const { projectId } = (0, core_1.useProject)();
const dispatch = (0, core_1.useReplykeDispatch)();
const accessToken = (0, core_1.useReplykeSelector)(core_1.selectAccessToken);
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
const [error, setError] = (0, react_1.useState)(null);
// Shared helper for both /authorize and /link endpoints
const startOAuthFlow = (0, react_1.useCallback)(async (endpoint, provider, redirectAfterAuth) => {
if (!projectId) {
setError("No projectId available.");
return;
}
if (endpoint === "link" && !accessToken) {
setError("Must be authenticated to link an OAuth provider.");
return;
}
setIsLoading(true);
setError(null);
try {
const redirect = redirectAfterAuth || window.location.href;
// /authorize is unauthenticated, /link requires the access token
const headers = {
"Content-Type": "application/json",
};
if (endpoint === "link") {
headers["Authorization"] = `Bearer ${accessToken}`;
}
const response = await fetch(`${BASE_URL}/${projectId}/oauth/${endpoint}`, {
method: "POST",
headers,
body: JSON.stringify({ provider, redirectAfterAuth: redirect }),
});
if (!response.ok) {
const data = await response.json();
throw new Error(data.error || "Failed to initiate OAuth");
}
const data = await response.json();
// Redirect browser to provider's authorization page.
// isLoading intentionally stays true since we're navigating away.
window.location.href = data.authorizationUrl;
}
catch (err) {
setError(err.message);
setIsLoading(false);
}
}, [projectId, accessToken]);
const initiateOAuth = (0, react_1.useCallback)(({ provider, redirectAfterAuth }) => startOAuthFlow("authorize", provider, redirectAfterAuth), [startOAuthFlow]);
const linkOAuthProvider = (0, react_1.useCallback)(({ provider, redirectAfterAuth }) => startOAuthFlow("link", provider, redirectAfterAuth), [startOAuthFlow]);
const handleOAuthCallback = (0, react_1.useCallback)(() => {
// Tokens arrive in the URL fragment (#accessToken=...&refreshToken=...)
// Errors arrive in query params (?error=...&error_description=...)
const hash = window.location.hash.substring(1); // Remove leading #
const fragmentParams = new URLSearchParams(hash);
const queryParams = new URLSearchParams(window.location.search);
const fragmentAccessToken = fragmentParams.get("accessToken");
const refreshToken = fragmentParams.get("refreshToken");
const oauthError = queryParams.get("error");
if (oauthError) {
setError(queryParams.get("error_description") || oauthError);
// Clean URL
window.history.replaceState({}, "", window.location.pathname);
return false;
}
if (fragmentAccessToken && refreshToken) {
// Store tokens in Redux. The AccountManager (via useAccountSync)
// will detect the new tokens and persist them to localStorage.
dispatch((0, core_1.setTokens)({ accessToken: fragmentAccessToken, refreshToken }));
dispatch((0, core_1.setInitialized)(true));
// Fetch user profile so useAccountSync can persist the account.
// The thunk reads the just-set refresh token from Redux, calls the
// server, and dispatches setUser + setUserInUserSlice on success.
if (projectId) {
dispatch((0, core_1.requestNewAccessTokenThunk)({ projectId }));
}
// Clean URL (remove fragment with tokens)
window.history.replaceState({}, "", window.location.pathname);
return true;
}
return false;
}, [dispatch, projectId]);
return { initiateOAuth, linkOAuthProvider, handleOAuthCallback, isLoading, error };
}
exports.default = useOAuthSignIn;
//# sourceMappingURL=useOAuthSignIn.js.map