Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AuthUrlAction to override UrlAction for SSO auth #545

Merged
merged 1 commit into from
Aug 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

* Guided setup is now more simple unless user provides the `--advanced` flag #530
* Guided setup now strips leading and trailing spaces for string input
* Revert #491 so SSO auth uses Firefox containers

### New Features

Expand All @@ -25,6 +26,8 @@
* `config-profiles` now supports the `--aws-config` flag
* Added [ecs list](docs/ecs-server.md#listing-profiles) command to list
profiles in named slots #517
* Add [AuthUrlAction](docs/config.md#authurlaction) to override [UrlAction](docs/config.md#urlaction)
during SSO Authentication. #524

## [v1.12.0] - 2023-08-12

Expand Down
7 changes: 7 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ SSOConfig:
SSORegion: <AWS Region where AWS SSO is deployed>
StartUrl: <URL for AWS SSO Portal>
DefaultRegion: <AWS_DEFAULT_REGION>
AuthUrlAction: [clip|exec|print|printurl|open|granted-containers|open-url-in-container]
Accounts: # optional block for specifying tags & overrides
<AccountId>:
Name: <Friendly Name of Account>
Expand Down Expand Up @@ -129,6 +130,12 @@ selected (most specific to most generic):
1. At the AWS SSO Instance level: `SSOConfig -> <AWS SSO Instance>`
1. At the config file level (default is `us-east-1`)

### AuthUrlAction

Override the global [UrlAction](#urlaction) when authenticating with your SSO provider
to retrieve an AWS SSO token. Generally only useful when you wish to use your default
browser with one `SSOConfig` block to re-use your existing SSO browser authentication cookie.

### Accounts

The `Accounts` block is completely optional! The only purpose of this block
Expand Down
11 changes: 8 additions & 3 deletions sso/awssso_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,13 @@ func (as *AWSSSO) reauthenticate() error {
return fmt.Errorf("Unable to get device auth info from AWS SSO: %s", err.Error())
}

urlOpener := url.NewHandleUrl(url.SSOAuthAction(as.urlAction), auth.VerificationUriComplete,
as.browser, as.urlExecCommand)
action := as.urlAction
if as.SSOConfig.AuthUrlAction != url.Undef {
// specific action for authentication?
action = as.SSOConfig.AuthUrlAction
}

urlOpener := url.NewHandleUrl(action, auth.VerificationUriComplete, as.browser, as.urlExecCommand)
urlOpener.ContainerSettings(as.StoreKey(), DEFAULT_AUTH_COLOR, DEFAULT_AUTH_ICON)

if err = urlOpener.Open(); err != nil {
Expand Down Expand Up @@ -262,7 +267,7 @@ func (as *AWSSSO) createToken() error {
} else if errors.As(err, &ape) {
time.Sleep(retryInterval)
} else {
return err
return fmt.Errorf("createToken: %s", err.Error())
}
}

Expand Down
85 changes: 82 additions & 3 deletions sso/awssso_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,17 +344,96 @@ func TestAuthenticateFailure(t *testing.T) {
CreateToken: &ssooidc.CreateTokenOutput{},
Error: fmt.Errorf("some error"),
},
// fourth test
{
RegisterClient: &ssooidc.RegisterClientOutput{
AuthorizationEndpoint: nil,
ClientId: aws.String("this-is-my-client-id"),
ClientSecret: aws.String("this-is-my-client-secret"),
ClientIdIssuedAt: time.Now().Unix(),
ClientSecretExpiresAt: int64(expires),
TokenEndpoint: nil,
},
Error: nil,
},
{
StartDeviceAuthorization: &ssooidc.StartDeviceAuthorizationOutput{
DeviceCode: aws.String("device-code"),
UserCode: aws.String("user-code"),
VerificationUri: aws.String(""),
VerificationUriComplete: aws.String("verification-uri-complete"),
ExpiresIn: int32(expires),
Interval: 5,
},
Error: nil,
},
// fifth test
{
RegisterClient: &ssooidc.RegisterClientOutput{
AuthorizationEndpoint: nil,
ClientId: aws.String("this-is-my-client-id"),
ClientSecret: aws.String("this-is-my-client-secret"),
ClientIdIssuedAt: time.Now().Unix(),
ClientSecretExpiresAt: int64(expires),
TokenEndpoint: nil,
},
Error: nil,
},
{
StartDeviceAuthorization: &ssooidc.StartDeviceAuthorizationOutput{
DeviceCode: aws.String("device-code"),
UserCode: aws.String("user-code"),
VerificationUri: aws.String("verification-uri"),
VerificationUriComplete: aws.String("verification-uri-complete"),
ExpiresIn: int32(expires),
Interval: 5,
},
Error: nil,
},
// sixth test
{
RegisterClient: &ssooidc.RegisterClientOutput{
AuthorizationEndpoint: nil,
ClientId: aws.String("this-is-my-client-id"),
ClientSecret: aws.String("this-is-my-client-secret"),
ClientIdIssuedAt: time.Now().Unix(),
ClientSecretExpiresAt: int64(expires),
TokenEndpoint: nil,
},
Error: nil,
},
{
StartDeviceAuthorization: &ssooidc.StartDeviceAuthorizationOutput{
DeviceCode: aws.String("device-code"),
UserCode: aws.String("user-code"),
VerificationUri: aws.String("verification-uri"),
VerificationUriComplete: aws.String("verification-uri-complete"),
ExpiresIn: int32(expires),
Interval: 5,
},
Error: nil,
},
},
}

err = as.Authenticate("print", "fake-browser")
assert.Contains(t, err.Error(), "some error")
assert.Contains(t, err.Error(), "Unable to register client with AWS SSO")

err = as.Authenticate("print", "fake-browser")
assert.Contains(t, err.Error(), "Unable to start device authorization")

err = as.Authenticate("print", "fake-browser")
assert.Contains(t, err.Error(), "createToken:")

err = as.Authenticate("print", "fake-browser")
assert.Contains(t, err.Error(), "some error")
assert.Contains(t, err.Error(), "No valid verification url")

err = as.Authenticate("invalid", "fake-browser")
assert.Contains(t, err.Error(), "Unsupported Open action")

as.SSOConfig.AuthUrlAction = "invalid"
err = as.Authenticate("print", "fake-browser")
assert.Contains(t, err.Error(), "some error")
assert.Contains(t, err.Error(), "Unsupported Open action")
}

