Skip to content

Commit 6c50b85

Browse files
committed
Create initial route for generating an API key
1 parent 35291d3 commit 6c50b85

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

coderd/coderd.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ func New(options *Options) http.Handler {
3636
})
3737
r.Post("/login", api.postLogin)
3838
r.Post("/logout", api.postLogout)
39+
r.Route("/api-keys", func(r chi.Router) {
40+
r.Use(
41+
httpmw.ExtractAPIKey(options.Database, nil),
42+
)
43+
r.Post("/", api.postApiKey)
44+
})
45+
3946
// Used for setup.
4047
r.Get("/user", api.user)
4148
r.Post("/user", api.postUser)

coderd/users.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ type LoginWithPasswordResponse struct {
5555
SessionToken string `json:"session_token" validate:"required"`
5656
}
5757

58+
// GenerateAPIKeyResponse contains an API key for a user.
59+
type GenerateAPIKeyResponse struct {
60+
Key string `json:"key"`
61+
}
62+
5863
// Returns whether the initial user has been created or not.
5964
func (api *api) user(rw http.ResponseWriter, r *http.Request) {
6065
userCount, err := api.Database.GetUserCount(r.Context())
@@ -312,6 +317,43 @@ func (api *api) postLogin(rw http.ResponseWriter, r *http.Request) {
312317
})
313318
}
314319

320+
// Creates a new API key, used for logging in via the CLI
321+
func (api *api) postApiKey(rw http.ResponseWriter, r *http.Request) {
322+
apiKey := httpmw.APIKey(r)
323+
userID := apiKey.UserID
324+
325+
keyID, keySecret, err := generateAPIKeyIDSecret()
326+
if err != nil {
327+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
328+
Message: fmt.Sprintf("generate api key parts: %s", err.Error()),
329+
})
330+
return
331+
}
332+
hashed := sha256.Sum256([]byte(keySecret))
333+
334+
_, err = api.Database.InsertAPIKey(r.Context(), database.InsertAPIKeyParams{
335+
ID: keyID,
336+
UserID: userID,
337+
ExpiresAt: database.Now().AddDate(1, 0, 0), // Expire after 1 year (same as v1)
338+
CreatedAt: database.Now(),
339+
UpdatedAt: database.Now(),
340+
HashedSecret: hashed[:],
341+
LoginType: database.LoginTypeBuiltIn,
342+
})
343+
if err != nil {
344+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
345+
Message: fmt.Sprintf("insert api key: %s", err.Error()),
346+
})
347+
return
348+
}
349+
350+
// This format is consumed by the APIKey middleware.
351+
generatedApiKey := fmt.Sprintf("%s-%s", keyID, keySecret)
352+
353+
render.Status(r, http.StatusCreated)
354+
render.JSON(rw, r, GenerateAPIKeyResponse{Key: generatedApiKey})
355+
}
356+
315357
// Clear the user's session cookie
316358
func (*api) postLogout(rw http.ResponseWriter, r *http.Request) {
317359
// Get a blank token cookie

0 commit comments

Comments
 (0)