2015-03-30 21:30:27 +02:00
package providers
2015-06-23 13:23:39 +02:00
import (
2020-05-05 17:53:33 +02:00
"context"
2022-02-15 13:18:32 +02:00
"fmt"
"net/url"
2020-05-05 17:53:33 +02:00
2022-02-15 13:18:32 +02:00
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
2020-09-29 18:44:42 +02:00
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
2022-03-13 12:08:33 +02:00
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
2022-02-15 19:24:48 +02:00
internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
2022-02-15 13:18:32 +02:00
k8serrors "k8s.io/apimachinery/pkg/util/errors"
2015-06-23 13:23:39 +02:00
)
2022-03-13 12:08:33 +02:00
const (
CodeChallengeMethodPlain = "plain"
CodeChallengeMethodS256 = "S256"
)
2018-12-20 12:37:59 +02:00
// Provider represents an upstream identity provider implementation
2015-03-30 21:30:27 +02:00
type Provider interface {
Data ( ) * ProviderData
2022-03-13 12:08:33 +02:00
GetLoginURL ( redirectURI , finalRedirect , nonce string , extraParams url . Values ) string
Redeem ( ctx context . Context , redirectURI , code , codeVerifier string ) ( * sessions . SessionState , error )
2021-02-17 22:15:45 +02:00
// Deprecated: Migrate to EnrichSession
2020-05-05 17:53:33 +02:00
GetEmailAddress ( ctx context . Context , s * sessions . SessionState ) ( string , error )
2020-10-24 07:06:50 +02:00
EnrichSession ( ctx context . Context , s * sessions . SessionState ) error
2020-09-27 02:29:34 +02:00
Authorize ( ctx context . Context , s * sessions . SessionState ) ( bool , error )
2020-10-24 07:06:50 +02:00
ValidateSession ( ctx context . Context , s * sessions . SessionState ) bool
2021-03-07 01:33:13 +02:00
RefreshSession ( ctx context . Context , s * sessions . SessionState ) ( bool , error )
2020-11-16 04:57:48 +02:00
CreateSessionFromToken ( ctx context . Context , token string ) ( * sessions . SessionState , error )
2015-03-30 21:30:27 +02:00
}
2022-02-15 13:18:32 +02:00
func NewProvider ( providerConfig options . Provider ) ( Provider , error ) {
providerData , err := newProviderDataFromConfig ( providerConfig )
if err != nil {
return nil , fmt . Errorf ( "could not create provider data: %v" , err )
}
switch providerConfig . Type {
case options . ADFSProvider :
return NewADFSProvider ( providerData , providerConfig . ADFSConfig ) , nil
case options . AzureProvider :
return NewAzureProvider ( providerData , providerConfig . AzureConfig ) , nil
case options . BitbucketProvider :
return NewBitbucketProvider ( providerData , providerConfig . BitbucketConfig ) , nil
case options . DigitalOceanProvider :
return NewDigitalOceanProvider ( providerData ) , nil
case options . FacebookProvider :
return NewFacebookProvider ( providerData ) , nil
case options . GitHubProvider :
return NewGitHubProvider ( providerData , providerConfig . GitHubConfig ) , nil
case options . GitLabProvider :
return NewGitLabProvider ( providerData , providerConfig . GitLabConfig )
case options . GoogleProvider :
return NewGoogleProvider ( providerData , providerConfig . GoogleConfig )
case options . KeycloakProvider :
return NewKeycloakProvider ( providerData , providerConfig . KeycloakConfig ) , nil
case options . KeycloakOIDCProvider :
return NewKeycloakOIDCProvider ( providerData , providerConfig . KeycloakConfig ) , nil
case options . LinkedInProvider :
return NewLinkedInProvider ( providerData ) , nil
case options . LoginGovProvider :
return NewLoginGovProvider ( providerData , providerConfig . LoginGovConfig )
case options . NextCloudProvider :
return NewNextcloudProvider ( providerData ) , nil
case options . OIDCProvider :
return NewOIDCProvider ( providerData , providerConfig . OIDCConfig ) , nil
default :
return nil , fmt . Errorf ( "unknown provider type %q" , providerConfig . Type )
}
}
func newProviderDataFromConfig ( providerConfig options . Provider ) ( * ProviderData , error ) {
p := & ProviderData {
Scope : providerConfig . Scope ,
ClientID : providerConfig . ClientID ,
ClientSecret : providerConfig . ClientSecret ,
ClientSecretFile : providerConfig . ClientSecretFile ,
}
needsVerifier , err := providerRequiresOIDCProviderVerifier ( providerConfig . Type )
if err != nil {
return nil , err
}
if needsVerifier {
2022-02-16 17:35:59 +02:00
pv , err := internaloidc . NewProviderVerifier ( context . TODO ( ) , internaloidc . ProviderVerifierOptions {
AudienceClaims : providerConfig . OIDCConfig . AudienceClaims ,
ClientID : providerConfig . ClientID ,
ExtraAudiences : providerConfig . OIDCConfig . ExtraAudiences ,
IssuerURL : providerConfig . OIDCConfig . IssuerURL ,
JWKsURL : providerConfig . OIDCConfig . JwksURL ,
SkipDiscovery : providerConfig . OIDCConfig . SkipDiscovery ,
SkipIssuerVerification : providerConfig . OIDCConfig . InsecureSkipIssuerVerification ,
} )
2022-02-15 13:18:32 +02:00
if err != nil {
2022-02-16 17:35:59 +02:00
return nil , fmt . Errorf ( "error building OIDC ProviderVerifier: %v" , err )
2022-02-15 13:18:32 +02:00
}
2022-02-16 17:35:59 +02:00
p . Verifier = pv . Verifier ( )
if pv . DiscoveryEnabled ( ) {
2022-02-15 13:18:32 +02:00
// Use the discovered values rather than any specified values
2022-02-16 17:35:59 +02:00
endpoints := pv . Provider ( ) . Endpoints ( )
2022-03-13 12:08:33 +02:00
pkce := pv . Provider ( ) . PKCE ( )
2022-02-16 17:35:59 +02:00
providerConfig . LoginURL = endpoints . AuthURL
providerConfig . RedeemURL = endpoints . TokenURL
providerConfig . ProfileURL = endpoints . UserInfoURL
providerConfig . OIDCConfig . JwksURL = endpoints . JWKsURL
2022-03-13 12:08:33 +02:00
p . SupportedCodeChallengeMethods = pkce . CodeChallengeAlgs
2022-02-15 13:18:32 +02:00
}
}
errs := [ ] error { }
for name , u := range map [ string ] struct {
2022-02-16 18:47:55 +02:00
dst * * url . URL
2022-02-15 13:18:32 +02:00
raw string
} {
2022-02-16 18:47:55 +02:00
"login" : { dst : & p . LoginURL , raw : providerConfig . LoginURL } ,
"redeem" : { dst : & p . RedeemURL , raw : providerConfig . RedeemURL } ,
"profile" : { dst : & p . ProfileURL , raw : providerConfig . ProfileURL } ,
"validate" : { dst : & p . ValidateURL , raw : providerConfig . ValidateURL } ,
"resource" : { dst : & p . ProtectedResource , raw : providerConfig . ProtectedResource } ,
2022-02-15 13:18:32 +02:00
} {
var err error
2022-02-16 18:47:55 +02:00
* u . dst , err = url . Parse ( u . raw )
2022-02-15 13:18:32 +02:00
if err != nil {
errs = append ( errs , fmt . Errorf ( "could not parse %s URL: %v" , name , err ) )
}
}
2022-02-16 18:18:51 +02:00
// handle LoginURLParameters
errs = append ( errs , p . compileLoginParams ( providerConfig . LoginURLParameters ) ... )
2022-02-15 13:18:32 +02:00
if len ( errs ) > 0 {
return nil , k8serrors . NewAggregate ( errs )
}
// Make the OIDC options available to all providers that support it
p . AllowUnverifiedEmail = providerConfig . OIDCConfig . InsecureAllowUnverifiedEmail
p . EmailClaim = providerConfig . OIDCConfig . EmailClaim
p . GroupsClaim = providerConfig . OIDCConfig . GroupsClaim
2022-03-13 12:08:33 +02:00
// Set PKCE enabled or disabled based on discovery and force options
p . CodeChallengeMethod = parseCodeChallengeMethod ( providerConfig )
if len ( p . SupportedCodeChallengeMethods ) != 0 && p . CodeChallengeMethod == "" {
logger . Printf ( "Warning: Your provider supports PKCE methods %+q, but you have not enabled one with --code-challenge-method" , p . SupportedCodeChallengeMethods )
}
2022-12-05 21:55:19 +02:00
if providerConfig . OIDCConfig . UserIDClaim == "" {
providerConfig . OIDCConfig . UserIDClaim = "email"
}
2022-02-15 13:18:32 +02:00
// TODO (@NickMeves) - Remove This
// Backwards Compatibility for Deprecated UserIDClaim option
if providerConfig . OIDCConfig . EmailClaim == options . OIDCEmailClaim &&
providerConfig . OIDCConfig . UserIDClaim != options . OIDCEmailClaim {
p . EmailClaim = providerConfig . OIDCConfig . UserIDClaim
}
2022-12-23 11:00:57 +02:00
if providerConfig . Type == "oidc" && p . Scope == "" {
2022-02-16 18:47:55 +02:00
p . Scope = "openid email profile"
2022-02-15 13:18:32 +02:00
if len ( providerConfig . AllowedGroups ) > 0 {
2022-02-16 18:47:55 +02:00
p . Scope += " groups"
2022-02-15 13:18:32 +02:00
}
}
p . setAllowedGroups ( providerConfig . AllowedGroups )
return p , nil
}
2022-03-13 12:08:33 +02:00
// Pick the most appropriate code challenge method for PKCE
// At this time we do not consider what the server supports to be safe and
// only enable PKCE if the user opts-in
func parseCodeChallengeMethod ( providerConfig options . Provider ) string {
switch {
case providerConfig . CodeChallengeMethod != "" :
return providerConfig . CodeChallengeMethod
default :
return ""
}
}
2022-02-15 13:18:32 +02:00
func providerRequiresOIDCProviderVerifier ( providerType options . ProviderType ) ( bool , error ) {
switch providerType {
case options . BitbucketProvider , options . DigitalOceanProvider , options . FacebookProvider , options . GitHubProvider ,
options . GoogleProvider , options . KeycloakProvider , options . LinkedInProvider , options . LoginGovProvider , options . NextCloudProvider :
return false , nil
case options . ADFSProvider , options . AzureProvider , options . GitLabProvider , options . KeycloakOIDCProvider , options . OIDCProvider :
return true , nil
2020-11-11 07:53:56 +02:00
default :
2022-02-15 13:18:32 +02:00
return false , fmt . Errorf ( "unknown provider type: %s" , providerType )
}
}