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  }