Skip to content

Commit d1dbc8f

Browse files
committed
Use lestrrat-go/jwx to obtain the info from token claims
1 parent ab51c25 commit d1dbc8f

3 files changed

Lines changed: 56 additions & 45 deletions

File tree

proxy/auth/k8s.go

Lines changed: 27 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package auth
22

33
import (
44
"crypto/tls"
5-
"encoding/base64"
6-
"encoding/json"
75
"fmt"
86
"net/http"
97
"strings"
@@ -12,6 +10,7 @@ import (
1210
"github.com/flightctl/flightctl-ui/bridge"
1311
"github.com/flightctl/flightctl-ui/config"
1412
"github.com/flightctl/flightctl-ui/log"
13+
"github.com/lestrrat-go/jwx/v2/jwt"
1514
)
1615

1716
type TokenAuthProvider struct {
@@ -85,14 +84,13 @@ func (t *TokenAuthProvider) ValidateToken(token string) (TokenData, *int64, erro
8584
// extractTokenExpiration extracts the expiration time from a JWT token
8685
// Returns the number of seconds until expiration, or nil if no expiration is set
8786
func extractTokenExpiration(token string) *int64 {
88-
claims, err := extractJwtTokenClaims(token)
87+
parsedToken, err := jwt.ParseInsecure([]byte(token))
8988
if err != nil {
9089
return nil
9190
}
9291

93-
// Look for the "exp" claim (standard JWT expiration claim)
94-
exp, ok := claims["exp"]
95-
if !ok {
92+
exp, exists := parsedToken.Get("exp")
93+
if !exists {
9694
return nil
9795
}
9896

@@ -119,66 +117,50 @@ func extractTokenExpiration(token string) *int64 {
119117
return &expiresIn
120118
}
121119

122-
// extractJwtTokenClaims extracts the claims from a JWT token
123-
func extractJwtTokenClaims(token string) (map[string]interface{}, error) {
124-
// K8s tokens have the format: <header>.<payload>.<signature>
125-
parts := strings.Split(token, ".")
126-
if len(parts) != 3 {
127-
return nil, fmt.Errorf("invalid token format")
128-
}
129-
130-
// Decode the payload (second part)
131-
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
132-
if err != nil {
133-
return nil, fmt.Errorf("failed to decode token payload: %w", err)
134-
}
135-
136-
// Parse the JSON payload
137-
var claims map[string]interface{}
138-
if err := json.Unmarshal(payload, &claims); err != nil {
139-
return nil, fmt.Errorf("failed to unmarshal token claims: %w", err)
140-
}
141-
142-
return claims, nil
143-
}
144-
145120
// extractUsernameFromClaims extracts the username from JWT claims
146-
func extractUsernameFromClaims(claims map[string]interface{}) (string, bool) {
121+
func extractUsernameFromClaims(parsedToken jwt.Token) (string, bool) {
147122
// Prefer the short service account name over the full sub claim
148-
if k8sInfo, ok := claims["kubernetes.io"].(map[string]interface{}); ok {
149-
if sa, ok := k8sInfo["serviceaccount"].(map[string]interface{}); ok {
150-
if saName, ok := sa["name"].(string); ok && saName != "" {
151-
return saName, true
123+
if k8sInfo, exists := parsedToken.Get("kubernetes.io"); exists {
124+
if k8sInfoMap, ok := k8sInfo.(map[string]interface{}); ok {
125+
if sa, ok := k8sInfoMap["serviceaccount"].(map[string]interface{}); ok {
126+
if saName, ok := sa["name"].(string); ok && saName != "" {
127+
return saName, true
128+
}
152129
}
153130
}
154131
}
155132

156133
// Try to extract the service account name from the old flat claim structure
157-
if saName, ok := claims["kubernetes.io/serviceaccount/service-account.name"].(string); ok && saName != "" {
158-
return saName, true
134+
if saName, exists := parsedToken.Get("kubernetes.io/serviceaccount/service-account.name"); exists {
135+
if saNameStr, ok := saName.(string); ok && saNameStr != "" {
136+
return saNameStr, true
137+
}
159138
}
160139

161140
// Fallback to sub claim (which contains the full system:serviceaccount:namespace:name format)
162-
if username, ok := claims["sub"].(string); ok && username != "" {
163-
// Try to extract just the service account name from sub if it's in the right format
164-
parts := strings.Split(username, ":")
165-
if len(parts) == 4 && parts[0] == "system" && parts[1] == "serviceaccount" {
166-
return parts[3], true // Return just the service account name
141+
if username, exists := parsedToken.Get("sub"); exists {
142+
if usernameStr, ok := username.(string); ok && usernameStr != "" {
143+
// Try to extract just the service account name from sub if it's in the right format
144+
parts := strings.Split(usernameStr, ":")
145+
if len(parts) == 4 && parts[0] == "system" && parts[1] == "serviceaccount" {
146+
return parts[3], true // Return just the service account name
147+
}
148+
return usernameStr, true
167149
}
168-
return username, true
169150
}
170151

171152
return "", false
172153
}
173154

174155
// ExtractUsernameFromToken extracts the username from a K8s JWT token
175156
func ExtractUsernameFromToken(token string) (string, error) {
176-
claims, err := extractJwtTokenClaims(token)
157+
// Parse the token without signature validation (we only need to extract claims)
158+
parsedToken, err := jwt.ParseInsecure([]byte(token))
177159
if err != nil {
178-
return "", err
160+
return "", fmt.Errorf("failed to parse JWT token: %w", err)
179161
}
180162

181-
username, ok := extractUsernameFromClaims(claims)
163+
username, ok := extractUsernameFromClaims(parsedToken)
182164
if !ok {
183165
return "", fmt.Errorf("could not extract username from token claims")
184166
}

proxy/go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,32 @@ require (
99
github.com/gorilla/handlers v1.5.2
1010
github.com/gorilla/mux v1.8.1
1111
github.com/gorilla/websocket v1.5.3
12+
github.com/lestrrat-go/jwx/v2 v2.1.0
1213
github.com/openshift/osincli v0.0.0-20160924135400-fababb0555f2
1314
github.com/sirupsen/logrus v1.9.3
1415
)
1516

1617
require (
1718
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
19+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
1820
github.com/felixge/httpsnoop v1.0.4 // indirect
1921
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
2022
github.com/getkin/kin-openapi v0.131.0 // indirect
2123
github.com/go-chi/chi/v5 v5.2.2 // indirect
2224
github.com/go-logr/logr v1.4.3 // indirect
2325
github.com/go-openapi/jsonpointer v0.21.0 // indirect
2426
github.com/go-openapi/swag v0.23.0 // indirect
27+
github.com/goccy/go-json v0.10.5 // indirect
2528
github.com/gogo/protobuf v1.3.2 // indirect
2629
github.com/google/gofuzz v1.2.0 // indirect
2730
github.com/google/uuid v1.6.0 // indirect
2831
github.com/josharian/intern v1.0.0 // indirect
2932
github.com/json-iterator/go v1.1.12 // indirect
33+
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
34+
github.com/lestrrat-go/httpcc v1.0.1 // indirect
35+
github.com/lestrrat-go/httprc v1.0.5 // indirect
36+
github.com/lestrrat-go/iter v1.0.2 // indirect
37+
github.com/lestrrat-go/option v1.0.1 // indirect
3038
github.com/mailru/easyjson v0.7.7 // indirect
3139
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
3240
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -38,6 +46,7 @@ require (
3846
github.com/robfig/cron/v3 v3.0.1 // indirect
3947
github.com/samber/lo v1.49.1 // indirect
4048
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
49+
github.com/segmentio/asm v1.2.0 // indirect
4150
github.com/x448/float16 v0.8.4 // indirect
4251
go.yaml.in/yaml/v2 v2.4.2 // indirect
4352
golang.org/x/crypto v0.39.0 // indirect

proxy/go.sum

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
66
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
77
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
88
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
10+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
911
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
1012
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
1113
github.com/flightctl/flightctl v0.10.0-rc1 h1:S/wrWnjjoa4WAQ7Lukl1FFA6NHTUG90fI9BU7wlMgGc=
@@ -26,6 +28,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
2628
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
2729
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
2830
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
31+
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
32+
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
2933
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
3034
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
3135
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -55,6 +59,18 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
5559
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
5660
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
5761
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
62+
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
63+
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
64+
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
65+
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
66+
github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk=
67+
github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
68+
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
69+
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
70+
github.com/lestrrat-go/jwx/v2 v2.1.0 h1:0zs7Ya6+39qoit7gwAf+cYm1zzgS3fceIdo7RmQ5lkw=
71+
github.com/lestrrat-go/jwx/v2 v2.1.0/go.mod h1:Xpw9QIaUGiIUD1Wx0NcY1sIHwFf8lDuZn/cmxtXYRys=
72+
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
73+
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
5874
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
5975
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
6076
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -89,14 +105,18 @@ github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
89105
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
90106
github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA=
91107
github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU=
108+
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
109+
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
92110
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
93111
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
94112
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
95113
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
96114
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
97115
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
98116
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
117+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
99118
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
119+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
100120
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
101121
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
102122
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=

0 commit comments

Comments
 (0)