cache

package
v0.5.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 27, 2026 License: BSD-2-Clause Imports: 17 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrNoDocuments = errors.New("no documents found")

Functions

This section is empty.

Types

type AuthContextStore

type AuthContextStore interface {
	// Save stores an authorization context with sessionID as primary key.
	Save(ctx context.Context, doc *AuthorizationContext) error

	// Create is an alias for Save.
	Create(ctx context.Context, doc *AuthorizationContext) error

	// Get retrieves an authorization context by query fields (sessionID, requestURI, code, state, etc.).
	Get(ctx context.Context, query *AuthorizationContext) (*AuthorizationContext, error)

	// GetByID retrieves an authorization context by session ID.
	GetByID(ctx context.Context, id string) (*AuthorizationContext, error)

	// GetByAuthorizationCode retrieves an authorization context by authorization code.
	GetByAuthorizationCode(ctx context.Context, code string) (*AuthorizationContext, error)

	// GetByAccessToken retrieves an authorization context by access token.
	GetByAccessToken(ctx context.Context, token string) (*AuthorizationContext, error)

	// GetWithAccessToken retrieves an authorization context by access token (legacy method).
	GetWithAccessToken(ctx context.Context, token string) (*AuthorizationContext, error)

	// Update updates an existing authorization context.
	Update(ctx context.Context, doc *AuthorizationContext) error

	// Delete removes an authorization context by session ID.
	Delete(ctx context.Context, id string) error

	// ForfeitAuthorizationCode marks an authorization code as used and returns the updated context.
	ForfeitAuthorizationCode(ctx context.Context, query *AuthorizationContext) (*AuthorizationContext, error)

	// MarkCodeAsForfeited marks an authorization code as forfeited by session ID.
	MarkCodeAsForfeited(ctx context.Context, id string) error

	// Consent marks an authorization context as consented.
	Consent(ctx context.Context, query *AuthorizationContext) error

	// AddToken adds a token to an authorization context identified by code.
	AddToken(ctx context.Context, code string, token *Token) error

	// SetAuthenticSource sets the authentic source for an authorization context.
	SetAuthenticSource(ctx context.Context, query *AuthorizationContext, authenticSource string) error

	// AddIdentity adds identity information to an authorization context.
	AddIdentity(ctx context.Context, query *AuthorizationContext, input *AuthorizationContext) error
}

AuthContextStore defines the interface for authorization context storage. Implementations can use in-memory caching, MongoDB, or other backends. This abstraction enables horizontal scaling (HA) by allowing shared storage backends.

type AuthorizationContext

