11 "github.com/coreos/go-oidc/v3/oidc"
13 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
14 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
15 . "github.com/onsi/ginkgo/v2"
16 . "github.com/onsi/gomega"
18 internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
22 idTokenHeader = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJjV1IteTRzRVU1MjZVelk1SFd6UEZJbWdMMWRKUllfQ0gyY1FFRXh4UGN3In0"
23 idTokenSignature = "Rh0zQGhWAm-2hn5JTWB3Lzuk9Ahpzs7As7ks-1VInl4"
24 accessTokenHeader = "ewogICJhbGciOiAiUlMyNTYiLAogICJ0eXAiOiAiSldUIgp9"
25 accessTokenSignature = "dyt0CoTl4WoVjAHI9Q_CwSKhl6d_9rhM3NrXuJttkao"
26 defaultAudienceClaim = "aud"
27 mockClientID = "cd6d4fae-f6a6-4a34-8454-2c6b598e9532"
31 accessTokenPayload = base64.RawURLEncoding.EncodeToString([]byte(
32 fmt.Sprintf(`{"%s": "%s", "realm_access": {"roles": ["write"]}, "resource_access": {"default": {"roles": ["read"]}}}`, defaultAudienceClaim, mockClientID)))
34 idTokenPayload = base64.RawURLEncoding.EncodeToString([]byte(
35 fmt.Sprintf(`{"%s": "%s"}`, defaultAudienceClaim, mockClientID)))
38type DummyKeySet struct{}
40func (DummyKeySet) VerifySignature(_ context.Context, jwt string) (payload []byte, err error) {
41 parts := strings.Split(jwt, ".")
42 p, _ := base64.RawURLEncoding.DecodeString(parts[1])
46func makeIDToken() string {
47 return fmt.Sprintf("%s.%s.%s", idTokenHeader, idTokenPayload, idTokenSignature)
50func makeAccessToken() string {
51 return fmt.Sprintf("%s.%s.%s", accessTokenHeader, accessTokenPayload, accessTokenSignature)
54func newTestKeycloakOIDCSetup() (*httptest.Server, *KeycloakOIDCProvider) {
55 redeemURL, server := newOIDCServer([]byte(fmt.Sprintf(`{"email": "new@thing.com", "expires_in": 300, "id_token": "%v", "access_token": "%v"}`, makeIDToken(), makeAccessToken())))
56 provider := newKeycloakOIDCProvider(redeemURL, options.Provider{})
57 return server, provider
60func newKeycloakOIDCProvider(serverURL *url.URL, opts options.Provider) *KeycloakOIDCProvider {
61 verificationOptions := internaloidc.IDTokenVerificationOptions{
62 AudienceClaims: []string{defaultAudienceClaim},
63 ClientID: mockClientID,
65 p := NewKeycloakOIDCProvider(
69 Host: "keycloak-oidc.com",
73 Host: "keycloak-oidc.com",
74 Path: "/oauth/token"},
77 Host: "keycloak-oidc.com",
78 Path: "/api/v3/user"},
79 ValidateURL: &url.URL{
81 Host: "keycloak-oidc.com",
82 Path: "/api/v3/user"},
87 p.RedeemURL.Scheme = serverURL.Scheme
88 p.RedeemURL.Host = serverURL.Host
91 keyset := DummyKeySet{}
92 p.Verifier = internaloidc.NewVerifier(oidc.NewVerifier("", keyset, &oidc.Config{
94 SkipIssuerCheck: true,
95 SkipClientIDCheck: true,
96 SkipExpiryCheck: true,
97 }), verificationOptions)
98 p.EmailClaim = "email"
99 p.GroupsClaim = "groups"
103var _ = Describe("Keycloak OIDC Provider Tests", func() {
104 Context("New Provider Init", func() {
105 It("creates new keycloak oidc provider with expected defaults", func() {
106 p := newKeycloakOIDCProvider(nil, options.Provider{})
107 providerData := p.Data()
108 Expect(providerData.ProviderName).To(Equal(keycloakOIDCProviderName))
109 Expect(providerData.LoginURL.String()).To(Equal("https://keycloak-oidc.com/oauth/auth"))
110 Expect(providerData.RedeemURL.String()).To(Equal("https://keycloak-oidc.com/oauth/token"))
111 Expect(providerData.ProfileURL.String()).To(Equal("https://keycloak-oidc.com/api/v3/user"))
112 Expect(providerData.ValidateURL.String()).To(Equal("https://keycloak-oidc.com/api/v3/user"))
113 Expect(providerData.Scope).To(Equal(oidcDefaultScope))
115 It("creates new keycloak oidc provider with custom scope", func() {
116 p := NewKeycloakOIDCProvider(&ProviderData{Scope: "openid email"}, options.Provider{})
117 providerData := p.Data()
119 Expect(providerData.ProviderName).To(Equal(keycloakOIDCProviderName))
120 Expect(providerData.Scope).To(Equal("openid email"))
121 Expect(providerData.Scope).NotTo(Equal(oidcDefaultScope))
125 Context("Allowed Roles", func() {
126 It("should prefix allowed roles and add them to groups", func() {
127 p := newKeycloakOIDCProvider(nil, options.Provider{
128 KeycloakConfig: options.KeycloakOptions{
129 Roles: []string{"admin", "editor"},
132 Expect(p.AllowedGroups).To(HaveKey("role:admin"))
133 Expect(p.AllowedGroups).To(HaveKey("role:editor"))
137 Context("Enrich Session", func() {
138 It("should not fail when groups are not assigned", func() {
139 server, provider := newTestKeycloakOIDCSetup()
140 url, err := url.Parse(server.URL)
141 Expect(err).To(BeNil())
144 provider.ProfileURL = url
146 existingSession := &sessions.SessionState{
150 IDToken: makeIDToken(),
151 AccessToken: makeAccessToken(),
152 RefreshToken: refreshToken,
154 expectedSession := &sessions.SessionState{
157 Groups: []string{"role:write", "role:default:read"},
158 IDToken: makeIDToken(),
159 AccessToken: makeAccessToken(),
160 RefreshToken: refreshToken,
163 err = provider.EnrichSession(context.Background(), existingSession)
164 Expect(err).To(BeNil())
165 Expect(existingSession).To(Equal(expectedSession))
168 It("should add roles to existing groups", func() {
169 server, provider := newTestKeycloakOIDCSetup()
170 url, err := url.Parse(server.URL)
171 Expect(err).To(BeNil())
174 provider.ProfileURL = url
176 existingSession := &sessions.SessionState{
179 Groups: []string{"existing", "group"},
180 IDToken: makeIDToken(),
181 AccessToken: makeAccessToken(),
182 RefreshToken: refreshToken,
184 expectedSession := &sessions.SessionState{
187 Groups: []string{"existing", "group", "role:write", "role:default:read"},
188 IDToken: makeIDToken(),
189 AccessToken: makeAccessToken(),
190 RefreshToken: refreshToken,
193 err = provider.EnrichSession(context.Background(), existingSession)
194 Expect(err).To(BeNil())
195 Expect(existingSession).To(Equal(expectedSession))
199 Context("Refresh Session", func() {
200 It("should refresh session and extract roles again", func() {
201 server, provider := newTestKeycloakOIDCSetup()
202 url, err := url.Parse(server.URL)
203 Expect(err).To(BeNil())
206 provider.ProfileURL = url
208 existingSession := &sessions.SessionState{
212 IDToken: makeIDToken(),
213 AccessToken: makeAccessToken(),
214 RefreshToken: refreshToken,
217 refreshed, err := provider.RefreshSession(context.Background(), existingSession)
218 Expect(err).To(BeNil())
219 Expect(refreshed).To(BeTrue())
220 Expect(existingSession.ExpiresOn).ToNot(BeNil())
221 Expect(existingSession.CreatedAt).ToNot(BeNil())
222 Expect(existingSession.Groups).To(BeEquivalentTo([]string{"role:write", "role:default:read"}))
226 Context("Create new session from token", func() {
227 It("should create a session and extract roles ", func() {
228 server, provider := newTestKeycloakOIDCSetup()
229 url, err := url.Parse(server.URL)
230 Expect(err).To(BeNil())
233 provider.ProfileURL = url
235 session, err := provider.CreateSessionFromToken(context.Background(), makeAccessToken())
236 Expect(err).To(BeNil())
237 Expect(session.ExpiresOn).ToNot(BeNil())
238 Expect(session.CreatedAt).ToNot(BeNil())
239 Expect(session.Groups).To(BeEquivalentTo([]string{"role:write", "role:default:read"}))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list