fix(sso): send the PKCE verifier for GitHub login (#1534)

This commit is contained in:
ZacharyZcR authored and GitHub committed 2026-10-03 21:00:03 +08:00
1 parent 28e74de452
commit 93db4799c0
2 files changed
+17 -4

No files matched your search

+3 -4
View File
@@ -194,7 +194,6 @@ export function createSsoLogin(ctx: PluginContext, store: ProviderStore) {
config: OidcConfig, config: OidcConfig,
code: string, code: string,
pending: PendingState, pending: PendingState,
withVerifier: boolean,
): Promise<Record<string, unknown>> { ): Promise<Record<string, unknown>> {
const response = await fetch(config.token_url, { const response = await fetch(config.token_url, {
method: "POST", method: "POST",
@@ -208,7 +207,7 @@ export function createSsoLogin(ctx: PluginContext, store: ProviderStore) {
client_secret: config.client_secret, client_secret: config.client_secret,
code, code,
redirect_uri: pending.backendCallback, redirect_uri: pending.backendCallback,
...(withVerifier ? { code_verifier: pending.codeVerifier } : {}), code_verifier: pending.codeVerifier,
}), }),
...buildFetchOptions(config.ca_cert), ...buildFetchOptions(config.ca_cert),
} as RequestInit); } as RequestInit);
@@ -231,7 +230,7 @@ export function createSsoLogin(ctx: PluginContext, store: ProviderStore) {
code: string, code: string,
pending: PendingState, pending: PendingState,
): Promise<PluginVerifiedIdentity> { ): Promise<PluginVerifiedIdentity> {
const tokenData = await exchangeCode(provider.config, code, pending, false); const tokenData = await exchangeCode(provider.config, code, pending);
const fetchOptions = buildFetchOptions(provider.config.ca_cert); const fetchOptions = buildFetchOptions(provider.config.ca_cert);
const headers = { const headers = {
Authorization: `Bearer ${tokenData.access_token}`, Authorization: `Bearer ${tokenData.access_token}`,
@@ -341,7 +340,7 @@ export function createSsoLogin(ctx: PluginContext, store: ProviderStore) {
pending: PendingState, pending: PendingState,
): Promise<PluginVerifiedIdentity> { ): Promise<PluginVerifiedIdentity> {
const { config } = provider; const { config } = provider;
const tokenData = await exchangeCode(config, code, pending, true); const tokenData = await exchangeCode(config, code, pending);
let userInfo: Record<string, unknown> | null = null; let userInfo: Record<string, unknown> | null = null;
const claimSources: Record<string, unknown>[] = []; const claimSources: Record<string, unknown>[] = [];
+14
View File
@@ -1,3 +1,4 @@
import { createHash } from "node:crypto";
import { afterEach, describe, expect, it, vi } from "vitest"; import { afterEach, describe, expect, it, vi } from "vitest";
import { import {
LEGACY_DDL, LEGACY_DDL,
@@ -356,6 +357,19 @@ describe("the callback", () => {
"GET", "GET",
`/callback?code=abc&state=${url.searchParams.get("state")}`, `/callback?code=abc&state=${url.searchParams.get("state")}`,
); );
const exchange = vi
.mocked(fetch)
.mock.calls.find(
([url]) =>
String(url) === "https://github.com/login/oauth/access_token",
);
const token = new URLSearchParams(String(exchange?.[1]?.body));
const verifier = token.get("code_verifier");
expect(verifier).toBeTruthy();
expect(url.searchParams.get("code_challenge_method")).toBe("S256");
expect(createHash("sha256").update(verifier!).digest("base64url")).toBe(
url.searchParams.get("code_challenge"),
);
expect(callback.body.identity).toMatchObject({ expect(callback.body.identity).toMatchObject({
provider: String(created.body.id), provider: String(created.body.id),
subject: "42", subject: "42",