mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 02:16:28 +02:00
84 lines
3.1 KiB
Go
84 lines
3.1 KiB
Go
package cryptutil // import "github.com/pomerium/pomerium/internal/cryptutil"
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
jose "gopkg.in/square/go-jose.v2"
|
|
"gopkg.in/square/go-jose.v2/jwt"
|
|
)
|
|
|
|
// JWTSigner implements JWT signing according to JSON Web Token (JWT) RFC7519
|
|
// https://tools.ietf.org/html/rfc7519
|
|
type JWTSigner interface {
|
|
SignJWT(string, string) (string, error)
|
|
}
|
|
|
|
// ES256Signer is struct containing the required fields to create a ES256 signed JSON Web Tokens
|
|
type ES256Signer struct {
|
|
// User (sub) is unique, stable identifier for the user.
|
|
// Use in place of the x-pomerium-authenticated-user-id header.
|
|
User string `json:"sub,omitempty"`
|
|
// Email (sub) is a **private** claim name identifier for the user email address.
|
|
// Use in place of the x-pomerium-authenticated-user-email header.
|
|
Email string `json:"email,omitempty"`
|
|
// Audience (aud) must be the destination of the upstream proxy locations.
|
|
// e.g. `helloworld.corp.example.com`
|
|
Audience jwt.Audience `json:"aud,omitempty"`
|
|
// Issuer (iss) is the URL of the proxy.
|
|
// e.g. `proxy.corp.example.com`
|
|
Issuer string `json:"iss,omitempty"`
|
|
// Expiry (exp) is the expiration time in seconds since the UNIX epoch.
|
|
// Allow 1 minute for skew. The maximum lifetime of a token is 10 minutes + 2 * skew.
|
|
Expiry jwt.NumericDate `json:"exp,omitempty"`
|
|
// IssuedAt (iat) is the time is measured in seconds since the UNIX epoch.
|
|
// Allow 1 minute for skew.
|
|
IssuedAt jwt.NumericDate `json:"iat,omitempty"`
|
|
// IssuedAt (nbf) is the time is measured in seconds since the UNIX epoch.
|
|
// Allow 1 minute for skew.
|
|
NotBefore jwt.NumericDate `json:"nbf,omitempty"`
|
|
|
|
signer jose.Signer
|
|
}
|
|
|
|
// NewES256Signer creates an Elliptic Curve, NIST P-256 (aka secp256r1 aka prime256v1) JWT signer.
|
|
//
|
|
// RSA is not supported due to performance considerations of needing to sign each request.
|
|
// Go's P-256 is constant-time and SHA-256 is faster on 64-bit machines and immune
|
|
// to length extension attacks.
|
|
// See also:
|
|
// - https://cloud.google.com/iot/docs/how-tos/credentials/keys
|
|
func NewES256Signer(privKey []byte, audience string) (*ES256Signer, error) {
|
|
key, err := DecodePrivateKey(privKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("internal/cryptutil parsing key failed %v", err)
|
|
}
|
|
signer, err := jose.NewSigner(
|
|
jose.SigningKey{
|
|
Algorithm: jose.ES256, // ECDSA using P-256 and SHA-256
|
|
Key: key,
|
|
},
|
|
(&jose.SignerOptions{}).WithType("JWT"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("internal/cryptutil new signer failed %v", err)
|
|
}
|
|
return &ES256Signer{
|
|
Issuer: "pomerium-proxy",
|
|
Audience: jwt.Audience{audience},
|
|
signer: signer,
|
|
}, nil
|
|
}
|
|
|
|
// SignJWT creates a signed JWT containing claims for the logged in user id (`sub`) and email (`email`).
|
|
func (s *ES256Signer) SignJWT(user, email string) (string, error) {
|
|
s.User = user
|
|
s.Email = email
|
|
now := time.Now()
|
|
s.IssuedAt = jwt.NewNumericDate(now)
|
|
s.Expiry = jwt.NewNumericDate(now.Add(jwt.DefaultLeeway))
|
|
s.NotBefore = jwt.NewNumericDate(now.Add(-1 * jwt.DefaultLeeway))
|
|
rawJWT, err := jwt.Signed(s.signer).Claims(s).CompactSerialize()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return rawJWT, nil
|
|
}
|