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 }