jwt_token.go raw

   1  package credentials
   2  
   3  import (
   4  	"crypto/rsa"
   5  	"errors"
   6  	"fmt"
   7  	"time"
   8  
   9  	"github.com/golang-jwt/jwt/v4"
  10  
  11  	"github.com/yandex-cloud/go-sdk/v2/pkg/iamkey"
  12  )
  13  
  14  // newServiceAccountJWTBuilder creates a JWT builder for a given IAM service account key.
  15  // Returns an error if the key validation or parsing of the private key fails.
  16  func newServiceAccountJWTBuilder(key *iamkey.Key) (*serviceAccountJWTBuilder, error) {
  17  	err := validateServiceAccountKey(key)
  18  	if err != nil {
  19  		return nil, fmt.Errorf("key validation failed: %w", err)
  20  	}
  21  
  22  	rsaPrivateKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(key.PrivateKey))
  23  	if err != nil {
  24  		return nil, fmt.Errorf("private key parsing failed: %w", err)
  25  	}
  26  
  27  	return &serviceAccountJWTBuilder{
  28  		key:           key,
  29  		rsaPrivateKey: rsaPrivateKey,
  30  	}, nil
  31  }
  32  
  33  // validateServiceAccountKey validates an IAM key to ensure it has an ID and is issued for a service account.
  34  func validateServiceAccountKey(key *iamkey.Key) error {
  35  	if key.Id == "" {
  36  		return errors.New("key id is missing")
  37  	}
  38  
  39  	if key.GetServiceAccountId() == "" {
  40  		return fmt.Errorf("key should be issued for service account, but subject is %#v", key.Subject)
  41  	}
  42  
  43  	return nil
  44  }
  45  
  46  // serviceAccountJWTBuilder is used to generate signed JWT tokens for service account authorization.
  47  // It relies on a service account key and an associated RSA private key.
  48  type serviceAccountJWTBuilder struct {
  49  	key           *iamkey.Key
  50  	rsaPrivateKey *rsa.PrivateKey
  51  }
  52  
  53  // SignedToken generates a signed JWT token using the service account's private RSA key and returns it as a string.
  54  func (b *serviceAccountJWTBuilder) SignedToken() (string, error) {
  55  	return b.issueToken().SignedString(b.rsaPrivateKey)
  56  }
  57  
  58  // issueToken creates a new JWT token with claims using the service account's ID and sets the token's header information.
  59  func (b *serviceAccountJWTBuilder) issueToken() *jwt.Token {
  60  	issuedAt := time.Now()
  61  	token := jwt.NewWithClaims(jwt.SigningMethodPS256, jwt.RegisteredClaims{
  62  		Issuer:    b.key.GetServiceAccountId(),
  63  		IssuedAt:  jwt.NewNumericDate(issuedAt),
  64  		ExpiresAt: jwt.NewNumericDate(issuedAt.Add(time.Hour)),
  65  		Audience:  jwt.ClaimStrings{"https://iam.api.cloud.yandex.net/iam/v1/tokens"},
  66  	})
  67  	token.Header["kid"] = b.key.Id
  68  
  69  	return token
  70  }
  71