type AuthorizationContext struct {
	// Core session fields
	SessionID string        `json:"session_id" bson:"session_id" validate:"required,max=128,printascii"`
	Status    SessionStatus `json:"status,omitempty" bson:"status,omitempty" validate:"omitempty,max=32,printascii"`
	CreatedAt time.Time     `json:"created_at" bson:"created_at,omitempty"`
	ExpiresAt int64         `json:"expires_at" bson:"expires_at"`

	// Client and authorization fields
	ClientID            string   `json:"client_id" bson:"client_id" validate:"omitempty,max=128,printascii"`
	WalletClientID      string   `json:"wallet_client_id,omitempty" bson:"wallet_client_id,omitempty" validate:"omitempty,max=128,printascii"`
	Scopes              []string `json:"scopes,omitempty" bson:"scopes,omitempty"`
	State               string   `json:"state,omitempty" bson:"state,omitempty" validate:"omitempty,max=500,printascii"`
	Nonce               string   `json:"nonce,omitempty" bson:"nonce,omitempty" validate:"omitempty,max=128,printascii"`
	CodeChallenge       string   `json:"code_challenge,omitempty" bson:"code_challenge,omitempty" validate:"omitempty,max=128,printascii"`
	CodeChallengeMethod string   `json:"code_challenge_method,omitempty" bson:"code_challenge_method,omitempty" validate:"omitempty,max=16,printascii"`

	// Authorization code fields
	Code      string `json:"code,omitempty" bson:"code,omitempty" validate:"omitempty,max=128,printascii"`
	Forfeited bool   `json:"forfeited,omitempty" bson:"forfeited,omitempty"`

	// Token fields
	Token       *Token `json:"token,omitempty" bson:"token,omitempty"`
	AccessToken string `json:"access_token,omitempty" bson:"access_token,omitempty" validate:"omitempty,max=4096,printascii"`
	IDToken     string `json:"id_token,omitempty" bson:"id_token,omitempty" validate:"omitempty,max=8192,printascii"`

	// Issuer-specific fields (credential issuance)
	AuthorizationDetails []openid4vci.AuthorizationDetailsParameter `json:"authorization_details,omitempty" bson:"authorization_details,omitempty"`
	RequestURI           string                                     `json:"request_uri,omitempty" bson:"request_uri,omitempty" validate:"omitempty,max=2048,printascii"`
	WalletURI            string                                     `json:"redirect_url,omitempty" bson:"redirect_url,omitempty" validate:"omitempty,max=2048,printascii"`
	Consent              bool                                       `json:"consent,omitempty" bson:"consent,omitempty"`
	AuthenticSource      string                                     `json:"authentic_source,omitempty" bson:"authentic_source,omitempty" validate:"omitempty,max=128,printascii"`
	VCT                  string                                     `json:"vct,omitempty" bson:"vct,omitempty" validate:"omitempty,max=256,printascii"`
	Identity             *model.Identity                            `json:"identity,omitempty" bson:"identity,omitempty"`

	// Verifier-specific fields (presentation/RP flows)
	RedirectURI            string         `json:"redirect_uri,omitempty" bson:"redirect_uri,omitempty" validate:"omitempty,max=2048,printascii"`
	ResponseType           string         `json:"response_type,omitempty" bson:"response_type,omitempty" validate:"omitempty,max=32,printascii"`
	ResponseMode           string         `json:"response_mode,omitempty" bson:"response_mode,omitempty" validate:"omitempty,max=32,printascii"`
	ShowCredentialDetails  bool           `json:"show_credential_details,omitempty" bson:"show_credential_details,omitempty"`
	CodeExpiresAt          int64          `json:"code_expires_at,omitempty" bson:"code_expires_at,omitempty"`                 // Unix timestamp
	AccessTokenExpiresAt   int64          `json:"access_token_expires_at,omitempty" bson:"access_token_expires_at,omitempty"` // Unix timestamp
	RefreshToken           string         `json:"refresh_token,omitempty" bson:"refresh_token,omitempty" validate:"omitempty,max=4096,printascii"`
	RefreshTokenExpiresAt  int64          `json:"refresh_token_expires_at,omitempty" bson:"refresh_token_expires_at,omitempty"` // Unix timestamp
	VerifiedClaims         map[string]any `json:"verified_claims,omitempty" bson:"verified_claims,omitempty"`
	VPToken                string         `json:"vp_token,omitempty" bson:"vp_token,omitempty" validate:"omitempty,max=65536,printascii"`
	PresentationSubmission any            `json:"presentation_submission,omitempty" bson:"presentation_submission,omitempty"`

	// OpenID4VP fields (wallet interaction)
	EphemeralEncryptionKeyID string          `` /* 129-byte string literal not displayed */
	VerifierResponseCode     string          `json:"verifier_response_code,omitempty" bson:"verifier_response_code,omitempty" validate:"omitempty,max=128,printascii"`
	RequestObjectID          string          `json:"request_object_id,omitempty" bson:"request_object_id,omitempty" validate:"omitempty,max=128,printascii"`
	RequestObjectNonce       string          `json:"request_object_nonce,omitempty" bson:"request_object_nonce,omitempty" validate:"omitempty,max=128,printascii"`
	DCQLQuery                *openid4vp.DCQL `json:"dcql_query,omitempty" bson:"dcql_query,omitempty"`
	WalletID                 string          `json:"wallet_id,omitempty" bson:"wallet_id,omitempty" validate:"omitempty,max=128,printascii"`
}

