* feat(op): split the access and ID token hint verifiers In zitadel we require different behaviors wrt public key expiry between access tokens and ID token hints. This change splits the two verifiers in the OP. The default is still based on Storage and passed to both verifier fields. * add new options to tests
666 lines
18 KiB
Go
666 lines
18 KiB
Go
package op
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
jose "github.com/go-jose/go-jose/v3"
|
|
"github.com/rs/cors"
|
|
"github.com/zitadel/schema"
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/trace"
|
|
"golang.org/x/exp/slog"
|
|
"golang.org/x/text/language"
|
|
|
|
httphelper "github.com/zitadel/oidc/v3/pkg/http"
|
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
|
)
|
|
|
|
const (
|
|
healthEndpoint = "/healthz"
|
|
readinessEndpoint = "/ready"
|
|
authCallbackPathSuffix = "/callback"
|
|
defaultAuthorizationEndpoint = "authorize"
|
|
defaultTokenEndpoint = "oauth/token"
|
|
defaultIntrospectEndpoint = "oauth/introspect"
|
|
defaultUserinfoEndpoint = "userinfo"
|
|
defaultRevocationEndpoint = "revoke"
|
|
defaultEndSessionEndpoint = "end_session"
|
|
defaultKeysEndpoint = "keys"
|
|
defaultDeviceAuthzEndpoint = "/device_authorization"
|
|
)
|
|
|
|
var (
|
|
DefaultEndpoints = &Endpoints{
|
|
Authorization: NewEndpoint(defaultAuthorizationEndpoint),
|
|
Token: NewEndpoint(defaultTokenEndpoint),
|
|
Introspection: NewEndpoint(defaultIntrospectEndpoint),
|
|
Userinfo: NewEndpoint(defaultUserinfoEndpoint),
|
|
Revocation: NewEndpoint(defaultRevocationEndpoint),
|
|
EndSession: NewEndpoint(defaultEndSessionEndpoint),
|
|
JwksURI: NewEndpoint(defaultKeysEndpoint),
|
|
DeviceAuthorization: NewEndpoint(defaultDeviceAuthzEndpoint),
|
|
}
|
|
|
|
DefaultSupportedClaims = []string{
|
|
"sub",
|
|
"aud",
|
|
"exp",
|
|
"iat",
|
|
"iss",
|
|
"auth_time",
|
|
"nonce",
|
|
"acr",
|
|
"amr",
|
|
"c_hash",
|
|
"at_hash",
|
|
"act",
|
|
"scopes",
|
|
"client_id",
|
|
"azp",
|
|
"preferred_username",
|
|
"name",
|
|
"family_name",
|
|
"given_name",
|
|
"locale",
|
|
"email",
|
|
"email_verified",
|
|
"phone_number",
|
|
"phone_number_verified",
|
|
}
|
|
|
|
defaultCORSOptions = cors.Options{
|
|
AllowCredentials: true,
|
|
AllowedHeaders: []string{
|
|
"Origin",
|
|
"Accept",
|
|
"Accept-Language",
|
|
"Authorization",
|
|
"Content-Type",
|
|
"X-Requested-With",
|
|
},
|
|
AllowedMethods: []string{
|
|
http.MethodGet,
|
|
http.MethodHead,
|
|
http.MethodPost,
|
|
},
|
|
ExposedHeaders: []string{
|
|
"Location",
|
|
"Content-Length",
|
|
},
|
|
AllowOriginFunc: func(_ string) bool {
|
|
return true
|
|
},
|
|
}
|
|
)
|
|
|
|
var tracer trace.Tracer
|
|
|
|
func init() {
|
|
tracer = otel.Tracer("github.com/zitadel/oidc/pkg/op")
|
|
}
|
|
|
|
type OpenIDProvider interface {
|
|
http.Handler
|
|
Configuration
|
|
Storage() Storage
|
|
Decoder() httphelper.Decoder
|
|
Encoder() httphelper.Encoder
|
|
IDTokenHintVerifier(context.Context) *IDTokenHintVerifier
|
|
AccessTokenVerifier(context.Context) *AccessTokenVerifier
|
|
Crypto() Crypto
|
|
DefaultLogoutRedirectURI() string
|
|
Probes() []ProbesFn
|
|
|
|
// EXPERIMENTAL: Will change to log/slog import after we drop support for Go 1.20
|
|
Logger() *slog.Logger
|
|
|
|
// Deprecated: Provider now implements http.Handler directly.
|
|
HttpHandler() http.Handler
|
|
}
|
|
|
|
type HttpInterceptor func(http.Handler) http.Handler
|
|
|
|
type corsOptioner interface {
|
|
CORSOptions() *cors.Options
|
|
}
|
|
|
|
func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) chi.Router {
|
|
router := chi.NewRouter()
|
|
if co, ok := o.(corsOptioner); ok {
|
|
if opts := co.CORSOptions(); opts != nil {
|
|
router.Use(cors.New(*opts).Handler)
|
|
}
|
|
} else {
|
|
router.Use(cors.New(defaultCORSOptions).Handler)
|
|
}
|
|
router.Use(intercept(o.IssuerFromRequest, interceptors...))
|
|
router.HandleFunc(healthEndpoint, healthHandler)
|
|
router.HandleFunc(readinessEndpoint, readyHandler(o.Probes()))
|
|
router.HandleFunc(oidc.DiscoveryEndpoint, discoveryHandler(o, o.Storage()))
|
|
router.HandleFunc(o.AuthorizationEndpoint().Relative(), authorizeHandler(o))
|
|
router.HandleFunc(authCallbackPath(o), authorizeCallbackHandler(o))
|
|
router.HandleFunc(o.TokenEndpoint().Relative(), tokenHandler(o))
|
|
router.HandleFunc(o.IntrospectionEndpoint().Relative(), introspectionHandler(o))
|
|
router.HandleFunc(o.UserinfoEndpoint().Relative(), userinfoHandler(o))
|
|
router.HandleFunc(o.RevocationEndpoint().Relative(), revocationHandler(o))
|
|
router.HandleFunc(o.EndSessionEndpoint().Relative(), endSessionHandler(o))
|
|
router.HandleFunc(o.KeysEndpoint().Relative(), keysHandler(o.Storage()))
|
|
router.HandleFunc(o.DeviceAuthorizationEndpoint().Relative(), DeviceAuthorizationHandler(o))
|
|
return router
|
|
}
|
|
|
|
// AuthCallbackURL builds the url for the redirect (with the requestID) after a successful login
|
|
func AuthCallbackURL(o OpenIDProvider) func(context.Context, string) string {
|
|
return func(ctx context.Context, requestID string) string {
|
|
return o.AuthorizationEndpoint().Absolute(IssuerFromContext(ctx)) + authCallbackPathSuffix + "?id=" + requestID
|
|
}
|
|
}
|
|
|
|
func authCallbackPath(o OpenIDProvider) string {
|
|
return o.AuthorizationEndpoint().Relative() + authCallbackPathSuffix
|
|
}
|
|
|
|
type Config struct {
|
|
CryptoKey [32]byte
|
|
DefaultLogoutRedirectURI string
|
|
CodeMethodS256 bool
|
|
AuthMethodPost bool
|
|
AuthMethodPrivateKeyJWT bool
|
|
GrantTypeRefreshToken bool
|
|
RequestObjectSupported bool
|
|
SupportedUILocales []language.Tag
|
|
SupportedClaims []string
|
|
DeviceAuthorization DeviceAuthorizationConfig
|
|
}
|
|
|
|
// Endpoints defines endpoint routes.
|
|
type Endpoints struct {
|
|
Authorization *Endpoint
|
|
Token *Endpoint
|
|
Introspection *Endpoint
|
|
Userinfo *Endpoint
|
|
Revocation *Endpoint
|
|
EndSession *Endpoint
|
|
CheckSessionIframe *Endpoint
|
|
JwksURI *Endpoint
|
|
DeviceAuthorization *Endpoint
|
|
}
|
|
|
|
// NewOpenIDProvider creates a provider. The provider provides (with HttpHandler())
|
|
// a http.Router that handles a suite of endpoints (some paths can be overridden):
|
|
//
|
|
// /healthz
|
|
// /ready
|
|
// /.well-known/openid-configuration
|
|
// /oauth/token
|
|
// /oauth/introspect
|
|
// /callback
|
|
// /authorize
|
|
// /userinfo
|
|
// /revoke
|
|
// /end_session
|
|
// /keys
|
|
// /device_authorization
|
|
//
|
|
// This does not include login. Login is handled with a redirect that includes the
|
|
// request ID. The redirect for logins is specified per-client by Client.LoginURL().
|
|
// Successful logins should mark the request as authorized and redirect back to to
|
|
// op.AuthCallbackURL(provider) which is probably /callback. On the redirect back
|
|
// to the AuthCallbackURL, the request id should be passed as the "id" parameter.
|
|
//
|
|
// Deprecated: use [NewProvider] with an issuer function direct.
|
|
func NewOpenIDProvider(issuer string, config *Config, storage Storage, opOpts ...Option) (*Provider, error) {
|
|
return NewProvider(config, storage, StaticIssuer(issuer), opOpts...)
|
|
}
|
|
|
|
// NewForwardedOpenIDProvider tries to establishes the issuer from the request Host.
|
|
//
|
|
// Deprecated: use [NewProvider] with an issuer function direct.
|
|
func NewDynamicOpenIDProvider(path string, config *Config, storage Storage, opOpts ...Option) (*Provider, error) {
|
|
return NewProvider(config, storage, IssuerFromHost(path), opOpts...)
|
|
}
|
|
|
|
// NewForwardedOpenIDProvider tries to establish the Issuer from a Forwarded request header, if it is set.
|
|
// See [IssuerFromForwardedOrHost] for details.
|
|
//
|
|
// Deprecated: use [NewProvider] with an issuer function direct.
|
|
func NewForwardedOpenIDProvider(path string, config *Config, storage Storage, opOpts ...Option) (*Provider, error) {
|
|
return NewProvider(config, storage, IssuerFromForwardedOrHost(path), opOpts...)
|
|
}
|
|
|
|
// NewProvider creates a provider with a router on it's embedded http.Handler.
|
|
// Issuer is a function that must return the issuer on every request.
|
|
// Typically [StaticIssuer], [IssuerFromHost] or [IssuerFromForwardedOrHost] can be used.
|
|
//
|
|
// The router handles a suite of endpoints (some paths can be overridden):
|
|
//
|
|
// /healthz
|
|
// /ready
|
|
// /.well-known/openid-configuration
|
|
// /oauth/token
|
|
// /oauth/introspect
|
|
// /callback
|
|
// /authorize
|
|
// /userinfo
|
|
// /revoke
|
|
// /end_session
|
|
// /keys
|
|
// /device_authorization
|
|
//
|
|
// This does not include login. Login is handled with a redirect that includes the
|
|
// request ID. The redirect for logins is specified per-client by Client.LoginURL().
|
|
// Successful logins should mark the request as authorized and redirect back to to
|
|
// op.AuthCallbackURL(provider) which is probably /callback. On the redirect back
|
|
// to the AuthCallbackURL, the request id should be passed as the "id" parameter.
|
|
func NewProvider(config *Config, storage Storage, issuer func(insecure bool) (IssuerFromRequest, error), opOpts ...Option) (_ *Provider, err error) {
|
|
keySet := &OpenIDKeySet{storage}
|
|
o := &Provider{
|
|
config: config,
|
|
storage: storage,
|
|
accessTokenKeySet: keySet,
|
|
idTokenHinKeySet: keySet,
|
|
endpoints: DefaultEndpoints,
|
|
timer: make(<-chan time.Time),
|
|
corsOpts: &defaultCORSOptions,
|
|
logger: slog.Default(),
|
|
}
|
|
|
|
for _, optFunc := range opOpts {
|
|
if err := optFunc(o); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
o.issuer, err = issuer(o.insecure)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
o.Handler = CreateRouter(o, o.interceptors...)
|
|
o.decoder = schema.NewDecoder()
|
|
o.decoder.IgnoreUnknownKeys(true)
|
|
o.encoder = oidc.NewEncoder()
|
|
o.crypto = NewAESCrypto(config.CryptoKey)
|
|
return o, nil
|
|
}
|
|
|
|
type Provider struct {
|
|
http.Handler
|
|
config *Config
|
|
issuer IssuerFromRequest
|
|
insecure bool
|
|
endpoints *Endpoints
|
|
storage Storage
|
|
accessTokenKeySet oidc.KeySet
|
|
idTokenHinKeySet oidc.KeySet
|
|
crypto Crypto
|
|
decoder *schema.Decoder
|
|
encoder *schema.Encoder
|
|
interceptors []HttpInterceptor
|
|
timer <-chan time.Time
|
|
accessTokenVerifierOpts []AccessTokenVerifierOpt
|
|
idTokenHintVerifierOpts []IDTokenHintVerifierOpt
|
|
corsOpts *cors.Options
|
|
logger *slog.Logger
|
|
}
|
|
|
|
func (o *Provider) IssuerFromRequest(r *http.Request) string {
|
|
return o.issuer(r)
|
|
}
|
|
|
|
func (o *Provider) Insecure() bool {
|
|
return o.insecure
|
|
}
|
|
|
|
func (o *Provider) AuthorizationEndpoint() *Endpoint {
|
|
return o.endpoints.Authorization
|
|
}
|
|
|
|
func (o *Provider) TokenEndpoint() *Endpoint {
|
|
return o.endpoints.Token
|
|
}
|
|
|
|
func (o *Provider) IntrospectionEndpoint() *Endpoint {
|
|
return o.endpoints.Introspection
|
|
}
|
|
|
|
func (o *Provider) UserinfoEndpoint() *Endpoint {
|
|
return o.endpoints.Userinfo
|
|
}
|
|
|
|
func (o *Provider) RevocationEndpoint() *Endpoint {
|
|
return o.endpoints.Revocation
|
|
}
|
|
|
|
func (o *Provider) EndSessionEndpoint() *Endpoint {
|
|
return o.endpoints.EndSession
|
|
}
|
|
|
|
func (o *Provider) DeviceAuthorizationEndpoint() *Endpoint {
|
|
return o.endpoints.DeviceAuthorization
|
|
}
|
|
|
|
func (o *Provider) KeysEndpoint() *Endpoint {
|
|
return o.endpoints.JwksURI
|
|
}
|
|
|
|
func (o *Provider) AuthMethodPostSupported() bool {
|
|
return o.config.AuthMethodPost
|
|
}
|
|
|
|
func (o *Provider) CodeMethodS256Supported() bool {
|
|
return o.config.CodeMethodS256
|
|
}
|
|
|
|
func (o *Provider) AuthMethodPrivateKeyJWTSupported() bool {
|
|
return o.config.AuthMethodPrivateKeyJWT
|
|
}
|
|
|
|
func (o *Provider) TokenEndpointSigningAlgorithmsSupported() []string {
|
|
return []string{"RS256"}
|
|
}
|
|
|
|
func (o *Provider) GrantTypeRefreshTokenSupported() bool {
|
|
return o.config.GrantTypeRefreshToken
|
|
}
|
|
|
|
func (o *Provider) GrantTypeTokenExchangeSupported() bool {
|
|
_, ok := o.storage.(TokenExchangeStorage)
|
|
return ok
|
|
}
|
|
|
|
func (o *Provider) GrantTypeJWTAuthorizationSupported() bool {
|
|
return true
|
|
}
|
|
|
|
func (o *Provider) GrantTypeDeviceCodeSupported() bool {
|
|
_, ok := o.storage.(DeviceAuthorizationStorage)
|
|
return ok
|
|
}
|
|
|
|
func (o *Provider) IntrospectionAuthMethodPrivateKeyJWTSupported() bool {
|
|
return true
|
|
}
|
|
|
|
func (o *Provider) IntrospectionEndpointSigningAlgorithmsSupported() []string {
|
|
return []string{"RS256"}
|
|
}
|
|
|
|
func (o *Provider) GrantTypeClientCredentialsSupported() bool {
|
|
_, ok := o.storage.(ClientCredentialsStorage)
|
|
return ok
|
|
}
|
|
|
|
func (o *Provider) RevocationAuthMethodPrivateKeyJWTSupported() bool {
|
|
return true
|
|
}
|
|
|
|
func (o *Provider) RevocationEndpointSigningAlgorithmsSupported() []string {
|
|
return []string{"RS256"}
|
|
}
|
|
|
|
func (o *Provider) RequestObjectSupported() bool {
|
|
return o.config.RequestObjectSupported
|
|
}
|
|
|
|
func (o *Provider) RequestObjectSigningAlgorithmsSupported() []string {
|
|
return []string{"RS256"}
|
|
}
|
|
|
|
func (o *Provider) SupportedUILocales() []language.Tag {
|
|
return o.config.SupportedUILocales
|
|
}
|
|
|
|
func (o *Provider) DeviceAuthorization() DeviceAuthorizationConfig {
|
|
return o.config.DeviceAuthorization
|
|
}
|
|
|
|
func (o *Provider) Storage() Storage {
|
|
return o.storage
|
|
}
|
|
|
|
func (o *Provider) Decoder() httphelper.Decoder {
|
|
return o.decoder
|
|
}
|
|
|
|
func (o *Provider) Encoder() httphelper.Encoder {
|
|
return o.encoder
|
|
}
|
|
|
|
func (o *Provider) IDTokenHintVerifier(ctx context.Context) *IDTokenHintVerifier {
|
|
return NewIDTokenHintVerifier(IssuerFromContext(ctx), o.idTokenHinKeySet, o.idTokenHintVerifierOpts...)
|
|
}
|
|
|
|
func (o *Provider) JWTProfileVerifier(ctx context.Context) *JWTProfileVerifier {
|
|
return NewJWTProfileVerifier(o.Storage(), IssuerFromContext(ctx), 1*time.Hour, time.Second)
|
|
}
|
|
|
|
func (o *Provider) AccessTokenVerifier(ctx context.Context) *AccessTokenVerifier {
|
|
return NewAccessTokenVerifier(IssuerFromContext(ctx), o.accessTokenKeySet, o.accessTokenVerifierOpts...)
|
|
}
|
|
|
|
func (o *Provider) Crypto() Crypto {
|
|
return o.crypto
|
|
}
|
|
|
|
func (o *Provider) DefaultLogoutRedirectURI() string {
|
|
return o.config.DefaultLogoutRedirectURI
|
|
}
|
|
|
|
func (o *Provider) Probes() []ProbesFn {
|
|
return []ProbesFn{
|
|
ReadyStorage(o.Storage()),
|
|
}
|
|
}
|
|
|
|
func (o *Provider) CORSOptions() *cors.Options {
|
|
return o.corsOpts
|
|
}
|
|
|
|
func (o *Provider) Logger() *slog.Logger {
|
|
return o.logger
|
|
}
|
|
|
|
// Deprecated: Provider now implements http.Handler directly.
|
|
func (o *Provider) HttpHandler() http.Handler {
|
|
return o
|
|
}
|
|
|
|
type OpenIDKeySet struct {
|
|
Storage
|
|
}
|
|
|
|
// VerifySignature implements the oidc.KeySet interface
|
|
// providing an implementation for the keys stored in the OP Storage interface
|
|
func (o *OpenIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
|
|
keySet, err := o.Storage.KeySet(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error fetching keys: %w", err)
|
|
}
|
|
keyID, alg := oidc.GetKeyIDAndAlg(jws)
|
|
key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, jsonWebKeySet(keySet).Keys...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid signature: %w", err)
|
|
}
|
|
return jws.Verify(&key)
|
|
}
|
|
|
|
type Option func(o *Provider) error
|
|
|
|
// WithAllowInsecure allows the use of http (instead of https) for issuers
|
|
// this is not recommended for production use and violates the OIDC specification
|
|
func WithAllowInsecure() Option {
|
|
return func(o *Provider) error {
|
|
o.insecure = true
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithCustomAuthEndpoint(endpoint *Endpoint) Option {
|
|
return func(o *Provider) error {
|
|
if err := endpoint.Validate(); err != nil {
|
|
return err
|
|
}
|
|
o.endpoints.Authorization = endpoint
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithCustomTokenEndpoint(endpoint *Endpoint) Option {
|
|
return func(o *Provider) error {
|
|
if err := endpoint.Validate(); err != nil {
|
|
return err
|
|
}
|
|
o.endpoints.Token = endpoint
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithCustomIntrospectionEndpoint(endpoint *Endpoint) Option {
|
|
return func(o *Provider) error {
|
|
if err := endpoint.Validate(); err != nil {
|
|
return err
|
|
}
|
|
o.endpoints.Introspection = endpoint
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithCustomUserinfoEndpoint(endpoint *Endpoint) Option {
|
|
return func(o *Provider) error {
|
|
if err := endpoint.Validate(); err != nil {
|
|
return err
|
|
}
|
|
o.endpoints.Userinfo = endpoint
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithCustomRevocationEndpoint(endpoint *Endpoint) Option {
|
|
return func(o *Provider) error {
|
|
if err := endpoint.Validate(); err != nil {
|
|
return err
|
|
}
|
|
o.endpoints.Revocation = endpoint
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithCustomEndSessionEndpoint(endpoint *Endpoint) Option {
|
|
return func(o *Provider) error {
|
|
if err := endpoint.Validate(); err != nil {
|
|
return err
|
|
}
|
|
o.endpoints.EndSession = endpoint
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithCustomKeysEndpoint(endpoint *Endpoint) Option {
|
|
return func(o *Provider) error {
|
|
if err := endpoint.Validate(); err != nil {
|
|
return err
|
|
}
|
|
o.endpoints.JwksURI = endpoint
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithCustomDeviceAuthorizationEndpoint(endpoint *Endpoint) Option {
|
|
return func(o *Provider) error {
|
|
if err := endpoint.Validate(); err != nil {
|
|
return err
|
|
}
|
|
o.endpoints.DeviceAuthorization = endpoint
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithCustomEndpoints sets multiple endpoints at once.
|
|
// Non of the endpoints may be nil, or an error will
|
|
// be returned when the Option used by the Provider.
|
|
func WithCustomEndpoints(auth, token, userInfo, revocation, endSession, keys *Endpoint) Option {
|
|
return func(o *Provider) error {
|
|
for _, e := range []*Endpoint{auth, token, userInfo, revocation, endSession, keys} {
|
|
if err := e.Validate(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
o.endpoints.Authorization = auth
|
|
o.endpoints.Token = token
|
|
o.endpoints.Userinfo = userInfo
|
|
o.endpoints.Revocation = revocation
|
|
o.endpoints.EndSession = endSession
|
|
o.endpoints.JwksURI = keys
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithHttpInterceptors(interceptors ...HttpInterceptor) Option {
|
|
return func(o *Provider) error {
|
|
o.interceptors = append(o.interceptors, interceptors...)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithAccessTokenKeySet allows passing a KeySet with public keys for Access Token verification.
|
|
// The default KeySet uses the [Storage] interface
|
|
func WithAccessTokenKeySet(keySet oidc.KeySet) Option {
|
|
return func(o *Provider) error {
|
|
o.accessTokenKeySet = keySet
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithAccessTokenVerifierOpts(opts ...AccessTokenVerifierOpt) Option {
|
|
return func(o *Provider) error {
|
|
o.accessTokenVerifierOpts = opts
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithIDTokenHintKeySet allows passing a KeySet with public keys for ID Token Hint verification.
|
|
// The default KeySet uses the [Storage] interface.
|
|
func WithIDTokenHintKeySet(keySet oidc.KeySet) Option {
|
|
return func(o *Provider) error {
|
|
o.idTokenHinKeySet = keySet
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithIDTokenHintVerifierOpts(opts ...IDTokenHintVerifierOpt) Option {
|
|
return func(o *Provider) error {
|
|
o.idTokenHintVerifierOpts = opts
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithCORSOptions(opts *cors.Options) Option {
|
|
return func(o *Provider) error {
|
|
o.corsOpts = opts
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithLogger lets a logger other than slog.Default().
|
|
//
|
|
// EXPERIMENTAL: Will change to log/slog import after we drop support for Go 1.20
|
|
func WithLogger(logger *slog.Logger) Option {
|
|
return func(o *Provider) error {
|
|
o.logger = logger
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func intercept(i IssuerFromRequest, interceptors ...HttpInterceptor) func(handler http.Handler) http.Handler {
|
|
issuerInterceptor := NewIssuerInterceptor(i)
|
|
return func(handler http.Handler) http.Handler {
|
|
for i := len(interceptors) - 1; i >= 0; i-- {
|
|
handler = interceptors[i](handler)
|
|
}
|
|
return issuerInterceptor.Handler(handler)
|
|
}
|
|
}
|