Add auth code input flow for headless OAuth

This commit is contained in:
Zac Gaetano 2026-04-04 23:54:24 -04:00
parent 68968fe7f4
commit 3ca9c8fb92

View file

@ -80,9 +80,12 @@ const App = () => {
}; };
const [loginLoading, setLoginLoading] = useState(false); const [loginLoading, setLoginLoading] = useState(false);
const [authCode, setAuthCode] = useState('');
const [codeSubmitting, setCodeSubmitting] = useState(false);
const handleLogin = async () => { const handleLogin = async () => {
setLoginLoading(true); setLoginLoading(true);
setAuthCode('');
try { try {
const response = await fetch('/api/auth/login', { method: 'POST' }); const response = await fetch('/api/auth/login', { method: 'POST' });
const data = await response.json(); const data = await response.json();
@ -99,6 +102,29 @@ const App = () => {
setLoginLoading(false); setLoginLoading(false);
}; };
const handleSubmitCode = async (e) => {
e.preventDefault();
if (!authCode.trim()) return;
setCodeSubmitting(true);
try {
const response = await fetch('/api/auth/code', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: authCode.trim() })
});
const data = await response.json();
if (data.status === 'logged_in') {
setAuthCode('');
fetchAuthStatus();
} else {
alert(data.message || 'Code submission failed');
}
} catch (error) {
console.error('Error submitting auth code:', error);
}
setCodeSubmitting(false);
};
const handleLogout = async () => { const handleLogout = async () => {
try { try {
await fetch('/api/auth/logout', { method: 'POST' }); await fetch('/api/auth/logout', { method: 'POST' });
@ -229,7 +255,13 @@ const App = () => {
{authStatus?.status === 'logged_in' ? ( {authStatus?.status === 'logged_in' ? (
<span className="auth-ok" title={authStatus.account}> {authStatus.account || 'Logged in'}</span> <span className="auth-ok" title={authStatus.account}> {authStatus.account || 'Logged in'}</span>
) : authStatus?.status === 'pending' ? ( ) : authStatus?.status === 'pending' ? (
<span className="auth-pending"> Awaiting login</span> <form className="header-code-form" onSubmit={handleSubmitCode}>
<input type="text" placeholder="Paste auth code here" value={authCode}
onChange={e => setAuthCode(e.target.value)} className="header-code-input" />
<button type="submit" className="header-code-btn" disabled={codeSubmitting}>
{codeSubmitting ? '…' : '→'}
</button>
</form>
) : ( ) : (
<button className="auth-login-btn" onClick={handleLogin} disabled={loginLoading}> <button className="auth-login-btn" onClick={handleLogin} disabled={loginLoading}>
{loginLoading ? '⏳ Connecting…' : '🔐 Login with Claude Max'} {loginLoading ? '⏳ Connecting…' : '🔐 Login with Claude Max'}
@ -301,14 +333,23 @@ const App = () => {
) : authStatus?.status === 'pending' ? ( ) : authStatus?.status === 'pending' ? (
<div className="auth-pending-panel"> <div className="auth-pending-panel">
<span className="auth-icon"></span> <span className="auth-icon"></span>
<div> <div className="auth-pending-content">
<div className="auth-title">Waiting for browser login</div> <div className="auth-title">Waiting for authorization code</div>
<div className="auth-sub">Complete the login in the browser tab that opened, then come back here.</div> <div className="auth-sub">1. Click the link below to authorize in your browser</div>
<div className="auth-sub">2. After authorizing, you'll see a code paste it below</div>
{authStatus.auth_url && ( {authStatus.auth_url && (
<a className="auth-url-link" href={authStatus.auth_url} target="_blank" rel="noreferrer"> <a className="auth-url-link" href={authStatus.auth_url} target="_blank" rel="noreferrer">
Click here if the browser tab didn't open Open authorization page
</a> </a>
)} )}
<form className="auth-code-form" onSubmit={handleSubmitCode}>
<input type="text" placeholder="Paste your authorization code here"
value={authCode} onChange={e => setAuthCode(e.target.value)}
className="auth-code-input" />
<button type="submit" className="btn btn-primary btn-sm" disabled={codeSubmitting}>
{codeSubmitting ? 'Verifying…' : 'Submit Code'}
</button>
</form>
</div> </div>
</div> </div>
) : ( ) : (