go-oidc

module
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Mar 8, 2026 License: MIT

README

go-oidc

Go Reference Go Report Card License

A configurable OpenID Connect Provider for Go.

Supported Specifications

Certification

Luiky Vasconcelos has certified that go-oidc conforms to the following profiles of the OpenID Connect™ protocol.

  • Basic OP, Implicit OP, Hybrid OP, Config OP and Dynamic OP
  • FAPI 1.0
  • FAPI 2.0

OpenID Certification

Get Started

Install the module:

go get github.com/luikyv/go-oidc@latest

Create and run a provider:

key, _ := rsa.GenerateKey(rand.Reader, 2048)
jwks := goidc.JSONWebKeySet{
  Keys: []goidc.JSONWebKey{{
    KeyID:     "key_id",
    Key:       key,
    Algorithm: "RS256",
  }},
}

op, _ := provider.New(
  goidc.ProfileOpenID,
  "http://localhost",
  func(_ context.Context) (goidc.JSONWebKeySet, error) {
    return jwks, nil
  },
)
op.Run(":80")

Verify the setup at http://localhost/.well-known/openid-configuration.

Table of Contents

Running the Provider

The simplest way to run the provider:

op.Run(":80")

For more flexibility, use op.Handler() to get an http.Handler with all endpoints configured:

mux := http.NewServeMux()
mux.Handle("/", op.Handler())

server := &http.Server{
  Addr:    ":443",
  Handler: mux,
}
server.ListenAndServeTLS(certFilePath, certKeyFilePath)

Entities

go-oidc revolves around four entities: goidc.Client, goidc.AuthnSession, goidc.Grant and goidc.Token.

These entities are managed by implementations of goidc.ClientManager, goidc.AuthnSessionManager, goidc.GrantManager and goidc.TokenManager respectively.

By default, all entities are stored in memory and lost when the server shuts down. For production, replace the default managers with persistent implementations using provider.WithClientManager, provider.WithAuthnSessionManager, provider.WithGrantManager and provider.WithTokenManager.

Client

goidc.Client represents an OAuth 2.0 client that interacts with the authorization server to request tokens and access protected resources. It is always identified and queried by its ID.

Authentication Session

goidc.AuthnSession is a short-lived session that tracks the state of an authorization request as it progresses through authentication.

At any given time, goidc.AuthnSession has an ID and exactly one of the following lookup identifiers:

  • Pushed Authorization Request ID – Created during POST /par. See RFC 9126.
  • Callback ID – Present while the authentication policy is in progress.
  • Authorization Code – Set when authentication completes successfully with the authorization_code grant type.
  • Authentication Request ID – Used for CIBA. See CIBA.
Grant

goidc.Grant represents what a user (or the client itself) has authorized: the subject, scopes, authorization details, resources, and proof-of-possession bindings (DPoP or mTLS).

It may contain the following lookup identifiers:

  • Refresh Token – Present when the grant allows refresh tokens.
  • Authorization Code – Present for the authorization_code grant type.
Token

goidc.Token is the credential issued under a grant. It captures a snapshot of the active scopes, resources, and authorization details at the moment of issuance, which may be a subset of what the grant holds.

Each token has its own lifetime and is linked to its grant via GrantID. During a refresh token request, a new token is issued under the same grant. The refresh token on the grant is updated only if rotation is enabled.

Authentication Policies

Authorization requests (starting at /authorize by default) are handled by goidc.AuthnPolicy. A policy has two parts:

  1. Setup function – Determines whether the policy applies to a given request. If it returns false, the policy is skipped.
  2. Authentication function – Handles user interaction and authentication.

The authentication function returns one of:

  • goidc.StatusSuccess – Authentication succeeded. The Subject field on the session must be set.
  • goidc.StatusInProgress – Awaiting user interaction. Authentication resumes when a request is made to /authorize/{callback_id} (the callback ID is available via goidc.AuthnSession.CallbackID).
  • goidc.StatusFailure (or an error) – Authentication failed, and the grant is denied.
