git.iko.soy/typeable/oauth2-proxy
on master help sign in
typeable/oauth2-proxy/providers/providers_test.go
404 lines 12 kB history raw
1package providers
3import (
4 "context"
5 "net"
6 "os"
7 "testing"
8 "time"
10 "github.com/golang-jwt/jwt/v5"
11 "github.com/oauth2-proxy/mockoidc"
12 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
13 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
14 . "github.com/onsi/gomega"
17const (
18 clientID = "bazquux"
19 clientSecret = "xyzzyplugh"
20 providerID = "providerID"
22 msIssuerURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/v2.0/"
23 msKeysURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/discovery/v2.0/keys"
24 msAuthURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_sign_in"
25 msTokenURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_sign_in"
28func TestClientSecretFileOptionFails(t *testing.T) {
29 g := NewWithT(t)
31 providerConfig := options.Provider{
32 ID: providerID,
33 Type: "google",
34 ClientID: clientID,
35 ClientSecretFile: clientSecret,
36 }
38 p, err := newProviderDataFromConfig(providerConfig)
39 g.Expect(err).ToNot(HaveOccurred())
40 g.Expect(p.ClientSecretFile).To(Equal(clientSecret))
41 g.Expect(p.ClientSecret).To(BeEmpty())
43 s, err := p.GetClientSecret()
44 g.Expect(err).To(HaveOccurred())
45 g.Expect(s).To(BeEmpty())
48func TestClientSecretFileOption(t *testing.T) {
49 g := NewWithT(t)
51 f, err := os.CreateTemp("", "client_secret_temp_file_")
52 g.Expect(err).ToNot(HaveOccurred())
54 clientSecretFileName := f.Name()
56 defer func() {
57 g.Expect(f.Close()).To(Succeed())
58 g.Expect(os.Remove(clientSecretFileName)).To(Succeed())
59 }()
61 _, err = f.WriteString("testcase")
62 g.Expect(err).ToNot(HaveOccurred())
64 providerConfig := options.Provider{
65 ID: providerID,
66 Type: "google",
67 ClientID: clientID,
68 ClientSecretFile: clientSecretFileName,
69 }
71 p, err := newProviderDataFromConfig(providerConfig)
72 g.Expect(err).ToNot(HaveOccurred())
73 g.Expect(p.ClientSecretFile).To(Equal(clientSecretFileName))
74 g.Expect(p.ClientSecret).To(BeEmpty())
76 s, err := p.GetClientSecret()
77 g.Expect(err).ToNot(HaveOccurred())
78 g.Expect(s).To(Equal("testcase"))
81func TestSkipOIDCDiscovery(t *testing.T) {
82 g := NewWithT(t)
83 providerConfig := options.Provider{
84 ID: providerID,
85 Type: "oidc",
86 ClientID: clientID,
87 ClientSecretFile: clientSecret,
88 OIDCConfig: options.OIDCOptions{
89 IssuerURL: msIssuerURL,
90 SkipDiscovery: ptr.To(true),
91 },
92 }
94 _, err := newProviderDataFromConfig(providerConfig)
95 g.Expect(err).To(MatchError("error building OIDC ProviderVerifier: invalid provider verifier options: missing required setting: jwks-url or public-key-files"))
97 providerConfig.LoginURL = msAuthURL
98 providerConfig.RedeemURL = msTokenURL
99 providerConfig.OIDCConfig.JwksURL = msKeysURL
101 _, err = newProviderDataFromConfig(providerConfig)
102 g.Expect(err).ToNot(HaveOccurred())
105func TestURLsCorrectlyParsed(t *testing.T) {
106 g := NewWithT(t)
108 providerConfig := options.Provider{
109 ID: providerID,
110 Type: "oidc",
111 ClientID: clientID,
112 ClientSecretFile: clientSecret,
113 LoginURL: msAuthURL,
114 RedeemURL: msTokenURL,
115 OIDCConfig: options.OIDCOptions{
116 IssuerURL: msIssuerURL,
117 SkipDiscovery: ptr.To(true),
118 JwksURL: msKeysURL,
119 },
120 }
122 pd, err := newProviderDataFromConfig(providerConfig)
123 g.Expect(err).ToNot(HaveOccurred())
125 g.Expect(pd.LoginURL.String()).To(Equal(msAuthURL))
126 g.Expect(pd.RedeemURL.String()).To(Equal(msTokenURL))
129func TestEnabledSigningAlgsAreAppliedToProviderVerifier(t *testing.T) {
130 g := NewWithT(t)
132 m, err := mockoidc.NewServer(nil)
133 g.Expect(err).ToNot(HaveOccurred())
134 m.AddMiddleware(newSigningAlgsIssuerMiddleware(m, []string{"RS256", "HS256"}))
136 listener, err := net.Listen("tcp", "127.0.0.1:0")
137 g.Expect(err).ToNot(HaveOccurred())
138 g.Expect(m.Start(listener, nil)).To(Succeed())
139 defer func() {
140 g.Expect(m.Shutdown()).To(Succeed())
141 }()
143 providerConfig := options.Provider{
144 ID: providerID,
145 Type: "oidc",
146 ClientID: m.Config().ClientID,
147 ClientSecretFile: clientSecret,
148 OIDCConfig: options.OIDCOptions{
149 IssuerURL: m.Issuer(),
150 AudienceClaims: []string{"aud"},
151 EnabledSigningAlgs: []string{"HS256"},
152 },
153 }
155 pd, err := newProviderDataFromConfig(providerConfig)
156 g.Expect(err).ToNot(HaveOccurred())
158 rawIDToken, err := m.Keypair.SignJWT(jwt.RegisteredClaims{
159 Audience: jwt.ClaimStrings{m.Config().ClientID},
160 Issuer: m.Issuer(),
161 ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
162 IssuedAt: jwt.NewNumericDate(time.Now()),
163 Subject: "user",
164 })
165 g.Expect(err).ToNot(HaveOccurred())
167 _, err = pd.Verifier.Verify(context.Background(), rawIDToken)
168 g.Expect(err).To(HaveOccurred())
169 g.Expect(err.Error()).To(ContainSubstring("unexpected signature algorithm"))
172func TestEnabledSigningAlgsRejectUnsupportedTokens(t *testing.T) {
173 g := NewWithT(t)
175 m, err := mockoidc.Run()
176 g.Expect(err).ToNot(HaveOccurred())
177 defer func() {
178 g.Expect(m.Shutdown()).To(Succeed())
179 }()
181 providerConfig := options.Provider{
182 ID: providerID,
183 Type: "oidc",
184 ClientID: m.Config().ClientID,
185 ClientSecretFile: clientSecret,
186 LoginURL: m.AuthorizationEndpoint(),
187 RedeemURL: m.TokenEndpoint(),
188 OIDCConfig: options.OIDCOptions{
189 IssuerURL: m.Issuer(),
190 SkipDiscovery: ptr.To(true),
191 JwksURL: m.JWKSEndpoint(),
192 AudienceClaims: []string{"aud"},
193 EnabledSigningAlgs: []string{"HS256"},
194 },
195 }
197 pd, err := newProviderDataFromConfig(providerConfig)
198 g.Expect(err).ToNot(HaveOccurred())
200 rawIDToken, err := m.Keypair.SignJWT(jwt.RegisteredClaims{
201 Audience: jwt.ClaimStrings{m.Config().ClientID},
202 Issuer: m.Issuer(),
203 ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
204 IssuedAt: jwt.NewNumericDate(time.Now()),
205 Subject: "user",
206 })
207 g.Expect(err).ToNot(HaveOccurred())
209 _, err = pd.Verifier.Verify(context.Background(), rawIDToken)
210 g.Expect(err).To(HaveOccurred())
211 g.Expect(err.Error()).To(ContainSubstring("unexpected signature algorithm"))
214func TestScope(t *testing.T) {
215 g := NewWithT(t)
217 testCases := []struct {
218 name string
219 configuredType options.ProviderType
220 configuredScope string
221 expectedScope string
222 allowedGroups []string
223 }{
224 {
225 name: "oidc: with no scope provided",
226 configuredType: "oidc",
227 configuredScope: "",
228 expectedScope: "openid email profile",
229 },
230 {
231 name: "oidc: with no scope provided and allowed groups",
232 configuredType: "oidc",
233 configuredScope: "",
234 expectedScope: "openid email profile groups",
235 allowedGroups: []string{"foo"},
236 },
237 {
238 name: "oidc: with custom scope including groups without allowed groups",
239 configuredType: "oidc",
240 configuredScope: "myscope groups",
241 expectedScope: "myscope groups",
242 },
243 {
244 name: "oidc: with custom scope without groups but allowed groups",
245 configuredType: "oidc",
246 configuredScope: "myscope",
247 expectedScope: "myscope",
248 allowedGroups: []string{"foo"},
249 },
250 {
251 name: "oidc: with custom scope with groups and allowed groups",
252 configuredType: "oidc",
253 configuredScope: "myscope groups",
254 expectedScope: "myscope groups",
255 allowedGroups: []string{"foo"},
256 },
257 {
258 name: "oidc: with a configured scope provided",
259 configuredType: "oidc",
260 configuredScope: "openid",
261 expectedScope: "openid",
262 },
263 {
264 name: "github: with no scope provided",
265 configuredType: "github",
266 configuredScope: "",
267 expectedScope: "user:email read:org",
268 },
269 {
270 name: "github: with a configured scope provided",
271 configuredType: "github",
272 configuredScope: "read:user read:org",
273 expectedScope: "read:user read:org",
274 },
275 {
276 name: "keycloak: with no scope provided and groups",
277 configuredType: "keycloak-oidc",
278 configuredScope: "",
279 expectedScope: "openid email profile groups",
280 allowedGroups: []string{"foo"},
281 },
282 {
283 name: "keycloak: with custom scope and groups",
284 configuredType: "keycloak-oidc",
285 configuredScope: "myscope",
286 expectedScope: "myscope",
287 allowedGroups: []string{"foo"},
288 },
289 {
290 name: "keycloak: with custom scope and groups scope",
291 configuredType: "keycloak-oidc",
292 configuredScope: "myscope groups",
293 expectedScope: "myscope groups",
294 allowedGroups: []string{"foo"},
295 },
296 }
298 for _, tc := range testCases {
299 providerConfig := options.Provider{
300 ID: providerID,
301 Type: tc.configuredType,
302 ClientID: clientID,
303 ClientSecretFile: clientSecret,
304 LoginURL: msAuthURL,
305 RedeemURL: msTokenURL,
306 Scope: tc.configuredScope,
307 AllowedGroups: tc.allowedGroups,
308 OIDCConfig: options.OIDCOptions{
309 IssuerURL: msIssuerURL,
310 SkipDiscovery: ptr.To(true),
311 JwksURL: msKeysURL,
312 },
313 }
315 pd, err := NewProvider(providerConfig)
316 g.Expect(err).ToNot(HaveOccurred())
318 g.Expect(pd.Data().Scope).To(Equal(tc.expectedScope))
319 }
322func TestForcedMethodS256(t *testing.T) {
323 g := NewWithT(t)
324 options := options.NewOptions()
325 options.Providers[0].CodeChallengeMethod = CodeChallengeMethodS256
326 method := parseCodeChallengeMethod(options.Providers[0])
328 g.Expect(method).To(Equal(CodeChallengeMethodS256))
331func TestForcedMethodPlain(t *testing.T) {
332 g := NewWithT(t)
333 options := options.NewOptions()
334 options.Providers[0].CodeChallengeMethod = CodeChallengeMethodPlain
335 method := parseCodeChallengeMethod(options.Providers[0])
337 g.Expect(method).To(Equal(CodeChallengeMethodPlain))
340func TestPrefersS256(t *testing.T) {
341 g := NewWithT(t)
342 options := options.NewOptions()
343 method := parseCodeChallengeMethod(options.Providers[0])
345 g.Expect(method).To(Equal(""))
348func TestCanOverwriteS256(t *testing.T) {
349 g := NewWithT(t)
350 options := options.NewOptions()
351 options.Providers[0].CodeChallengeMethod = "plain"
352 method := parseCodeChallengeMethod(options.Providers[0])
354 g.Expect(method).To(Equal(CodeChallengeMethodPlain))
357func TestEmailClaimCorrectlySet(t *testing.T) {
358 g := NewWithT(t)
360 testCases := []struct {
361 name string
362 userIDClaim string
363 emailClaim string
364 expectedEmailClaim string
365 }{
366 {
367 name: "do not override EmailClaim if UserIDClaim is empty",
368 userIDClaim: "",
369 emailClaim: "email",
370 expectedEmailClaim: "email",
371 },
372 {
373 name: "set EmailClaim to UserIDClaim",
374 userIDClaim: "user_id_claim",
375 emailClaim: "email",
376 expectedEmailClaim: "user_id_claim",
377 },
378 }
380 for _, tc := range testCases {
381 t.Run(tc.name, func(t *testing.T) {
382 providerConfig := options.Provider{
383 ID: providerID,
384 Type: "oidc",
385 ClientID: clientID,
386 ClientSecretFile: clientSecret,
387 LoginURL: msAuthURL,
388 RedeemURL: msTokenURL,
389 OIDCConfig: options.OIDCOptions{
390 IssuerURL: msIssuerURL,
391 SkipDiscovery: ptr.To(true),
392 JwksURL: msKeysURL,
393 UserIDClaim: tc.userIDClaim,
394 EmailClaim: tc.emailClaim,
395 },
396 }
398 pd, err := newProviderDataFromConfig(providerConfig)
399 g.Expect(err).ToNot(HaveOccurred())
401 g.Expect(pd.EmailClaim).To(Equal(tc.expectedEmailClaim))
402 })
403 }