package usecase import ( "context" "errors" "time" memberdomain "gateway/internal/model/member/domain" domrepo "gateway/internal/model/member/domain/repository" domusecase "gateway/internal/model/member/domain/usecase" memberconfig "gateway/internal/model/member/config" "github.com/google/uuid" ) type stepUpUseCase struct { store domrepo.StepUpStore config memberconfig.Config } // StepUpUseCaseParam wires StepUpUseCase. type StepUpUseCaseParam struct { Store domrepo.StepUpStore Config memberconfig.Config } // MustStepUpUseCase constructs StepUpUseCase. func MustStepUpUseCase(param StepUpUseCaseParam) domusecase.StepUpUseCase { if param.Store == nil { panic("member: step-up store is required") } return &stepUpUseCase{store: param.Store, config: param.Config.Defaults()} } func (uc *stepUpUseCase) Issue(ctx context.Context, req *domusecase.IssueStepUpRequest) (*domusecase.StepUpView, error) { if req == nil || req.TenantID == "" || req.UID == "" { return nil, errb.InputMissingRequired("tenant_id, uid and purpose are required") } if !req.Purpose.Valid() { return nil, errb.InputInvalidFormat("unsupported step-up purpose") } ttl := req.TTL if ttl <= 0 { ttl = time.Duration(uc.config.TOTP.StepUpTTLSeconds) * time.Second } tokenID := uuid.NewString() if err := uc.store.Save(ctx, &domrepo.StepUpSession{ TokenID: tokenID, TenantID: req.TenantID, UID: req.UID, Purpose: req.Purpose.String(), }, ttl); err != nil { return nil, wrapRepoErr(err, "save step-up session failed") } return &domusecase.StepUpView{ StepUpToken: tokenID, ExpiresIn: int(ttl.Seconds()), }, nil } func (uc *stepUpUseCase) Consume(ctx context.Context, req *domusecase.ConsumeStepUpRequest) error { if req == nil || req.TokenID == "" || req.TenantID == "" || req.UID == "" { return errb.InputMissingRequired("step_up_token, tenant_id and uid are required") } if !req.Purpose.Valid() { return errb.InputInvalidFormat("unsupported step-up purpose") } session, err := uc.store.Get(ctx, req.TokenID) if err != nil { if errors.Is(err, memberdomain.ErrStepUpNotFound) { return errb.ResNotFound("step-up session", req.TokenID).WithCause(err) } return wrapRepoErr(err, "read step-up session failed") } if session.TenantID != req.TenantID || session.UID != req.UID || session.Purpose != req.Purpose.String() { return errb.AuthForbidden("step-up token mismatch") } if err := uc.store.Delete(ctx, req.TokenID); err != nil { return wrapRepoErr(err, "delete step-up session failed") } return nil } var _ domusecase.StepUpUseCase = (*stepUpUseCase)(nil)