github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package jws provides encoding and decoding utilities for 6 // signed JWS messages. 7 package jws 8 9 import ( 10 "bytes" 11 "crypto" 12 "crypto/rand" 13 "crypto/rsa" 14 "crypto/sha256" 15 "encoding/base64" 16 "encoding/json" 17 "errors" 18 "fmt" 19 "strings" 20 "time" 21 ) 22 23 // ClaimSet contains information about the JWT signature including the 24 // permissions being requested (scopes), the target of the token, the issuer, 25 // the time the token was issued, and the lifetime of the token. 26 type ClaimSet struct { 27 Iss string `json:"iss"` // email address of the client_id of the application making the access token request 28 Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests 29 Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional). 30 Exp int64 `json:"exp"` // the expiration time of the assertion (seconds since Unix epoch) 31 Iat int64 `json:"iat"` // the time the assertion was issued (seconds since Unix epoch) 32 Typ string `json:"typ,omitempty"` // token type (Optional). 33 34 // Email for which the application is requesting delegated access (Optional). 35 Sub string `json:"sub,omitempty"` 36 37 // The old name of Sub. Client keeps setting Prn to be 38 // complaint with legacy OAuth 2.0 providers. (Optional) 39 Prn string `json:"prn,omitempty"` 40 41 // See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3 42 // This array is marshalled using custom code (see (c *ClaimSet) encode()). 43 PrivateClaims map[string]interface{} `json:"-"` 44 } 45 46 func (c *ClaimSet) encode() (string, error) { 47 // Reverting time back for machines whose time is not perfectly in sync. 48 // If client machine's time is in the future according 49 // to Google servers, an access token will not be issued. 50 now := time.Now().Add(-10 * time.Second) 51 if c.Iat == 0 { 52 c.Iat = now.Unix() 53 } 54 if c.Exp == 0 { 55 c.Exp = now.Add(time.Hour).Unix() 56 } 57 if c.Exp < c.Iat { 58 return "", fmt.Errorf("jws: invalid Exp = %v; must be later than Iat = %v", c.Exp, c.Iat) 59 } 60 61 b, err := json.Marshal(c) 62 if err != nil { 63 return "", err 64 } 65 66 if len(c.PrivateClaims) == 0 { 67 return base64Encode(b), nil 68 } 69 70 // Marshal private claim set and then append it to b. 71 prv, err := json.Marshal(c.PrivateClaims) 72 if err != nil { 73 return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims) 74 } 75 76 // Concatenate public and private claim JSON objects. 77 if !bytes.HasSuffix(b, []byte{'}'}) { 78 return "", fmt.Errorf("jws: invalid JSON %s", b) 79 } 80 if !bytes.HasPrefix(prv, []byte{'{'}) { 81 return "", fmt.Errorf("jws: invalid JSON %s", prv) 82 } 83 b[len(b)-1] = ',' // Replace closing curly brace with a comma. 84 b = append(b, prv[1:]...) // Append private claims. 85 return base64Encode(b), nil 86 } 87 88 // Header represents the header for the signed JWS payloads. 89 type Header struct { 90 // The algorithm used for signature. 91 Algorithm string `json:"alg"` 92 93 // Represents the token type. 94 Typ string `json:"typ"` 95 } 96 97 func (h *Header) encode() (string, error) { 98 b, err := json.Marshal(h) 99 if err != nil { 100 return "", err 101 } 102 return base64Encode(b), nil 103 } 104 105 // Decode decodes a claim set from a JWS payload. 106 func Decode(payload string) (*ClaimSet, error) { 107 // decode returned id token to get expiry 108 s := strings.Split(payload, ".") 109 if len(s) < 2 { 110 // TODO(jbd): Provide more context about the error. 111 return nil, errors.New("jws: invalid token received") 112 } 113 decoded, err := base64Decode(s[1]) 114 if err != nil { 115 return nil, err 116 } 117 c := &ClaimSet{} 118 err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c) 119 return c, err 120 } 121 122 // Signer returns a signature for the given data. 123 type Signer func(data []byte) (sig []byte, err error) 124 125 // EncodeWithSigner encodes a header and claim set with the provided signer. 126 func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) { 127 head, err := header.encode() 128 if err != nil { 129 return "", err 130 } 131 cs, err := c.encode() 132 if err != nil { 133 return "", err 134 } 135 ss := fmt.Sprintf("%s.%s", head, cs) 136 sig, err := sg([]byte(ss)) 137 if err != nil { 138 return "", err 139 } 140 return fmt.Sprintf("%s.%s", ss, base64Encode(sig)), nil 141 } 142 143 // Encode encodes a signed JWS with provided header and claim set. 144 // This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key. 145 func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) { 146 sg := func(data []byte) (sig []byte, err error) { 147 h := sha256.New() 148 h.Write([]byte(data)) 149 return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil)) 150 } 151 return EncodeWithSigner(header, c, sg) 152 } 153 154 // base64Encode returns and Base64url encoded version of the input string with any 155 // trailing "=" stripped. 156 func base64Encode(b []byte) string { 157 return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") 158 } 159 160 // base64Decode decodes the Base64url encoded string 161 func base64Decode(s string) ([]byte, error) { 162 // add back missing padding 163 switch len(s) % 4 { 164 case 1: 165 s += "===" 166 case 2: 167 s += "==" 168 case 3: 169 s += "=" 170 } 171 return base64.URLEncoding.DecodeString(s) 172 }