Skip to content

Commit 6f6054e

Browse files
authored
Add possibility to add userinfo claims (#680)
1 parent cfdb0e6 commit 6f6054e

File tree

9 files changed

+46
-6
lines changed

9 files changed

+46
-6
lines changed

docs/content/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ option is set to `true`.
751751

752752
## Custom claim headers
753753

754-
You can inject additional claims from the access token and from version 4.6.0 also from ID token into the
754+
You can inject additional claims from the access token and from version 4.6.0 also from ID token and userinfo into the
755755
upstream headers with the `--add-claims` option. For example, a
756756
token from a Keycloak provider might include the following
757757
claims:

docs/content/configuration/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ weight: 2
158158
| \--enable-request-upstream-compression | enables asking upstream for compression, by adding Accept-Encoding: gzip header and decompressing response from upstream | true | PROXY_ENABLE_REQUEST_UPSTREAM_COMPRESSION
159159
| \--enable-accept-encoding-header | pass Accept-Encoding header from client to upstream | false | PROXY_ENABLE_ACCEPT_ENCODING_HEADER
160160
| \--enable-id-token-claims | extract claims also from ID token, you can then use --add-claims option to specify claims from ID token | false | PROXY_ENABLE_ID_TOKEN_CLAIMS
161+
| \--enable-userinfo-claims | extract claims also from userinfo, you can then use --add-claims option to specify claims | false | PROXY_ENABLE_USER_INFO_CLAIMS
161162
| \--enable-loa | enable level of authentication | false |
162163
| \--enable-store-ha | enable RedisCluster HA client | false | PROXY_ENABLE_STORE_HA
163164
| \--disable-all-logging | disables all logging to stdout and stderr | false | PROXY_DISABLE_ALL_LOGGING

e2e/e2e_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,9 @@ var _ = Describe("Code Flow login/logout", func() {
593593
"--enable-encrypted-token=false",
594594
"--enable-id-token-claims=true",
595595
"--enable-id-token-cookie=true",
596+
"--enable-user-info-claims=true",
596597
"--add-claims=email_verified",
598+
"--add-claims=email",
597599
"--enable-pkce=false",
598600
"--tls-cert=" + tlsCertificate,
599601
"--tls-private-key=" + tlsPrivateKey,
@@ -655,9 +657,11 @@ var _ = Describe("Code Flow login/logout", func() {
655657
Expect(err).NotTo(HaveOccurred())
656658
Expect(resp.Header().Get("Proxy-Accepted")).To(Equal("true"))
657659
body = resp.Body()
660+
By(string(body))
658661
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
659662
Expect(strings.Contains(string(body), anyURI)).To(BeTrue())
660-
Expect(body).To(ContainSubstring("X-Auth-Email-Verified"))
663+
Expect(body).To(ContainSubstring(`"X-Auth-Email-Verified":["true"]`))
664+
Expect(body).To(ContainSubstring(`"X-Auth-Email":["somebody@somewhere.com"]`))
661665

662666
By("log out")
663667
resp, err = rClient.R().Get(proxyAddress + logoutURI)
@@ -832,6 +836,7 @@ var _ = Describe("Code Flow login/logout mTLS", func() {
832836
"--enable-register-handler=true",
833837
"--enable-encrypted-token=false",
834838
"--enable-pkce=false",
839+
"--add-claims=email",
835840
"--tls-cert=" + tlsCertificate,
836841
"--tls-private-key=" + tlsPrivateKey,
837842
"--upstream-ca=" + tlsCaCertificate,
@@ -911,6 +916,7 @@ var _ = Describe("Code Flow login/logout mTLS", func() {
911916
body = resp.Body()
912917
Expect(strings.Contains(string(body), anyURI)).To(BeTrue())
913918
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
919+
Expect(body).To(ContainSubstring(`"X-Auth-Email":[""]`))
914920

915921
By("log out")
916922
resp, err = rClient.R().Get(proxyAddress + logoutURI)

e2e/k8s/manifest.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,8 +1025,8 @@ data:
10251025
"config": {
10261026
"userinfo.token.claim": "true",
10271027
"user.attribute": "email",
1028-
"id.token.claim": "true",
1029-
"access.token.claim": "true",
1028+
"id.token.claim": "false",
1029+
"access.token.claim": "false",
10301030
"claim.name": "email",
10311031
"jsonType.label": "String"
10321032
}

pkg/keycloak/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ type Config struct {
201201
EnableRequestUpstreamCompression bool `env:"ENABLE_REQUEST_UPSTREAM_COMPRESSION" json:"enable-request-upstream-compression" usage:"enables asking upstream for compression, by adding Accept-Encoding: gzip header and decompressing response from upstream" yaml:"enable-request-upstream-compression"`
202202
EnableAcceptEncodingHeader bool `env:"ENABLE_ACCEPT_ENCODING_HEADER" json:"enable-accept-encoding-header" usage:"pass Accept-Encoding header from client to upstream" yaml:"enable-accept-encoding-header"`
203203
EnableIDTokenClaims bool `env:"ENABLE_ID_TOKEN_CLAIMS" json:"enable-id-token-claims" usage:"extract claims also from id token, you can then use --add-claims option to specify claims from id token" yaml:"enable-id-token-claims"`
204+
EnableUserInfoClaims bool `env:"ENABLE_USER_INFO_CLAIMS" json:"enable-user-info-claims" usage:"extract information from userinfo endpoint, you can then use --add-claims to specify claims" yaml:"enable-user-info-claims"`
204205
IsDiscoverURILegacy bool
205206
}
206207

pkg/keycloak/proxy/server.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ func (r *OauthProxy) CreateReverseProxy() error {
564564
r.Config.EnableOptionalEncryption,
565565
r.Config.EnableCompressToken,
566566
r.Config.EnableIDTokenClaims,
567+
r.Config.EnableUserInfoClaims,
567568
compressTokenPool,
568569
)
569570

@@ -858,6 +859,7 @@ func (r *OauthProxy) CreateReverseProxy() error {
858859
r.Config.EnableAuthorizationCookies,
859860
r.Config.EnableHeaderEncoding,
860861
r.Config.EnableIDTokenClaims,
862+
r.Config.EnableUserInfoClaims,
861863
)
862864

863865
middlewares := []func(http.Handler) http.Handler{

pkg/proxy/middleware/base.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ func IdentityHeadersMiddleware(
227227
enableAuthzCookies bool,
228228
enableHeaderEncoding bool,
229229
enableIDTokenClaims bool,
230+
enableUserInfoClaims bool,
230231
) func(http.Handler) http.Handler {
231232
customClaims := make(map[string]string)
232233

@@ -317,6 +318,17 @@ func IdentityHeadersMiddleware(
317318
headers.Set(header, val)
318319
}
319320
}
321+
322+
if enableUserInfoClaims {
323+
if claim, found := user.UserInfoClaims[claim]; found {
324+
val := fmt.Sprintf("%v", claim)
325+
if enableHeaderEncoding {
326+
val = mime.BEncoding.Encode(encoding, val)
327+
}
328+
329+
headers.Set(header, val)
330+
}
331+
}
320332
}
321333
}
322334

pkg/proxy/middleware/oauth.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func AuthenticationMiddleware(
5050
enableOptionalEncryption bool,
5151
enableCompressToken bool,
5252
enableIDTokenClaims bool,
53+
enableUserInfoClaims bool,
5354
compressTokenPool *utils.LimitedBufferPool,
5455
) func(http.Handler) http.Handler {
5556
return func(next http.Handler) http.Handler {
@@ -430,19 +431,34 @@ func AuthenticationMiddleware(
430431
scope.Identity = user
431432
}
432433

433-
if enableIDPSessionCheck {
434+
if enableIDPSessionCheck || enableUserInfoClaims {
434435
tokenSource := oauth2.StaticTokenSource(
435436
&oauth2.Token{AccessToken: scope.Identity.RawToken},
436437
)
437438

438-
_, err := provider.UserInfo(oidcLibCtx, tokenSource)
439+
userInfo, err := provider.UserInfo(oidcLibCtx, tokenSource)
439440
if err != nil {
440441
scope.Logger.Error(err.Error())
441442
core.RevokeProxy(logger, req)
442443
next.ServeHTTP(wrt, req)
443444

444445
return
445446
}
447+
448+
if enableUserInfoClaims {
449+
claims := map[string]any{}
450+
451+
err = userInfo.Claims(&claims)
452+
if err != nil {
453+
scope.Logger.Error(err.Error())
454+
core.RevokeProxy(logger, req)
455+
next.ServeHTTP(wrt, req)
456+
457+
return
458+
}
459+
460+
scope.Identity.UserInfoClaims = claims
461+
}
446462
}
447463

448464
*req = *(req.WithContext(ctx))

pkg/proxy/models/user.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,6 @@ type UserContext struct {
7777
Permissions Permissions
7878
// IDClaims
7979
IDTokenClaims map[string]any
80+
// UserInfoClaims
81+
UserInfoClaims map[string]any
8082
}

0 commit comments

Comments
 (0)