test/mini-oidc: Allow setting expiration for tokens

access token and refresh token

Signed-off-by: Lucas Bremgartner <lucas.bremgartner@futurfusion.io>
This commit is contained in:
Lucas Bremgartner 2025-10-16 13:17:16 +02:00
parent 493b970d71
commit 24646d8db5
No known key found for this signature in database
GPG Key ID: 500FC8A961848325
3 changed files with 51 additions and 23 deletions

View File

@ -12,4 +12,8 @@ mini-oidc <port> [<user-file>]
By default, it will authenticate everyone as `unknown`, but this can be overridden by writing the username to be returned in the file named in the 2nd
(optional) argument. This effectively allows scripting a variety of users without having to deal with actual login.
The `storage` sub-package is a copy of https://github.com/zitadel/oidc/tree/main/example/server/storage with the exception of the added IncusDeviceClient.
The `storage` sub-package is a based on https://github.com/zitadel/oidc/tree/main/example/server/storage.
The following changes were made:
- Added IncusDeviceClient
- Added option to configure access token and refresh token expiry

View File

@ -41,7 +41,7 @@ func WithDeviceAuthorizationPollInterval(interval time.Duration) Option {
// Run starts minioidc on the given port.
// This starts ListenAndServe and will therefore block.
func Run(port string) error {
server, err := setup(port)
server, err := setup(port, nil, nil)
if err != nil {
return err
}
@ -57,7 +57,7 @@ func Run(port string) error {
// RunTest runs minioidc for use in tests.
// It picks a random port and returns its address. The address
// is also the issuer URL.
func RunTest(t *testing.T, opts ...Option) string {
func RunTest(t *testing.T, storageOpts []storage.Option, configOpts []Option) string {
t.Helper()
iport, err := getFreePort()
@ -67,7 +67,7 @@ func RunTest(t *testing.T, opts ...Option) string {
port := strconv.Itoa(iport)
server, err := setup(port, opts...)
server, err := setup(port, storageOpts, configOpts)
if err != nil {
t.Fatalf("minioidc setup: %v", err)
}
@ -89,14 +89,14 @@ func RunTest(t *testing.T, opts ...Option) string {
return fmt.Sprintf("http://%s/", server.Addr)
}
func setup(port string, opts ...Option) (*http.Server, error) {
func setup(port string, storageOpts []storage.Option, configOpts []Option) (*http.Server, error) {
issuer := fmt.Sprintf("http://127.0.0.1:%s/", port)
// Setup the OIDC provider.
key := sha256.Sum256([]byte("test"))
router := chi.NewRouter()
users := &userStore{}
storageBackend := storage.NewStorage(users)
storageBackend := storage.NewStorage(users, storageOpts...)
// Create the provider.
config := &op.Config{
@ -114,7 +114,7 @@ func setup(port string, opts ...Option) (*http.Server, error) {
},
}
for _, opt := range opts {
for _, opt := range configOpts {
opt(config)
}

View File

@ -36,18 +36,20 @@ var (
// typically you would implement this as a layer on top of your database
// for simplicity this example keeps everything in-memory
type Storage struct {
lock sync.Mutex
authRequests map[string]*AuthRequest
codes map[string]string
tokens map[string]*Token
clients map[string]*Client
userStore UserStore
services map[string]Service
refreshTokens map[string]*RefreshToken
signingKey signingKey
deviceCodes map[string]deviceAuthorizationEntry
userCodes map[string]string
serviceUsers map[string]*Client
lock sync.Mutex
authRequests map[string]*AuthRequest
codes map[string]string
tokens map[string]*Token
clients map[string]*Client
userStore UserStore
services map[string]Service
refreshTokens map[string]*RefreshToken
signingKey signingKey
deviceCodes map[string]deviceAuthorizationEntry
userCodes map[string]string
serviceUsers map[string]*Client
accessTokenExpiration time.Duration
refreshTokenExpiration time.Duration
}
type signingKey struct {
@ -88,9 +90,23 @@ func (s *publicKey) Key() any {
return &s.key.PublicKey
}
func NewStorage(userStore UserStore) *Storage {
type Option func(storage *Storage)
func WithAccessTokenExpiration(expiration time.Duration) Option {
return func(storage *Storage) {
storage.accessTokenExpiration = expiration
}
}
func WithRefreshTokenExpiration(expiration time.Duration) Option {
return func(storage *Storage) {
storage.refreshTokenExpiration = expiration
}
}
func NewStorage(userStore UserStore, opts ...Option) *Storage {
key, _ := rsa.GenerateKey(rand.Reader, 2048)
return &Storage{
storage := &Storage{
authRequests: make(map[string]*AuthRequest),
codes: make(map[string]string),
tokens: make(map[string]*Token),
@ -121,7 +137,15 @@ func NewStorage(userStore UserStore) *Storage {
accessTokenType: op.AccessTokenTypeBearer,
},
},
accessTokenExpiration: 5 * time.Minute,
refreshTokenExpiration: 5 * time.Hour,
}
for _, opt := range opts {
opt(storage)
}
return storage
}
// CheckUsernamePassword implements the `authenticate` interface of the login
@ -587,7 +611,7 @@ func (s *Storage) createRefreshToken(accessToken *Token, amr []string, authTime
ApplicationID: accessToken.ApplicationID,
UserID: accessToken.Subject,
Audience: accessToken.Audience,
Expiration: time.Now().Add(5 * time.Hour),
Expiration: time.Now().Add(s.refreshTokenExpiration),
Scopes: accessToken.Scopes,
}
s.refreshTokens[token.ID] = token
@ -628,7 +652,7 @@ func (s *Storage) accessToken(applicationID, refreshTokenID, subject string, aud
RefreshTokenID: refreshTokenID,
Subject: subject,
Audience: audience,
Expiration: time.Now().Add(5 * time.Minute),
Expiration: time.Now().Add(s.accessTokenExpiration),
Scopes: scopes,
}
s.tokens[token.ID] = token