policy := goidc.NewPolicy(
  "main_policy",
  func(_ *http.Request, _ *goidc.Client, _ *goidc.AuthnSession) bool {
    return true
  },
  func(w http.ResponseWriter, r *http.Request, as *goidc.AuthnSession) (goidc.Status, error) {
    username := r.PostFormValue("username")
    if username == "" {
      renderHTMLPage(w)
      return goidc.StatusInProgress, nil
    }

    if username == "banned_user" {
      return goidc.StatusFailure, errors.New("the user is banned")
    }

    as.Subject = username
    return goidc.StatusSuccess, nil
  },
)

op, _ := provider.New(
  ...,
  provider.WithAuthorizationCodeGrant(),
  provider.WithPolicies(policy),
  ...,
)

For more examples, see the examples folder.

Signing and Encryption

When creating a provider.Provider, a JWKS function must be provided. This function returns the keys used for signing and encryption. It should typically return both private and public key material.

Every algorithm configured for the provider must have a corresponding JWK in the JWKS.

key, _ := rsa.GenerateKey(rand.Reader, 2048)
jwks := goidc.JSONWebKeySet{
  Keys: []goidc.JSONWebKey{{
    KeyID:     "key_id",
    Key:       key,
    Algorithm: "RS256",
  }},
}

op, _ := provider.New(
  goidc.ProfileOpenID,
  "http://localhost",
  func(_ context.Context) (goidc.JSONWebKeySet, error) {
    return jwks, nil
  },
)

If direct access to private keys is unavailable or granular control over signing is needed, the JWKS function can return only public key material. In that case, provider.WithSignerFunc must be added:

key, _ := rsa.GenerateKey(rand.Reader, 2048)
jwks := goidc.JSONWebKeySet{
  Keys: []goidc.JSONWebKey{{
    KeyID:     "key_id",
    Key:       key.Public(),
    Algorithm: "RS256",
  }},
}

op, _ := provider.New(
  goidc.ProfileOpenID,
  "http://localhost",
  func(_ context.Context) (goidc.JSONWebKeySet, error) {
    return jwks, nil
  },
  provider.WithSignerFunc(func(_ context.Context, _ goidc.SignatureAlgorithm) (kid string, signer crypto.Signer, err error) {
    return "key_id", key, nil
  }),
)

Similarly, if server-side decryption is needed (e.g., for encrypted JARs), configure provider.WithDecrypterFunc.

Tokens

ID tokens are signed using RS256 by default. Use provider.WithIDTokenSignatureAlgs to change the default or add additional algorithms.

Access tokens are opaque by default. To customize this, provide a goidc.TokenOptionsFunc:

op, _ := provider.New(
  ...,
  provider.WithTokenOptions(func(_ context.Context, _ *goidc.Grant, _ *goidc.Client) goidc.TokenOptions {
    return goidc.NewJWTTokenOptions(goidc.RS256, 600)
  }),
  ...,
)

Use goidc.NewJWTTokenOptions for JWT access tokens or goidc.NewOpaqueTokenOptions for opaque ones.

Refresh tokens are always opaque.

Scopes

goidc.NewScope creates a scope matched by exact string comparison:

scope := goidc.NewScope("openid")

goidc.NewDynamicScope creates a scope with custom matching logic:

paymentScope := goidc.NewDynamicScope("payment", func(requestedScope string) bool {
  return strings.HasPrefix(requestedScope, "payment:")
})
paymentScope.Matches("payment:30") // true

Dynamic scopes appear by their base name (e.g., "payment") in scopes_supported.

op, _ := provider.New(
  ...,
  provider.WithScopes(goidc.ScopeOpenID, goidc.ScopeOfflineAccess),
  ...,
)

Dynamic Client Registration (DCR)

DCR allows clients to register and update themselves dynamically:

op, _ := provider.New(
  ...,
  provider.WithDCR(
    func(r *http.Request, id string, meta *goidc.ClientMeta) error {
      return nil
    },
    func(_ context.Context, initialToken string) error {
      return nil
    },
  ),
  ...,
)

