func (c *AuthConfig) getAuthenticationRequestHandler() (authenticator.Request, error) { var authRequestHandlers []authenticator.Request if c.SessionAuth != nil { authRequestHandlers = append(authRequestHandlers, c.SessionAuth) } for _, identityProvider := range c.Options.IdentityProviders { identityMapper, err := identitymapper.NewIdentityUserMapper(c.IdentityRegistry, c.UserRegistry, identitymapper.MappingMethodType(identityProvider.MappingMethod)) if err != nil { return nil, err } if configapi.IsPasswordAuthenticator(identityProvider) { passwordAuthenticator, err := c.getPasswordAuthenticator(identityProvider) if err != nil { return nil, err } authRequestHandlers = append(authRequestHandlers, basicauthrequest.NewBasicAuthAuthentication(identityProvider.Name, passwordAuthenticator, true)) } else { switch provider := identityProvider.Provider.(type) { case (*configapi.RequestHeaderIdentityProvider): var authRequestHandler authenticator.Request authRequestConfig := &headerrequest.Config{ IDHeaders: provider.Headers, NameHeaders: provider.NameHeaders, EmailHeaders: provider.EmailHeaders, PreferredUsernameHeaders: provider.PreferredUsernameHeaders, } authRequestHandler = headerrequest.NewAuthenticator(identityProvider.Name, authRequestConfig, identityMapper) // Wrap with an x509 verifier if len(provider.ClientCA) > 0 { caData, err := ioutil.ReadFile(provider.ClientCA) if err != nil { return nil, fmt.Errorf("Error reading %s: %v", provider.ClientCA, err) } opts := x509request.DefaultVerifyOptions() opts.Roots = x509.NewCertPool() if ok := opts.Roots.AppendCertsFromPEM(caData); !ok { return nil, fmt.Errorf("Error loading certs from %s: %v", provider.ClientCA, err) } authRequestHandler = x509request.NewVerifier(opts, authRequestHandler, sets.NewString(provider.ClientCommonNames...)) } authRequestHandlers = append(authRequestHandlers, authRequestHandler) } } } authRequestHandler := unionrequest.NewUnionAuthentication(authRequestHandlers...) return authRequestHandler, nil }
func ValidateOAuthConfig(config *api.OAuthConfig) ValidationResults { validationResults := ValidationResults{} if len(config.MasterURL) == 0 { validationResults.AddErrors(fielderrors.NewFieldRequired("masterURL")) } if _, urlErrs := ValidateURL(config.MasterPublicURL, "masterPublicURL"); len(urlErrs) > 0 { validationResults.AddErrors(urlErrs...) } if len(config.AssetPublicURL) == 0 { validationResults.AddErrors(fielderrors.NewFieldRequired("assetPublicURL")) } if config.SessionConfig != nil { validationResults.AddErrors(ValidateSessionConfig(config.SessionConfig).Prefix("sessionConfig")...) } validationResults.AddErrors(ValidateGrantConfig(config.GrantConfig).Prefix("grantConfig")...) providerNames := util.NewStringSet() redirectingIdentityProviders := []string{} for i, identityProvider := range config.IdentityProviders { if identityProvider.UseAsLogin { redirectingIdentityProviders = append(redirectingIdentityProviders, identityProvider.Name) if api.IsPasswordAuthenticator(identityProvider) { if config.SessionConfig == nil { validationResults.AddErrors(fielderrors.NewFieldInvalid("sessionConfig", config, "sessionConfig is required if a password identity provider is used for browser based login")) } } } validationResults.Append(ValidateIdentityProvider(identityProvider).Prefix(fmt.Sprintf("identityProvider[%d]", i))) if len(identityProvider.Name) > 0 { if providerNames.Has(identityProvider.Name) { validationResults.AddErrors(fielderrors.NewFieldInvalid(fmt.Sprintf("identityProvider[%d].name", i), identityProvider.Name, "must have a unique name")) } providerNames.Insert(identityProvider.Name) } } if len(redirectingIdentityProviders) > 1 { validationResults.AddErrors(fielderrors.NewFieldInvalid("identityProviders", config.IdentityProviders, fmt.Sprintf("only one identity provider can support login for a browser, found: %v", redirectingIdentityProviders))) } return validationResults }
func ValidateOAuthConfig(config *api.OAuthConfig) ValidationResults { validationResults := ValidationResults{} if len(config.MasterURL) == 0 { validationResults.AddErrors(fielderrors.NewFieldRequired("masterURL")) } if _, urlErrs := ValidateURL(config.MasterPublicURL, "masterPublicURL"); len(urlErrs) > 0 { validationResults.AddErrors(urlErrs...) } if len(config.AssetPublicURL) == 0 { validationResults.AddErrors(fielderrors.NewFieldRequired("assetPublicURL")) } if config.SessionConfig != nil { validationResults.AddErrors(ValidateSessionConfig(config.SessionConfig).Prefix("sessionConfig")...) } validationResults.AddErrors(ValidateGrantConfig(config.GrantConfig).Prefix("grantConfig")...) providerNames := sets.NewString() redirectingIdentityProviders := []string{} challengeIssuingIdentityProviders := []string{} challengeRedirectingIdentityProviders := []string{} for i, identityProvider := range config.IdentityProviders { if identityProvider.UseAsLogin { redirectingIdentityProviders = append(redirectingIdentityProviders, identityProvider.Name) if api.IsPasswordAuthenticator(identityProvider) { if config.SessionConfig == nil { validationResults.AddErrors(fielderrors.NewFieldInvalid("sessionConfig", config, "sessionConfig is required if a password identity provider is used for browser based login")) } } } if identityProvider.UseAsChallenger { // RequestHeaderIdentityProvider is special, it can only react to challenge clients by redirecting them // Make sure we don't have more than a single redirector, and don't have a mix of challenge issuers and redirectors if _, isRequestHeader := identityProvider.Provider.Object.(*api.RequestHeaderIdentityProvider); isRequestHeader { challengeRedirectingIdentityProviders = append(challengeRedirectingIdentityProviders, identityProvider.Name) } else { challengeIssuingIdentityProviders = append(challengeIssuingIdentityProviders, identityProvider.Name) } } validationResults.Append(ValidateIdentityProvider(identityProvider).Prefix(fmt.Sprintf("identityProvider[%d]", i))) if len(identityProvider.Name) > 0 { if providerNames.Has(identityProvider.Name) { validationResults.AddErrors(fielderrors.NewFieldInvalid(fmt.Sprintf("identityProvider[%d].name", i), identityProvider.Name, "must have a unique name")) } providerNames.Insert(identityProvider.Name) } } if len(redirectingIdentityProviders) > 1 { validationResults.AddErrors(fielderrors.NewFieldInvalid("identityProviders", "login", fmt.Sprintf("only one identity provider can support login for a browser, found: %v", strings.Join(redirectingIdentityProviders, ", ")))) } if len(challengeRedirectingIdentityProviders) > 1 { validationResults.AddErrors(fielderrors.NewFieldInvalid("identityProviders", "challenge", fmt.Sprintf("only one identity provider can redirect clients requesting an authentication challenge, found: %v", strings.Join(challengeRedirectingIdentityProviders, ", ")))) } if len(challengeRedirectingIdentityProviders) > 0 && len(challengeIssuingIdentityProviders) > 0 { validationResults.AddErrors( fielderrors.NewFieldInvalid("identityProviders", "challenge", fmt.Sprintf( "cannot mix providers that redirect clients requesting auth challenges (%s) with providers issuing challenges to those clients (%s)", strings.Join(challengeRedirectingIdentityProviders, ", "), strings.Join(challengeIssuingIdentityProviders, ", "), ))) } if config.Templates != nil && len(config.Templates.Login) > 0 { content, err := ioutil.ReadFile(config.Templates.Login) if err != nil { validationResults.AddErrors(fielderrors.NewFieldInvalid("templates.login", config.Templates.Login, "could not read file")) } else { for _, err = range login.ValidateLoginTemplate(content) { validationResults.AddErrors(fielderrors.NewFieldInvalid("templates.login", config.Templates.Login, err.Error())) } } } return validationResults }
func (c *AuthConfig) getAuthenticationHandler(mux cmdutil.Mux, errorHandler handlers.AuthenticationErrorHandler) (handlers.AuthenticationHandler, error) { // TODO: make this ordered once we can have more than one challengers := map[string]handlers.AuthenticationChallenger{} redirectors := new(handlers.AuthenticationRedirectors) // Determine if we have more than one password-based Identity Provider multiplePasswordProviders := false passwordProviderCount := 0 for _, identityProvider := range c.Options.IdentityProviders { if configapi.IsPasswordAuthenticator(identityProvider) && identityProvider.UseAsLogin { passwordProviderCount++ if passwordProviderCount > 1 { multiplePasswordProviders = true break } } } for _, identityProvider := range c.Options.IdentityProviders { identityMapper, err := identitymapper.NewIdentityUserMapper(c.IdentityRegistry, c.UserRegistry, identitymapper.MappingMethodType(identityProvider.MappingMethod)) if err != nil { return nil, err } // TODO: refactor handler building per type if configapi.IsPasswordAuthenticator(identityProvider) { passwordAuth, err := c.getPasswordAuthenticator(identityProvider) if err != nil { return nil, err } if identityProvider.UseAsLogin { // Password auth requires: // 1. a session success handler (to remember you logged in) // 2. a redirectSuccessHandler (to go back to the "then" param) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for password-based login") } passwordSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, redirectSuccessHandler{}} var ( // loginPath is unescaped, the way the mux will see it once URL-decoding is done loginPath = openShiftLoginPrefix // redirectLoginPath is escaped, the way we would need to send a Location redirect to a client redirectLoginPath = openShiftLoginPrefix ) if multiplePasswordProviders { // If there is more than one Identity Provider acting as a login // provider, we need to give each of them their own login path, // to avoid ambiguity. loginPath = path.Join(openShiftLoginPrefix, identityProvider.Name) // url-encode the provider name for redirecting redirectLoginPath = path.Join(openShiftLoginPrefix, (&url.URL{Path: identityProvider.Name}).String()) } // Since we're redirecting to a local login page, we don't need to force absolute URL resolution redirectors.Add(identityProvider.Name, redirector.NewRedirector(nil, redirectLoginPath+"?then=${url}")) var loginTemplateFile string if c.Options.Templates != nil { loginTemplateFile = c.Options.Templates.Login } loginFormRenderer, err := login.NewLoginFormRenderer(loginTemplateFile) if err != nil { return nil, err } login := login.NewLogin(identityProvider.Name, c.getCSRF(), &callbackPasswordAuthenticator{passwordAuth, passwordSuccessHandler}, loginFormRenderer) login.Install(mux, loginPath) } if identityProvider.UseAsChallenger { // For now, all password challenges share a single basic challenger, since they'll all respond to any basic credentials challengers["basic-challenge"] = passwordchallenger.NewBasicAuthChallenger("openshift") } } else if configapi.IsOAuthIdentityProvider(identityProvider) { oauthProvider, err := c.getOAuthProvider(identityProvider) if err != nil { return nil, err } // Default state builder, combining CSRF and return URL handling state := external.CSRFRedirectingState(c.getCSRF()) // OAuth auth requires // 1. a session success handler (to remember you logged in) // 2. a state success handler (to go back to the URL encoded in the state) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for OAuth-based login") } oauthSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, state} // If the specified errorHandler doesn't handle the login error, let the state error handler attempt to propagate specific errors back to the token requester oauthErrorHandler := handlers.AuthenticationErrorHandlers{errorHandler, state} callbackPath := path.Join(OpenShiftOAuthCallbackPrefix, identityProvider.Name) oauthRedirector, oauthHandler, err := external.NewExternalOAuthRedirector(oauthProvider, state, c.Options.MasterPublicURL+callbackPath, oauthSuccessHandler, oauthErrorHandler, identityMapper) if err != nil { return nil, fmt.Errorf("unexpected error: %v", err) } mux.Handle(callbackPath, oauthHandler) if identityProvider.UseAsLogin { redirectors.Add(identityProvider.Name, oauthRedirector) } if identityProvider.UseAsChallenger { // For now, all password challenges share a single basic challenger, since they'll all respond to any basic credentials challengers["basic-challenge"] = passwordchallenger.NewBasicAuthChallenger("openshift") } } else if requestHeaderProvider, isRequestHeader := identityProvider.Provider.(*configapi.RequestHeaderIdentityProvider); isRequestHeader { // We might be redirecting to an external site, we need to fully resolve the request URL to the public master baseRequestURL, err := url.Parse(c.Options.MasterPublicURL + OpenShiftOAuthAPIPrefix + osinserver.AuthorizePath) if err != nil { return nil, err } if identityProvider.UseAsChallenger { challengers["requestheader-"+identityProvider.Name+"-redirect"] = redirector.NewChallenger(baseRequestURL, requestHeaderProvider.ChallengeURL) } if identityProvider.UseAsLogin { redirectors.Add(identityProvider.Name, redirector.NewRedirector(baseRequestURL, requestHeaderProvider.LoginURL)) } } } if redirectors.Count() > 0 && len(challengers) == 0 { // Add a default challenger that will warn and give a link to the web browser token-granting location challengers["placeholder"] = placeholderchallenger.New(OpenShiftOAuthTokenRequestURL(c.Options.MasterPublicURL)) } var selectProviderTemplateFile string if c.Options.Templates != nil { selectProviderTemplateFile = c.Options.Templates.ProviderSelection } selectProviderRenderer, err := selectprovider.NewSelectProviderRenderer(selectProviderTemplateFile) if err != nil { return nil, err } selectProvider := selectprovider.NewSelectProvider(selectProviderRenderer, c.Options.AlwaysShowProviderSelection) authHandler := handlers.NewUnionAuthenticationHandler(challengers, redirectors, errorHandler, selectProvider) return authHandler, nil }
func ValidateOAuthConfig(config *api.OAuthConfig, fldPath *field.Path) ValidationResults { validationResults := ValidationResults{} if config.MasterCA == nil { validationResults.AddErrors(field.Invalid(fldPath.Child("masterCA"), config.MasterCA, "a filename or empty string is required")) } else if len(*config.MasterCA) > 0 { validationResults.AddErrors(ValidateFile(*config.MasterCA, fldPath.Child("masterCA"))...) } if len(config.MasterURL) == 0 { validationResults.AddErrors(field.Required(fldPath.Child("masterURL"), "")) } else if _, urlErrs := ValidateURL(config.MasterURL, fldPath.Child("masterURL")); len(urlErrs) > 0 { validationResults.AddErrors(urlErrs...) } if _, urlErrs := ValidateURL(config.MasterPublicURL, fldPath.Child("masterPublicURL")); len(urlErrs) > 0 { validationResults.AddErrors(urlErrs...) } if len(config.AssetPublicURL) == 0 { validationResults.AddErrors(field.Required(fldPath.Child("assetPublicURL"), "")) } if config.SessionConfig != nil { validationResults.AddErrors(validateSessionConfig(config.SessionConfig, fldPath.Child("sessionConfig"))...) } validationResults.AddErrors(validateGrantConfig(config.GrantConfig, fldPath.Child("grantConfig"))...) providerNames := sets.NewString() redirectingIdentityProviders := []string{} challengeIssuingIdentityProviders := []string{} challengeRedirectingIdentityProviders := []string{} for i, identityProvider := range config.IdentityProviders { if identityProvider.UseAsLogin { redirectingIdentityProviders = append(redirectingIdentityProviders, identityProvider.Name) if api.IsPasswordAuthenticator(identityProvider) { if config.SessionConfig == nil { validationResults.AddErrors(field.Invalid(fldPath.Child("sessionConfig"), config, "sessionConfig is required if a password identity provider is used for browser based login")) } } } if identityProvider.UseAsChallenger { // RequestHeaderIdentityProvider is special, it can only react to challenge clients by redirecting them // Make sure we don't have more than a single redirector, and don't have a mix of challenge issuers and redirectors if _, isRequestHeader := identityProvider.Provider.(*api.RequestHeaderIdentityProvider); isRequestHeader { challengeRedirectingIdentityProviders = append(challengeRedirectingIdentityProviders, identityProvider.Name) } else { challengeIssuingIdentityProviders = append(challengeIssuingIdentityProviders, identityProvider.Name) } } identityProviderPath := fldPath.Child("identityProvider").Index(i) validationResults.Append(ValidateIdentityProvider(identityProvider, identityProviderPath)) if len(identityProvider.Name) > 0 { if providerNames.Has(identityProvider.Name) { validationResults.AddErrors(field.Invalid(identityProviderPath.Child("name"), identityProvider.Name, "must have a unique name")) } providerNames.Insert(identityProvider.Name) } } if len(challengeRedirectingIdentityProviders) > 1 { validationResults.AddErrors(field.Invalid(fldPath.Child("identityProviders"), "challenge", fmt.Sprintf("only one identity provider can redirect clients requesting an authentication challenge, found: %v", strings.Join(challengeRedirectingIdentityProviders, ", ")))) } if len(challengeRedirectingIdentityProviders) > 0 && len(challengeIssuingIdentityProviders) > 0 { validationResults.AddErrors( field.Invalid(fldPath.Child("identityProviders"), "challenge", fmt.Sprintf( "cannot mix providers that redirect clients requesting auth challenges (%s) with providers issuing challenges to those clients (%s)", strings.Join(challengeRedirectingIdentityProviders, ", "), strings.Join(challengeIssuingIdentityProviders, ", "), ))) } if config.Templates != nil { if len(config.Templates.Login) > 0 { content, err := ioutil.ReadFile(config.Templates.Login) if err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("templates", "login"), config.Templates.Login, "could not read file")) } else { for _, err = range login.ValidateLoginTemplate(content) { validationResults.AddErrors(field.Invalid(fldPath.Child("templates", "login"), config.Templates.Login, err.Error())) } } } if len(config.Templates.ProviderSelection) > 0 { content, err := ioutil.ReadFile(config.Templates.ProviderSelection) if err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("templates", "providerSelection"), config.Templates.ProviderSelection, "could not read file")) } else { for _, err = range selectprovider.ValidateSelectProviderTemplate(content) { validationResults.AddErrors(field.Invalid(fldPath.Child("templates", "providerSelection"), config.Templates.ProviderSelection, err.Error())) } } } if len(config.Templates.Error) > 0 { content, err := ioutil.ReadFile(config.Templates.Error) if err != nil { validationResults.AddErrors(field.Invalid(fldPath.Child("templates", "error"), config.Templates.Error, "could not read file")) } else { for _, err = range errorpage.ValidateErrorPageTemplate(content) { validationResults.AddErrors(field.Invalid(fldPath.Child("templates", "error"), config.Templates.Error, err.Error())) } } } } return validationResults }
func (c *AuthConfig) getAuthenticationHandler(mux cmdutil.Mux, errorHandler handlers.AuthenticationErrorHandler) (handlers.AuthenticationHandler, error) { challengers := map[string]handlers.AuthenticationChallenger{} redirectors := map[string]handlers.AuthenticationRedirector{} for _, identityProvider := range c.Options.IdentityProviders { identityMapper := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(c.IdentityRegistry, c.UserRegistry) // TODO: refactor handler building per type if configapi.IsPasswordAuthenticator(identityProvider) { passwordAuth, err := c.getPasswordAuthenticator(identityProvider) if err != nil { return nil, err } if identityProvider.UseAsLogin { // Password auth requires: // 1. a session success handler (to remember you logged in) // 2. a redirectSuccessHandler (to go back to the "then" param) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for password-based login") } passwordSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, redirectSuccessHandler{}} // Since we're redirecting to a local login page, we don't need to force absolute URL resolution redirectors["login-"+identityProvider.Name+"-redirect"] = redirector.NewRedirector(nil, OpenShiftLoginPrefix+"?then=${url}") var loginTemplateFile string if c.Options.Templates != nil { loginTemplateFile = c.Options.Templates.Login } loginFormRenderer, err := login.NewLoginFormRenderer(loginTemplateFile) if err != nil { return nil, err } login := login.NewLogin(c.getCSRF(), &callbackPasswordAuthenticator{passwordAuth, passwordSuccessHandler}, loginFormRenderer) login.Install(mux, OpenShiftLoginPrefix) } if identityProvider.UseAsChallenger { // For now, all password challenges share a single basic challenger, since they'll all respond to any basic credentials challengers["basic-challenge"] = passwordchallenger.NewBasicAuthChallenger("openshift") } } else if configapi.IsOAuthIdentityProvider(identityProvider) { oauthProvider, err := c.getOAuthProvider(identityProvider) if err != nil { return nil, err } // Default state builder, combining CSRF and return URL handling state := external.CSRFRedirectingState(c.getCSRF()) // OAuth auth requires // 1. a session success handler (to remember you logged in) // 2. a state success handler (to go back to the URL encoded in the state) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for OAuth-based login") } oauthSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, state} // If the specified errorHandler doesn't handle the login error, let the state error handler attempt to propagate specific errors back to the token requester oauthErrorHandler := handlers.AuthenticationErrorHandlers{errorHandler, state} callbackPath := path.Join(OpenShiftOAuthCallbackPrefix, identityProvider.Name) oauthHandler, err := external.NewExternalOAuthRedirector(oauthProvider, state, c.Options.MasterPublicURL+callbackPath, oauthSuccessHandler, oauthErrorHandler, identityMapper) if err != nil { return nil, fmt.Errorf("unexpected error: %v", err) } mux.Handle(callbackPath, oauthHandler) if identityProvider.UseAsLogin { redirectors["oauth-"+identityProvider.Name+"-redirect"] = oauthHandler } if identityProvider.UseAsChallenger { return nil, errors.New("oauth identity providers cannot issue challenges") } } else if requestHeaderProvider, isRequestHeader := identityProvider.Provider.Object.(*configapi.RequestHeaderIdentityProvider); isRequestHeader { // We might be redirecting to an external site, we need to fully resolve the request URL to the public master baseRequestURL, err := url.Parse(c.Options.MasterPublicURL + OpenShiftOAuthAPIPrefix + osinserver.AuthorizePath) if err != nil { return nil, err } if identityProvider.UseAsChallenger { challengers["requestheader-"+identityProvider.Name+"-redirect"] = redirector.NewChallenger(baseRequestURL, requestHeaderProvider.ChallengeURL) } if identityProvider.UseAsLogin { redirectors["requestheader-"+identityProvider.Name+"-redirect"] = redirector.NewRedirector(baseRequestURL, requestHeaderProvider.LoginURL) } } } if len(redirectors) > 0 && len(challengers) == 0 { // Add a default challenger that will warn and give a link to the web browser token-granting location challengers["placeholder"] = placeholderchallenger.New(OpenShiftOAuthTokenRequestURL(c.Options.MasterPublicURL)) } authHandler := handlers.NewUnionAuthenticationHandler(challengers, redirectors, errorHandler) return authHandler, nil }
func (c *AuthConfig) getAuthenticationHandler(mux cmdutil.Mux, errorHandler handlers.AuthenticationErrorHandler) (handlers.AuthenticationHandler, error) { challengers := map[string]handlers.AuthenticationChallenger{} redirectors := map[string]handlers.AuthenticationRedirector{} for _, identityProvider := range c.Options.IdentityProviders { identityMapper := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(c.IdentityRegistry, c.UserRegistry) if configapi.IsPasswordAuthenticator(identityProvider) { passwordAuth, err := c.getPasswordAuthenticator(identityProvider) if err != nil { return nil, err } if identityProvider.UseAsLogin { // Password auth requires: // 1. a session success handler (to remember you logged in) // 2. a redirectSuccessHandler (to go back to the "then" param) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for password-based login") } passwordSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, redirectSuccessHandler{}} redirectors["login"] = &redirector{RedirectURL: OpenShiftLoginPrefix, ThenParam: "then"} login := login.NewLogin(c.getCSRF(), &callbackPasswordAuthenticator{passwordAuth, passwordSuccessHandler}, login.DefaultLoginFormRenderer) login.Install(mux, OpenShiftLoginPrefix) } if identityProvider.UseAsChallenger { challengers["login"] = passwordchallenger.NewBasicAuthChallenger("openshift") } } else if configapi.IsOAuthIdentityProvider(identityProvider) { oauthProvider, err := c.getOAuthProvider(identityProvider) if err != nil { return nil, err } // Default state builder, combining CSRF and return URL handling state := external.CSRFRedirectingState(c.getCSRF()) // OAuth auth requires // 1. a session success handler (to remember you logged in) // 2. a state success handler (to go back to the URL encoded in the state) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for OAuth-based login") } oauthSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, state} // If the specified errorHandler doesn't handle the login error, let the state error handler attempt to propagate specific errors back to the token requester oauthErrorHandler := handlers.AuthenticationErrorHandlers{errorHandler, state} callbackPath := path.Join(OpenShiftOAuthCallbackPrefix, identityProvider.Name) oauthHandler, err := external.NewExternalOAuthRedirector(oauthProvider, state, c.Options.MasterPublicURL+callbackPath, oauthSuccessHandler, oauthErrorHandler, identityMapper) if err != nil { return nil, fmt.Errorf("unexpected error: %v", err) } mux.Handle(callbackPath, oauthHandler) if identityProvider.UseAsLogin { redirectors[identityProvider.Name] = oauthHandler } if identityProvider.UseAsChallenger { return nil, errors.New("oauth identity providers cannot issue challenges") } } } if len(redirectors) > 0 && len(challengers) == 0 { // Add a default challenger that will warn and give a link to the web browser token-granting location challengers["placeholder"] = placeholderchallenger.New(OpenShiftOAuthTokenRequestURL(c.Options.MasterPublicURL)) } authHandler := handlers.NewUnionAuthenticationHandler(challengers, redirectors, errorHandler) return authHandler, nil }