Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrChallengeNotFound indicates the requested challenge does not exist or has expired. ErrChallengeNotFound = errors.New("challenge not found") // ErrChallengeExpired indicates the challenge has exceeded its validity period. ErrChallengeExpired = errors.New("challenge expired") // ErrInvalidTransaction indicates the provided transaction is malformed or invalid. ErrInvalidTransaction = errors.New("invalid transaction") // ErrTransactionMismatch indicates the signed transaction does not match the original challenge. ErrTransactionMismatch = errors.New("transaction does not match challenge") // ErrInvalidSignature indicates the transaction is not signed by the expected keypair. ErrInvalidSignature = errors.New("invalid or missing signature") // ErrInvalidToken indicates the JWT token is malformed, expired, or has an invalid signature. ErrInvalidToken = errors.New("invalid token") // ErrInvalidTokenClaims indicates the token claims are invalid or cannot be parsed. ErrInvalidTokenClaims = errors.New("invalid token claims") // ErrUnexpectedSigningMethod indicates the token uses an unsupported signing algorithm. ErrUnexpectedSigningMethod = errors.New("unexpected signing method") )
Authentication errors that can be mapped to HTTP status codes by handlers.
Functions ¶
This section is empty.
Types ¶
type Challenge ¶
type Challenge struct {
ID string `json:"id"` // Unique identifier for the challenge
Transaction string `json:"transaction"` // Base64-encoded Stellar transaction envelope
CreatedAt time.Time `json:"created_at"` // Timestamp when the challenge was created
ExpiresAt time.Time `json:"expires_at"` // Timestamp when the challenge expires
}
Challenge represents a cryptographic challenge used in the authentication flow. It contains a Stellar transaction that must be signed by the user to prove ownership of their private key. Challenges are temporary and expire after a configured duration.
type ChallengeService ¶
type ChallengeService interface {
// GenerateChallenge creates a new authentication challenge containing a Stellar transaction
// that must be signed by the user. The challenge expires after a configured duration.
GenerateChallenge() (*Challenge, error)
// VerifySignedChallenge validates that the provided transaction was signed by the correct
// keypair and matches the original challenge. Challenges are single-use and deleted after verification.
VerifySignedChallenge(challengeID, signedTxB64 string) error
}
ChallengeService defines the interface for Stellar transaction-based challenge-response authentication. This implements a cryptographic proof-of-ownership flow where users must sign a server-generated Stellar transaction with their private key to prove they control a specific public key.
func NewChallengeService ¶
func NewChallengeService(authConfig *config.AuthConfig, stellarConfig *config.StellarConfig, store ChallengeStore) (ChallengeService, error)
NewChallengeService creates a new ChallengeService with the provided configuration. The serverSecret is the Stellar private key used to sign challenges, proving they originated from the server. Returns an error if the serverSecret is not a valid Stellar private key.
type ChallengeStore ¶
type ChallengeStore interface {
// Store persists a challenge with automatic expiration based on the challenge's ExpiresAt field.
// Returns an error if the challenge is already expired or if storage fails.
Store(challengeID string, challenge *Challenge) error
// Get retrieves a challenge by its ID. Returns an error if the challenge is not found,
// has expired, or if retrieval fails.
Get(challengeID string) (*Challenge, error)
// Delete removes a challenge from storage. This should be called after successful
// authentication to prevent challenge reuse. Returns an error if deletion fails.
Delete(challengeID string) error
}
ChallengeStore defines the interface for storing and retrieving authentication challenges. Implementations must provide thread-safe operations with automatic expiration support.
func NewRedisStore ¶
func NewRedisStore(client *redis.Client, keyPrefix string) (ChallengeStore, error)
NewRedisStore creates a new RedisStore instance for storing authentication challenges. The keyPrefix parameter is used to namespace challenge keys in Redis, allowing multiple applications or environments to share the same Redis instance without key collisions.
Example:
store := NewRedisStore(redisClient, "microvault:auth") // This will create keys like: "microvault:auth:challenge:abc123"
type Claims ¶
type Claims struct {
AdminPublicKey string `json:"admin_pk"`
jwt.RegisteredClaims
}
Claims represents the JWT token payload containing user authentication information. It embeds the standard JWT registered claims and adds the admin's Stellar public key.
type JWTService ¶
type JWTService struct {
// contains filtered or unexported fields
}
JWTService provides JWT token generation and validation functionality. It uses HMAC-SHA256 signing and includes configurable expiration and refresh windows.
func NewJWTService ¶
func NewJWTService(config *config.AuthConfig) *JWTService
NewJWTService creates a new JWTService instance with the provided authentication configuration.
func (*JWTService) GenerateToken ¶
GenerateToken creates a new JWT token for the specified admin public key. The token is signed using HMAC-SHA256 and includes standard claims (issuer, subject, timestamps). Returns the signed token string, expiration time, and any error encountered during generation.
func (*JWTService) ShouldRefresh ¶
func (s *JWTService) ShouldRefresh(claims *Claims) bool
ShouldRefresh determines if a token should be refreshed based on its remaining lifetime. Returns true if the token will expire within the configured refresh window, allowing clients to obtain a new token before the current one expires.
func (*JWTService) ValidateToken ¶
func (s *JWTService) ValidateToken(tokenString string) (*Claims, error)
ValidateToken parses and validates a JWT token string. It verifies the signature using HMAC-SHA256 and ensures the token hasn't expired. Returns the extracted claims if valid, or an error if validation fails.
type RedisStore ¶
type RedisStore struct {
// contains filtered or unexported fields
}
RedisStore is a Redis-backed implementation of ChallengeStore. It uses Redis's native TTL functionality for automatic challenge expiration.
func (*RedisStore) Delete ¶
func (s *RedisStore) Delete(challengeID string) error
Delete removes a challenge from Redis by its ID. This method should be called after successful authentication to prevent replay attacks where an attacker might try to reuse a valid signed challenge.
Returns an error if:
- Redis DEL operation fails
- The operation times out (3 second timeout)
Note: This method does not return an error if the challenge doesn't exist. DEL operations in Redis are idempotent.
func (*RedisStore) Get ¶
func (s *RedisStore) Get(challengeID string) (*Challenge, error)
Get retrieves a challenge from Redis by its ID. The challenge data is retrieved from Redis and deserialized from JSON.
Returns an error if:
- The challenge is not found (may have expired or never existed)
- Redis GET operation fails
- JSON unmarshaling fails
- The operation times out (3 second timeout)
Note: If a challenge has expired, Redis will automatically delete it and this method will return a "challenge not found" error.
func (*RedisStore) Store ¶
func (s *RedisStore) Store(challengeID string, challenge *Challenge) error
Store persists a challenge to Redis with automatic expiration. The challenge is serialized to JSON and stored with a TTL based on the ExpiresAt field. Redis will automatically remove the challenge when the TTL expires.
Returns an error if:
- The challenge is already expired (ExpiresAt is in the past)
- JSON marshaling fails
- Redis SET operation fails
- The operation times out (3 second timeout)