package clientassertion
import "github.com/hashicorp/cap/oidc/clientassertion"
Package clientassertion signs JWTs with a Private Key or Client Secret for use in OIDC client_assertion requests, A.K.A. private_key_jwt. reference: https://oauth.net/private-key-jwt/
Index ¶
- Constants
- Variables
- type HSAlgorithm
- type JWT
- func NewJWTWithHMAC(clientID string, audience []string, alg HSAlgorithm, secret string, opts ...Option, ) (*JWT, error)
- func NewJWTWithRSAKey(clientID string, audience []string, alg RSAlgorithm, key *rsa.PrivateKey, opts ...Option, ) (*JWT, error)
- func (j *JWT) Serialize() (string, error)
- type Option
- type RSAlgorithm
Examples ¶
Constants ¶
const ( HS256 HSAlgorithm = "HS256" // HMAC using SHA-256 HS384 HSAlgorithm = "HS384" // HMAC using SHA-384 HS512 HSAlgorithm = "HS512" // HMAC using SHA-512 RS256 RSAlgorithm = "RS256" // RSASSA-PKCS-v1.5 using SHA-256 RS384 RSAlgorithm = "RS384" // RSASSA-PKCS-v1.5 using SHA-384 RS512 RSAlgorithm = "RS512" // RSASSA-PKCS-v1.5 using SHA-512 )
JOSE asymmetric signing algorithm values as defined by RFC 7518. See: https://tools.ietf.org/html/rfc7518#section-3.1
const ( // JWTTypeParam is the proper value for client_assertion_type. // https://www.rfc-editor.org/rfc/rfc7523.html#section-2.2 JWTTypeParam = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" )
const KeyIDHeader = "kid"
KeyIDHeader is the "kid" header on a JWT, which providers use to look up the right public key to verify the JWT.
Variables ¶
var ( ErrMissingClientID = errors.New("missing client ID") ErrMissingAudience = errors.New("missing audience") ErrMissingAlgorithm = errors.New("missing signing algorithm") ErrMissingKeyID = errors.New("missing key ID") ErrMissingKey = errors.New("missing private key") ErrMissingSecret = errors.New("missing client secret") ErrKidHeader = errors.New(`"kid" not allowed in WithHeaders; use WithKeyID instead`) ErrCreatingSigner = errors.New("error creating jwt signer") ErrUnsupportedAlgorithm = errors.New("unsupported algorithm") ErrInvalidSecretLength = errors.New("invalid secret length for algorithm") ErrNilPrivateKey = errors.New("nil private key") )
Types ¶
type HSAlgorithm ¶
type HSAlgorithm string
HSAlgorithm is an HMAC signature algorithm
func (HSAlgorithm) Validate ¶
func (a HSAlgorithm) Validate(secret string) error
Validate checks that the secret is a supported algorithm and that it's the proper length for the HSAlgorithm:
- HS256: >= 32 bytes
- HS384: >= 48 bytes
- HS512: >= 64 bytes
type JWT ¶
type JWT struct {
// contains filtered or unexported fields
}
JWT is used to create a client assertion JWT, a special JWT used by an OAuth
2.0 or OIDC client to authenticate themselves to an authorization server
Code:
Output:Example¶
{
cid := "client-id"
aud := []string{"audience"}
// With an HMAC client secret
secret := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // 32 bytes for HS256
j, err := NewJWTWithHMAC(cid, aud, HS256, secret)
if err != nil {
log.Fatal(err)
}
signed, err := j.Serialize()
if err != nil {
log.Fatal(err)
}
{
// decode and inspect the JWT -- this is the IDP's job,
// but it illustrates the example.
token, err := jwt.ParseSigned(signed, []jose.SignatureAlgorithm{"HS256"})
if err != nil {
log.Fatal(err)
}
headers := token.Headers[0]
fmt.Printf("ClientSecret\n Headers - Algorithm: %s; typ: %s\n",
headers.Algorithm, headers.ExtraHeaders["typ"])
var claim jwt.Claims
err = token.Claims([]byte(secret), &claim)
if err != nil {
log.Fatal(err)
}
fmt.Printf(" Claims - Issuer: %s; Subject: %s; Audience: %v\n",
claim.Issuer, claim.Subject, claim.Audience)
}
// With an RSA key
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Fatal(err)
}
pubKey, ok := privKey.Public().(*rsa.PublicKey)
if !ok {
log.Fatal("couldn't get rsa.PublicKey from PrivateKey")
}
j, err = NewJWTWithRSAKey(cid, aud, RS256, privKey,
// note: for some providers, they key ID may be an x5t derivation
// of a cert generated from the private key.
// if your key has an associated JWKS endpoint, it will be the "kid"
// for the public key at /.well-known/jwks.json
WithKeyID("some-key-id"),
// extra headers, like x5t, are optional
WithHeaders(map[string]string{
"x5t": "should-be-derived-from-a-cert",
}),
)
if err != nil {
log.Fatal(err)
}
signed, err = j.Serialize()
if err != nil {
log.Fatal(err)
}
{ // decode and inspect the JWT -- this is the IDP's job
token, err := jwt.ParseSigned(signed, []jose.SignatureAlgorithm{"RS256"})
if err != nil {
log.Fatal(err)
}
h := token.Headers[0]
fmt.Printf("PrivateKey\n Headers - KeyID: %s; Algorithm: %s; typ: %s; x5t: %s\n",
h.KeyID, h.Algorithm, h.ExtraHeaders["typ"], h.ExtraHeaders["x5t"])
var claim jwt.Claims
err = token.Claims(pubKey, &claim)
if err != nil {
log.Fatal(err)
}
fmt.Printf(" Claims - Issuer: %s; Subject: %s; Audience: %v\n",
claim.Issuer, claim.Subject, claim.Audience)
}
// Output:
// ClientSecret
// Headers - Algorithm: HS256; typ: JWT
// Claims - Issuer: client-id; Subject: client-id; Audience: [audience]
// PrivateKey
// Headers - KeyID: some-key-id; Algorithm: RS256; typ: JWT; x5t: should-be-derived-from-a-cert
// Claims - Issuer: client-id; Subject: client-id; Audience: [audience]
}
ClientSecret
Headers - Algorithm: HS256; typ: JWT
Claims - Issuer: client-id; Subject: client-id; Audience: [audience]
PrivateKey
Headers - KeyID: some-key-id; Algorithm: RS256; typ: JWT; x5t: should-be-derived-from-a-cert
Claims - Issuer: client-id; Subject: client-id; Audience: [audience]
func NewJWTWithHMAC ¶
func NewJWTWithHMAC(clientID string, audience []string, alg HSAlgorithm, secret string, opts ...Option, ) (*JWT, error)
NewJWTWithHMAC creates a new JWT which will be signed with an HMAC secret.
alg must be one of: * HS256 with a >= 32 byte secret * HS384 with a >= 48 byte secret * HS512 with a >= 64 byte secret
Supported Options: * WithKeyID * WithHeaders
func NewJWTWithRSAKey ¶
func NewJWTWithRSAKey(clientID string, audience []string, alg RSAlgorithm, key *rsa.PrivateKey, opts ...Option, ) (*JWT, error)
NewJWTWithRSAKey creates a new JWT which will be signed with a private key.
alg must be one of: * RS256 * RS384 * RS512
Supported Options: * WithKeyID * WithHeaders
func (*JWT) Serialize ¶
Serialize returns client assertion JWT which can be used by an OAuth 2.0 or OIDC client to authenticate themselves to an authorization server
type Option ¶
Option configures the JWT
func WithHeaders ¶
WithHeaders sets extra JWT headers. Do not set a "kid" header here; instead use WithKeyID.
func WithKeyID ¶
WithKeyID sets the "kid" header that OIDC providers use to look up the public key to check the signed JWT
type RSAlgorithm ¶
type RSAlgorithm string
RSAlgorithm is an RSA signature algorithm
func (RSAlgorithm) Validate ¶
func (a RSAlgorithm) Validate(key *rsa.PrivateKey) error
Validate checks that the key is a supported algorithm and is valid per rsa.PrivateKey's Validate() method.
Source Files ¶
algorithms.go client_assertion.go error.go options.go
- Version
- v0.9.0 (latest)
- Published
- Feb 28, 2025
- Platform
- linux/amd64
- Imports
- 7 packages
- Last checked
- 2 months ago –
Tools for package owners.