func TestReauthenticate(t *testing.T) {
Expand Down
10 changes: 10 additions & 0 deletions sso/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"strings"

"github.com/synfinatic/aws-sso-cli/internal/tags"
"github.com/synfinatic/aws-sso-cli/internal/url"
"github.com/synfinatic/aws-sso-cli/internal/utils"
)

Expand All @@ -33,6 +34,10 @@ type SSOConfig struct {
StartUrl string `koanf:"StartUrl" yaml:"StartUrl"`
Accounts map[string]*SSOAccount `koanf:"Accounts" yaml:"Accounts,omitempty"` // key must be a string to avoid parse errors!
DefaultRegion string `koanf:"DefaultRegion" yaml:"DefaultRegion,omitempty"`

// overrides for this SSO Instance
AuthUrlAction url.Action `koanf:"AuthUrlAction" yaml:"AuthUrlAction,omitempty"`

// passed to AWSSSO from our Settings
MaxBackoff int `koanf:"-" yaml:"-"`
MaxRetry int `koanf:"-" yaml:"-"`
Expand Down Expand Up @@ -62,6 +67,11 @@ type SSORole struct {
func (c *SSOConfig) Refresh(s *Settings) {
c.MaxBackoff = s.MaxBackoff
c.MaxRetry = s.MaxRetry

if c.AuthUrlAction == url.Undef {
c.AuthUrlAction = s.UrlAction
}

for accountId, a := range c.Accounts {
a.SetParentConfig(c)
for roleName, r := range a.Roles {
Expand Down