github.com/argoproj/argo-cd/v3@v3.2.1/util/security/jwt.go (about) 1 package security 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "fmt" 7 "strings" 8 ) 9 10 // parseJWT parses a jwt and returns it as json bytes. 11 // 12 // This function DOES NOT VERIFY THE TOKEN. You still have to verify the token to confirm that the token holder has not 13 // altered the claims. 14 // 15 // This code is copied almost verbatim from go-oidc (https://github.com/coreos/go-oidc). 16 func parseJWT(p string) ([]byte, error) { 17 parts := strings.Split(p, ".") 18 if len(parts) < 2 { 19 return nil, fmt.Errorf("malformed jwt, expected 3 parts got %d", len(parts)) 20 } 21 payload, err := base64.RawURLEncoding.DecodeString(parts[1]) 22 if err != nil { 23 return nil, fmt.Errorf("malformed jwt payload: %w", err) 24 } 25 return payload, nil 26 } 27 28 type audience []string 29 30 // UnmarshalJSON allows us to unmarshal either a single audience or a list of audiences. 31 // Taken from: https://github.com/coreos/go-oidc/blob/a8ceb9a2043fca2e43518633920db746808b1138/oidc/oidc.go#L475 32 func (a *audience) UnmarshalJSON(b []byte) error { 33 var s string 34 if json.Unmarshal(b, &s) == nil { 35 *a = audience{s} 36 return nil 37 } 38 var auds []string 39 if err := json.Unmarshal(b, &auds); err != nil { 40 return err 41 } 42 *a = auds 43 return nil 44 } 45 46 // jwtWithOnlyAudClaim represents a jwt where only the "aud" claim is present. This struct allows us to unmarshal a jwt 47 // and be confident that the only information retrieved from that jwt is the "aud" claim. 48 type jwtWithOnlyAudClaim struct { 49 Aud audience `json:"aud"` 50 } 51 52 // getUnverifiedAudClaim gets the "aud" claim from a jwt. 53 // 54 // This function DOES NOT VERIFY THE TOKEN. You still have to verify the token to confirm that the token holder has not 55 // altered the "aud" claim. 56 // 57 // This code is copied almost verbatim from go-oidc (https://github.com/coreos/go-oidc). 58 func getUnverifiedAudClaim(rawIDToken string) ([]string, error) { 59 payload, err := parseJWT(rawIDToken) 60 if err != nil { 61 return nil, fmt.Errorf("malformed jwt: %w", err) 62 } 63 var token jwtWithOnlyAudClaim 64 if err = json.Unmarshal(payload, &token); err != nil { 65 return nil, fmt.Errorf("failed to unmarshal claims: %w", err) 66 } 67 return token.Aud, nil 68 } 69 70 // UnverifiedHasAudClaim returns whether the "aud" claim is present in the given JWT. 71 // 72 // This function DOES NOT VERIFY THE TOKEN. You still have to verify the token to confirm that the token holder has not 73 // altered the "aud" claim. 74 func UnverifiedHasAudClaim(rawIDToken string) (bool, error) { 75 aud, err := getUnverifiedAudClaim(rawIDToken) 76 if err != nil { 77 return false, fmt.Errorf("failed to determine whether token had an audience claim: %w", err) 78 } 79 return aud != nil, nil 80 }