@magpie-community/opencode-kiro-auth 0.1.12 → 0.1.14
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.
- package/README.md +15 -2
- package/index.mjs +44 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,8 +17,21 @@ Power) and makes its requests, in OpenCode and in magpie. Provider id:
|
|
|
17
17
|
- Builder ID and Identity Center come back with an AWS sign-in URL and
|
|
18
18
|
region. The plugin registers a client with AWS's OIDC service, sends
|
|
19
19
|
the browser to AWS, and trades the code AWS returns.
|
|
20
|
-
- The account is named by the email Kiro has for it (`Get-Usage-Limits`)
|
|
21
|
-
and
|
|
20
|
+
- The account is named by the email Kiro has for it (`Get-Usage-Limits`)
|
|
21
|
+
and the way it was signed in to: `email (Google)`, `email (GitHub)`,
|
|
22
|
+
`email (Builder ID)`, `email (IAM Identity Center)`, `email
|
|
23
|
+
(Internal)`. Its plan is read from the same call, and its usage names
|
|
24
|
+
it the same way.
|
|
25
|
+
- Kiro keeps a Google, a GitHub, a Builder ID and an Identity Center
|
|
26
|
+
sign-in of one email as different accounts. The sign-in carries a `uid`
|
|
27
|
+
(`google`, `github`, `builderid`, `enterprise`), so magpie keeps each one
|
|
28
|
+
beside the others, and a sign-in again the same way replaces only its
|
|
29
|
+
own account. An account signed in before 0.1.13 is given its `uid`,
|
|
30
|
+
and one named by its bare email before 0.1.14 its name, on its first
|
|
31
|
+
request, model listing, usage read or renewal. Until then, a sign-in
|
|
32
|
+
again the same way goes beside it rather than replacing it: magpie
|
|
33
|
+
matches a sign-in by its name, and the plugin can't see the other
|
|
34
|
+
accounts to match it by `uid`.
|
|
22
35
|
- A company's own identity provider (`external_idp`) is not supported
|
|
23
36
|
here. Sign in with `kiro-cli login` and use the next method instead.
|
|
24
37
|
- **Kiro CLI's or Kiro IDE's sign-in**: uses the account kiro-cli is signed
|
package/index.mjs
CHANGED
|
@@ -50,6 +50,21 @@ const SIGN_IN_TIMEOUT = 10 * 60 * 1000
|
|
|
50
50
|
const BUILDER_ID_START = "https://view.awsapps.com/start"
|
|
51
51
|
const BUILDER_ID_PROFILE = "arn:aws:codewhisperer:us-east-1:638616132270:profile/AAAACCCCXXXX"
|
|
52
52
|
const isBuilderID = (provider) => String(provider ?? "").toLowerCase() === "builderid"
|
|
53
|
+
// uidOf is what tells two Kiro accounts of one email apart: the way it was
|
|
54
|
+
// signed in to. Kiro keeps a Google and a GitHub sign-in of one email (or
|
|
55
|
+
// Builder ID's, or Identity Center's) as different accounts, and magpie
|
|
56
|
+
// keeps a sign-in of an email already signed in as that account unless
|
|
57
|
+
// both have a uid and the two differ (#309). "" when it isn't known.
|
|
58
|
+
const uidOf = (loginProvider) => String(loginProvider ?? "").trim().toLowerCase()
|
|
59
|
+
// nameOf is what an account is called: its email and the way it was signed
|
|
60
|
+
// in to, as Kiro's sign-in page names it, so two accounts of one email read
|
|
61
|
+
// apart (#309; magpie names a plugin's account by its accountId). A way not
|
|
62
|
+
// known, kiro-cli's or the Kiro IDE's sign-in among them, leaves the email
|
|
63
|
+
// as it is, and magpie tells two such apart by their slot.
|
|
64
|
+
const WAYS = { google: "Google", github: "GitHub", builderid: "Builder ID", enterprise: "IAM Identity Center", internal: "Internal" }
|
|
65
|
+
const nameOf = (email, loginProvider) => (WAYS[uidOf(loginProvider)] ? `${email} (${WAYS[uidOf(loginProvider)]})` : email)
|
|
66
|
+
// a bare email: what an account was called before nameOf
|
|
67
|
+
const bareEmail = (id) => typeof id === "string" && /^[^\s()]+@[^\s()]+$/.test(id)
|
|
53
68
|
|
|
54
69
|
const env = (k) => process.env[k] ?? ""
|
|
55
70
|
const home = () => env("HOME") || homedir()
|
|
@@ -438,11 +453,21 @@ function account(client) {
|
|
|
438
453
|
lock = run.catch(() => {})
|
|
439
454
|
return run
|
|
440
455
|
}
|
|
456
|
+
// an account signed in here before its uid was kept is given it, so a
|
|
457
|
+
// sign-in with another way to the same email no longer replaces it
|
|
458
|
+
const uidSaved = new Set()
|
|
459
|
+
const uidField = (auth) => (!auth?.uid && uidOf(auth?.loginProvider) ? { uid: uidOf(auth.loginProvider) } : {})
|
|
460
|
+
// and one called by its bare email is given its name, the way it was
|
|
461
|
+
// signed in to with it
|
|
462
|
+
const nameField = (auth) =>
|
|
463
|
+
bareEmail(auth?.accountId) && nameOf(auth.accountId, auth.loginProvider) !== auth.accountId
|
|
464
|
+
? { accountId: nameOf(auth.accountId, auth.loginProvider) } : {}
|
|
465
|
+
const idFields = (auth) => ({ ...uidField(auth), ...nameField(auth) })
|
|
441
466
|
const save = async (auth, c) => {
|
|
442
467
|
if (!c.own || !client?.auth?.set) return
|
|
443
468
|
const { type: _t, ...rest } = auth
|
|
444
469
|
await client.auth.set({ path: { id: ID }, body: { ...rest, type: "oauth", access: c.access, refresh: c.refresh, expires: c.expires,
|
|
445
|
-
profileArn: c.profile, region: c.region } }).catch(() => {})
|
|
470
|
+
profileArn: c.profile, region: c.region, ...idFields(auth) } }).catch(() => {})
|
|
446
471
|
}
|
|
447
472
|
const creds = (auth, stale = false) =>
|
|
448
473
|
locked(async () => {
|
|
@@ -460,6 +485,7 @@ function account(client) {
|
|
|
460
485
|
}
|
|
461
486
|
read = newest(read)
|
|
462
487
|
let c = held?.id === id ? held.c : null
|
|
488
|
+
let saved = false
|
|
463
489
|
if (!c || read.access !== c.access || !fresh(c) || stale) {
|
|
464
490
|
if (!read.profile && c && read.access === c.access) read.profile = c.profile
|
|
465
491
|
if (stale && c && read.access !== c.access && fresh(read)) stale = false // the owner refreshed it already
|
|
@@ -468,11 +494,20 @@ function account(client) {
|
|
|
468
494
|
if (c.method === "apikey") throw gone("Kiro turned down the API key saved on the provider")
|
|
469
495
|
c = renewed(c, await refresh(c))
|
|
470
496
|
await save(auth, c)
|
|
497
|
+
saved = true
|
|
471
498
|
}
|
|
472
499
|
}
|
|
473
500
|
if (!c.profile) {
|
|
474
501
|
c.profile = await profileOf(c)
|
|
475
|
-
if (!auth?.profileArn
|
|
502
|
+
if (!auth?.profileArn && !saved) {
|
|
503
|
+
await save(auth, c)
|
|
504
|
+
saved = true
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
// once per token: a save that didn't take isn't tried on every request
|
|
508
|
+
if (!saved && c.own && Object.keys(idFields(auth)).length && !uidSaved.has(c.access)) {
|
|
509
|
+
uidSaved.add(c.access)
|
|
510
|
+
await save(auth, c)
|
|
476
511
|
}
|
|
477
512
|
held = { id, c }
|
|
478
513
|
return { token: c.access, tokenType: tokenType(c), profile: c.profile, region: regionOf(c.profile, c.region) }
|
|
@@ -493,7 +528,7 @@ function account(client) {
|
|
|
493
528
|
const out = { access: c.access, expires: c.expires }
|
|
494
529
|
if (c.refresh !== auth.refresh) out.refresh = c.refresh
|
|
495
530
|
if (c.profile && c.profile !== auth.profileArn) out.profileArn = c.profile
|
|
496
|
-
return out
|
|
531
|
+
return { ...out, ...idFields(auth) }
|
|
497
532
|
})
|
|
498
533
|
return creds
|
|
499
534
|
}
|
|
@@ -1403,8 +1438,9 @@ async function browserSignIn() {
|
|
|
1403
1438
|
return fail(e.message)
|
|
1404
1439
|
}
|
|
1405
1440
|
const who = await whoIs(c)
|
|
1406
|
-
const user = who.email
|
|
1441
|
+
const user = who.email ? nameOf(who.email, c.loginProvider) : "Kiro account"
|
|
1407
1442
|
finish({ type: "success", provider: ID, refresh: c.refresh, access: c.access, expires: c.expires, accountId: user,
|
|
1443
|
+
...(uidOf(c.loginProvider) ? { uid: uidOf(c.loginProvider) } : {}),
|
|
1408
1444
|
method: c.method, loginProvider: c.loginProvider, region: c.region, profileArn: who.profile,
|
|
1409
1445
|
...(c.clientId ? { clientId: c.clientId, clientSecret: c.clientSecret } : {}), ...(who.plan ? { plan: who.plan } : {}) })
|
|
1410
1446
|
html(200, page(true, "You're signed in", `${user} is signed in. You can close this tab.`))
|
|
@@ -1494,7 +1530,10 @@ export async function KiroAuthPlugin({ client } = {}) {
|
|
|
1494
1530
|
const auth = await getAuth()
|
|
1495
1531
|
if (!(auth?.type === "api" && auth.key) && auth?.type !== "oauth") return { error: "not signed in", signIn: "kept" }
|
|
1496
1532
|
try {
|
|
1497
|
-
|
|
1533
|
+
const u = usageOf(await usageLimits(await creds(auth), true))
|
|
1534
|
+
// named as the account is
|
|
1535
|
+
if (u.user && auth.type === "oauth" && !auth.source) u.user = nameOf(u.user, auth.loginProvider)
|
|
1536
|
+
return { ...u, signIn: "kept" }
|
|
1498
1537
|
} catch (e) {
|
|
1499
1538
|
return { error: e.message, signIn: "kept" }
|
|
1500
1539
|
}
|