AuthorizationContext is the unified model for OIDC/OpenID4VP sessions It supports both issuer credential issuance flows and verifier presentation/RP flows

func (*AuthorizationContext) Validate

func (a *AuthorizationContext) Validate() error

Validate checks the AuthorizationContext against its struct validation tags.

type Cache

type Cache[V any] interface {
	// Get retrieves a value by key. Returns the value and true if found,
	// or the zero value and false if not found or expired.
	Get(ctx context.Context, key string) (V, bool)

	// Set stores a value with the default TTL configured at creation time.
	Set(ctx context.Context, key string, value V)

	// SetWithTTL stores a value with a custom TTL, overriding the default.
	SetWithTTL(ctx context.Context, key string, value V, ttl time.Duration)

	// Delete removes a value by key.
	Delete(ctx context.Context, key string)

	// Len returns the number of items currently in the cache.
	Len() int
}

Cache is a generic key-value cache interface with TTL support. All in-memory caches MUST use this interface to allow swapping backends for HA deployments (e.g. memory → mongo).

V is the value type. Keys are always strings, which covers all current usage across the codebase.

func NewGenericCache

func NewGenericCache[V any](s *Service, ctx context.Context, collection string, ttl time.Duration) (Cache[V], error)

NewGenericCache creates a Cache[V] backed by the service's backend.

type Logger

type Logger interface {
	Error(err error, msg string, keysAndValues ...any)
}

Logger is a minimal logging interface for operational error reporting. Both *logger.Log and logr.Logger satisfy this interface.

type MemoryCache

type MemoryCache[V any] struct {
	// contains filtered or unexported fields
}

MemoryCache is a generic in-memory cache backed by ttlcache. Suitable for single-instance deployments. For HA, swap with MongoCache.

func NewMemoryCache

func NewMemoryCache[V any](ttl time.Duration) *MemoryCache[V]

NewMemoryCache creates a new in-memory generic cache with the given default TTL.

Example
package main

import (
	"fmt"
	"time"

	"github.com/SUNET/vc/pkg/cache"
)

func main() {
	c := cache.NewMemoryCache[string](5 * time.Minute)
	defer c.Stop()

	fmt.Printf("%T\n", c)
}
Output:
*cache.MemoryCache[string]

func (*MemoryCache[V]) Delete

func (m *MemoryCache[V]) Delete(_ context.Context, key string)

Delete removes a value by key.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/SUNET/vc/pkg/cache"
)

func main() {
	ctx := context.Background()
	c := cache.NewMemoryCache[string](5 * time.Minute)
	defer c.Stop()

	c.Set(ctx, "key", "value")
	fmt.Println("before delete:", c.Len())

	c.Delete(ctx, "key")
	_, found := c.Get(ctx, "key")
	fmt.Println("after delete found:", found)
	fmt.Println("after delete len:", c.Len())
}
Output:
before delete: 1
after delete found: false
after delete len: 0

func (*MemoryCache[V]) Get

func (m *MemoryCache[V]) Get(_ context.Context, key string) (V, bool)

Get retrieves a value by key.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/SUNET/vc/pkg/cache"
)

func main() {
	ctx := context.Background()
	c := cache.NewMemoryCache[int](5 * time.Minute)
	defer c.Stop()

	// Get a key that does not exist
	_, found := c.Get(ctx, "missing")
	fmt.Println("missing key found:", found)

	// Set and get a key
	c.Set(ctx, "count", 42)
	val, found := c.Get(ctx, "count")
	fmt.Println("count found:", found)
	fmt.Println("count value:", val)
}
Output:
missing key found: false
count found: true
count value: 42

func (*MemoryCache[V]) Len

func (m *MemoryCache[V]) Len() int

Len returns the number of items currently in the cache.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/SUNET/vc/pkg/cache"
)

