Initial cloud-services repo - gateway service + pkg modules

This commit is contained in:
Chris Rai
2026-01-30 23:14:52 -05:00
commit fbb820d7b3
1037 changed files with 171318 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
package tmobtokengen
import (
"encoding/base64"
"encoding/json"
"encoding/pem"
"strings"
"time"
"github.com/go-jose/go-jose/v4"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/youmark/pkcs8"
)
func buildClientSecret(clientID, secret string) string {
return base64.StdEncoding.EncodeToString([]byte(clientID + ":" + secret))
}
var (
ErrFailedToDecodePem = errors.New("failed to decode privateKey")
ErrNilSigner = errors.New("signer cannot be nil")
)
func ParsePrivateKey(pemFileBytes []byte, pwd []byte) (any, error) {
block, _ := pem.Decode(pemFileBytes)
if block == nil {
return nil, ErrFailedToDecodePem
}
key, _, err := pkcs8.ParsePrivateKey(block.Bytes, pwd)
if err != nil {
return nil, errors.Wrap(err, "failed to parse pkcs8 private key")
}
return key, nil
}
type jwtClaims struct {
EDTS string `json:"edts,omitempty"`
V string `json:"v,omitempty"`
Exp int64 `json:"exp,omitempty"`
EHTS string `json:"ehts,omitempty"`
IAT int64 `json:"iat,omitempty"`
UniqueStr string `json:"jti,omitempty"`
}
func buildClaims(
ehts map[EhtsKey]string,
curTime time.Time,
expDuration time.Duration,
uuidFunc func() string,
) ([]byte, error) {
curTime = curTime.UTC()
ehtsKeys, ehtsValues := ehtsToString(ehts)
encoded := b64EncodeEHTS([]byte(ehtsValues))
expTime := curTime.Add(expDuration)
uniqStr := uuidFunc()
objClaims := jwtClaims{
EDTS: string(encoded),
V: "1",
Exp: expTime.Unix(),
EHTS: ehtsKeys,
IAT: curTime.Unix(),
UniqueStr: uniqStr,
}
c, err := json.Marshal(objClaims)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal claims during build")
}
return c, nil
}
func sign(signer jose.Signer, claims []byte) (string, error) {
if signer == nil {
return "", ErrNilSigner
}
signature, err := signer.Sign(claims)
if err != nil {
return "", errors.Wrap(err, "failed to sign claims during pop token build")
}
signed, err := signature.CompactSerialize()
if err != nil {
return "", errors.Wrap(err, "failed to sign claims during pop token build")
}
return signed, nil
}
type Generator interface {
Generate(ehts EHTSMap) (string, error)
ClientSecretAsAuthVal() string
}
type PopTokenGenerator struct {
signer jose.Signer
clientSecret string // client secret composed from client id and secret via base64.StdEncoding
expDuration time.Duration // token expiration duration
genUuid func() string // uniq string is set as generator field for testing purposes
}
func GenerateUUID() string {
return uuid.New().String()
}
// NewTokenGenerator creates a new PopTokenGenerator.
func NewTokenGenerator(
clientID string,
secret string,
expDuration time.Duration,
pkValue string,
pkPwd ...[]byte,
) (*PopTokenGenerator, error) {
// Removing the read from file. We read our key from the environment
pkValue = strings.ReplaceAll(pkValue, "\\n", "\n")
keyBytes := pkValue
var pwd []byte
if len(pkPwd) != 0 {
pwd = pkPwd[0]
}
pk, err := ParsePrivateKey([]byte(keyBytes), pwd)
if err != nil {
return nil, errors.Wrap(err, "failed to parse private key file")
}
opts := new(jose.SignerOptions)
opts.WithType("JWT")
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: pk}, opts)
if err != nil {
return nil, errors.Wrap(err, "failed to create signer")
}
return &PopTokenGenerator{
signer: signer,
clientSecret: buildClientSecret(clientID, secret),
expDuration: expDuration,
genUuid: GenerateUUID,
}, nil
}
func (g *PopTokenGenerator) ClientSecretAsAuthVal() string {
return "Basic " + g.clientSecret
}
// Generate generates a pop token.
// It expects EHTSMap to be populated with the following keys, order is changed intrinsically, so don't worry:
// - ContentType
// - Authorization
// - URI
// - HTTPMethod
// - Body
func (g *PopTokenGenerator) Generate(ehts EHTSMap) (string, error) {
return g.generate(ehts, time.Now())
}
// generate generates a pop token.
// It takes the current time as parameter for testing.
func (g *PopTokenGenerator) generate(ehts EHTSMap, curTime time.Time) (string, error) {
c, err := buildClaims(ehts, curTime, g.expDuration, g.genUuid)
if err != nil {
return "", errors.Wrap(err, "failed to build claims")
}
signed, err := sign(g.signer, c)
if err != nil {
return "", errors.Wrap(err, "failed to sign claims")
}
return signed, nil
}