yao/openapi/otp/verify.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

76 lines
1.7 KiB
Go

package otp
import (
"encoding/json"
"fmt"
)
// Verify looks up an OTP code and returns its stored Payload.
// It does NOT consume the code — the code remains valid within its TTL.
func (s *Service) Verify(code string) (*Payload, error) {
if code == "" {
return nil, fmt.Errorf("code is required")
}
key := s.storeKey(code)
val, ok := s.store.Get(key)
if !ok || val == nil {
return nil, fmt.Errorf("invalid or expired OTP code")
}
return coercePayload(val)
}
// coercePayload converts a store value into a *Payload.
// The store may return *Payload, map[string]interface{}, or raw JSON bytes.
func coercePayload(val interface{}) (*Payload, error) {
switch v := val.(type) {
case *Payload:
return v, nil
case Payload:
return &v, nil
case map[string]interface{}:
p := &Payload{Consume: true}
if s, ok := v["team_id"].(string); ok {
p.TeamID = s
}
if s, ok := v["member_id"].(string); ok {
p.MemberID = s
}
if s, ok := v["user_id"].(string); ok {
p.UserID = s
}
if s, ok := v["redirect"].(string); ok {
p.Redirect = s
}
if s, ok := v["scope"].(string); ok {
p.Scope = s
}
switch te := v["token_expires_in"].(type) {
case float64:
p.TokenExpiresIn = int(te)
case int:
p.TokenExpiresIn = te
case int64:
p.TokenExpiresIn = int(te)
}
if b, ok := v["consume"].(bool); ok {
p.Consume = b
}
return p, nil
default:
// JSON fallback for serialised store backends
raw, err := json.Marshal(val)
if err != nil {
return nil, fmt.Errorf("unexpected OTP payload type: %T", val)
}
p := &Payload{Consume: true}
if err := json.Unmarshal(raw, p); err != nil {
return nil, fmt.Errorf("failed to decode OTP payload: %w", err)
}
return p, nil
}
}