Skip to content

Commit 9a7705c

Browse files
authored
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
1 parent 88c35d3 commit 9a7705c

File tree

5 files changed

+51
-11
lines changed

5 files changed

+51
-11
lines changed

cli/login.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,18 @@ func (r *RootCmd) login() *clibase.Cmd {
4141
const firstUserTrialEnv = "CODER_FIRST_USER_TRIAL"
4242

4343
var (
44-
email string
45-
username string
46-
password string
47-
trial bool
44+
email string
45+
username string
46+
password string
47+
trial bool
48+
useTokenForSession bool
4849
)
4950
cmd := &clibase.Cmd{
5051
Use: "login <url>",
5152
Short: "Authenticate with Coder deployment",
5253
Middleware: clibase.RequireRangeArgs(0, 1),
5354
Handler: func(inv *clibase.Invocation) error {
55+
ctx := inv.Context()
5456
rawURL := ""
5557
if len(inv.Args) == 0 {
5658
rawURL = r.clientURL.String()
@@ -89,7 +91,7 @@ func (r *RootCmd) login() *clibase.Cmd {
8991
_, _ = fmt.Fprintln(inv.Stderr, cliui.DefaultStyles.Warn.Render(err.Error()))
9092
}
9193

92-
hasInitialUser, err := client.HasFirstUser(inv.Context())
94+
hasInitialUser, err := client.HasFirstUser(ctx)
9395
if err != nil {
9496
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)
9597
}
@@ -182,7 +184,7 @@ func (r *RootCmd) login() *clibase.Cmd {
182184
trial = v == "yes" || v == "y"
183185
}
184186

185-
_, err = client.CreateFirstUser(inv.Context(), codersdk.CreateFirstUserRequest{
187+
_, err = client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{
186188
Email: email,
187189
Username: username,
188190
Password: password,
@@ -191,7 +193,7 @@ func (r *RootCmd) login() *clibase.Cmd {
191193
if err != nil {
192194
return xerrors.Errorf("create initial user: %w", err)
193195
}
194-
resp, err := client.LoginWithPassword(inv.Context(), codersdk.LoginWithPasswordRequest{
196+
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
195197
Email: email,
196198
Password: password,
197199
})
@@ -235,7 +237,7 @@ func (r *RootCmd) login() *clibase.Cmd {
235237
Secret: true,
236238
Validate: func(token string) error {
237239
client.SetSessionToken(token)
238-
_, err := client.User(inv.Context(), codersdk.Me)
240+
_, err := client.User(ctx, codersdk.Me)
239241
if err != nil {
240242
return xerrors.New("That's not a valid token!")
241243
}
@@ -245,11 +247,27 @@ func (r *RootCmd) login() *clibase.Cmd {
245247
if err != nil {
246248
return xerrors.Errorf("paste token prompt: %w", err)
247249
}
250+
} else if !useTokenForSession {
251+
// If a session token is provided on the cli, use it to generate
252+
// a new one. This is because the cli `--token` flag provides
253+
// a token for the command being invoked. We should not store
254+
// this token, and `/logout` should not delete it.
255+
// /login should generate a new token and store that.
256+
client.SetSessionToken(sessionToken)
257+
// Use CreateAPIKey over CreateToken because this is a session
258+
// key that should not show on the `tokens` page. This should
259+
// match the same behavior of the `/cli-auth` page for generating
260+
// a session token.
261+
key, err := client.CreateAPIKey(ctx, "me")
262+
if err != nil {
263+
return xerrors.Errorf("create api key: %w", err)
264+
}
265+
sessionToken = key.Key
248266
}
249267

250268
// Login to get user data - verify it is OK before persisting
251269
client.SetSessionToken(sessionToken)
252-
resp, err := client.User(inv.Context(), codersdk.Me)
270+
resp, err := client.User(ctx, codersdk.Me)
253271
if err != nil {
254272
return xerrors.Errorf("get user: %w", err)
255273
}
@@ -293,6 +311,11 @@ func (r *RootCmd) login() *clibase.Cmd {
293311
Description: "Specifies whether a trial license should be provisioned for the Coder deployment or not.",
294312
Value: clibase.BoolOf(&trial),
295313
},
314+
{
315+
Flag: "use-token-as-session",
316+
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.",
317+
Value: clibase.BoolOf(&useTokenForSession),
318+
},
296319
}
297320
return cmd
298321
}

cli/login_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ func TestLogin(t *testing.T) {
197197
<-doneChan
198198
})
199199

200+
// TokenFlag should generate a new session token and store it in the session file.
200201
t.Run("TokenFlag", func(t *testing.T) {
201202
t.Parallel()
202203
client := coderdtest.New(t, nil)
@@ -206,6 +207,7 @@ func TestLogin(t *testing.T) {
206207
require.NoError(t, err)
207208
sessionFile, err := cfg.Session().Read()
208209
require.NoError(t, err)
209-
require.Equal(t, client.SessionToken(), sessionFile)
210+
// This **should not be equal** to the token we passed in.
211+
require.NotEqual(t, client.SessionToken(), sessionFile)
210212
})
211213
}

cli/testdata/coder_login_--help.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,9 @@ Authenticate with Coder deployment
1919
Specifies a username to use if creating the first user for the
2020
deployment.
2121

22+
--use-token-as-session bool
23+
By default, the CLI will generate a new session token when logging in.
24+
This flag will instead use the provided token as the session token.
25+
2226
---
2327
Run `coder --help` for a list of global options.

codersdk/apikey.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ func (c *Client) CreateToken(ctx context.Context, userID string, req CreateToken
7878
}
7979

8080
// CreateAPIKey generates an API key for the user ID provided.
81-
// DEPRECATED: use CreateToken instead.
81+
// CreateToken should be used over CreateAPIKey. CreateToken allows better
82+
// tracking of the token's usage and allows for custom expiration.
83+
// Only use CreateAPIKey if you want to emulate the session created for
84+
// a browser like login.
8285
func (c *Client) CreateAPIKey(ctx context.Context, user string) (GenerateAPIKeyResponse, error) {
8386
res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/keys", user), nil)
8487
if err != nil {

docs/cli/login.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,11 @@ Specifies whether a trial license should be provisioned for the Coder deployment
4747
| Environment | <code>$CODER_FIRST_USER_USERNAME</code> |
4848

4949
Specifies a username to use if creating the first user for the deployment.
50+
51+
### --use-token-as-session
52+
53+
| | |
54+
| ---- | ----------------- |
55+
| Type | <code>bool</code> |
56+
57+
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.

0 commit comments

Comments
 (0)