Files
openchamber/packages/web/server/lib/github/device-flow.js
T
bot-hermes 0fc857959e feat(web,ui): per-provider git forge API base URL and detection URLs
Configure a default API base URL per git provider (github/gitlab/gitea)
in settings.json gitProviders, with GitHub Enterprise support (Octokit
baseUrl + device-flow web origin derived from the API base), and replace
the client-side custom-domains list with server-persisted detection URL
chips (SSH/HTTPS forms normalized to hosts). The configured API base host
auto-counts as a detection host. Settings round-trip through the existing
/api/config/settings sanitizer; the UI store hydrates from server settings
with a one-time localStorage migration.
2026-08-17 09:55:11 +00:00

53 lines
1.7 KiB
JavaScript

const DEVICE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
const DEFAULT_WEB_ORIGIN = 'https://github.com';
const encodeForm = (params) => {
const body = new URLSearchParams();
for (const [key, value] of Object.entries(params)) {
if (value == null) continue;
body.set(key, String(value));
}
return body.toString();
};
async function postForm(url, params) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
body: encodeForm(params),
});
const payload = await response.json().catch(() => null);
if (!response.ok) {
const message = payload?.error_description || payload?.error || response.statusText;
const error = new Error(message || 'GitHub request failed');
error.status = response.status;
error.payload = payload;
throw error;
}
return payload;
}
const deviceCodeUrl = (webOrigin) => `${(webOrigin || DEFAULT_WEB_ORIGIN).replace(/\/+$/, '')}/login/device/code`;
const accessTokenUrl = (webOrigin) => `${(webOrigin || DEFAULT_WEB_ORIGIN).replace(/\/+$/, '')}/login/oauth/access_token`;
export async function startDeviceFlow({ clientId, scope, webOrigin }) {
return postForm(deviceCodeUrl(webOrigin), {
client_id: clientId,
scope,
});
}
export async function exchangeDeviceCode({ clientId, deviceCode, webOrigin }) {
// GitHub returns 200 with {error: 'authorization_pending'|...} for non-success states.
const payload = await postForm(accessTokenUrl(webOrigin), {
client_id: clientId,
device_code: deviceCode,
grant_type: DEVICE_GRANT_TYPE,
});
return payload;
}