From 77aade0fc234761e0f8c042f38e183fd8593f07c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 13 Jul 2021 10:35:32 -0700 Subject: [PATCH 1/4] internal/cli: waypoint login supports tokens --- internal/cli/login.go | 71 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/internal/cli/login.go b/internal/cli/login.go index 05f30c42ab1..0426d38ece2 100644 --- a/internal/cli/login.go +++ b/internal/cli/login.go @@ -24,6 +24,7 @@ type LoginCommand struct { *baseCommand flagAuthMethod string + flagToken string } func (c *LoginCommand) Run(args []string) int { @@ -57,8 +58,18 @@ func (c *LoginCommand) Run(args []string) int { return 1 } - // Login with OIDC - token, exitCode := c.loginOIDC() + // Determine our auth func, which by default is OIDC + var authFunc func() (string, int) + switch { + case c.flagToken != "": + authFunc = c.loginToken + + default: + authFunc = c.loginOIDC + } + + // Log in + token, exitCode := authFunc() if exitCode > 0 { return exitCode } @@ -93,6 +104,41 @@ func (c *LoginCommand) Run(args []string) int { return 0 } +func (c *LoginCommand) loginToken() (string, int) { + // First we decode the token to ensure it is valid and also to figure + // out if we have a login or invite token. + decodeResp, err := c.project.Client().DecodeToken(c.Ctx, &pb.DecodeTokenRequest{ + Token: c.flagToken, + }) + if err != nil { + c.ui.Output(clierrors.Humanize(err), terminal.WithErrorStyle()) + return "", 1 + } + token := decodeResp.Token + + // If we have a login token, then we're just done cause that can be stored directly. + if _, ok := token.Kind.(*pb.Token_Login_); ok { + return c.flagToken, 0 + } + + // Then it must be an invite token + if _, ok := token.Kind.(*pb.Token_Invite_); !ok { + c.ui.Output(strings.TrimSpace(errTokenInvalid), terminal.WithErrorStyle()) + return "", 1 + } + + // Convert it + convertResp, err := c.project.Client().ConvertInviteToken(c.Ctx, &pb.ConvertInviteTokenRequest{ + Token: c.flagToken, + }) + if err != nil { + c.ui.Output(clierrors.Humanize(err), terminal.WithErrorStyle()) + return "", 1 + } + + return convertResp.Token, 0 +} + func (c *LoginCommand) loginOIDC() (string, int) { // Get our OIDC auth methods respList, err := c.project.Client().ListOIDCAuthMethods(c.Ctx, &empty.Empty{}) @@ -146,6 +192,8 @@ func (c *LoginCommand) loginOIDC() (string, int) { } // Open the auth URL in the user browser or ask them to visit it. + // We purposely use fmt here and NOT c.ui because the ui will truncate + // our URL (a known bug). fmt.Printf(strings.TrimSpace(outVisitURL)+"\n\n", respURL.Url) if err := util.OpenURL(respURL.Url); err != nil { c.Log.Warn("error opening auth url", "err", err) @@ -186,6 +234,13 @@ func (c *LoginCommand) Flags() *flag.Sets { Usage: "Auth method to use for login. This will default to " + "the only available auth method if only one exists.", }) + + f.StringVar(&flag.StringVar{ + Name: "token", + Target: &c.flagToken, + Usage: "Auth with a token. This will force auth-method to 'token'. " + + "This works with both login and invite tokens.", + }) }) } @@ -207,10 +262,17 @@ Usage: waypoint login [server address] Log in to a Waypoint server. + This is usually the first command a new user runs to gain CLI access to + an existing Waypoint server. + If the server address is not specified and you have an active context (see "waypoint context"), then this command will reauthenticate to the currently active server. + This command can be used for token-based authentication as well as + other forms such as OIDC. You can use "-token" to specify a login or + invite token and configure the CLI to access the server. + ` + c.Flags().Help()) } @@ -238,6 +300,11 @@ waypoint login example.com or waypoint login https://example.com +` + + errTokenInvalid = ` +The specified token is not a valid login or invite token. Please +double-check the token and try again. ` outVisitURL = ` From e7cc8511f6d0fac5602d476965d8add3ba997d31 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 13 Jul 2021 10:51:05 -0700 Subject: [PATCH 2/4] internal/cli: capture client context --- internal/cli/login.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/cli/login.go b/internal/cli/login.go index 0426d38ece2..1f0a52c50e8 100644 --- a/internal/cli/login.go +++ b/internal/cli/login.go @@ -78,6 +78,12 @@ func (c *LoginCommand) Run(args []string) int { // which is already configured with the basic server connection stuff // from this command. newContext := c.flagConnection + if c.clientContext != nil { + // clientContext is always set to our actual context we used to + // create our client. So this will accurately grab non-flag based + // access i.e. loading our default context. + newContext = *c.clientContext + } newContext.Server.AuthToken = token newContext.Server.RequireAuth = true From e9070963f6f07da99791b6ac15d89cee062fc5a4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 13 Jul 2021 10:53:12 -0700 Subject: [PATCH 3/4] internal/cli: update error message for token --- internal/cli/login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cli/login.go b/internal/cli/login.go index 1f0a52c50e8..f9790c1c909 100644 --- a/internal/cli/login.go +++ b/internal/cli/login.go @@ -285,7 +285,7 @@ Usage: waypoint login [server address] const ( errNoAuthMethods = ` Only token-based authentication is allowed by this server. To login using -a token, use the "waypoint context create" command. +a token, use the "waypoint login" command with the "-token" flag. ` errManyAuthMethods = ` From 99d91781f54e35e8bb17e50e4abeb005d1df3387 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 13 Jul 2021 10:59:47 -0700 Subject: [PATCH 4/4] changelog --- .changelog/1848.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/1848.txt diff --git a/.changelog/1848.txt b/.changelog/1848.txt new file mode 100644 index 00000000000..62cdce99049 --- /dev/null +++ b/.changelog/1848.txt @@ -0,0 +1,3 @@ +```release-note:feature +cli: can login with a token using the new `waypoint login` command +```