From 8806e30591bda6d799c8bea712c312eb10f8197d Mon Sep 17 00:00:00 2001 From: MrMelon54 Date: Sun, 18 Jun 2023 18:09:49 +0100 Subject: [PATCH] Add comments to all of this --- mjwt.go | 49 +++++++++++++++++++++++++------------------------ signer.go | 8 ++++++++ verifier.go | 24 +++++++++++++----------- 3 files changed, 46 insertions(+), 35 deletions(-) diff --git a/mjwt.go b/mjwt.go index 6871b8a..b147ba8 100644 --- a/mjwt.go +++ b/mjwt.go @@ -1,7 +1,6 @@ package mjwt import ( - "bytes" "encoding/json" "github.com/golang-jwt/jwt/v4" "github.com/pkg/errors" @@ -48,6 +47,8 @@ type baseTypeClaim interface { InternalClaimType() string } +// BaseTypeClaims is a wrapper for combining the jwt.RegisteredClaims with a ClaimType +// and generic Claims data type BaseTypeClaims[T Claims] struct { jwt.RegisteredClaims ClaimType string @@ -59,6 +60,7 @@ func (b *BaseTypeClaims[T]) init() *BaseTypeClaims[T] { return b } +// Valid checks the InternalClaimType matches and the type claim type func (b *BaseTypeClaims[T]) Valid() error { if b.ClaimType != b.InternalClaimType() { return ErrClaimTypeMismatch @@ -66,61 +68,60 @@ func (b *BaseTypeClaims[T]) Valid() error { return b.Claims.Valid() } -func (b *BaseTypeClaims[T]) InternalClaimType() string { - return b.Claims.Type() -} +// InternalClaimType returns the Type of the generic claim struct +func (b *BaseTypeClaims[T]) InternalClaimType() string { return b.Claims.Type() } +// MarshalJSON converts the internalBaseTypeClaims and generic claim struct into +// a serialized JSON byte array func (b *BaseTypeClaims[T]) MarshalJSON() ([]byte, error) { - // setup buffers - buf := new(bytes.Buffer) - buf2 := new(bytes.Buffer) - - // encode into both buffers - err := json.NewEncoder(buf).Encode(internalBaseTypeClaims{ + // encode the internalBaseTypeClaims + b1, err := json.Marshal(internalBaseTypeClaims{ RegisteredClaims: b.RegisteredClaims, ClaimType: b.InternalClaimType(), }) if err != nil { return nil, err } - err = json.NewEncoder(buf2).Encode(b.Claims) + + // encode the generic claims struct + b2, err := json.Marshal(b.Claims) if err != nil { return nil, err } - // decode into a single map - var a map[string]any - err = json.NewDecoder(buf).Decode(&a) - if err != nil { - return nil, err - } - err = json.NewDecoder(buf2).Decode(&a) - if err != nil { - return nil, err - } - - // encode to output - return json.Marshal(a) + // replace starting '{' with ',' + b2[0] = ',' + // join the two json strings and remove the last char '}' from the first string + return append(b1[:len(b1)-1], b2...), nil } +// UnmarshalJSON reads the internalBaseTypeClaims and generic claim struct from +// a serialized JSON byte array func (b *BaseTypeClaims[T]) UnmarshalJSON(bytes []byte) error { a := internalBaseTypeClaims{} var t T + + // convert JSON to internalBaseTypeClaims err := json.Unmarshal(bytes, &a) if err != nil { return err } + + // convert JSON to the generic claim struct err = json.Unmarshal(bytes, &t) if err != nil { return err } + // assign the fields in BaseTypeClaims b.RegisteredClaims = a.RegisteredClaims b.ClaimType = a.ClaimType b.Claims = t return err } +// internalBaseTypeClaims is a wrapper for jwt.RegisteredClaims which adds a +// ClaimType field containing the type of the generic claim struct type internalBaseTypeClaims struct { jwt.RegisteredClaims ClaimType string `json:"mct"` diff --git a/signer.go b/signer.go index 22d966f..bb50311 100644 --- a/signer.go +++ b/signer.go @@ -6,6 +6,8 @@ import ( "time" ) +// defaultMJwtSigner implements Signer and uses an rsa.PrivateKey and issuer name +// to generate MJWT tokens type defaultMJwtSigner struct { issuer string key *rsa.PrivateKey @@ -14,6 +16,7 @@ type defaultMJwtSigner struct { var _ Signer = &defaultMJwtSigner{} +// NewMJwtSigner creates a new defaultMJwtSigner using the issuer name and rsa.PrivateKey func NewMJwtSigner(issuer string, key *rsa.PrivateKey) Signer { return &defaultMJwtSigner{ issuer: issuer, @@ -22,17 +25,22 @@ func NewMJwtSigner(issuer string, key *rsa.PrivateKey) Signer { } } +// Issuer returns the name of the issuer func (d *defaultMJwtSigner) Issuer() string { return d.issuer } +// GenerateJwt generates and returns a JWT string using the sub, id, duration and claims func (d *defaultMJwtSigner) GenerateJwt(sub, id string, dur time.Duration, claims Claims) (string, error) { return d.SignJwt(wrapClaims[Claims](d, sub, id, dur, claims)) } +// SignJwt signs a jwt.Claims compatible struct, this is used internally by +// GenerateJwt but is available for signing custom structs func (d *defaultMJwtSigner) SignJwt(wrapped jwt.Claims) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodRS512, wrapped) return token.SignedString(d.key) } +// VerifyJwt validates and parses MJWT tokens see defaultMJwtVerifier.VerifyJwt() func (d *defaultMJwtSigner) VerifyJwt(token string, claims baseTypeClaim) (*jwt.Token, error) { return d.verify.VerifyJwt(token, claims) } diff --git a/verifier.go b/verifier.go index da7edf8..ef54dc4 100644 --- a/verifier.go +++ b/verifier.go @@ -7,17 +7,19 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/pkg/errors" "os" - "time" ) var ErrCannotGenerateMJwtToken = errors.New("cannot generate mjwt token with verifier") +// defaultMJwtVerifier implements Verifier and uses an rsa.PublicKey to validate +// MJWT tokens type defaultMJwtVerifier struct { pub *rsa.PublicKey } var _ Verifier = &defaultMJwtVerifier{} +// NewMJwtVerifier creates a new defaultMJwtVerifier using the rsa.PublicKey func NewMJwtVerifier(key *rsa.PublicKey) Verifier { return newMJwtVerifier(key) } @@ -26,29 +28,29 @@ func newMJwtVerifier(key *rsa.PublicKey) *defaultMJwtVerifier { return &defaultMJwtVerifier{pub: key} } +// NewMJwtVerifierFromFile creates a new defaultMJwtVerifier using the path of a +// rsa.PublicKey file func NewMJwtVerifierFromFile(file string) (Verifier, error) { + // read file f, err := os.ReadFile(file) if err != nil { return nil, err } + + // decode pem block block, _ := pem.Decode(f) + + // parse public key from pem block pub, err := x509.ParsePKCS1PublicKey(block.Bytes) if err != nil { return nil, err } + + // create verifier using rsa.PublicKey return NewMJwtVerifier(pub), nil } -func (d *defaultMJwtVerifier) Issuer() string { return "" } - -func (d *defaultMJwtVerifier) GenerateJwt(_, _ string, _ time.Duration, _ Claims) (string, error) { - return "", ErrCannotGenerateMJwtToken -} - -func (d *defaultMJwtVerifier) SignJwt(_ jwt.Claims) (string, error) { - return "", ErrCannotGenerateMJwtToken -} - +// VerifyJwt validates and parses MJWT tokens and returns the claims func (d *defaultMJwtVerifier) VerifyJwt(token string, claims baseTypeClaim) (*jwt.Token, error) { withClaims, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) { return d.pub, nil