The first function (goidc.HandleDynamicClientFunc) runs during registration and update requests. Use it for custom validation or to set default metadata values.

The second function (goidc.ValidateInitialAccessTokenFunc) validates the initial access token during registration. Pass nil to skip validation.

By default, the DCR endpoint is /register and the management endpoint is /register/{client_id}.

To rotate the registration access token on each update request, add provider.WithDCRTokenRotation().

Mutual TLS (mTLS)

mTLS enables client authentication and certificate-bound access tokens via TLS certificates:

op, _ := provider.New(
  ...,
  provider.WithMTLS(
    "https://matls-go-oidc.com",
    func(r *http.Request) (*x509.Certificate, error) {
      ...
    },
  ),
  ...,
)

All enabled endpoints are listed under mtls_endpoint_aliases in the discovery response:

{
  "mtls_endpoint_aliases": {
    "token_endpoint": "https://matls-go-oidc.com/token"
  }
}

The certificate function (goidc.ClientCertFunc) may be called multiple times per request. Consider caching the result if extraction is expensive.

JWT-Secured Authorization Request (JAR)

JAR allows clients to send authorization requests as signed (and optionally encrypted) JWTs:

op, _ := provider.New(
  ...,
  provider.WithJAR(goidc.RS256, goidc.PS256),
  ...,
)

This adds the following to the discovery response:

{
  "request_parameter_supported": true,
  "request_object_signing_alg_values_supported": ["RS256", "PS256"]
}

To enable encryption:

provider.WithJAREncryption(goidc.RSA_OAEP_256)

To customize content encryption algorithms, use provider.WithJARContentEncryptionAlgs.

JWT-Secured Authorization Response Mode (JARM)

JARM returns authorization responses as signed (and optionally encrypted) JWTs, adding the response modes jwt, query.jwt, fragment.jwt and form_post.jwt:

op, _ := provider.New(
  ...,
  provider.WithJARM(goidc.RS256, goidc.PS256),
  ...,
)

To enable encryption:

provider.WithJARMEncryption(goidc.RSA_OAEP_256)

To customize content encryption algorithms, use provider.WithJARMContentEncryptionAlgs.

OpenID Federation

OpenID Federation establishes trust dynamically through signed entity statements, allowing federated clients to authenticate without prior manual registration.

op, _ := provider.New(
  ...,
  provider.WithOpenIDFederation(
    func(_ context.Context) (goidc.JSONWebKeySet, error) {
      return fedJWKS, nil
    },
    "https://trust-anchor.example.com",
  ),
  provider.WithOpenIDFedAuthorityHints("https://intermediate.example.com"),
  ...,
)

The entity configuration is exposed at GET /.well-known/openid-federation.

Client Registration Types

Federated clients can use automatic or explicit registration:

provider.WithOpenIDFedClientRegistrationTypes(
  goidc.ClientRegistrationTypeAutomatic,
  goidc.ClientRegistrationTypeExplicit,
)

With automatic registration, the provider resolves the trust chain by fetching entity configurations and subordinate statements. With explicit registration, the client provides the trust chain directly.

Trust Marks

Require specific trust marks from clients:

provider.WithOpenIDFedRequiredTrustMarksFunc(
  func(_ context.Context, _ *goidc.Client) []goidc.TrustMark {
    return []goidc.TrustMark{"https://trust-anchor.example.com/marks/certified"}
  },
)

Include trust marks in the provider's entity configuration:

provider.WithOpenIDFedTrustMark(
  "https://trust-anchor.example.com/marks/certified",
  "https://trust-mark-issuer.example.com",
)
Additional Options
provider.WithOpenIDFedSignatureAlgs(goidc.RS256, goidc.PS256)
provider.WithOpenIDFedTrustChainMaxDepth(5)
provider.WithOpenIDFedOrganizationName("Example Organization")
provider.WithOpenIDFedHTTPClientFunc(func(_ context.Context) *http.Client {
  return customHTTPClient
})

Shared Signals Framework (SSF)

The Shared Signals Framework allows the provider to act as an SSF transmitter, publishing Security Event Tokens (SETs) to receivers. go-oidc supports CAEP and RISC event types.

