Skip to content

Commit 351305a

Browse files
authored
fix: set JWT audience claim to external URL for RFC 8707 compliance (#133)
Closes #129
1 parent 287e3c0 commit 351305a

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

pkg/idp/idp.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ func (a *IDPRouter) handleAuthorizationReturn(c *gin.Context) {
158158
for _, scope := range ar.GetRequestedScopes() {
159159
ar.GrantScope(scope)
160160
}
161+
ar.GrantAudience(a.externalURL)
161162
jwtSession, err := NewJWTSessionWithKey(a.externalURL, "user", a.privKey)
162163
if err != nil {
163164
a.logger.With(utils.Err(err)...).Error("Failed to create JWT session", zap.Error(err))
@@ -284,6 +285,7 @@ func (a *IDPRouter) handleRegister(c *gin.Context) {
284285
GrantTypes: req.GrantTypes,
285286
ResponseTypes: req.ResponseTypes,
286287
Scopes: strings.Fields(req.Scope),
288+
Audience: []string{a.externalURL},
287289
Public: isPublic,
288290
}
289291
if err := a.repo.RegisterClient(ctx, client); err != nil {

pkg/idp/idp_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/rand"
66
"crypto/rsa"
77
"crypto/sha256"
8+
"encoding/base64"
89
"encoding/json"
910
"fmt"
1011
"net/http"
@@ -413,3 +414,55 @@ func TestAuthWithEmptyState(t *testing.T) {
413414
require.NoError(t, err)
414415
require.NotEmpty(t, tokenResult["access_token"])
415416
}
417+
418+
func TestAccessTokenAudienceClaim(t *testing.T) {
419+
server, _, _ := setupTestServer(t)
420+
regResp := registerTestClient(t, server.URL)
421+
422+
config := &oauth2.Config{
423+
ClientID: regResp.ClientID,
424+
ClientSecret: regResp.ClientSecret,
425+
RedirectURL: "http://localhost:8080/callback",
426+
Scopes: []string{},
427+
Endpoint: oauth2.Endpoint{
428+
AuthURL: server.URL + AuthorizationEndpoint,
429+
TokenURL: server.URL + TokenEndpoint,
430+
},
431+
}
432+
433+
callbackURL := testAuthFlowWithURL(t, server.URL, config.AuthCodeURL("test-state"))
434+
code := callbackURL.Query().Get("code")
435+
436+
tokenReq := url.Values{}
437+
tokenReq.Set("grant_type", "authorization_code")
438+
tokenReq.Set("code", code)
439+
tokenReq.Set("redirect_uri", "http://localhost:8080/callback")
440+
tokenReq.Set("client_id", regResp.ClientID)
441+
tokenReq.Set("client_secret", regResp.ClientSecret)
442+
443+
tokenResp, err := http.PostForm(server.URL+TokenEndpoint, tokenReq)
444+
require.NoError(t, err)
445+
defer tokenResp.Body.Close()
446+
require.Equal(t, http.StatusOK, tokenResp.StatusCode)
447+
448+
var tokenResult map[string]any
449+
err = json.NewDecoder(tokenResp.Body).Decode(&tokenResult)
450+
require.NoError(t, err)
451+
452+
accessToken := tokenResult["access_token"].(string)
453+
454+
// Decode JWT payload and verify aud claim contains the external URL
455+
parts := strings.Split(accessToken, ".")
456+
require.Len(t, parts, 3, "access token should be a JWT with 3 parts")
457+
458+
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
459+
require.NoError(t, err)
460+
461+
var claims map[string]any
462+
err = json.Unmarshal(payload, &claims)
463+
require.NoError(t, err)
464+
465+
aud, ok := claims["aud"].([]any)
466+
require.True(t, ok, "aud claim should be present as an array")
467+
require.Contains(t, aud, "http://localhost:8080", "aud should contain the external URL")
468+
}

0 commit comments

Comments
 (0)