func main() {
	ctx := context.Background()
	c := cache.NewMemoryCache[string](5 * time.Minute)
	defer c.Stop()

	fmt.Println("empty cache len:", c.Len())

	c.Set(ctx, "a", "alpha")
	c.Set(ctx, "b", "bravo")
	c.Set(ctx, "c", "charlie")
	fmt.Println("after 3 inserts len:", c.Len())
}
Output:
empty cache len: 0
after 3 inserts len: 3

func (*MemoryCache[V]) Set

func (m *MemoryCache[V]) Set(_ context.Context, key string, value V)

Set stores a value with the default TTL.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/SUNET/vc/pkg/cache"
)

func main() {
	ctx := context.Background()
	c := cache.NewMemoryCache[string](5 * time.Minute)
	defer c.Stop()

	c.Set(ctx, "greeting", "hello")

	val, found := c.Get(ctx, "greeting")
	fmt.Println("found:", found)
	fmt.Println("value:", val)
}
Output:
found: true
value: hello

func (*MemoryCache[V]) SetWithTTL

func (m *MemoryCache[V]) SetWithTTL(_ context.Context, key string, value V, ttl time.Duration)

SetWithTTL stores a value with a custom TTL.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/SUNET/vc/pkg/cache"
)

func main() {
	ctx := context.Background()
	c := cache.NewMemoryCache[string](5 * time.Minute)
	defer c.Stop()

	// Set a value with a custom TTL
	c.SetWithTTL(ctx, "session", "abc123", 30*time.Second)

	val, found := c.Get(ctx, "session")
	fmt.Println("found:", found)
	fmt.Println("value:", val)
}
Output:
found: true
value: abc123

func (*MemoryCache[V]) Stop

func (m *MemoryCache[V]) Stop()

Stop stops the background expiration goroutine.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore implements authorization context storage using an in-memory ttlcache. Suitable for single-instance deployments.

func NewMemoryStore

func NewMemoryStore(ttl time.Duration) *MemoryStore

NewMemoryStore creates a new in-memory authorization context store.

func (*MemoryStore) AddIdentity

func (c *MemoryStore) AddIdentity(ctx context.Context, query *AuthorizationContext, input *AuthorizationContext) error

AddIdentity adds identity information to an authorization context

func (*MemoryStore) AddToken

func (c *MemoryStore) AddToken(ctx context.Context, code string, token *Token) error

AddToken adds a token to an authorization context

func (*MemoryStore) Consent

func (c *MemoryStore) Consent(ctx context.Context, query *AuthorizationContext) error

Consent marks an authorization context as consented

func (*MemoryStore) Create

func (c *MemoryStore) Create(ctx context.Context, doc *AuthorizationContext) error

Create is an alias for Save to match the Session API

func (*MemoryStore) Delete

func (c *MemoryStore) Delete(ctx context.Context, id string) error

Delete removes an authorization context from the cache

func (*MemoryStore) ForfeitAuthorizationCode

func (c *MemoryStore) ForfeitAuthorizationCode(ctx context.Context, query *AuthorizationContext) (*AuthorizationContext, error)

ForfeitAuthorizationCode marks an authorization code as used

func (*MemoryStore) Get

Get retrieves an authorization context by query fields

func (*MemoryStore) GetByAccessToken

func (c *MemoryStore) GetByAccessToken(ctx context.Context, token string) (*AuthorizationContext, error)

GetByAccessToken retrieves an authorization context by access token

func (*MemoryStore) GetByAuthorizationCode

func (c *MemoryStore) GetByAuthorizationCode(ctx context.Context, code string) (*AuthorizationContext, error)

GetByAuthorizationCode retrieves an authorization context by authorization code

func (*MemoryStore) GetByID

func (c *MemoryStore) GetByID(ctx context.Context, id string) (*AuthorizationContext, error)

GetByID retrieves an authorization context by session ID

func (*MemoryStore) GetWithAccessToken

func (c *MemoryStore) GetWithAccessToken(ctx context.Context, token string) (*AuthorizationContext, error)

GetWithAccessToken retrieves an authorization context by access token

func (*MemoryStore) MarkCodeAsForfeited

func (c *MemoryStore) MarkCodeAsForfeited(ctx context.Context, id string) error