op, _ := provider.New(
  ...,
  provider.WithSSF(
    func(_ context.Context) (goidc.JSONWebKeySet, error) {
      return ssfJWKS, nil
    },
    func(ctx context.Context) (goidc.SSFReceiver, error) {
      return goidc.SSFReceiver{ID: "receiver"}, nil
    },
  ),
  provider.WithSSFEventTypes(goidc.SSFEventTypeCAEPSessionRevoked, goidc.SSFEventTypeCAEPCredentialChange),
  provider.WithSSFDeliveryMethods(goidc.SSFDeliveryMethodPoll, goidc.SSFDeliveryMethodPush),
  ...,
)

The transmitter configuration is exposed at GET /.well-known/ssf-configuration.

Push delivery (RFC 8935) sends SETs to a receiver-provided endpoint. Poll delivery (RFC 8936) lets receivers fetch pending events from /ssf/poll.

To publish events:

op.PublishSSFEvent(ctx, streamID, goidc.SSFEvent{
  Type: goidc.SSFEventTypeCAEPSessionRevoked,
  Subject: goidc.SSFSubject{
    Format: goidc.SSFSubjectFormatEmail,
    Email:  "user@example.com",
  },
})

Additional options:

// Allow receivers to update stream status (enabled/paused/disabled).
provider.WithSSFEventStreamStatusManagement()
// Allow receivers to add/remove subjects from a stream.
provider.WithSSFEventStreamSubjectManagement()
// Allow receivers to request verification events.
provider.WithSSFEventStreamVerification(func(ctx context.Context, streamID string, opts goidc.SSFStreamVerificationOptions) error {
  // Schedule the verification event for async delivery.
  return nil
})

For production, replace the in-memory SSF storage with persistent implementations using provider.WithSSFEventStreamManager and provider.WithSSFEventPollManager.

For a complete example, see examples/ssf.

Directories

