| /* |
| 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) |
| } |