MarkCodeAsForfeited marks an authorization code as forfeited

func (*MemoryStore) Save

Save stores an authorization context in the cache with sessionID as primary key

func (*MemoryStore) SetAuthenticSource

func (c *MemoryStore) SetAuthenticSource(ctx context.Context, query *AuthorizationContext, authenticSource string) error

SetAuthenticSource sets the authentic source for an authorization context

func (*MemoryStore) Update

func (c *MemoryStore) Update(ctx context.Context, doc *AuthorizationContext) error

Update updates an existing authorization context

type MongoCache

type MongoCache[V any] struct {
	// contains filtered or unexported fields
}

MongoCache is a generic cache backed by a MongoDB collection. It stores values as BSON documents and uses a TTL index on `created_at` for automatic expiration. Enables HA by sharing state across instances.

V must be serializable to BSON. Primitive types (string, bool, int, []byte) and structs with bson tags are supported out of the box.

func NewMongoCache

func NewMongoCache[V any](ctx context.Context, client *mongo.Client, database, collection string, ttl time.Duration, log Logger) (*MongoCache[V], error)

NewMongoCache creates a new MongoDB-backed generic cache. It creates the necessary indexes including a TTL index for automatic expiration. If log is nil operational errors are silently discarded.

func (*MongoCache[V]) Delete

func (m *MongoCache[V]) Delete(ctx context.Context, key string)

Delete removes a value by key.

func (*MongoCache[V]) Get

func (m *MongoCache[V]) Get(ctx context.Context, key string) (V, bool)

Get retrieves a value by key.

func (*MongoCache[V]) Len

func (m *MongoCache[V]) Len() int

Len returns the estimated number of items in the cache.

func (*MongoCache[V]) Set

func (m *MongoCache[V]) Set(ctx context.Context, key string, value V)

Set stores a value with the default TTL (uses upsert).

func (*MongoCache[V]) SetWithTTL

func (m *MongoCache[V]) SetWithTTL(ctx context.Context, key string, value V, _ time.Duration)

SetWithTTL stores a value with a custom TTL. Note: MongoDB TTL is per-collection, so custom TTL is approximated by setting created_at in the past/future relative to the collection TTL. For most use cases, use Set with the collection default.

type MongoStore

type MongoStore struct {
	// contains filtered or unexported fields
}

MongoStore implements AuthContextStore using MongoDB as the backend. This enables horizontal scaling (HA) by sharing session state across instances.

func NewMongoStore

func NewMongoStore(ctx context.Context, client *mongo.Client, database, collection string, ttl time.Duration) (*MongoStore, error)

NewMongoStore creates a new MongoDB-backed authorization context store. It sets up the collection with required indexes including a TTL index for automatic expiration.

func (*MongoStore) AddIdentity

func (s *MongoStore) AddIdentity(ctx context.Context, query *AuthorizationContext, input *AuthorizationContext) error

AddIdentity adds identity information to an authorization context.

func (*MongoStore) AddToken

func (s *MongoStore) AddToken(ctx context.Context, code string, token *Token) error

AddToken adds a token to an authorization context identified by code.

func (*MongoStore) Consent

func (s *MongoStore) Consent(ctx context.Context, query *AuthorizationContext) error

Consent marks an authorization context as consented.

func (*MongoStore) Create

func (s *MongoStore) Create(ctx context.Context, doc *AuthorizationContext) error

Create is an alias for Save.

func (*MongoStore) Delete

func (s *MongoStore) Delete(ctx context.Context, id string) error

Delete removes an authorization context by session ID.

func (*MongoStore) ForfeitAuthorizationCode

func (s *MongoStore) ForfeitAuthorizationCode(ctx context.Context, query *AuthorizationContext) (*AuthorizationContext, error)

ForfeitAuthorizationCode marks an authorization code as used.

func (*MongoStore) Get

Get retrieves an authorization context by query fields.

func (*MongoStore) GetByAccessToken

func (s *MongoStore) GetByAccessToken(ctx context.Context, token string) (*AuthorizationContext, error)

