@@ -0,0 +1,189 @@
10+ "github.com/coreos/go-oidc/v3/oidc"
12+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
13+ . "github.com/onsi/ginkgo"
14+ . "github.com/onsi/gomega"
18+ accessTokenHeader = "ewogICJhbGciOiAiUlMyNTYiLAogICJ0eXAiOiAiSldUIgp9"
19+ accessTokenPayload = "eyJyZWFsbV9hY2Nlc3MiOiB7InJvbGVzIjogWyJ3cml0ZSJdfSwgInJlc291cmNlX2FjY2VzcyI6IHsiZGVmYXVsdCI6IHsicm9sZXMiOiBbInJlYWQiXX19fQ"
20+ accessTokenSignature = "dyt0CoTl4WoVjAHI9Q_CwSKhl6d_9rhM3NrXuJttkao"
23+type DummyKeySet struct{}
25+func (DummyKeySet) VerifySignature(_ context.Context, _ string) (payload []byte, err error) {
26+ p, _ := base64.RawURLEncoding.DecodeString(accessTokenPayload)
30+func getAccessToken() string {
31+ return fmt.Sprintf("%s.%s.%s", accessTokenHeader, accessTokenPayload, accessTokenSignature)
34+func newTestKeycloakOIDCSetup() (*httptest.Server, *KeycloakOIDCProvider) {
35+ redeemURL, server := newOIDCServer([]byte(fmt.Sprintf(`{"email": "new@thing.com", "expires_in": 300, "access_token": "%v"}`, getAccessToken())))
36+ provider := newKeycloakOIDCProvider(redeemURL)
37+ return server, provider
40+func newKeycloakOIDCProvider(serverURL *url.URL) *KeycloakOIDCProvider {
41+ p := NewKeycloakOIDCProvider(
45+ Host: "keycloak-oidc.com",
46+ Path: "/oauth/auth"},
49+ Host: "keycloak-oidc.com",
50+ Path: "/oauth/token"},
51+ ProfileURL: &url.URL{
53+ Host: "keycloak-oidc.com",
54+ Path: "/api/v3/user"},
55+ ValidateURL: &url.URL{
57+ Host: "keycloak-oidc.com",
58+ Path: "/api/v3/user"},
59+ Scope: "openid email profile"})
61+ if serverURL != nil {
62+ p.RedeemURL.Scheme = serverURL.Scheme
63+ p.RedeemURL.Host = serverURL.Host
66+ keyset := DummyKeySet{}
67+ p.Verifier = oidc.NewVerifier("", keyset, &oidc.Config{
69+ SkipIssuerCheck: true,
70+ SkipClientIDCheck: true,
71+ SkipExpiryCheck: true,
73+ p.EmailClaim = "email"
74+ p.GroupsClaim = "groups"
78+var _ = Describe("Keycloak OIDC Provider Tests", func() {
79+ Context("New Provider Init", func() {
80+ It("creates new keycloak oidc provider with expected defaults", func() {
81+ p := newKeycloakOIDCProvider(nil)
82+ providerData := p.Data()
83+ Expect(providerData.ProviderName).To(Equal(keycloakOIDCProviderName))
84+ Expect(providerData.LoginURL.String()).To(Equal("https://keycloak-oidc.com/oauth/auth"))
85+ Expect(providerData.RedeemURL.String()).To(Equal("https://keycloak-oidc.com/oauth/token"))
86+ Expect(providerData.ProfileURL.String()).To(Equal("https://keycloak-oidc.com/api/v3/user"))
87+ Expect(providerData.ValidateURL.String()).To(Equal("https://keycloak-oidc.com/api/v3/user"))
88+ Expect(providerData.Scope).To(Equal("openid email profile"))
92+ Context("Allowed Roles", func() {
93+ It("should prefix allowed roles and add them to groups", func() {
94+ p := newKeycloakOIDCProvider(nil)
95+ p.AddAllowedRoles([]string{"admin", "editor"})
96+ Expect(p.AllowedGroups).To(HaveKey("role:admin"))
97+ Expect(p.AllowedGroups).To(HaveKey("role:editor"))
101+ Context("Enrich Session", func() {
102+ It("should not fail when groups are not assigned", func() {
103+ server, provider := newTestKeycloakOIDCSetup()
104+ url, err := url.Parse(server.URL)
105+ Expect(err).To(BeNil())
106+ defer server.Close()
108+ provider.ProfileURL = url
110+ existingSession := &sessions.SessionState{
115+ AccessToken: getAccessToken(),
116+ RefreshToken: refreshToken,
118+ expectedSession := &sessions.SessionState{
121+ Groups: []string{"role:write", "role:default:read"},
123+ AccessToken: getAccessToken(),
124+ RefreshToken: refreshToken,
127+ err = provider.EnrichSession(context.Background(), existingSession)
128+ Expect(err).To(BeNil())
129+ Expect(existingSession).To(Equal(expectedSession))
132+ It("should add roles to existing groups", func() {
133+ server, provider := newTestKeycloakOIDCSetup()
134+ url, err := url.Parse(server.URL)
135+ Expect(err).To(BeNil())
136+ defer server.Close()
138+ provider.ProfileURL = url
140+ existingSession := &sessions.SessionState{
143+ Groups: []string{"existing", "group"},
145+ AccessToken: getAccessToken(),
146+ RefreshToken: refreshToken,
148+ expectedSession := &sessions.SessionState{
151+ Groups: []string{"existing", "group", "role:write", "role:default:read"},
153+ AccessToken: getAccessToken(),
154+ RefreshToken: refreshToken,
157+ err = provider.EnrichSession(context.Background(), existingSession)
158+ Expect(err).To(BeNil())
159+ Expect(existingSession).To(Equal(expectedSession))
163+ Context("Refresh Session", func() {
164+ It("should refresh session and extract roles again", func() {
165+ server, provider := newTestKeycloakOIDCSetup()
166+ url, err := url.Parse(server.URL)
167+ Expect(err).To(BeNil())
168+ defer server.Close()
170+ provider.ProfileURL = url
172+ existingSession := &sessions.SessionState{
177+ AccessToken: getAccessToken(),
178+ RefreshToken: refreshToken,
181+ refreshed, err := provider.RefreshSession(context.Background(), existingSession)
182+ Expect(err).To(BeNil())
183+ Expect(refreshed).To(BeTrue())
184+ Expect(existingSession.ExpiresOn).ToNot(BeNil())
185+ Expect(existingSession.CreatedAt).ToNot(BeNil())
186+ Expect(existingSession.Groups).To(BeEquivalentTo([]string{"role:write", "role:default:read"}))