mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 20:00:47 +00:00
Feat Kiro OAuth, Fix Codex
This commit is contained in:
@@ -113,6 +113,24 @@ export const GITHUB_CONFIG = {
|
||||
editorPluginVersion: "copilot-chat/0.26.7",
|
||||
};
|
||||
|
||||
// Kiro OAuth Configuration (AWS SSO OIDC Device Code Flow)
|
||||
export const KIRO_CONFIG = {
|
||||
// AWS SSO OIDC endpoints for Builder ID
|
||||
ssoOidcEndpoint: "https://oidc.us-east-1.amazonaws.com",
|
||||
registerClientUrl: "https://oidc.us-east-1.amazonaws.com/client/register",
|
||||
deviceAuthUrl: "https://oidc.us-east-1.amazonaws.com/device_authorization",
|
||||
tokenUrl: "https://oidc.us-east-1.amazonaws.com/token",
|
||||
refreshTokenUrl: "https://prod.us-east-1.auth.desktop.kiro.dev/refreshToken",
|
||||
// AWS Builder ID start URL
|
||||
startUrl: "https://view.awsapps.com/start",
|
||||
// Client registration params
|
||||
clientName: "kiro-cli",
|
||||
clientType: "public",
|
||||
scopes: ["codewhisperer:completions", "codewhisperer:analysis", "codewhisperer:conversations"],
|
||||
grantTypes: ["urn:ietf:params:oauth:grant-type:device_code", "refresh_token"],
|
||||
issuerUrl: "https://identitycenter.amazonaws.com/ssoins-722374e8c3c8e6c6",
|
||||
};
|
||||
|
||||
// OAuth timeout (5 minutes)
|
||||
export const OAUTH_TIMEOUT = 300000;
|
||||
|
||||
@@ -126,4 +144,5 @@ export const PROVIDERS = {
|
||||
ANTIGRAVITY: "antigravity",
|
||||
OPENAI: "openai",
|
||||
GITHUB: "github",
|
||||
KIRO: "kiro",
|
||||
};
|
||||
|
||||
+126
-2
@@ -12,6 +12,7 @@ import {
|
||||
IFLOW_CONFIG,
|
||||
ANTIGRAVITY_CONFIG,
|
||||
GITHUB_CONFIG,
|
||||
KIRO_CONFIG,
|
||||
} from "./constants/oauth";
|
||||
|
||||
// Provider configurations
|
||||
@@ -536,6 +537,125 @@ const PROVIDERS = {
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
kiro: {
|
||||
config: KIRO_CONFIG,
|
||||
flowType: "device_code",
|
||||
// Kiro uses AWS SSO OIDC - requires client registration first
|
||||
requestDeviceCode: async (config) => {
|
||||
// Step 1: Register client with AWS SSO OIDC
|
||||
const registerRes = await fetch(config.registerClientUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
clientName: config.clientName,
|
||||
clientType: config.clientType,
|
||||
scopes: config.scopes,
|
||||
grantTypes: config.grantTypes,
|
||||
issuerUrl: config.issuerUrl,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!registerRes.ok) {
|
||||
const error = await registerRes.text();
|
||||
throw new Error(`Client registration failed: ${error}`);
|
||||
}
|
||||
|
||||
const clientInfo = await registerRes.json();
|
||||
|
||||
// Step 2: Request device authorization
|
||||
const deviceRes = await fetch(config.deviceAuthUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
clientId: clientInfo.clientId,
|
||||
clientSecret: clientInfo.clientSecret,
|
||||
startUrl: config.startUrl,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!deviceRes.ok) {
|
||||
const error = await deviceRes.text();
|
||||
throw new Error(`Device authorization failed: ${error}`);
|
||||
}
|
||||
|
||||
const deviceData = await deviceRes.json();
|
||||
|
||||
// Return combined data for polling
|
||||
return {
|
||||
device_code: deviceData.deviceCode,
|
||||
user_code: deviceData.userCode,
|
||||
verification_uri: deviceData.verificationUri,
|
||||
verification_uri_complete: deviceData.verificationUriComplete,
|
||||
expires_in: deviceData.expiresIn,
|
||||
interval: deviceData.interval || 5,
|
||||
// Store client credentials for token exchange
|
||||
_clientId: clientInfo.clientId,
|
||||
_clientSecret: clientInfo.clientSecret,
|
||||
};
|
||||
},
|
||||
pollToken: async (config, deviceCode, codeVerifier, extraData) => {
|
||||
const response = await fetch(config.tokenUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
clientId: extraData?._clientId,
|
||||
clientSecret: extraData?._clientSecret,
|
||||
deviceCode: deviceCode,
|
||||
grantType: "urn:ietf:params:oauth:grant-type:device_code",
|
||||
}),
|
||||
});
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch (e) {
|
||||
const text = await response.text();
|
||||
data = { error: "invalid_response", error_description: text };
|
||||
}
|
||||
|
||||
// AWS SSO OIDC returns camelCase
|
||||
if (data.accessToken) {
|
||||
return {
|
||||
ok: true,
|
||||
data: {
|
||||
access_token: data.accessToken,
|
||||
refresh_token: data.refreshToken,
|
||||
expires_in: data.expiresIn,
|
||||
// Store client credentials for refresh
|
||||
_clientId: extraData?._clientId,
|
||||
_clientSecret: extraData?._clientSecret,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
data: {
|
||||
error: data.error || "authorization_pending",
|
||||
error_description: data.error_description || data.message,
|
||||
},
|
||||
};
|
||||
},
|
||||
mapTokens: (tokens) => ({
|
||||
accessToken: tokens.access_token,
|
||||
refreshToken: tokens.refresh_token,
|
||||
expiresIn: tokens.expires_in,
|
||||
providerSpecificData: {
|
||||
clientId: tokens._clientId,
|
||||
clientSecret: tokens._clientSecret,
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -614,14 +734,18 @@ export async function requestDeviceCode(providerName, codeChallenge) {
|
||||
|
||||
/**
|
||||
* Poll for token (for device_code flow)
|
||||
* @param {string} providerName - Provider name
|
||||
* @param {string} deviceCode - Device code from requestDeviceCode
|
||||
* @param {string} codeVerifier - PKCE code verifier (optional for some providers)
|
||||
* @param {object} extraData - Extra data from device code response (e.g. clientId/clientSecret for Kiro)
|
||||
*/
|
||||
export async function pollForToken(providerName, deviceCode, codeVerifier) {
|
||||
export async function pollForToken(providerName, deviceCode, codeVerifier, extraData) {
|
||||
const provider = getProvider(providerName);
|
||||
if (provider.flowType !== "device_code") {
|
||||
throw new Error(`Provider ${providerName} does not support device code flow`);
|
||||
}
|
||||
|
||||
const result = await provider.pollToken(provider.config, deviceCode, codeVerifier);
|
||||
const result = await provider.pollToken(provider.config, deviceCode, codeVerifier, extraData);
|
||||
|
||||
if (result.ok) {
|
||||
// For device code flows, success is only when we have an access token
|
||||
|
||||
Reference in New Issue
Block a user