gapi.go - randomcrap - random crap programs of varying quality
(HTM) git clone git://git.codemadness.org/randomcrap
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
gapi.go (4143B)
---
1 package gapi
2
3 import (
4 "bytes"
5 "crypto"
6 "crypto/rand"
7 "crypto/rsa"
8 "crypto/sha256"
9 "crypto/x509"
10 "encoding/base64"
11 "encoding/json"
12 "encoding/pem"
13 "errors"
14 "io/ioutil"
15 "net/http"
16 "net/url"
17 "strings"
18 "time"
19 )
20
21 type Token struct {
22 Access_token string
23 Token_type string
24 Expires_in int64
25 }
26
27 type Key struct {
28 Private_key_id string
29 Private_key string
30 Client_email string
31 Client_id string
32 Type string
33 }
34
35 func ReadKeyfile(filename string) (Key, error) {
36 var keydata Key
37 data, err := ioutil.ReadFile(filename)
38 if err != nil {
39 return keydata, err
40 }
41 err = json.Unmarshal(data, &keydata)
42 if err != nil {
43 return keydata, err
44 }
45 return keydata, nil
46 }
47
48 func GetJwt(key Key, scopes []string) (string, error) {
49 now := time.Now()
50
51 headerfields := map[string]string{
52 "alg": "RS256",
53 "typ": "JWT",
54 }
55 claimfields := map[string]interface{}{
56 "iss": key.Client_email,
57 "scope": strings.Join(scopes, " "),
58 "aud": "https://accounts.google.com/o/oauth2/token",
59 "exp": now.Unix() + 3600, // 1 hour
60 "iat": now.Unix(),
61 }
62
63 //header
64 headerfieldsjson, err := json.Marshal(&headerfields)
65 if err != nil {
66 return "", err
67 }
68 header := base64.StdEncoding.EncodeToString(headerfieldsjson)
69 // claim
70 claimfieldsjson, err := json.Marshal(&claimfields)
71 if err != nil {
72 return "", err
73 }
74 claim := base64.StdEncoding.EncodeToString(claimfieldsjson)
75
76 // Decode PEM key.
77 block, _ := pem.Decode([]byte(key.Private_key))
78
79 err = nil
80
81 var priv *rsa.PrivateKey
82 switch block.Type {
83 case "PRIVATE KEY": // NOTE: key type is in text.
84 p, err := x509.ParsePKCS8PrivateKey(block.Bytes)
85 if err != nil {
86 return "", err
87 }
88 if _, ok := p.(*rsa.PrivateKey); !ok {
89 return "", errors.New("gapi.GetToken: found non-RSA private key in PKCS#8 wrapping")
90 }
91 priv = p.(*rsa.PrivateKey)
92 //case "RSA PRIVATE KEY":
93 //priv, err = x509.ParsePKCS1PrivateKey(block.Bytes)
94 default:
95 return "", errors.New("key type not supported: " + block.Type)
96 }
97 if err != nil {
98 return "", err
99 }
100
101 // SHA256 checksum of data.
102 h := sha256.New()
103 h.Write([]byte(header + "." + claim))
104
105 // sign data with private key.
106 sig, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, h.Sum(nil))
107 if err != nil {
108 return "", err
109 }
110 sigstr := base64.StdEncoding.EncodeToString(sig)
111 return header + "." + claim + "." + sigstr, nil
112 }
113
114 func RequestToken(jwt string) (Token, error) {
115 token := Token{}
116 client := &http.Client{
117 Timeout: time.Duration(30) * time.Second,
118 }
119
120 reqdata := bytes.NewReader([]byte((url.Values{
121 "grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"},
122 "assertion": {jwt},
123 }).Encode()))
124 tokenurl := "https://accounts.google.com:443/o/oauth2/token"
125 req, err := http.NewRequest("POST", tokenurl, reqdata)
126 if err != nil {
127 return token, err
128 }
129 req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
130 req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0")
131
132 resp, err := client.Do(req)
133 if resp != nil {
134 defer resp.Body.Close() // err != nil && resp != nil can happen on redirect failure.
135 }
136 if err != nil {
137 return token, err
138 }
139
140 bdata, err := ioutil.ReadAll(resp.Body)
141 if err != nil {
142 return token, err
143 }
144 // decode JSON response as token.
145 err = json.Unmarshal(bdata, &token)
146 if err != nil {
147 return token, err
148 }
149 return token, err
150 }
151
152 func GetToken(key Key, scopes []string) (Token, error) {
153 jwt, err := GetJwt(key, scopes)
154 if err != nil {
155 return Token{}, err
156 }
157 return RequestToken(jwt)
158 }
159
160 func Call(url string, token Token) ([]byte, error) {
161 req, err := http.NewRequest("GET", url, nil)
162 if err != nil {
163 return nil, err
164 }
165 req.Header.Add("Authorization", token.Token_type+" "+token.Access_token)
166 req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0")
167 client := &http.Client{
168 Timeout: time.Duration(30) * time.Second,
169 }
170 resp, err := client.Do(req)
171 if resp != nil {
172 defer resp.Body.Close() // err != nil && resp != nil can happen on redirect failure.
173 }
174 if err != nil {
175 return nil, err
176 }
177 return ioutil.ReadAll(resp.Body)
178 }