feat: generate a new session with coder login --token (#8275)

* feat: coder login --token generates a new session

Makes sure /logout does not delete the inputted token
* flag to enable previous behavior if needed
This commit is contained in:
Steven Masley
2023-07-05 08:59:38 -04:00
committed by GitHub
parent 88c35d3f04
commit 9a7705c656
5 changed files with 51 additions and 11 deletions
+32 -9
View File
@@ -41,16 +41,18 @@ func (r *RootCmd) login() *clibase.Cmd {
const firstUserTrialEnv = "CODER_FIRST_USER_TRIAL"
var (
email string
username string
password string
trial bool
email string
username string
password string
trial bool
useTokenForSession bool
)
cmd := &clibase.Cmd{
Use: "login <url>",
Short: "Authenticate with Coder deployment",
Middleware: clibase.RequireRangeArgs(0, 1),
Handler: func(inv *clibase.Invocation) error {
ctx := inv.Context()
rawURL := ""
if len(inv.Args) == 0 {
rawURL = r.clientURL.String()
@@ -89,7 +91,7 @@ func (r *RootCmd) login() *clibase.Cmd {
_, _ = fmt.Fprintln(inv.Stderr, cliui.DefaultStyles.Warn.Render(err.Error()))
}
hasInitialUser, err := client.HasFirstUser(inv.Context())
hasInitialUser, err := client.HasFirstUser(ctx)
if err != nil {
return xerrors.Errorf("Failed to check server %q for first user, is the URL correct and is coder accessible from your browser? Error - has initial user: %w", serverURL.String(), err)
}
@@ -182,7 +184,7 @@ func (r *RootCmd) login() *clibase.Cmd {
trial = v == "yes" || v == "y"
}
_, err = client.CreateFirstUser(inv.Context(), codersdk.CreateFirstUserRequest{
_, err = client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{
Email: email,
Username: username,
Password: password,
@@ -191,7 +193,7 @@ func (r *RootCmd) login() *clibase.Cmd {
if err != nil {
return xerrors.Errorf("create initial user: %w", err)
}
resp, err := client.LoginWithPassword(inv.Context(), codersdk.LoginWithPasswordRequest{
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
Email: email,
Password: password,
})
@@ -235,7 +237,7 @@ func (r *RootCmd) login() *clibase.Cmd {
Secret: true,
Validate: func(token string) error {
client.SetSessionToken(token)
_, err := client.User(inv.Context(), codersdk.Me)
_, err := client.User(ctx, codersdk.Me)
if err != nil {
return xerrors.New("That's not a valid token!")
}
@@ -245,11 +247,27 @@ func (r *RootCmd) login() *clibase.Cmd {
if err != nil {
return xerrors.Errorf("paste token prompt: %w", err)
}
} else if !useTokenForSession {
// If a session token is provided on the cli, use it to generate
// a new one. This is because the cli `--token` flag provides
// a token for the command being invoked. We should not store
// this token, and `/logout` should not delete it.
// /login should generate a new token and store that.
client.SetSessionToken(sessionToken)
// Use CreateAPIKey over CreateToken because this is a session
// key that should not show on the `tokens` page. This should
// match the same behavior of the `/cli-auth` page for generating
// a session token.
key, err := client.CreateAPIKey(ctx, "me")
if err != nil {
return xerrors.Errorf("create api key: %w", err)
}
sessionToken = key.Key
}
// Login to get user data - verify it is OK before persisting
client.SetSessionToken(sessionToken)
resp, err := client.User(inv.Context(), codersdk.Me)
resp, err := client.User(ctx, codersdk.Me)
if err != nil {
return xerrors.Errorf("get user: %w", err)
}
@@ -293,6 +311,11 @@ func (r *RootCmd) login() *clibase.Cmd {
Description: "Specifies whether a trial license should be provisioned for the Coder deployment or not.",
Value: clibase.BoolOf(&trial),
},
{
Flag: "use-token-as-session",
Description: "By default, the CLI will generate a new session token when logging in. This flag will instead use the provided token as the session token.",
Value: clibase.BoolOf(&useTokenForSession),
},
}
return cmd
}
+3 -1
View File
@@ -197,6 +197,7 @@ func TestLogin(t *testing.T) {
<-doneChan
})
// TokenFlag should generate a new session token and store it in the session file.
t.Run("TokenFlag", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
@@ -206,6 +207,7 @@ func TestLogin(t *testing.T) {
require.NoError(t, err)
sessionFile, err := cfg.Session().Read()
require.NoError(t, err)
require.Equal(t, client.SessionToken(), sessionFile)
// This **should not be equal** to the token we passed in.
require.NotEqual(t, client.SessionToken(), sessionFile)
})
}
+4
View File
@@ -19,5 +19,9 @@ Authenticate with Coder deployment
Specifies a username to use if creating the first user for the
deployment.
--use-token-as-session bool
By default, the CLI will generate a new session token when logging in.
This flag will instead use the provided token as the session token.
---
Run `coder --help` for a list of global options.
+4 -1
View File
@@ -78,7 +78,10 @@ func (c *Client) CreateToken(ctx context.Context, userID string, req CreateToken
}
// CreateAPIKey generates an API key for the user ID provided.
// DEPRECATED: use CreateToken instead.
// CreateToken should be used over CreateAPIKey. CreateToken allows better
// tracking of the token's usage and allows for custom expiration.
// Only use CreateAPIKey if you want to emulate the session created for
// a browser like login.
func (c *Client) CreateAPIKey(ctx context.Context, user string) (GenerateAPIKeyResponse, error) {
res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/keys", user), nil)
if err != nil {
+8
View File
@@ -47,3 +47,11 @@ Specifies whether a trial license should be provisioned for the Coder deployment
| Environment | <code>$CODER_FIRST_USER_USERNAME</code> |
Specifies a username to use if creating the first user for the deployment.
### --use-token-as-session
| | |
| ---- | ----------------- |
| Type | <code>bool</code> |
By default, the CLI will generate a new session token when logging in. This flag will instead use the provided token as the session token.