Skip to content

Use case — Minimal OP

You want a working Authorization Code + PKCE round-trip end-to-end with the absolute minimum option list. The upstream example brings the OP and a paired RP up in the same process so a browser can drive the full flow without any external setup.

Source: examples/01-minimal/main.go

Architecture

One Go process, nothing else running
A browser drives the bundled relying party, which speaks OIDC to the provider in the same process. The provider reads and writes an in-memory store and signs with an ephemeral keyset — no database and no key management to set up.Browseryoursa single Go processrpkit RP:9090OIDCop.New(…):8080inmem.Storegone on restartKeysetephemeralECDSA P-256no database, no key management, no configuration file — everything above is created at start-up
Both boxes on the right are deliberately throwaway. Restarting loses every session and every key, which is exactly what you want while you are still finding out whether the flow works at all.

The library is one process. The store is in-memory. Keys are generated at boot. The example seeds one demo user (demo/demo) and registers a public client whose redirect_uri points back at the embedded RP.

Code (essentials)

go
package main

import (
  "log"
  "net/http"

  "github.com/libraz/go-oidc-provider/op"
  "github.com/libraz/go-oidc-provider/op/storeadapter/inmem"
)

func main() {
  keys := /* devkeys.MustEphemeral("minimal-1") in the example */
  st := inmem.New()
  // seedUser hashes "demo"/"demo" via op.HashPassword and PUTs a
  // *store.User into st.UserPasswords(); see the example for the body.

  // The upstream example uses opkit.DefaultLoginFlow(st.UserPasswords())
  // from examples/internal/opkit — a thin wrapper that constructs the
  // same value below. The public API is the LoginFlow struct shown here;
  // import opkit only if you are reading the example's source, not for
  // production code.
  flow := op.LoginFlow{
    Primary: op.PrimaryPassword{Store: st.UserPasswords()},
  }

  provider, err := op.New(
    op.WithIssuer("http://127.0.0.1:8080"),
    op.WithStore(st),
    op.WithKeyset(keys.Keyset()),
    op.WithCookieKeys(keys.CookieKey),
    op.WithLoginFlow(flow),
    op.WithStaticClients(op.PublicClient{
      ID:           "demo-rp",
      RedirectURIs: []string{"http://127.0.0.1:9090/callback"},
      Scopes:       []string{"openid", "profile"},
    }),
  )
  if err != nil {
    log.Fatalf("op.New: %v", err)
  }

  mux := http.NewServeMux()
  mux.Handle("/", provider)
  log.Fatal(http.ListenAndServe(":8080", mux))
}

WithIssuer, WithStore, and WithKeyset on their own let /oidc/.well-known/openid-configuration and /oidc/jwks answer. WithCookieKeys becomes required as soon as the authorization-code grant is enabled, which is the default browser-flow setup. Everything that depends on a user (authorize, token, userinfo) also needs the WithLoginFlow + WithStaticClients pair. getting-started/minimal shows the discovery-only shape if that is what you want.

What the OP exposes

The defaults mount under /oidc (override with op.WithMountPrefix):

PathPurpose
/.well-known/openid-configurationDiscovery (always at root, OIDC Discovery 1.0 §4)
/oidc/jwksPublic JWKS for ID Token / JWT access token verification
/oidc/authAuthorization endpoint
/oidc/tokenToken endpoint
/oidc/userinfoUserInfo (RFC 6749 + OIDC Core §5.3)
/oidc/end_sessionRP-Initiated Logout 1.0

Optional endpoints (/par, /introspect, /revoke, /register, /interaction/*, /session/*) only mount when their corresponding feature is enabled.

What's missing for a real deployment

GapFix
Single demo user is hard-codedEnrol users through your own management plane and store.User PUTs.
Ephemeral keys → ID Tokens become unverifiable on restartLoad from a vault / KMS / file.
In-memory store → state lost on restartSwitch to op/storeadapter/sql or op/storeadapter/composite.
Plain HTTP listener (http://127.0.0.1)Front behind a TLS-terminating ingress; switch issuer to https://.
Single-factor (password only)Add RuleAlways(StepTOTP{...}) — see MFA / step-up.
Demo RP code in examples/internal/rpkitProduction RPs use golang.org/x/oauth2 + github.com/coreos/go-oidc/v3 directly.

examples/02-bundle fills these in for a "comprehensive embedder" reference.

Run it

sh
git clone https://github.com/libraz/go-oidc-provider.git
cd go-oidc-provider
(cd examples/01-minimal && GOWORK=off go run -tags example .)
# in another terminal:
curl -s http://localhost:8080/.well-known/openid-configuration | jq