Replace tests that depend on Apigee SSO API with tests that generate a
JWT token themselves and verify against a public key stored in GitHub.
Also, change the OAuth handler so that the stored public key is not
a global variable.
diff --git a/oauth.go b/oauth.go
index 5836d33..350c780 100644
--- a/oauth.go
+++ b/oauth.go
@@ -4,23 +4,19 @@
 	"context"
 	"crypto/rsa"
 	"encoding/json"
+	"net/http"
+	"sync"
+	"time"
+
 	"github.com/SermoDigital/jose/crypto"
 	"github.com/SermoDigital/jose/jws"
 	"github.com/julienschmidt/httprouter"
 	"github.com/justinas/alice"
-	"net/http"
-	"sync"
-	"time"
-)
-
-var (
-	gPkey   *rsa.PublicKey = nil
-	rwMutex sync.RWMutex
 )
 
 const params = "params"
 
-/* Errors to return */
+// Errors to return
 type Errors []string
 
 /*
@@ -36,14 +32,16 @@
 }
 
 /*
-OAuth structure that provides http connection to the URL that has the public
+oauth provides http an connection to the URL that has the public
 key for verifying the JWT token
 */
-type OAuth struct {
+type oauth struct {
+	gPkey   *rsa.PublicKey
+	rwMutex *sync.RWMutex
 }
 
 /*
-The interface functions offered to clients that act on OAuth param,
+OAuthService offers interface functions that act on OAuth param,
 used to verify JWT tokens for the Http handler functions client
 wishes to validate against (via SSOHandler).
 */
@@ -58,18 +56,13 @@
 verification.
 */
 func (s *HTTPScaffold) CreateOAuth(keyURL string) OAuthService {
-
-	pk, err := getPublicKey(keyURL)
-	if err == nil {
-		setPkSafe(pk)
+	pk, _ := getPublicKey(keyURL)
+	oa := &oauth{
+		rwMutex: &sync.RWMutex{},
 	}
-	/*
-	 * Routine that will fetch & update the public keys in safe manner
-	 */
-	updatePublicKeysPeriodic(keyURL)
-
-	return &OAuth{}
-
+	oa.setPkSafe(pk)
+	oa.updatePublicKeysPeriodic(keyURL)
+	return oa
 }
 
 /*
@@ -92,7 +85,7 @@
 SSOHandler offers the users the flexibility of choosing which http handlers
 need JWT validation.
 */
-func (a *OAuth) SSOHandler(p string, h func(http.ResponseWriter, *http.Request)) (string, httprouter.Handle) {
+func (a *oauth) SSOHandler(p string, h func(http.ResponseWriter, *http.Request)) (string, httprouter.Handle) {
 	return p, a.VerifyOAuth(alice.New().ThenFunc(h))
 }
 
@@ -100,7 +93,7 @@
 VerifyOAuth verifies the JWT token in the request using the public key configured
 via CreateOAuth constructor.
 */
-func (a *OAuth) VerifyOAuth(next http.Handler) httprouter.Handle {
+func (a *oauth) VerifyOAuth(next http.Handler) httprouter.Handle {
 
 	return func(rw http.ResponseWriter, r *http.Request, ps httprouter.Params) {
 
@@ -112,7 +105,7 @@
 		}
 
 		/* Get the pulic key from cache */
-		pk := getPkSafe()
+		pk := a.getPkSafe()
 		if pk == nil {
 			WriteErrorResponse(http.StatusBadRequest, "Public key not configured. Validation failed.", rw)
 			return
@@ -152,7 +145,7 @@
 /*
 updatePulicKeysPeriodic updates the cache periodically (every hour)
 */
-func updatePublicKeysPeriodic(keyURL string) {
+func (a *oauth) updatePublicKeysPeriodic(keyURL string) {
 
 	ticker := time.NewTicker(time.Hour)
 	quit := make(chan struct{})
@@ -162,7 +155,7 @@
 			case <-ticker.C:
 				pk, err := getPublicKey(keyURL)
 				if err == nil {
-					setPkSafe(pk)
+					a.setPkSafe(pk)
 				}
 			case <-quit:
 				ticker.Stop()
@@ -204,18 +197,18 @@
 /*
 setPkSafe Safely stores the Public Key (via a Write Lock)
 */
-func setPkSafe(pk *rsa.PublicKey) {
-	rwMutex.Lock()
-	gPkey = pk
-	rwMutex.Unlock()
+func (a *oauth) setPkSafe(pk *rsa.PublicKey) {
+	a.rwMutex.Lock()
+	a.gPkey = pk
+	a.rwMutex.Unlock()
 }
 
 /*
 getPkSafe returns the stored key (via a read lock)
 */
-func getPkSafe() *rsa.PublicKey {
-	rwMutex.RLock()
-	pk := gPkey
-	rwMutex.RUnlock()
+func (a *oauth) getPkSafe() *rsa.PublicKey {
+	a.rwMutex.RLock()
+	pk := a.gPkey
+	a.rwMutex.RUnlock()
 	return pk
 }
diff --git a/scaffold_test.go b/scaffold_test.go
index 1b7a145..b2007b7 100644
--- a/scaffold_test.go
+++ b/scaffold_test.go
@@ -6,23 +6,28 @@
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/julienschmidt/httprouter"
 	"io/ioutil"
 	"net"
 	"net/http"
-	"os"
 	"strings"
 	"sync/atomic"
 	"time"
 
+	"github.com/SermoDigital/jose/crypto"
+	"github.com/SermoDigital/jose/jws"
+	"github.com/julienschmidt/httprouter"
+
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 )
 
+const (
+	validJWTSigner   = "https://raw.githubusercontent.com/30x/goscaffold/master/testkeys/jwtcert.json"
+	invalidJWTSigner = "https://raw.githubusercontent.com/30x/goscaffold/master/testkeys/notfound.json"
+)
+
 var (
-	dbURL  string
-	ssoURL string
-	bToken string
+	dbURL string
 )
 
 var insecureClient = &http.Client{
@@ -32,21 +37,6 @@
 		},
 	},
 }
-var _ = BeforeSuite(func() {
-	ssoURL = os.Getenv("TEST_SSO_URL")
-	bToken = os.Getenv("BEARER_JWT_TOKEN")
-	if ssoURL == "" || bToken == "" {
-		fmt.Println("Tests aborted: TEST_SSO_URL/BEARER_JWT_TOKEN not set\n")
-		fmt.Println("Example:")
-		fmt.Println("TEST_SSO_URL=https://login.e2e.apigee.net/token_key")
-		fmt.Println("BEARER_JWT_TOKEN=eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIwMDgwNWNlYi0yNzI5LTQ2OTgtYWNiMy1jNTRkZmIzMWM4MjEiLCJzdWIiO\n")
-		fmt.Println("NOTE:-")
-		fmt.Println("BEARER_JWT_TOKEN can be gotten by `get_token -u user@apigee.com:password`")
-		fmt.Println("get_token download, SSO_LOGIN_URL setup details are at https://apigeesc.atlassian.net/wiki/display/EH/get_token\n")
-		Fail("Please set Environment variables as expected")
-	}
-
-})
 
 var _ = Describe("Scaffold Tests", func() {
 	It("Validate framework", func() {
@@ -396,45 +386,61 @@
 		Expect(scaf).ShouldNot(BeNil())
 		err := scaf.Open()
 		Expect(err).Should(Succeed())
-		oauth := scaf.CreateOAuth(ssoURL)
+		oauth := scaf.CreateOAuth(validJWTSigner)
 		Expect(oauth).ShouldNot(BeNil())
 		go func() {
 			fmt.Fprintf(GinkgoWriter, "Gonna listen on %s\n", scaf.InsecureAddress())
 			router.GET(oauth.SSOHandler("/foobar/:param1/:param2", buslogicHandler))
 			scaf.Listen(router)
 		}()
-		Eventually(func() bool {
-			req, err := http.NewRequest("GET",
+
+		Eventually(func() int {
+			req, reqerr := http.NewRequest("GET",
 				"http://"+scaf.InsecureAddress()+"/foobar/xyz/123", nil)
-			if err != nil {
-				return false
-			}
-			req.Header.Set("Authorization", "Bearer "+bToken)
+			Expect(reqerr).Should(Succeed())
+			req.Header.Set("Authorization", "Bearer "+string(createJWT()))
 			client := &http.Client{}
-			resp, err := client.Do(req)
-			Expect(err).Should(Succeed())
+			resp, reqerr := client.Do(req)
+			Expect(reqerr).Should(Succeed())
 			defer resp.Body.Close()
-			Expect(resp.StatusCode).To(Equal(http.StatusOK))
+			return resp.StatusCode
+		}, 2*time.Second).Should(Equal(200))
 
-			return true
-		}, 1*time.Second).Should(BeTrue())
+		req, err := http.NewRequest("GET",
+			"http://"+scaf.InsecureAddress()+"/foobar/xyz/123", nil)
+		Expect(err).Should(Succeed())
+		req.Header.Set("Authorization", "Bearer DEADBEEF")
+		client := &http.Client{}
+		resp, err := client.Do(req)
+		Expect(err).Should(Succeed())
+		defer resp.Body.Close()
+		Expect(resp.StatusCode).Should(Equal(400))
+	})
 
-		Eventually(func() bool {
-			req, err := http.NewRequest("GET",
-				"http://"+scaf.InsecureAddress()+"/foobar/xyz/123", nil)
-			if err != nil {
-				return false
-			}
-			req.Header.Set("Authorization", "Bearer DEADBEEF")
-			client := &http.Client{}
-			resp, err := client.Do(req)
-			Expect(err).Should(Succeed())
-			defer resp.Body.Close()
-			Expect(resp.StatusCode).To(Equal(http.StatusBadRequest))
+	It("SSO handler validation bad public key", func() {
+		router := httprouter.New()
+		Expect(router).ShouldNot(BeNil())
+		scaf := CreateHTTPScaffold()
+		Expect(scaf).ShouldNot(BeNil())
+		err := scaf.Open()
+		Expect(err).Should(Succeed())
+		oauth := scaf.CreateOAuth(invalidJWTSigner)
+		Expect(oauth).ShouldNot(BeNil())
+		go func() {
+			fmt.Fprintf(GinkgoWriter, "Gonna listen on %s\n", scaf.InsecureAddress())
+			router.GET(oauth.SSOHandler("/foobar/:param1/:param2", buslogicHandler))
+			scaf.Listen(router)
+		}()
 
-			return true
-		}, 1*time.Second).Should(BeTrue())
-
+		req, err := http.NewRequest("GET",
+			"http://"+scaf.InsecureAddress()+"/foobar/xyz/123", nil)
+		Expect(err).Should(Succeed())
+		req.Header.Set("Authorization", "Bearer "+string(createJWT()))
+		client := &http.Client{}
+		resp, err := client.Do(req)
+		Expect(err).Should(Succeed())
+		defer resp.Body.Close()
+		Expect(resp.StatusCode).Should(Equal(400))
 	})
 
 	It("Get stack trace", func() {
@@ -443,6 +449,22 @@
 		Expect(b.Len()).ShouldNot(BeZero())
 	})
 
+	It("Verify JWT creation", func() {
+		// Ensure that our logic in this test for creating a JWT really works
+		jwt := createJWT()
+		fmt.Fprintf(GinkgoWriter, "JWT: %s\n", string(jwt))
+
+		certBytes, err := ioutil.ReadFile("./testkeys/jwtcert.pem")
+		Expect(err).Should(Succeed())
+		cert, err := crypto.ParseRSAPublicKeyFromPEM(certBytes)
+		Expect(err).Should(Succeed())
+
+		parsedJwt, err := jws.ParseJWT(jwt)
+		Expect(err).Should(Succeed())
+
+		err = parsedJwt.Validate(cert, crypto.SigningMethodRS256)
+		Expect(err).Should(Succeed())
+	})
 })
 
 func buslogicHandler(w http.ResponseWriter, r *http.Request) {
@@ -566,3 +588,24 @@
 	}
 	return ""
 }
+
+func createJWT() []byte {
+	keyBytes, err := ioutil.ReadFile("./testkeys/jwtkey.pem")
+	Expect(err).Should(Succeed())
+	pk, err := crypto.ParseRSAPrivateKeyFromPEM(keyBytes)
+	Expect(err).Should(Succeed())
+
+	claims := jws.Claims{}
+	now := time.Now()
+	claims.SetAudience("http://github.com/30x/goscaffold")
+	claims.SetIssuer("http://github.com/30x/goscaffold")
+	claims.SetSubject("http://github.com/30x/goscaffold")
+	claims.SetIssuedAt(now)
+	claims.SetNotBefore(now)
+	claims.SetExpiration(now.Add(time.Hour))
+	jwt := jws.NewJWT(claims, crypto.SigningMethodRS256)
+
+	rawJwt, err := jwt.Serialize(pk)
+	Expect(err).Should(Succeed())
+	return rawJwt
+}