Path Synopsis
examples
authutil
Package authutil contains utilities to set up example authorization server using goidc.
Package authutil contains utilities to set up example authorization server using goidc.
fapi1_op_mtls command
Example fapi1_op_mtls demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with mTLS authentication.
Example fapi1_op_mtls demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with mTLS authentication.
fapi1_op_mtls_jarm command
Example fapi1_op_mtls_jarm demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with mTLS authentication and JARM.
Example fapi1_op_mtls_jarm demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with mTLS authentication and JARM.
fapi1_op_mtls_par command
Example fapi1_op_mtls_par demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with mTLS authentication and PAR.
Example fapi1_op_mtls_par demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with mTLS authentication and PAR.
fapi1_op_mtls_par_jarm command
Example fapi1_op_mtls_par_jarm demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with mTLS authentication and PAR and JARM.
Example fapi1_op_mtls_par_jarm demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with mTLS authentication and PAR and JARM.
fapi1_op_private_key command
Example fapi1_op_private_key demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with private key JWT authentication.
Example fapi1_op_private_key demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with private key JWT authentication.
fapi1_op_private_key_jarm command
Example fapi1_op_private_key_jarm demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with private key JWT authentication and JARM.
Example fapi1_op_private_key_jarm demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with private key JWT authentication and JARM.
fapi1_op_private_key_par command
Example fapi1_op_private_key_par demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with private key JWT authentication and PAR.
Example fapi1_op_private_key_par demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with private key JWT authentication and PAR.
fapi1_op_private_key_par_jarm command
Example fapi1_op_private_key_par_jarm demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with private key JWT authentication and PAR and JARM.
Example fapi1_op_private_key_par_jarm demonstrates the implementation of a FAPI1 Advanced Security Profile OpenID Provider with private key JWT authentication and PAR and JARM.
fapi2_ms_op_jar command
Example fapi2_ms_op_jar demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with JAR.
Example fapi2_ms_op_jar demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with JAR.
fapi2_ms_op_jarm command
Example fapi2_ms_op_jarm demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with JARM.
Example fapi2_ms_op_jarm demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with JARM.
fapi2_rar command
Example fapi2_rar demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with rich authorization requests.
Example fapi2_rar demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with rich authorization requests.
fapi2_sp_op_mtls_dpop command
Example fapi2_sp_op_mtls_dpop demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with mTLS authentication and DPoP tokens.
Example fapi2_sp_op_mtls_dpop demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with mTLS authentication and DPoP tokens.
fapi2_sp_op_mtls_mtls command
Example fapi2_sp_op_mtls_mtls demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with mTLS authentication and sender constrained tokens using TLS certificates.
Example fapi2_sp_op_mtls_mtls demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with mTLS authentication and sender constrained tokens using TLS certificates.
fapi2_sp_op_private_key_dpop command
Example fapi2_sp_op_private_key_dpop demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with private key JWT authentication and DPoP tokens.
Example fapi2_sp_op_private_key_dpop demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with private key JWT authentication and DPoP tokens.
fapi2_sp_op_private_key_mtls command
Example fapi2_sp_op_private_key_mtls demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with private key JWT authentication and sender constrained tokens using TLS certificates.
Example fapi2_sp_op_private_key_mtls demonstrates the implementation of a FAPI2 Security Profile OpenID Provider with private key JWT authentication and sender constrained tokens using TLS certificates.
fapiciba command
Example fapiciba demonstrates the implementation of an Authorization Server that complies with the FAPI CIBA specification.
Example fapiciba demonstrates the implementation of an Authorization Server that complies with the FAPI CIBA specification.
federation command
multitenancy command
Example oidc demonstrates the implementation of an Authorization Server that complies with the OpenID Connect specifications.
Example oidc demonstrates the implementation of an Authorization Server that complies with the OpenID Connect specifications.
oidc command
Example oidc demonstrates the implementation of an Authorization Server that complies with the OpenID Connect specifications.
Example oidc demonstrates the implementation of an Authorization Server that complies with the OpenID Connect specifications.
ssf command
Example ssf demonstrates the implementation of an Authorization Server that supports Shared Signals Framework (SSF) for security event transmission.
Example ssf demonstrates the implementation of an Authorization Server that supports Shared Signals Framework (SSF) for security event transmission.
ui
internal
authorize
Package authorize handles the implementation of endpoints for authorization requests and pushed authorization requests.
Package authorize handles the implementation of endpoints for authorization requests and pushed authorization requests.
dcr
Package dcr implements the dynamic client registration and management endpoints.
Package dcr implements the dynamic client registration and management endpoints.
discovery
Package discovery implements the endpoints where information about the Open ID Provider and its public JWKS is shared.
Package discovery implements the endpoints where information about the Open ID Provider and its public JWKS is shared.
oidc
Package oidc is a complement of the package goidc containing private structs and functions that are not meant to be accessible for users of goidc.
Package oidc is a complement of the package goidc containing private structs and functions that are not meant to be accessible for users of goidc.
ssf
storage
Package storage provides the default implementations of the storage interfaces goidc.ClientManager, goidc.AuthnSessionManager and goidc.GrantSessionManager.
Package storage provides the default implementations of the storage interfaces goidc.ClientManager, goidc.AuthnSessionManager and goidc.GrantSessionManager.
strutil
Package strutil contains functions to help handling strings.
Package strutil contains functions to help handling strings.
timeutil
Package timeutil provides utilities for working with time in a consistent manner.
Package timeutil provides utilities for working with time in a consistent manner.
token
Package token implements all the logic to issue and validate rules related to access and ID tokens.
Package token implements all the logic to issue and validate rules related to access and ID tokens.
userinfo
Package userinfo implements the user info endpoint.
Package userinfo implements the user info endpoint.
pkg
goidc
Package goidc contains structs and functions which serve as the base to set up and interact with an OpenID Provider.
Package goidc contains structs and functions which serve as the base to set up and interact with an OpenID Provider.
provider
Package provider implements a configurable Open ID provider.
Package provider implements a configurable Open ID provider.

Jump to

Keyboard shortcuts

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