@crvouga/mockingbird-service-oauth 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1384,7 +1384,7 @@ var restoreNamespace = (sqlite, namespace, snapshot) => {
1384
1384
  };
1385
1385
 
1386
1386
  // ../core/dist/version.js
1387
- var PACKAGE_VERSION = true ? "1.0.2" : UNRELEASED_VERSION;
1387
+ var PACKAGE_VERSION = true ? "1.2.0" : UNRELEASED_VERSION;
1388
1388
 
1389
1389
  // ../core/dist/webhooks.js
1390
1390
  var json2 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
@@ -2069,7 +2069,10 @@ var OAUTH_SCENARIOS = {
2069
2069
  rotating_refresh_tokens: { tokens: { refreshRotation: "rotate" } },
2070
2070
  short_lived_tokens: { tokens: { accessTtlSeconds: 5, codeTtlSeconds: 5, refreshTtlSeconds: 30 } },
2071
2071
  consent_denied: { consent: { error: "access_denied" } },
2072
- intermittent_token_failure: { probabilities: { tokenUnavailable: 0.25 } }
2072
+ intermittent_token_failure: { probabilities: { tokenUnavailable: 0.25 } },
2073
+ id_token_clock_ahead: { tokens: { idTokenClockSkewSeconds: 600 } },
2074
+ id_token_stale: { tokens: { idTokenClockSkewSeconds: -7200 } },
2075
+ id_token_unknown_key: { tokens: { idTokenSigningKey: "unpublished" } }
2073
2076
  };
2074
2077
  var object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
2075
2078
  var keys = (value, allowed) => {
@@ -2154,10 +2157,16 @@ function validateBehavior(input) {
2154
2157
  "codeTtlSeconds",
2155
2158
  "refreshTtlSeconds",
2156
2159
  "refreshRotation",
2157
- "refreshError"
2160
+ "refreshError",
2161
+ "idTokenClockSkewSeconds",
2162
+ "idTokenSigningKey"
2158
2163
  ]);
2159
2164
  enumeration(tokens.refreshRotation, ["reuse", "rotate"]);
2160
2165
  enumeration(tokens.refreshError, ["invalid_grant", "invalid_rapt"]);
