@@ -142,6 +142,56 @@ func ExtractAPIKeyMW(cfg ExtractAPIKeyConfig) func(http.Handler) http.Handler {
142
142
}
143
143
}
144
144
145
+ func APIKeyFromRequest (ctx context.Context , db database.Store , sessionTokenFunc func (r * http.Request ) string , r * http.Request ) (* database.APIKey , codersdk.Response , bool ) {
146
+ tokenFunc := APITokenFromRequest
147
+ if sessionTokenFunc != nil {
148
+ tokenFunc = sessionTokenFunc
149
+ }
150
+
151
+ token := tokenFunc (r )
152
+ if token == "" {
153
+ return nil , codersdk.Response {
154
+ Message : SignedOutErrorMessage ,
155
+ Detail : fmt .Sprintf ("Cookie %q or query parameter must be provided." , codersdk .SessionTokenCookie ),
156
+ }, false
157
+ }
158
+
159
+ keyID , keySecret , err := SplitAPIToken (token )
160
+ if err != nil {
161
+ return nil , codersdk.Response {
162
+ Message : SignedOutErrorMessage ,
163
+ Detail : "Invalid API key format: " + err .Error (),
164
+ }, false
165
+ }
166
+
167
+ //nolint:gocritic // System needs to fetch API key to check if it's valid.
168
+ key , err := db .GetAPIKeyByID (dbauthz .AsSystemRestricted (ctx ), keyID )
169
+ if err != nil {
170
+ if errors .Is (err , sql .ErrNoRows ) {
171
+ return nil , codersdk.Response {
172
+ Message : SignedOutErrorMessage ,
173
+ Detail : "API key is invalid." ,
174
+ }, false
175
+ }
176
+
177
+ return nil , codersdk.Response {
178
+ Message : internalErrorMessage ,
179
+ Detail : fmt .Sprintf ("Internal error fetching API key by id. %s" , err .Error ()),
180
+ }, false
181
+ }
182
+
183
+ // Checking to see if the secret is valid.
184
+ hashedSecret := sha256 .Sum256 ([]byte (keySecret ))
185
+ if subtle .ConstantTimeCompare (key .HashedSecret , hashedSecret [:]) != 1 {
186
+ return nil , codersdk.Response {
187
+ Message : SignedOutErrorMessage ,
188
+ Detail : "API key secret is invalid." ,
189
+ }, false
190
+ }
191
+
192
+ return & key , codersdk.Response {}, true
193
+ }
194
+
145
195
// ExtractAPIKey requires authentication using a valid API key. It handles
146
196
// extending an API key if it comes close to expiry, updating the last used time
147
197
// in the database.
@@ -179,49 +229,9 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
179
229
return nil , nil , false
180
230
}
181
231
182
- tokenFunc := APITokenFromRequest
183
- if cfg .SessionTokenFunc != nil {
184
- tokenFunc = cfg .SessionTokenFunc
185
- }
186
- token := tokenFunc (r )
187
- if token == "" {
188
- return optionalWrite (http .StatusUnauthorized , codersdk.Response {
189
- Message : SignedOutErrorMessage ,
190
- Detail : fmt .Sprintf ("Cookie %q or query parameter must be provided." , codersdk .SessionTokenCookie ),
191
- })
192
- }
193
-
194
- keyID , keySecret , err := SplitAPIToken (token )
195
- if err != nil {
196
- return optionalWrite (http .StatusUnauthorized , codersdk.Response {
197
- Message : SignedOutErrorMessage ,
198
- Detail : "Invalid API key format: " + err .Error (),
199
- })
200
- }
201
-
202
- //nolint:gocritic // System needs to fetch API key to check if it's valid.
203
- key , err := cfg .DB .GetAPIKeyByID (dbauthz .AsSystemRestricted (ctx ), keyID )
204
- if err != nil {
205
- if errors .Is (err , sql .ErrNoRows ) {
206
- return optionalWrite (http .StatusUnauthorized , codersdk.Response {
207
- Message : SignedOutErrorMessage ,
208
- Detail : "API key is invalid." ,
209
- })
210
- }
211
-
212
- return write (http .StatusInternalServerError , codersdk.Response {
213
- Message : internalErrorMessage ,
214
- Detail : fmt .Sprintf ("Internal error fetching API key by id. %s" , err .Error ()),
215
- })
216
- }
217
-
218
- // Checking to see if the secret is valid.
219
- hashedSecret := sha256 .Sum256 ([]byte (keySecret ))
220
- if subtle .ConstantTimeCompare (key .HashedSecret , hashedSecret [:]) != 1 {
221
- return optionalWrite (http .StatusUnauthorized , codersdk.Response {
222
- Message : SignedOutErrorMessage ,
223
- Detail : "API key secret is invalid." ,
224
- })
232
+ key , resp , ok := APIKeyFromRequest (ctx , cfg .DB , cfg .SessionTokenFunc , r )
233
+ if ! ok {
234
+ return optionalWrite (http .StatusUnauthorized , resp )
225
235
}
226
236
227
237
var (
@@ -232,7 +242,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
232
242
)
233
243
if key .LoginType == database .LoginTypeGithub || key .LoginType == database .LoginTypeOIDC {
234
244
//nolint:gocritic // System needs to fetch UserLink to check if it's valid.
235
- link , err = cfg .DB .GetUserLinkByUserIDLoginType (dbauthz .AsSystemRestricted (ctx ), database.GetUserLinkByUserIDLoginTypeParams {
245
+ link , err : = cfg .DB .GetUserLinkByUserIDLoginType (dbauthz .AsSystemRestricted (ctx ), database.GetUserLinkByUserIDLoginTypeParams {
236
246
UserID : key .UserID ,
237
247
LoginType : key .LoginType ,
238
248
})
@@ -427,7 +437,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
427
437
}.WithCachedASTValue (),
428
438
}
429
439
430
- return & key , & authz , true
440
+ return key , & authz , true
431
441
}
432
442
433
443
// APITokenFromRequest returns the api token from the request.
0 commit comments