@@ -29,6 +29,7 @@ func Validate(o *options.Options) error {
29 msgs = append(msgs, validateRedisSessionStore(o)...)
29 msgs = append(msgs, validateRedisSessionStore(o)...)
30 msgs = append(msgs, prefixValues("injectRequestHeaders: ", validateHeaders(o.InjectRequestHeaders)...)...)
30 msgs = append(msgs, prefixValues("injectRequestHeaders: ", validateHeaders(o.InjectRequestHeaders)...)...)
31 msgs = append(msgs, prefixValues("injectResponseHeaders: ", validateHeaders(o.InjectResponseHeaders)...)...)
31 msgs = append(msgs, prefixValues("injectResponseHeaders: ", validateHeaders(o.InjectResponseHeaders)...)...)
32+ msgs = append(msgs, validateProviders(o)...)
32 msgs = configureLogger(o.Logging, msgs)
33 msgs = configureLogger(o.Logging, msgs)
33 msgs = parseSignatureKey(o, msgs)
34 msgs = parseSignatureKey(o, msgs)
@@ -39,8 +40,8 @@ func Validate(o *options.Options) error {
39 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
40 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
41 http.DefaultClient = &http.Client{Transport: insecureTransport}
42 http.DefaultClient = &http.Client{Transport: insecureTransport}
42− } else if len(o.ProviderCAFiles) > 0 {
43+ } else if len(o.Providers[0].CAFiles) > 0 {
43− pool, err := util.GetCertPool(o.ProviderCAFiles)
44+ pool, err := util.GetCertPool(o.Providers[0].CAFiles)
44 if err == nil {
45 if err == nil {
45 transport := http.DefaultTransport.(*http.Transport).Clone()
46 transport := http.DefaultTransport.(*http.Transport).Clone()
46 transport.TLSClientConfig = &tls.Config{
47 transport.TLSClientConfig = &tls.Config{
@@ -54,38 +55,23 @@ func Validate(o *options.Options) error {
57− if o.ClientID == "" {
58− msgs = append(msgs, "missing setting: client-id")
60− // login.gov uses a signed JWT to authenticate, not a client-secret
61− if o.ProviderType != "login.gov" {
62− if o.ClientSecret == "" && o.ClientSecretFile == "" {
63− msgs = append(msgs, "missing setting: client-secret or client-secret-file")
65− if o.ClientSecret == "" && o.ClientSecretFile != "" {
66− _, err := ioutil.ReadFile(o.ClientSecretFile)
68− msgs = append(msgs, "could not read client secret file: "+o.ClientSecretFile)
72 if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" {
58 if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" {
73 msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required."+
59 msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required."+
74 "\n use email-domain=* to authorize all email addresses")
60 "\n use email-domain=* to authorize all email addresses")
77− if o.OIDCIssuerURL != "" {
63+ if o.Providers[0].OIDCConfig.IssuerURL != "" {
79 ctx := context.Background()
65 ctx := context.Background()
81− if o.InsecureOIDCSkipIssuerVerification && !o.SkipOIDCDiscovery {
67+ if o.Providers[0].OIDCConfig.InsecureSkipIssuerVerification && !o.Providers[0].OIDCConfig.SkipDiscovery {
82 // go-oidc doesn't let us pass bypass the issuer check this in the oidc.NewProvider call
68 // go-oidc doesn't let us pass bypass the issuer check this in the oidc.NewProvider call
83 // (which uses discovery to get the URLs), so we'll do a quick check ourselves and if
69 // (which uses discovery to get the URLs), so we'll do a quick check ourselves and if
84 // we get the URLs, we'll just use the non-discovery path.
70 // we get the URLs, we'll just use the non-discovery path.
86 logger.Printf("Performing OIDC Discovery...")
72 logger.Printf("Performing OIDC Discovery...")
88− requestURL := strings.TrimSuffix(o.OIDCIssuerURL, "/") + "/.well-known/openid-configuration"
74+ requestURL := strings.TrimSuffix(o.Providers[0].OIDCConfig.IssuerURL, "/") + "/.well-known/openid-configuration"
89 body, err := requests.New(requestURL).
75 body, err := requests.New(requestURL).
90 WithContext(ctx).
76 WithContext(ctx).
@@ -96,23 +82,23 @@ func Validate(o *options.Options) error {
96 // Prefer manually configured URLs. It's a bit unclear
82 // Prefer manually configured URLs. It's a bit unclear
97 // why you'd be doing discovery and also providing the URLs
83 // why you'd be doing discovery and also providing the URLs
98 // explicitly though...
84 // explicitly though...
99− if o.LoginURL == "" {
85+ if o.Providers[0].LoginURL == "" {
100− o.LoginURL = body.Get("authorization_endpoint").MustString()
86+ o.Providers[0].LoginURL = body.Get("authorization_endpoint").MustString()
103− if o.RedeemURL == "" {
89+ if o.Providers[0].RedeemURL == "" {
104− o.RedeemURL = body.Get("token_endpoint").MustString()
90+ o.Providers[0].RedeemURL = body.Get("token_endpoint").MustString()
107− if o.OIDCJwksURL == "" {
93+ if o.Providers[0].OIDCConfig.JwksURL == "" {
108− o.OIDCJwksURL = body.Get("jwks_uri").MustString()
94+ o.Providers[0].OIDCConfig.JwksURL = body.Get("jwks_uri").MustString()
111− if o.ProfileURL == "" {
97+ if o.Providers[0].ProfileURL == "" {
112− o.ProfileURL = body.Get("userinfo_endpoint").MustString()
98+ o.Providers[0].ProfileURL = body.Get("userinfo_endpoint").MustString()
115− o.SkipOIDCDiscovery = true
101+ o.Providers[0].OIDCConfig.SkipDiscovery = true
@@ -120,42 +106,45 @@ func Validate(o *options.Options) error {
120 // instead of metadata discovery if we enable -skip-oidc-discovery.
106 // instead of metadata discovery if we enable -skip-oidc-discovery.
121 // In this case we need to make sure the required endpoints for
107 // In this case we need to make sure the required endpoints for
122 // the provider are configured.
108 // the provider are configured.
123− if o.SkipOIDCDiscovery {
109+ if o.Providers[0].OIDCConfig.SkipDiscovery {
124− if o.LoginURL == "" {
110+ if o.Providers[0].LoginURL == "" {
125 msgs = append(msgs, "missing setting: login-url")
111 msgs = append(msgs, "missing setting: login-url")
127− if o.RedeemURL == "" {
113+ if o.Providers[0].RedeemURL == "" {
128 msgs = append(msgs, "missing setting: redeem-url")
114 msgs = append(msgs, "missing setting: redeem-url")
130− if o.OIDCJwksURL == "" {
116+ if o.Providers[0].OIDCConfig.JwksURL == "" {
131 msgs = append(msgs, "missing setting: oidc-jwks-url")
117 msgs = append(msgs, "missing setting: oidc-jwks-url")
133− keySet := oidc.NewRemoteKeySet(ctx, o.OIDCJwksURL)
119+ keySet := oidc.NewRemoteKeySet(ctx, o.Providers[0].OIDCConfig.JwksURL)
134− o.SetOIDCVerifier(oidc.NewVerifier(o.OIDCIssuerURL, keySet, &oidc.Config{
120+ o.SetOIDCVerifier(oidc.NewVerifier(o.Providers[0].OIDCConfig.IssuerURL, keySet, &oidc.Config{
135− ClientID: o.ClientID,
121+ ClientID: o.Providers[0].ClientID,
136− SkipIssuerCheck: o.InsecureOIDCSkipIssuerVerification,
122+ SkipIssuerCheck: o.Providers[0].OIDCConfig.InsecureSkipIssuerVerification,
139 // Configure discoverable provider data.
125 // Configure discoverable provider data.
140− provider, err := oidc.NewProvider(ctx, o.OIDCIssuerURL)
126+ provider, err := oidc.NewProvider(ctx, o.Providers[0].OIDCConfig.IssuerURL)
141 if err != nil {
127 if err != nil {
142 return err
128 return err
144 o.SetOIDCVerifier(provider.Verifier(&oidc.Config{
130 o.SetOIDCVerifier(provider.Verifier(&oidc.Config{
145− ClientID: o.ClientID,
131+ ClientID: o.Providers[0].ClientID,
146− SkipIssuerCheck: o.InsecureOIDCSkipIssuerVerification,
132+ SkipIssuerCheck: o.Providers[0].OIDCConfig.InsecureSkipIssuerVerification,
149− o.LoginURL = provider.Endpoint().AuthURL
135+ o.Providers[0].LoginURL = provider.Endpoint().AuthURL
150− o.RedeemURL = provider.Endpoint().TokenURL
136+ o.Providers[0].RedeemURL = provider.Endpoint().TokenURL
152− if o.Scope == "" {
138+ if o.Providers[0].Scope == "" {
153− o.Scope = "openid email profile"
139+ o.Providers[0].Scope = "openid email profile"
155− if len(o.AllowedGroups) > 0 {
141+ if len(o.Providers[0].AllowedGroups) > 0 {
156− o.Scope += " groups"
142+ o.Providers[0].Scope += " groups"
145+ if o.Providers[0].OIDCConfig.UserIDClaim == "" {
146+ o.Providers[0].OIDCConfig.UserIDClaim = "email"
161 if o.SkipJwtBearerTokens {
150 if o.SkipJwtBearerTokens {
@@ -183,18 +172,6 @@ func Validate(o *options.Options) error {
183 msgs = append(msgs, validateUpstreams(o.UpstreamServers)...)
172 msgs = append(msgs, validateUpstreams(o.UpstreamServers)...)
184 msgs = parseProviderInfo(o, msgs)
173 msgs = parseProviderInfo(o, msgs)
186− if len(o.GoogleGroups) > 0 || o.GoogleAdminEmail != "" || o.GoogleServiceAccountJSON != "" {
187− if len(o.GoogleGroups) < 1 {
188− msgs = append(msgs, "missing setting: google-group")
190− if o.GoogleAdminEmail == "" {
191− msgs = append(msgs, "missing setting: google-admin-email")
193− if o.GoogleServiceAccountJSON == "" {
194− msgs = append(msgs, "missing setting: google-service-account-json")
198 if o.ReverseProxy {
175 if o.ReverseProxy {
199 parser, err := ip.GetRealClientIPParser(o.RealClientIPHeader)
176 parser, err := ip.GetRealClientIPParser(o.RealClientIPHeader)
200 if err != nil {
177 if err != nil {
@@ -220,79 +197,79 @@ func Validate(o *options.Options) error {
221func parseProviderInfo(o *options.Options, msgs []string) []string {
198func parseProviderInfo(o *options.Options, msgs []string) []string {
222 p := &providers.ProviderData{
199 p := &providers.ProviderData{
223− Scope: o.Scope,
200+ Scope: o.Providers[0].Scope,
224− ClientID: o.ClientID,
201+ ClientID: o.Providers[0].ClientID,
225− ClientSecret: o.ClientSecret,
202+ ClientSecret: o.Providers[0].ClientSecret,
226− ClientSecretFile: o.ClientSecretFile,
203+ ClientSecretFile: o.Providers[0].ClientSecretFile,
227− Prompt: o.Prompt,
204+ Prompt: o.Providers[0].Prompt,
228− ApprovalPrompt: o.ApprovalPrompt,
205+ ApprovalPrompt: o.Providers[0].ApprovalPrompt,
229− AcrValues: o.AcrValues,
206+ AcrValues: o.Providers[0].AcrValues,
231− p.LoginURL, msgs = parseURL(o.LoginURL, "login", msgs)
208+ p.LoginURL, msgs = parseURL(o.Providers[0].LoginURL, "login", msgs)
232− p.RedeemURL, msgs = parseURL(o.RedeemURL, "redeem", msgs)
209+ p.RedeemURL, msgs = parseURL(o.Providers[0].RedeemURL, "redeem", msgs)
233− p.ProfileURL, msgs = parseURL(o.ProfileURL, "profile", msgs)
210+ p.ProfileURL, msgs = parseURL(o.Providers[0].ProfileURL, "profile", msgs)
234− p.ValidateURL, msgs = parseURL(o.ValidateURL, "validate", msgs)
211+ p.ValidateURL, msgs = parseURL(o.Providers[0].ValidateURL, "validate", msgs)
235− p.ProtectedResource, msgs = parseURL(o.ProtectedResource, "resource", msgs)
212+ p.ProtectedResource, msgs = parseURL(o.Providers[0].ProtectedResource, "resource", msgs)
237 // Make the OIDC options available to all providers that support it
214 // Make the OIDC options available to all providers that support it
238− p.AllowUnverifiedEmail = o.InsecureOIDCAllowUnverifiedEmail
215+ p.AllowUnverifiedEmail = o.Providers[0].OIDCConfig.InsecureAllowUnverifiedEmail
239− p.EmailClaim = o.OIDCEmailClaim
216+ p.EmailClaim = o.Providers[0].OIDCConfig.EmailClaim
240− p.GroupsClaim = o.OIDCGroupsClaim
217+ p.GroupsClaim = o.Providers[0].OIDCConfig.GroupsClaim
241 p.Verifier = o.GetOIDCVerifier()
218 p.Verifier = o.GetOIDCVerifier()
243 // TODO (@NickMeves) - Remove This
220 // TODO (@NickMeves) - Remove This
244 // Backwards Compatibility for Deprecated UserIDClaim option
221 // Backwards Compatibility for Deprecated UserIDClaim option
245− if o.OIDCEmailClaim == providers.OIDCEmailClaim &&
222+ if o.Providers[0].OIDCConfig.EmailClaim == providers.OIDCEmailClaim &&
246− o.UserIDClaim != providers.OIDCEmailClaim {
223+ o.Providers[0].OIDCConfig.UserIDClaim != providers.OIDCEmailClaim {
247− p.EmailClaim = o.UserIDClaim
224+ p.EmailClaim = o.Providers[0].OIDCConfig.UserIDClaim
250− p.SetAllowedGroups(o.AllowedGroups)
227+ p.SetAllowedGroups(o.Providers[0].AllowedGroups)
252− provider := providers.New(o.ProviderType, p)
229+ provider := providers.New(o.Providers[0].Type, p)
253 if provider == nil {
230 if provider == nil {
254− msgs = append(msgs, fmt.Sprintf("invalid setting: provider '%s' is not available", o.ProviderType))
231+ msgs = append(msgs, fmt.Sprintf("invalid setting: provider '%s' is not available", o.Providers[0].Type))
255 return msgs
232 return msgs
257 o.SetProvider(provider)
234 o.SetProvider(provider)
259 switch p := o.GetProvider().(type) {
236 switch p := o.GetProvider().(type) {
260 case *providers.AzureProvider:
237 case *providers.AzureProvider:
261− p.Configure(o.AzureTenant)
238+ p.Configure(o.Providers[0].AzureConfig.Tenant)
262 case *providers.GitHubProvider:
239 case *providers.GitHubProvider:
263− p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
240+ p.SetOrgTeam(o.Providers[0].GitHubConfig.Org, o.Providers[0].GitHubConfig.Team)
264− p.SetRepo(o.GitHubRepo, o.GitHubToken)
241+ p.SetRepo(o.Providers[0].GitHubConfig.Repo, o.Providers[0].GitHubConfig.Token)
265− p.SetUsers(o.GitHubUsers)
242+ p.SetUsers(o.Providers[0].GitHubConfig.Users)
266 case *providers.KeycloakProvider:
243 case *providers.KeycloakProvider:
267 // Backwards compatibility with `--keycloak-group` option
244 // Backwards compatibility with `--keycloak-group` option
268− if len(o.KeycloakGroups) > 0 {
245+ if len(o.Providers[0].KeycloakConfig.Groups) > 0 {
269− p.SetAllowedGroups(o.KeycloakGroups)
246+ p.SetAllowedGroups(o.Providers[0].KeycloakConfig.Groups)
271 case *providers.GoogleProvider:
248 case *providers.GoogleProvider:
272− if o.GoogleServiceAccountJSON != "" {
249+ if o.Providers[0].GoogleConfig.ServiceAccountJSON != "" {
273− file, err := os.Open(o.GoogleServiceAccountJSON)
250+ file, err := os.Open(o.Providers[0].GoogleConfig.ServiceAccountJSON)
274 if err != nil {
251 if err != nil {
275− msgs = append(msgs, "invalid Google credentials file: "+o.GoogleServiceAccountJSON)
252+ msgs = append(msgs, "invalid Google credentials file: "+o.Providers[0].GoogleConfig.ServiceAccountJSON)
277− groups := o.AllowedGroups
254+ groups := o.Providers[0].AllowedGroups
278 // Backwards compatibility with `--google-group` option
255 // Backwards compatibility with `--google-group` option
279− if len(o.GoogleGroups) > 0 {
256+ if len(o.Providers[0].GoogleConfig.Groups) > 0 {
280− groups = o.GoogleGroups
257+ groups = o.Providers[0].GoogleConfig.Groups
281 p.SetAllowedGroups(groups)
258 p.SetAllowedGroups(groups)
283− p.SetGroupRestriction(groups, o.GoogleAdminEmail, file)
260+ p.SetGroupRestriction(groups, o.Providers[0].GoogleConfig.AdminEmail, file)
286 case *providers.BitbucketProvider:
263 case *providers.BitbucketProvider:
287− p.SetTeam(o.BitbucketTeam)
264+ p.SetTeam(o.Providers[0].BitbucketConfig.Team)
288− p.SetRepository(o.BitbucketRepository)
265+ p.SetRepository(o.Providers[0].BitbucketConfig.Repository)
289 case *providers.OIDCProvider:
266 case *providers.OIDCProvider:
290 if p.Verifier == nil {
267 if p.Verifier == nil {
291 msgs = append(msgs, "oidc provider requires an oidc issuer URL")
268 msgs = append(msgs, "oidc provider requires an oidc issuer URL")
293 case *providers.GitLabProvider:
270 case *providers.GitLabProvider:
294− p.Groups = o.GitLabGroup
271+ p.Groups = o.Providers[0].GitLabConfig.Group
295− err := p.AddProjects(o.GitlabProjects)
272+ err := p.AddProjects(o.Providers[0].GitLabConfig.Projects)
296 if err != nil {
273 if err != nil {
297 msgs = append(msgs, "failed to setup gitlab project access level")
274 msgs = append(msgs, "failed to setup gitlab project access level")
@@ -308,7 +285,7 @@ func parseProviderInfo(o *options.Options, msgs []string) []string {
308 msgs = append(msgs, "failed to initialize oidc provider for gitlab.com")
285 msgs = append(msgs, "failed to initialize oidc provider for gitlab.com")
310 p.Verifier = provider.Verifier(&oidc.Config{
287 p.Verifier = provider.Verifier(&oidc.Config{
311− ClientID: o.ClientID,
288+ ClientID: o.Providers[0].ClientID,
314 p.LoginURL, msgs = parseURL(provider.Endpoint().AuthURL, "login", msgs)
291 p.LoginURL, msgs = parseURL(provider.Endpoint().AuthURL, "login", msgs)
@@ -316,31 +293,31 @@ func parseProviderInfo(o *options.Options, msgs []string) []string {
318 case *providers.LoginGovProvider:
295 case *providers.LoginGovProvider:
319− p.PubJWKURL, msgs = parseURL(o.PubJWKURL, "pubjwk", msgs)
296+ p.PubJWKURL, msgs = parseURL(o.Providers[0].LoginGovConfig.PubJWKURL, "pubjwk", msgs)
321 // JWT key can be supplied via env variable or file in the filesystem, but not both.
298 // JWT key can be supplied via env variable or file in the filesystem, but not both.
323− case o.JWTKey != "" && o.JWTKeyFile != "":
300+ case o.Providers[0].LoginGovConfig.JWTKey != "" && o.Providers[0].LoginGovConfig.JWTKeyFile != "":
324 msgs = append(msgs, "cannot set both jwt-key and jwt-key-file options")
301 msgs = append(msgs, "cannot set both jwt-key and jwt-key-file options")
325− case o.JWTKey == "" && o.JWTKeyFile == "":
302+ case o.Providers[0].LoginGovConfig.JWTKey == "" && o.Providers[0].LoginGovConfig.JWTKeyFile == "":
326 msgs = append(msgs, "login.gov provider requires a private key for signing JWTs")
303 msgs = append(msgs, "login.gov provider requires a private key for signing JWTs")
327− case o.JWTKey != "":
304+ case o.Providers[0].LoginGovConfig.JWTKey != "":
328 // The JWT Key is in the commandline argument
305 // The JWT Key is in the commandline argument
329− signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(o.JWTKey))
306+ signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(o.Providers[0].LoginGovConfig.JWTKey))
330 if err != nil {
307 if err != nil {
331 msgs = append(msgs, "could not parse RSA Private Key PEM")
308 msgs = append(msgs, "could not parse RSA Private Key PEM")
333 p.JWTKey = signKey
310 p.JWTKey = signKey
335− case o.JWTKeyFile != "":
312+ case o.Providers[0].LoginGovConfig.JWTKeyFile != "":
336 // The JWT key is in the filesystem
313 // The JWT key is in the filesystem
337− keyData, err := ioutil.ReadFile(o.JWTKeyFile)
314+ keyData, err := ioutil.ReadFile(o.Providers[0].LoginGovConfig.JWTKeyFile)
338 if err != nil {
315 if err != nil {
339− msgs = append(msgs, "could not read key file: "+o.JWTKeyFile)
316+ msgs = append(msgs, "could not read key file: "+o.Providers[0].LoginGovConfig.JWTKeyFile)
341 signKey, err := jwt.ParseRSAPrivateKeyFromPEM(keyData)
318 signKey, err := jwt.ParseRSAPrivateKeyFromPEM(keyData)
342 if err != nil {
319 if err != nil {
343− msgs = append(msgs, "could not parse private key from PEM file:"+o.JWTKeyFile)
320+ msgs = append(msgs, "could not parse private key from PEM file:"+o.Providers[0].LoginGovConfig.JWTKeyFile)
345 p.JWTKey = signKey
322 p.JWTKey = signKey