yao/openapi/otp/generate.go
Max 37ab2bc84b Enhance user login functionality with options for customization
- Introduce `LoginWithOptions` method to allow for customizable login flows, enabling overrides for scopes, token expiration, and refresh token issuance.
- Add `LoginOptions` struct to encapsulate optional parameters for login, improving flexibility in user authentication.
- Update token handling in `issueTokens` to accommodate new options, ensuring proper management of access and refresh tokens based on user preferences.
- Integrate OTP service initialization into the OpenAPI server setup for enhanced authentication capabilities.
2026-02-21 21:38:39 +08:00

70 lines
1.7 KiB
Go

package otp
import (
"fmt"
nanoid "github.com/matoous/go-nanoid/v2"
)
// Create generates a new OTP code, stores the payload, and returns the code.
// It validates required fields and retries on NanoID collision.
func (s *Service) Create(params *GenerateParams) (string, error) {
if err := validateCreateParams(params); err != nil {
return "", err
}
data := map[string]interface{}{
"redirect": params.Redirect,
"consume": params.Consume,
}
if params.TeamID != "" {
data["team_id"] = params.TeamID
}
if params.MemberID != "" {
data["member_id"] = params.MemberID
}
if params.UserID != "" {
data["user_id"] = params.UserID
}
if params.Scope != "" {
data["scope"] = params.Scope
}
if params.TokenExpiresIn != 0 {
data["token_expires_in"] = params.TokenExpiresIn
}
for i := 0; i < maxCollisionRetry; i++ {
code, err := nanoid.Generate(codeAlphabet, codeLength)
if err != nil {
return "", fmt.Errorf("failed to generate OTP code: %w", err)
}
key := s.storeKey(code)
if s.store.Has(key) {
continue
}
if err := s.store.Set(key, data, ttl(params.ExpiresIn)); err != nil {
return "", fmt.Errorf("failed to store OTP code: %w", err)
}
return code, nil
}
return "", fmt.Errorf("failed to generate unique OTP code after %d attempts", maxCollisionRetry)
}
func validateCreateParams(p *GenerateParams) error {
if p == nil {
return fmt.Errorf("params is required")
}
if p.UserID == "" && p.MemberID == "" {
return fmt.Errorf("user_id or member_id is required")
}
if p.MemberID != "" && p.TeamID == "" {
return fmt.Errorf("team_id is required when member_id is set")
}
if p.Redirect == "" {
return fmt.Errorf("redirect is required")
}
return nil
}