(link)
| 647 | bytes.forEach((byte) => { binary += String.fromCharCode(byte); }); |
| 648 | return window.btoa(binary) |
| 649 | .replace(/\+/g, "-") |
| 650 | .replace(/\//g, "_") |
| 651 | .replace(/=+$/, ""); |
| 652 | } |
| 653 | |
| 654 | async function beginGithubLogin(link) { |
| 655 | const nonce = oauthNonce(); |
| 656 | try { |
| 657 | window.sessionStorage.setItem(OAUTH_NONCE_KEY, nonce); |
| 658 | const url = new URL(link.href, window.location.href); |
| 659 | url.searchParams.set("client_nonce", nonce); |
| 660 | const response = await fetch(url, { |
| 661 | credentials: "same-origin", |
| 662 | headers: { Accept: "application/json" }, |
| 663 | }); |
| 664 | const body = await response.json(); |
| 665 | if (!response.ok || !body.authorizationUrl) { |
| 666 | throw new Error(body.error || "GitHub sign-in is unavailable."); |
| 667 | } |
| 668 | window.location.assign(body.authorizationUrl); |
| 669 | } catch (error) { |
| 670 | try { |
| 671 | window.sessionStorage.removeItem(OAUTH_NONCE_KEY); |
| 672 | } catch { |
| 673 | // Storage may be unavailable in hardened browser modes. |
| 674 | } |
| 675 | link.removeAttribute("aria-disabled"); |
| 676 | showToast(error.message || "GitHub sign-in is unavailable."); |
no test coverage detected