Commit9374d58
fix(auth): clean up login handler + wire Google OAuth into signin-v2
fix(auth): clean up login handler + wire Google OAuth into signin-v2 - Remove corrupted JSX that bled through from rebase conflict resolution - Pass googleEnabled/githubEnabled flags from route to SignInV2 - SignInV2 now conditionally renders Google/GitHub buttons (default true) - Fix OAuth hrefs: /auth/google → /login/google, /auth/github → /login/github Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2 files changed+25−1939374d58214607ce1475a40b67010e0766fd8fdc9
2 changed files+25−193
Modifiedsrc/routes/auth.tsx+2−176View fileUnifiedSplit
@@ -359,186 +359,12 @@ auth.get("/login", softAuth, async (c) => {
359359 const googleCfg = await getGoogleOauthConfig();
360360 const googleEnabled =
361361 !!googleCfg?.enabled && !!googleCfg.clientId && !!googleCfg.clientSecret;
362 const csrf = c.get("csrfToken") as string | undefined;
363362 return c.html(
364363 <SignInV2
365 <script dangerouslySetInnerHTML={{__html: /* js */`
366 (function(){
367 var inp = document.querySelector('input[name="username"]');
368 var hint = document.getElementById('sso-domain-hint');
369 if(!inp || !hint) return;
370 var last = '';
371 inp.addEventListener('input', function(){
372 var val = inp.value.trim();
373 var at = val.indexOf('@');
374 if(at < 0) { hint.style.display='none'; return; }
375 var domain = val.slice(at+1).toLowerCase();
376 if(!domain || domain === last) return;
377 last = domain;
378 fetch('/api/sso/domain-hint?domain='+encodeURIComponent(domain))
379 .then(function(r){ return r.ok ? r.json() : null; })
380 .then(function(d){ hint.style.display = (d && d.sso) ? '' : 'none'; })
381 .catch(function(){ hint.style.display='none'; });
382 });
383 })();
384 `}} />
385 {/* Provider buttons (Google + GitHub) — only rendered when the
386 admin has configured + enabled them via /admin/google-oauth or
387 /admin/github-oauth. The "or" divider above the first available
388 provider sets the visual break from the password form. */}
389 {(googleEnabled || githubEnabled) && (
390
391 )}
392 {googleEnabled && (
393
394 href="/login/google"
395 class="btn btn-block oauth-btn oauth-google"
396 aria-label="Sign in with Google"
397 >
398 <svg
399 class="oauth-icon"
400 width="18"
401 height="18"
402 viewBox="0 0 18 18"
403 aria-hidden="true"
404 >
405 <path
406 d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844a4.14 4.14 0 0 1-1.796 2.716v2.259h2.908c1.702-1.567 2.684-3.875 2.684-6.615z"
407 fill="#4285F4"
408 />
409 <path
410 d="M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18z"
411 fill="#34A853"
412 />
413 <path
414 d="M3.964 10.71A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.042l3.007-2.332z"
415 fill="#FBBC05"
416 />
417 <path
418 d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z"
419 fill="#EA4335"
420 />
421 </svg>
422 <span>Sign in with Google</span>
423 </a>
424 )}
425 {githubEnabled && (
426 <a
427 href="/login/github"
428 class="btn btn-block oauth-btn oauth-github"
429 aria-label="Sign in with GitHub"
430 style={googleEnabled ? "margin-top:8px" : undefined}
431 >
432 <svg
433 class="oauth-icon"
434 width="18"
435 height="18"
436 viewBox="0 0 16 16"
437 aria-hidden="true"
438 fill="currentColor"
439 >
440 <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z" />
441 </svg>
442 <span>Sign in with GitHub</span>
443 </a>
444 )}
445 {ssoEnabled && (
446 <a
447 href="/login/sso"
448 class="btn btn-block oauth-btn oauth-sso"
449 aria-label={`Sign in with ${ssoLabel}`}
450 style={(googleEnabled || githubEnabled) ? "margin-top:8px" : undefined}
451 >
452 <span>Sign in with {ssoLabel}</span>
453 </a>
454 )}
455 <div class="auth-passkey">
456
457 <button type="button" id="pk-signin-btn" class="btn">
458 Sign in with passkey
459 </button>
460
461 id="pk-signin-status"
462 class="auth-status"
463 aria-live="polite"
464 ></div>
465 </div>
466
467 <Text>New to gluecron? <a href="/register">Create an account</a></Text>
468 </p>
469 <script
470 dangerouslySetInnerHTML={{
471 __html: /* js */ `
472 (function () {
473 const btn = document.getElementById('pk-signin-btn');
474 const status = document.getElementById('pk-signin-status');
475 const userInput = document.getElementById('username');
476 const redirect = ${JSON.stringify(redirect || "/")};
477 if (!btn) return;
478 function b64uToBuf(s) {
479 s = s.replace(/-/g,'+').replace(/_/g,'/');
480 while (s.length % 4) s += '=';
481 const bin = atob(s);
482 const buf = new Uint8Array(bin.length);
483 for (let i=0;i<bin.length;i++) buf[i] = bin.charCodeAt(i);
484 return buf.buffer;
485 }
486 function bufToB64u(buf) {
487 const bytes = new Uint8Array(buf);
488 let bin = '';
489 for (let i=0;i<bytes.length;i++) bin += String.fromCharCode(bytes[i]);
490 return btoa(bin).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,'');
491 }
492 btn.addEventListener('click', async function () {
493 if (!window.PublicKeyCredential) {
494 status.textContent = 'Passkeys not supported in this browser.';
495 return;
496 }
497 status.textContent = 'Preparing…';
498 try {
499 const username = (userInput && userInput.value || '').trim();
500 const optsRes = await fetch('/api/passkeys/auth/options', {
501 method: 'POST',
502 headers: { 'content-type': 'application/json' },
503 body: JSON.stringify(username ? { username: username } : {})
504 });
505 if (!optsRes.ok) throw new Error('options failed');
506 const { options, sessionKey } = await optsRes.json();
507 options.challenge = b64uToBuf(options.challenge);
508 if (options.allowCredentials) {
509 options.allowCredentials = options.allowCredentials.map(function (c) {
510 return Object.assign({}, c, { id: b64uToBuf(c.id) });
511 });
512 }
513 status.textContent = 'Touch your authenticator…';
514 const cred = await navigator.credentials.get({ publicKey: options });
515 const resp = {
516 id: cred.id,
517 rawId: bufToB64u(cred.rawId),
518 type: cred.type,
519 response: {
520 clientDataJSON: bufToB64u(cred.response.clientDataJSON),
521 authenticatorData: bufToB64u(cred.response.authenticatorData),
522 signature: bufToB64u(cred.response.signature),
523 userHandle: cred.response.userHandle ? bufToB64u(cred.response.userHandle) : null
524 },
525 clientExtensionResults: cred.getClientExtensionResults ? cred.getClientExtensionResults() : {}
526 };
527 const verifyRes = await fetch('/api/passkeys/auth/verify', {
528 method: 'POST',
529 headers: { 'content-type': 'application/json' },
530 body: JSON.stringify({ sessionKey: sessionKey, response: resp })
531 });
532 if (!verifyRes.ok) {
533 const j = await verifyRes.json().catch(function () { return {}; });
534 throw new Error(j.error || 'verify failed');
535 }
536 status.textContent = 'Signed in. Redirecting…';
537 window.location.href = redirect;
538 } catch (e) {
539 status.textContent = 'Error: ' + (e && e.message ? e.message : e);
540364 redirect={redirect}
541365 error={error ? decodeURIComponent(error) : ""}
366 googleEnabled={googleEnabled}
367 githubEnabled={githubEnabled}
542368 />
543369 );
544370});
Modifiedsrc/views/signin-v2.tsx+23−17View fileUnifiedSplit
@@ -4,10 +4,12 @@ export interface SignInV2Props {
44 redirect?: string;
55 error?: string;
66 csrfToken?: string;
7 googleEnabled?: boolean;
8 githubEnabled?: boolean;
79}
810
911export const SignInV2: FC<SignInV2Props> = (props) => {
10 const { redirect = "", error = "", csrfToken } = props;
12 const { redirect = "", error = "", csrfToken, googleEnabled = true, githubEnabled = true } = props;
1113
1214 return (
1315 <>
@@ -32,22 +34,26 @@ export const SignInV2: FC<SignInV2Props> = (props) => {
3234 )}
3335
3436 <div class="si-oauth-group">
35 <a href="/auth/github" class="si-btn si-btn-github">
36 <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
37 <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z" />
38 </svg>
39 Continue with GitHub
40 </a>
41
42 <a href="/auth/google" class="si-btn si-btn-secondary">
43 <svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
44 <path fill="#4285F4" d="M23.5 12.27c0-.85-.08-1.66-.22-2.45H12v4.63h6.45a5.52 5.52 0 0 1-2.39 3.62v3h3.87c2.26-2.09 3.57-5.16 3.57-8.8z" />
45 <path fill="#34A853" d="M12 24c3.24 0 5.96-1.07 7.94-2.91l-3.87-3c-1.07.72-2.45 1.15-4.07 1.15-3.13 0-5.78-2.11-6.73-4.96H1.29v3.1A12 12 0 0 0 12 24z" />
46 <path fill="#FBBC05" d="M5.27 14.28a7.2 7.2 0 0 1 0-4.56v-3.1H1.29a12 12 0 0 0 0 10.76l3.98-3.1z" />
47 <path fill="#EA4335" d="M12 4.76c1.76 0 3.34.61 4.59 1.8l3.44-3.44C17.95 1.19 15.24 0 12 0A12 12 0 0 0 1.29 6.62l3.98 3.1C6.22 6.87 8.87 4.76 12 4.76z" />
48 </svg>
49 Continue with Google
50 </a>
37 {githubEnabled && (
38 <a href="/login/github" class="si-btn si-btn-github">
39 <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
40 <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z" />
41 </svg>
42 Continue with GitHub
43 </a>
44 )}
45
46 {googleEnabled && (
47 <a href="/login/google" class="si-btn si-btn-secondary">
48 <svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
49 <path fill="#4285F4" d="M23.5 12.27c0-.85-.08-1.66-.22-2.45H12v4.63h6.45a5.52 5.52 0 0 1-2.39 3.62v3h3.87c2.26-2.09 3.57-5.16 3.57-8.8z" />
50 <path fill="#34A853" d="M12 24c3.24 0 5.96-1.07 7.94-2.91l-3.87-3c-1.07.72-2.45 1.15-4.07 1.15-3.13 0-5.78-2.11-6.73-4.96H1.29v3.1A12 12 0 0 0 12 24z" />
51 <path fill="#FBBC05" d="M5.27 14.28a7.2 7.2 0 0 1 0-4.56v-3.1H1.29a12 12 0 0 0 0 10.76l3.98-3.1z" />
52 <path fill="#EA4335" d="M12 4.76c1.76 0 3.34.61 4.59 1.8l3.44-3.44C17.95 1.19 15.24 0 12 0A12 12 0 0 0 1.29 6.62l3.98 3.1C6.22 6.87 8.87 4.76 12 4.76z" />
53 </svg>
54 Continue with Google
55 </a>
56 )}
5157
5258 <button
5359 id="pk-signin-btn"
5460