Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/oauth/kiro-device-login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,10 @@ async function poll(flow: Flow, principal: KiroDevicePrincipal): Promise<KiroDev
if (data.status === "authorization_pending") return publicView(flow);
if (data.status === "expired_token" || data.status === "expired") return terminal(flow, "expired");
}
// The social poll reports a completed grant as status "authorized"; "approved" is kept as an alias.
if (result.status !== 200 || Object.hasOwn(data, "error") || error || (builder && (data.status !== undefined || !positive(data.expiresIn) || !isStorableKiroClientPart(flow.clientId)
|| !isStorableKiroClientPart(flow.clientSecret)))
|| (!builder && (data.status !== undefined && data.status !== "approved"))) return terminal(flow, "failed");
|| (!builder && (data.status !== undefined && data.status !== "authorized" && data.status !== "approved"))) return terminal(flow, "failed");
if (!code(data.accessToken) || !code(data.refreshToken)
|| (!builder && (typeof data.profileArn !== "string" || !PROFILE_ARN.test(data.profileArn)))) return terminal(flow, "failed");
const credential: OAuthCredentials = {
Expand Down
21 changes: 21 additions & 0 deletions tests/providers/kiro/kiro-device-social.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ describe("Kiro social device grant", () => {
expect(JSON.stringify(done)).not.toMatch(/private-access|private-refresh|private-device/);
});

test("accepts the authorized status the social poll returns after a pending reply", async () => {
fixture = kiroDeviceFixture();
const polls = [
{ status: "authorization_pending", accessToken: null, refreshToken: null, profileArn: null },
{ status: "authorized", identityProvider: "Google", accessToken: "private-access", refreshToken: "private-refresh", profileArn },
];
fixture.setPost(async url => url.endsWith("/authorization") ? response(socialAuthorization) : response(polls.shift()!));
const start = await startKiroDeviceLogin("google", "admin-token");
fixture.advance(5_000);
expect((await statusKiroDeviceLogin(start.flowId, "admin-token"))?.state).toBe("pending");
expect(getAccountSet("kiro")).toBeNull();
fixture.advance(5_000);
const done = await statusKiroDeviceLogin(start.flowId, "admin-token");
expect(done?.state).toBe("done");
expect(polls).toHaveLength(0);
const account = getAccountSet("kiro")?.accounts[0];
expect(account?.credential.refresh).toBe("private-refresh");
expect(account?.credential.kiro?.profileArn).toBe(profileArn);
expect(JSON.stringify(done)).not.toMatch(/private-access|private-refresh|private-device/);
});

test("unknown approval status or malformed profile persists nothing", async () => {
for (const approval of [
{ status: "strange", accessToken: "a", refreshToken: "r", profileArn },
Expand Down
Loading