github.com/twilio/twilio-go@v1.20.1/client/jwt/jwt.go (about)

     1  package jwt
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"time"
     8  
     9  	"github.com/golang-jwt/jwt"
    10  	. "github.com/twilio/twilio-go/client/jwt/util"
    11  )
    12  
    13  type TokenType string
    14  
    15  type Jwt struct {
    16  	// The secret used to encode the JWT token.
    17  	SecretKey string
    18  	// The issuer of the JWT token.
    19  	Issuer string
    20  	// The subject of this JWT, omitted from the payload by default.
    21  	Subject string
    22  	// The algorithm used to encode the JWT token, defaults to 'HS256'.
    23  	Algorithm string
    24  	// Time in seconds before the JWT token is invalid. Defaults to now.
    25  	Nbf float64
    26  	// Time to live of the JWT in seconds; defaults to 1 hour.
    27  	Ttl float64
    28  	// Time in seconds since epoch this JWT is valid for. Override ttl if provided.
    29  	ValidUntil float64
    30  
    31  	DecodedHeaders map[string]interface{}
    32  	DecodedPayload map[string]interface{}
    33  }
    34  
    35  func (token *Jwt) generatePayload(payload map[string]interface{}) map[string]interface{} {
    36  	now := time.Now().Unix()
    37  
    38  	payload["iss"] = token.Issuer
    39  	payload["exp"] = float64(now) + token.Ttl
    40  
    41  	if token.Nbf != 0 {
    42  		payload["nbf"] = token.Nbf
    43  	} else {
    44  		payload["nbf"] = float64(now)
    45  	}
    46  
    47  	if token.ValidUntil != 0 {
    48  		payload["exp"] = token.ValidUntil
    49  	}
    50  	if token.Subject != "" {
    51  		payload["sub"] = token.Subject
    52  	}
    53  
    54  	return payload
    55  }
    56  
    57  func (token *Jwt) FromJwt(jwtStr string, key string) (*Jwt, error) {
    58  	verifyToken := true
    59  	if key == "" {
    60  		verifyToken = false
    61  	}
    62  
    63  	// Parse takes the token string and a function for looking up the key. The latter is especially
    64  	// useful if you use multiple keys for your application.  The standard is to use 'kid' in the
    65  	// head of the token to identify which key to use, but the parsed token (head and claims) is provided
    66  	// to the callback, providing flexibility.
    67  	decodedToken, err := jwt.Parse(jwtStr, func(token *jwt.Token) (interface{}, error) {
    68  		// Validate the alg is what you expect
    69  		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
    70  			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
    71  		}
    72  
    73  		return []byte(key), nil
    74  	})
    75  
    76  	if decodedToken != nil {
    77  		if claims, ok := decodedToken.Claims.(jwt.MapClaims); ok {
    78  			if verifyToken && !decodedToken.Valid {
    79  				return nil, errors.New("token could not be validated")
    80  			}
    81  			jwtToken := Jwt{
    82  				SecretKey: key,
    83  				Issuer:    claims["iss"].(string),
    84  				Algorithm: decodedToken.Header["alg"].(string),
    85  			}
    86  
    87  			if val, ok := claims["sub"].(string); ok {
    88  				jwtToken.Subject = val
    89  			}
    90  
    91  			if val, ok := claims["exp"].(float64); ok {
    92  				jwtToken.ValidUntil = val
    93  			}
    94  
    95  			nbf, err := strconv.ParseFloat(fmt.Sprintf("%v", claims["nbf"]), 64)
    96  			if err == nil {
    97  				jwtToken.Nbf = nbf
    98  			}
    99  
   100  			jwtToken.DecodedHeaders = decodedToken.Header
   101  			jwtToken.DecodedPayload = claims
   102  			return &jwtToken, nil
   103  		} else {
   104  			return nil, err
   105  		}
   106  	}
   107  
   108  	return nil, errors.New("error decoding JWT token")
   109  }
   110  
   111  func (token *Jwt) Payload() map[string]interface{} {
   112  	if token.DecodedPayload == nil {
   113  		token.DecodedPayload = token.generatePayload(map[string]interface{}{})
   114  	}
   115  
   116  	return token.DecodedPayload
   117  }
   118  
   119  func (token *Jwt) Headers() map[string]interface{} {
   120  	if token.DecodedHeaders == nil {
   121  		token.DecodedHeaders = token.generateHeaders()
   122  	}
   123  
   124  	return token.DecodedHeaders
   125  }
   126  
   127  func (token *Jwt) generateHeaders() map[string]interface{} {
   128  	headers := make(map[string]interface{})
   129  	headers["alg"] = HS256
   130  	headers["typ"] = JWT
   131  	return headers
   132  }
   133  
   134  // Encode this JWT struct into a string.
   135  // algorithm - algorithm used to encode the JWT that overrides the default
   136  // ttl - specify ttl to override the default
   137  func (token *Jwt) ToJwt(generateHeaders, generatePayload func() map[string]interface{}) (string, error) {
   138  	if token.SecretKey == "" {
   139  		panic("JWT does not have a signing key configured.")
   140  	}
   141  
   142  	headers := generateHeaders()
   143  	payload := generatePayload()
   144  	if signedToken, err := SignTokenWithHMAC(headers, payload, token.SecretKey); err != nil {
   145  		return "", err
   146  	} else {
   147  		return signedToken, nil
   148  	}
   149  }
   150  
   151  func SignTokenWithHMAC(headers, payload map[string]interface{}, secret string) (string, error) {
   152  	claims := jwt.MapClaims{}
   153  
   154  	for k, v := range payload {
   155  		claims[k] = v
   156  	}
   157  
   158  	jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
   159  
   160  	for hk, hv := range headers {
   161  		jwtToken.Header[hk] = hv
   162  	}
   163  
   164  	if tokenString, err := jwtToken.SignedString([]byte(secret)); err != nil {
   165  		return "", err
   166  	} else {
   167  		return tokenString, nil
   168  	}
   169  }