Skip to content

Device code SSO login fails against dual-stack Identity Center instances, and AWS_USE_DUALSTACK_ENDPOINT is silently ignored #960

Description

@jjrawlins

assume and granted sso login can't complete a device-code login against an Identity Center instance on the new dual-stack portal domain (https://<instance>.portal.<region>.app.aws). The link granted prints points at the legacy IPv4 portal on a different domain than the configured start URL, and sign-in there fails unless you already have a session on that domain.

Repro

Profile pointing at a dual-stack instance:

[profile example]
granted_sso_start_url = https://ssoins-REDACTED.portal.us-east-2.app.aws
granted_sso_region    = us-east-2
granted_sso_account_id = 000000000000
granted_sso_role_name  = AdministratorAccess

assume example prints:

[i] If the browser does not open automatically, please open this link: https://ssoins-REDACTED.us-east-2.portal.amazonaws.com/#/device?user_code=XXXX-XXXX

Different domain than the start URL.

Root cause

Two separate things.

granted is faithful — pkg/idclogin/run.go prints deviceAuth.VerificationUriComplete verbatim, and AWS is what returns the legacy domain. Confirmed with a raw unauthenticated call using the same start URL, where the only variable is which endpoint the request lands on:

$ aws sso-oidc start-device-authorization \
    --start-url https://ssoins-REDACTED.portal.us-east-2.app.aws --region us-east-2 ...
verificationUriComplete: https://ssoins-REDACTED.us-east-2.portal.amazonaws.com/#/device?user_code=RDKV-JGVM

$ AWS_USE_DUALSTACK_ENDPOINT=true aws sso-oidc start-device-authorization ...same args...
verificationUriComplete: https://ssoins-REDACTED.portal.us-east-2.app.aws/#/device?user_code=XVNK-HVKG

So AWS returns a usable URI if the sso-oidc call goes to the dual-stack endpoint. That half is tracked upstream at aws/aws-cli#10507.

The granted-side bug is that there's no way to ask for it. pkg/cfaws/assumer_aws_sso.go:193 and pkg/granted/sso.go:311 build the client config as:

newCfg := aws.NewConfig()
newCfg.Region = rootProfile.SSORegion()

aws.NewConfig() leaves ConfigSources empty, and the SDK bails out early:

func resolveUseDualStackEndpoint(cfg aws.Config, o *Options) error {
	if len(cfg.ConfigSources) == 0 {
		return nil
	}
	...
}

(service/ssooidc/api_client.go, unchanged as of v1.35.15, the version pinned on main.)

So both AWS_USE_DUALSTACK_ENDPOINT=true and use_dualstack_endpoint = true in ~/.aws/config are dropped before they reach the client. The documented aws-cli workaround has no effect on granted.

Workaround

UseAuthorizationCode = true in ~/.granted/config. PKCE never calls StartDeviceAuthorization, so the bad URI is never produced — it registers with IssuerUrl set to the configured start URL and goes straight to oidc.<region>.amazonaws.com/authorize. Confirmed working on 0.39.0.

Worth flagging that #923 pitched PKCE purely as a UX improvement and deliberately kept device code as the default. It's also currently the only working path for dual-stack instances, which is maybe an argument for revisiting that default, or at least documenting it.

Suggested fix

Load shared config instead of a bare one, so the standard AWS env vars and ~/.aws/config settings apply:

cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(rootProfile.SSORegion()))

Same pattern applies at the other aws.NewConfig() sites (pkg/cfaws/creds.go:111, pkg/cfaws/assumer_aws_sso.go:227, pkg/granted/doctor/doctor.go:228) if AWS config is meant to be honored there too.

granted 0.39.0 (homebrew), macOS 26.5 arm64.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions