| /* | 
 | Copyright 2017 Google Inc. | 
 |  | 
 | Licensed under the Apache License, Version 2.0 (the "License"); | 
 | you may not use this file except in compliance with the License. | 
 | You may obtain a copy of the License at | 
 |  | 
 |     http://www.apache.org/licenses/LICENSE-2.0 | 
 |  | 
 | Unless required by applicable law or agreed to in writing, software | 
 | distributed under the License is distributed on an "AS IS" BASIS, | 
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | See the License for the specific language governing permissions and | 
 | limitations under the License. | 
 | */ | 
 |  | 
 | package adapter | 
 |  | 
 | // TODO: key rotation | 
 |  | 
 | import( | 
 | 	// ref: https://godoc.org/github.com/dgrijalva/jwt-go ,  https://github.com/dgrijalva/jwt-go | 
 | 	jwt "github.com/dgrijalva/jwt-go" | 
 | 	"fmt" | 
 | 	"regexp" | 
 | 	"net/http" | 
 | 	"io/ioutil" | 
 | 	"time" | 
 | ) | 
 |  | 
 | const ( | 
 | 	defaultKeyFetch 	= time.Hour | 
 | 	defaultPublicKeyURL	= "https://%s-%s.apigee.net/edgemicro-auth/publicKey" | 
 | ) | 
 |  | 
 | type apigeeClaims struct { | 
 | 	jwt.StandardClaims | 
 | 	ClientId 		string `json:"client_id,omitempty"` | 
 | 	ApplicationName        	string `json:"application_name,omitempty"` | 
 | 	Scopes   		[]string `json:"scopes,omitempty"` | 
 | } | 
 |  | 
 | type jwtVerifier struct { | 
 | 	parser 		  *jwt.Parser | 
 | 	refresh    	  time.Duration | 
 | 	public_key_rsa256 []byte | 
 | 	// TODO add support for other algorithms | 
 | } | 
 |  | 
 | // return: verifier object that can be used to verify JWT | 
 | // input: keys | 
 | // TODO: input HMAC and ECDSA keys | 
 | func newVerifier(refresh time.Duration, public_key_url string) (*jwtVerifier){ | 
 | 	verifier_obj_pointer := new(jwtVerifier) | 
 |  | 
 | 	// TODO: handle error | 
 | 	public_key_rsa256_raw_data, _ := getHttp(public_key_url) | 
 |  | 
 | 	// TODO: store parsed key | 
 | 	verifier_obj_pointer.public_key_rsa256   = public_key_rsa256_raw_data | 
 | 	verifier_obj_pointer.parser		 = new(jwt.Parser) | 
 | 	// TODO: add more methods based on keys supplied to this function, while adding support for other algorithms | 
 | 	verifier_obj_pointer.parser.ValidMethods = []string{"RS256"} | 
 |  | 
 | 	return verifier_obj_pointer | 
 | } | 
 |  | 
 | // Verify a token and output the claims | 
 | func (this *jwtVerifier) Verify(to_verify_token string) (jwt.Claims, error) { | 
 |  | 
 | 	// get byte stream of token | 
 | 	to_verify_token_in_bytes := []byte(to_verify_token) | 
 |  | 
 | 	// trim possible whitespace from token | 
 | 	to_verify_token_in_bytes = regexp.MustCompile(`\s*$`).ReplaceAll(to_verify_token_in_bytes, []byte{}) | 
 |  | 
 | 	// Parse the token | 
 | 	token, err := this.parser.ParseWithClaims(string(to_verify_token), &apigeeClaims{}, func(t *jwt.Token) (interface{}, error) { | 
 | 		// this function should return right key to parse the token | 
 | 		if(t.Method.Alg() == "RS256") { | 
 | 			return jwt.ParseRSAPublicKeyFromPEM(this.public_key_rsa256) | 
 | 		} else { | 
 | 			return nil, fmt.Errorf("signing method not supported") | 
 | 		} | 
 | 	}) | 
 |  | 
 | 	if err != nil { | 
 | 		return nil, fmt.Errorf("Couldn't parse token: %v", err) | 
 | 	} | 
 |  | 
 | 	if !token.Valid { | 
 | 		return nil, fmt.Errorf("Token is invalid") | 
 | 	} | 
 |  | 
 | 	// return jwt.Claim object (https://github.com/dgrijalva/jwt-go/blob/master/claims.go#L11) (http://godoc.org/github.com/dgrijalva/jwt-go#Claims) | 
 | 	return token.Claims, err | 
 | } | 
 |  | 
 | func getHttp(public_key_url string) ([]byte, error) { | 
 | 	resp, err := http.Get(public_key_url) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 |  | 
 | 	defer resp.Body.Close() | 
 |  | 
 | 	if resp.StatusCode != 200 { | 
 | 		return nil, fmt.Errorf("HTTP error fetching from url", public_key_url, " with response code ", resp.StatusCode) | 
 | 	} | 
 |  | 
 | 	return ioutil.ReadAll(resp.Body) | 
 | } |