84c3c3aa9c
- Add `AllowPasswordGrant` boolean field to OIDCIdentityProvider's spec - The oidc upstream watcher controller copies the value of `AllowPasswordGrant` into the configuration of the cached provider - Add password grant to the UpstreamOIDCIdentityProviderI interface which is implemented by the cached provider instance for use in the authorization endpoint - Enhance the IDP discovery endpoint to return the supported "flows" for each IDP ("cli_password" and/or "browser_authcode") - Enhance `pinniped get kubeconfig` to help the user choose the desired flow for the selected IDP, and to write the flow into the resulting kubeconfg - Enhance `pinniped login oidc` to have a flow flag to tell it which client-side flow it should use for auth (CLI-based or browser-based) - In the Dex config, allow the resource owner password grant, which Dex implements to also return ID tokens, for use in integration tests - Enhance the authorize endpoint to perform password grant when requested by the incoming headers. This commit does not include unit tests for the enhancements to the authorize endpoint, which will come in the next commit - Extract some shared helpers from the callback endpoint to share the code with the authorize endpoint - Add new integration tests
118 lines
4.4 KiB
Go
118 lines
4.4 KiB
Go
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package provider
|
|
|
|
import (
|
|
"context"
|
|
"net/url"
|
|
"sync"
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
"go.pinniped.dev/internal/authenticators"
|
|
"go.pinniped.dev/pkg/oidcclient/nonce"
|
|
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
|
"go.pinniped.dev/pkg/oidcclient/pkce"
|
|
)
|
|
|
|
type UpstreamOIDCIdentityProviderI interface {
|
|
// GetName returns a name for this upstream provider, which will be used as a component of the path for the
|
|
// callback endpoint hosted by the Supervisor.
|
|
GetName() string
|
|
|
|
// GetClientID returns the OAuth client ID registered with the upstream provider to be used in the authorization code flow.
|
|
GetClientID() string
|
|
|
|
// GetAuthorizationURL returns the Authorization Endpoint fetched from discovery.
|
|
GetAuthorizationURL() *url.URL
|
|
|
|
// GetScopes returns the scopes to request in authorization (authcode or password grant) flow.
|
|
GetScopes() []string
|
|
|
|
// GetUsernameClaim returns the ID Token username claim name. May return empty string, in which case we
|
|
// will use some reasonable defaults.
|
|
GetUsernameClaim() string
|
|
|
|
// GetGroupsClaim returns the ID Token groups claim name. May return empty string, in which case we won't
|
|
// try to read groups from the upstream provider.
|
|
GetGroupsClaim() string
|
|
|
|
// AllowsPasswordGrant returns true if a client should be allowed to use the resource owner password credentials grant
|
|
// flow with this upstream provider. When false, it should not be allowed.
|
|
AllowsPasswordGrant() bool
|
|
|
|
// PasswordCredentialsGrantAndValidateTokens performs upstream OIDC resource owner password credentials grant and
|
|
// token validation. Returns the validated raw tokens as well as the parsed claims of the ID token.
|
|
PasswordCredentialsGrantAndValidateTokens(ctx context.Context, username, password string) (*oidctypes.Token, error)
|
|
|
|
// ExchangeAuthcodeAndValidateTokens performs upstream OIDC authorization code exchange and token validation.
|
|
// Returns the validated raw tokens as well as the parsed claims of the ID token.
|
|
ExchangeAuthcodeAndValidateTokens(
|
|
ctx context.Context,
|
|
authcode string,
|
|
pkceCodeVerifier pkce.Code,
|
|
expectedIDTokenNonce nonce.Nonce,
|
|
redirectURI string,
|
|
) (*oidctypes.Token, error)
|
|
|
|
ValidateToken(ctx context.Context, tok *oauth2.Token, expectedIDTokenNonce nonce.Nonce) (*oidctypes.Token, error)
|
|
}
|
|
|
|
type UpstreamLDAPIdentityProviderI interface {
|
|
// GetName returns a name for this upstream provider.
|
|
GetName() string
|
|
|
|
// GetURL returns a URL which uniquely identifies this LDAP provider, e.g. "ldaps://host.example.com:1234".
|
|
// This URL is not used for connecting to the provider, but rather is used for creating a globally unique user
|
|
// identifier by being combined with the user's UID, since user UIDs are only unique within one provider.
|
|
GetURL() *url.URL
|
|
|
|
// UserAuthenticator adds an interface method for performing user authentication against the upstream LDAP provider.
|
|
authenticators.UserAuthenticator
|
|
}
|
|
|
|
type DynamicUpstreamIDPProvider interface {
|
|
SetOIDCIdentityProviders(oidcIDPs []UpstreamOIDCIdentityProviderI)
|
|
GetOIDCIdentityProviders() []UpstreamOIDCIdentityProviderI
|
|
SetLDAPIdentityProviders(ldapIDPs []UpstreamLDAPIdentityProviderI)
|
|
GetLDAPIdentityProviders() []UpstreamLDAPIdentityProviderI
|
|
}
|
|
|
|
type dynamicUpstreamIDPProvider struct {
|
|
oidcUpstreams []UpstreamOIDCIdentityProviderI
|
|
ldapUpstreams []UpstreamLDAPIdentityProviderI
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
func NewDynamicUpstreamIDPProvider() DynamicUpstreamIDPProvider {
|
|
return &dynamicUpstreamIDPProvider{
|
|
oidcUpstreams: []UpstreamOIDCIdentityProviderI{},
|
|
ldapUpstreams: []UpstreamLDAPIdentityProviderI{},
|
|
}
|
|
}
|
|
|
|
func (p *dynamicUpstreamIDPProvider) SetOIDCIdentityProviders(oidcIDPs []UpstreamOIDCIdentityProviderI) {
|
|
p.mutex.Lock() // acquire a write lock
|
|
defer p.mutex.Unlock()
|
|
p.oidcUpstreams = oidcIDPs
|
|
}
|
|
|
|
func (p *dynamicUpstreamIDPProvider) GetOIDCIdentityProviders() []UpstreamOIDCIdentityProviderI {
|
|
p.mutex.RLock() // acquire a read lock
|
|
defer p.mutex.RUnlock()
|
|
return p.oidcUpstreams
|
|
}
|
|
|
|
func (p *dynamicUpstreamIDPProvider) SetLDAPIdentityProviders(ldapIDPs []UpstreamLDAPIdentityProviderI) {
|
|
p.mutex.Lock() // acquire a write lock
|
|
defer p.mutex.Unlock()
|
|
p.ldapUpstreams = ldapIDPs
|
|
}
|
|
|
|
func (p *dynamicUpstreamIDPProvider) GetLDAPIdentityProviders() []UpstreamLDAPIdentityProviderI {
|
|
p.mutex.RLock() // acquire a read lock
|
|
defer p.mutex.RUnlock()
|
|
return p.ldapUpstreams
|
|
}
|