Skip to content

Commit

Permalink
add MustGenerateRandomString helper
Browse files Browse the repository at this point in the history
  • Loading branch information
kataras committed Nov 9, 2023
1 parent 454d951 commit f89638b
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 49 deletions.
14 changes: 7 additions & 7 deletions claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,16 @@ func MaxAgeMap(maxAge time.Duration, claims Map) {
//
// Usage:
//
// claims := Merge(map[string]interface{}{"foo":"bar"}, Claims{
// MaxAge: 15 * time.Minute,
// Issuer: "an-issuer",
// })
// Sign(alg, key, claims)
// claims := Merge(map[string]interface{}{"foo":"bar"}, Claims{
// MaxAge: 15 * time.Minute,
// Issuer: "an-issuer",
// })
// Sign(alg, key, claims)
//
// Merge is automatically called when:
//
// Sign(alg, key, claims, MaxAge(time.Duration))
// Sign(alg, key, claims, Claims{...})
// Sign(alg, key, claims, MaxAge(time.Duration))
// Sign(alg, key, claims, Claims{...})
func Merge(claims interface{}, other interface{}) []byte {
claimsB, err := Marshal(claims)
if err != nil {
Expand Down
3 changes: 0 additions & 3 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/*
Package jwt aims to provide an implementation of the JSON Web Token standard.
The library supports the JSON Web Algorithm standard with HMAC, RSA, ECDSA and EdDSA.
The signing operation can accept multiple claims and merge as one,
Expand Down Expand Up @@ -43,7 +42,5 @@ Getting Started:
var claims map[string]interface{}
err = verifiedToken.Claims(&claims)
}
*/
package jwt
16 changes: 9 additions & 7 deletions expected.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@ import (
// between standard claims values.
//
// Usage:
// expected := Expected{
// Issuer: "my-app",
// }
// verifiedToken, err := Verify(..., expected)
//
// expected := Expected{
// Issuer: "my-app",
// }
// verifiedToken, err := Verify(..., expected)
type Expected Claims // We could use the same Claims structure but for concept separation we use a different one.

var _ TokenValidator = Expected{}

// ErrExpected indicates a standard claims post-validation error.
// Usage:
// verifiedToken, err := Verify(...)
// if errors.Is(ErrExpected, err) {
//
// }
// verifiedToken, err := Verify(...)
// if errors.Is(ErrExpected, err) {
//
// }
var ErrExpected = errors.New("jwt: field not match")

// ValidateToken completes the TokenValidator interface.
Expand Down
13 changes: 7 additions & 6 deletions gcm.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ var ErrDecrypt = errors.New("jwt: decrypt: payload authentication failed")
// Can be set to nil to ignore.
//
// Usage:
// var encKey = MustGenerateRandom(32)
// var sigKey = MustGenerateRandom(32)
//
// encrypt, decrypt, err := GCM(encKey, nil)
// if err != nil { ... }
// token, err := SignEncrypted(jwt.HS256, sigKey, encrypt, claims, jwt.MaxAge(15 * time.Minute))
// verifiedToken, err := VerifyEncrypted(jwt.HS256, sigKey, decrypt, token)
// var encKey = MustGenerateRandom(32)
// var sigKey = MustGenerateRandom(32)
//
// encrypt, decrypt, err := GCM(encKey, nil)
// if err != nil { ... }
// token, err := SignEncrypted(jwt.HS256, sigKey, encrypt, claims, jwt.MaxAge(15 * time.Minute))
// verifiedToken, err := VerifyEncrypted(jwt.HS256, sigKey, decrypt, token)
func GCM(key, additionalData []byte) (encrypt, decrypt InjectFunc, err error) {
block, err := aes.NewCipher(key)
if err != nil {
Expand Down
30 changes: 29 additions & 1 deletion hmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,45 @@ var panicHandler = func(v interface{}) {

// MustGenerateRandom returns a random HMAC key.
// Usage:
// MustGenerateRandom(64)
//
// MustGenerateRandom(64)
func MustGenerateRandom(n int) []byte {
key := make([]byte, n)
_, err := rand.Read(key)
// _, err := io.ReadFull(rand.Reader, key)
if err != nil {
panicHandler(err)
}

return key
}

// AI-generated.
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // 52 possibilities
letterIdxBits = 6 // 6 bits to represent 64 possibilities / indexes
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
)

// MustGenerateRandomString returns a random string based on the passed length.
func MustGenerateRandomString(length int) string {
result := make([]byte, length)
bufferSize := int(float64(length) * 1.3)
for i, j, randomBytes := 0, 0, []byte{}; i < length; j++ {
if j%bufferSize == 0 {
randomBytes = MustGenerateRandom(bufferSize)
}
if idx := int(randomBytes[j%length] & letterIdxMask); idx < len(letterBytes) {
result[i] = letterBytes[idx]
i++
}
}

return string(result)
}

//

// MustLoadHMAC accepts a single filename
// which its plain text data should contain the HMAC shared key.
// Pass the returned value to both `Token` and `Verify` functions.
Expand Down
11 changes: 6 additions & 5 deletions jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@ var Unmarshal = defaultUnmarshal
// Returns ErrMissingKey if a required value is missing from the payload.
//
// Usage:
// Unmarshal = UnmarshalWithRequired
// [...]
// A Go struct like: UserClaims { Username string `json:"username,required" `}
// [...]
// And `Verify` as usual.
//
// Unmarshal = UnmarshalWithRequired
// [...]
// A Go struct like: UserClaims { Username string `json:"username,required" `}
// [...]
// And `Verify` as usual.
func UnmarshalWithRequired(payload []byte, dest interface{}) error {
if err := defaultUnmarshal(payload, dest); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion kid_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (c KeysConfiguration) Load() (Keys, error) {
alg := RS256

for _, algo := range allAlgs {
if strings.ToLower(algo.Name()) == strings.ToLower(entry.Alg) {
if strings.EqualFold(algo.Name(), entry.Alg) {
alg = algo
break
}
Expand Down
24 changes: 12 additions & 12 deletions sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,27 @@ package jwt
//
// Example Code to pass only standard Claims:
//
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), jwt.Claims{...})
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), jwt.Claims{...})
//
// Example Code to pass custom and expiration Claims manually:
//
// now := time.Now()
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), map[string]interface{}{
// "iat": now.Unix(),
// "exp": now.Add(15 * time.Minute).Unix(),
// "foo": "bar",
// })
// now := time.Now()
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), map[string]interface{}{
// "iat": now.Unix(),
// "exp": now.Add(15 * time.Minute).Unix(),
// "foo": "bar",
// })
//
// Example Code for custom and standard claims using a SignOption:
//
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), jwt.Map{"foo":"bar"}, jwt.MaxAge(15 * time.Minute))
// OR
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), jwt.Map{"foo":"bar"}, jwt.Claims {Expiry: ...})
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), jwt.Map{"foo":"bar"}, jwt.MaxAge(15 * time.Minute))
// OR
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), jwt.Map{"foo":"bar"}, jwt.Claims {Expiry: ...})
//
// Example Code for custom type as Claims + standard Claims:
//
// type User struct { Username string `json:"username"` }
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), User{Username: "kataras"}, jwt.MaxAge(15 * time.Minute))
// type User struct { Username string `json:"username"` }
// token, err := jwt.Sign(jwt.HS256, []byte("secret"), User{Username: "kataras"}, jwt.MaxAge(15 * time.Minute))
func Sign(alg Alg, key PrivateKey, claims interface{}, opts ...SignOption) ([]byte, error) {
return signToken(alg, key, nil, claims, nil, opts...)
}
Expand Down
15 changes: 8 additions & 7 deletions verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import (
//
// Example Code:
//
// verifiedToken, err := jwt.Verify(jwt.HS256, []byte("secret"), token)
// [handle error...]
// var claims map[string]interface{}
// verifiedToken.Claims(&claims)
// verifiedToken, err := jwt.Verify(jwt.HS256, []byte("secret"), token)
// [handle error...]
// var claims map[string]interface{}
// verifiedToken.Claims(&claims)
func Verify(alg Alg, key PublicKey, token []byte, validators ...TokenValidator) (*VerifiedToken, error) {
return verifyToken(alg, key, nil, token, nil, validators...)
}
Expand Down Expand Up @@ -132,9 +132,10 @@ var errPayloadNotJSON = errors.New("jwt: payload is not a type of JSON") // malf
// to allow tokens with plain payload (no JSON or malformed JSON) to be successfully validated.
//
// Usage:
// verifiedToken, err := jwt.Verify(jwt.HS256, []byte("secret"), token, jwt.Plain)
// [handle error...]
// [verifiedToken.Payload...]
//
// verifiedToken, err := jwt.Verify(jwt.HS256, []byte("secret"), token, jwt.Plain)
// [handle error...]
// [verifiedToken.Payload...]
var Plain = TokenValidatorFunc(func(token []byte, standardClaims Claims, err error) error {
if err == errPayloadNotJSON {
return nil // skip this error entirely.
Expand Down

0 comments on commit f89638b

Please sign in to comment.