@tonycodes/auth-react 1.0.1
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/LICENSE +21 -0
- package/dist/AuthCallback.d.ts +28 -0
- package/dist/AuthCallback.d.ts.map +1 -0
- package/dist/AuthCallback.js +105 -0
- package/dist/AuthCallback.js.map +1 -0
- package/dist/AuthProvider.d.ts +11 -0
- package/dist/AuthProvider.d.ts.map +1 -0
- package/dist/AuthProvider.js +207 -0
- package/dist/AuthProvider.js.map +1 -0
- package/dist/OrganizationSwitcher.d.ts +6 -0
- package/dist/OrganizationSwitcher.d.ts.map +1 -0
- package/dist/OrganizationSwitcher.js +29 -0
- package/dist/OrganizationSwitcher.js.map +1 -0
- package/dist/SignInForm.d.ts +14 -0
- package/dist/SignInForm.d.ts.map +1 -0
- package/dist/SignInForm.js +85 -0
- package/dist/SignInForm.js.map +1 -0
- package/dist/components.d.ts +18 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +34 -0
- package/dist/components.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/style.css +1 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/useAuth.d.ts +4 -0
- package/dist/useAuth.d.ts.map +1 -0
- package/dist/useAuth.js +17 -0
- package/dist/useAuth.js.map +1 -0
- package/dist/useProviders.d.ts +16 -0
- package/dist/useProviders.d.ts.map +1 -0
- package/dist/useProviders.js +47 -0
- package/dist/useProviders.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Tony
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
interface AuthCallbackProps {
|
|
2
|
+
/** API URL to exchange code (overrides config.apiUrl, defaults to current origin) */
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
/** Where to redirect after successful auth */
|
|
5
|
+
onSuccess?: (returnTo: string) => void;
|
|
6
|
+
/** Called on auth failure */
|
|
7
|
+
onError?: (error: string) => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Component that handles the OAuth callback.
|
|
11
|
+
* Mount at /auth/callback route in your React router.
|
|
12
|
+
*
|
|
13
|
+
* When mounted, this component:
|
|
14
|
+
* 1. Extracts the authorization code from the URL
|
|
15
|
+
* 2. Calls /api/auth/callback on the backend to exchange the code for tokens
|
|
16
|
+
* 3. Redirects to the original page (from state)
|
|
17
|
+
*
|
|
18
|
+
* Your Express backend should mount callbackHandler() at /api/auth/callback.
|
|
19
|
+
*
|
|
20
|
+
* The API URL is resolved in this order:
|
|
21
|
+
* 1. `apiUrl` prop (explicit override)
|
|
22
|
+
* 2. `config.apiUrl` from AuthProvider
|
|
23
|
+
* 3. `config.appUrl` from AuthProvider
|
|
24
|
+
* 4. Current window origin (fallback)
|
|
25
|
+
*/
|
|
26
|
+
export declare function AuthCallback({ apiUrl: apiUrlProp, onSuccess, onError }: AuthCallbackProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=AuthCallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthCallback.d.ts","sourceRoot":"","sources":["../src/AuthCallback.tsx"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,6BAA6B;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,iBAAiB,2CAkGzF"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useContext, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { AuthConfigContext } from './AuthProvider.js';
|
|
4
|
+
/**
|
|
5
|
+
* Component that handles the OAuth callback.
|
|
6
|
+
* Mount at /auth/callback route in your React router.
|
|
7
|
+
*
|
|
8
|
+
* When mounted, this component:
|
|
9
|
+
* 1. Extracts the authorization code from the URL
|
|
10
|
+
* 2. Calls /api/auth/callback on the backend to exchange the code for tokens
|
|
11
|
+
* 3. Redirects to the original page (from state)
|
|
12
|
+
*
|
|
13
|
+
* Your Express backend should mount callbackHandler() at /api/auth/callback.
|
|
14
|
+
*
|
|
15
|
+
* The API URL is resolved in this order:
|
|
16
|
+
* 1. `apiUrl` prop (explicit override)
|
|
17
|
+
* 2. `config.apiUrl` from AuthProvider
|
|
18
|
+
* 3. `config.appUrl` from AuthProvider
|
|
19
|
+
* 4. Current window origin (fallback)
|
|
20
|
+
*/
|
|
21
|
+
export function AuthCallback({ apiUrl: apiUrlProp, onSuccess, onError }) {
|
|
22
|
+
const config = useContext(AuthConfigContext);
|
|
23
|
+
const [error, setError] = useState(null);
|
|
24
|
+
const exchangedRef = useRef(false);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
// Guard against React 18 strict mode double-firing
|
|
27
|
+
if (exchangedRef.current)
|
|
28
|
+
return;
|
|
29
|
+
exchangedRef.current = true;
|
|
30
|
+
const params = new URLSearchParams(window.location.search);
|
|
31
|
+
const code = params.get('code');
|
|
32
|
+
const state = params.get('state');
|
|
33
|
+
const errorParam = params.get('error');
|
|
34
|
+
if (errorParam) {
|
|
35
|
+
setError(errorParam);
|
|
36
|
+
if (onError) {
|
|
37
|
+
onError(errorParam);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
let redirectReturnTo = '/';
|
|
41
|
+
if (state) {
|
|
42
|
+
try {
|
|
43
|
+
const decoded = JSON.parse(atob(state));
|
|
44
|
+
redirectReturnTo = decoded.returnTo || '/';
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Invalid state — default to /
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const loginUrl = new URL('/login', window.location.origin);
|
|
51
|
+
loginUrl.searchParams.set('error', errorParam);
|
|
52
|
+
if (redirectReturnTo !== '/')
|
|
53
|
+
loginUrl.searchParams.set('returnTo', redirectReturnTo);
|
|
54
|
+
window.location.href = loginUrl.toString();
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!code) {
|
|
59
|
+
setError('Missing authorization code');
|
|
60
|
+
onError?.('Missing authorization code');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Resolve API URL: prop > config.apiUrl > config.appUrl > current origin
|
|
64
|
+
const baseUrl = apiUrlProp || config?.apiUrl || config?.appUrl || window.location.origin;
|
|
65
|
+
async function exchange() {
|
|
66
|
+
try {
|
|
67
|
+
const res = await fetch(`${baseUrl}/api/auth/callback?code=${encodeURIComponent(code)}`, {
|
|
68
|
+
credentials: 'include',
|
|
69
|
+
});
|
|
70
|
+
if (!res.ok) {
|
|
71
|
+
const data = await res.json();
|
|
72
|
+
throw new Error(data.error || 'Authentication failed');
|
|
73
|
+
}
|
|
74
|
+
// Decode state to get returnTo path
|
|
75
|
+
let returnTo = '/';
|
|
76
|
+
if (state) {
|
|
77
|
+
try {
|
|
78
|
+
const decoded = JSON.parse(atob(state));
|
|
79
|
+
returnTo = decoded.returnTo || '/';
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Invalid state — default to /
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (onSuccess) {
|
|
86
|
+
onSuccess(returnTo);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
window.location.href = returnTo;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
const message = err instanceof Error ? err.message : 'Authentication failed';
|
|
94
|
+
setError(message);
|
|
95
|
+
onError?.(message);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exchange();
|
|
99
|
+
}, [apiUrlProp, config, onSuccess, onError]);
|
|
100
|
+
if (error) {
|
|
101
|
+
return (_jsxs("div", { style: { padding: '2rem', textAlign: 'center' }, children: [_jsx("h2", { children: "Authentication Failed" }), _jsx("p", { children: error }), _jsx("a", { href: "/", children: "Go Home" })] }));
|
|
102
|
+
}
|
|
103
|
+
return (_jsx("div", { style: { padding: '2rem', textAlign: 'center' }, children: _jsx("p", { children: "Signing in..." }) }));
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=AuthCallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthCallback.js","sourceRoot":"","sources":["../src/AuthCallback.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { AuthConfig, AuthState } from './types.js';
|
|
3
|
+
export declare const AuthContext: import("react").Context<AuthState | null>;
|
|
4
|
+
export declare const AuthConfigContext: import("react").Context<AuthConfig | null>;
|
|
5
|
+
interface AuthProviderProps {
|
|
6
|
+
config: AuthConfig;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
export declare function AuthProvider({ config, children }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=AuthProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAChG,OAAO,KAAK,EAAE,UAAU,EAA8B,SAAS,EAAE,MAAM,YAAY,CAAC;AAkBpF,eAAO,MAAM,WAAW,2CAAwC,CAAC;AACjE,eAAO,MAAM,iBAAiB,4CAAyC,CAAC;AAExE,UAAU,iBAAiB;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CAwNnE"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
function decodeJWT(token) {
|
|
4
|
+
const base64 = token.split('.')[1];
|
|
5
|
+
const json = atob(base64.replace(/-/g, '+').replace(/_/g, '/'));
|
|
6
|
+
return JSON.parse(json);
|
|
7
|
+
}
|
|
8
|
+
export const AuthContext = createContext(null);
|
|
9
|
+
export const AuthConfigContext = createContext(null);
|
|
10
|
+
export function AuthProvider({ config, children }) {
|
|
11
|
+
const { authUrl, clientId, appUrl, apiUrl } = config;
|
|
12
|
+
const baseApiUrl = apiUrl || appUrl;
|
|
13
|
+
const [accessToken, setAccessToken] = useState(null);
|
|
14
|
+
const [user, setUser] = useState(null);
|
|
15
|
+
const [organization, setOrganization] = useState(null);
|
|
16
|
+
const [organizations, setOrganizations] = useState([]);
|
|
17
|
+
const [orgRole, setOrgRole] = useState('member');
|
|
18
|
+
const [isSuperAdmin, setIsSuperAdmin] = useState(false);
|
|
19
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
20
|
+
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
|
21
|
+
const refreshTimerRef = useRef();
|
|
22
|
+
const refreshLockRef = useRef(false);
|
|
23
|
+
const updateFromToken = useCallback((token) => {
|
|
24
|
+
const payload = decodeJWT(token);
|
|
25
|
+
setUser({
|
|
26
|
+
id: payload.sub,
|
|
27
|
+
email: payload.email,
|
|
28
|
+
name: payload.name || 'User',
|
|
29
|
+
role: payload.org?.role === 'owner' || payload.org?.role === 'admin' ? 'admin' : 'member',
|
|
30
|
+
imageUrl: payload.avatarUrl,
|
|
31
|
+
});
|
|
32
|
+
if (payload.org) {
|
|
33
|
+
setOrganization({
|
|
34
|
+
id: payload.org.id,
|
|
35
|
+
name: payload.org.name,
|
|
36
|
+
slug: payload.org.slug,
|
|
37
|
+
imageUrl: null,
|
|
38
|
+
});
|
|
39
|
+
setOrgRole(payload.org.role);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
setOrganization(null);
|
|
43
|
+
setOrgRole('member');
|
|
44
|
+
}
|
|
45
|
+
setIsSuperAdmin(payload.isSuperAdmin);
|
|
46
|
+
setAccessToken(token);
|
|
47
|
+
return payload;
|
|
48
|
+
}, []);
|
|
49
|
+
const refreshToken = useCallback(async () => {
|
|
50
|
+
if (refreshLockRef.current)
|
|
51
|
+
return null;
|
|
52
|
+
refreshLockRef.current = true;
|
|
53
|
+
try {
|
|
54
|
+
const res = await fetch(`${baseApiUrl}/auth/refresh`, {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
credentials: 'include',
|
|
57
|
+
headers: { 'Content-Type': 'application/json' },
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok) {
|
|
60
|
+
setAccessToken(null);
|
|
61
|
+
setUser(null);
|
|
62
|
+
setOrganization(null);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const data = await res.json();
|
|
66
|
+
const payload = updateFromToken(data.access_token);
|
|
67
|
+
// Schedule next refresh 1 minute before expiry
|
|
68
|
+
const expiresIn = payload.exp * 1000 - Date.now();
|
|
69
|
+
const refreshIn = Math.max(expiresIn - 60000, 10000);
|
|
70
|
+
if (refreshTimerRef.current)
|
|
71
|
+
clearTimeout(refreshTimerRef.current);
|
|
72
|
+
refreshTimerRef.current = setTimeout(() => {
|
|
73
|
+
refreshToken();
|
|
74
|
+
}, refreshIn);
|
|
75
|
+
return data.access_token;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
refreshLockRef.current = false;
|
|
82
|
+
}
|
|
83
|
+
}, [baseApiUrl, updateFromToken]);
|
|
84
|
+
// Fetch user organizations list
|
|
85
|
+
const fetchOrganizations = useCallback(async (token) => {
|
|
86
|
+
try {
|
|
87
|
+
const res = await fetch(`${authUrl}/api/organizations`, {
|
|
88
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
89
|
+
});
|
|
90
|
+
if (res.ok) {
|
|
91
|
+
const data = await res.json();
|
|
92
|
+
setOrganizations(data.organizations || []);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Silent failure — orgs list is supplementary
|
|
97
|
+
}
|
|
98
|
+
}, [authUrl]);
|
|
99
|
+
// Initial auth check — try to refresh on mount
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
let mounted = true;
|
|
102
|
+
async function init() {
|
|
103
|
+
const token = await refreshToken();
|
|
104
|
+
if (mounted) {
|
|
105
|
+
if (token) {
|
|
106
|
+
await fetchOrganizations(token);
|
|
107
|
+
}
|
|
108
|
+
setIsLoading(false);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
init();
|
|
112
|
+
return () => {
|
|
113
|
+
mounted = false;
|
|
114
|
+
if (refreshTimerRef.current)
|
|
115
|
+
clearTimeout(refreshTimerRef.current);
|
|
116
|
+
};
|
|
117
|
+
}, [refreshToken, fetchOrganizations]);
|
|
118
|
+
const login = useCallback((provider) => {
|
|
119
|
+
const redirectUri = `${appUrl}/auth/callback`;
|
|
120
|
+
const state = btoa(JSON.stringify({ returnTo: window.location.pathname }));
|
|
121
|
+
const params = new URLSearchParams({
|
|
122
|
+
client_id: clientId,
|
|
123
|
+
redirect_uri: redirectUri,
|
|
124
|
+
state,
|
|
125
|
+
});
|
|
126
|
+
if (provider)
|
|
127
|
+
params.set('provider', provider);
|
|
128
|
+
window.location.href = `${authUrl}/authorize?${params}`;
|
|
129
|
+
}, [authUrl, clientId, appUrl]);
|
|
130
|
+
const logout = useCallback(async () => {
|
|
131
|
+
setIsLoggingOut(true);
|
|
132
|
+
try {
|
|
133
|
+
await fetch(`${baseApiUrl}/auth/logout`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
credentials: 'include',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Best-effort
|
|
140
|
+
}
|
|
141
|
+
if (refreshTimerRef.current)
|
|
142
|
+
clearTimeout(refreshTimerRef.current);
|
|
143
|
+
setAccessToken(null);
|
|
144
|
+
setUser(null);
|
|
145
|
+
setOrganization(null);
|
|
146
|
+
setOrganizations([]);
|
|
147
|
+
setIsLoggingOut(false);
|
|
148
|
+
}, [baseApiUrl]);
|
|
149
|
+
const switchOrganization = useCallback(async (orgId) => {
|
|
150
|
+
try {
|
|
151
|
+
const res = await fetch(`${baseApiUrl}/auth/switch-org`, {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
credentials: 'include',
|
|
154
|
+
headers: { 'Content-Type': 'application/json' },
|
|
155
|
+
body: JSON.stringify({ org_id: orgId }),
|
|
156
|
+
});
|
|
157
|
+
if (res.ok) {
|
|
158
|
+
const data = await res.json();
|
|
159
|
+
updateFromToken(data.access_token);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Silent failure
|
|
164
|
+
}
|
|
165
|
+
}, [baseApiUrl, updateFromToken]);
|
|
166
|
+
const isAdmin = orgRole === 'admin' || orgRole === 'owner';
|
|
167
|
+
const isOwner = orgRole === 'owner';
|
|
168
|
+
const getAccessToken = useCallback(async () => {
|
|
169
|
+
if (accessToken) {
|
|
170
|
+
// Check if token expires within 60 seconds
|
|
171
|
+
try {
|
|
172
|
+
const payload = decodeJWT(accessToken);
|
|
173
|
+
if (payload.exp * 1000 - Date.now() > 60000) {
|
|
174
|
+
return accessToken;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Fall through to refresh
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return refreshToken();
|
|
182
|
+
}, [accessToken, refreshToken]);
|
|
183
|
+
const value = {
|
|
184
|
+
isAuthenticated: !!accessToken && !!organization,
|
|
185
|
+
isLoading,
|
|
186
|
+
user,
|
|
187
|
+
organization,
|
|
188
|
+
tenant: organization ? { id: organization.id, name: organization.name, slug: organization.slug } : null,
|
|
189
|
+
isAdmin,
|
|
190
|
+
isOwner,
|
|
191
|
+
orgRole,
|
|
192
|
+
isSuperAdmin,
|
|
193
|
+
isPlatformAdmin: isSuperAdmin,
|
|
194
|
+
accessToken,
|
|
195
|
+
getAccessToken,
|
|
196
|
+
login,
|
|
197
|
+
logout,
|
|
198
|
+
switchOrganization,
|
|
199
|
+
organizations,
|
|
200
|
+
isLoggingOut,
|
|
201
|
+
isLoggingIn: false,
|
|
202
|
+
loginError: null,
|
|
203
|
+
impersonating: false,
|
|
204
|
+
};
|
|
205
|
+
return (_jsx(AuthConfigContext.Provider, { value: config, children: _jsx(AuthContext.Provider, { value: value, children: children }) }));
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=AuthProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthProvider.js","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OrganizationSwitcher.d.ts","sourceRoot":"","sources":["../src/OrganizationSwitcher.tsx"],"names":[],"mappings":"AAGA,UAAU,yBAAyB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,oBAAoB,CAAC,EAAE,SAAc,EAAE,EAAE,yBAAyB,2CAmEjF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect } from 'react';
|
|
3
|
+
import { useAuth } from './useAuth.js';
|
|
4
|
+
export function OrganizationSwitcher({ className = '' }) {
|
|
5
|
+
const { organization, organizations, switchOrganization } = useAuth();
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
const dropdownRef = useRef(null);
|
|
8
|
+
// Close dropdown when clicking outside
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
function handleClickOutside(event) {
|
|
11
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
12
|
+
setIsOpen(false);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
16
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
17
|
+
}, []);
|
|
18
|
+
if (organizations.length <= 1) {
|
|
19
|
+
// Single org — just display name
|
|
20
|
+
return (_jsx("div", { className: className, children: _jsx("span", { children: organization?.name || 'No organization' }) }));
|
|
21
|
+
}
|
|
22
|
+
return (_jsxs("div", { ref: dropdownRef, className: `relative ${className}`, children: [_jsxs("button", { onClick: () => setIsOpen(!isOpen), className: "flex items-center gap-2 px-3 py-2 text-sm border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors", children: [_jsx("span", { className: "font-medium", children: organization?.name || 'Select org' }), _jsx("svg", { className: `w-4 h-4 transition-transform ${isOpen ? 'rotate-180' : ''}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), isOpen && (_jsx("div", { className: "absolute top-full left-0 mt-1 w-64 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50", children: _jsx("div", { className: "py-1", children: organizations.map((org) => (_jsxs("button", { onClick: () => {
|
|
23
|
+
switchOrganization(org.id);
|
|
24
|
+
setIsOpen(false);
|
|
25
|
+
}, className: `w-full text-left px-4 py-2 text-sm hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors ${org.id === organization?.id
|
|
26
|
+
? 'bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400'
|
|
27
|
+
: 'text-gray-700 dark:text-gray-300'}`, children: [_jsx("div", { className: "font-medium", children: org.name }), _jsx("div", { className: "text-xs text-gray-400", children: org.slug })] }, org.id))) }) }))] }));
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=OrganizationSwitcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OrganizationSwitcher.js","sourceRoot":"","sources":["../src/OrganizationSwitcher.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAMvC,MAAM,UAAU,oBAAoB,CAAC,EAAE,SAAS,GAAG,EAAE,EAA6B;IAChF,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,GAAG,OAAO,EAAE,CAAC;IACtE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAEjD,uCAAuC;IACvC,SAAS,CAAC,GAAG,EAAE;QACb,SAAS,kBAAkB,CAAC,KAAiB;YAC3C,IAAI,WAAW,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAc,CAAC,EAAE,CAAC;gBAC/E,SAAS,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;QAC3D,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;IAC7E,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,IAAI,aAAa,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QAC9B,iCAAiC;QACjC,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,YACvB,yBAAO,YAAY,EAAE,IAAI,IAAI,iBAAiB,GAAQ,GAClD,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,eAAK,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,SAAS,EAAE,aACvD,kBACE,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EACjC,SAAS,EAAC,4JAA4J,aAEtK,eAAM,SAAS,EAAC,aAAa,YAAE,YAAY,EAAE,IAAI,IAAI,YAAY,GAAQ,EACzE,cACE,SAAS,EAAE,gCAAgC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,EACvE,IAAI,EAAC,MAAM,EACX,OAAO,EAAC,WAAW,EACnB,MAAM,EAAC,cAAc,YAErB,eAAM,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,EAAC,WAAW,EAAE,CAAC,EAAE,CAAC,EAAC,gBAAgB,GAAG,GACpF,IACC,EAER,MAAM,IAAI,CACT,cAAK,SAAS,EAAC,oIAAoI,YACjJ,cAAK,SAAS,EAAC,MAAM,YAClB,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAC1B,kBAEE,OAAO,EAAE,GAAG,EAAE;4BACZ,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;4BAC3B,SAAS,CAAC,KAAK,CAAC,CAAC;wBACnB,CAAC,EACD,SAAS,EAAE,gGACT,GAAG,CAAC,EAAE,KAAK,YAAY,EAAE,EAAE;4BACzB,CAAC,CAAC,iEAAiE;4BACnE,CAAC,CAAC,kCACN,EAAE,aAEF,cAAK,SAAS,EAAC,aAAa,YAAE,GAAG,CAAC,IAAI,GAAO,EAC7C,cAAK,SAAS,EAAC,uBAAuB,YAAE,GAAG,CAAC,IAAI,GAAO,KAZlD,GAAG,CAAC,EAAE,CAaJ,CACV,CAAC,GACE,GACF,CACP,IACG,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type Mode = 'signin' | 'signup';
|
|
2
|
+
interface SignInFormProps {
|
|
3
|
+
/** List of provider IDs to show. If not provided, auto-fetches from auth service. */
|
|
4
|
+
providers?: string[];
|
|
5
|
+
/** Whether to auto-fetch available providers from the auth service. Default: true */
|
|
6
|
+
autoFetch?: boolean;
|
|
7
|
+
/** Additional CSS class names */
|
|
8
|
+
className?: string;
|
|
9
|
+
/** Mode to display the form in */
|
|
10
|
+
mode?: Mode;
|
|
11
|
+
}
|
|
12
|
+
export declare function SignInForm({ providers: providersProp, autoFetch, className, mode: modeProp, }: SignInFormProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=SignInForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignInForm.d.ts","sourceRoot":"","sources":["../src/SignInForm.tsx"],"names":[],"mappings":"AAIA,KAAK,IAAI,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAoChC,UAAU,eAAe;IACvB,qFAAqF;IACrF,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,qFAAqF;IACrF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,wBAAgB,UAAU,CAAC,EACzB,SAAS,EAAE,aAAa,EACxB,SAAgB,EAChB,SAAS,EACT,IAAI,EAAE,QAAQ,GACf,EAAE,eAAe,2CA0JjB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { useAuthConfig } from './useAuth.js';
|
|
4
|
+
import { useProviders } from './useProviders.js';
|
|
5
|
+
const ERROR_MESSAGES = {
|
|
6
|
+
account_not_found: 'No account found with that login. Sign up to create one.',
|
|
7
|
+
oauth_failed: 'Something went wrong during sign in. Please try again.',
|
|
8
|
+
missing_code: 'Authorization failed. Please try again.',
|
|
9
|
+
invalid_state: 'Session expired. Please try again.',
|
|
10
|
+
};
|
|
11
|
+
const GITHUB_ICON = (_jsx("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor", children: _jsx("path", { d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0 1 12 6.844a9.59 9.59 0 0 1 2.504.337c1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.02 10.02 0 0 0 22 12.017C22 6.484 17.522 2 12 2z" }) }));
|
|
12
|
+
const GOOGLE_ICON = (_jsxs("svg", { viewBox: "0 0 24 24", width: "20", height: "20", children: [_jsx("path", { fill: "#4285F4", d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" }), _jsx("path", { fill: "#34A853", d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" }), _jsx("path", { fill: "#FBBC05", d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" }), _jsx("path", { fill: "#EA4335", d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" })] }));
|
|
13
|
+
const APPLE_ICON = (_jsx("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor", children: _jsx("path", { d: "M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" }) }));
|
|
14
|
+
const BITBUCKET_ICON = (_jsx("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor", children: _jsx("path", { d: "M.778 1.213a.768.768 0 00-.768.892l3.263 19.81c.084.5.515.868 1.022.873H19.95a.772.772 0 00.77-.646l3.27-20.03a.768.768 0 00-.768-.891zM14.52 15.53H9.522L8.17 8.466h7.561z" }) }));
|
|
15
|
+
export function SignInForm({ providers: providersProp, autoFetch = true, className, mode: modeProp, }) {
|
|
16
|
+
const config = useAuthConfig();
|
|
17
|
+
const { providers: fetchedProviders, isLoading: providersLoading } = useProviders();
|
|
18
|
+
const [activeTab, setActiveTab] = useState(modeProp || 'signin');
|
|
19
|
+
const [errorMessage, setErrorMessage] = useState(null);
|
|
20
|
+
const [returnTo, setReturnTo] = useState('/');
|
|
21
|
+
// Determine which providers to show
|
|
22
|
+
const enabledProviderIds = providersProp
|
|
23
|
+
? providersProp
|
|
24
|
+
: autoFetch
|
|
25
|
+
? fetchedProviders.filter((p) => p.enabled).map((p) => p.id)
|
|
26
|
+
: ['github'];
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const params = new URLSearchParams(window.location.search);
|
|
29
|
+
const errorParam = params.get('error');
|
|
30
|
+
const tabParam = params.get('tab');
|
|
31
|
+
const returnToParam = params.get('returnTo');
|
|
32
|
+
if (returnToParam)
|
|
33
|
+
setReturnTo(returnToParam);
|
|
34
|
+
if (errorParam) {
|
|
35
|
+
setErrorMessage(ERROR_MESSAGES[errorParam] || `Authentication error: ${errorParam}`);
|
|
36
|
+
if (errorParam === 'account_not_found') {
|
|
37
|
+
setActiveTab('signup');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!modeProp && (tabParam === 'signin' || tabParam === 'signup')) {
|
|
41
|
+
setActiveTab(tabParam);
|
|
42
|
+
}
|
|
43
|
+
}, [modeProp]);
|
|
44
|
+
function handleOAuth(provider) {
|
|
45
|
+
const redirectUri = `${config.appUrl}/auth/callback`;
|
|
46
|
+
const state = btoa(JSON.stringify({ returnTo }));
|
|
47
|
+
const params = new URLSearchParams({
|
|
48
|
+
client_id: config.clientId,
|
|
49
|
+
redirect_uri: redirectUri,
|
|
50
|
+
state,
|
|
51
|
+
provider,
|
|
52
|
+
mode: activeTab,
|
|
53
|
+
});
|
|
54
|
+
window.location.href = `${config.authUrl}/authorize?${params}`;
|
|
55
|
+
}
|
|
56
|
+
function switchTab(tab) {
|
|
57
|
+
setActiveTab(tab);
|
|
58
|
+
setErrorMessage(null);
|
|
59
|
+
}
|
|
60
|
+
function tabClass(tab) {
|
|
61
|
+
const isActive = activeTab === tab;
|
|
62
|
+
return [
|
|
63
|
+
'flex-1 py-2 text-sm font-medium rounded-lg text-center cursor-pointer transition-colors',
|
|
64
|
+
isActive ? 'bg-white text-zinc-900 shadow-sm' : 'text-zinc-500 hover:text-zinc-700',
|
|
65
|
+
].join(' ');
|
|
66
|
+
}
|
|
67
|
+
function getProviderButton(providerId) {
|
|
68
|
+
const actionText = activeTab === 'signin' ? 'Sign in with' : 'Sign up with';
|
|
69
|
+
switch (providerId) {
|
|
70
|
+
case 'github':
|
|
71
|
+
return (_jsxs("button", { type: "button", className: "w-full flex items-center justify-center gap-3 px-6 py-3 bg-zinc-900 text-white font-medium rounded-xl hover:bg-zinc-800 transition-colors cursor-pointer", onClick: () => handleOAuth('github'), children: [GITHUB_ICON, _jsxs("span", { children: [actionText, " GitHub"] })] }, "github"));
|
|
72
|
+
case 'google':
|
|
73
|
+
return (_jsxs("button", { type: "button", className: "w-full flex items-center justify-center gap-3 px-6 py-3 bg-white text-zinc-700 font-medium rounded-xl border border-zinc-300 hover:bg-zinc-50 transition-colors cursor-pointer", onClick: () => handleOAuth('google'), children: [GOOGLE_ICON, _jsxs("span", { children: [actionText, " Google"] })] }, "google"));
|
|
74
|
+
case 'apple':
|
|
75
|
+
return (_jsxs("button", { type: "button", className: "w-full flex items-center justify-center gap-3 px-6 py-3 bg-black text-white font-medium rounded-xl hover:bg-zinc-900 transition-colors cursor-pointer", onClick: () => handleOAuth('apple'), children: [APPLE_ICON, _jsxs("span", { children: [actionText, " Apple"] })] }, "apple"));
|
|
76
|
+
case 'bitbucket':
|
|
77
|
+
return (_jsxs("button", { type: "button", className: "w-full flex items-center justify-center gap-3 px-6 py-3 bg-blue-600 text-white font-medium rounded-xl hover:bg-blue-700 transition-colors cursor-pointer", onClick: () => handleOAuth('bitbucket'), children: [BITBUCKET_ICON, _jsxs("span", { children: [actionText, " Bitbucket"] })] }, "bitbucket"));
|
|
78
|
+
default:
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const showLoading = autoFetch && providersLoading && !providersProp;
|
|
83
|
+
return (_jsxs("div", { className: className || '', children: [_jsxs("div", { className: "flex gap-1 p-1 bg-zinc-100 rounded-xl mb-6", children: [_jsx("button", { type: "button", className: tabClass('signin'), onClick: () => switchTab('signin'), children: "Sign In" }), _jsx("button", { type: "button", className: tabClass('signup'), onClick: () => switchTab('signup'), children: "Sign Up" })] }), errorMessage && (_jsx("div", { className: "mb-4 p-3 rounded-lg bg-red-50 border border-red-200 text-red-700 text-sm", children: errorMessage })), showLoading ? (_jsx("div", { className: "text-center py-4 text-zinc-500 text-sm", children: "Loading providers..." })) : (_jsx("div", { className: "space-y-3", children: enabledProviderIds.map((providerId) => getProviderButton(providerId)) })), _jsx("p", { className: "mt-6 text-center text-xs text-zinc-400", children: "By continuing, you agree to our terms of service." })] }));
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=SignInForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignInForm.js","sourceRoot":"","sources":["../src/SignInForm.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
interface AuthGateProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Renders children only when user is authenticated
|
|
7
|
+
*/
|
|
8
|
+
export declare function SignedIn({ children }: AuthGateProps): import("react/jsx-runtime").JSX.Element | null;
|
|
9
|
+
/**
|
|
10
|
+
* Renders children only when user is NOT authenticated
|
|
11
|
+
*/
|
|
12
|
+
export declare function SignedOut({ children }: AuthGateProps): import("react/jsx-runtime").JSX.Element | null;
|
|
13
|
+
/**
|
|
14
|
+
* Redirects to auth service sign-in when user is not authenticated
|
|
15
|
+
*/
|
|
16
|
+
export declare function RedirectToSignIn(): null;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=components.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,UAAU,aAAa;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE,aAAa,kDAInD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,aAAa,kDAIpD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,SAW/B"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth } from './useAuth.js';
|
|
3
|
+
/**
|
|
4
|
+
* Renders children only when user is authenticated
|
|
5
|
+
*/
|
|
6
|
+
export function SignedIn({ children }) {
|
|
7
|
+
const { isAuthenticated, isLoading } = useAuth();
|
|
8
|
+
if (isLoading || !isAuthenticated)
|
|
9
|
+
return null;
|
|
10
|
+
return _jsx(_Fragment, { children: children });
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Renders children only when user is NOT authenticated
|
|
14
|
+
*/
|
|
15
|
+
export function SignedOut({ children }) {
|
|
16
|
+
const { isAuthenticated, isLoading } = useAuth();
|
|
17
|
+
if (isLoading || isAuthenticated)
|
|
18
|
+
return null;
|
|
19
|
+
return _jsx(_Fragment, { children: children });
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Redirects to auth service sign-in when user is not authenticated
|
|
23
|
+
*/
|
|
24
|
+
export function RedirectToSignIn() {
|
|
25
|
+
const { isAuthenticated, isLoading, login } = useAuth();
|
|
26
|
+
if (isLoading)
|
|
27
|
+
return null;
|
|
28
|
+
if (!isAuthenticated) {
|
|
29
|
+
login();
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=components.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.js","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAMvC;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAiB;IAClD,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE,CAAC;IACjD,IAAI,SAAS,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,4BAAG,QAAQ,GAAI,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,QAAQ,EAAiB;IACnD,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE,CAAC;IACjD,IAAI,SAAS,IAAI,eAAe;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,4BAAG,QAAQ,GAAI,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC;IAExD,IAAI,SAAS;QAAE,OAAO,IAAI,CAAC;IAE3B,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,KAAK,EAAE,CAAC;QACR,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { AuthProvider } from './AuthProvider.js';
|
|
2
|
+
export { useAuth, useAuthConfig } from './useAuth.js';
|
|
3
|
+
export { useProviders } from './useProviders.js';
|
|
4
|
+
export { SignedIn, SignedOut, RedirectToSignIn } from './components.js';
|
|
5
|
+
export { OrganizationSwitcher } from './OrganizationSwitcher.js';
|
|
6
|
+
export { AuthCallback } from './AuthCallback.js';
|
|
7
|
+
export { SignInForm } from './SignInForm.js';
|
|
8
|
+
export type { AuthConfig, AuthUser, AuthOrganization, AuthState } from './types.js';
|
|
9
|
+
export type { ProviderInfo, UseProvidersResult } from './useProviders.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACpF,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { AuthProvider } from './AuthProvider.js';
|
|
2
|
+
export { useAuth, useAuthConfig } from './useAuth.js';
|
|
3
|
+
export { useProviders } from './useProviders.js';
|
|
4
|
+
export { SignedIn, SignedOut, RedirectToSignIn } from './components.js';
|
|
5
|
+
export { OrganizationSwitcher } from './OrganizationSwitcher.js';
|
|
6
|
+
export { AuthCallback } from './AuthCallback.js';
|
|
7
|
+
export { SignInForm } from './SignInForm.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.absolute{position:absolute}.relative{position:relative}.left-0{left:0}.top-full{top:100%}.z-50{z-index:50}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mt-1{margin-top:.25rem}.mt-6{margin-top:1.5rem}.flex{display:flex}.h-4{height:1rem}.w-4{width:1rem}.w-64{width:16rem}.w-full{width:100%}.flex-1{flex:1 1 0%}.rotate-180{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.border-zinc-300{--tw-border-opacity:1;border-color:rgb(212 212 216/var(--tw-border-opacity,1))}.bg-black{background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black,.bg-blue-50{--tw-bg-opacity:1}.bg-blue-50{background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-red-50{background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-50,.bg-white{--tw-bg-opacity:1}.bg-white{background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-zinc-100{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.bg-zinc-900{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.p-1{padding:.25rem}.p-3{padding:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-zinc-400{--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.text-zinc-500{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.text-zinc-700{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}.text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:bg-zinc-50:hover{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.hover\:bg-zinc-800:hover{--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.hover\:bg-zinc-900:hover{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.hover\:text-zinc-700:hover{--tw-text-opacity:1;color:rgb(63 63 70/var(--tw-text-opacity,1))}@media (prefers-color-scheme:dark){.dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.dark\:bg-blue-900\/20{background-color:rgba(30,58,138,.2)}.dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.dark\:text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:hover\:bg-gray-800:hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface AuthConfig {
|
|
2
|
+
/** Auth service URL (e.g., https://auth.tony.codes or https://auth.test) */
|
|
3
|
+
authUrl: string;
|
|
4
|
+
/** Client ID registered with auth service */
|
|
5
|
+
clientId: string;
|
|
6
|
+
/** This app's base URL (for redirect_uri construction) */
|
|
7
|
+
appUrl: string;
|
|
8
|
+
/**
|
|
9
|
+
* API base URL for proxied auth endpoints (callback, refresh, logout).
|
|
10
|
+
* Required if your API runs on a different subdomain than your frontend
|
|
11
|
+
* (e.g., api.myapp.test vs myapp.test).
|
|
12
|
+
* Defaults to appUrl if not specified.
|
|
13
|
+
*/
|
|
14
|
+
apiUrl?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface AuthUser {
|
|
17
|
+
id: string;
|
|
18
|
+
email: string;
|
|
19
|
+
name: string;
|
|
20
|
+
role: string;
|
|
21
|
+
imageUrl: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface AuthOrganization {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
slug: string;
|
|
27
|
+
imageUrl: string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface AuthState {
|
|
30
|
+
isAuthenticated: boolean;
|
|
31
|
+
isLoading: boolean;
|
|
32
|
+
user: AuthUser | null;
|
|
33
|
+
organization: AuthOrganization | null;
|
|
34
|
+
tenant: {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
slug: string;
|
|
38
|
+
} | null;
|
|
39
|
+
isAdmin: boolean;
|
|
40
|
+
isOwner: boolean;
|
|
41
|
+
orgRole: string;
|
|
42
|
+
isSuperAdmin: boolean;
|
|
43
|
+
isPlatformAdmin: boolean;
|
|
44
|
+
accessToken: string | null;
|
|
45
|
+
getAccessToken: () => Promise<string | null>;
|
|
46
|
+
login: (provider?: string) => void;
|
|
47
|
+
logout: () => Promise<void>;
|
|
48
|
+
switchOrganization: (orgId: string) => Promise<void>;
|
|
49
|
+
organizations: AuthOrganization[];
|
|
50
|
+
isLoggingOut: boolean;
|
|
51
|
+
isLoggingIn: boolean;
|
|
52
|
+
loginError: string | null;
|
|
53
|
+
impersonating: boolean;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1D,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;CACxB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../src/useAuth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,wBAAgB,OAAO,IAAI,SAAS,CAMnC;AAED,wBAAgB,aAAa,IAAI,UAAU,CAM1C"}
|
package/dist/useAuth.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { AuthContext, AuthConfigContext } from './AuthProvider.js';
|
|
3
|
+
export function useAuth() {
|
|
4
|
+
const context = useContext(AuthContext);
|
|
5
|
+
if (!context) {
|
|
6
|
+
throw new Error('useAuth must be used within an AuthProvider');
|
|
7
|
+
}
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
10
|
+
export function useAuthConfig() {
|
|
11
|
+
const config = useContext(AuthConfigContext);
|
|
12
|
+
if (!config) {
|
|
13
|
+
throw new Error('useAuthConfig must be used within an AuthProvider');
|
|
14
|
+
}
|
|
15
|
+
return config;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=useAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAuth.js","sourceRoot":"","sources":["../src/useAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGnE,MAAM,UAAU,OAAO;IACrB,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ProviderInfo {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface UseProvidersResult {
|
|
7
|
+
providers: ProviderInfo[];
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
error: string | null;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Hook to fetch available SSO providers for the current organization.
|
|
13
|
+
* Automatically fetches providers based on the client_id from AuthProvider config.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useProviders(): UseProvidersResult;
|
|
16
|
+
//# sourceMappingURL=useProviders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useProviders.d.ts","sourceRoot":"","sources":["../src/useProviders.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,kBAAkB,CA6CjD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useAuthConfig } from './useAuth.js';
|
|
3
|
+
/**
|
|
4
|
+
* Hook to fetch available SSO providers for the current organization.
|
|
5
|
+
* Automatically fetches providers based on the client_id from AuthProvider config.
|
|
6
|
+
*/
|
|
7
|
+
export function useProviders() {
|
|
8
|
+
const config = useAuthConfig();
|
|
9
|
+
const [providers, setProviders] = useState([]);
|
|
10
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
11
|
+
const [error, setError] = useState(null);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
let mounted = true;
|
|
14
|
+
async function fetchProviders() {
|
|
15
|
+
try {
|
|
16
|
+
setIsLoading(true);
|
|
17
|
+
setError(null);
|
|
18
|
+
const url = new URL(`${config.authUrl}/providers`);
|
|
19
|
+
url.searchParams.set('client_id', config.clientId);
|
|
20
|
+
const res = await fetch(url.toString());
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
throw new Error(`Failed to fetch providers: ${res.status}`);
|
|
23
|
+
}
|
|
24
|
+
const data = await res.json();
|
|
25
|
+
if (mounted) {
|
|
26
|
+
setProviders(data.providers || []);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (mounted) {
|
|
31
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch providers');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
if (mounted) {
|
|
36
|
+
setIsLoading(false);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
fetchProviders();
|
|
41
|
+
return () => {
|
|
42
|
+
mounted = false;
|
|
43
|
+
};
|
|
44
|
+
}, [config.authUrl, config.clientId]);
|
|
45
|
+
return { providers, isLoading, error };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=useProviders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useProviders.js","sourceRoot":"","sources":["../src/useProviders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAc7C;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAiB,EAAE,CAAC,CAAC;IAC/D,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAExD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,GAAG,IAAI,CAAC;QAEnB,KAAK,UAAU,cAAc;YAC3B,IAAI,CAAC;gBACH,YAAY,CAAC,IAAI,CAAC,CAAC;gBACnB,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAEf,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;gBACnD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAEnD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACxC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC9D,CAAC;gBAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC9B,IAAI,OAAO,EAAE,CAAC;oBACZ,YAAY,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,OAAO,EAAE,CAAC;oBACZ,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC;gBAC7E,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,IAAI,OAAO,EAAE,CAAC;oBACZ,YAAY,CAAC,KAAK,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;QAED,cAAc,EAAE,CAAC;QAEjB,OAAO,GAAG,EAAE;YACV,OAAO,GAAG,KAAK,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEtC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AACzC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tonycodes/auth-react",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "React SDK for Tony Auth service",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./style.css": "./dist/style.css"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc && npx tailwindcss -i src/style.css -o dist/style.css --minify",
|
|
20
|
+
"dev": "tsc --watch",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"react",
|
|
25
|
+
"auth",
|
|
26
|
+
"authentication",
|
|
27
|
+
"sdk"
|
|
28
|
+
],
|
|
29
|
+
"author": "Tony <tony@tony.codes>",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/tonycodes/auth-react.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/tonycodes/auth-react#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/tonycodes/auth-react/issues"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"react": ">=18.0.0",
|
|
44
|
+
"react-dom": ">=18.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/react": "^18.2.47",
|
|
48
|
+
"tailwindcss": "^3.4.19",
|
|
49
|
+
"typescript": "^5.3.3"
|
|
50
|
+
}
|
|
51
|
+
}
|