common-fate / granted

The easiest way to access your cloud.
https://granted.dev
MIT License
956 stars 90 forks source link

Add support for refreshable AWS SSO tokens #616

Closed chrnorm closed 4 months ago

chrnorm commented 4 months ago

What changed?

This PR adds opt-in support for the new IAM Identity Center refreshable token login flow. You can opt-in to using refreshable tokens by configuring your AWS profiles to include granted_sso_registration_scopes as follows:

[profile example]
granted_sso_account_id = 123456789012
granted_sso_role_name = AWSAdministratorAccess
+ granted_sso_registration_scopes = sso:account:access
granted_sso_start_url = https://commonfate.awsapps.com/start
granted_sso_region = ap-southeast-2
credential_process         = granted credential-process --profile example

At the moment, support for SSO registration scopes has been implemented only for profiles using the Granted Credential Process configuration.

Some background here; the native AWS CLI way of configuring this is by using the sso-session entry as follows:

[profile AWSAdministratorAccess-123456789012]
sso_session = commonfate
sso_account_id = 123456789012
sso_role_name = AWSAdministratorAccess
region = ap-southeast-2

[sso-session commonfate]
sso_start_url = https://example.awsapps.com/start
sso_region = ap-southeast-2
sso_registration_scopes = sso:account:access

I would like Granted to support this configuration too, however the AWS v2 Go SDK does not yet support reading sso_registration_scopes. So the initial implementation here just uses granted_sso_registration_scopes.

In future it's likely we'll make the refreshable token flow the default, but for now this behaviour is opt-in.

Additionally, this PR tidies up some of our AWS SSO login flow code. In the Granted codebase the cfaws package has grown to be quite large. I've moved the SSO login flow code out of this package into a new idclogin package.

Why?

Fixes #615.

How did you test it?

Tested and verified on MacOS by manually logging in a few times to verify the functionality was correct. I tested the refresh flow by manipulating the Expiry value of the token inside my keychain.

I also tested the case where the refresh token is invalid, by overriding the refreshToken value inside my keychain and overriding the Expiry to force a refresh. The behaviour here is that an error is logged to stderr and the user is prompted to reauthenticate, which is what we want.

Given that no platform-specific keychain functionality has been modified I expect this to work across other OSes too.

Potential risks