Organize backend into product executable and shared Go modules
This commit is contained in:
1 parent
981e84a4b4
commit
f0720ec66b
382 files changed
+1944
-6989
No files matched your search
@@ -0,0 +1,106 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
customermodel "git.sendico.io/tech/sxchnge/product/customers/model"
|
||||
sessionmodel "git.sendico.io/tech/sxchnge/product/sessions/model"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.sendico.io/tech/sxchnge/product/auth/request"
|
||||
"git.sendico.io/tech/sxchnge/product/auth/response"
|
||||
"git.sendico.io/tech/sxchnge/product/sessions/browser"
|
||||
"git.sendico.io/tech/sxchnge/shared/errors"
|
||||
"git.sendico.io/tech/sxchnge/shared/logging/fields"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var errClientIPNotAllowed = merrors.Define("client_ip_not_allowed")
|
||||
|
||||
func isExpectedRefreshValidationError(err error) bool {
|
||||
return errors.Is(err, merrors.ErrUnauthorized)
|
||||
}
|
||||
|
||||
func validateToken(token string, rt *sessionmodel.RefreshToken) string {
|
||||
if rt.AccountRef == nil {
|
||||
return "missing account reference"
|
||||
}
|
||||
if token != rt.RefreshToken {
|
||||
return "tokens do not match"
|
||||
}
|
||||
if rt.ExpiresAt.Before(time.Now()) {
|
||||
return "token expired"
|
||||
}
|
||||
if rt.IsRevoked {
|
||||
return "token has been revoked"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (pr *PublicRouter) validateRefreshToken(ctx context.Context, r *http.Request, req *srequest.TokenRefreshRotate) (*customermodel.Account, *sresponse.TokenData, *sessionmodel.RefreshToken, error) {
|
||||
credential, err := browser.RefreshCredential(r, req.RefreshToken)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
req.RefreshToken = credential
|
||||
client, err := pr.rtdb.GetClient(ctx, req.ClientID)
|
||||
if errors.Is(err, merrors.ErrNoData) || client == nil {
|
||||
pr.logger.Debug("Refresh token rejected: client not found", zap.String("client_id", req.ClientID))
|
||||
return nil, nil, nil, merrors.Unauthorized("client not found")
|
||||
}
|
||||
if err != nil {
|
||||
pr.logger.Warn("Failed to fetch client for refresh token validation", zap.Error(err), zap.String("client_id", req.ClientID))
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if client.IsRevoked {
|
||||
return nil, nil, nil, merrors.Unauthorized("client has been revoked")
|
||||
}
|
||||
if h := pr.validateClientIPPolicy(r, req.ClientID, client); h != nil {
|
||||
return nil, nil, nil, errClientIPNotAllowed
|
||||
}
|
||||
|
||||
rt, err := pr.rtdb.GetByCRT(ctx, req)
|
||||
if errors.Is(err, merrors.ErrNoData) || errors.Is(err, merrors.ErrAccessDenied) {
|
||||
pr.logger.Debug("Refresh session is inactive", zap.String("client_id", req.ClientID), zap.String("device_id", req.DeviceID))
|
||||
return nil, nil, nil, merrors.Unauthorized("refresh session is inactive")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if rt.SessionVersion == "" {
|
||||
pr.logger.Debug("Refresh token rejected: legacy session requires reauthentication",
|
||||
zap.String("client_id", req.ClientID), zap.String("device_id", req.DeviceID))
|
||||
return nil, nil, nil, merrors.Unauthorized("session reauthentication required")
|
||||
}
|
||||
|
||||
if reason := validateToken(req.RefreshToken, rt); len(reason) > 0 {
|
||||
pr.logger.Debug("Token validation failed", zap.String("reason", reason))
|
||||
return nil, nil, nil, merrors.Unauthorized(reason)
|
||||
}
|
||||
|
||||
var account customermodel.Account
|
||||
if err := pr.db.Get(ctx, *rt.AccountRef, &account); errors.Is(err, merrors.ErrNoData) {
|
||||
pr.logger.Debug("User not found while rotating refresh token", zap.Error(err), mzap.AccRef(*rt.AccountRef))
|
||||
return nil, nil, nil, merrors.Unauthorized("user not found")
|
||||
} else if err != nil {
|
||||
pr.logger.Warn("Failed to fetch user while validating refresh token", zap.Error(err), mzap.AccRef(*rt.AccountRef))
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if !account.IsActive() || account.IsArchived() {
|
||||
return nil, nil, nil, merrors.Unauthorized("customer is inactive")
|
||||
}
|
||||
if client.AccountRef != nil && *client.AccountRef != account.ID {
|
||||
return nil, nil, nil, merrors.Unauthorized("client account mismatch")
|
||||
}
|
||||
|
||||
accessToken, err := pr.imp.CreateAccessTokenForSession(&account, rt.SessionIdentifier)
|
||||
if err != nil {
|
||||
pr.logger.Warn("Failed to generate access token", zap.Error(err))
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return &account, &accessToken, rt, nil
|
||||
}
|
||||
Reference in new issue
Block a user