Repository navigation
feat(catalog): oauth/oidc-client with its proof suite - #17
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Comment on lines
+28
to
+44
| router.get('/login', async (req, res) => { | ||
| const oidc = await getOidc() | ||
| const codeVerifier = client.randomPKCECodeVerifier() | ||
| const state = client.randomState() | ||
| const nonce = client.randomNonce() | ||
| req.session.pendingLogin = { codeVerifier, state, nonce, startedAt: Date.now() } | ||
| const url = client.buildAuthorizationUrl(oidc, { | ||
| redirect_uri: config.oidc.redirectUri, | ||
| scope: 'openid email profile', | ||
| code_challenge: await client.calculatePKCECodeChallenge(codeVerifier), | ||
| code_challenge_method: 'S256', | ||
| state, | ||
| nonce, | ||
| }) | ||
| await promisify((cb) => req.session.save(cb))() | ||
| res.redirect(url.href) | ||
| }) |
Comment on lines
+46
to
+85
| router.get('/auth/callback', async (req, res) => { | ||
| // Single use: whatever happens next, this login attempt is consumed. | ||
| const pending = req.session.pendingLogin | ||
| delete req.session.pendingLogin | ||
| if (!pending || Date.now() - pending.startedAt > LOGIN_TIMEOUT_MS) { | ||
| res.status(400).send('This sign-in link has expired. Please sign in again.') | ||
| return | ||
| } | ||
|
|
||
| let tokens: Awaited<ReturnType<typeof client.authorizationCodeGrant>> | ||
| try { | ||
| tokens = await client.authorizationCodeGrant(await getOidc(), callbackUrl(config, req), { | ||
| pkceCodeVerifier: pending.codeVerifier, | ||
| expectedState: pending.state, | ||
| expectedNonce: pending.nonce, | ||
| idTokenExpected: true, | ||
| }) | ||
| } catch (err) { | ||
| console.warn('sign-in failed:', err instanceof Error ? err.message : err) | ||
| res.status(400).send('Sign-in failed. Please try again.') | ||
| return | ||
| } | ||
|
|
||
| const claims = tokens.claims() | ||
| if (!claims) { | ||
| res.status(400).send('Sign-in failed. Please try again.') | ||
| return | ||
| } | ||
|
|
||
| // A new session id after login, so a session id planted before login is worthless. | ||
| await promisify((cb) => req.session.regenerate(cb))() | ||
| req.session.user = { | ||
| sub: claims.sub, | ||
| email: typeof claims.email === 'string' ? claims.email : undefined, | ||
| name: typeof claims.name === 'string' ? claims.name : undefined, | ||
| } | ||
| req.session.idToken = tokens.id_token | ||
| await promisify((cb) => req.session.save(cb))() | ||
| res.redirect('/') | ||
| }) |
Comment on lines
+30
to
+41
| return session({ | ||
| name: SESSION_COOKIE, | ||
| secret: config.sessionSecret, | ||
| resave: false, | ||
| saveUninitialized: false, | ||
| cookie: { | ||
| httpOnly: true, | ||
| sameSite: 'lax', | ||
| secure: new URL(config.oidc.redirectUri).protocol === 'https:', | ||
| maxAge: 8 * 60 * 60 * 1000, | ||
| }, | ||
| }) |
Comment on lines
+25
to
+44
| router.get('/login', async (_req, res) => { | ||
| const attempt = randomBytes(32).toString('base64url') | ||
| const login: PendingLogin = { | ||
| codeVerifier: client.randomPKCECodeVerifier(), | ||
| state: client.randomState(), | ||
| nonce: client.randomNonce(), | ||
| expiresAt: Date.now() + LOGIN_TTL_MS, | ||
| } | ||
| pending.set(attempt, login) | ||
| setCookie(res, LOGIN_COOKIE, attempt, LOGIN_TTL_MS) | ||
| const url = client.buildAuthorizationUrl(await oidc(), { | ||
| redirect_uri: config.oidc.redirectUri, | ||
| scope: 'openid email profile offline_access', | ||
| code_challenge: await client.calculatePKCECodeChallenge(login.codeVerifier), | ||
| code_challenge_method: 'S256', | ||
| state: login.state, | ||
| nonce: login.nonce, | ||
| }) | ||
| res.redirect(url.href) | ||
| }) |
Comment on lines
+46
to
+86
| router.get('/auth/callback', async (req, res) => { | ||
| const attempt = readCookie(req, LOGIN_COOKIE) ?? '' | ||
| const login = pending.get(attempt) | ||
| pending.delete(attempt) | ||
| res.clearCookie(LOGIN_COOKIE, { path: '/' }) | ||
| if (!login || login.expiresAt < Date.now()) { | ||
| res.status(400).send('This sign-in link has expired. Please sign in again.') | ||
| return | ||
| } | ||
| const currentUrl = new URL(config.oidc.redirectUri) | ||
| currentUrl.search = new URL(req.originalUrl, currentUrl).search | ||
| try { | ||
| const tokens = await client.authorizationCodeGrant(await oidc(), currentUrl, { | ||
| pkceCodeVerifier: login.codeVerifier, | ||
| expectedState: login.state, | ||
| expectedNonce: login.nonce, | ||
| }) | ||
| const claims = tokens.claims() | ||
| if (!claims || !tokens.refresh_token) throw new Error('the IdP did not return the expected tokens') | ||
| const sid = randomBytes(32).toString('base64url') | ||
| await sessions.set(sid, { | ||
| user: { | ||
| sub: claims.sub, | ||
| email: typeof claims.email === 'string' ? claims.email : undefined, | ||
| name: typeof claims.name === 'string' ? claims.name : undefined, | ||
| }, | ||
| tokens: { | ||
| accessToken: tokens.access_token, | ||
| refreshToken: tokens.refresh_token, | ||
| idToken: tokens.id_token, | ||
| expiresAt: Date.now() + (tokens.expiresIn() ?? 300) * 1000, | ||
| }, | ||
| createdAt: Date.now(), | ||
| }) | ||
| setCookie(res, SESSION_COOKIE, sid, SESSION_TTL_MS) | ||
| res.redirect('/') | ||
| } catch (err) { | ||
| console.warn('sign-in failed:', err instanceof Error ? err.message : err) | ||
| res.status(400).send('Sign-in failed. Please try again.') | ||
| } | ||
| }) |
| this.#server = createServer((req, res) => { | ||
| this.#handle(req, res).catch((err: unknown) => { | ||
| res.statusCode = 500 | ||
| res.end(String(err)) |
| this.#server = createServer((req, res) => { | ||
| this.#handle(req, res).catch((err: unknown) => { | ||
| res.statusCode = 500 | ||
| res.end(String(err)) |
| this.#server = createServer((req, res) => { | ||
| this.#handle(req, res).catch((err: unknown) => { | ||
| res.statusCode = 500 | ||
| res.end(String(err)) |
| this.#server = createServer((req, res) => { | ||
| this.#handle(req, res).catch((err: unknown) => { | ||
| res.statusCode = 500 | ||
| res.end(String(err)) |
| this.#server = createServer((req, res) => { | ||
| this.#handle(req, res).catch((err: unknown) => { | ||
| res.statusCode = 500 | ||
| res.end(String(err)) |
This was referenced Nov 2, 2026
dgesteves
added a commit
that referenced
this pull request
Nov 2, 2026
…e it stands (#22) The README was a 12-line "under construction" stub. This replaces it with the front door: the problem, what Sigil does, a quick start, how a proof works, trust levels, an honest status and the repo layout. How it was written: - **Every number checked against its source today.** SkillsBench's abstract has been revised since we first read it: +16.6 points on average (33.9% to 50.5%), ranging from +4.1 to +25.7 across 18 model-harness setups. SWE-Skills-Bench: 92 of 98 skills had no statistically significant effect (76.6% to 76.9%). Scan numbers come from `packages/sigil/src/scan/BENCHMARK.md` (47 benign skills with zero high or critical findings, all 14 malicious samples flagged). Agent counts come from `core/ids.ts` (22 agents plus `generic`). - **Only commands that exist,** with their real flags. Sigil isn't on npm yet, so the quick start runs from a clone and says so. - **Honest status:** all capabilities are `submitted` and `untested`, the first local lab runs hit ceiling effects, and no number is published until a run produced it. The header image is the site's OG image, rendered from the live registry, so it can't drift. - **No named-competitor comparison table.** The claims about other tools can't be re-verified today, so they stay out. Also corrects `spec/METHODOLOGY.md`, which said evidence "are published with GitHub artifact attestations" in the present tense. The CI proving workflow isn't live yet, so it now says CI-produced evidence is attested and local runs are labelled local until then. The catalog line lists what is on main at this commit. When #17 and #19 land, it gains Next.js, Svelte, Vue, Angular and OAuth.
dgesteves
added a commit
that referenced
this pull request
Nov 2, 2026
…es (#21) CodeQL default setup cannot ignore paths. Every proof suite adds deliberately imperfect starting repos, test harnesses and reference solutions that only ever run inside the lab sandbox, and CodeQL flags them. #17 (OAuth) and #19 (web) brought 25 such alerts, all under `tasks/`: HTML-stripping helpers in Next.js test harnesses, prototype-pollution warnings on the toy database of a starting repo, and rate-limit and CSRF heuristics on a demo OIDC login app. This moves CodeQL to an advanced workflow with the same default query suite and the same languages (`actions`, `javascript-typescript`). It runs on PRs, on main and weekly, is SHA-pinned like the rest of CI, and has least-privilege permissions. `.github/codeql/codeql-config.yml` ignores `tasks/`, `site/fixtures/` and `packages/sigil/test/fixtures/`. Capability content keeps its own scanner job ("Scan the catalog"). **Before merging:** default setup has to be switched off, or GitHub rejects the advanced upload. Settings → Code security → CodeQL analysis → Switch to advanced, or: ```sh gh api -X PATCH repos/dgesteves/sigil/code-scanning/default-setup -f state=not-configured ``` Then re-run this PR's checks, merge it, and re-run CodeQL on #17 and #19.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
oauth/oidc-client: secure OIDC sign-in, JWT access-token validation and rotating refresh tokens in Node.js with openid-client 6, jose 6 and oauth4webapi 3. References cover openid-client v6, resource servers and the token lifecycle, with dated sources.Suite
oauth-oidc-client(3 tasks):api-access-tokens,oidc-login,refresh-token-rotation.Each suite passed
node tasks/_tools/check-task.mjs tasks/<suite> --runs 3in Docker today: the hidden verifier fails on the untouched repo and passes on repo + reference solution three times in a row, with the verifier run under--network none.sigil validate catalogandsigil scan catalog --fail-on highpass on the branch (no new findings).Not done yet, on purpose: the calibration probe (a short baseline run in the lab) that checks whether today's models already pass these tasks without the skill. The first lab runs hit ceiling effects on zod-4 and express-5, so every suite gets probed before its first published proof. Until then these capabilities show as
submittedanduntested, which is what they are.