jwt support
diff --git a/adapter/BUILD b/adapter/BUILD
index f42b913..a864253 100644
--- a/adapter/BUILD
+++ b/adapter/BUILD
@@ -12,12 +12,14 @@
         "applications.go",
         "products.go",
         "rejecter.go",
+        "jwt.go",
     ],
     deps = [
         "//adapter/config:go_default_library",
         "//common:go_default_library",
         "//external:mixer_adapter",
         "@com_github_hashicorp_go_multierror//:go_default_library",
+        "@com_github_dgrijalva_jwtgo//:go_default_library",
     ],
 )
 
@@ -33,5 +35,6 @@
     library = ":go_default_library",
     deps = [
         "//mock:go_default_library",
+
     ],
 )
diff --git a/adapter/apigeeKeyAttributes.go b/adapter/apigeeKeyAttributes.go
index 6971f98..5c5047c 100644
--- a/adapter/apigeeKeyAttributes.go
+++ b/adapter/apigeeKeyAttributes.go
@@ -19,7 +19,7 @@
 import (
 	"fmt"
 	"strconv"
-
+	"strings"
 	"github.com/apid/istioApigeeAdapter/adapter/config"
 	"istio.io/mixer/pkg/adapter"
 )
@@ -29,6 +29,7 @@
 	keyAttrsDesc = "Set attributes based on an Apigee API key"
 
 	keyParam           = "apiKey"
+	jwtParam	   = "jwtToken"
 	pathParam          = "requestPath"
 	successParam       = "success"
 	successStringParam = "successString"
@@ -47,6 +48,7 @@
 	env          adapter.Env
 	applications *applicationManager
 	products     *productManager
+	jwtVerifier  *jwtVerifier
 }
 
 func newKeyAttrsBuilder() adapter.AttributesGeneratorBuilder {
@@ -62,10 +64,13 @@
 func (b keyAttrsBuilder) BuildAttributesGenerator(env adapter.Env, c adapter.Config) (adapter.AttributesGenerator, error) {
 	cfg := c.(*config.VerifyKeyParams)
 	verifyPath, productsPath := getPaths(cfg)
+	publicKeyUrl := getPulicKeyUrl(cfg)
+
 	g := &keyAttrsGenerator{
 		env:          env,
 		applications: newApplicationManager(env, defaultAppLifetime, verifyPath),
 		products:     newProductManager(productsPath, defaultProductsFetch),
+		jwtVerifier:  newVerifier(defaultKeyFetch, publicKeyUrl),
 	}
 	env.Logger().Infof("Created Apigee attributes generator to invoke \"%s\"", verifyPath)
 	env.Logger().Infof("Checking API products using \"%s\"", productsPath)
@@ -80,14 +85,48 @@
 	out[successStringParam] = "false"
 
 	key := getString(in, keyParam)
-	if key == "" {
+	jwt_token := getString(in, jwtParam)
+
+	if key == "" && jwt_token == "" {
 		return out, nil
 	}
+
 	path := getString(in, pathParam)
 	if path == "" {
 		return out, nil
 	}
 
+	if jwt_token != "" {
+		if strings.HasPrefix(jwt_token, "Bearer") {
+			token := strings.Split(jwt_token, " ")[1]
+			jwt_token = token
+		} else {
+			g.env.Logger().Errorf("Cannot verify jwt token, Bearer missing")
+			return out, nil
+		}
+
+		claims, err := g.jwtVerifier.Verify(jwt_token)
+
+		if err != nil {
+			g.env.Logger().Errorf("Cannot verify jwt token : %s", err)
+			return out, nil
+		}
+
+		// considers token as valid if exp, iat, iss is not present
+		err = claims.Valid()
+
+		// invalid token
+		if err != nil {
+			g.env.Logger().Errorf("jwt token invalid : %s", err)
+			return out, nil
+		}
+
+		// TODO: check scopes
+
+		// prioritizing jwt over apikey (if user passes both jwt and apikey)
+		key = claims.(*apigeeClaims).ClientId
+	}
+
 	// Look up API key from cache, making HTTP request if necessary
 	app, err := g.applications.get(key)
 	if err != nil {
@@ -116,9 +155,14 @@
 	if success {
 		out[productNameParam] = products[0].Name
 	}
+
 	return out, nil
 }
 
 func (g *keyAttrsGenerator) Close() error {
 	return nil
 }
+
+func getPulicKeyUrl(cfg *config.VerifyKeyParams) (string) {
+	return fmt.Sprintf(defaultPublicKeyURL, cfg.Organization, cfg.Environment)
+}
\ No newline at end of file
diff --git a/adapter/jwt.go b/adapter/jwt.go
new file mode 100644
index 0000000..290ba62
--- /dev/null
+++ b/adapter/jwt.go
@@ -0,0 +1,112 @@
+/*
+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)
+}
\ No newline at end of file