@@ -29,6 +29,7 @@ func Validate(o *options.Options) error {
2929 msgs = append(msgs, validateRedisSessionStore(o)...)
3030 msgs = append(msgs, prefixValues("injectRequestHeaders: ", validateHeaders(o.InjectRequestHeaders)...)...)
3131 msgs = append(msgs, prefixValues("injectResponseHeaders: ", validateHeaders(o.InjectResponseHeaders)...)...)
32+ msgs = append(msgs, validateProviders(o)...)
3233 msgs = configureLogger(o.Logging, msgs)
3334 msgs = parseSignatureKey(o, msgs)
3435
@@ -39,8 +40,8 @@ func Validate(o *options.Options) error {
3940 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
4041 }
4142 http.DefaultClient = &http.Client{Transport: insecureTransport}
42− } else if len(o.ProviderCAFiles) > 0 {
43− pool, err := util.GetCertPool(o.ProviderCAFiles)
43+ } else if len(o.Providers[0].CAFiles) > 0 {
44+ pool, err := util.GetCertPool(o.Providers[0].CAFiles)
4445 if err == nil {
4546 transport := http.DefaultTransport.(*http.Transport).Clone()
4647 transport.TLSClientConfig = &tls.Config{
@@ -54,38 +55,23 @@ func Validate(o *options.Options) error {
5455 }
5556 }
5657
57− if o.ClientID == "" {
58− msgs = append(msgs, "missing setting: client-id")
59− }
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")
64− }
65− if o.ClientSecret == "" && o.ClientSecretFile != "" {
66− _, err := ioutil.ReadFile(o.ClientSecretFile)
67− if err != nil {
68− msgs = append(msgs, "could not read client secret file: "+o.ClientSecretFile)
69− }
70− }
71− }
7258 if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" {
7359 msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required."+
7460 "\n use email-domain=* to authorize all email addresses")
7561 }
7662
77− if o.OIDCIssuerURL != "" {
63+ if o.Providers[0].OIDCConfig.IssuerURL != "" {
7864
7965 ctx := context.Background()
8066
81− if o.InsecureOIDCSkipIssuerVerification && !o.SkipOIDCDiscovery {
67+ if o.Providers[0].OIDCConfig.InsecureSkipIssuerVerification && !o.Providers[0].OIDCConfig.SkipDiscovery {
8268 // go-oidc doesn't let us pass bypass the issuer check this in the oidc.NewProvider call
8369 // (which uses discovery to get the URLs), so we'll do a quick check ourselves and if
8470 // we get the URLs, we'll just use the non-discovery path.
8571
8672 logger.Printf("Performing OIDC Discovery...")
8773
88− requestURL := strings.TrimSuffix(o.OIDCIssuerURL, "/") + "/.well-known/openid-configuration"
74+ requestURL := strings.TrimSuffix(o.Providers[0].OIDCConfig.IssuerURL, "/") + "/.well-known/openid-configuration"
8975 body, err := requests.New(requestURL).
9076 WithContext(ctx).
9177 Do().
@@ -96,23 +82,23 @@ func Validate(o *options.Options) error {
9682 // Prefer manually configured URLs. It's a bit unclear
9783 // why you'd be doing discovery and also providing the URLs
9884 // explicitly though...
99− if o.LoginURL == "" {
100− o.LoginURL = body.Get("authorization_endpoint").MustString()
85+ if o.Providers[0].LoginURL == "" {
86+ o.Providers[0].LoginURL = body.Get("authorization_endpoint").MustString()
10187 }
10288
103− if o.RedeemURL == "" {
104− o.RedeemURL = body.Get("token_endpoint").MustString()
89+ if o.Providers[0].RedeemURL == "" {
90+ o.Providers[0].RedeemURL = body.Get("token_endpoint").MustString()
10591 }
10692
107− if o.OIDCJwksURL == "" {
108− o.OIDCJwksURL = body.Get("jwks_uri").MustString()
93+ if o.Providers[0].OIDCConfig.JwksURL == "" {
94+ o.Providers[0].OIDCConfig.JwksURL = body.Get("jwks_uri").MustString()
10995 }
11096
111− if o.ProfileURL == "" {
112− o.ProfileURL = body.Get("userinfo_endpoint").MustString()
97+ if o.Providers[0].ProfileURL == "" {
98+ o.Providers[0].ProfileURL = body.Get("userinfo_endpoint").MustString()
11399 }
114100
115− o.SkipOIDCDiscovery = true
101+ o.Providers[0].OIDCConfig.SkipDiscovery = true
116102 }
117103 }
118104
@@ -120,42 +106,45 @@ func Validate(o *options.Options) error {
120106 // instead of metadata discovery if we enable -skip-oidc-discovery.
121107 // In this case we need to make sure the required endpoints for
122108 // the provider are configured.
123− if o.SkipOIDCDiscovery {
124− if o.LoginURL == "" {
109+ if o.Providers[0].OIDCConfig.SkipDiscovery {
110+ if o.Providers[0].LoginURL == "" {
125111 msgs = append(msgs, "missing setting: login-url")
126112 }
127− if o.RedeemURL == "" {
113+ if o.Providers[0].RedeemURL == "" {
128114 msgs = append(msgs, "missing setting: redeem-url")
129115 }
130− if o.OIDCJwksURL == "" {
116+ if o.Providers[0].OIDCConfig.JwksURL == "" {
131117 msgs = append(msgs, "missing setting: oidc-jwks-url")
132118 }
133− keySet := oidc.NewRemoteKeySet(ctx, o.OIDCJwksURL)
134− o.SetOIDCVerifier(oidc.NewVerifier(o.OIDCIssuerURL, keySet, &oidc.Config{
135− ClientID: o.ClientID,
136− SkipIssuerCheck: o.InsecureOIDCSkipIssuerVerification,
119+ keySet := oidc.NewRemoteKeySet(ctx, o.Providers[0].OIDCConfig.JwksURL)
120+ o.SetOIDCVerifier(oidc.NewVerifier(o.Providers[0].OIDCConfig.IssuerURL, keySet, &oidc.Config{
121+ ClientID: o.Providers[0].ClientID,
122+ SkipIssuerCheck: o.Providers[0].OIDCConfig.InsecureSkipIssuerVerification,
137123 }))
138124 } else {
139125 // Configure discoverable provider data.
140− provider, err := oidc.NewProvider(ctx, o.OIDCIssuerURL)
126+ provider, err := oidc.NewProvider(ctx, o.Providers[0].OIDCConfig.IssuerURL)
141127 if err != nil {
142128 return err
143129 }
144130 o.SetOIDCVerifier(provider.Verifier(&oidc.Config{
145− ClientID: o.ClientID,
146− SkipIssuerCheck: o.InsecureOIDCSkipIssuerVerification,
131+ ClientID: o.Providers[0].ClientID,
132+ SkipIssuerCheck: o.Providers[0].OIDCConfig.InsecureSkipIssuerVerification,
147133 }))
148134
149− o.LoginURL = provider.Endpoint().AuthURL
150− o.RedeemURL = provider.Endpoint().TokenURL
135+ o.Providers[0].LoginURL = provider.Endpoint().AuthURL
136+ o.Providers[0].RedeemURL = provider.Endpoint().TokenURL
151137 }
152− if o.Scope == "" {
153− o.Scope = "openid email profile"
138+ if o.Providers[0].Scope == "" {
139+ o.Providers[0].Scope = "openid email profile"
154140
155− if len(o.AllowedGroups) > 0 {
156− o.Scope += " groups"
141+ if len(o.Providers[0].AllowedGroups) > 0 {
142+ o.Providers[0].Scope += " groups"
157143 }
158144 }
145+ if o.Providers[0].OIDCConfig.UserIDClaim == "" {
146+ o.Providers[0].OIDCConfig.UserIDClaim = "email"
147+ }
159148 }
160149
161150 if o.SkipJwtBearerTokens {
@@ -183,18 +172,6 @@ func Validate(o *options.Options) error {
183172 msgs = append(msgs, validateUpstreams(o.UpstreamServers)...)
184173 msgs = parseProviderInfo(o, msgs)
185174
186− if len(o.GoogleGroups) > 0 || o.GoogleAdminEmail != "" || o.GoogleServiceAccountJSON != "" {
187− if len(o.GoogleGroups) < 1 {
188− msgs = append(msgs, "missing setting: google-group")
189− }
190− if o.GoogleAdminEmail == "" {
191− msgs = append(msgs, "missing setting: google-admin-email")
192− }
193− if o.GoogleServiceAccountJSON == "" {
194− msgs = append(msgs, "missing setting: google-service-account-json")
195− }
196− }
197−
198175 if o.ReverseProxy {
199176 parser, err := ip.GetRealClientIPParser(o.RealClientIPHeader)
200177 if err != nil {
@@ -220,79 +197,79 @@ func Validate(o *options.Options) error {
220197
221198func parseProviderInfo(o *options.Options, msgs []string) []string {
222199 p := &providers.ProviderData{
223− Scope: o.Scope,
224− ClientID: o.ClientID,
225− ClientSecret: o.ClientSecret,
226− ClientSecretFile: o.ClientSecretFile,
227− Prompt: o.Prompt,
228− ApprovalPrompt: o.ApprovalPrompt,
229− AcrValues: o.AcrValues,
200+ Scope: o.Providers[0].Scope,
201+ ClientID: o.Providers[0].ClientID,
202+ ClientSecret: o.Providers[0].ClientSecret,
203+ ClientSecretFile: o.Providers[0].ClientSecretFile,
204+ Prompt: o.Providers[0].Prompt,
205+ ApprovalPrompt: o.Providers[0].ApprovalPrompt,
206+ AcrValues: o.Providers[0].AcrValues,
230207 }
231− p.LoginURL, msgs = parseURL(o.LoginURL, "login", msgs)
232− p.RedeemURL, msgs = parseURL(o.RedeemURL, "redeem", msgs)
233− p.ProfileURL, msgs = parseURL(o.ProfileURL, "profile", msgs)
234− p.ValidateURL, msgs = parseURL(o.ValidateURL, "validate", msgs)
235− p.ProtectedResource, msgs = parseURL(o.ProtectedResource, "resource", msgs)
208+ p.LoginURL, msgs = parseURL(o.Providers[0].LoginURL, "login", msgs)
209+ p.RedeemURL, msgs = parseURL(o.Providers[0].RedeemURL, "redeem", msgs)
210+ p.ProfileURL, msgs = parseURL(o.Providers[0].ProfileURL, "profile", msgs)
211+ p.ValidateURL, msgs = parseURL(o.Providers[0].ValidateURL, "validate", msgs)
212+ p.ProtectedResource, msgs = parseURL(o.Providers[0].ProtectedResource, "resource", msgs)
236213
237214 // Make the OIDC options available to all providers that support it
238− p.AllowUnverifiedEmail = o.InsecureOIDCAllowUnverifiedEmail
239− p.EmailClaim = o.OIDCEmailClaim
240− p.GroupsClaim = o.OIDCGroupsClaim
215+ p.AllowUnverifiedEmail = o.Providers[0].OIDCConfig.InsecureAllowUnverifiedEmail
216+ p.EmailClaim = o.Providers[0].OIDCConfig.EmailClaim
217+ p.GroupsClaim = o.Providers[0].OIDCConfig.GroupsClaim
241218 p.Verifier = o.GetOIDCVerifier()
242219
243220 // TODO (@NickMeves) - Remove This
244221 // Backwards Compatibility for Deprecated UserIDClaim option
245− if o.OIDCEmailClaim == providers.OIDCEmailClaim &&
246− o.UserIDClaim != providers.OIDCEmailClaim {
247− p.EmailClaim = o.UserIDClaim
222+ if o.Providers[0].OIDCConfig.EmailClaim == providers.OIDCEmailClaim &&
223+ o.Providers[0].OIDCConfig.UserIDClaim != providers.OIDCEmailClaim {
224+ p.EmailClaim = o.Providers[0].OIDCConfig.UserIDClaim
248225 }
249226
250− p.SetAllowedGroups(o.AllowedGroups)
227+ p.SetAllowedGroups(o.Providers[0].AllowedGroups)
251228
252− provider := providers.New(o.ProviderType, p)
229+ provider := providers.New(o.Providers[0].Type, p)
253230 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))
255232 return msgs
256233 }
257234 o.SetProvider(provider)
258235
259236 switch p := o.GetProvider().(type) {
260237 case *providers.AzureProvider:
261− p.Configure(o.AzureTenant)
238+ p.Configure(o.Providers[0].AzureConfig.Tenant)
262239 case *providers.GitHubProvider:
263− p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
264− p.SetRepo(o.GitHubRepo, o.GitHubToken)
265− p.SetUsers(o.GitHubUsers)
240+ p.SetOrgTeam(o.Providers[0].GitHubConfig.Org, o.Providers[0].GitHubConfig.Team)
241+ p.SetRepo(o.Providers[0].GitHubConfig.Repo, o.Providers[0].GitHubConfig.Token)
242+ p.SetUsers(o.Providers[0].GitHubConfig.Users)
266243 case *providers.KeycloakProvider:
267244 // Backwards compatibility with `--keycloak-group` option
268− if len(o.KeycloakGroups) > 0 {
269− p.SetAllowedGroups(o.KeycloakGroups)
245+ if len(o.Providers[0].KeycloakConfig.Groups) > 0 {
246+ p.SetAllowedGroups(o.Providers[0].KeycloakConfig.Groups)
270247 }
271248 case *providers.GoogleProvider:
272− if o.GoogleServiceAccountJSON != "" {
273− file, err := os.Open(o.GoogleServiceAccountJSON)
249+ if o.Providers[0].GoogleConfig.ServiceAccountJSON != "" {
250+ file, err := os.Open(o.Providers[0].GoogleConfig.ServiceAccountJSON)
274251 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)
276253 } else {
277− groups := o.AllowedGroups
254+ groups := o.Providers[0].AllowedGroups
278255 // Backwards compatibility with `--google-group` option
279− if len(o.GoogleGroups) > 0 {
280− groups = o.GoogleGroups
256+ if len(o.Providers[0].GoogleConfig.Groups) > 0 {
257+ groups = o.Providers[0].GoogleConfig.Groups
281258 p.SetAllowedGroups(groups)
282259 }
283− p.SetGroupRestriction(groups, o.GoogleAdminEmail, file)
260+ p.SetGroupRestriction(groups, o.Providers[0].GoogleConfig.AdminEmail, file)
284261 }
285262 }
286263 case *providers.BitbucketProvider:
287− p.SetTeam(o.BitbucketTeam)
288− p.SetRepository(o.BitbucketRepository)
264+ p.SetTeam(o.Providers[0].BitbucketConfig.Team)
265+ p.SetRepository(o.Providers[0].BitbucketConfig.Repository)
289266 case *providers.OIDCProvider:
290267 if p.Verifier == nil {
291268 msgs = append(msgs, "oidc provider requires an oidc issuer URL")
292269 }
293270 case *providers.GitLabProvider:
294− p.Groups = o.GitLabGroup
295− err := p.AddProjects(o.GitlabProjects)
271+ p.Groups = o.Providers[0].GitLabConfig.Group
272+ err := p.AddProjects(o.Providers[0].GitLabConfig.Projects)
296273 if err != nil {
297274 msgs = append(msgs, "failed to setup gitlab project access level")
298275 }
@@ -308,7 +285,7 @@ func parseProviderInfo(o *options.Options, msgs []string) []string {
308285 msgs = append(msgs, "failed to initialize oidc provider for gitlab.com")
309286 } else {
310287 p.Verifier = provider.Verifier(&oidc.Config{
311− ClientID: o.ClientID,
288+ ClientID: o.Providers[0].ClientID,
312289 })
313290
314291 p.LoginURL, msgs = parseURL(provider.Endpoint().AuthURL, "login", msgs)
@@ -316,31 +293,31 @@ func parseProviderInfo(o *options.Options, msgs []string) []string {
316293 }
317294 }
318295 case *providers.LoginGovProvider:
319− p.PubJWKURL, msgs = parseURL(o.PubJWKURL, "pubjwk", msgs)
296+ p.PubJWKURL, msgs = parseURL(o.Providers[0].LoginGovConfig.PubJWKURL, "pubjwk", msgs)
320297
321298 // JWT key can be supplied via env variable or file in the filesystem, but not both.
322299 switch {
323− case o.JWTKey != "" && o.JWTKeyFile != "":
300+ case o.Providers[0].LoginGovConfig.JWTKey != "" && o.Providers[0].LoginGovConfig.JWTKeyFile != "":
324301 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 == "":
326303 msgs = append(msgs, "login.gov provider requires a private key for signing JWTs")
327− case o.JWTKey != "":
304+ case o.Providers[0].LoginGovConfig.JWTKey != "":
328305 // 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))
330307 if err != nil {
331308 msgs = append(msgs, "could not parse RSA Private Key PEM")
332309 } else {
333310 p.JWTKey = signKey
334311 }
335− case o.JWTKeyFile != "":
312+ case o.Providers[0].LoginGovConfig.JWTKeyFile != "":
336313 // The JWT key is in the filesystem
337− keyData, err := ioutil.ReadFile(o.JWTKeyFile)
314+ keyData, err := ioutil.ReadFile(o.Providers[0].LoginGovConfig.JWTKeyFile)
338315 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)
340317 }
341318 signKey, err := jwt.ParseRSAPrivateKeyFromPEM(keyData)
342319 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)
344321 } else {
345322 p.JWTKey = signKey
346323 }