1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-08-08 22:46:33 +02:00

feat: ability to parse JWT encoded profile claims (#3014)

* fix: parse JWT profile claims

* Comment with OIDC specs reference

* fix: formatting

* Updated changelog

---------

Co-authored-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
ikarius
2025-04-28 16:39:09 +02:00
committed by GitHub
parent 7b41c8e987
commit b7ff804506
3 changed files with 67 additions and 3 deletions

View File

@ -12,6 +12,7 @@
- [#3001](https://github.com/oauth2-proxy/oauth2-proxy/pull/3001) Allow to set non-default authorization request response mode (@stieler-it) - [#3001](https://github.com/oauth2-proxy/oauth2-proxy/pull/3001) Allow to set non-default authorization request response mode (@stieler-it)
- [#3041](https://github.com/oauth2-proxy/oauth2-proxy/pull/3041) chore(deps): upgrade to latest golang v1.23.x release (@TheImplementer) - [#3041](https://github.com/oauth2-proxy/oauth2-proxy/pull/3041) chore(deps): upgrade to latest golang v1.23.x release (@TheImplementer)
- [#1916](https://github.com/oauth2-proxy/oauth2-proxy/pull/1916) fix: role extraction from access token in keycloak oidc (@Elektordi / @tuunit) - [#1916](https://github.com/oauth2-proxy/oauth2-proxy/pull/1916) fix: role extraction from access token in keycloak oidc (@Elektordi / @tuunit)
- [#3014](https://github.com/oauth2-proxy/oauth2-proxy/pull/3014) feat: ability to parse JWT encoded profile claims (@ikarius)
# V7.8.2 # V7.8.2

View File

@ -5,6 +5,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"mime"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -94,11 +95,25 @@ func (c *claimExtractor) loadProfileClaims() (*simplejson.Json, error) {
return simplejson.New(), nil return simplejson.New(), nil
} }
claims, err := requests.New(c.profileURL.String()). builder := requests.New(c.profileURL.String()).
WithContext(c.ctx). WithContext(c.ctx).
WithHeaders(c.requestHeaders). WithHeaders(c.requestHeaders).
Do(). Do()
UnmarshalSimpleJSON()
// We first check if the result is a JWT token
// https://openid.net/specs/openid-connect-core-1_0-final.html#UserInfoResponse
mediaType, _, parseErr := mime.ParseMediaType(builder.Headers().Get("Content-Type"))
if parseErr == nil && mediaType == "application/jwt" {
// Decode and use JWT payload as profile claims
if pl, err := parseJWT(string(builder.Body())); err == nil {
return simplejson.NewJson(pl)
}
}
// Otherwise, process as normal JSON payload
claims, err := builder.UnmarshalSimpleJSON()
if err != nil { if err != nil {
return nil, fmt.Errorf("error making request to profile URL: %v", err) return nil, fmt.Errorf("error making request to profile URL: %v", err)
} }

View File

@ -497,6 +497,54 @@ var _ = Describe("Claim Extractor Suite", func() {
expectedDst: stringPointer("{\"foo\":[\"bar\",\"baz\"]}"), expectedDst: stringPointer("{\"foo\":[\"bar\",\"baz\"]}"),
}), }),
) )
It("should extract claims from a JWT response", func() {
jwtResponsePayload := `{
"user": "jwtUser",
"email": "jwtEmail",
"groups": [
"jwtGroup1",
"jwtGroup2"
]
}`
jwtResponseHandler := func(rw http.ResponseWriter, req *http.Request) {
if !hasAuthorizedHeader(req.Header) {
rw.WriteHeader(403)
rw.Write([]byte("Unauthorized"))
return
}
rw.Header().Set("Content-Type", "application/jwt; charset=utf-8")
rw.Write([]byte(createJWTFromPayload(jwtResponsePayload)))
}
claimExtractor, serverClose, err := newTestClaimExtractor(testClaimExtractorOpts{
idTokenPayload: emptyJSON,
setProfileURL: true,
profileRequestHeaders: newAuthorizedHeader(),
profileRequestHandler: jwtResponseHandler,
})
Expect(err).ToNot(HaveOccurred())
if serverClose != nil {
defer serverClose()
}
value, exists, err := claimExtractor.GetClaim("user")
Expect(err).ToNot(HaveOccurred())
Expect(exists).To(BeTrue())
Expect(value).To(Equal("jwtUser"))
value, exists, err = claimExtractor.GetClaim("email")
Expect(err).ToNot(HaveOccurred())
Expect(exists).To(BeTrue())
Expect(value).To(Equal("jwtEmail"))
value, exists, err = claimExtractor.GetClaim("groups")
Expect(err).ToNot(HaveOccurred())
Expect(exists).To(BeTrue())
Expect(value).To(Equal([]interface{}{"jwtGroup1", "jwtGroup2"}))
})
}) })
// ****************************************** // ******************************************