GetByAccessToken retrieves an authorization context by access token.

func (*MongoStore) GetByAuthorizationCode

func (s *MongoStore) GetByAuthorizationCode(ctx context.Context, code string) (*AuthorizationContext, error)

GetByAuthorizationCode retrieves an authorization context by authorization code.

func (*MongoStore) GetByID

func (s *MongoStore) GetByID(ctx context.Context, id string) (*AuthorizationContext, error)

GetByID retrieves an authorization context by session ID.

func (*MongoStore) GetWithAccessToken

func (s *MongoStore) GetWithAccessToken(ctx context.Context, token string) (*AuthorizationContext, error)

GetWithAccessToken retrieves an authorization context by access token (legacy method).

func (*MongoStore) MarkCodeAsForfeited

func (s *MongoStore) MarkCodeAsForfeited(ctx context.Context, id string) error

MarkCodeAsForfeited marks an authorization code as forfeited by session ID.

func (*MongoStore) Save

Save stores an authorization context in MongoDB with sessionID as primary key.

func (*MongoStore) SetAuthenticSource

func (s *MongoStore) SetAuthenticSource(ctx context.Context, query *AuthorizationContext, authenticSource string) error

SetAuthenticSource sets the authentic source for an authorization context.

func (*MongoStore) Update

func (s *MongoStore) Update(ctx context.Context, doc *AuthorizationContext) error

Update updates an existing authorization context.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service manages cache creation. When HA is enabled, caches are backed by MongoDB using the provided client; otherwise in-memory.

func New

func New(ha bool, databaseName string, client *mongo.Client, log Logger) *Service

New creates a cache Service. When ha is true the supplied mongo client is used for all caches; otherwise every cache is in-memory. The caller owns the client lifecycle. databaseName is the MongoDB database name to use for all caches. If log is nil operational errors from mongo-backed caches are silently discarded.

func (*Service) NewAuthContextCache

func (s *Service) NewAuthContextCache(ctx context.Context, collection string, ttl time.Duration) (AuthContextStore, error)

NewAuthContextCache creates an AuthContextStore backed by the service's backend.

type SessionStatus

type SessionStatus string

SessionStatus represents the status of an OIDC session

const (
	SessionStatusPending              SessionStatus = "pending"
	SessionStatusAwaitingPresentation SessionStatus = "awaiting_presentation"
	SessionStatusCodeIssued           SessionStatus = "code_issued"
	SessionStatusTokenIssued          SessionStatus = "token_issued"
	SessionStatusCompleted            SessionStatus = "completed"
	SessionStatusExpired              SessionStatus = "expired"
	SessionStatusError                SessionStatus = "error"
)

type SharedSecrets

type SharedSecrets struct {
	// ServiceName is the owning service (e.g. "apigw", "verifier"). Used as _id.
	ServiceName string `bson:"_id"`
	// SessionAuthKey is the HMAC authentication key for session cookies.
	SessionAuthKey string `bson:"session_auth_key"`
	// SessionEncKey is the AES encryption key for session cookies.
	SessionEncKey string `bson:"session_enc_key"`
}

SharedSecrets holds session keys that must be identical across all instances of a service in HA mode.

func EnsureSharedSecrets

func EnsureSharedSecrets(ctx context.Context, s *Service, serviceName string) (*SharedSecrets, error)

EnsureSharedSecrets returns session keys that are guaranteed to be identical across every instance that calls this function with the same serviceName.

When the Service is in HA mode it generates candidate keys and atomically inserts them into MongoDB using FindOneAndUpdate with $setOnInsert + upsert. If another instance races and inserts first, MongoDB returns that existing document instead — no conflicts.

When HA is disabled it simply generates ephemeral keys.

type Token

type Token struct {
	AccessToken string `json:"access_token" bson:"access_token" validate:"required,max=4096,printascii"`
	ExpiresAt   int64  `json:"expires_at" bson:"expires_at" validate:"required"`
}

Token represents an access token with expiration

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL