Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/oauth2-proxy/mockoidc

A Mock OIDC Server for Unit & Integration Tests
https://github.com/oauth2-proxy/mockoidc

golang oauth2 oidc

Last synced: about 1 month ago
JSON representation

A Mock OIDC Server for Unit & Integration Tests

Awesome Lists containing this project

README

        

# mockoidc

A Mock OpenID Connect Server for Authentication Unit and Integration Tests.

Created by @NickMeves and @egrif during the [Greenhouse Software](https://medium.com/in-the-weeds)
2021 Q1 Hack Day.

[![Go Report Card](https://goreportcard.com/badge/github.com/oauth2-proxy/mockoidc)](https://goreportcard.com/report/github.com/oauth2-proxy/mockoidc)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![Maintainability](https://api.codeclimate.com/v1/badges/99c0561090d1002dc7e3/maintainability)](https://codeclimate.com/github/oauth2-proxy/mockoidc/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/99c0561090d1002dc7e3/test_coverage)](https://codeclimate.com/github/oauth2-proxy/mockoidc/test_coverage)

## Usage

Import the package
```
import "github.com/oauth2-proxy/mockoidc"
```

Start the MockOIDC Server. This will spin up a minimal OIDC server in its own
goroutine. It will listen on localhost on a random port.

Then pull its configuration to integrate it with your application. Begin
testing!

```
m, _ := mockoidc.Run()
defer m.Shutdown()

cfg := m.Config()
// type Config struct {
// ClientID string
// ClientSecret string
// Issuer string
//
// AccessTTL time.Duration
// RefreshTTL time.Duration
// }
```

### RunTLS

Alternatively, if you provide your own `tls.Config`, the server can run with
TLS:

```
tlsConfig = &tls.Config{
// ...your TLS settings
}

m, _ := mockoidc.RunTLS(tlsConfig)
defer m.Shutdown()
```

### Endpoints

The following endpoints are implemented. They can either be pulled from the
OIDC discovery document (`m.Issuer() + "/.well-known/openid-configuration`)
or retrieved directly from the MockOIDC server.

```
m, _ := mockoidc.Run()
defer m.Shutdown()

m.Issuer()
m.DiscoveryEndpoint()
m.AuthorizationEndpoint()
m.TokenEndpoint()
m.UserinfoEndpoint()
m.JWKSEndpoint()
```

### Seeding Users and Codes

By default, calls to the `authorization_endpoint` will start a session as if
the `mockoidc.DefaultUser()` had logged in, and it will return a random code
for the `token_endpoint`. The User in the session started by this call to the
`authorization_endpoint` will be the one in the tokens returned by the
subsequent `token_endpoint` call.

These can be seeded with your own test Users & codes that will be returned:

```
m, _ := mockoidc.Run()
defer m.Shutdown()

user := &mockoidc.User{
// User details...
}

// Add the User to the queue, this will be returned by the next login
m.QueueUser(user)

// Preset the code returned by the next login
m.QueueCode("12345")

// ...Request to m.AuthorizationEndpoint()
```

### Forcing Errors

Arbitrary errors can also be queued for handlers to return instead of their
default behavior:

```
m, err := mockoidc.Run()
defer m.Shutdown()

m.QueueError(&mockoidc.ServerError{
Code: http.StatusInternalServerError,
Error: mockoidc.InternalServerError,
Description: "Some Custom Description",
})
```

### Manipulating Time

To accurately test token expiration scenarios, the MockOIDC server's view of
time is completely mutable.

You can override the server's view of `time.Now`

```
mockoidc.NowFunc = func() { //...custom logic }
```

As tests are running, you can fast-forward time to critical test points (e.g.
Access & Refresh Token expirations).

```
m, _ := mockoidc.Run()

m.FastForward(time.Duration(1) * time.Hour)
```

#### Synchronizing with `jwt-go` time

Even though we can fast-forward time, the underlying tokens processed by the
[jwt-go](https://github.com/dgrijalva/jwt-go) library still have timing logic.

We need to synchronize our timer with theirs:

```
m, _ := mockoidc.Run()
defer m.Shutdown()

// Overrides jwt.TimeFunc to m.Now
reset := m.Synchronize()

// reset is a mockoidc.ResetTime function that reverts jwt.TimeFunc to
// its original state
defer reset()
```

### Manual Configuration

Everything started up with `mockoidc.Run()` can be done manually giving the
opportunity to finely tune the settings:

```
// Create a fresh RSA Private Key for token signing
rsaKey, _ := rsa.GenerateKey(rand.Reader, 2048)

// Create an unstarted MockOIDC server
m, _ := mockoidc.NewServer(rsaKey)

// Create the net.Listener on the exact IP:Port you want
ln, _ := net.Listen("tcp", "127.0.0.1:8080")

tlsConfig = &tls.Config{
// ...your TLS settings
}

// tlsConfig can be nil if you want HTTP
m.Start(ln, tlsConfig)
defer m.Shutdown()
```

Nearly all the MockOIDC struct is public. If you want to update any settings
to predefined values (e.g. `clientID`, `clientSecret`, `AccessTTL`,
`RefreshTTL`) you can before calling `m.Start`.

Additional internal components of the MockOIDC server are public if you need
to tamper with them as well:

```
type MockOIDC struct {
// ...other stuff

// Normally, these would be private. Expose them publicly for
// power users.
Server *http.Server
Keypair *Keypair
SessionStore *SessionStore
UserQueue *UserQueue
ErrorQueue *ErrorQueue
}
```

#### Adding Middleware

When configuring the MockOIDC server manually, you have the opportunity to add
custom middleware before starting the server (e.g. request logging, test
validators, etc).

```
m, _ := mockoidc.NewServer(nil)

middleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// custom middleware logic here...
next.ServeHTTP(rw, req)
// custom middleware logic here...
})
}

m.AddMiddleware(middleware)
```