blob: 290ba6292cb7e62613d09024d36cd34a0adf3235 [file] [log] [blame]
/*
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)
}