UNPKG

membros-react-sdk

Version:

React authentication library for Membros platform with subscription management and plan-based access control

306 lines 13.5 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MembrosProvider = exports.signOut = exports.useAuth = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const cookies_1 = require("./utils/cookies.cjs"); const sonner_1 = require("sonner"); const constants_1 = require("./constants.cjs"); const AuthContext = (0, react_1.createContext)(undefined); const useAuth = () => { const context = (0, react_1.useContext)(AuthContext); if (!context) { throw new Error("useAuth must be used within an MembrosProvider"); } return context; }; exports.useAuth = useAuth; const signOut = () => { (0, cookies_1.destroyCookie)(null, "nextauth.token"); (0, cookies_1.destroyCookie)(null, "nextauth.refreshToken"); window.location.reload(); }; exports.signOut = signOut; const MembrosProvider = ({ children, clientId, authorizationParams, }) => { const [user, setUser] = (0, react_1.useState)(null); const [token, setToken] = (0, react_1.useState)(null); const [internalIsLoadingUser, setInternalIsLoadingUser] = (0, react_1.useState)(true); const [internalIsLoadingSubscriptions, setInternalIsLoadingSubscriptions] = (0, react_1.useState)(true); const [error, setError] = (0, react_1.useState)(null); const [adimplent, setAdimplent] = (0, react_1.useState)(false); const [originalUser, setOriginalUser] = (0, react_1.useState)(null); const [userSubscriptions, setUserSubscriptions] = (0, react_1.useState)([]); const [isLoggingOut, setIsLoggingOut] = (0, react_1.useState)(false); const isAuthenticated = !!user; const isLoading = internalIsLoadingUser || internalIsLoadingSubscriptions; (0, react_1.useEffect)(() => { const loadUserFromCookies = async () => { setInternalIsLoadingUser(true); setInternalIsLoadingSubscriptions(true); // First check for authorization code in URL (for redirect flow) const urlParams = new URLSearchParams(window.location.search); const authCode = urlParams.get('code'); if (authCode) { console.log("Found authorization code in URL, processing..."); await login(authCode); // Clean up URL const newUrl = window.location.pathname; window.history.replaceState({}, document.title, newUrl); return; } // If no auth code, check for existing token in cookies const { "nextauth.token": accessToken } = (0, cookies_1.parseCookies)(); if (accessToken) { await loadUserByToken(accessToken); } else { setInternalIsLoadingUser(false); setInternalIsLoadingSubscriptions(false); } }; loadUserFromCookies(); }, []); const loadUserSubscriptions = async (email, currentToken) => { setInternalIsLoadingSubscriptions(true); try { const response = await fetch(`${constants_1.MEMBROS_API_URL}/subscription/account/email/${email}`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${currentToken}`, }, }); if (response.status === 404) { setUserSubscriptions([]); return; } const subscriptions = await response.json(); setUserSubscriptions(subscriptions); } catch (error) { console.error("An unexpected error occurred while loading subscriptions", error); setUserSubscriptions([]); } finally { setInternalIsLoadingSubscriptions(false); } }; const loadUserByToken = async (accessToken) => { try { setError(null); setInternalIsLoadingUser(true); setToken(accessToken); (0, cookies_1.setCookie)(null, "nextauth.token", accessToken, { path: "/", maxAge: 60 * 60 * 24 * 30, // 30 days in seconds }); const response = await fetch(`${constants_1.MEMBROS_API_URL}/whoami`, { headers: { Authorization: `Bearer ${accessToken}` }, }); if (response.status === 401) { (0, exports.signOut)(); setInternalIsLoadingUser(false); setInternalIsLoadingSubscriptions(false); return; } if (response.ok) { const userData = await response.json(); setUser(userData); setInternalIsLoadingUser(false); await loadUserSubscriptions(userData.email, accessToken); } else { console.error("Failed to load user data"); setUser(null); setToken(null); (0, cookies_1.destroyCookie)(null, "nextauth.token"); (0, cookies_1.destroyCookie)(null, "nextauth.refreshToken"); setInternalIsLoadingUser(false); setInternalIsLoadingSubscriptions(false); } } catch (error) { console.error("Error in loadUserByToken:", error); setError(error); setUser(null); setToken(null); (0, cookies_1.destroyCookie)(null, "nextauth.token"); (0, cookies_1.destroyCookie)(null, "nextauth.refreshToken"); setInternalIsLoadingUser(false); setInternalIsLoadingSubscriptions(false); } }; const loginWithRedirect = async (options) => { var _a; const redirectUri = ((_a = options === null || options === void 0 ? void 0 : options.authorizationParams) === null || _a === void 0 ? void 0 : _a.redirect_uri) || (options === null || options === void 0 ? void 0 : options.redirectUri) || (authorizationParams === null || authorizationParams === void 0 ? void 0 : authorizationParams.redirect_uri) || window.location.origin; // Use the OAuth2 page with redirect flow const authUrl = `http://localhost:3003/oauth2/${clientId}?flow=redirect&redirect_uri=${encodeURIComponent(redirectUri)}`; window.location.href = authUrl; }; const loginWithPopup = async (options) => { return new Promise((resolve, reject) => { const redirectOrigin = window.location.origin; // Use the OAuth2 page with popup flow const authUrl = `http://localhost:3003/oauth2/${clientId}?flow=popup&redirect_origin=${encodeURIComponent(redirectOrigin)}`; const popup = window.open(authUrl, "auth-popup", "width=500,height=600"); const checkClosed = setInterval(() => { if (popup === null || popup === void 0 ? void 0 : popup.closed) { clearInterval(checkClosed); reject(new Error("Popup was closed")); } }, 1000); const messageHandler = (event) => { // Allow localhost for debugging if (event.origin !== "http://localhost:3003") return; if (event.data.type === "oauth" && event.data.code) { clearInterval(checkClosed); popup === null || popup === void 0 ? void 0 : popup.close(); window.removeEventListener("message", messageHandler); login(event.data.code).then(() => resolve()).catch(reject); } }; window.addEventListener("message", messageHandler); }); }; const getAccessTokenSilently = async (options) => { if (!token) { throw new Error("No access token available. User might need to log in again."); } return token; }; const login = async (authorizationCode) => { try { // For debugging with mock codes, simulate token exchange if (authorizationCode.startsWith("auth_code_")) { // Simulate a successful token exchange const mockToken = `mock_token_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const mockRefreshToken = `mock_refresh_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; (0, cookies_1.setCookie)(null, "nextauth.token", mockToken, { path: "/", maxAge: 60 * 60 * 24 * 30, // 30 days in seconds }); (0, cookies_1.setCookie)(null, "nextauth.refreshToken", mockRefreshToken, { path: "/", maxAge: 60 * 60 * 24 * 30, // 30 days in seconds }); // Mock user data for testing const mockUser = { id: "mock-user-id", name: "Mock User", email: "mock@example.com", plano: "vestibulando", }; setUser(mockUser); setToken(mockToken); setInternalIsLoadingUser(false); setInternalIsLoadingSubscriptions(false); sonner_1.toast.success("Login Successful", { description: "You are now logged in (mock mode)." }); return; } // Real token exchange for production const res = await fetch(`${constants_1.MEMBROS_API_URL}/user/auth/token`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ authorization_code: authorizationCode }), }); const responseData = await res.json(); if (res.ok && responseData.access_token) { (0, cookies_1.setCookie)(null, "nextauth.token", responseData.access_token, { path: "/", maxAge: 60 * 60 * 24 * 30, // 30 days in seconds }); (0, cookies_1.setCookie)(null, "nextauth.refreshToken", responseData.refresh_token, { path: "/", maxAge: 60 * 60 * 24 * 30, // 30 days in seconds }); await loadUserByToken(responseData.access_token); sonner_1.toast.success("Login Successful", { description: "You are now logged in." }); } else { sonner_1.toast.error("Login Failed", { description: responseData.message || "Failed to retrieve token." }); } } catch (error) { console.error("Login error:", error); sonner_1.toast.error("Server Error", { description: "An error occurred while fetching the token." }); } }; const logout = (options) => { var _a; setIsLoggingOut(true); (0, cookies_1.destroyCookie)(null, "nextauth.token"); (0, cookies_1.destroyCookie)(null, "nextauth.refreshToken"); setUser(null); setToken(null); setUserSubscriptions([]); setOriginalUser(null); setInternalIsLoadingUser(false); setInternalIsLoadingSubscriptions(false); const returnTo = ((_a = options === null || options === void 0 ? void 0 : options.logoutParams) === null || _a === void 0 ? void 0 : _a.returnTo) || (options === null || options === void 0 ? void 0 : options.returnTo) || window.location.origin; if (typeof window !== "undefined") { window.location.href = returnTo; } }; const hasActivePlan = (planIds) => { if (!userSubscriptions || userSubscriptions.length === 0) { return false; } if (!planIds || planIds.length === 0) { return userSubscriptions.some(sub => sub.status === 'active'); } for (const subscription of userSubscriptions) { const subscriptionPlanId = String(subscription.plan.id); if (planIds.includes(subscriptionPlanId)) { if (subscription.status === 'active') { return true; } } } return false; }; const overwriteUser = (newUser) => { if (!originalUser) { setOriginalUser(user); } setUser(newUser); if (typeof window !== "undefined") { window.location.href = "/"; } }; const revertToOriginalUser = () => { if (originalUser) { setUser(originalUser); setOriginalUser(null); } }; return ((0, jsx_runtime_1.jsxs)(AuthContext.Provider, { value: { user, isAuthenticated, isLoading, isLoggingOut, error, loginWithRedirect, loginWithPopup, logout, getAccessTokenSilently, originalUser, token, login, loadUserByToken, adimplent, overwriteUser, revertToOriginalUser, publicKey: clientId, hasActivePlan, userSubscriptions, }, children: [(0, jsx_runtime_1.jsx)(sonner_1.Toaster, { richColors: true }), children] })); }; exports.MembrosProvider = MembrosProvider; //# sourceMappingURL=AuthContext.js.map