...
1
21
22 package clients
23
24 import (
25 "context"
26 "crypto/rsa"
27 "encoding/json"
28 "io"
29 "io/ioutil"
30 "net/http"
31 "net/url"
32 "strings"
33
34 "gopkg.in/square/go-jose.v2"
35 "gopkg.in/square/go-jose.v2/jwt"
36 )
37
38 const jwtBearerGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
39
40 type JWTBearer struct {
41 tokenURL string
42 header *Header
43 client *http.Client
44
45 Signer jose.Signer
46 }
47
48 type Token struct {
49 AccessToken string `json:"access_token"`
50 TokenType string `json:"token_type,omitempty"`
51 RefreshToken string `json:"refresh_token,omitempty"`
52 ExpiresIn int64 `json:"expires_in,omitempty"`
53 }
54
55 type Header struct {
56 Algorithm string `json:"alg"`
57 Typ string `json:"typ"`
58 KeyID string `json:"kid,omitempty"`
59 }
60
61 type JWTBearerPayload struct {
62 *jwt.Claims
63
64 PrivateClaims map[string]interface{}
65 }
66
67 func (c *JWTBearer) SetPrivateKey(keyID string, privateKey *rsa.PrivateKey) error {
68 jwk := jose.JSONWebKey{Key: privateKey, KeyID: keyID, Algorithm: string(jose.RS256)}
69 signingKey := jose.SigningKey{
70 Algorithm: jose.RS256,
71 Key: jwk,
72 }
73 signerOptions := &jose.SignerOptions{}
74 signerOptions.WithType("JWT")
75
76 sig, err := jose.NewSigner(signingKey, signerOptions)
77 if err != nil {
78 return err
79 }
80
81 c.Signer = sig
82
83 return nil
84 }
85
86 func (c *JWTBearer) GetToken(ctx context.Context, payloadData *JWTBearerPayload, scope []string) (*Token, error) {
87 builder := jwt.Signed(c.Signer).
88 Claims(payloadData.Claims).
89 Claims(payloadData.PrivateClaims)
90
91 assertion, err := builder.CompactSerialize()
92 if err != nil {
93 return nil, err
94 }
95
96 requestBodyReader, err := c.getRequestBodyReader(assertion, scope)
97 if err != nil {
98 return nil, err
99 }
100
101 request, err := http.NewRequestWithContext(ctx, "POST", c.tokenURL, requestBodyReader)
102 if err != nil {
103 return nil, err
104 }
105
106 request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
107
108 response, err := c.client.Do(request)
109 if err != nil {
110 return nil, err
111 }
112
113 defer response.Body.Close()
114
115 body, err := ioutil.ReadAll(response.Body)
116 if err != nil {
117 return nil, err
118 }
119
120 if c := response.StatusCode; c < 200 || c > 299 {
121 return nil, &RequestError{
122 Response: response,
123 Body: body,
124 }
125 }
126
127 token := &Token{}
128
129 if err := json.Unmarshal(body, token); err != nil {
130 return nil, err
131 }
132
133 return token, err
134 }
135
136 func (c *JWTBearer) getRequestBodyReader(assertion string, scope []string) (io.Reader, error) {
137 data := url.Values{}
138 data.Set("grant_type", jwtBearerGrantType)
139 data.Set("assertion", string(assertion))
140
141 if len(scope) != 0 {
142 data.Set("scope", strings.Join(scope, " "))
143 }
144
145 return strings.NewReader(data.Encode()), nil
146 }
147
148 func NewJWTBearer(tokenURL string) *JWTBearer {
149 return &JWTBearer{
150 client: &http.Client{},
151 tokenURL: tokenURL,
152 }
153 }
154
View as plain text