MCPcopy Index your code
hub / github.com/coder/coder / postRequestOneTimePasscode

Method postRequestOneTimePasscode

coderd/userauth.go:230–308  ·  view source on GitHub ↗

Requests a one-time passcode for a user. @Summary Request one-time passcode @ID request-one-time-passcode @Accept json @Tags Authorization @Param request body codersdk.RequestOneTimePasscodeRequest true "One-time passcode request" @Success 204 @Router /api/v2/users/otp/request [post]

(rw http.ResponseWriter, r *http.Request)

Source from the content-addressed store, hash-verified

228// @Success 204
229// @Router /api/v2/users/otp/request [post]
230func (api *API) postRequestOneTimePasscode(rw http.ResponseWriter, r *http.Request) {
231 var (
232 ctx = r.Context()
233 auditor = api.Auditor.Load()
234 logger = api.Logger.Named(userAuthLoggerName)
235 aReq, commitAudit = audit.InitRequest[database.User](rw, &audit.RequestParams{
236 Audit: *auditor,
237 Log: api.Logger,
238 Request: r,
239 Action: database.AuditActionRequestPasswordReset,
240 })
241 )
242 defer commitAudit()
243
244 if api.DeploymentValues.DisablePasswordAuth {
245 httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{
246 Message: "Password authentication is disabled.",
247 })
248 return
249 }
250
251 var req codersdk.RequestOneTimePasscodeRequest
252 if !httpapi.Read(ctx, rw, r, &req) {
253 return
254 }
255
256 defer func() {
257 // We always send the same response. If we give a more detailed response
258 // it would open us up to an enumeration attack.
259 rw.WriteHeader(http.StatusNoContent)
260 }()
261
262 //nolint:gocritic // In order to request a one-time passcode, we need to get the user first - and can only do that in the system auth context.
263 user, err := api.Database.GetUserByEmailOrUsername(dbauthz.AsSystemRestricted(ctx), database.GetUserByEmailOrUsernameParams{
264 Email: req.Email,
265 })
266 if err != nil && !errors.Is(err, sql.ErrNoRows) {
267 logger.Error(ctx, "unable to get user by email", slog.Error(err))
268 return
269 }
270 // We continue if err == sql.ErrNoRows to help prevent a timing-based attack.
271 aReq.Old = user
272 aReq.UserID = user.ID
273
274 passcode := uuid.New()
275 passcodeExpiresAt := dbtime.Now().Add(api.OneTimePasscodeValidityPeriod)
276
277 hashedPasscode, err := userpassword.Hash(passcode.String())
278 if err != nil {
279 logger.Error(ctx, "unable to hash passcode", slog.Error(err))
280 return
281 }
282
283 //nolint:gocritic // We need the system auth context to be able to save the one-time passcode.
284 err = api.Database.UpdateUserHashedOneTimePasscode(dbauthz.AsSystemRestricted(ctx), database.UpdateUserHashedOneTimePasscodeParams{
285 ID: user.ID,
286 HashedOneTimePasscode: []byte(hashedPasscode),
287 OneTimePasscodeExpiresAt: sql.NullTime{Time: passcodeExpiresAt, Valid: true},

Callers

nothing calls this directly

Calls 15

InitRequestFunction · 0.92
WriteFunction · 0.92
ReadFunction · 0.92
AsSystemRestrictedFunction · 0.92
NowFunction · 0.92
HashFunction · 0.92
NamedMethod · 0.80
ContextMethod · 0.65
NewMethod · 0.65
AddMethod · 0.65

Tested by

no test coverage detected