memory

package
v11.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2026 License: AGPL-3.0 Imports: 9 Imported by: 0

Documentation

Overview

Package memory keeps an authorization server's state in maps.

store := memory.NewStore(memory.WithSweeper(ctx, oauth2server.DefaultSweepInterval))
srv, _ := oauth2server.NewServer(cfg, store, authenticator)

It exists for tests, for a single-process development server, and for nothing else — and saying so is the point of the package rather than a caveat on it, because a map-backed authorization server is exactly what a consumer assembling this from the reference examples ends up with, and it looks like it works.

What it cannot do

An authorization code is issued by the replica that served /authorize and redeemed by whichever replica serves /token. With this store those are the same process or the redemption fails, so a fleet behind a load balancer fails logins in proportion to how well the balancer spreads them — which reads as a flaky login rather than as a missing dependency.

A restart drops every registered client. Under RFC 7591 dynamic registration that is every client there is, so a deploy invalidates the entire client population and each one has to discover and re-register before its next login works.

oauth2server/database is the answer to both, and it is the same interface.

What it does do

Everything the interface promises, atomically, under one mutex. It passes the same oauth2servertest suite the database store does, including the case where two goroutines redeem one authorization code and exactly one of them wins. That is why it is a usable test double rather than a shape that compiles: a suite that schedules against this store inherits whatever it gets wrong, so it is held to the same contract as the real thing.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Option

type Option func(*options)

Option configures a Store at construction.

func WithClock

func WithClock(c clock.Clock) Option

WithClock swaps the clock this store stamps and expires against, so a test can move expiry without waiting for it.

func WithLogger

func WithLogger(logger logging.Logger) Option

WithLogger attaches a logger. An absent logger logs nowhere.

func WithSweeper

func WithSweeper(ctx context.Context, interval time.Duration) Option

WithSweeper starts a background sweep that removes dead records every interval, until ctx is done.

It matters less here than it does for the database store — this store's maps die with the process — but a long-lived single-process deployment still accumulates one authorization code per login attempt forever without it.

A nil context or a non-positive interval starts nothing.

func WithTracerProvider

func WithTracerProvider(tracerProvider tracing.Provider) Option

WithTracerProvider attaches a tracer provider. An absent one traces nowhere.

type Store

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

Store keeps every authorization server record in maps behind one mutex.

One mutex rather than one per map, because the operations that matter span maps: revoking a family touches both token maps, and consuming a code has to be atomic against everything else. Four locks would make that a lock-ordering problem in a type whose entire job is to be the simple one.

func NewStore

func NewStore(opts ...Option) *Store

NewStore builds an in-memory Store.

It is for tests, for single-process development, and for nothing else. Two replicas do not share it, which under this package's flow means the authorization code issued by one replica cannot be redeemed at the other — so a login fails whenever the load balancer does its job. A restart loses every registered client, which under dynamic registration is every client there is. Deployments want oauth2server/database.

func (*Store) Close

func (s *Store) Close() error

Close releases nothing. The maps go when the Store does.

func (*Store) ConsumeAuthorizationCode

func (s *Store) ConsumeAuthorizationCode(ctx context.Context, hash string) (*oauth2server.AuthorizationCode, error)

ConsumeAuthorizationCode marks a code redeemed and returns it.

The whole method runs under the mutex, which is what makes two concurrent redemptions of one code resolve to exactly one success — the case the conformance suite exists to hold every implementation to.

func (*Store) ConsumeRefreshToken

func (s *Store) ConsumeRefreshToken(ctx context.Context, hash string) (*oauth2server.RefreshToken, error)

ConsumeRefreshToken marks a refresh token redeemed and returns it.

func (*Store) CreateAccessToken

func (s *Store) CreateAccessToken(ctx context.Context, token *oauth2server.AccessToken) error

CreateAccessToken records an issued access token.

func (*Store) CreateAuthorizationCode

func (s *Store) CreateAuthorizationCode(ctx context.Context, code *oauth2server.AuthorizationCode) error

CreateAuthorizationCode records an issued code.

func (*Store) CreateClient

func (s *Store) CreateClient(ctx context.Context, client *oauth2server.Client) error

CreateClient records a registration.

func (*Store) CreateRefreshToken

func (s *Store) CreateRefreshToken(ctx context.Context, token *oauth2server.RefreshToken) error

CreateRefreshToken records an issued refresh token.

func (*Store) DeleteClient

func (s *Store) DeleteClient(ctx context.Context, clientID string) error

DeleteClient removes a registration. Absent is not an error.

func (*Store) GetAccessToken

func (s *Store) GetAccessToken(ctx context.Context, hash string) (*oauth2server.AccessToken, error)

GetAccessToken reads an access token.

func (*Store) GetClient

func (s *Store) GetClient(ctx context.Context, clientID string) (*oauth2server.Client, error)

GetClient reads a registration.

func (*Store) GetRefreshToken

func (s *Store) GetRefreshToken(ctx context.Context, hash string) (*oauth2server.RefreshToken, error)

GetRefreshToken reads a refresh token without consuming it.

func (*Store) RevokeAccessToken

func (s *Store) RevokeAccessToken(ctx context.Context, hash string) error

RevokeAccessToken marks one access token revoked. Absent is not an error.

func (*Store) RevokeFamily

func (s *Store) RevokeFamily(ctx context.Context, familyID string) (int64, error)

RevokeFamily revokes every access and refresh token in a family.

func (*Store) RevokeRefreshToken

func (s *Store) RevokeRefreshToken(ctx context.Context, hash string) error

RevokeRefreshToken marks one refresh token revoked. Absent is not an error.

func (*Store) Sweep

func (s *Store) Sweep(ctx context.Context, now time.Time) (int64, error)

Sweep removes records whose deadlines have passed.

Revoked-but-unexpired tokens are deliberately kept: a resource server holding one is entitled to be told "no" rather than to have its request treated as carrying a token nobody ever issued, and the difference shows up in whichever log line an operator reads when a user complains that signing out did not work.

Jump to

Keyboard shortcuts

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