From a9fc2ea0b7da903dd5ddb2def64391cf2fa0bb0c Mon Sep 17 00:00:00 2001 From: Pavel Brm <5097196+pavelbrm@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:06:57 +1200 Subject: [PATCH] fix: use leo annual issuer if redemption fails for regular leo (#2647) * fix: use leo annual issuer if redemption fails for regilar leo * fix: use original issuer as payload --- services/skus/controllers.go | 7 ++-- services/skus/service.go | 62 ++++++++++++++++++++---------------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/services/skus/controllers.go b/services/skus/controllers.go index c02ac4097..48957e830 100644 --- a/services/skus/controllers.go +++ b/services/skus/controllers.go @@ -943,14 +943,13 @@ func MerchantTransactions(service *Service) handlers.AppHandler { }) } -// VerifyCredentialV2 - version 2 of verify credential func VerifyCredentialV2(service *Service) handlers.AppHandler { return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - l := logging.Logger(ctx, "VerifyCredentialV2") - var req = new(VerifyCredentialRequestV2) + l := logging.Logger(ctx, "skus").With().Str("func", "VerifyCredentialV2").Logger() + + req := &VerifyCredentialRequestV2{} if err := inputs.DecodeAndValidateReader(ctx, req, r.Body); err != nil { l.Error().Err(err).Msg("failed to read request") return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) diff --git a/services/skus/service.go b/services/skus/service.go index 7f439730e..54328a9a5 100644 --- a/services/skus/service.go +++ b/services/skus/service.go @@ -1415,32 +1415,21 @@ func (s *Service) verifyCredential(ctx context.Context, cred credential, w http. return handlers.WrapError(err, "Error getting auth merchant", http.StatusInternalServerError) } - logger.Debug().Str("merchant", merchant).Msg("got merchant from the context") - caveats := caveatsFromCtx(ctx) - if cred.GetMerchantID(ctx) != merchant { - logger.Warn(). - Str("req.MerchantID", cred.GetMerchantID(ctx)). - Str("merchant", merchant). - Msg("merchant does not match the key's merchant") + if merchID := cred.GetMerchantID(ctx); merchID != merchant { + logger.Warn().Str("req.MerchantID", merchID).Str("merchant", merchant).Msg("merchant does not match the key's merchant") return handlers.WrapError(nil, "Verify request merchant does not match authentication", http.StatusForbidden) } - logger.Debug().Str("merchant", merchant).Msg("merchant matches the key's merchant") - if caveats != nil { if sku, ok := caveats["sku"]; ok { - if cred.GetSku(ctx) != sku { - logger.Warn(). - Str("req.SKU", cred.GetSku(ctx)). - Str("sku", sku). - Msg("sku caveat does not match") + if csku := cred.GetSku(ctx); csku != sku { + logger.Warn().Str("req.SKU", csku).Str("sku", sku).Msg("sku caveat does not match") return handlers.WrapError(nil, "Verify request sku does not match authentication", http.StatusForbidden) } } } - logger.Debug().Msg("caveats validated") kind := cred.GetType(ctx) switch kind { @@ -1958,27 +1947,22 @@ func (s *Service) redeemBlindedCred(ctx context.Context, w http.ResponseWriter, // FIXME: we shouldn't be using the issuer as the payload, it ideally would be a unique request identifier // to allow for more flexible idempotent behavior. if err := redeemFn(ctx, cred.Issuer, cred.TokenPreimage, cred.Signature, cred.Issuer); err != nil { - msg := err.Error() - - // Time limited v2: Expose a credential id so the caller can decide whether to allow multiple redemptions. - if kind == timeLimitedV2 && msg == cbr.ErrDupRedeem.Error() { - data := &blindedCredVrfResult{ID: cred.TokenPreimage, Duplicate: true} - - return handlers.RenderContent(ctx, data, w, http.StatusOK) + if !shouldRetryRedeemFn(kind, cred.Issuer, err) { + return handleRedeemFnError(ctx, w, kind, cred, err) } - // Duplicate redemptions are not verified. - if msg == cbr.ErrDupRedeem.Error() || msg == cbr.ErrBadRequest.Error() { - return handlers.WrapError(err, "invalid credentials", http.StatusForbidden) + // Fix for https://github.com/brave-intl/challenge-bypass-server/pull/371. + const leoa = "brave.com?sku=brave-leo-premium-year" + if err := redeemFn(ctx, leoa, cred.TokenPreimage, cred.Signature, cred.Issuer); err != nil { + return handleRedeemFnError(ctx, w, kind, cred, err) } - - return handlers.WrapError(err, "Error verifying credentials", http.StatusInternalServerError) } // TODO(clD11): cleanup after quick fix if kind == timeLimitedV2 { return handlers.RenderContent(ctx, &blindedCredVrfResult{ID: cred.TokenPreimage}, w, http.StatusOK) } + return handlers.RenderContent(ctx, "Credentials successfully verified", w, http.StatusOK) } @@ -2575,3 +2559,27 @@ func buildStripeLineItems(items []model.OrderItem) []*stripe.CheckoutSessionLine return result } + +func handleRedeemFnError(ctx context.Context, w http.ResponseWriter, kind string, cred *cbr.CredentialRedemption, err error) *handlers.AppError { + msg := err.Error() + + // Time limited v2: Expose a credential id so the caller can decide whether to allow multiple redemptions. + if kind == timeLimitedV2 && msg == cbr.ErrDupRedeem.Error() { + data := &blindedCredVrfResult{ID: cred.TokenPreimage, Duplicate: true} + + return handlers.RenderContent(ctx, data, w, http.StatusOK) + } + + // Duplicate redemptions are not verified. + if msg == cbr.ErrDupRedeem.Error() || msg == cbr.ErrBadRequest.Error() { + return handlers.WrapError(err, "invalid credentials", http.StatusForbidden) + } + + return handlers.WrapError(err, "Error verifying credentials", http.StatusInternalServerError) +} + +func shouldRetryRedeemFn(kind, issuer string, err error) bool { + const leo = "brave.com?sku=brave-leo-premium" + + return kind == timeLimitedV2 && issuer == leo && err.Error() == cbr.ErrBadRequest.Error() +}