Skip to content

Commit

Permalink
Merge branch 'v1' of https://github.com/auth0/auth0-cli into v1
Browse files Browse the repository at this point in the history
  • Loading branch information
willvedd committed Jan 5, 2023
2 parents 494cbf3 + abb6b2e commit 7222ab4
Show file tree
Hide file tree
Showing 14 changed files with 296 additions and 192 deletions.
10 changes: 0 additions & 10 deletions internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import (
const (
audiencePath = "/api/v2/"
waitThresholdInSeconds = 3
// SecretsNamespace is the namespace used to set/get values from the keychain.
SecretsNamespace = "auth0-cli"
)

var requiredScopes = []string{
Expand Down Expand Up @@ -52,14 +50,6 @@ type Authenticator struct {
OauthTokenEndpoint string
}

// SecretStore provides access to stored sensitive data.
type SecretStore interface {
// Get gets the secret
Get(namespace, key string) (string, error)
// Delete removes the secret
Delete(namespace, key string) error
}

type Result struct {
Tenant string
Domain string
Expand Down
22 changes: 0 additions & 22 deletions internal/auth/secrets.go

This file was deleted.

63 changes: 0 additions & 63 deletions internal/auth/secrets_test.go

This file was deleted.

15 changes: 5 additions & 10 deletions internal/auth/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"io"
"net/http"
"net/url"

"github.com/auth0/auth0-cli/internal/keyring"
)

type TokenResponse struct {
Expand All @@ -19,25 +21,18 @@ type TokenResponse struct {

type TokenRetriever struct {
Authenticator *Authenticator
Secrets SecretStore
Client *http.Client
}

// Delete deletes the given tenant from the secrets storage.
func (t *TokenRetriever) Delete(tenant string) error {
return t.Secrets.Delete(SecretsNamespace, tenant)
}

// Refresh gets a new access token from the provided refresh token,
// The request is used the default client_id and endpoint for device authentication.
func (t *TokenRetriever) Refresh(ctx context.Context, tenant string) (TokenResponse, error) {
// get stored refresh token:
refreshToken, err := t.Secrets.Get(SecretsNamespace, tenant)
refreshToken, err := keyring.GetRefreshToken(tenant)
if err != nil {
return TokenResponse{}, fmt.Errorf("cannot get the stored refresh token: %w", err)
return TokenResponse{}, fmt.Errorf("failed to retrieve refresh token from keyring: %w", err)
}
if refreshToken == "" {
return TokenResponse{}, errors.New("cannot use the stored refresh token: the token is empty")
return TokenResponse{}, errors.New("failed to use stored refresh token: the token is empty")
}
// get access token:
r, err := t.Client.PostForm(t.Authenticator.OauthTokenEndpoint, url.Values{
Expand Down
17 changes: 11 additions & 6 deletions internal/auth/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import (
"testing"

"github.com/golang/mock/gomock"
goKeyring "github.com/zalando/go-keyring"

"github.com/auth0/auth0-cli/internal/auth/mock"
"github.com/auth0/auth0-cli/internal/keyring"
)

// HTTPTransport implements an http.RoundTripper for testing purposes only.
// HTTPTransport implements a http.RoundTripper for testing purposes only.
type testTransport struct {
withResponse *http.Response
withError error
Expand All @@ -29,8 +30,13 @@ func TestTokenRetriever_Refresh(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

secretsMock := mock.NewMockSecretStore(ctrl)
secretsMock.EXPECT().Get("auth0-cli", "mytenant").Return("refresh-token-here", nil).Times(1)
testTenantName := "auth0-cli-test.us.auth0.com"

goKeyring.MockInit()
err := keyring.StoreRefreshToken(testTenantName, "refresh-token-here")
if err != nil {
t.Fatal(err)
}

transport := &testTransport{
withResponse: &http.Response{
Expand All @@ -48,11 +54,10 @@ func TestTokenRetriever_Refresh(t *testing.T) {

tr := &TokenRetriever{
Authenticator: &Authenticator{"https://test.com/api/v2/", "client-id", "https://test.com/oauth/device/code", "https://test.com/token"},
Secrets: secretsMock,
Client: client,
}

got, err := tr.Refresh(context.Background(), "mytenant")
got, err := tr.Refresh(context.Background(), testTenantName)
if err != nil {
t.Fatal(err)
}
Expand Down
34 changes: 21 additions & 13 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/auth0/auth0-cli/internal/buildinfo"
"github.com/auth0/auth0-cli/internal/display"
"github.com/auth0/auth0-cli/internal/iostream"
"github.com/auth0/auth0-cli/internal/keyring"
)

const (
Expand Down Expand Up @@ -53,7 +54,6 @@ type Tenant struct {
Apps map[string]app `json:"apps,omitempty"`
DefaultAppID string `json:"default_app_id,omitempty"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
}

type app struct {
Expand Down Expand Up @@ -96,11 +96,11 @@ type cli struct {
}

func (t *Tenant) authenticatedWithClientCredentials() bool {
return t.ClientID != "" && t.ClientSecret != ""
return t.ClientID != ""
}

func (t *Tenant) authenticatedWithDeviceCodeFlow() bool {
return t.ClientID == "" && t.ClientSecret == ""
return t.ClientID == ""
}

func (t *Tenant) hasExpiredToken() bool {
Expand Down Expand Up @@ -130,11 +130,16 @@ func (t *Tenant) additionalRequestedScopes() []string {

func (t *Tenant) regenerateAccessToken(ctx context.Context, c *cli) error {
if t.authenticatedWithClientCredentials() {
clientSecret, err := keyring.GetClientSecret(t.Domain)
if err != nil {
return fmt.Errorf("failed to retrieve client secret from keyring: %w", err)
}

token, err := auth.GetAccessTokenFromClientCreds(
ctx,
auth.ClientCredentials{
ClientID: t.ClientID,
ClientSecret: t.ClientSecret,
ClientSecret: clientSecret,
Domain: t.Domain,
},
)
Expand All @@ -149,7 +154,6 @@ func (t *Tenant) regenerateAccessToken(ctx context.Context, c *cli) error {
if t.authenticatedWithDeviceCodeFlow() {
tokenRetriever := &auth.TokenRetriever{
Authenticator: c.authenticator,
Secrets: &auth.Keyring{},
Client: http.DefaultClient,
}

Expand Down Expand Up @@ -242,15 +246,20 @@ func (c *cli) prepareTenant(ctx context.Context) (Tenant, error) {

if err := t.regenerateAccessToken(ctx, c); err != nil {
if t.authenticatedWithClientCredentials() {
return t, fmt.Errorf(
"failed to fetch access token using client credentials.\n\n"+
"This may occur if the designated application has been deleted or the client secret has been rotated.\n\n"+
errorMessage := fmt.Errorf(
"failed to fetch access token using client credentials: %w\n\n"+
"This may occur if the designated Auth0 application has been deleted, "+
"the client secret has been rotated or previous failure to store client secret in the keyring.\n\n"+
"Please re-authenticate by running: %s",
err,
ansi.Bold("auth0 login --domain <tenant-domain --client-id <client-id> --client-secret <client-secret>"),
)

return t, errorMessage
}

c.renderer.Warnf("Failed to renew access token. Please log in to re-authorize the CLI.\n")
c.renderer.Warnf("Failed to renew access token: %s", err)
c.renderer.Warnf("Please log in to re-authorize the CLI.\n")

return RunLoginAsUser(ctx, c, t.additionalRequestedScopes())
}
Expand Down Expand Up @@ -365,12 +374,11 @@ func (c *cli) removeTenant(ten string) error {
}

if err := c.persistConfig(); err != nil {
return fmt.Errorf("Unexpected error persisting config: %w", err)
return fmt.Errorf("failed to persist config: %w", err)
}

tr := &auth.TokenRetriever{Secrets: &auth.Keyring{}}
if err := tr.Delete(ten); err != nil {
return fmt.Errorf("Unexpected error clearing tenant information: %w", err)
if err := keyring.DeleteSecretsForTenant(ten); err != nil {
return fmt.Errorf("failed to delete tenant secrets: %w", err)
}

return nil
Expand Down
27 changes: 14 additions & 13 deletions internal/cli/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/auth0/auth0-cli/internal/ansi"
"github.com/auth0/auth0-cli/internal/auth"
"github.com/auth0/auth0-cli/internal/keyring"
"github.com/auth0/auth0-cli/internal/prompt"
)

Expand Down Expand Up @@ -195,13 +196,9 @@ func RunLoginAsUser(ctx context.Context, cli *cli, additionalScopes []string) (T
cli.renderer.Infof("Tenant: %s", result.Domain)
cli.renderer.Newline()

// Store the refresh token.
secretsStore := &auth.Keyring{}
err = secretsStore.Set(auth.SecretsNamespace, result.Domain, result.RefreshToken)
if err != nil {
message = "Could not store the refresh token locally, " +
"please expect to login again once your access token expired. See %s."
cli.renderer.Warnf(message, "https://github.com/auth0/auth0-cli/blob/main/KNOWN-ISSUES.md")
if err := keyring.StoreRefreshToken(result.Domain, result.RefreshToken); err != nil {
cli.renderer.Warnf("Could not store the refresh token to the keyring: %s", err)
cli.renderer.Warnf("Expect to login again when your access token expires.")
}

tenant := Tenant{
Expand Down Expand Up @@ -269,15 +266,19 @@ func RunLoginAsMachine(ctx context.Context, inputs LoginInputs, cli *cli, cmd *c
"Ensure that the provided client-id, client-secret and domain are correct. \n\nerror: %w\n", err)
}

if err = keyring.StoreClientSecret(inputs.Domain, inputs.ClientSecret); err != nil {
cli.renderer.Warnf("Could not store the client secret to the keyring: %s", err)
cli.renderer.Warnf("Expect to login again when your access token expires.")
}

t := Tenant{
Domain: inputs.Domain,
AccessToken: token.AccessToken,
ExpiresAt: token.ExpiresAt,
ClientID: inputs.ClientID,
ClientSecret: inputs.ClientSecret,
Domain: inputs.Domain,
AccessToken: token.AccessToken,
ExpiresAt: token.ExpiresAt,
ClientID: inputs.ClientID,
}

if err := cli.addTenant(t); err != nil {
if err = cli.addTenant(t); err != nil {
return fmt.Errorf("unexpected error when attempting to save tenant data: %w", err)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/cli/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func logoutCmd(cli *cli) *cobra.Command {
}

if err := cli.removeTenant(selectedTenant); err != nil {
return err // This error is already formatted for display
return err
}

cli.renderer.Infof("Successfully logged out tenant: %s", selectedTenant)
Expand Down
Loading

0 comments on commit 7222ab4

Please sign in to comment.