diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 94dde77cf..88dd710d8 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -33,6 +33,9 @@ var requiredScopes = []string{ "read:client_keys", "read:logs", } +// RequiredScopes returns the scopes used for login. +func RequiredScopes() []string { return requiredScopes } + // SecretStore provides access to stored sensitive data. type SecretStore interface { // Get gets the secret diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 3c3186a18..2bfc5050e 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -10,6 +10,7 @@ import ( "os" "path" "path/filepath" + "sort" "strings" "sync" "time" @@ -43,6 +44,7 @@ type tenant struct { Name string `json:"name"` Domain string `json:"domain"` AccessToken string `json:"access_token,omitempty"` + Scopes []string `json:"scopes,omitempty"` ExpiresAt time.Time `json:"expires_at"` Apps map[string]app `json:"apps,omitempty"` DefaultAppID string `json:"default_app_id,omitempty"` @@ -129,8 +131,15 @@ func (c *cli) setup(ctx context.Context) error { return errUnauthenticated } - // check if the stored access token is expired: - if isExpired(t.ExpiresAt, accessTokenExpThreshold) { + if scopesChanged(t) { + // required scopes changed, + // a new token is required + err = RunLogin(ctx, c, true) + if err != nil { + return err + } + } else if isExpired(t.ExpiresAt, accessTokenExpThreshold) { + // check if the stored access token is expired: // use the refresh token to get a new access token: tr := &auth.TokenRetriever{ Secrets: &auth.Keyring{}, @@ -179,6 +188,32 @@ func isExpired(t time.Time, threshold time.Duration) bool { return time.Now().Add(threshold).After(t) } +// scopesChanged compare the tenant scopes +// with the currently required scopes. +func scopesChanged(t tenant) bool { + want := auth.RequiredScopes() + got := t.Scopes + + sort.Strings(want) + sort.Strings(got) + + if (want == nil) != (got == nil) { + return true + } + + if len(want) != len(got) { + return true + } + + for i := range t.Scopes { + if want[i] != got[i] { + return true + } + } + + return false +} + // getTenant fetches the default tenant configured (or the tenant specified via // the --tenant flag). func (c *cli) getTenant() (tenant, error) { diff --git a/internal/cli/login.go b/internal/cli/login.go index 086b346ed..a66a418c4 100644 --- a/internal/cli/login.go +++ b/internal/cli/login.go @@ -79,6 +79,7 @@ func RunLogin(ctx context.Context, cli *cli, expired bool) error { ExpiresAt: time.Now().Add( time.Duration(res.ExpiresIn) * time.Second, ), + Scopes: auth.RequiredScopes(), }) if err != nil { return fmt.Errorf("Unexpected error adding tenant to config: %w", err)