@opble/auth0-react 1.1.5 → 1.1.6
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/dist/components/AccessForbidden.js +6 -0
- package/dist/components/AuthClearing.js +20 -0
- package/dist/components/AuthError.js +7 -0
- package/dist/components/AuthLoading.js +4 -0
- package/dist/components/ProtectedRoute.js +79 -0
- package/dist/components/Unauthorized.js +7 -0
- package/dist/helpers/jwt.js +27 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -7784
- package/dist/slices/auth.js +19 -0
- package/dist/slices/selectors.js +3 -0
- package/dist/slices/types.js +1 -0
- package/dist/style.css +893 -0
- package/dist/util/rtk.js +21 -0
- package/package.json +8 -9
- package/dist/index.css +0 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth0 } from '@auth0/auth0-react';
|
|
3
|
+
export function AccessForbidden({ homeUrl = '/', supportEmail, supportUrl, } = {}) {
|
|
4
|
+
const { logout } = useAuth0();
|
|
5
|
+
return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-linear-to-b from-white to-gray-100 dark:from-slate-900 dark:to-slate-800 transition-colors px-4", children: _jsxs("div", { className: "w-full max-w-lg p-8 rounded-2xl shadow-xl border border-gray-100 dark:border-slate-700 bg-white/70 dark:bg-slate-900/60 backdrop-blur-sm", children: [_jsxs("div", { className: "flex flex-col items-center text-center", children: [_jsx("div", { className: "shrink-0 h-16 w-16 rounded-full bg-red-100 dark:bg-red-900/20 flex items-center justify-center mb-4", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-8 w-8 text-red-600 dark:text-red-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }), _jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] }) }), _jsx("h1", { className: "text-3xl font-bold text-slate-900 dark:text-slate-100 mb-2", children: "Access Forbidden" }), _jsx("p", { className: "text-base text-slate-600 dark:text-slate-300 mb-6", children: "You don't have permission to access this page. Please contact your administrator if you believe this is an error." }), _jsxs("div", { className: "inline-flex items-center gap-2 px-4 py-2 rounded-full bg-red-50 dark:bg-red-900/10 border border-red-200 dark:border-red-800 mb-8", children: [_jsx("svg", { className: "h-4 w-4 text-red-600 dark:text-red-400", fill: "currentColor", viewBox: "0 0 20 20", children: _jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z", clipRule: "evenodd" }) }), _jsx("span", { className: "text-sm font-medium text-red-700 dark:text-red-300", children: "Error 403" })] }), _jsxs("div", { className: "flex flex-col gap-4 w-full", children: [_jsxs("div", { className: "flex flex-col sm:flex-row gap-3", children: [_jsxs("button", { onClick: () => (window.location.href = homeUrl), className: "flex-1 px-6 py-3 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white font-medium transition-colors shadow-lg shadow-indigo-500/20 cursor-pointer flex items-center justify-center gap-2", children: [_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), _jsx("polyline", { points: "9 22 9 12 15 12 15 22" })] }), "Go to Home"] }), _jsxs("button", { onClick: () => logout(), className: "flex-1 px-6 py-3 rounded-lg bg-red-600 hover:bg-red-700 dark:bg-red-700/80 dark:hover:bg-red-700 text-white font-medium transition-colors shadow-lg shadow-red-500/20 dark:shadow-red-900/30 cursor-pointer flex items-center justify-center gap-2", children: ["Sign Out", _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }), _jsx("polyline", { points: "16 17 21 12 16 7" }), _jsx("line", { x1: "21", y1: "12", x2: "9", y2: "12" })] })] })] }), _jsx("button", { onClick: () => window.history.back(), className: "text-sm text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200 underline underline-offset-4 transition-colors cursor-pointer", children: "\u2190 Go back to previous page" })] })] }), (supportEmail || supportUrl) && (_jsx("div", { className: "mt-8 pt-6 border-t border-slate-200 dark:border-slate-700", children: _jsxs("p", { className: "text-sm text-slate-500 dark:text-slate-400 text-center", children: ["Need help?", ' ', _jsx("a", { href: supportUrl || `mailto:${supportEmail}`, className: "text-indigo-600 dark:text-indigo-400 hover:underline font-medium cursor-pointer", children: "Contact support" })] }) }))] }) }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function AuthClearing() {
|
|
3
|
+
return (_jsxs("div", { className: "min-h-screen flex items-center justify-center bg-linear-to-b from-white to-gray-100 dark:from-slate-900 dark:to-slate-800 transition-colors", children: [_jsx("div", { className: "w-full max-w-lg p-8 rounded-2xl shadow-xl border border-gray-100 dark:border-slate-700 bg-white/70 dark:bg-slate-900/60 backdrop-blur-sm", children: _jsxs("div", { className: "flex flex-col items-center text-center", children: [_jsxs("div", { className: "relative mb-6", children: [_jsx("div", { className: "shrink-0 h-16 w-16 rounded-full bg-indigo-100 dark:bg-indigo-900/20 flex items-center justify-center", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-8 w-8 text-indigo-600 dark:text-indigo-400 animate-pulse", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }), _jsx("polyline", { points: "16 17 21 12 16 7" }), _jsx("line", { x1: "21", y1: "12", x2: "9", y2: "12" })] }) }), _jsx("div", { className: "absolute inset-0 rounded-full bg-indigo-400/30 dark:bg-indigo-600/20 animate-ping" })] }), _jsx("h1", { className: "text-2xl font-bold text-slate-900 dark:text-slate-100 mb-2", children: "Signing You Out" }), _jsx("p", { className: "text-base text-slate-600 dark:text-slate-300 mb-6", children: "Please wait while we securely clear your session..." }), _jsx("div", { className: "w-full max-w-xs", children: _jsx("div", { className: "h-1.5 bg-slate-200 dark:bg-slate-700 rounded-full overflow-hidden", children: _jsx("div", { className: "h-full bg-indigo-600 dark:bg-indigo-500 rounded-full animate-progress" }) }) }), _jsx("p", { className: "mt-6 text-sm text-slate-500 dark:text-slate-400", children: "You'll be redirected shortly" })] }) }), _jsx("style", { children: `
|
|
4
|
+
@keyframes progress {
|
|
5
|
+
0% {
|
|
6
|
+
width: 0%;
|
|
7
|
+
transform: translateX(0);
|
|
8
|
+
}
|
|
9
|
+
50% {
|
|
10
|
+
width: 70%;
|
|
11
|
+
}
|
|
12
|
+
100% {
|
|
13
|
+
width: 100%;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
.animate-progress {
|
|
17
|
+
animation: progress 1.5s ease-in-out infinite;
|
|
18
|
+
}
|
|
19
|
+
` })] }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth0 } from '@auth0/auth0-react';
|
|
3
|
+
export function AuthError({ homeUrl = '/', supportEmail, supportUrl, error = null, } = {}) {
|
|
4
|
+
const { loginWithRedirect } = useAuth0();
|
|
5
|
+
return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-linear-to-b from-white to-gray-100 dark:from-slate-900 dark:to-slate-800 transition-colors px-4", children: _jsxs("div", { className: "w-full max-w-lg p-8 rounded-2xl shadow-xl border border-gray-100 dark:border-slate-700 bg-white/70 dark:bg-slate-900/60 backdrop-blur-sm", children: [_jsxs("div", { className: "flex flex-col items-center text-center", children: [_jsx("div", { className: "shrink-0 h-16 w-16 rounded-full bg-red-50 dark:bg-red-900/10 flex items-center justify-center mb-4", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-8 w-8 text-red-600 dark:text-red-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 8v4" }), _jsx("path", { d: "M12 16h.01" })] }) }), _jsx("h1", { className: "text-3xl font-bold text-slate-900 dark:text-slate-100 mb-2", children: "Authentication Error" }), _jsx("p", { className: "text-base text-slate-600 dark:text-slate-300 mb-6", children: "Authentication failed. Sign out and sign back in to resolve transient errors, or return home." }), error && (_jsx("div", { className: "mb-4 w-full px-4 py-3 rounded-md bg-red-50 dark:bg-red-900/10 border border-red-100 dark:border-red-800 text-sm text-red-700 dark:text-red-200", children: typeof error === 'string' ? error : error.message })), _jsxs("div", { className: "flex flex-col gap-4 w-full", children: [_jsxs("div", { className: "flex flex-col sm:flex-row gap-3", children: [_jsxs("button", { onClick: () => (window.location.href = homeUrl), className: "flex-1 px-6 py-3 rounded-lg bg-transparent border border-gray-200 dark:border-white/5 text-slate-700 dark:text-slate-200 hover:bg-gray-50 dark:hover:bg-slate-800 font-medium transition-colors shadow-sm cursor-pointer flex items-center justify-center gap-2", children: [_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), _jsx("polyline", { points: "9 22 9 12 15 12 15 22" })] }), "Go to Home"] }), _jsxs("button", { onClick: () => loginWithRedirect(), className: "flex-1 px-6 py-3 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white font-medium transition-colors shadow-lg shadow-indigo-500/20 dark:bg-indigo-500 dark:hover:bg-indigo-600 cursor-pointer flex items-center justify-center gap-2", children: ["Sign In", _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" }), _jsx("polyline", { points: "10 17 15 12 10 7" }), _jsx("line", { x1: "15", y1: "12", x2: "3", y2: "12" })] })] })] }), _jsx("button", { onClick: () => window.history.back(), className: "text-sm text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200 underline underline-offset-4 transition-colors cursor-pointer", children: "\u2190 Go back to previous page" })] })] }), (supportEmail || supportUrl) && (_jsx("div", { className: "mt-8 pt-6 border-t border-slate-200 dark:border-slate-700", children: _jsxs("p", { className: "text-sm text-slate-500 dark:text-slate-400 text-center", children: ["Need help?", ' ', _jsx("a", { href: supportUrl || `mailto:${supportEmail}`, className: "text-indigo-600 dark:text-indigo-400 hover:underline font-medium cursor-pointer", children: "Contact support" })] }) }))] }) }));
|
|
6
|
+
}
|
|
7
|
+
export default AuthError;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function AuthLoading() {
|
|
3
|
+
return (_jsxs("div", { className: "min-h-screen flex items-center justify-center bg-linear-to-b from-white to-gray-100 dark:from-slate-900 dark:to-slate-800 transition-colors", children: [_jsxs("div", { className: "w-full max-w-lg p-8 rounded-2xl shadow-xl border border-gray-100 dark:border-slate-700 bg-white/70 dark:bg-slate-900/60 backdrop-blur-sm", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "shrink-0 h-14 w-14 rounded-lg bg-indigo-600 flex items-center justify-center text-white", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-7 w-7", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 2v10l3-3" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 12v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7" })] }) }), _jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-slate-900 dark:text-slate-100", children: "Signing you in" }), _jsx("p", { className: "mt-1 text-sm text-slate-600 dark:text-slate-300", children: "Hang tight \u2014 we're securely authenticating your account." })] })] }), _jsxs("div", { className: "mt-8 flex flex-col items-center", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("svg", { className: "h-8 w-8 text-indigo-600 animate-spin", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" })] }), _jsx("span", { className: "text-sm text-slate-700 dark:text-slate-200", children: "Authenticating\u2026" })] }), _jsx("p", { className: "mt-4 text-xs text-slate-500 dark:text-slate-400 text-center", children: "If this takes longer than a few seconds, try refreshing the page or checking your network connection." })] })] }), _jsx("div", { className: "absolute bottom-6 text-xs text-slate-500 dark:text-slate-400", children: "opble \u2022 secure authentication" })] }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth0 } from '@auth0/auth0-react';
|
|
3
|
+
import { SerializableUserFactory } from '@opble/entity-auth';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
6
|
+
import { decodeJWT } from '../helpers/jwt';
|
|
7
|
+
import { setAccessToken, setUser } from '../slices/auth';
|
|
8
|
+
import { selectAccessToken, selectUser } from '../slices/selectors';
|
|
9
|
+
import { AccessForbidden } from './AccessForbidden';
|
|
10
|
+
import AuthError from './AuthError';
|
|
11
|
+
import { AuthLoading } from './AuthLoading';
|
|
12
|
+
import { Unauthorized } from './Unauthorized';
|
|
13
|
+
/**
|
|
14
|
+
* Component to protect routes that require authentication.
|
|
15
|
+
*
|
|
16
|
+
* Usage in route configuration:
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <Route
|
|
19
|
+
* path="/dashboard"
|
|
20
|
+
* element={
|
|
21
|
+
* <ProtectedRoute>
|
|
22
|
+
* <Dashboard />
|
|
23
|
+
* </ProtectedRoute>
|
|
24
|
+
* }
|
|
25
|
+
* />
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function ProtectedRoute({ children, requiredPermissions, }) {
|
|
29
|
+
const dispatch = useDispatch();
|
|
30
|
+
const accessToken = useSelector(selectAccessToken);
|
|
31
|
+
const user = useSelector(selectUser);
|
|
32
|
+
const { isAuthenticated, isLoading, getAccessTokenSilently, getIdTokenClaims, } = useAuth0();
|
|
33
|
+
const [error, setError] = useState(null);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const loadAuthData = async () => {
|
|
36
|
+
if (isAuthenticated) {
|
|
37
|
+
try {
|
|
38
|
+
const idToken = await getIdTokenClaims();
|
|
39
|
+
const token = await getAccessTokenSilently();
|
|
40
|
+
const payload = decodeJWT(token);
|
|
41
|
+
if (payload && idToken) {
|
|
42
|
+
dispatch(setUser(SerializableUserFactory.create({
|
|
43
|
+
id: idToken.sub,
|
|
44
|
+
email: idToken.email,
|
|
45
|
+
name: idToken.name,
|
|
46
|
+
emailVerified: idToken.email_verified,
|
|
47
|
+
permissions: payload['permissions'] || [],
|
|
48
|
+
})));
|
|
49
|
+
}
|
|
50
|
+
dispatch(setAccessToken(token));
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
console.error('Failed to get access token silently:', e);
|
|
54
|
+
setError('Failed to get access token silently.');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
loadAuthData();
|
|
59
|
+
}, [isAuthenticated, getAccessTokenSilently, getIdTokenClaims, dispatch]);
|
|
60
|
+
if (isLoading) {
|
|
61
|
+
return _jsx(AuthLoading, {});
|
|
62
|
+
}
|
|
63
|
+
else if (error) {
|
|
64
|
+
return _jsx(AuthError, { error: error });
|
|
65
|
+
}
|
|
66
|
+
else if (!isAuthenticated) {
|
|
67
|
+
return _jsx(Unauthorized, {});
|
|
68
|
+
}
|
|
69
|
+
else if (!accessToken) {
|
|
70
|
+
return _jsx(AuthLoading, {});
|
|
71
|
+
}
|
|
72
|
+
else if (requiredPermissions && user) {
|
|
73
|
+
const hasAllPermissions = requiredPermissions.every((perm) => user.permissions.includes(perm));
|
|
74
|
+
if (!hasAllPermissions) {
|
|
75
|
+
return _jsx(AccessForbidden, {});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return _jsx(_Fragment, { children: children });
|
|
79
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth0 } from '@auth0/auth0-react';
|
|
3
|
+
export function Unauthorized({ homeUrl = '/', supportEmail, supportUrl, } = {}) {
|
|
4
|
+
const { loginWithRedirect } = useAuth0();
|
|
5
|
+
return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-linear-to-b from-white to-gray-100 dark:from-slate-900 dark:to-slate-800 transition-colors px-4", children: _jsxs("div", { className: "w-full max-w-lg p-8 rounded-2xl shadow-xl border border-gray-100 dark:border-slate-700 bg-white/70 dark:bg-slate-900/60 backdrop-blur-sm", children: [_jsxs("div", { className: "flex flex-col items-center text-center", children: [_jsx("div", { className: "shrink-0 h-16 w-16 rounded-full bg-yellow-50 dark:bg-yellow-900/10 flex items-center justify-center mb-4", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-8 w-8 text-yellow-600 dark:text-yellow-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M12 2l7 4v6c0 5-3.6 9.7-7 11-3.4-1.3-7-6-7-11V6l7-4z" }), _jsx("path", { d: "M9.5 11.5l1.5 1.5 3-3" })] }) }), _jsx("h1", { className: "text-3xl font-bold text-slate-900 dark:text-slate-100 mb-2", children: "Unauthorized" }), _jsx("p", { className: "text-base text-slate-600 dark:text-slate-300 mb-6", children: "You need to sign in to access this page. Please sign in with your account or return home." }), _jsxs("div", { className: "flex flex-col gap-4 w-full", children: [_jsxs("div", { className: "flex flex-col sm:flex-row gap-3", children: [_jsxs("button", { onClick: () => (window.location.href = homeUrl), className: "flex-1 px-6 py-3 rounded-lg bg-transparent border border-gray-200 dark:border-white/5 text-slate-700 dark:text-slate-200 hover:bg-gray-50 dark:hover:bg-slate-800 font-medium transition-colors shadow-sm cursor-pointer flex items-center justify-center gap-2", children: [_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), _jsx("polyline", { points: "9 22 9 12 15 12 15 22" })] }), "Go to Home"] }), _jsxs("button", { onClick: () => loginWithRedirect(), className: "flex-1 px-6 py-3 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-slate-700 dark:text-slate-200 font-medium transition-colors shadow-lg shadow-indigo-500/20 dark:bg-indigo-500 dark:hover:bg-indigo-600 cursor-pointer flex items-center justify-center gap-2", children: ["Sign In", _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" }), _jsx("polyline", { points: "10 17 15 12 10 7" }), _jsx("line", { x1: "15", y1: "12", x2: "3", y2: "12" })] })] })] }), _jsx("button", { onClick: () => window.history.back(), className: "text-sm text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200 underline underline-offset-4 transition-colors cursor-pointer", children: "\u2190 Go back to previous page" })] })] }), (supportEmail || supportUrl) && (_jsx("div", { className: "mt-8 pt-6 border-t border-slate-200 dark:border-slate-700", children: _jsxs("p", { className: "text-sm text-slate-500 dark:text-slate-400 text-center", children: ["Need help?", ' ', _jsx("a", { href: supportUrl || `mailto:${supportEmail}`, className: "text-indigo-600 dark:text-indigo-400 hover:underline font-medium cursor-pointer", children: "Contact support" })] }) }))] }) }));
|
|
6
|
+
}
|
|
7
|
+
export default Unauthorized;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decode JWT token payload without external dependencies.
|
|
3
|
+
* Returns the decoded payload or null if invalid.
|
|
4
|
+
*/
|
|
5
|
+
export function decodeJWT(token) {
|
|
6
|
+
try {
|
|
7
|
+
const parts = token.split('.');
|
|
8
|
+
if (parts.length !== 3) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
// Decode base64url (replace URL-safe chars and pad)
|
|
12
|
+
if (parts[1] === undefined) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
16
|
+
const paddedBase64 = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');
|
|
17
|
+
const jsonPayload = decodeURIComponent(atob(paddedBase64)
|
|
18
|
+
.split('')
|
|
19
|
+
.map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
|
|
20
|
+
.join(''));
|
|
21
|
+
return JSON.parse(jsonPayload);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error('Failed to decode JWT:', error);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
|