auth.go raw
1 package internal
2
3 import (
4 "crypto/hmac"
5 "crypto/sha1"
6 "encoding/base64"
7 "errors"
8 "fmt"
9 "net/http"
10 "strconv"
11 "time"
12 )
13
14 const securityTokenHeader = "x-cns-security-token"
15
16 // TokenTransport HTTP transport for API authentication.
17 type TokenTransport struct {
18 apiKey string
19 secretKey string
20
21 // Transport is the underlying HTTP transport to use when making requests.
22 // It will default to http.DefaultTransport if nil.
23 Transport http.RoundTripper
24 }
25
26 // NewTokenTransport Creates an HTTP transport for API authentication.
27 func NewTokenTransport(apiKey, secretKey string) (*TokenTransport, error) {
28 if apiKey == "" {
29 return nil, errors.New("credentials missing: API key")
30 }
31
32 if secretKey == "" {
33 return nil, errors.New("credentials missing: secret key")
34 }
35
36 return &TokenTransport{apiKey: apiKey, secretKey: secretKey}, nil
37 }
38
39 // RoundTrip executes a single HTTP transaction.
40 func (t *TokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
41 enrichedReq := &http.Request{}
42 *enrichedReq = *req
43
44 enrichedReq.Header = make(http.Header, len(req.Header))
45 for k, s := range req.Header {
46 enrichedReq.Header[k] = append([]string(nil), s...)
47 }
48
49 if t.apiKey != "" && t.secretKey != "" {
50 securityToken := createCnsSecurityToken(t.apiKey, t.secretKey)
51 enrichedReq.Header.Set(securityTokenHeader, securityToken)
52 }
53
54 return t.transport().RoundTrip(enrichedReq)
55 }
56
57 func (t *TokenTransport) transport() http.RoundTripper {
58 if t.Transport != nil {
59 return t.Transport
60 }
61
62 return http.DefaultTransport
63 }
64
65 // Client Creates a new HTTP client.
66 func (t *TokenTransport) Client() *http.Client {
67 return &http.Client{Transport: t}
68 }
69
70 // Wrap wraps an HTTP client Transport with the TokenTransport.
71 func (t *TokenTransport) Wrap(client *http.Client) *http.Client {
72 backup := client.Transport
73 t.Transport = backup
74 client.Transport = t
75
76 return client
77 }
78
79 func createCnsSecurityToken(apiKey, secretKey string) string {
80 timestamp := time.Now().Round(time.Millisecond).UnixNano() / int64(time.Millisecond)
81
82 hm := encodedHmac(timestamp, secretKey)
83 requestDate := strconv.FormatInt(timestamp, 10)
84
85 return fmt.Sprintf("%s:%s:%s", apiKey, hm, requestDate)
86 }
87
88 func encodedHmac(message int64, secret string) string {
89 h := hmac.New(sha1.New, []byte(secret))
90 _, _ = h.Write([]byte(strconv.FormatInt(message, 10)))
91
92 return base64.StdEncoding.EncodeToString(h.Sum(nil))
93 }
94