Skip to content

fix 'Refresh failed: Invalid token provided' caused by token-fresher request to wrong idc - #118

Open
CavemanIV wants to merge 1 commit into
tickernelz:masterfrom
CavemanIV:fix-invalid-fresh-token-provided
Open

fix 'Refresh failed: Invalid token provided' caused by token-fresher request to wrong idc#118
CavemanIV wants to merge 1 commit into
tickernelz:masterfrom
CavemanIV:fix-invalid-fresh-token-provided

Conversation

@CavemanIV

Copy link
Copy Markdown

User may experience frequently error if they use non US access point.

An IdC account has two regions and they are not always the same: the OIDC
region that issued the SSO session and refresh token, and the service
region where the CodeWhisperer profile lives. The sync layer set both to
the profile ARN's region, discarding the real OIDC region:

    const serviceRegion = extractRegionFromArn(profileArn) || ...
    const oidcRegion = serviceRegion

Since token.ts resolves auth.oidcRegion || auth.region to build the
refresh URL, every refresh for such an account went to the wrong endpoint
and failed deterministically.

Confirmed by probing both endpoints with the account's real credentials:
POST oidc.ap-southeast-1.amazonaws.com/token -> 200, expiresIn 3600
POST oidc.us-east-1.amazonaws.com/token
-> 400 invalid_request / "Invalid token provided"

The observed case is an ap-southeast-1 session paired with a us-east-1
profile ARN. The DB schema already had separate region/oidc_region
columns and token.ts already preferred oidcRegion, so only the sync layer
needed fixing.

…request to wrong idc if serviceRegion is not same with ARN region
@Zaldaryon

Zaldaryon commented Aug 19, 2026

Copy link
Copy Markdown

Second data point for this, from a different region pair. My Identity Center directory is in sa-east-1 and the CodeWhisperer profile ARN is in us-east-1, and the failure matches yours: every refresh after the first hour returns 400 invalid_request / "Invalid token provided".

Probing both endpoints with the same refreshToken, clientId and clientSecret:

POST https://oidc.us-east-1.amazonaws.com/token  -> 400 {"error":"invalid_request","error_description":"Invalid token provided"}
POST https://oidc.sa-east-1.amazonaws.com/token  -> 200 {"expiresIn":3600}

The 200 response returns the same refreshToken it was given, so kiro-cli and the plugin can both refresh one session without invalidating each other's copy.

One addition worth considering on top of deriveRegions. The OIDC clientId carries the region it was registered in, base64 encoded in its tail: on my account the trailing bytes of the clientId decode to sa-east-1. That gives token.ts a per-account source for the region that does not depend on what the sync layer wrote:

const oidcRegion = regionFromOidcClientId(p.clientId) ?? auth.oidcRegion ?? auth.region
import { isValidRegion } from '../constants.js'

function regionFromOidcClientId(clientId?: string): string | undefined {
  if (!clientId) return undefined
  for (let i = 0; i < clientId.length; i++) {
    const tail = clientId.slice(i).replace(/-/g, '+').replace(/_/g, '/')
    const padded = tail + '='.repeat((4 - (tail.length % 4)) % 4)
    let decoded: string
    try {
      decoded = Buffer.from(padded, 'base64').toString('utf8')
    } catch {
      continue
    }
    const match = /([a-z]{2}(?:-[a-z]+)+-\d)$/.exec(decoded)
    if (match && isValidRegion(match[1])) return match[1]
  }
  return undefined
}

The isValidRegion check does the real work: RegionSchema is a closed list, so a chance decode cannot turn into a region that does not exist, and the function returns undefined for a clientId with no region in it, which falls through to the current behavior.

This buys two things. An account whose row holds a wrong oidc_region recovers on the next request rather than waiting for a sync to rewrite the row, which matters when the CLI token in the local SQLite has also expired and the sync has no fresh credential to hand back. It also covers the case the old comment in kiro-cli.ts described, where data.region is not the OIDC region, since it reads neither the token row nor the ARN.

I ran this on my account with oidc_region set to us-east-1 in the DB and an expired access token, and the refresh succeeded because the clientId wins. I can send it as a commit here if you want it in this PR, with a case in token-region.test.ts covering a stored region that disagrees with the clientId, or keep it as a follow-up once this lands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants