package verificationimp import ( "encoding/json" "net/http" "strings" "github.com/tech/sendico/pkg/api/http/response" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mutil/mzap" "github.com/tech/sendico/server/interface/api/sresponse" emodel "github.com/tech/sendico/server/interface/model" rtokens "github.com/tech/sendico/server/internal/api/routers/tokens" mutil "github.com/tech/sendico/server/internal/mutil/verification" "go.uber.org/zap" ) func (a *VerificationAPI) verifyCode(r *http.Request, account *model.Account, token *emodel.AccountToken) http.HandlerFunc { var req codeVerificationRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { a.logger.Warn("Failed to decode confirmation verification request", zap.Error(err)) return response.BadPayload(a.logger, a.Name(), err) } purpose, err := model.VPFromString(req.Purpose) if err != nil { return response.BadRequest(a.logger, a.Name(), "invalid_target", err.Error()) } code := strings.TrimSpace(req.Code) if code == "" { return response.BadRequest(a.logger, a.Name(), "missing_code", "confirmation code is required") } target := a.resolveTarget(req.Target, account) if target == "" { return response.BadRequest(a.logger, a.Name(), "missing_destination", "email destination is required") } dst, err := a.store.Verify(r.Context(), account.ID, purpose, code) if err != nil { a.logger.Debug("Code verification failed", zap.Error(err), mzap.AccRef(account.ID), zap.String("purpose", req.Purpose), ) return mutil.MapTokenErrorToResponse(a.logger, a.Name(), err) } if dst != target { a.logger.Warn("Verification code destination mismatch", zap.String("expected", target), zap.String("actual", dst), mzap.AccRef(account.ID)) return response.DataConflict(a.logger, a.Name(), "the provided code does not match the expected destination") } a.logger.Info("Confirmation code verified", zap.String("purpose", req.Purpose), mzap.AccRef(account.ID)) if purpose == model.PurposeLogin { if req.SessionIdentifier.ClientID == "" || req.SessionIdentifier.DeviceID == "" { return response.BadRequest(a.logger, a.Name(), "missing_session", "session identifier is required") } accessToken, err := a.createAccessToken(account, req.SessionIdentifier.ClientID) if err != nil { a.logger.Warn("Failed to generate access token", zap.Error(err)) return response.Internal(a.logger, a.Name(), err) } refreshToken, err := rtokens.PrepareRefreshToken( r.Context(), r, a.rtdb, a.tokenConfig.Length, a.tokenConfig.Expiration.Refresh, &req.SessionIdentifier, account, a.logger, ) if err != nil { a.logger.Warn("Failed to generate refresh token", zap.Error(err)) return response.Internal(a.logger, a.Name(), err) } rt := sresponse.TokenData{ Token: refreshToken.RefreshToken, Expiration: refreshToken.ExpiresAt, } return sresponse.Login(a.logger, account, &accessToken, &rt) } return response.Success(a.logger) }