2166
+ enumeration(tokens.idTokenSigningKey, ["published", "unpublished"]);
2167
+ const skew = tokens.idTokenClockSkewSeconds;
2168
+ if (skew !== void 0 && (typeof skew !== "number" || !Number.isSafeInteger(skew) || Math.abs(skew) > 31536e4))
2169
+ throw new Error("idTokenClockSkewSeconds must be an integer in [-315360000,315360000]");
2161
2170
  for (const key of ["accessTtlSeconds", "codeTtlSeconds", "refreshTtlSeconds"]) {
2162
2171
  const v = tokens[key];
2163
2172
  if (v !== void 0 && (typeof v !== "number" || !Number.isSafeInteger(v) || v < 1 || v > 31536e4))
@@ -2322,9 +2331,9 @@ var style = `
2322
2331
  .theme{border:0;padding:0;display:flex;gap:16px;align-items:center}.theme legend{float:left;margin-right:16px;padding:0}.theme label{display:flex;align-items:center;gap:5px;margin:0;font-weight:400;font-size:12px}.theme input{width:14px;height:14px;min-height:0;margin:0;padding:0;accent-color:var(--accent)}
2323
2332
 
2324
2333
  `;
2325
- function page(title, body, status = 200, action = "'self'", nonce = crypto.randomUUID()) {
2334
+ function page(title, body, status = 200, action = "'self'", nonce = crypto.randomUUID(), testId = "") {
2326
2335
  return new Response(
2327
- `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="light dark"><title>${escapeHtml(title)} \xB7 OAuth Mock</title><style nonce="${nonce}">${style}</style></head><body><a class="skip" href="#main">Skip to content</a><header><span>OAuth Mock</span></header><main id="main"><section class="card" aria-labelledby="title">${body}</section><p class="note">Test accounts only.</p></main><footer><fieldset class="theme"><legend>Appearance</legend><label><input type="radio" name="oauth-theme" value="system" checked>System</label><label><input type="radio" name="oauth-theme" value="light">Light</label><label><input type="radio" name="oauth-theme" value="dark">Dark</label></fieldset></footer><script nonce="${nonce}">(()=>{const root=document.documentElement;const inputs=document.querySelectorAll('input[name="oauth-theme"]');const apply=value=>{root.dataset.theme=value;for(const input of inputs)input.checked=input.value===value;try{sessionStorage.setItem('oauth-mock-theme',value)}catch{}};try{const saved=sessionStorage.getItem('oauth-mock-theme');if(['system','light','dark'].includes(saved))apply(saved)}catch{}for(const input of inputs)input.addEventListener('change',()=>apply(input.value))})()</script></body></html>`,
2336
+ `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="light dark"><title>${escapeHtml(title)} \xB7 OAuth Mock</title><style nonce="${nonce}">${style}</style></head><body><a class="skip" href="#main">Skip to content</a><header><span>OAuth Mock</span></header><main id="main"><section class="card" aria-labelledby="title"${testId ? ` data-testid="${testId}"` : ""}>${body}</section><p class="note">Test accounts only.</p></main><footer><fieldset class="theme"><legend>Appearance</legend><label><input type="radio" name="oauth-theme" value="system" checked>System</label><label><input type="radio" name="oauth-theme" value="light">Light</label><label><input type="radio" name="oauth-theme" value="dark">Dark</label></fieldset></footer><script nonce="${nonce}">(()=>{const root=document.documentElement;const inputs=document.querySelectorAll('input[name="oauth-theme"]');const apply=value=>{root.dataset.theme=value;for(const input of inputs)input.checked=input.value===value;try{sessionStorage.setItem('oauth-mock-theme',value)}catch{}};try{const saved=sessionStorage.getItem('oauth-mock-theme');if(['system','light','dark'].includes(saved))apply(saved)}catch{}for(const input of inputs)input.addEventListener('change',()=>apply(input.value))})()</script></body></html>`,
2328
2337
  {
2329
2338
  status,
2330
2339
  headers: {
@@ -2340,13 +2349,15 @@ function page(title, body, status = 200, action = "'self'", nonce = crypto.rando
2340
2349
  function loginPage(transaction, client, accounts, base, action, signup = false, error = "") {
2341
2350
  const hidden = `<input type="hidden" name="transaction" value="${escapeHtml(transaction)}">`;
2342
2351
  const endpoint = escapeHtml(`${base}/interaction`);
2343
- const intro = `<span class="eyebrow">Sign in</span><h1 id="title">${signup ? "Create your account" : "Choose an account"}</h1><p>${signup ? "Create an account to continue to" : "Choose an account to continue to"}<br><span class="app">${escapeHtml(client)}</span></p>${error ? `<div class="error" role="alert">${escapeHtml(error)}</div>` : ""}`;
2344
- const body = signup ? `<form method="post" action="${endpoint}">${hidden}<input type="hidden" name="action" value="signup"><div class="fields"><label for="name">Full name</label><input id="name" name="name" autocomplete="name" required maxlength="120"><label for="email">Email address</label><input id="email" name="email" type="email" autocomplete="email" required maxlength="254" aria-describedby="email-help"><p id="email-help" class="note">Use a fictional address. No email will be sent.</p></div><button class="primary">Create account &amp; continue</button></form><a class="back" href="${endpoint}?transaction=${escapeHtml(transaction)}">Back to accounts</a>` : `<div class="accounts">${accounts.map((a) => `<form method="post" action="${endpoint}">${hidden}<input type="hidden" name="action" value="select"><button class="account" name="account" value="${escapeHtml(a.id)}"><span class="avatar" aria-hidden="true">${escapeHtml(a.name.slice(0, 1).toUpperCase())}</span><span class="identity"><strong>${escapeHtml(a.name)}</strong><small>${escapeHtml(a.email)}</small></span><span class="arrow" aria-hidden="true">\u2192</span></button></form>`).join("") || '<p class="empty">No accounts yet. Create your first test identity below.</p>'}</div><form method="get" action="${endpoint}">${hidden}<input type="hidden" name="screen" value="signup"><button class="secondary">\uFF0B Create a new account</button></form>`;
2352
+ const intro = `<span class="eyebrow">Sign in</span><h1 id="title">${signup ? "Create your account" : "Choose an account"}</h1><p>${signup ? "Create an account to continue to" : "Choose an account to continue to"}<br><span class="app">${escapeHtml(client)}</span></p>${error ? `<div class="error" role="alert" data-testid="oauth-mock-${signup ? "signup" : "chooser"}-error">${escapeHtml(error)}</div>` : ""}`;
2353
+ const body = signup ? `<form method="post" action="${endpoint}">${hidden}<input type="hidden" name="action" value="signup"><div class="fields"><label for="name">Full name</label><input id="name" name="name" data-testid="oauth-mock-signup-name" autocomplete="name" required maxlength="120"><label for="email">Email address</label><input id="email" name="email" data-testid="oauth-mock-signup-email" type="email" autocomplete="email" required maxlength="254" aria-describedby="email-help"><p id="email-help" class="note">Use a fictional address. No email will be sent.</p></div><button class="primary" data-testid="oauth-mock-signup-submit">Create account &amp; continue</button></form><a class="back" href="${endpoint}?transaction=${escapeHtml(transaction)}">Back to accounts</a>` : `<div class="accounts">${accounts.map((a) => `<form method="post" action="${endpoint}" data-testid="oauth-mock-account" data-account-id="${escapeHtml(a.id)}" data-email="${escapeHtml(a.email)}">${hidden}<input type="hidden" name="action" value="select"><button class="account" data-testid="oauth-mock-account-${escapeHtml(a.id)}" name="account" value="${escapeHtml(a.id)}"><span class="avatar" aria-hidden="true">${escapeHtml(a.name.slice(0, 1).toUpperCase())}</span><span class="identity"><strong>${escapeHtml(a.name)}</strong><small>${escapeHtml(a.email)}</small></span><span class="arrow" aria-hidden="true">\u2192</span></button></form>`).join("") || '<p class="empty">No accounts yet. Create your first test identity below.</p>'}</div><form method="get" action="${endpoint}">${hidden}<input type="hidden" name="screen" value="signup"><button class="secondary" data-testid="oauth-mock-create-account">\uFF0B Create a new account</button></form>`;
2345
2354
  return page(
2346
2355
  signup ? "Create account" : "Choose an account",
2347
- `${intro}${body}<div class="divider"></div><form method="post" action="${endpoint}">${hidden}<button class="secondary" name="action" value="deny">Cancel sign-in</button></form>`,
2356
+ `${intro}${body}<div class="divider"></div><form method="post" action="${endpoint}">${hidden}<button class="secondary" data-testid="oauth-mock-deny" name="action" value="deny">Cancel sign-in</button></form>`,
2348
2357
  error ? 400 : 200,
2349
- action
2358
+ action,
2359
+ void 0,
2360
+ signup ? "oauth-mock-signup" : "oauth-mock-chooser"
2350
2361
  );
2351
2362
  }
2352
2363
  function consentPage(transaction, client, account, scopes2, base, action, privacy) {
@@ -2357,21 +2368,23 @@ function consentPage(transaction, client, account, scopes2, base, action, privac
2357
2368
  name: "View your name",
2358
2369
  offline_access: "Stay connected when you\u2019re away"
2359
2370
  };
2360
- const privacyFields = privacy ? privacy.choice ? `<fieldset class="privacy"><legend>Choose what to share</legend><label><input type="radio" name="email_choice" value="share" ${!privacy.hideEmail ? "checked" : ""}><span>Share my email<small>Your app will receive ${escapeHtml(account.email)}.</small></span></label><label><input type="radio" name="email_choice" value="hide" ${privacy.hideEmail ? "checked" : ""}><span>Hide my email<small>Use a private relay address to keep your email private.</small></span></label></fieldset>` : `<p>${privacy.hideEmail ? "A private relay address will be shared with this app." : "Your email address will be shared with this app."}</p>` : "";
2371
+ const privacyFields = privacy ? privacy.choice ? `<fieldset class="privacy"><legend>Choose what to share</legend><label><input type="radio" name="email_choice" data-testid="oauth-mock-share-email" value="share" ${!privacy.hideEmail ? "checked" : ""}><span>Share my email<small>Your app will receive ${escapeHtml(account.email)}.</small></span></label><label><input type="radio" name="email_choice" data-testid="oauth-mock-hide-email" value="hide" ${privacy.hideEmail ? "checked" : ""}><span>Hide my email<small>Use a private relay address to keep your email private.</small></span></label></fieldset>` : `<p>${privacy.hideEmail ? "A private relay address will be shared with this app." : "Your email address will be shared with this app."}</p>` : "";
2361
2372
  return page(
2362
2373
  "Review access",
2363
2374
  `<span class="eyebrow">Permissions</span><h1 id="title">Review access</h1><p><span class="app">${escapeHtml(client)}</span> would like access to your account.</p><div class="account"><span class="avatar" aria-hidden="true">${escapeHtml(account.name.slice(0, 1))}</span><span class="identity"><strong>${escapeHtml(account.name)}</strong><small>${escapeHtml(account.email)}</small></span></div><ul class="permissions">${scopes2.split(" ").filter(Boolean).map(
2364
2375
  (s) => `<li><span class="check" aria-hidden="true">\u2713</span>${escapeHtml(labels[s] ?? s)}</li>`
2365
2376
  ).join(
2366
2377
  ""
2367
- )}</ul><p>You can cancel now without sharing anything.</p><form method="post" action="${escapeHtml(base)}/interaction"><input type="hidden" name="transaction" value="${escapeHtml(transaction)}">${privacyFields}<button class="primary" name="action" value="allow">Allow &amp; continue</button><button class="secondary" name="action" value="deny">Cancel</button></form>`,
2378
+ )}</ul><p>You can cancel now without sharing anything.</p><form method="post" action="${escapeHtml(base)}/interaction"><input type="hidden" name="transaction" value="${escapeHtml(transaction)}">${privacyFields}<button class="primary" data-testid="oauth-mock-allow" name="action" value="allow">Allow &amp; continue</button><button class="secondary" data-testid="oauth-mock-deny" name="action" value="deny">Cancel</button></form>`,
2368
2379
  200,
2369
- action
2380
+ action,
2381
+ void 0,
2382
+ "oauth-mock-consent"
2370
2383
  );
2371
2384
  }
2372
2385
 
2373
2386
  // src/generated/openapi.ts
2374
- var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Mockingbird OAuth & Social Login","version":"1.0.0"},"paths":{"/.well-known/openid-configuration":{"get":{"operationId":"Discovery","summary":"Discovery","x-mockingbird":{"parity":{"enabled":true,"reason":"Google, Apple and Microsoft discovery metadata is checked against their public live endpoints by scripts/parity.ts."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/jwks":{"get":{"operationId":"Jwks","summary":"Jwks","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/authorize":{"get":{"operationId":"Authorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/interaction":{"get":{"operationId":"InteractionPage","summary":"InteractionPage","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"operationId":"Interact","summary":"Interact","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/token":{"post":{"operationId":"Token","summary":"Token","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/userinfo":{"get":{"operationId":"UserInfo","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"UserInfoPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/revoke":{"post":{"operationId":"Revoke","summary":"Revoke","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/o/oauth2/v2/auth":{"get":{"operationId":"GoogleAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/o/oauth2/auth":{"get":{"operationId":"GoogleLegacyAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/auth/authorize":{"get":{"operationId":"AppleAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/auth/token":{"post":{"operationId":"AppleToken","summary":"Token","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/auth/keys":{"get":{"operationId":"AppleKeys","summary":"Jwks","x-mockingbird":{"parity":{"enabled":true,"reason":"Apple's public signing-key contract is checked by scripts/parity.ts."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/oauth2/v3/certs":{"get":{"operationId":"GoogleKeys","summary":"Jwks","x-mockingbird":{"parity":{"enabled":true,"reason":"Google's public signing-key contract is checked by scripts/parity.ts."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/userinfo":{"get":{"operationId":"GoogleUserInfo","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"GoogleUserInfoPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/oauth2/v3/userinfo":{"get":{"operationId":"GoogleV3UserInfo","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"GoogleV3UserInfoPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/auth/revoke":{"post":{"operationId":"AppleRevoke","summary":"Revoke","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/oauth2/v2.0/authorize":{"get":{"operationId":"MicrosoftAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/oauth2/v2.0/token":{"post":{"operationId":"MicrosoftToken","summary":"Token","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/discovery/v2.0/keys":{"get":{"operationId":"MicrosoftKeys","summary":"Jwks","x-mockingbird":{"parity":{"enabled":true,"reason":"Microsoft's public signing-key contract is checked by scripts/parity.ts."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/oidc/userinfo":{"get":{"operationId":"MicrosoftUserInfo","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"MicrosoftUserInfoPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/login/oauth/authorize":{"get":{"operationId":"GitHubAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/login/oauth/access_token":{"post":{"operationId":"GitHubToken","summary":"Token","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}}}}},"/user":{"get":{"operationId":"GitHubUser","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"GitHubUserPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/user/emails":{"get":{"operationId":"GitHubEmails","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true}}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"GitHubEmailsPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true}}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/":{"get":{"operationId":"Welcome","summary":"InteractionPage","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}}}}`);
2387
+ var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Mockingbird OAuth & Social Login","version":"1.0.0"},"paths":{"/.well-known/openid-configuration":{"get":{"operationId":"Discovery","summary":"Discovery","x-mockingbird":{"parity":{"enabled":true,"reason":"Google, Apple and Microsoft discovery metadata is checked against their public live endpoints by scripts/parity.ts."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/jwks":{"get":{"operationId":"Jwks","summary":"Jwks","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/authorize":{"get":{"operationId":"Authorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/interaction":{"get":{"operationId":"InteractionPage","summary":"InteractionPage","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests. The pages carry the documented oauth-mock-* data-testid hooks (README, \\"Test hooks on the interaction pages\\")."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"operationId":"Interact","summary":"Interact","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests. The pages carry the documented oauth-mock-* data-testid hooks (README, \\"Test hooks on the interaction pages\\")."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/token":{"post":{"operationId":"Token","summary":"Token","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/userinfo":{"get":{"operationId":"UserInfo","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"UserInfoPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/revoke":{"post":{"operationId":"Revoke","summary":"Revoke","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/o/oauth2/v2/auth":{"get":{"operationId":"GoogleAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/o/oauth2/auth":{"get":{"operationId":"GoogleLegacyAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/auth/authorize":{"get":{"operationId":"AppleAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/auth/token":{"post":{"operationId":"AppleToken","summary":"Token","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/auth/keys":{"get":{"operationId":"AppleKeys","summary":"Jwks","x-mockingbird":{"parity":{"enabled":true,"reason":"Apple's public signing-key contract is checked by scripts/parity.ts."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/oauth2/v3/certs":{"get":{"operationId":"GoogleKeys","summary":"Jwks","x-mockingbird":{"parity":{"enabled":true,"reason":"Google's public signing-key contract is checked by scripts/parity.ts."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/userinfo":{"get":{"operationId":"GoogleUserInfo","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"GoogleUserInfoPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/oauth2/v3/userinfo":{"get":{"operationId":"GoogleV3UserInfo","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"GoogleV3UserInfoPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/auth/revoke":{"post":{"operationId":"AppleRevoke","summary":"Revoke","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/oauth2/v2.0/authorize":{"get":{"operationId":"MicrosoftAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/oauth2/v2.0/token":{"post":{"operationId":"MicrosoftToken","summary":"Token","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/discovery/v2.0/keys":{"get":{"operationId":"MicrosoftKeys","summary":"Jwks","x-mockingbird":{"parity":{"enabled":true,"reason":"Microsoft's public signing-key contract is checked by scripts/parity.ts."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/oidc/userinfo":{"get":{"operationId":"MicrosoftUserInfo","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"MicrosoftUserInfoPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/login/oauth/authorize":{"get":{"operationId":"GitHubAuthorize","summary":"Authorize","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/login/oauth/access_token":{"post":{"operationId":"GitHubToken","summary":"Token","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}},"application/x-www-form-urlencoded":{"schema":{"type":"string"}}}}}}},"/user":{"get":{"operationId":"GitHubUser","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"GitHubUserPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/user/emails":{"get":{"operationId":"GitHubEmails","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true}}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"operationId":"GitHubEmailsPost","summary":"UserInfo","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true}}}}},"302":{"description":"302","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"400","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"401","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"403","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"404","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"503":{"description":"503","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/":{"get":{"operationId":"Welcome","summary":"InteractionPage","x-mockingbird":{"parity":{"enabled":false,"reason":"Interactive OAuth transactions are exercised by protocol and independent JOSE tests."}},"responses":{"200":{"description":"200","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"302","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"400","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"401","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"403","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"404","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"503","content":{"text/html":{"schema":{"type":"string"}}}}}}}}}`);
2375
2388
  var operationIds = ["Discovery", "Jwks", "Authorize", "InteractionPage", "Interact", "Token", "UserInfo", "UserInfoPost", "Revoke", "GoogleAuthorize", "GoogleLegacyAuthorize", "AppleAuthorize", "AppleToken", "AppleKeys", "GoogleKeys", "GoogleUserInfo", "GoogleUserInfoPost", "GoogleV3UserInfo", "GoogleV3UserInfoPost", "AppleRevoke", "MicrosoftAuthorize", "MicrosoftToken", "MicrosoftKeys", "MicrosoftUserInfo", "MicrosoftUserInfoPost", "GitHubAuthorize", "GitHubToken", "GitHubUser", "GitHubUserPost", "GitHubEmails", "GitHubEmailsPost", "Welcome"];
2376
2389
  var supportedOperationIds = ["Discovery", "Jwks", "Authorize", "InteractionPage", "Interact", "Token", "UserInfo", "UserInfoPost", "Revoke", "GoogleAuthorize", "GoogleLegacyAuthorize", "AppleAuthorize", "AppleToken", "AppleKeys", "GoogleKeys", "GoogleUserInfo", "GoogleUserInfoPost", "GoogleV3UserInfo", "GoogleV3UserInfoPost", "AppleRevoke", "MicrosoftAuthorize", "MicrosoftToken", "MicrosoftKeys", "MicrosoftUserInfo", "MicrosoftUserInfoPost", "GitHubAuthorize", "GitHubToken", "GitHubUser", "GitHubUserPost", "GitHubEmails", "GitHubEmailsPost", "Welcome"];
2377
2390
 
@@ -2420,12 +2433,44 @@ function createRuntime2(options = {}) {
2420
2433
  return Response.json({ error: String(error) }, { status: 400 });
2421
2434
  }
2422
2435
  },
2423
- "POST /consents/revoke": ({ namespace, body }) => {
2436
+ "POST /consents/revoke": async ({ namespace, body, request }) => {
2424
2437
  const input = body;
2425
2438
  if (!input || typeof input.clientId !== "string" || typeof input.accountId !== "string")
2426
2439
  return Response.json({ error: "clientId and accountId are required" }, { status: 400 });
2427
- runtime.instance(namespace).revokeConsent(input.clientId, input.accountId);
2428
- return Response.json({ revoked: true });
2440
+ const api = runtime.instance(namespace);
2441
+ const notifications = await api.revokeConsent(
2442
+ input.clientId,
2443
+ input.accountId,
2444
+ api.issuer(request)
2445
+ );
2446
+ return Response.json({ revoked: true, notifications });
2447
+ },
2448
+ "POST /accounts/delete": async ({ namespace, body, request }) => {
2449
+ const input = body;
2450
+ if (!input || typeof input.accountId !== "string")
2451
+ return Response.json({ error: "accountId is required" }, { status: 400 });
2452
+ const api = runtime.instance(namespace);
2453
+ if (!api.accounts.has(input.accountId))
2454
+ return Response.json({ error: "Unknown account" }, { status: 404 });
2455
+ const notifications = await api.deleteAccount(input.accountId, api.issuer(request));
2456
+ return Response.json({ deleted: true, notifications });
2457
+ },
2458
+ "POST /relay-forwarding": async ({ namespace, body, request }) => {
2459
+ const input = body;
2460
+ if (!input || typeof input.accountId !== "string" || typeof input.forwarding !== "boolean")
2461
+ return Response.json(
2462
+ { error: "accountId and boolean forwarding are required" },
2463
+ { status: 400 }
2464
+ );
2465
+ const api = runtime.instance(namespace);
2466
+ if (!api.accounts.has(input.accountId))
2467
+ return Response.json({ error: "Unknown account" }, { status: 404 });
2468
+ const notifications = await api.setRelayForwarding(
2469
+ input.accountId,
2470
+ input.forwarding,
2471
+ api.issuer(request)
2472
+ );
2473
+ return Response.json({ forwarding: input.forwarding, notifications });
2429
2474
  },
2430
2475
  "POST /keys/rotate": ({ namespace, body }) => {
2431
2476
  const input = body;
@@ -2637,6 +2682,8 @@ var OAuthAPI = class {
2637
2682
  consents;
2638
2683
  signer = new Signer();
2639
2684
  previousSigners = [];
2685
+ /** Never published: signs ID tokens for `tokens.idTokenSigningKey: "unpublished"`. */
2686
+ unpublishedSigner;
2640
2687
  behavior;
2641
2688
  identities;
2642
2689
  appleDisclosures;
@@ -2680,6 +2727,10 @@ var OAuthAPI = class {
2680
2727
  throw new Error(
2681
2728
  "Apple client requires teamId, keyId and a public P-256 JWK, without a static secret"
2682
2729
  );
2730
+ if (client.apple?.notificationUrl !== void 0 && !(typeof client.apple.notificationUrl === "string" && safeRedirect(client.apple.notificationUrl) && WEB_SCHEMES.has(new URL(client.apple.notificationUrl).protocol)))
2731
+ throw new Error(
2732
+ "apple.notificationUrl must be an HTTP(S) URL without fragment or credentials"
2733
+ );
2683
2734
  this.clients.insert(client.id, structuredClone(client));
2684
2735
  return client;
2685
2736
  }
@@ -2700,7 +2751,95 @@ var OAuthAPI = class {
2700
2751
  this.signer = new Signer();
2701
2752
  return { kid: this.signer.kid };
2702
2753
  }
2703
- revokeConsent(clientId, accountId) {
2754
+ /**
2755
+ * Withdraws one client's consent. When the client registered `apple.notificationUrl` and held
2756
+ * the consent, Apple's `consent-revoked` notification is delivered before this resolves.
2757
+ */
2758
+ async revokeConsent(clientId, accountId, issuer) {
2759
+ const targets = this.notifiable(accountId).filter((t) => t.client.id === clientId);
2760
+ this.forget(clientId, accountId);
2761
+ return this.notify(targets, "consent-revoked", issuer);
2762
+ }
2763
+ /**
2764
+ * Deletes an account with its grants and sessions, after telling every client that held a
2765
+ * consent (`account-delete`).
2766
+ */
2767
+ async deleteAccount(accountId, issuer) {
2768
+ if (!this.accounts.has(accountId)) throw new Error("Unknown account");
2769
+ const targets = this.notifiable(accountId);
2770
+ for (const { value: client } of this.clients.list()) this.forget(client.id, accountId);
2771
+ for (const row of this.codes.list({ where: (c) => c.accountId === accountId }))
2772
+ this.codes.delete(row.id);
2773
+ for (const row of this.tokens.list({ where: (t) => t.accountId === accountId }))
2774
+ this.tokens.delete(row.id);
2775
+ for (const row of this.transactions.list({ where: (t) => t.accountId === accountId }))
2776
+ this.transactions.delete(row.id);
2777
+ for (const row of this.sessions.list({ where: (s) => s.accountId === accountId }))
2778
+ this.sessions.delete(row.id);
2779
+ this.accounts.delete(accountId);
2780
+ return this.notify(targets, "account-delete", issuer);
2781
+ }
2782
+ /**
2783
+ * Tells every client whose identity for the account is a private relay address that the user
2784
+ * turned forwarding off (`email-disabled`) or on (`email-enabled`).
2785
+ */
2786
+ async setRelayForwarding(accountId, forwarding, issuer) {
2787
+ if (!this.accounts.has(accountId)) throw new Error("Unknown account");
2788
+ return this.notify(
2789
+ this.notifiable(accountId).filter((t) => t.relay),
2790
+ forwarding ? "email-enabled" : "email-disabled",
2791
+ issuer,
2792
+ true
2793
+ );
2794
+ }
2795
+ /** Clients with a notification endpoint that hold a consent for the account. */
2796
+ notifiable(accountId) {
2797
+ return this.clients.list({ order: "oldest" }).flatMap(({ value: client }) => {
2798
+ const identity = this.identities.get(this.identityKey(client, accountId));
2799
+ return client.apple?.notificationUrl && identity && this.consents.has(JSON.stringify([client.id, accountId])) ? [{ client, sub: identity.sub, email: identity.email, relay: identity.privateEmail }] : [];
2800
+ });
2801
+ }
2802
+ async notify(targets, type, issuer, withEmail = false) {
2803
+ if (!targets.length) return [];
2804
+ const iss = issuer ?? this.options.issuer;
2805
+ if (!iss) throw new Error("A notification needs the issuer option or an issuer argument");
2806
+ const send = this.options.webhooks?.fetch ?? ((r) => fetch(r));
2807
+ const deliveries = [];
2808
+ for (const { client, sub, email } of targets) {
2809
+ const payload = await this.signer.sign({
2810
+ iss: iss.replace(/\/$/, ""),
2811
+ aud: client.id,
2812
+ iat: Math.floor(this.now() / 1e3),
2813
+ jti: random(),
2814
+ events: JSON.stringify({
2815
+ type,
2816
+ sub,
2817
+ ...withEmail ? { email, is_private_email: "true" } : {},
2818
+ event_time: this.now()
2819
+ })
2820
+ });
2821
+ try {
2822
+ const response = await send(
2823
+ new Request(client.apple?.notificationUrl ?? "", {
2824
+ method: "POST",
2825
+ headers: { "content-type": "application/json" },
2826
+ body: JSON.stringify({ payload })
2827
+ })
2828
+ );
2829
+ deliveries.push({
2830
+ clientId: client.id,
2831
+ type,
2832
+ delivered: response.ok,
2833
+ status: response.status
2834
+ });
2835
+ } catch (error) {
2836
+ deliveries.push({ clientId: client.id, type, delivered: false, error: String(error) });
2837
+ }
2838
+ }
2839
+ return deliveries;
2840
+ }
2841
+ /** Drops one client's grant for an account: consent, identity, first-use disclosure, codes, tokens. */
2842
+ forget(clientId, accountId) {
2704
2843
  const key = JSON.stringify([clientId, accountId]);
2705
2844
  this.consents.delete(key);
2706
2845
  this.refreshIssued.delete(key);
@@ -2843,13 +2982,18 @@ var OAuthAPI = class {
2843
2982
  async idToken(grant, issuer, extra = {}) {
2844
2983
  const account = this.accounts.get(grant.accountId);
2845
2984
  if (!account) throw new Error("Account no longer exists");
2846
- return this.signer.sign({
2985
+ const tokens = this.behavior.config.tokens;
2986
+ const skew = tokens?.idTokenClockSkewSeconds ?? 0;
2987
+ const iat = Math.floor(this.now() / 1e3) + skew;
2988
+ const unpublished = tokens?.idTokenSigningKey === "unpublished";
2989
+ if (unpublished) this.unpublishedSigner ??= new Signer();
2990
+ return (unpublished && this.unpublishedSigner || this.signer).sign({
2847
2991
  ...this.profile(account, grant),
2848
2992
  iss: issuer,
2849
2993
  aud: grant.clientId,
2850
- iat: Math.floor(this.now() / 1e3),
2851
- exp: Math.floor(this.now() / 1e3) + (this.behavior.config.tokens?.accessTtlSeconds ?? 3600),
2852
- auth_time: Math.floor(grant.authTime / 1e3),
2994
+ iat,
2995
+ exp: iat + (tokens?.accessTtlSeconds ?? 3600),
2996
+ auth_time: Math.floor(grant.authTime / 1e3) + skew,
2853
2997
  ...grant.nonce ? { nonce: grant.nonce } : {},
2854
2998
  ...extra
2855
2999
  });
@@ -3187,7 +3331,10 @@ var OAuthAPI = class {
3187
3331
  return page(
3188
3332
  "Session expired",
3189
3333
  '<span class="eyebrow">Let\u2019s try again</span><h1 id="title">This sign-in expired</h1><p>Return to your application and start sign-in again.</p>',
3190
- 400
3334
+ 400,
3335
+ void 0,
3336
+ void 0,
3337
+ "oauth-mock-error"
3191
3338
  );
3192
3339
  const accounts = () => this.accounts.list({ order: "oldest", where: (a) => !a.disabled }).map((a) => a.value);
3193
3340
  if (request.method === "GET")
@@ -3249,6 +3396,16 @@ var OAuthAPI = class {
3249
3396
  );
3250
3397
  auth.accountId = account.id;
3251
3398
  auth.authTime = this.now();
3399
+ const granted = this.consents.get(JSON.stringify([client.id, account.id]));
3400
+ if (this.provider === "google" && !auth.forceConsent && granted && [...scopes(auth.scope)].every((s) => scopes(granted.scope).has(s))) {
3401
+ this.transactions.delete(id);
3402
+ return this.startSession(
3403
+ await this.finish(auth, issuer),
3404
+ account.id,
3405
+ auth.authTime,
3406
+ issuer
3407
+ );
3408
+ }
3252
3409
  this.transactions.update(id, auth);
3253
3410
  return this.consent(id, client, account, auth, issuer);
3254
3411
  }
@@ -3261,21 +3418,24 @@ var OAuthAPI = class {
3261
3418
  if (choice && this.provider === "apple" && (this.behavior.config.apple?.emailMode ?? "choose") === "choose" && !this.identities.has(this.identityKey(client, account.id)))
3262
3419
  auth.emailChoice = choice;
3263
3420
  this.transactions.delete(id);
3264
- const result = await this.finish(auth, issuer);
3265
- const session = random();
3266
- this.sessions.insert(session, {
3267
- accountId: account.id,
3268
- authTime: auth.authTime,
3269
- expires: this.now() + 864e5
3270
- });
3271
- result.headers.append(
3272
- this.options.cookieHeaders?.response ?? "set-cookie",
3273
- `mb_session=${session}; Path=${new URL(issuer).pathname.replace(/\/$/, "") || "/"}; HttpOnly; SameSite=Lax; Max-Age=86400${issuer.startsWith("https:") ? "; Secure" : ""}`
3421
+ return this.startSession(
3422
+ await this.finish(auth, issuer),
3423
+ account.id,
3424
+ auth.authTime,
3425
+ issuer
3274
3426
  );
3275
- return result;
3276
3427
  }
3277
3428
  return fail("invalid_request", "Invalid interaction action");
3278
3429
  }
3430
+ startSession(result, accountId, authTime, issuer) {
3431
+ const session = random();
3432
+ this.sessions.insert(session, { accountId, authTime, expires: this.now() + 864e5 });
3433
+ result.headers.append(
3434
+ this.options.cookieHeaders?.response ?? "set-cookie",
3435
+ `mb_session=${session}; Path=${new URL(issuer).pathname.replace(/\/$/, "") || "/"}; HttpOnly; SameSite=Lax; Max-Age=86400${issuer.startsWith("https:") ? "; Secure" : ""}`
3436
+ );
3437
+ return result;
3438
+ }
3279
3439
  async finish(auth, issuer) {
3280
3440
  const behavior = this.behavior.config;
3281
3441
  if (auth.decisions.denyConsent || behavior.consent?.error)
@@ -3348,10 +3508,11 @@ var OAuthAPI = class {
3348
3508
  values
3349
3509
  ).map(([k, v]) => `<input type="hidden" name="${escapeHtml(k)}" value="${escapeHtml(v)}">`).join(
3350
3510
  ""
3351
- )}<button class="primary">Continue</button></form><script nonce="${nonce}">document.getElementById('callback').submit()</script>`,
3511
+ )}<button class="primary" data-testid="oauth-mock-form-post-continue">Continue</button></form><script nonce="${nonce}">document.getElementById('callback').submit()</script>`,
3352
3512
  200,
3353
3513
  formTarget(auth.redirectUri),
3354
- nonce
3514
+ nonce,
3515
+ "oauth-mock-form-post"
3355
3516
  );
3356
3517
  return result;
3357
3518
  }
@@ -3574,4 +3735,4 @@ export {
3574
3735
  createMultiRuntime,
3575
3736
  OAuthAPI
3576
3737
  };
3577
- //# sourceMappingURL=chunk-I24F66FL.js.map
3738
+ //# sourceMappingURL=chunk-XUUSJ73W.js.map