@@ -2,8 +2,6 @@ package auth
22
33import (
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
1716type 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
8786func 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
175156func 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 }
0 commit comments