- 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.
76 lines
1.7 KiB
Go
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
|
|
}
|
|
}
|