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

     1  package jwt
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"strconv"
     7  	"time"
     8  
     9  	. "github.com/twilio/twilio-go/client/jwt/util"
    10  )
    11  
    12  type AccessToken struct {
    13  	baseJwt *Jwt
    14  	// List of permissions that the token grants
    15  	Grants []BaseGrant `json:"grants,omitempty"`
    16  	// Twilio Account SID
    17  	AccountSid string `json:"account_sid,omitempty"`
    18  	// API key
    19  	SigningKeySid string `json:"signing_key_sid,omitempty"`
    20  	// User's identity
    21  	Identity string `json:"identity,omitempty"`
    22  	// User's region
    23  	Region interface{} `json:"region,omitempty"`
    24  }
    25  
    26  type AccessTokenParams struct {
    27  	// Twilio Account sid
    28  	AccountSid string
    29  	// The issuer of the token
    30  	SigningKeySid string
    31  	// The secret used to sign the token
    32  	Secret string
    33  	// Identity of the token issuer
    34  	Identity string
    35  	// User's Region
    36  	Region string
    37  	// Time in secs since epoch before which this JWT is invalid, defaults to now
    38  	Nbf float64
    39  	// Time to live of the JWT in seconds, defaults to 1 hour
    40  	Ttl float64
    41  	// Time in secs since epoch this JWT is valid for. Overrides ttl if provided.
    42  	ValidUntil float64
    43  	// Access permissions granted to this token
    44  	Grants []BaseGrant
    45  }
    46  
    47  func CreateAccessToken(params AccessTokenParams) AccessToken {
    48  	return AccessToken{
    49  		baseJwt: &Jwt{
    50  			SecretKey:  params.Secret,
    51  			Issuer:     params.SigningKeySid,
    52  			Subject:    params.AccountSid,
    53  			Algorithm:  HS256,
    54  			Nbf:        params.Nbf,
    55  			Ttl:        Max(params.Ttl, 3600),
    56  			ValidUntil: params.ValidUntil,
    57  		},
    58  		Grants:        params.Grants,
    59  		AccountSid:    params.AccountSid,
    60  		SigningKeySid: params.SigningKeySid,
    61  		Identity:      params.Identity,
    62  		Region:        params.Region,
    63  	}
    64  }
    65  
    66  func (token *AccessToken) Payload() map[string]interface{} {
    67  	if token.baseJwt.DecodedPayload == nil {
    68  		token.baseJwt.DecodedPayload = token.GeneratePayload()
    69  	}
    70  
    71  	return token.baseJwt.DecodedPayload
    72  }
    73  
    74  func (token *AccessToken) AddGrant(grant BaseGrant) {
    75  	if grant == nil {
    76  		panic("Grant to add is nil")
    77  	}
    78  	token.Grants = append(token.Grants, grant)
    79  }
    80  
    81  func (token *AccessToken) Headers() map[string]interface{} {
    82  	if token.baseJwt.DecodedHeaders == nil {
    83  		token.baseJwt.DecodedHeaders = token.generateHeaders()
    84  	}
    85  
    86  	return token.baseJwt.DecodedHeaders
    87  }
    88  
    89  func (token *AccessToken) generateHeaders() map[string]interface{} {
    90  	headers := make(map[string]interface{})
    91  	headers["cty"] = CType
    92  
    93  	if token.Region != "" {
    94  		headers["twr"] = token.Region
    95  	}
    96  
    97  	headers["alg"] = HS256
    98  	headers["typ"] = JWT
    99  
   100  	return headers
   101  }
   102  
   103  func (token *AccessToken) GeneratePayload() map[string]interface{} {
   104  	now := float64(time.Now().Unix())
   105  
   106  	grants := make(map[string]interface{})
   107  	for _, grant := range token.Grants {
   108  		grants[grant.Key()] = grant.ToPayload()
   109  	}
   110  
   111  	payload := map[string]interface{}{
   112  		"jti":    fmt.Sprintf("%s-%s", token.SigningKeySid, strconv.Itoa(int(now))),
   113  		"grants": grants,
   114  	}
   115  
   116  	if token.Identity != "" {
   117  		val := payload["grants"].(map[string]interface{})
   118  		val["identity"] = token.Identity
   119  	}
   120  
   121  	payload["iss"] = token.baseJwt.Issuer
   122  	payload["exp"] = now + token.baseJwt.Ttl
   123  
   124  	if token.baseJwt.Nbf != 0 {
   125  		payload["nbf"] = token.baseJwt.Nbf
   126  	} else {
   127  		payload["nbf"] = now
   128  	}
   129  
   130  	if token.baseJwt.ValidUntil != 0 {
   131  		payload["exp"] = token.baseJwt.ValidUntil
   132  	}
   133  	if token.baseJwt.Subject != "" {
   134  		payload["sub"] = token.baseJwt.Subject
   135  	}
   136  
   137  	return payload
   138  }
   139  
   140  // Encode this JWT struct into a string.
   141  // algorithm - algorithm used to encode the JWT that overrides the default
   142  // ttl - specify ttl to override the default
   143  func (token *AccessToken) ToJwt() (string, error) {
   144  	signedToken, err := token.baseJwt.ToJwt(token.generateHeaders, token.GeneratePayload)
   145  	if err != nil {
   146  		return "", err
   147  	}
   148  	return signedToken, nil
   149  }
   150  
   151  func decodeGrants(grants interface{}) []BaseGrant {
   152  	var decodedGrants []BaseGrant
   153  
   154  	for k, v := range grants.(map[string]interface{}) {
   155  		var grant BaseGrant
   156  		if data, err := json.Marshal(v); err == nil {
   157  			switch k {
   158  			case "chat":
   159  				grant = &ChatGrant{}
   160  			case "rtc":
   161  				grant = &ConversationsGrant{}
   162  			case "ip_messaging":
   163  				grant = &IpMessagingGrant{}
   164  			case "data_sync":
   165  				grant = &SyncGrant{}
   166  			case "task_router":
   167  				grant = &TaskRouterGrant{}
   168  			case "video":
   169  				grant = &VideoGrant{}
   170  			case "voice":
   171  				grant = &VoiceGrant{}
   172  			case "player":
   173  				grant = &PlaybackGrant{}
   174  			}
   175  
   176  			if errJson := json.Unmarshal(data, &grant); errJson == nil {
   177  				decodedGrants = append(decodedGrants, grant)
   178  			}
   179  		}
   180  	}
   181  
   182  	return decodedGrants
   183  }
   184  
   185  // Decode a JWT string into a Jwt struct.
   186  // jwt - JWT string
   187  // key - string key used to verify the JWT signature; if not provided, then validation is skipped
   188  func (token *AccessToken) FromJwt(jwtStr string, key string) (*AccessToken, error) {
   189  	baseToken, err := token.baseJwt.FromJwt(jwtStr, key)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	decodedToken := &AccessToken{
   195  		baseJwt:       baseToken,
   196  		Grants:        decodeGrants(baseToken.Payload()["grants"]),
   197  		AccountSid:    baseToken.Payload()["sub"].(string),
   198  		SigningKeySid: baseToken.Payload()["iss"].(string),
   199  	}
   200  
   201  	if val, ok := baseToken.Headers()["twr"]; ok {
   202  		decodedToken.Region = val
   203  	}
   204  	if val, ok := baseToken.Payload()["grants"]; ok {
   205  		if iVal, iOk := val.(map[string]interface{})["identity"]; iOk {
   206  			decodedToken.Identity = iVal.(string)
   207  		}
   208  	}
   209  
   210  	return decodedToken, nil
   211  }