Skip to content

Commit d380a00

Browse files
fix: skip plaintext cache tokens with non-SSO scopes
GetValidSSOTokenFromPlaintextCache matched tokens by startUrl only, causing it to pick up IDE extension tokens (e.g. codewhisperer:* scopes) that lack sso:account:access. This resulted in a 401 UnauthorizedException with no fallback to the browser login flow. Add scope filtering to skip tokens that contain only non-SSO scopes. Fixes #940
1 parent 7d9d3b7 commit d380a00

1 file changed

Lines changed: 23 additions & 7 deletions

File tree

pkg/cfaws/ssotoken.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ import (
1616
)
1717

1818
type SSOPlainTextOut struct {
19-
AccessToken string `json:"accessToken"`
20-
ExpiresAt string `json:"expiresAt"`
21-
SSOSessionName string `json:"ssoSessionName"`
22-
StartUrl string `json:"startUrl"`
23-
Region string `json:"region"`
19+
AccessToken string `json:"accessToken"`
20+
ExpiresAt string `json:"expiresAt"`
21+
SSOSessionName string `json:"ssoSessionName"`
22+
StartUrl string `json:"startUrl"`
23+
Region string `json:"region"`
24+
Scopes []string `json:"scopes,omitempty"`
2425
}
2526

2627
// CreatePlainTextSSO is currently unused. In a future version of the Granted CLI,
@@ -182,8 +183,8 @@ func ReadPlaintextSsoCreds(startUrl string) (SSOPlainTextOut, error) {
182183
if err != nil {
183184
return SSOPlainTextOut{}, err
184185
}
185-
// check if the startUrl matches
186-
if sso.StartUrl == startUrl {
186+
// check if the startUrl matches and the token has appropriate scopes
187+
if sso.StartUrl == startUrl && hasSSOScopes(sso.Scopes) {
187188
return sso, nil
188189
}
189190
}
@@ -192,6 +193,21 @@ func ReadPlaintextSsoCreds(startUrl string) (SSOPlainTextOut, error) {
192193
return SSOPlainTextOut{}, fmt.Errorf("no valid sso token found")
193194
}
194195

196+
// hasSSOScopes returns true if the token has scopes that include SSO account
197+
// access, or if no scopes are specified (for backwards compatibility with
198+
// tokens that don't include scope metadata).
199+
func hasSSOScopes(scopes []string) bool {
200+
if len(scopes) == 0 {
201+
return true
202+
}
203+
for _, scope := range scopes {
204+
if strings.HasPrefix(scope, "sso:") {
205+
return true
206+
}
207+
}
208+
return false
209+
}
210+
195211
func GetValidSSOTokenFromPlaintextCache(startUrl string) *securestorage.SSOToken {
196212
if SsoCredsAreInConfigCache() {
197213
creds, err := ReadPlaintextSsoCreds(startUrl)

0 commit comments

Comments
 (0)