@tonycodes/auth-react 1.1.0 → 1.2.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/.github/workflows/publish.yml +33 -0
- package/dist/SignInForm.d.ts +6 -1
- package/dist/SignInForm.d.ts.map +1 -1
- package/dist/SignInForm.js +141 -16
- package/dist/SignInForm.js.map +1 -1
- package/package.json +8 -29
- package/src/AuthCallback.tsx +128 -0
- package/src/AuthProvider.tsx +331 -0
- package/src/OrganizationSwitcher.tsx +75 -0
- package/src/SignInForm.tsx +323 -0
- package/src/components.tsx +40 -0
- package/src/index.ts +9 -0
- package/src/types.ts +61 -0
- package/src/useAuth.ts +19 -0
- package/src/useProviders.ts +136 -0
- package/src/validateConfig.ts +94 -0
- package/tsconfig.json +18 -0
- package/dist/style.css +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Setup Node.js
|
|
19
|
+
uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: '20'
|
|
22
|
+
registry-url: 'https://registry.npmjs.org'
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: npm ci
|
|
26
|
+
|
|
27
|
+
- name: Build
|
|
28
|
+
run: npm run build
|
|
29
|
+
|
|
30
|
+
- name: Publish to npm
|
|
31
|
+
run: npm publish --provenance --access public
|
|
32
|
+
env:
|
|
33
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/dist/SignInForm.d.ts
CHANGED
|
@@ -4,8 +4,13 @@ interface SignInFormProps {
|
|
|
4
4
|
providers?: SSOProvider[];
|
|
5
5
|
/** Whether to automatically fetch providers from the auth service. Defaults to true. */
|
|
6
6
|
autoFetch?: boolean;
|
|
7
|
+
/** Color theme. Defaults to 'auto' (follows prefers-color-scheme). */
|
|
8
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
9
|
+
/** Optional inline styles applied to the root container. */
|
|
10
|
+
style?: React.CSSProperties;
|
|
11
|
+
/** Optional CSS class applied to the root container. */
|
|
7
12
|
className?: string;
|
|
8
13
|
}
|
|
9
|
-
export declare function SignInForm({ providers: propProviders, autoFetch, className }: SignInFormProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare function SignInForm({ providers: propProviders, autoFetch, theme, style, className, }: SignInFormProps): import("react/jsx-runtime").JSX.Element;
|
|
10
15
|
export {};
|
|
11
16
|
//# sourceMappingURL=SignInForm.d.ts.map
|
package/dist/SignInForm.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SignInForm.d.ts","sourceRoot":"","sources":["../src/SignInForm.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"SignInForm.d.ts","sourceRoot":"","sources":["../src/SignInForm.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAwJnE,UAAU,eAAe;IACvB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1B,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sEAAsE;IACtE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,SAAS,EAAE,aAAa,EACxB,SAAgB,EAChB,KAAc,EACd,KAAK,EACL,SAAS,GACV,EAAE,eAAe,2CAqJjB"}
|
package/dist/SignInForm.js
CHANGED
|
@@ -8,16 +8,133 @@ const ERROR_MESSAGES = {
|
|
|
8
8
|
missing_code: 'Authorization failed. Please try again.',
|
|
9
9
|
invalid_state: 'Session expired. Please try again.',
|
|
10
10
|
};
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
// ─── Inline Styles ───────────────────────────────────────────────────────
|
|
12
|
+
const styles = {
|
|
13
|
+
tabContainer: (isDark) => ({
|
|
14
|
+
display: 'flex',
|
|
15
|
+
gap: '4px',
|
|
16
|
+
padding: '4px',
|
|
17
|
+
backgroundColor: isDark ? '#1f2937' : '#f4f4f5',
|
|
18
|
+
borderRadius: '12px',
|
|
19
|
+
marginBottom: '24px',
|
|
20
|
+
}),
|
|
21
|
+
tab: (isActive, isDark) => ({
|
|
22
|
+
flex: 1,
|
|
23
|
+
padding: '8px 0',
|
|
24
|
+
fontSize: '14px',
|
|
25
|
+
fontWeight: 500,
|
|
26
|
+
borderRadius: '8px',
|
|
27
|
+
textAlign: 'center',
|
|
28
|
+
cursor: 'pointer',
|
|
29
|
+
transition: 'background-color 0.15s, color 0.15s',
|
|
30
|
+
border: 'none',
|
|
31
|
+
backgroundColor: isActive
|
|
32
|
+
? (isDark ? '#374151' : '#ffffff')
|
|
33
|
+
: 'transparent',
|
|
34
|
+
color: isActive
|
|
35
|
+
? (isDark ? '#f9fafb' : '#18181b')
|
|
36
|
+
: (isDark ? '#9ca3af' : '#71717a'),
|
|
37
|
+
boxShadow: isActive ? '0 1px 2px rgba(0,0,0,0.05)' : 'none',
|
|
38
|
+
}),
|
|
39
|
+
error: (isDark) => ({
|
|
40
|
+
marginBottom: '16px',
|
|
41
|
+
padding: '12px',
|
|
42
|
+
borderRadius: '8px',
|
|
43
|
+
backgroundColor: isDark ? 'rgba(239,68,68,0.15)' : '#fef2f2',
|
|
44
|
+
border: `1px solid ${isDark ? 'rgba(239,68,68,0.3)' : '#fecaca'}`,
|
|
45
|
+
color: isDark ? '#fca5a5' : '#b91c1c',
|
|
46
|
+
fontSize: '14px',
|
|
47
|
+
}),
|
|
48
|
+
buttonGroup: {
|
|
49
|
+
display: 'flex',
|
|
50
|
+
flexDirection: 'column',
|
|
51
|
+
gap: '12px',
|
|
52
|
+
},
|
|
53
|
+
buttonBase: {
|
|
54
|
+
width: '100%',
|
|
55
|
+
display: 'flex',
|
|
56
|
+
alignItems: 'center',
|
|
57
|
+
justifyContent: 'center',
|
|
58
|
+
gap: '12px',
|
|
59
|
+
padding: '12px 24px',
|
|
60
|
+
fontWeight: 500,
|
|
61
|
+
fontSize: '15px',
|
|
62
|
+
borderRadius: '12px',
|
|
63
|
+
cursor: 'pointer',
|
|
64
|
+
border: 'none',
|
|
65
|
+
transition: 'opacity 0.15s',
|
|
66
|
+
fontFamily: 'inherit',
|
|
67
|
+
},
|
|
68
|
+
githubBtn: (isDark) => ({
|
|
69
|
+
backgroundColor: isDark ? '#ffffff' : '#18181b',
|
|
70
|
+
color: isDark ? '#18181b' : '#ffffff',
|
|
71
|
+
}),
|
|
72
|
+
googleBtn: (isDark) => ({
|
|
73
|
+
backgroundColor: isDark ? '#1f2937' : '#ffffff',
|
|
74
|
+
color: isDark ? '#e5e7eb' : '#18181b',
|
|
75
|
+
border: `1px solid ${isDark ? '#374151' : '#d4d4d8'}`,
|
|
76
|
+
}),
|
|
77
|
+
appleBtn: {
|
|
78
|
+
backgroundColor: '#000000',
|
|
79
|
+
color: '#ffffff',
|
|
80
|
+
},
|
|
81
|
+
bitbucketBtn: {
|
|
82
|
+
backgroundColor: '#2563eb',
|
|
83
|
+
color: '#ffffff',
|
|
84
|
+
},
|
|
85
|
+
spinner: (isDark) => ({
|
|
86
|
+
width: '20px',
|
|
87
|
+
height: '20px',
|
|
88
|
+
border: `2px solid ${isDark ? '#374151' : '#d4d4d8'}`,
|
|
89
|
+
borderTopColor: isDark ? '#9ca3af' : '#52525b',
|
|
90
|
+
borderRadius: '50%',
|
|
91
|
+
animation: 'tonycodes-auth-spin 0.6s linear infinite',
|
|
92
|
+
margin: '16px auto',
|
|
93
|
+
}),
|
|
94
|
+
terms: (isDark) => ({
|
|
95
|
+
marginTop: '24px',
|
|
96
|
+
textAlign: 'center',
|
|
97
|
+
fontSize: '12px',
|
|
98
|
+
color: isDark ? '#6b7280' : '#a1a1aa',
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
// ─── Provider Icons (inline SVG) ─────────────────────────────────────────
|
|
102
|
+
function GithubIcon() {
|
|
103
|
+
return (_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" }) }));
|
|
104
|
+
}
|
|
105
|
+
function GoogleIcon() {
|
|
106
|
+
return (_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" })] }));
|
|
107
|
+
}
|
|
108
|
+
function AppleIcon() {
|
|
109
|
+
return (_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" }) }));
|
|
110
|
+
}
|
|
111
|
+
function BitbucketIcon() {
|
|
112
|
+
return (_jsx("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor", children: _jsx("path", { d: "M.778 1.213a.768.768 0 0 0-.768.892l3.263 19.81c.084.5.515.868 1.022.873H19.95a.772.772 0 0 0 .77-.646l3.27-20.03a.768.768 0 0 0-.768-.891L.778 1.213zM14.52 15.53H9.522L8.17 8.466h7.561l-1.211 7.064z" }) }));
|
|
113
|
+
}
|
|
114
|
+
export function SignInForm({ providers: propProviders, autoFetch = true, theme = 'auto', style, className, }) {
|
|
16
115
|
const config = useAuthConfig();
|
|
17
116
|
const { providers: fetchedProviders, isLoading: providersLoading } = useProviders();
|
|
18
117
|
const [activeTab, setActiveTab] = useState('signin');
|
|
19
118
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
20
119
|
const [returnTo, setReturnTo] = useState('/');
|
|
120
|
+
const [isDark, setIsDark] = useState(false);
|
|
121
|
+
// Resolve theme
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (theme === 'dark') {
|
|
124
|
+
setIsDark(true);
|
|
125
|
+
}
|
|
126
|
+
else if (theme === 'light') {
|
|
127
|
+
setIsDark(false);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// auto — check prefers-color-scheme
|
|
131
|
+
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
|
132
|
+
setIsDark(mq.matches);
|
|
133
|
+
const handler = (e) => setIsDark(e.matches);
|
|
134
|
+
mq.addEventListener('change', handler);
|
|
135
|
+
return () => mq.removeEventListener('change', handler);
|
|
136
|
+
}
|
|
137
|
+
}, [theme]);
|
|
21
138
|
// Use prop providers if explicitly provided, otherwise use fetched providers
|
|
22
139
|
const enabledProviders = propProviders
|
|
23
140
|
? propProviders
|
|
@@ -57,23 +174,31 @@ export function SignInForm({ providers: propProviders, autoFetch = true, classNa
|
|
|
57
174
|
setActiveTab(tab);
|
|
58
175
|
setErrorMessage(null);
|
|
59
176
|
}
|
|
60
|
-
|
|
61
|
-
const
|
|
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
|
-
const buttonText = (provider) => {
|
|
68
|
-
const providerNames = {
|
|
177
|
+
const providerName = (id) => {
|
|
178
|
+
const names = {
|
|
69
179
|
github: 'GitHub',
|
|
70
180
|
google: 'Google',
|
|
71
181
|
apple: 'Apple',
|
|
72
182
|
bitbucket: 'Bitbucket',
|
|
73
183
|
};
|
|
74
|
-
|
|
184
|
+
return names[id] || id;
|
|
185
|
+
};
|
|
186
|
+
const buttonText = (provider) => {
|
|
187
|
+
const name = providerName(provider);
|
|
75
188
|
return activeTab === 'signin' ? `Sign in with ${name}` : `Sign up with ${name}`;
|
|
76
189
|
};
|
|
77
|
-
|
|
190
|
+
const providerStyles = {
|
|
191
|
+
github: { ...styles.buttonBase, ...styles.githubBtn(isDark) },
|
|
192
|
+
google: { ...styles.buttonBase, ...styles.googleBtn(isDark) },
|
|
193
|
+
apple: { ...styles.buttonBase, ...styles.appleBtn },
|
|
194
|
+
bitbucket: { ...styles.buttonBase, ...styles.bitbucketBtn },
|
|
195
|
+
};
|
|
196
|
+
const providerIcons = {
|
|
197
|
+
github: _jsx(GithubIcon, {}),
|
|
198
|
+
google: _jsx(GoogleIcon, {}),
|
|
199
|
+
apple: _jsx(AppleIcon, {}),
|
|
200
|
+
bitbucket: _jsx(BitbucketIcon, {}),
|
|
201
|
+
};
|
|
202
|
+
return (_jsxs("div", { className: className, style: style, children: [_jsx("style", { children: `@keyframes tonycodes-auth-spin { to { transform: rotate(360deg) } }` }), _jsxs("div", { style: styles.tabContainer(isDark), children: [_jsx("button", { type: "button", style: styles.tab(activeTab === 'signin', isDark), onClick: () => switchTab('signin'), children: "Sign In" }), _jsx("button", { type: "button", style: styles.tab(activeTab === 'signup', isDark), onClick: () => switchTab('signup'), children: "Sign Up" })] }), errorMessage && (_jsx("div", { style: styles.error(isDark), children: errorMessage })), autoFetch && !propProviders && providersLoading ? (_jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '16px 0' }, children: _jsx("div", { style: styles.spinner(isDark) }) })) : (_jsx("div", { style: styles.buttonGroup, children: enabledProviders.map((id) => (_jsxs("button", { type: "button", style: providerStyles[id] || styles.buttonBase, onClick: () => handleOAuth(id), onMouseEnter: (e) => { e.currentTarget.style.opacity = '0.9'; }, onMouseLeave: (e) => { e.currentTarget.style.opacity = '1'; }, children: [providerIcons[id], _jsx("span", { children: buttonText(id) })] }, id))) })), _jsx("p", { style: styles.terms(isDark), children: "By continuing, you agree to our terms of service." })] }));
|
|
78
203
|
}
|
|
79
204
|
//# sourceMappingURL=SignInForm.js.map
|
package/dist/SignInForm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SignInForm.js","sourceRoot":"","sources":["../src/SignInForm.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAoB,MAAM,mBAAmB,CAAC;AAInE,MAAM,cAAc,GAA2B;IAC7C,iBAAiB,EAAE,0DAA0D;IAC7E,YAAY,EAAE,wDAAwD;IACtE,YAAY,EAAE,yCAAyC;IACvD,aAAa,EAAE,oCAAoC;CACpD,CAAC;AAEF,MAAM,WAAW,GAAG,
|
|
1
|
+
{"version":3,"file":"SignInForm.js","sourceRoot":"","sources":["../src/SignInForm.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tonycodes/auth-react",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "React SDK for Tony Auth service",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/tonycodes/auth-react"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"main": "dist/index.js",
|
|
7
11
|
"types": "dist/index.d.ts",
|
|
@@ -9,35 +13,11 @@
|
|
|
9
13
|
".": {
|
|
10
14
|
"import": "./dist/index.js",
|
|
11
15
|
"types": "./dist/index.d.ts"
|
|
12
|
-
}
|
|
13
|
-
"./style.css": "./dist/style.css"
|
|
16
|
+
}
|
|
14
17
|
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist"
|
|
17
|
-
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"build": "tsc
|
|
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"
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"dev": "tsc --watch"
|
|
41
21
|
},
|
|
42
22
|
"peerDependencies": {
|
|
43
23
|
"react": ">=18.0.0",
|
|
@@ -45,7 +25,6 @@
|
|
|
45
25
|
},
|
|
46
26
|
"devDependencies": {
|
|
47
27
|
"@types/react": "^18.2.47",
|
|
48
|
-
"tailwindcss": "^3.4.19",
|
|
49
28
|
"typescript": "^5.3.3"
|
|
50
29
|
}
|
|
51
30
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { useContext, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { AuthConfigContext } from './AuthProvider.js';
|
|
3
|
+
|
|
4
|
+
interface AuthCallbackProps {
|
|
5
|
+
/** API URL to exchange code (overrides config.apiUrl, defaults to current origin) */
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
/** Where to redirect after successful auth */
|
|
8
|
+
onSuccess?: (returnTo: string) => void;
|
|
9
|
+
/** Called on auth failure */
|
|
10
|
+
onError?: (error: string) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Component that handles the OAuth callback.
|
|
15
|
+
* Mount at /auth/callback route in your React router.
|
|
16
|
+
*
|
|
17
|
+
* When mounted, this component:
|
|
18
|
+
* 1. Extracts the authorization code from the URL
|
|
19
|
+
* 2. Calls /api/auth/callback on the backend to exchange the code for tokens
|
|
20
|
+
* 3. Redirects to the original page (from state)
|
|
21
|
+
*
|
|
22
|
+
* Your Express backend should mount callbackHandler() at /api/auth/callback.
|
|
23
|
+
*
|
|
24
|
+
* The API URL is resolved in this order:
|
|
25
|
+
* 1. `apiUrl` prop (explicit override)
|
|
26
|
+
* 2. `config.apiUrl` from AuthProvider
|
|
27
|
+
* 3. `config.appUrl` from AuthProvider
|
|
28
|
+
* 4. Current window origin (fallback)
|
|
29
|
+
*/
|
|
30
|
+
export function AuthCallback({ apiUrl: apiUrlProp, onSuccess, onError }: AuthCallbackProps) {
|
|
31
|
+
const config = useContext(AuthConfigContext);
|
|
32
|
+
const [error, setError] = useState<string | null>(null);
|
|
33
|
+
const exchangedRef = useRef(false);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
// Guard against React 18 strict mode double-firing
|
|
37
|
+
if (exchangedRef.current) return;
|
|
38
|
+
exchangedRef.current = true;
|
|
39
|
+
|
|
40
|
+
const params = new URLSearchParams(window.location.search);
|
|
41
|
+
const code = params.get('code');
|
|
42
|
+
const state = params.get('state');
|
|
43
|
+
const errorParam = params.get('error');
|
|
44
|
+
|
|
45
|
+
if (errorParam) {
|
|
46
|
+
setError(errorParam);
|
|
47
|
+
if (onError) {
|
|
48
|
+
onError(errorParam);
|
|
49
|
+
} else {
|
|
50
|
+
let redirectReturnTo = '/';
|
|
51
|
+
if (state) {
|
|
52
|
+
try {
|
|
53
|
+
const decoded = JSON.parse(atob(state));
|
|
54
|
+
redirectReturnTo = decoded.returnTo || '/';
|
|
55
|
+
} catch {
|
|
56
|
+
// Invalid state — default to /
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const loginUrl = new URL('/login', window.location.origin);
|
|
60
|
+
loginUrl.searchParams.set('error', errorParam);
|
|
61
|
+
if (redirectReturnTo !== '/') loginUrl.searchParams.set('returnTo', redirectReturnTo);
|
|
62
|
+
window.location.href = loginUrl.toString();
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!code) {
|
|
68
|
+
setError('Missing authorization code');
|
|
69
|
+
onError?.('Missing authorization code');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Resolve API URL: prop > config.apiUrl > config.appUrl > current origin
|
|
74
|
+
const baseUrl = apiUrlProp || config?.apiUrl || config?.appUrl || window.location.origin;
|
|
75
|
+
|
|
76
|
+
async function exchange() {
|
|
77
|
+
try {
|
|
78
|
+
const res = await fetch(`${baseUrl}/api/auth/callback?code=${encodeURIComponent(code!)}`, {
|
|
79
|
+
credentials: 'include',
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (!res.ok) {
|
|
83
|
+
const data = await res.json();
|
|
84
|
+
throw new Error(data.error || 'Authentication failed');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Decode state to get returnTo path
|
|
88
|
+
let returnTo = '/';
|
|
89
|
+
if (state) {
|
|
90
|
+
try {
|
|
91
|
+
const decoded = JSON.parse(atob(state));
|
|
92
|
+
returnTo = decoded.returnTo || '/';
|
|
93
|
+
} catch {
|
|
94
|
+
// Invalid state — default to /
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (onSuccess) {
|
|
99
|
+
onSuccess(returnTo);
|
|
100
|
+
} else {
|
|
101
|
+
window.location.href = returnTo;
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
const message = err instanceof Error ? err.message : 'Authentication failed';
|
|
105
|
+
setError(message);
|
|
106
|
+
onError?.(message);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
exchange();
|
|
111
|
+
}, [apiUrlProp, config, onSuccess, onError]);
|
|
112
|
+
|
|
113
|
+
if (error) {
|
|
114
|
+
return (
|
|
115
|
+
<div style={{ padding: '2rem', textAlign: 'center' }}>
|
|
116
|
+
<h2>Authentication Failed</h2>
|
|
117
|
+
<p>{error}</p>
|
|
118
|
+
<a href="/">Go Home</a>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<div style={{ padding: '2rem', textAlign: 'center' }}>
|
|
125
|
+
<p>Signing in...</p>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
}
|