diff --git a/glide.lock b/glide.lock
index 80945c7b76f74257b2801f0d656c4bf4c80d70ef..d628c6e8fc5cd812584719d989f89b988de2d599 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,10 +1,10 @@
-hash: a453b9008bef3edc06f6df648bc40048ab387bf03a3e9127cdccb817569d518e
-updated: 2016-08-27T08:50:06.025458672-07:00
+hash: 5007c2ed2a8d71321949be6b1a525bdb7b827e7b7a5db711a308ade97e79a942
+updated: 2016-11-23T19:49:41.488352648+03:00
 imports:
 - name: github.com/andybalholm/cascadia
   version: 6122e68c2642b7b75c538a63b15168c6c80fb757
 - name: github.com/coreos/go-oidc
-  version: 9fae754a41cbdc3be9cb97a180eb323b625db614
+  version: d3e23e1446a65c7de3d36ed8b6dda1eb224c9194
   subpackages:
   - http
   - jose
@@ -61,8 +61,8 @@ imports:
 - name: golang.org/x/net
   version: dfe268fd2bb5c793f4c083803609fce9806c6f80
   subpackages:
-  - html
   - context
+  - html
   - html/atom
 - name: google.golang.org/api
   version: d3edb0282bde692467788c50070a9211afe75cf3
diff --git a/glide.yaml b/glide.yaml
index 93efdb109b70b9403b095588cf14557b7fcbd2ac..59f6a975c7453a4ec514e01b0e6ba1fc3d58c351 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -5,7 +5,7 @@ import:
 - package: github.com/andybalholm/cascadia
   version: 6122e68c2642b7b75c538a63b15168c6c80fb757
 - package: github.com/coreos/go-oidc
-  version: 9fae754a41cbdc3be9cb97a180eb323b625db614
+  version: d3e23e1446a65c7de3d36ed8b6dda1eb224c9194
   subpackages:
   - http
   - jose
diff --git a/server/http_test.go b/server/http_test.go
index fc5b2acce7756cddd96886a99837cc36ca042a1a..7d95970c6dcc139bf2a0a4e77db23459896c1a49 100644
--- a/server/http_test.go
+++ b/server/http_test.go
@@ -810,7 +810,7 @@ func TestHandleKeysFunc(t *testing.T) {
 		t.Fatalf("Incorrect headers: want=%#v got=%#v", wantHeader, gotHeader)
 	}
 
-	wantBody := `{"keys":[{"kid":"1234","kty":"RSA","alg":"RS256","use":"sig","e":"AQAB","n":"FE9chh46rg=="},{"kid":"5678","kty":"RSA","alg":"RS256","use":"sig","e":"AQAB","n":"BGKVohEShg=="}]}`
+	wantBody := `{"keys":[{"kid":"1234","kty":"RSA","alg":"RS256","use":"sig","e":"AQAB","n":"FE9chh46rg"},{"kid":"5678","kty":"RSA","alg":"RS256","use":"sig","e":"AQAB","n":"BGKVohEShg"}]}`
 	gotBody := w.Body.String()
 	if wantBody != gotBody {
 		t.Fatalf("Incorrect body: want=%s got=%s", wantBody, gotBody)
diff --git a/vendor/github.com/coreos/go-oidc/.travis.yml b/vendor/github.com/coreos/go-oidc/.travis.yml
index 6d488ac64c976911124bd9a9f7418870e0a6f465..fb89294c6b282d8954b27fcadf966c171d4c8901 100644
--- a/vendor/github.com/coreos/go-oidc/.travis.yml
+++ b/vendor/github.com/coreos/go-oidc/.travis.yml
@@ -1,14 +1,13 @@
 language: go
 
 go:
-  - 1.4.3
-  - 1.5.4
-  - 1.6.1
-  - 1.7
+  - 1.7.3
+  - 1.6.3
 
 install:
- - go get -v -t ./...
+ - go get -v -t github.com/coreos/go-oidc
  - go get golang.org/x/tools/cmd/cover
+ - go get github.com/golang/lint/golint
 
 script:
  - ./test
diff --git a/vendor/github.com/coreos/go-oidc/README.md b/vendor/github.com/coreos/go-oidc/README.md
index ebffc8c1d03508154d8c8e3dac0f2f8e0a3d4d7c..2a5c13e573b1d1a7fcf11a34a89a3f0d441cf4f5 100644
--- a/vendor/github.com/coreos/go-oidc/README.md
+++ b/vendor/github.com/coreos/go-oidc/README.md
@@ -3,13 +3,70 @@
 [![GoDoc](https://godoc.org/github.com/coreos/go-oidc?status.svg)](https://godoc.org/github.com/coreos/go-oidc)
 [![Build Status](https://travis-ci.org/coreos/go-oidc.png?branch=master)](https://travis-ci.org/coreos/go-oidc)
 
-go-oidc provides a comprehensive collection of golang libraries for other projects to implement [OpenID Connect (OIDC)][oidc] server and client components.
+## OpenID Connect support for Go
 
-[oidc]: http://openid.net/connect
+This package enables OpenID Connect support for the [golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2) package.
 
-## package documentation
+```go
+provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
+if err != nil {
+    // handle error
+}
 
-- [github.com/coreos/go-oidc/oidc](http://godoc.org/github.com/coreos/go-oidc/oidc) - OIDC client- and server-related components
-- [github.com/coreos/go-oidc/oauth2](http://godoc.org/github.com/coreos/go-oidc/oauth2) - OAuth2-specific code needed by the OIDC components
-- [github.com/coreos/go-oidc/jose](http://godoc.org/github.com/coreos/go-oidc/jose) - Javascript Object Signing and Encryption (JOSE) object ([JWS](https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41), [JWK](https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41)) generation, validation and serialization
-- [github.com/coreos/go-oidc/key](http://godoc.org/github.com/coreos/go-oidc/key) - RSA key management for OIDC components
+// Configure an OpenID Connect aware OAuth2 client.
+oauth2Config := oauth2.Config{
+    ClientID:     clientID,
+    ClientSecret: clientSecret,
+    RedirectURL:  redirectURL,
+
+    // Discovery returns the OAuth2 endpoints.
+    Endpoint: provider.Endpoint(),
+
+    // "openid" is a required scope for OpenID Connect flows.
+    Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
+}
+```
+
+OAuth2 redirects are unchanged.
+
+```go
+func handleRedirect(w http.ResponseWriter, r *http.Request) {
+    http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
+}
+```
+
+The on responses, the provider can be used to verify ID Tokens.
+
+```go
+var verifier = provider.Verifier()
+
+func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
+    // Verify state and errors.
+
+    oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
+    if err != nil {
+        // handle error
+    }
+
+    // Extract the ID Token from OAuth2 token.
+    rawIDToken, ok := oauth2Token.Extra("id_token").(string)
+    if !ok {
+        // handle missing token
+    }
+
+    // Parse and verify ID Token payload.
+    idToken, err := verifier.Verify(ctx, rawIDToken)
+    if err != nil {
+        // handle error
+    }
+
+    // Extract custom claims
+    var claims struct {
+        Email    string `json:"email"`
+        Verified bool   `json:"email_verified"`
+    }
+    if err := idToken.Claims(&claims); err != nil {
+        // handle error
+    }
+}
+```
diff --git a/vendor/github.com/coreos/go-oidc/build b/vendor/github.com/coreos/go-oidc/build
deleted file mode 100755
index 6f9f2d7d60e10a1b9fabcce0341d10b4221760ea..0000000000000000000000000000000000000000
--- a/vendor/github.com/coreos/go-oidc/build
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash -e
-
-GOBUILD="go build -a -installsuffix netgo -ldflags '-s'"
-
-echo "building bin/oidc-example-app..."
-${GOBUILD} -o bin/oidc-example-app github.com/coreos/go-oidc/example/app
-echo "building bin/oidc-example-cli..."
-${GOBUILD} -o bin/oidc-example-cli github.com/coreos/go-oidc/example/cli
-echo "done"
diff --git a/vendor/github.com/coreos/go-oidc/example/README.md b/vendor/github.com/coreos/go-oidc/example/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..765a4ea0cb6185d8cb8f91d10753a552cb41e63f
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/example/README.md
@@ -0,0 +1,21 @@
+# Examples
+
+These are example uses of the oidc package. Each requires a Google account and the client ID and secret of a registered OAuth2 application. To create one:
+
+1. Visit your [Google Developer Console][google-developer-console].
+2. Click "Credentials" on the left column.
+3. Click the "Create credentials" button followed by "OAuth client ID".
+4. Select "Web application" and add "http://127.0.0.1:5556/auth/google/callback" as an authorized redirect URI.
+5. Click create and add the printed client ID and secret to your environment using the following variables:
+
+```
+GOOGLE_OAUTH2_CLIENT_ID
+GOOGLE_OAUTH2_CLIENT_SECRET
+```
+
+Finally run the examples using the Go tool and navigate to http://127.0.0.1:5556.
+
+```
+go run ./examples/idtoken/app.go
+```
+[google-developer-console]: https://console.developers.google.com/apis/dashboard
diff --git a/vendor/github.com/coreos/go-oidc/example/app/main.go b/vendor/github.com/coreos/go-oidc/example/app/main.go
deleted file mode 100644
index 9e382d964a92bf3349336db46559a250e86fb3ff..0000000000000000000000000000000000000000
--- a/vendor/github.com/coreos/go-oidc/example/app/main.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package main
-
-import (
-	"encoding/json"
-	"flag"
-	"fmt"
-	"log"
-	"net"
-	"net/http"
-	"net/url"
-	"os"
-	"time"
-
-	"github.com/coreos/go-oidc/oidc"
-)
-
-var (
-	pathCallback      = "/oauth2callback"
-	defaultListenHost = "127.0.0.1:5555"
-)
-
-func main() {
-	log.SetOutput(os.Stderr)
-
-	fs := flag.NewFlagSet("oidc-example-app", flag.ExitOnError)
-	listen := fs.String("listen", defaultListenHost, "serve traffic on this address (<host>:<port>)")
-	redirectURL := fs.String("redirect-url", fmt.Sprintf("http://%s%s", defaultListenHost, pathCallback), "")
-	clientID := fs.String("client-id", "", "")
-	clientSecret := fs.String("client-secret", "", "")
-	discovery := fs.String("discovery", "https://accounts.google.com", "")
-
-	if err := fs.Parse(os.Args[1:]); err != nil {
-		log.Fatalf("failed parsing flags: %v", err)
-	}
-
-	if *clientID == "" {
-		log.Fatal("--client-id must be set")
-	}
-
-	if *clientSecret == "" {
-		log.Fatal("--client-secret must be set")
-	}
-
-	_, _, err := net.SplitHostPort(*listen)
-	if err != nil {
-		log.Fatalf("unable to parse host:port from --listen flag: %v", err)
-	}
-
-	cc := oidc.ClientCredentials{
-		ID:     *clientID,
-		Secret: *clientSecret,
-	}
-
-	log.Printf("fetching provider config from %s...", *discovery)
-
-	var cfg oidc.ProviderConfig
-	for {
-		cfg, err = oidc.FetchProviderConfig(http.DefaultClient, *discovery)
-		if err == nil {
-			break
-		}
-
-		sleep := 3 * time.Second
-		log.Printf("failed fetching provider config, trying again in %v: %v", sleep, err)
-		time.Sleep(sleep)
-	}
-
-	log.Printf("fetched provider config from %s: %#v", *discovery, cfg)
-
-	ccfg := oidc.ClientConfig{
-		ProviderConfig: cfg,
-		Credentials:    cc,
-		RedirectURL:    *redirectURL,
-	}
-
-	client, err := oidc.NewClient(ccfg)
-	if err != nil {
-		log.Fatalf("unable to create Client: %v", err)
-	}
-
-	client.SyncProviderConfig(*discovery)
-
-	redirectURLParsed, err := url.Parse(*redirectURL)
-	if err != nil {
-		log.Fatalf("unable to parse url from --redirect-url flag: %v", err)
-	}
-	hdlr := NewClientHandler(client, *redirectURLParsed)
-	httpsrv := &http.Server{
-		Addr:    fmt.Sprintf(*listen),
-		Handler: hdlr,
-	}
-
-	log.Printf("binding to %s...", httpsrv.Addr)
-	log.Fatal(httpsrv.ListenAndServe())
-}
-
-func NewClientHandler(c *oidc.Client, cbURL url.URL) http.Handler {
-	mux := http.NewServeMux()
-	mux.HandleFunc("/", handleIndex)
-	mux.HandleFunc("/login", handleLoginFunc(c))
-	mux.HandleFunc(pathCallback, handleCallbackFunc(c))
-	return mux
-}
-
-func handleIndex(w http.ResponseWriter, r *http.Request) {
-	w.Write([]byte("<a href='/login'>login</a>"))
-}
-
-func handleLoginFunc(c *oidc.Client) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		oac, err := c.OAuthClient()
-		if err != nil {
-			panic("unable to proceed")
-		}
-
-		u, err := url.Parse(oac.AuthCodeURL("", "", ""))
-		if err != nil {
-			panic("unable to proceed")
-		}
-		http.Redirect(w, r, u.String(), http.StatusFound)
-	}
-}
-
-func handleCallbackFunc(c *oidc.Client) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		code := r.URL.Query().Get("code")
-		if code == "" {
-			writeError(w, http.StatusBadRequest, "code query param must be set")
-			return
-		}
-
-		tok, err := c.ExchangeAuthCode(code)
-		if err != nil {
-			writeError(w, http.StatusBadRequest, fmt.Sprintf("unable to verify auth code with issuer: %v", err))
-			return
-		}
-
-		claims, err := tok.Claims()
-		if err != nil {
-			writeError(w, http.StatusBadRequest, fmt.Sprintf("unable to construct claims: %v", err))
-			return
-		}
-
-		s := fmt.Sprintf("claims: %v", claims)
-		w.Write([]byte(s))
-	}
-}
-
-func writeError(w http.ResponseWriter, code int, msg string) {
-	e := struct {
-		Error string `json:"error"`
-	}{
-		Error: msg,
-	}
-	b, err := json.Marshal(e)
-	if err != nil {
-		log.Printf("Failed marshaling %#v to JSON: %v", e, err)
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	w.Write(b)
-}
diff --git a/vendor/github.com/coreos/go-oidc/example/cli/main.go b/vendor/github.com/coreos/go-oidc/example/cli/main.go
deleted file mode 100644
index 2b6f330f5a4818e838fdfd47c821e96adf59bfef..0000000000000000000000000000000000000000
--- a/vendor/github.com/coreos/go-oidc/example/cli/main.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package main
-
-import (
-	"flag"
-	"fmt"
-	"net/http"
-	"os"
-	"time"
-
-	"github.com/coreos/go-oidc/oidc"
-)
-
-func main() {
-	fs := flag.NewFlagSet("oidc-example-cli", flag.ExitOnError)
-	clientID := fs.String("client-id", "", "")
-	clientSecret := fs.String("client-secret", "", "")
-	discovery := fs.String("discovery", "https://accounts.google.com", "")
-
-	if err := fs.Parse(os.Args[1:]); err != nil {
-		fmt.Fprintln(os.Stderr, err.Error())
-		os.Exit(1)
-	}
-
-	if *clientID == "" {
-		fmt.Println("--client-id must be set")
-		os.Exit(2)
-	}
-
-	if *clientSecret == "" {
-		fmt.Println("--client-secret must be set")
-		os.Exit(2)
-	}
-
-	cc := oidc.ClientCredentials{
-		ID:     *clientID,
-		Secret: *clientSecret,
-	}
-
-	fmt.Printf("fetching provider config from %s...", *discovery)
-
-	// NOTE: A real CLI would cache this config, or provide it via flags/config file.
-	var cfg oidc.ProviderConfig
-	var err error
-	for {
-		cfg, err = oidc.FetchProviderConfig(http.DefaultClient, *discovery)
-		if err == nil {
-			break
-		}
-
-		sleep := 1 * time.Second
-		fmt.Printf("failed fetching provider config, trying again in %v: %v\n", sleep, err)
-		time.Sleep(sleep)
-	}
-
-	fmt.Printf("fetched provider config from %s: %#v\n\n", *discovery, cfg)
-
-	ccfg := oidc.ClientConfig{
-		ProviderConfig: cfg,
-		Credentials:    cc,
-	}
-
-	client, err := oidc.NewClient(ccfg)
-	if err != nil {
-		fmt.Printf("unable to create Client: %v\n", err)
-		os.Exit(1)
-	}
-
-	tok, err := client.ClientCredsToken([]string{"openid"})
-	if err != nil {
-		fmt.Printf("unable to verify auth code with issuer: %v\n", err)
-		os.Exit(1)
-	}
-
-	fmt.Printf("got jwt: %v\n\n", tok.Encode())
-
-	claims, err := tok.Claims()
-	if err != nil {
-		fmt.Printf("unable to construct claims: %v\n", err)
-		os.Exit(1)
-	}
-
-	fmt.Printf("got claims %#v...\n", claims)
-}
diff --git a/vendor/github.com/coreos/go-oidc/example/idtoken/app.go b/vendor/github.com/coreos/go-oidc/example/idtoken/app.go
new file mode 100644
index 0000000000000000000000000000000000000000..fe5859e9e94bf214861dcac21c5e66160e9e4fa5
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/example/idtoken/app.go
@@ -0,0 +1,89 @@
+/*
+This is an example application to demonstrate parsing an ID Token.
+*/
+package main
+
+import (
+	"encoding/json"
+	"log"
+	"net/http"
+	"os"
+
+	oidc "github.com/coreos/go-oidc"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
+)
+
+var (
+	clientID     = os.Getenv("GOOGLE_OAUTH2_CLIENT_ID")
+	clientSecret = os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")
+)
+
+func main() {
+	ctx := context.Background()
+
+	provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
+	if err != nil {
+		log.Fatal(err)
+	}
+	verifier := provider.Verifier()
+
+	config := oauth2.Config{
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		Endpoint:     provider.Endpoint(),
+		RedirectURL:  "http://127.0.0.1:5556/auth/google/callback",
+		Scopes:       []string{oidc.ScopeOpenID, "profile", "email"},
+	}
+
+	state := "foobar" // Don't do this in production.
+
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		http.Redirect(w, r, config.AuthCodeURL(state), http.StatusFound)
+	})
+
+	http.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) {
+		if r.URL.Query().Get("state") != state {
+			http.Error(w, "state did not match", http.StatusBadRequest)
+			return
+		}
+
+		oauth2Token, err := config.Exchange(ctx, r.URL.Query().Get("code"))
+		if err != nil {
+			http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
+			return
+		}
+		rawIDToken, ok := oauth2Token.Extra("id_token").(string)
+		if !ok {
+			http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError)
+			return
+		}
+		idToken, err := verifier.Verify(ctx, rawIDToken)
+		if err != nil {
+			http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		oauth2Token.AccessToken = "*REDACTED*"
+
+		resp := struct {
+			OAuth2Token   *oauth2.Token
+			IDTokenClaims *json.RawMessage // ID Token payload is just JSON.
+		}{oauth2Token, new(json.RawMessage)}
+
+		if err := idToken.Claims(&resp.IDTokenClaims); err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		data, err := json.MarshalIndent(resp, "", "    ")
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		w.Write(data)
+	})
+
+	log.Printf("listening on http://%s/", "127.0.0.1:5556")
+	log.Fatal(http.ListenAndServe("127.0.0.1:5556", nil))
+}
diff --git a/vendor/github.com/coreos/go-oidc/example/nonce/app.go b/vendor/github.com/coreos/go-oidc/example/nonce/app.go
new file mode 100644
index 0000000000000000000000000000000000000000..8a78db9fae1bf83b2fb1087bb9f548fa0faffb35
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/example/nonce/app.go
@@ -0,0 +1,104 @@
+/*
+This is an example application to demonstrate verifying an ID Token with a nonce.
+*/
+package main
+
+import (
+	"encoding/json"
+	"errors"
+	"log"
+	"net/http"
+	"os"
+
+	oidc "github.com/coreos/go-oidc"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
+)
+
+var (
+	clientID     = os.Getenv("GOOGLE_OAUTH2_CLIENT_ID")
+	clientSecret = os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")
+)
+
+const appNonce = "a super secret nonce"
+
+// Create a nonce source.
+type nonceSource struct{}
+
+func (n nonceSource) ClaimNonce(nonce string) error {
+	if nonce != appNonce {
+		return errors.New("unregonized nonce")
+	}
+	return nil
+}
+
+func main() {
+	ctx := context.Background()
+
+	provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Use the nonce source to create a custom ID Token verifier.
+	nonceEnabledVerifier := provider.Verifier(oidc.VerifyNonce(nonceSource{}))
+
+	config := oauth2.Config{
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		Endpoint:     provider.Endpoint(),
+		RedirectURL:  "http://127.0.0.1:5556/auth/google/callback",
+		Scopes:       []string{oidc.ScopeOpenID, "profile", "email"},
+	}
+
+	state := "foobar" // Don't do this in production.
+
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		http.Redirect(w, r, config.AuthCodeURL(state, oidc.Nonce(appNonce)), http.StatusFound)
+	})
+
+	http.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) {
+		if r.URL.Query().Get("state") != state {
+			http.Error(w, "state did not match", http.StatusBadRequest)
+			return
+		}
+
+		oauth2Token, err := config.Exchange(ctx, r.URL.Query().Get("code"))
+		if err != nil {
+			http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		rawIDToken, ok := oauth2Token.Extra("id_token").(string)
+		if !ok {
+			http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError)
+			return
+		}
+		// Verify the ID Token signature and nonce.
+		idToken, err := nonceEnabledVerifier.Verify(ctx, rawIDToken)
+		if err != nil {
+			http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		resp := struct {
+			OAuth2Token   *oauth2.Token
+			IDTokenClaims *json.RawMessage // ID Token payload is just JSON.
+		}{oauth2Token, new(json.RawMessage)}
+
+		if err := idToken.Claims(&resp.IDTokenClaims); err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		data, err := json.MarshalIndent(resp, "", "    ")
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		w.Write(data)
+	})
+
+	log.Printf("listening on http://%s/", "127.0.0.1:5556")
+	log.Fatal(http.ListenAndServe("127.0.0.1:5556", nil))
+}
diff --git a/vendor/github.com/coreos/go-oidc/example/userinfo/app.go b/vendor/github.com/coreos/go-oidc/example/userinfo/app.go
new file mode 100644
index 0000000000000000000000000000000000000000..0039088ed173565b03cc8a36bbf1633885b26097
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/example/userinfo/app.go
@@ -0,0 +1,76 @@
+/*
+This is an example application to demonstrate querying the user info endpoint.
+*/
+package main
+
+import (
+	"encoding/json"
+	"log"
+	"net/http"
+	"os"
+
+	oidc "github.com/coreos/go-oidc"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
+)
+
+var (
+	clientID     = os.Getenv("GOOGLE_OAUTH2_CLIENT_ID")
+	clientSecret = os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")
+)
+
+func main() {
+	ctx := context.Background()
+
+	provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
+	if err != nil {
+		log.Fatal(err)
+	}
+	config := oauth2.Config{
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		Endpoint:     provider.Endpoint(),
+		RedirectURL:  "http://127.0.0.1:5556/auth/google/callback",
+		Scopes:       []string{oidc.ScopeOpenID, "profile", "email"},
+	}
+
+	state := "foobar" // Don't do this in production.
+
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		http.Redirect(w, r, config.AuthCodeURL(state), http.StatusFound)
+	})
+
+	http.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) {
+		if r.URL.Query().Get("state") != state {
+			http.Error(w, "state did not match", http.StatusBadRequest)
+			return
+		}
+
+		oauth2Token, err := config.Exchange(ctx, r.URL.Query().Get("code"))
+		if err != nil {
+			http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		userInfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token))
+		if err != nil {
+			http.Error(w, "Failed to get userinfo: "+err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		resp := struct {
+			OAuth2Token *oauth2.Token
+			UserInfo    *oidc.UserInfo
+		}{oauth2Token, userInfo}
+		data, err := json.MarshalIndent(resp, "", "    ")
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		w.Write(data)
+	})
+
+	log.Printf("listening on http://%s/", "127.0.0.1:5556")
+	log.Fatal(http.ListenAndServe("127.0.0.1:5556", nil))
+}
diff --git a/vendor/github.com/coreos/go-oidc/gen.go b/vendor/github.com/coreos/go-oidc/gen.go
new file mode 100644
index 0000000000000000000000000000000000000000..0c798f6730fae05cb29aacc1bc09fe1471e52476
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/gen.go
@@ -0,0 +1,150 @@
+// +build ignore
+
+// This file is used to generate keys for tests.
+
+package main
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"crypto/rsa"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"text/template"
+
+	jose "gopkg.in/square/go-jose.v2"
+)
+
+type key struct {
+	name string
+	new  func() (crypto.Signer, error)
+}
+
+var keys = []key{
+	{
+		"ECDSA_256", func() (crypto.Signer, error) {
+			return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+		},
+	},
+	{
+		"ECDSA_384", func() (crypto.Signer, error) {
+			return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
+		},
+	},
+	{
+		"ECDSA_521", func() (crypto.Signer, error) {
+			return ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
+		},
+	},
+	{
+		"RSA_1024", func() (crypto.Signer, error) {
+			return rsa.GenerateKey(rand.Reader, 1024)
+		},
+	},
+	{
+		"RSA_2048", func() (crypto.Signer, error) {
+			return rsa.GenerateKey(rand.Reader, 2048)
+		},
+	},
+	{
+		"RSA_4096", func() (crypto.Signer, error) {
+			return rsa.GenerateKey(rand.Reader, 4096)
+		},
+	},
+}
+
+func newJWK(k key, prefix, ident string) (privBytes, pubBytes []byte, err error) {
+	priv, err := k.new()
+	if err != nil {
+		return nil, nil, fmt.Errorf("generate %s: %v", k.name, err)
+	}
+	pub := priv.Public()
+
+	privKey := &jose.JSONWebKey{Key: priv}
+	thumbprint, err := privKey.Thumbprint(crypto.SHA256)
+	if err != nil {
+		return nil, nil, fmt.Errorf("computing thumbprint: %v", err)
+	}
+
+	keyID := hex.EncodeToString(thumbprint)
+	privKey.KeyID = keyID
+	pubKey := &jose.JSONWebKey{Key: pub, KeyID: keyID}
+
+	privBytes, err = json.MarshalIndent(privKey, prefix, ident)
+	if err != nil {
+		return
+	}
+	pubBytes, err = json.MarshalIndent(pubKey, prefix, ident)
+	return
+}
+
+type keyData struct {
+	Name string
+	Priv string
+	Pub  string
+}
+
+var tmpl = template.Must(template.New("").Parse(`// +build !golint
+
+// This file contains statically created JWKs for tests created by gen.go
+
+package oidc
+
+import (
+	"encoding/json"
+
+	jose "gopkg.in/square/go-jose.v2"
+)
+
+func mustLoadJWK(s string) jose.JSONWebKey {
+	var jwk jose.JSONWebKey
+	if err := json.Unmarshal([]byte(s), &jwk); err != nil {
+		panic(err)
+	}
+	return jwk
+}
+
+var (
+{{- range $i, $key := .Keys }}
+	testKey{{ $key.Name }} = mustLoadJWK(` + "`" + `{{ $key.Pub }}` + "`" + `)
+	testKey{{ $key.Name }}_Priv = mustLoadJWK(` + "`" + `{{ $key.Priv }}` + "`" + `)
+{{ end -}}
+)
+`))
+
+func main() {
+	var tmplData struct {
+		Keys []keyData
+	}
+	for _, k := range keys {
+		for i := 0; i < 4; i++ {
+			log.Printf("generating %s", k.name)
+			priv, pub, err := newJWK(k, "\t", "\t")
+			if err != nil {
+				log.Fatal(err)
+			}
+			name := fmt.Sprintf("%s_%d", k.name, i)
+
+			tmplData.Keys = append(tmplData.Keys, keyData{
+				Name: name,
+				Priv: string(priv),
+				Pub:  string(pub),
+			})
+		}
+	}
+
+	buff := new(bytes.Buffer)
+	if err := tmpl.Execute(buff, tmplData); err != nil {
+		log.Fatalf("excuting template: %v", err)
+	}
+
+	if err := ioutil.WriteFile("jose_test.go", buff.Bytes(), 0644); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/vendor/github.com/coreos/go-oidc/http/doc.go b/vendor/github.com/coreos/go-oidc/http/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..5687e8b81b4bf014b3b202239b589af1bb3f3379
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/http/doc.go
@@ -0,0 +1,2 @@
+// Package http is DEPRECATED. Use net/http instead.
+package http
diff --git a/vendor/github.com/coreos/go-oidc/jose.go b/vendor/github.com/coreos/go-oidc/jose.go
new file mode 100644
index 0000000000000000000000000000000000000000..f2e6bf4322487a55bc66a3391c2cb0560989abd1
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose.go
@@ -0,0 +1,20 @@
+// +build !golint
+
+// Don't lint this file. We don't want to have to add a comment to each constant.
+
+package oidc
+
+const (
+	// JOSE asymmetric signing algorithm values as defined by RFC 7518
+	//
+	// see: https://tools.ietf.org/html/rfc7518#section-3.1
+	RS256 = "RS256" // RSASSA-PKCS-v1.5 using SHA-256
+	RS384 = "RS384" // RSASSA-PKCS-v1.5 using SHA-384
+	RS512 = "RS512" // RSASSA-PKCS-v1.5 using SHA-512
+	ES256 = "ES256" // ECDSA using P-256 and SHA-256
+	ES384 = "ES384" // ECDSA using P-384 and SHA-384
+	ES512 = "ES512" // ECDSA using P-521 and SHA-512
+	PS256 = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256
+	PS384 = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384
+	PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512
+)
diff --git a/vendor/github.com/coreos/go-oidc/jose/doc.go b/vendor/github.com/coreos/go-oidc/jose/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..b5e1321781c3c82411f45948dc8dc920872a26c8
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose/doc.go
@@ -0,0 +1,2 @@
+// Package jose is DEPRECATED. Use gopkg.in/square/go-jose.v2 instead.
+package jose
diff --git a/vendor/github.com/coreos/go-oidc/jose/jwk.go b/vendor/github.com/coreos/go-oidc/jose/jwk.go
index b7a8e235583ac0bc5a659e3f7df523dd36a72856..119f073ff7a91550e5777e9e5d70a1fa7107f7a6 100644
--- a/vendor/github.com/coreos/go-oidc/jose/jwk.go
+++ b/vendor/github.com/coreos/go-oidc/jose/jwk.go
@@ -104,7 +104,7 @@ func encodeExponent(e int) string {
 			break
 		}
 	}
-	return base64.URLEncoding.EncodeToString(b[idx:])
+	return base64.RawURLEncoding.EncodeToString(b[idx:])
 }
 
 // Turns a URL encoded modulus of a key into a big int.
@@ -119,7 +119,7 @@ func decodeModulus(n string) (*big.Int, error) {
 }
 
 func encodeModulus(n *big.Int) string {
-	return base64.URLEncoding.EncodeToString(n.Bytes())
+	return base64.RawURLEncoding.EncodeToString(n.Bytes())
 }
 
 // decodeBase64URLPaddingOptional decodes Base64 whether there is padding or not.
diff --git a/vendor/github.com/coreos/go-oidc/jose/jwt_test.go b/vendor/github.com/coreos/go-oidc/jose/jwt_test.go
index 3a1cd9ae156d2b7446538d86c9a5db43c7a837c2..74691769aad27099bebd5f8d8a556dfb0adc1114 100644
--- a/vendor/github.com/coreos/go-oidc/jose/jwt_test.go
+++ b/vendor/github.com/coreos/go-oidc/jose/jwt_test.go
@@ -53,7 +53,7 @@ func TestParseJWT(t *testing.T) {
 	}
 }
 
-func TestNewJWTHeaderTyp(t *testing.T) {
+func TestNewJWTHeaderType(t *testing.T) {
 	jwt, err := NewJWT(JOSEHeader{}, Claims{})
 	if err != nil {
 		t.Fatalf("Unexpected error: %v", err)
diff --git a/vendor/github.com/coreos/go-oidc/jose/sig_hmac.go b/vendor/github.com/coreos/go-oidc/jose/sig_hmac.go
deleted file mode 100644
index 34cd0e6cd90419a723f08610c12761e2524371f4..0000000000000000000000000000000000000000
--- a/vendor/github.com/coreos/go-oidc/jose/sig_hmac.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package jose
-
-import (
-	"crypto"
-	"crypto/hmac"
-	_ "crypto/sha256"
-	"errors"
-	"fmt"
-)
-
-type VerifierHMAC struct {
-	KeyID  string
-	Hash   crypto.Hash
-	Secret []byte
-}
-
-type SignerHMAC struct {
-	VerifierHMAC
-}
-
-func NewVerifierHMAC(jwk JWK) (*VerifierHMAC, error) {
-	if jwk.Alg != "" && jwk.Alg != "HS256" {
-		return nil, fmt.Errorf("unsupported key algorithm %q", jwk.Alg)
-	}
-
-	v := VerifierHMAC{
-		KeyID:  jwk.ID,
-		Secret: jwk.Secret,
-		Hash:   crypto.SHA256,
-	}
-
-	return &v, nil
-}
-
-func (v *VerifierHMAC) ID() string {
-	return v.KeyID
-}
-
-func (v *VerifierHMAC) Alg() string {
-	return "HS256"
-}
-
-func (v *VerifierHMAC) Verify(sig []byte, data []byte) error {
-	h := hmac.New(v.Hash.New, v.Secret)
-	h.Write(data)
-	// hmac.Equal compares two hmacs but does it in constant time to mitigating time
-	// based attacks. See #98
-	if !hmac.Equal(sig, h.Sum(nil)) {
-		return errors.New("invalid hmac signature")
-	}
-	return nil
-}
-
-func NewSignerHMAC(kid string, secret []byte) *SignerHMAC {
-	return &SignerHMAC{
-		VerifierHMAC: VerifierHMAC{
-			KeyID:  kid,
-			Secret: secret,
-			Hash:   crypto.SHA256,
-		},
-	}
-}
-
-func (s *SignerHMAC) Sign(data []byte) ([]byte, error) {
-	h := hmac.New(s.Hash.New, s.Secret)
-	h.Write(data)
-	return h.Sum(nil), nil
-}
diff --git a/vendor/github.com/coreos/go-oidc/jose/sig_hmac_test.go b/vendor/github.com/coreos/go-oidc/jose/sig_hmac_test.go
deleted file mode 100644
index 76c6722fd42d4487981d69084a603ca201432607..0000000000000000000000000000000000000000
--- a/vendor/github.com/coreos/go-oidc/jose/sig_hmac_test.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package jose
-
-import (
-	"bytes"
-	"encoding/base64"
-	"testing"
-)
-
-var hmacTestCases = []struct {
-	data  string
-	sig   string
-	jwk   JWK
-	valid bool
-	desc  string
-}{
-	{
-		"test",
-		"Aymga2LNFrM-tnkr6MYLFY2Jou46h2_Omogeu0iMCRQ=",
-		JWK{
-			ID:     "fake-key",
-			Alg:    "HS256",
-			Secret: []byte("secret"),
-		},
-		true,
-		"valid case",
-	},
-	{
-		"test",
-		"Aymga2LNFrM-tnkr6MYLFY2Jou46h2_Omogeu0iMCRQ=",
-		JWK{
-			ID:     "different-key",
-			Alg:    "HS256",
-			Secret: []byte("secret"),
-		},
-		true,
-		"invalid: different key, should not match",
-	},
-	{
-		"test sig and non-matching data",
-		"Aymga2LNFrM-tnkr6MYLFY2Jou46h2_Omogeu0iMCRQ=",
-		JWK{
-			ID:     "fake-key",
-			Alg:    "HS256",
-			Secret: []byte("secret"),
-		},
-		false,
-		"invalid: sig and data should not match",
-	},
-}
-
-func TestVerify(t *testing.T) {
-	for _, tt := range hmacTestCases {
-		v, err := NewVerifierHMAC(tt.jwk)
-		if err != nil {
-			t.Errorf("should construct hmac verifier. test: %s. err=%v", tt.desc, err)
-		}
-
-		decSig, _ := base64.URLEncoding.DecodeString(tt.sig)
-		err = v.Verify(decSig, []byte(tt.data))
-		if err == nil && !tt.valid {
-			t.Errorf("verify failure. test: %s. expected: invalid, actual: valid.", tt.desc)
-		}
-		if err != nil && tt.valid {
-			t.Errorf("verify failure. test: %s. expected: valid, actual: invalid. err=%v", tt.desc, err)
-		}
-	}
-}
-
-func TestSign(t *testing.T) {
-	for _, tt := range hmacTestCases {
-		s := NewSignerHMAC("test", tt.jwk.Secret)
-		sig, err := s.Sign([]byte(tt.data))
-		if err != nil {
-			t.Errorf("sign failure. test: %s. err=%v", tt.desc, err)
-		}
-
-		expSig, _ := base64.URLEncoding.DecodeString(tt.sig)
-		if tt.valid && !bytes.Equal(sig, expSig) {
-			t.Errorf("sign failure. test: %s. expected: %s, actual: %s.", tt.desc, tt.sig, base64.URLEncoding.EncodeToString(sig))
-		}
-		if !tt.valid && bytes.Equal(sig, expSig) {
-			t.Errorf("sign failure. test: %s. expected: invalid signature.", tt.desc)
-		}
-	}
-}
diff --git a/vendor/github.com/coreos/go-oidc/jose_test.go b/vendor/github.com/coreos/go-oidc/jose_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ae5ca0e5048c70f65ee889aea129d6d08cceae04
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose_test.go
@@ -0,0 +1,405 @@
+// +build !golint
+
+// This file contains statically created JWKs for tests created by gen.go
+
+package oidc
+
+import (
+	"encoding/json"
+
+	jose "gopkg.in/square/go-jose.v2"
+)
+
+func mustLoadJWK(s string) jose.JSONWebKey {
+	var jwk jose.JSONWebKey
+	if err := json.Unmarshal([]byte(s), &jwk); err != nil {
+		panic(err)
+	}
+	return jwk
+}
+
+var (
+	testKeyECDSA_256_0 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "bd06f3e11f523e310f8c2c8b20a892727fb558e0e23602312568b20a41e11188",
+		"crv": "P-256",
+		"x": "xK5N69f0-SAgWbjw2otcQeCGs3qqYMyqOWk4Os5Z_Xc",
+		"y": "AXSaOPcMklJY9UKZhkGzVevqhAIUEzE3cfZ8o-ML5xE"
+	}`)
+	testKeyECDSA_256_0_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "bd06f3e11f523e310f8c2c8b20a892727fb558e0e23602312568b20a41e11188",
+		"crv": "P-256",
+		"x": "xK5N69f0-SAgWbjw2otcQeCGs3qqYMyqOWk4Os5Z_Xc",
+		"y": "AXSaOPcMklJY9UKZhkGzVevqhAIUEzE3cfZ8o-ML5xE",
+		"d": "L7jynYt-fMRPqw1e9vgXCGTg4yhGU4tlLxiFyNVimG4"
+	}`)
+
+	testKeyECDSA_256_1 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "27ebd2f5bf6723e4de06caaab03703be458a37bf28a9fa748576a0826acb6ec2",
+		"crv": "P-256",
+		"x": "KZisP6wLCph4q6056jr7BH_asiX9RcLcS3HrNjdCpkw",
+		"y": "5DrW-kEge0sePHlKmh1d2kqd10r32JEW6eyyewy18j8"
+	}`)
+	testKeyECDSA_256_1_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "27ebd2f5bf6723e4de06caaab03703be458a37bf28a9fa748576a0826acb6ec2",
+		"crv": "P-256",
+		"x": "KZisP6wLCph4q6056jr7BH_asiX9RcLcS3HrNjdCpkw",
+		"y": "5DrW-kEge0sePHlKmh1d2kqd10r32JEW6eyyewy18j8",
+		"d": "r6CiIpv0icIq5U4LYO39nBDVhCHCLObDFYC5IG9Y8Hk"
+	}`)
+
+	testKeyECDSA_256_2 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "2c7d179db6006c90ece4d91f554791ff35156693c693102c00f66f473d15df17",
+		"crv": "P-256",
+		"x": "oDwcKp7SqgeRvycK5GgYjrlW4fbHn2Ybfd5iG7kDiPc",
+		"y": "qazib9UwdUdbHSFzdy_HN10xZEItLvufPw0v7nIJOWA"
+	}`)
+	testKeyECDSA_256_2_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "2c7d179db6006c90ece4d91f554791ff35156693c693102c00f66f473d15df17",
+		"crv": "P-256",
+		"x": "oDwcKp7SqgeRvycK5GgYjrlW4fbHn2Ybfd5iG7kDiPc",
+		"y": "qazib9UwdUdbHSFzdy_HN10xZEItLvufPw0v7nIJOWA",
+		"d": "p79U6biKrOyrKzg-i3C7FVJiqzlqBhYQqmyOiZ9bhVM"
+	}`)
+
+	testKeyECDSA_256_3 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "aba0a256999af470c8b2449103ae75b257d907da4d1cbfc73c1d45ab1098f544",
+		"crv": "P-256",
+		"x": "CKRYWVt1R7FzuJ43vEprfIzgB-KgIhRDhxLmd5ixiXY",
+		"y": "QCxTVmK31ee710OYkNqdqEgHH3rqRQNQj3Wyq0xtYq0"
+	}`)
+	testKeyECDSA_256_3_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "aba0a256999af470c8b2449103ae75b257d907da4d1cbfc73c1d45ab1098f544",
+		"crv": "P-256",
+		"x": "CKRYWVt1R7FzuJ43vEprfIzgB-KgIhRDhxLmd5ixiXY",
+		"y": "QCxTVmK31ee710OYkNqdqEgHH3rqRQNQj3Wyq0xtYq0",
+		"d": "c_WflwwUxT_B5izk4iET49qoob0RH5hccnEScfBS9Qk"
+	}`)
+
+	testKeyECDSA_384_0 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "bc271d0a68251171e0c5edfd384b32d154e1717af27c5089bda8fb598a474b7b",
+		"crv": "P-384",
+		"x": "FZEhxw06oB86xCAZCtKTfX3ze9tgxkdN199g-cWrpQTWF-2m6Blg1MN5D60Z9KoX",
+		"y": "EyWjZ46gLlqfoU08iv0zcDWut1nbfoUoTd-7La2VY4PqQeDSFrxPCOyplxFMGJIW"
+	}`)
+	testKeyECDSA_384_0_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "bc271d0a68251171e0c5edfd384b32d154e1717af27c5089bda8fb598a474b7b",
+		"crv": "P-384",
+		"x": "FZEhxw06oB86xCAZCtKTfX3ze9tgxkdN199g-cWrpQTWF-2m6Blg1MN5D60Z9KoX",
+		"y": "EyWjZ46gLlqfoU08iv0zcDWut1nbfoUoTd-7La2VY4PqQeDSFrxPCOyplxFMGJIW",
+		"d": "t6oJHJBH2rvH3uyQkK_JwoUEwE1QHjwTnGLSDMZbNEDrEaR8BBiAo3s0p8rzGz5-"
+	}`)
+
+	testKeyECDSA_384_1 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "58ef375d6e63cedc637fed59f4012a779cd749a83373237cf3972bba74ebd738",
+		"crv": "P-384",
+		"x": "Gj3zkiRR4RCJb0Tke3lD2spG2jzuXgX50fEwDZTRjlQIzz3Rc96Fw32FCSDectxQ",
+		"y": "6alqN7ilqJAmqCU1BFrJJeivJiM0s1-RqBewRoNkTQinOLLbaiZlBAQxS4iq2dRv"
+	}`)
+	testKeyECDSA_384_1_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "58ef375d6e63cedc637fed59f4012a779cd749a83373237cf3972bba74ebd738",
+		"crv": "P-384",
+		"x": "Gj3zkiRR4RCJb0Tke3lD2spG2jzuXgX50fEwDZTRjlQIzz3Rc96Fw32FCSDectxQ",
+		"y": "6alqN7ilqJAmqCU1BFrJJeivJiM0s1-RqBewRoNkTQinOLLbaiZlBAQxS4iq2dRv",
+		"d": "-4eaoElyb_YANRownMtId_-glX2o45oc4_L_vgo3YOW5hxCq3KIBIdrhvBx1nw8B"
+	}`)
+
+	testKeyECDSA_384_2 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "a8bab6f438b6cd2080711404549c978d1c00dd77a3c532bba12c964c4883b2ec",
+		"crv": "P-384",
+		"x": "CBKwYgZFLdTBBFnWD20q2YNUnRnOsDgTxG3y-dzCUrb65kOKm0ZFaZQPe5ZPjvDS",
+		"y": "WlubxLv2qH-Aw-LsESKXAwm4HF3l4H1rVn3DZRpqcac6p-QSrXmfKCtxJVHDaTNi"
+	}`)
+	testKeyECDSA_384_2_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "a8bab6f438b6cd2080711404549c978d1c00dd77a3c532bba12c964c4883b2ec",
+		"crv": "P-384",
+		"x": "CBKwYgZFLdTBBFnWD20q2YNUnRnOsDgTxG3y-dzCUrb65kOKm0ZFaZQPe5ZPjvDS",
+		"y": "WlubxLv2qH-Aw-LsESKXAwm4HF3l4H1rVn3DZRpqcac6p-QSrXmfKCtxJVHDaTNi",
+		"d": "XfoOcxV3yfoAcRquJ9eaBvcY-H71B0XzXwx2eidolHDKLK7GHygEt9ToYSY_DvxO"
+	}`)
+
+	testKeyECDSA_384_3 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "32f0cfc686455840530d727fb029002b9757ffff15272f98f53be9f77560718a",
+		"crv": "P-384",
+		"x": "xxED1z8EUCOUQ_jwS9nuVUDVzxs-U1rl19y8jMWrv4TdPeGHTRTgNUE57-YAL3ly",
+		"y": "OsCN6-HmM-LP1itE5eW15WsSQoe3dZBX7AoHyKJUKtjzWKCJNUcyu3Np07xta1Cr"
+	}`)
+	testKeyECDSA_384_3_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "32f0cfc686455840530d727fb029002b9757ffff15272f98f53be9f77560718a",
+		"crv": "P-384",
+		"x": "xxED1z8EUCOUQ_jwS9nuVUDVzxs-U1rl19y8jMWrv4TdPeGHTRTgNUE57-YAL3ly",
+		"y": "OsCN6-HmM-LP1itE5eW15WsSQoe3dZBX7AoHyKJUKtjzWKCJNUcyu3Np07xta1Cr",
+		"d": "gfQA6wn4brWVT3OkeGiaCXGsQyTybZB9SdqTULsiSg8n6FS2T8hvK0doPwMTT5Gw"
+	}`)
+
+	testKeyECDSA_521_0 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "f7b325c848a6c9fc5b72f131f179d2f37296837262797983c632597a4927726e",
+		"crv": "P-521",
+		"x": "AUaLqCAMEWPqiYgd-D_6F5kxpOgqnbQnjIHZ-NhjRnKKYuij9Iz7bq9pZU4F79wsODFpWxMFfISrveUfgEGt4Hy2",
+		"y": "AeAKfmFsfcFttwQqv2B-fUfgLhw837YoGoNWh5qNE_LqTBxbYKRUkbSLxVRHEcVNnU1t3z9yMMdYXtuMlfJ0bhjr"
+	}`)
+	testKeyECDSA_521_0_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "f7b325c848a6c9fc5b72f131f179d2f37296837262797983c632597a4927726e",
+		"crv": "P-521",
+		"x": "AUaLqCAMEWPqiYgd-D_6F5kxpOgqnbQnjIHZ-NhjRnKKYuij9Iz7bq9pZU4F79wsODFpWxMFfISrveUfgEGt4Hy2",
+		"y": "AeAKfmFsfcFttwQqv2B-fUfgLhw837YoGoNWh5qNE_LqTBxbYKRUkbSLxVRHEcVNnU1t3z9yMMdYXtuMlfJ0bhjr",
+		"d": "AbD3guIvlVd8CZD0xUNuebgnQkE24XJnxCQ69P5VL3etEMmdr4HPJLPMQfMs10Gz8RTmrGKo-bdsU-cjS3d7dKIC"
+	}`)
+
+	testKeyECDSA_521_1 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "4ea260ba2c9ed5fe9d6adeb998bb68b3edbab839d8bfd2be09fa628680793b90",
+		"crv": "P-521",
+		"x": "AE8-53o64GiywneanVZAHb96NcPbq0Ml6zynIcgLdKUiHGVs2te7SABq_9keFZJC6wooACeNWWT7VDK3kY77fjdh",
+		"y": "AAnrIu0-D7JEd3-mqTR8Rdz53kw7DLAIypv0-_u4rqn0glwTZCkMpQ17wFH71bMInXaTi2Z_uq67NuVxFvUCaTvS"
+	}`)
+	testKeyECDSA_521_1_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "4ea260ba2c9ed5fe9d6adeb998bb68b3edbab839d8bfd2be09fa628680793b90",
+		"crv": "P-521",
+		"x": "AE8-53o64GiywneanVZAHb96NcPbq0Ml6zynIcgLdKUiHGVs2te7SABq_9keFZJC6wooACeNWWT7VDK3kY77fjdh",
+		"y": "AAnrIu0-D7JEd3-mqTR8Rdz53kw7DLAIypv0-_u4rqn0glwTZCkMpQ17wFH71bMInXaTi2Z_uq67NuVxFvUCaTvS",
+		"d": "AVVL9TIWcJCYH5r3QUhHXtsbkVXhLQSpp4sL1ta_H2_3bpRb9ZdVv10YOA-xN7Yz2wa-FMhIhj1ULe9z18ZM8dDF"
+	}`)
+
+	testKeyECDSA_521_2 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "d69caddc821807ea63a46519b538a7d2f3135dbdda1ba628781f8567a47893d8",
+		"crv": "P-521",
+		"x": "AO618rH8GP78-mi9Z5FaPGqpyc_OVMK-BajZK-pwL89ZQdOvFa0fY0ENpB_KaRf5ELw9IP17lQh3T-O9O7jePDFj",
+		"y": "AT1x9pCMbY-BXqAJPQDQxp6j8Gca7IpdxL8OS4td_XPiwXtX4JKcj_VKxtOw6k64yr_VuFYCs6wImOc72jNRBjA7"
+	}`)
+	testKeyECDSA_521_2_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "d69caddc821807ea63a46519b538a7d2f3135dbdda1ba628781f8567a47893d8",
+		"crv": "P-521",
+		"x": "AO618rH8GP78-mi9Z5FaPGqpyc_OVMK-BajZK-pwL89ZQdOvFa0fY0ENpB_KaRf5ELw9IP17lQh3T-O9O7jePDFj",
+		"y": "AT1x9pCMbY-BXqAJPQDQxp6j8Gca7IpdxL8OS4td_XPiwXtX4JKcj_VKxtOw6k64yr_VuFYCs6wImOc72jNRBjA7",
+		"d": "AYmtaW9ojb0Gb8zSqrlRnEKzRVIBIM5dsb1qWkd3mfr4Wl5tbPuiEctGLN9s6LDtY0JOL3nukOVoDbrmS4qCW64"
+	}`)
+
+	testKeyECDSA_521_3 = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "7193cba4fd9c72153133ce5cffc423857517ab8eb176a827e1a002eb5621edca",
+		"crv": "P-521",
+		"x": "AQfpLehZCER3ZTw1V1pN0RsX8-4WEW4IDxFDSGwrULQ79YHiLNmubrOSlSxiOSv2S-tHq-hxgma1PZlQRghJfemx",
+		"y": "AAF41XT7jptLsy8FAaVRnex2WcSfdebcjjXMGO4rn6IlD3u9qnvrpR8MBp1gz5G1C5S7_NVgIeLSIYbdfd_wjVkd"
+	}`)
+	testKeyECDSA_521_3_Priv = mustLoadJWK(`{
+		"kty": "EC",
+		"kid": "7193cba4fd9c72153133ce5cffc423857517ab8eb176a827e1a002eb5621edca",
+		"crv": "P-521",
+		"x": "AQfpLehZCER3ZTw1V1pN0RsX8-4WEW4IDxFDSGwrULQ79YHiLNmubrOSlSxiOSv2S-tHq-hxgma1PZlQRghJfemx",
+		"y": "AAF41XT7jptLsy8FAaVRnex2WcSfdebcjjXMGO4rn6IlD3u9qnvrpR8MBp1gz5G1C5S7_NVgIeLSIYbdfd_wjVkd",
+		"d": "hzh0loqrhYFUik86SYBv3CC_GKNYankLMK95-Cfr1gLBD1l0M6W-7gn3XlyaQEInz0TBIbaQ1fL78HHjbVsNLrY"
+	}`)
+
+	testKeyRSA_1024_0 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "979d62e7114052027b11bcb51282d8228790134ef34e746f6372fa5aadaa9bf2",
+		"n": "6zxmGE5X74SUBWfDEo8Tl-YxrkVfvxljQG9vKmNPQ2RhEmJ8eplZpKn9_nlxVDHGLzfJMqqUBS19EarIfDnOqyFBRkyKRbsQdUVU9XLjDIXqImJ8aN-UmShAYoeOClJVDsBJuxBGgS3pdgG7u3YQpvTV_hGuMoJr7UYgIrqb0qc",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_1024_0_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "979d62e7114052027b11bcb51282d8228790134ef34e746f6372fa5aadaa9bf2",
+		"n": "6zxmGE5X74SUBWfDEo8Tl-YxrkVfvxljQG9vKmNPQ2RhEmJ8eplZpKn9_nlxVDHGLzfJMqqUBS19EarIfDnOqyFBRkyKRbsQdUVU9XLjDIXqImJ8aN-UmShAYoeOClJVDsBJuxBGgS3pdgG7u3YQpvTV_hGuMoJr7UYgIrqb0qc",
+		"e": "AQAB",
+		"d": "IARggP5oyZjp7LJqwqPmrs4OBQI8Pe5eq-5-2u4ZY7rN24q8FpO4t8jLYU92NVdw-gxFvjepXesLEtSD5SSZFD7s-5rqmX9tW_t5t1a1sBY6IKz_YBSf2p2LtdTyrqgo4nRD0nYH3sMvGtOKCTV4K_vwfWPD3elXq752trG-HwE",
+		"p": "9tgo12L3earNNrJfyIIDD2dqgKuNfWhQzhbe-Ju17dF37uGF6xnLqR0WXU-agGpUdYTMOd7IQi_bX_xkjQBYlw",
+		"q": "8_YDvufPdccjGM2rAXs-2LbeP_LrzLUk1uGNpEjIt2lXEm6sQKjIfNLcAfVKh9zsqwqroveL2iI1ijsDgNAIcQ"
+	}`)
+
+	testKeyRSA_1024_1 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "4a3a37dc10aec5f427509e4014683102a0fafa32bec2ff9921ff19e1a55c79e8",
+		"n": "sn7sU7dBEkU1RBIz_LKgrswSS7-68vTlOe7n-lanAqAlczm01_6IWrvcIC7lPv1iHQqWngusskANZirCZGTv6kK8kJLHBVRSROB9VkPTPNFYnSB6dacyPa0ty0otsaYOVM2RFvcCX7lBKKat2Tmst8vITdpvnoEzTgT_eGOKGAs",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_1024_1_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "4a3a37dc10aec5f427509e4014683102a0fafa32bec2ff9921ff19e1a55c79e8",
+		"n": "sn7sU7dBEkU1RBIz_LKgrswSS7-68vTlOe7n-lanAqAlczm01_6IWrvcIC7lPv1iHQqWngusskANZirCZGTv6kK8kJLHBVRSROB9VkPTPNFYnSB6dacyPa0ty0otsaYOVM2RFvcCX7lBKKat2Tmst8vITdpvnoEzTgT_eGOKGAs",
+		"e": "AQAB",
+		"d": "KTXqpE1sBaba7HNzc0Vemdzd4IVMyWlHPz_saTz2ZEHLQ7YwDapjmudCpF-PaCKiM2hNbAHwBluJfGwk4372cPHcJyri3Gs4GGj-cOlbXJh9aUp5o8fqn904B1UcPxMZ2DrZAKjseuLj3zyJgCsSJOGU1kJFRAkM8UN4a9cE8sE",
+		"p": "3ntHMGDJEXwqldLNR-DucKGp32aJVAaKnn4j9qy1Tj6KhH70o3HyFVO_TKYxGg-pG3zTV-VzyMqmeh9hDdnyKw",
+		"q": "zWMxEjyxvp-P-mNxhWQe1pJXZi6iPpcnt0SZLfweT3AtAzEaoSs4-4D1jT911LD4xDI8_a7-gYHgD8ME62PhoQ"
+	}`)
+
+	testKeyRSA_1024_2 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "d1c32aaea6c9527038e589dd94a546ca1beedb90f90e073228986cc258cf1fda",
+		"n": "3mZrDaB5-7e29zok9XkGuu6FXYB00FqFgnGTJAGCMpql5uHz1h9p0DljZL4vsGkkYOZUMvqFS1pCEuzdSsupPNClf0NKMRux6yLv6iIR9C4pE9RBKPUrinzJuYs634rq5JOEP4IpP_fJfKxMw4Na85otd9KposKwP14cCkOibYM",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_1024_2_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "d1c32aaea6c9527038e589dd94a546ca1beedb90f90e073228986cc258cf1fda",
+		"n": "3mZrDaB5-7e29zok9XkGuu6FXYB00FqFgnGTJAGCMpql5uHz1h9p0DljZL4vsGkkYOZUMvqFS1pCEuzdSsupPNClf0NKMRux6yLv6iIR9C4pE9RBKPUrinzJuYs634rq5JOEP4IpP_fJfKxMw4Na85otd9KposKwP14cCkOibYM",
+		"e": "AQAB",
+		"d": "Ft2M0B_ZqsmepBh0SFCjIoD3cT-NwwYrh9fJewA0tKM1v2EnwrIEHQZpc6giGw8UUGod6gfbwH2NIYj8z33U7lyrKWPR7F8gJRm1KR5NLCBCnAnz0ukhsg24ktB25LZLZRCStRRhtCev95Vvmew0ip5081hv730Z2T_PsEyOU6E",
+		"p": "8moFzLKJSQuhaIYTo1qNX0w8o4NBFb1atOlthHmq6Y6rdYTm_nyM9Q3mrNBolPS7LHTiBXtCEJ_m4V5hWDuGKw",
+		"q": "6t0_mZKOoZ5NAE--MxkuOCcRhuqNo9VoacfPEH39CeoKAam4v5k56R7aQcb_raQK396pgV-evM2dpfdUaasiCQ"
+	}`)
+
+	testKeyRSA_1024_3 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "838742d0e3195b19f974fa30352db79070fa19aaaeb678ec95c499de7bd48d52",
+		"n": "17Uc89-QvCjqBLXDJSCWxUoohjwFPI63Gub8g-lH1GSK3flXqiohz33KfKhqrdKsLrpRjskGTMg3Vo0IcLBnMYdp1i1nceORghtYLQsDNS7tqlHiKx725fLmWldGgiuP_0Ak0Knisw-j_q7Jx3OVAJnuS1o3vPLfJxJdyq6yV1k",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_1024_3_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "838742d0e3195b19f974fa30352db79070fa19aaaeb678ec95c499de7bd48d52",
+		"n": "17Uc89-QvCjqBLXDJSCWxUoohjwFPI63Gub8g-lH1GSK3flXqiohz33KfKhqrdKsLrpRjskGTMg3Vo0IcLBnMYdp1i1nceORghtYLQsDNS7tqlHiKx725fLmWldGgiuP_0Ak0Knisw-j_q7Jx3OVAJnuS1o3vPLfJxJdyq6yV1k",
+		"e": "AQAB",
+		"d": "B8cM-zIVauNixLa1CZKqPQTWfziMy8ktivfHJQ51O5BAfY5u_cC1JWEYuvPrnMba1Hh9VlOjOYOCk0lUg5OotMx5hxym6M5y_2rIrW90a8r3gGttPlZmyHQnFIgj2QZHlEyZGU1SIPTOtoECW5RGk7cYpA1s1_zfz8uyG-MiCPE",
+		"p": "2dwia5iWWLZwFcCVylPh_7QDr3Wxt3kW_TmHIbaRT10Rborj1lAwltyEx7aVpHq-aNfvrYIEBbOXWwUU8PDUrQ",
+		"q": "_XiDT55hn-Txi2C_peMVSDgXozf01qHKvdLirXM2uT_CI8kruYZYjWYn6_cSUptJ60eioM3WNTxjrSxWRS923Q"
+	}`)
+
+	testKeyRSA_2048_0 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "13b668c7b4f5d46d53de65e4b9f9c522fad4870e4d1656e459c88de6e90984d6",
+		"n": "1j-CxBxkn20C_M24t6ueLp02T4MMAiPLXf5yaDcj7EcsXGbnsGEhrAUwCZwdEJAxeZ0PolmlE0XBOhQfRtsCVJmwu918aknptToyDbOUBr6WtPIK_c_BuGVanLCx3SnczV4jle9Bz7tGfpj2vAXytSBrfnZhdCHNEFeefQTQMnavfMhfWf_njiTa76BRyAHjb-XZIJHKovwBu0y3glmzhSKYNsUrW11RsWx6DbueWEbE3FpsHTiEdnJipcP3UKl3Z2z6t6n9ZYtFWkx4zCVVBQu-RWUQwjr2XnR1LFwXgL9xQocDBmS1O-wMTHqL3_oosNUdV3vuMPdxs_SEoys2rQ",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_2048_0_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "13b668c7b4f5d46d53de65e4b9f9c522fad4870e4d1656e459c88de6e90984d6",
+		"n": "1j-CxBxkn20C_M24t6ueLp02T4MMAiPLXf5yaDcj7EcsXGbnsGEhrAUwCZwdEJAxeZ0PolmlE0XBOhQfRtsCVJmwu918aknptToyDbOUBr6WtPIK_c_BuGVanLCx3SnczV4jle9Bz7tGfpj2vAXytSBrfnZhdCHNEFeefQTQMnavfMhfWf_njiTa76BRyAHjb-XZIJHKovwBu0y3glmzhSKYNsUrW11RsWx6DbueWEbE3FpsHTiEdnJipcP3UKl3Z2z6t6n9ZYtFWkx4zCVVBQu-RWUQwjr2XnR1LFwXgL9xQocDBmS1O-wMTHqL3_oosNUdV3vuMPdxs_SEoys2rQ",
+		"e": "AQAB",
+		"d": "C8F4X2Jfcw_8Nfrjw9A64bvmmv5JzmRAaGvpwyYjZneRS5Cp7demjVXLiPtz7NC8pjuj-_iHQkN1ksY_4RdrTVERjX1dskdT94m17WKJIMWcZ1lQmRSpQIDvM-HOIKCHaQ1dToDOT6Oq_o9OGosJAj9BJrNALasdIWRtYda9xcb7roLl_U3AtOlK9RiFygtt5uVBIPh1rsxaT0Y1MvMk4EMbFnv7NXXk65UM_3p2leSoPpO7LvlYs3WoRRX9ABH7zI-ppwLGEDuxfnwKzAOaRBe9oUh5rTYdazaY9XqofvekBc9Xqa2HpjkYX-L4Zy3oj04u-u5zX8KTh25jFC2a0Q",
+		"p": "6L6icXqV7jLSXHrhMyLpW-tdFFp2XRQ_uKk972jmUJ-sEeh1YYSx_JpK6oaUnYshGbRLfEAOLy03iweAL4uMZJcASOR44TwSnn_qJZ72PkwhD42uKhE0eJRpxVht6jf5qhsynaMNUWc_FIW5ESpQIf6j4rqFV5WrKHAt0ypChTc",
+		"q": "66fAza4rLvyjqQAIGQ07RiRYW4no1h4cK8RH_CDUgI3CCFQSuqTB8ZqM38r2u0tZoKzCNgHcnde6Jk07X6-LSQW2kypMXt3idbgaeVSYSbdZndgG_jTibJvoybxSjo6HfLpPvCrfsihXo7O5M13LE7VqBLbGTLI5iubOxcrfFTs"
+	}`)
+
+	testKeyRSA_2048_1 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "6d2b0efff0011bbbbf694de684b5b97cf454fd4086fa53b9af92545f3fa65459",
+		"n": "1CmSBUbxU1jjnmaBG0r0cLLmyZgCOMbfpG6Z3HwfVDgCK6P-rU3F6QQazrOgGJJ0sz6vP50VK5u7BR6vSrPBBX5CicJPM2iNdz2JuV9ODEIkDQBLeI6TfIGhNOls-14tXKOPExY8b6JdyDMP31Hwo_0pF1FaunZ7yY1bgoKCtDV5-RKGd2EgylDGNPu0Ilr92MqCsAntBC8eQSkO4CcTcti4t9cX45VY5nPtwQRmp5zIgUHnU4LV3QVTLJnU-uidaAxRVQbS1pVql5xR6nYZHYvFk1IU-wTY6gGk5WvGWWQ440UTTaMAfnJP6VFDggUXeGSlKfKkDcz7JLT2Ma2KXQ",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_2048_1_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "6d2b0efff0011bbbbf694de684b5b97cf454fd4086fa53b9af92545f3fa65459",
+		"n": "1CmSBUbxU1jjnmaBG0r0cLLmyZgCOMbfpG6Z3HwfVDgCK6P-rU3F6QQazrOgGJJ0sz6vP50VK5u7BR6vSrPBBX5CicJPM2iNdz2JuV9ODEIkDQBLeI6TfIGhNOls-14tXKOPExY8b6JdyDMP31Hwo_0pF1FaunZ7yY1bgoKCtDV5-RKGd2EgylDGNPu0Ilr92MqCsAntBC8eQSkO4CcTcti4t9cX45VY5nPtwQRmp5zIgUHnU4LV3QVTLJnU-uidaAxRVQbS1pVql5xR6nYZHYvFk1IU-wTY6gGk5WvGWWQ440UTTaMAfnJP6VFDggUXeGSlKfKkDcz7JLT2Ma2KXQ",
+		"e": "AQAB",
+		"d": "DYJcHuPmh-UYEUT7oY5DRE3P7jQ0qALZyLGWMHji0c0DLl4x4D0chfrR7il33zisH6G1LPrGl1FCNlA-3yXU-5GPkRADVQWqRFZxx5Du-k7X1tAW_iUt9PaYGjNm0hasEsMDYDbBQGZ5TD8cGp8wEHEVRbvTaB4VQb8zfXrr8ad8XCFdtYQF5Sw_VuvUBcn-50kdl7S0MmQiwLx5xkisJHkVdVsrWbq-JJPMYrjYzPGo07kt8pQH2ecxrQD2waTzkLiAg-nytPBkAyMIFQ-XCulWCNiI-V_HJ0pqNctgVpdaSihlPCYhfllxUSU8yg4UvcfFEAN2S5yjvPyrJeJLAQ",
+		"p": "4db4J-vxAyb_3otKidzYISIo8NuPxPXuYeImbvVbY6CWHdAYAkPgbwADh592D1vM6kwqZLot4VQ4XUVtX7Vf8tCaq5BAzcVAslK0rhK86kSmzuvtpS1ruzm1DGwJcQWOl_9qYnaAov8Ny0NG8fTm8iwvWJH7rVa3XNtPL0t_fx0",
+		"q": "8H7_sX4kY3_4t90HbA8IvVkTf3-OGaIjtcDn2J3HLq1wMKAtdxwsbMtjhLYP70PPBe_FBNpcWhE7p-tOVhH1i0N0wwFqwlRQy-3Z4oiLeSsy0Npl29cfP7teO5UTrbrqfmHB0j2u_nEqA4n4iDF5QhPr_9yzxSR4Pg56z4PgFEE"
+	}`)
+
+	testKeyRSA_2048_2 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "7988e9dea9647281b04d094e4dc5737eaf739a412a570aa4d6deed7ee5640c30",
+		"n": "wWOFgHHiJ54ZjQEKxvfqYDuhYymbfvYxvreroqx7E-cAuU4QBsOvKV3HNnH_vDRQE1AlqihNWmPFLptp7wxdMrLEtDnRFqTxyTXJ7wLCaaaB6Wwx1dhpr9QEG5-8rxLGFYv2w5i0-o76JPHG0tuqf0YNmHp9oWcv524XnDBuji4-u6km1KynT1EQKHYgy57JWgUpukgJGImBEvf0hC2Y5s5mHvVby3xAD9-cFa2o9Vj3G-SBYFsrRIVR8GcVNwo88oj8F7-2nGD_wOOu_qT_5I3vfToUYGj5-2rAK1ja0bZpXFibrkPrW4w9paG-3F7I0sPeL3e_BDC_rQ8TgL03uQ",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_2048_2_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "7988e9dea9647281b04d094e4dc5737eaf739a412a570aa4d6deed7ee5640c30",
+		"n": "wWOFgHHiJ54ZjQEKxvfqYDuhYymbfvYxvreroqx7E-cAuU4QBsOvKV3HNnH_vDRQE1AlqihNWmPFLptp7wxdMrLEtDnRFqTxyTXJ7wLCaaaB6Wwx1dhpr9QEG5-8rxLGFYv2w5i0-o76JPHG0tuqf0YNmHp9oWcv524XnDBuji4-u6km1KynT1EQKHYgy57JWgUpukgJGImBEvf0hC2Y5s5mHvVby3xAD9-cFa2o9Vj3G-SBYFsrRIVR8GcVNwo88oj8F7-2nGD_wOOu_qT_5I3vfToUYGj5-2rAK1ja0bZpXFibrkPrW4w9paG-3F7I0sPeL3e_BDC_rQ8TgL03uQ",
+		"e": "AQAB",
+		"d": "RPC4j-CJUbw_uY-Miv-oMuQvFU2o3Crh8u5BJn28ZozsKiMU_YRW9jUzJkqfczVm8muY8b7qTHXSvlmy-v_6XW9zRhhyXFMyypr9QNJIAifUmiTy4xwCGSdIy5w3RGY57UZ3EqVmpwe_TtpOGa8rabHMePX5wUcqwaLykcCGOPLOFKfPF93GqZYi2StS1IaiijLi2IAMhxQGqS6Ct3dA7yacQwegPiDzjb4Is3V9UQ9k_aS3I1w56tz5qspVmfDEuuWToc-2Qyk-eMKWl1tTDJuGTCiS5IFtMPerRpPq9GCpycV0csIW-6AnW79b038DxWjDAUsNnDPnX0y1dQ1G3Q",
+		"p": "yh2KH_JqU29L2SLG5-ul1EBbaSbt3F5UUZbCvm_MIsiH1TzafVyBd8FeG1d-pMR8bgGjaDSA-DNkOEDAeAlLx-UCTl2Uk2ySoMnZr5JjVLW2DTuF7e8ySKrKQSSLe63aQyKuIZh9P1RNrwrmFwvJgXdzudVjirmdTwxAN0lijE8",
+		"q": "9PJitAE_kk6SoKvJisXbDb7Xursj26xiYIdWuzlzQ1CbwYcJ1oORDpo0bEpy0BfIbZDiJJ79Xrh5Lo1BVysQ-IZJCXK7mavqiSa8g3B8rmqLXmAtjDbwLGsJOayCpYnsGgOLT7XIUYXCFH7C-tSSydhJ1j8JAbsJveMiBPKnUXc"
+	}`)
+
+	testKeyRSA_2048_3 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "d214aea9b78d15dd2667f5d19c36df6647fa50023140fd4f5866d285718b897c",
+		"n": "qFnqWpBOZ_5jnIr_XkXnonmHII5gKzxPhUBfvWBhGN2eH8nmnGh6aUnKyKuCLP_qfYLU7cf9bah-e00451iGite8Tg9ZMYAPFX4NM5j0rGWNv9Z6lSn1xXezJv-FU9VUwXm0DG5eVcB8OV99JWxHivQUSBzY0Q3DUlgrD7FhMd7BrrcmTUO07KnW6SxN3oTbT7fNMxfJSTRxmvU-t-6sLhYP4Wbg39zEUgI8M7b7tvHp34klSqOn2DibVBhvWGF1-IgNT7ng6pS5iAZN7Cz7NjPc9ZM_IWyngPKZV49Tf-ikX2O8uiCb4ccgRur6znwkE7MPrDTXSMHALn10sUcO_w",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_2048_3_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "d214aea9b78d15dd2667f5d19c36df6647fa50023140fd4f5866d285718b897c",
+		"n": "qFnqWpBOZ_5jnIr_XkXnonmHII5gKzxPhUBfvWBhGN2eH8nmnGh6aUnKyKuCLP_qfYLU7cf9bah-e00451iGite8Tg9ZMYAPFX4NM5j0rGWNv9Z6lSn1xXezJv-FU9VUwXm0DG5eVcB8OV99JWxHivQUSBzY0Q3DUlgrD7FhMd7BrrcmTUO07KnW6SxN3oTbT7fNMxfJSTRxmvU-t-6sLhYP4Wbg39zEUgI8M7b7tvHp34klSqOn2DibVBhvWGF1-IgNT7ng6pS5iAZN7Cz7NjPc9ZM_IWyngPKZV49Tf-ikX2O8uiCb4ccgRur6znwkE7MPrDTXSMHALn10sUcO_w",
+		"e": "AQAB",
+		"d": "cCIT2uarktD6gFaE6cIeGzZfLuwmWiX9wX-zRWxgwDM9E2dj12IvxtmD3E2Ak4CSK69tLEQ9JUFJnc89y7pHQ0uW_VdzzWjCo0omeOu0bO_njpPJanlcXn7wMVWY9NHvdj8eEfmhk_R1ybE0piyNKpyQtcehEv3bz4kyhW1ck936zafn5GWxCEWKIF-7OcotxtOl6z1GrJ7WqglMk8ooLucnAXzaPtcvD6seUhVH0vG60yI2AEInI_jMHR-mfDxrebG2xPPJLsqH3GChqTsxG5vjuaq71sNBiY_TbaUDbmWZxV66zdjhN93Rw-lc3vCPwG5vmuA2igWH7SKsHmQOAQ",
+		"p": "xvTQi8nCJGyZQm3fr7HU23FkSUYkKFUN1FRDWqjyz7kFj5rJxq5dSsNrrgVBvIBNY4TwDDcYia4rvx7KSRRwfV_rsU2-TCOxBebqVlbeV0HI7xMcVZXsv61Eugz-dznUU4wqmP_rCQ8vyNcVvCBbBLBW92n-IRQXsfuSUfuHnT8",
+		"q": "2J64YbgvH62PB4h-0iYuaykgUzWVSrXENg3_GaF8877AMTe98gjcH7HzTDW1fOhIcNZtmYTwlA2SgFudLsIJLoOn0-kmHzZjZJTWcsZsfTVwKhHD8Lzic7QkrrlbTIZlVu4mfT2f0kvaQiZp6zDCke3SLvVjlD-ebqTFCDIlXkE"
+	}`)
+
+	testKeyRSA_4096_0 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "4941c1e95df518587e54ddde3510c084fedf92098473537fb3fe022eaece9b42",
+		"n": "ypJYaH9_PLdEgGN3WFPL1V9qhC2BRXr24f1kGocaQe7wORFlZGX6gRpOCQ1lLmziLafERoSqZuBWF4vSGLeanCRQ7UKObYalMRR_rTJk6D_VD95LNOmtR4DrucdZvjQrAuLXhM5dNLGVoSq_zQowLDMeLWDUBB_TK4WlofoxI0RVoUOt4UXnSev8M_6yIYASpOAk8gG4FAqRfmU-3JhgkZOQCD0BhKYZVw_kEhRBrS3aik-1pQkan9hYIxWwc4KKtrLHNls7--vk5xDk2Vod5sZYRLlimbqHavN2IBeAeJGJqt1grXOVlnagKqmyq2MoPKufwptJBiVrVsTplyUB9FZe9FxUfxrkkxYJHufwP-3wXhaXpPFdL86TCUuOz-jTfng1rsTpwzmwm3RqFzz01yMc1RhaeWininQqj42bhW4bgFGVRP2QqSFbCbRZ4WMW3qWr3aR2QpJ7b8ac7JUhAJoh7-UtMmyGx9UJP0gjA8Wm-O81UENoDjMMdNUaVTfpSUKB7xZppg244jNZDIm1ptq4QyvvWkr0brp9Ymxpu0e3g2HyywyPjbeyNrIelb4E2tU5vCR4Fs_zGFG71AieruJGfzPTXE7ZaiLhP8n1652NCzIH76Y7OGz4ap2D55-RKpUkSI3KqWxUKxkM0tuZ7LS2F-1wEVs8P01K6RuQpxE",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_4096_0_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "4941c1e95df518587e54ddde3510c084fedf92098473537fb3fe022eaece9b42",
+		"n": "ypJYaH9_PLdEgGN3WFPL1V9qhC2BRXr24f1kGocaQe7wORFlZGX6gRpOCQ1lLmziLafERoSqZuBWF4vSGLeanCRQ7UKObYalMRR_rTJk6D_VD95LNOmtR4DrucdZvjQrAuLXhM5dNLGVoSq_zQowLDMeLWDUBB_TK4WlofoxI0RVoUOt4UXnSev8M_6yIYASpOAk8gG4FAqRfmU-3JhgkZOQCD0BhKYZVw_kEhRBrS3aik-1pQkan9hYIxWwc4KKtrLHNls7--vk5xDk2Vod5sZYRLlimbqHavN2IBeAeJGJqt1grXOVlnagKqmyq2MoPKufwptJBiVrVsTplyUB9FZe9FxUfxrkkxYJHufwP-3wXhaXpPFdL86TCUuOz-jTfng1rsTpwzmwm3RqFzz01yMc1RhaeWininQqj42bhW4bgFGVRP2QqSFbCbRZ4WMW3qWr3aR2QpJ7b8ac7JUhAJoh7-UtMmyGx9UJP0gjA8Wm-O81UENoDjMMdNUaVTfpSUKB7xZppg244jNZDIm1ptq4QyvvWkr0brp9Ymxpu0e3g2HyywyPjbeyNrIelb4E2tU5vCR4Fs_zGFG71AieruJGfzPTXE7ZaiLhP8n1652NCzIH76Y7OGz4ap2D55-RKpUkSI3KqWxUKxkM0tuZ7LS2F-1wEVs8P01K6RuQpxE",
+		"e": "AQAB",
+		"d": "VQagPRxm16FFC260hUqG4ASwvNIs1HEMd0bYYZobl1knU4zNthpnzxCveHU65wWk2ez1IXRF4fB_slpp0R4fszI7FZs-FRLS-4rTHGtul11TnNl9T7RVmxGt38ihDojvFMMKGyBTVu7DE2bSIsoH9kVugTWHSEPjav0pzJcrUNY56vpxXYDt18VJkrlxI0aSjMnYOAwoq6DT-O2eORFsVy5M4mhY3sipEjYFUOFXv8zjUfKrF55-omE4fWF5MsK0XoMjwtkAkHkvFx2sMN72dgsCubXmgQgeFvIhvs6eifzsf99z2NoPC5y3FbEs4Ws5VF3lLNXpDL9gEoeMVHigHKNoztIzbJnFLjzEag4wOBqbig4nWIlYEcLwd5-E0oQ8GHAhWtJn5AJQKOwTcasRWiUnRAulCcI8f9R2ATTSjAGU-juFUfhProp4w3ZlnUbO6U3tbh90KdFSJa-Bapts6ijPgEp6MHubVCHIxh1KGmod_CuGGjze1GuISEwI_4Yh0SrFlp4Wy6ecg7mKNnXvkBMd6h90LJaTzubnts33cCs-5UzCtYqmWHwXrMA6dRJuicY6su3NXxStuZn2bxU_Dn8LrS1vx_lhDU__NqMNbYj5ZvHkPvZiJNBI6Z1R5SY3pQzpH6vh9W2KRKF8cKOkushIaHNnHo7W5o4eZ9HjJFE",
+		"p": "7Bxd2wTrXfeSZAEGZEnfoJaMh6hKFG-fx0wLmF9U-Myq1fCIQ-gdl_Atqy0xQxYtQl2lOPUWfySegJSxpdRF8pwvLq9Yt7xFQDrV0B8d5-FjYJ_Mk2bbyHv_PhNmnfcYHqE_VdADeSaU-MRV1iknvjdbxSxYUF-U2C6llfJhn-ZbGPmZJyz8fTWHSh8aKLPJkuYV84wcNL04hpFPJq7Bhy7HAFhRYNpxXyDO3I36DCU94uvUCY758VKLl8IYMMSqjh-thluKJSkWibo85ZOJR38XFksMr70pH-DkT0WMv3PpLtNu6ac6Ry4CEkZexSWmbVk-pduRooo0On1CMsrsLw",
+		"q": "26K4gFJ0Ygqr4KaJ4QcRGN29XcjDpX5XTlHy3hCs_wEWc5wedydVgIH4tadiZcu0WinTtl0I6FYewWNpI8nFh8XJXsLOLepdKKR798zW8kEhzxdYdP1MTtGfy12KUCMeS2RQRsBJlmNdgrI0_m0JvW2LlGeK0EqvrqeS06L8bKGXkfY2aFGHl6ipSfUd5OpuSUqgfrj4-HgB8zywm-yAvn7UK-ySyQrP3K4PuOiY5QZmcPDxl8yqDAjorStrjPieYrlMqREHMsyq2FtAXhx4FzpKN7OljbE3EpgyFKkFN-wDTWRGxu3au0hQJx0-XthZ9i5r8zGcEbArQ5jZH7CQvw"
+	}`)
+
+	testKeyRSA_4096_1 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "d01929d205e83facdc5080a0a4ade80853978298a5c73780e863c035d32127c9",
+		"n": "ve_rSTi1vwfV2Oj7YNu1kv8Xz2ImAEqf7qo2RehTYW4FJpbj0wkMBVGmAulm4dz-1knnaPdpbrrIwkEYr1XR6kSIeB8aXkCZqDGZATxLSRqGEzu2J8Xt9z_qTQLWYD-NuMEf9H3B1CiP3Q2RnoWUlGvOr_KI5h6anzeqgZxlQLgqQujXscwqWpX7MVh3sheZ5SbyTkDcFWvQS_NEPaBGso-Au9X-yhZsHU1Ky02Nd_DPOrrRzl7uymE07hy3bIbxmrh4ZeEz6P2ixsHHYbd15GNMRlr1cWbg91RBB-akSxX0VYoLjjuqwo33UHk1hBbSATbobfpOKRruuZPZ_xOiPi-m0tUdD1Pj-h6xRcA5sZ1d55IdL8IY_9kgLc_WyU2RWFTXV6zA2SDDdE6EdEB_8q6U0oLU5T-YRxd-V2qOTqW1388qUaL5BalSvqM0g8kVfBYIM3uwLqQI_hCer4CL7_0DGlUtheye3qcoyqWVJd9iqfcWC-1S8NljAJwM9sehx6kOSM85UuUTH89VM35oAVp0rSPcLy20dPzEVQ0LAcy_iRHTkqy9nZ1z6mo-IWQE2ZyPCuESEfG7nFJ3YlUfwBJ5uZnk0N1FVYX5zgEKdkP322vShrv1blB_JtUedpbCcuS-qCaywpwjL0pzTDKpJMEqHds0-DFT7AbULcujw_U",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_4096_1_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "d01929d205e83facdc5080a0a4ade80853978298a5c73780e863c035d32127c9",
+		"n": "ve_rSTi1vwfV2Oj7YNu1kv8Xz2ImAEqf7qo2RehTYW4FJpbj0wkMBVGmAulm4dz-1knnaPdpbrrIwkEYr1XR6kSIeB8aXkCZqDGZATxLSRqGEzu2J8Xt9z_qTQLWYD-NuMEf9H3B1CiP3Q2RnoWUlGvOr_KI5h6anzeqgZxlQLgqQujXscwqWpX7MVh3sheZ5SbyTkDcFWvQS_NEPaBGso-Au9X-yhZsHU1Ky02Nd_DPOrrRzl7uymE07hy3bIbxmrh4ZeEz6P2ixsHHYbd15GNMRlr1cWbg91RBB-akSxX0VYoLjjuqwo33UHk1hBbSATbobfpOKRruuZPZ_xOiPi-m0tUdD1Pj-h6xRcA5sZ1d55IdL8IY_9kgLc_WyU2RWFTXV6zA2SDDdE6EdEB_8q6U0oLU5T-YRxd-V2qOTqW1388qUaL5BalSvqM0g8kVfBYIM3uwLqQI_hCer4CL7_0DGlUtheye3qcoyqWVJd9iqfcWC-1S8NljAJwM9sehx6kOSM85UuUTH89VM35oAVp0rSPcLy20dPzEVQ0LAcy_iRHTkqy9nZ1z6mo-IWQE2ZyPCuESEfG7nFJ3YlUfwBJ5uZnk0N1FVYX5zgEKdkP322vShrv1blB_JtUedpbCcuS-qCaywpwjL0pzTDKpJMEqHds0-DFT7AbULcujw_U",
+		"e": "AQAB",
+		"d": "mCjZ3wDVaMJIKMsMhx28KpS9aGACfX1K_pHRhNOH6KeQ7Mc4oFnBDYnJas-8ofi_FsCB6G88QX7VUfmAYwZncjuQ8FpKb3NlJX8GSh0ZWukqu8G8PcSszMShWSyKvPRs_rOIe_87BlGwXrB-FfaBfx2WqRGtZlziFecsa0T1QJHJGW0bTs52p7c7Ut7ClSOfIBrBRrtjFK4YYp_x7US3HlkkElZvFUo9NoQzBQeN66Y4_Z2ocqFOv0Z8drz-nKzGZOKfYU62nVKD0qJurfOhOGPsOPipZD28v6b5qfC1cYmXAefjNgDK3a2JkShpHPaDKoHoViKN9xQiZvzxSQ1bjQCgB6xQWk61YwmiB44gFLshXvXkx_wcccO2XC4m-Pk0r-Uj9Ugvgizk8HYp1H9eqnp8A6sqU44mrmdt14vM68SGzEqYpLoUkcxPttHvNoORAzgQfuK3SOIFyuwFqKTMBupR4A42XvxLj_PuKEKFekS2JEK8fON2t_LuUDVemjMxwXD0gjljHSKD9HnguihI5IEd6Kgr-HlTwiiDQfzcmWQdoFUs_Je4tVCSbGRrJYju01gLouDu7gzN14pZcEyoBLPkj-_MDiiymvlBLOBU2s5nQRZCaC_0IKgHLaT_rzQaXwmVhU-OURdwdoViMVc_cdXtle4TqU1g41nNE_0tiwE",
+		"p": "2h_9HZCOpf3S9qpAkNS2ncMsRjBlWCpRltoCs-XBF_IgOJlCkJ-42k_V1zaf6YrgMy00XD4Ij5MMiUOharvc3w_WKJF-AwGu9JEpi38Wzj1nkY86sdB1ZPu4ihSRZm81jIjuLZ2siILqkupXaY0S_0CeNLKPoVpvfPWCb3psCWuLVRDhYECPCx9FYg2tzisbFpVAPPqNCFswMYNvhyJ2NQ0hzueKDc1uHBUanCSG6gIbeY1bQvYYFGySYkWZcQg98RSeDbLysHalCSQCgPjss3likTT9OjWLv23vpXURFhgywv6Phx4He0yWB8ZRrnskfW0S76kZ325SPUlnOhf31Q",
+		"q": "3urwELOa7TDpwLMeMEOf2aspz1gkMjoN-V4SMzRGxIoPy93-bQ62Z8nPduma1gK0zHOIs0gU8dDqjy8P8Z7vudJqRFJIrzjD-RnDqQDz7KM_6x0kbEN4GLQiNGVAckRhoEmYjDXXkG3teD2ST5NqlYwxGvFmCvw5RCJZG7eNjXY3KnJQrDyx61NIFEvSm3sBlJkOVhSanuqHBL6T-efaBkUcQ7mY7_iYtCt7cjMG8qQtF0KovVsMF6BUc9KDobpUvDsvPrSTAzsYJvVNkzGJtF8Strr-61VA6qZnRcDW3ukYvmJp_SdAcv3KzSkWwEmJEaAtaiOh2jWkRG9QpV_LoQ"
+	}`)
+
+	testKeyRSA_4096_2 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "822512e8098b9fa56c9152a56d2607bda912c89f6f4fe1b6ccf41627bf06de29",
+		"n": "3bcg0diA0TPvK283P02o5UT8cBf5q48uUsfuCkIbLqNFtskGuoft8OJLmik7c7pLNSK3Jad_-xRlfsrcV1fDBs8EJDFnoY-4r_xk_-GQl-OsHzuz7HMvjVYUSYeVZMtzPAfCIFJxSIaiIW9MEwCwYyrtRefedfKUV7Ax6hjOUXtyRZ5DRHj2FnNl6MjE8mTSJ8OQI9ETErg0wF448fJb6hYCidVHfeBSCOceljXMg8a9iDYHVnzsUb9ETl7xlRZDvUpIoG0jU-aC3oZdM0YgXspvw4FcXg1-ad-TbgRjEm_iJ-4KUhAYso6-Y6eimDT_TMhbKBLGE86pUjRPIXVNl6odjkXmcmh7bz9v0-gIfHB9zVbrqE8i8iIwkux-Uhq_VMiIFikjtjmDo5W-7qA_cjvq1dA1QqOJ54ijbdvglUtneuA5CnwfGHAvf94lEPebgP5eCMZjz_Dtl0FTX0u5lOVBs4qeyEfV1XANOq_h1gHmwJDlspGZVuPfSk4-YMCeBy5Ua_gNDqOIQo3EQbmjyN5yD9byFh3WDSQJN1kKT3BZSou-Q8-KT2lu9KT_CpTfVMaNXnPpXJ_H0S9Q7sbUzA6dF1h0Q_mu_bOlWTRlH3vXfZ3_3Vikjk-jhQdaRv8yjTdhX33wAVHk7omKlo76G3QJdnvjysJ6aagFPZ3qhmc",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_4096_2_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "822512e8098b9fa56c9152a56d2607bda912c89f6f4fe1b6ccf41627bf06de29",
+		"n": "3bcg0diA0TPvK283P02o5UT8cBf5q48uUsfuCkIbLqNFtskGuoft8OJLmik7c7pLNSK3Jad_-xRlfsrcV1fDBs8EJDFnoY-4r_xk_-GQl-OsHzuz7HMvjVYUSYeVZMtzPAfCIFJxSIaiIW9MEwCwYyrtRefedfKUV7Ax6hjOUXtyRZ5DRHj2FnNl6MjE8mTSJ8OQI9ETErg0wF448fJb6hYCidVHfeBSCOceljXMg8a9iDYHVnzsUb9ETl7xlRZDvUpIoG0jU-aC3oZdM0YgXspvw4FcXg1-ad-TbgRjEm_iJ-4KUhAYso6-Y6eimDT_TMhbKBLGE86pUjRPIXVNl6odjkXmcmh7bz9v0-gIfHB9zVbrqE8i8iIwkux-Uhq_VMiIFikjtjmDo5W-7qA_cjvq1dA1QqOJ54ijbdvglUtneuA5CnwfGHAvf94lEPebgP5eCMZjz_Dtl0FTX0u5lOVBs4qeyEfV1XANOq_h1gHmwJDlspGZVuPfSk4-YMCeBy5Ua_gNDqOIQo3EQbmjyN5yD9byFh3WDSQJN1kKT3BZSou-Q8-KT2lu9KT_CpTfVMaNXnPpXJ_H0S9Q7sbUzA6dF1h0Q_mu_bOlWTRlH3vXfZ3_3Vikjk-jhQdaRv8yjTdhX33wAVHk7omKlo76G3QJdnvjysJ6aagFPZ3qhmc",
+		"e": "AQAB",
+		"d": "wcO4mAxJUAu-OsxgkR9SusPWhjQ9y5Q_XLNDso1hahng5ES9b6k55mouvlTIk3Q9I_vp6auAKrMBnJS3ilG1rK6hJOxUcBrFwm-m6QV9s3CSzV0E-mEULsYKxtQKWOOBGvaAznSeck7PRL8a0gSpIpGyeYSRo6zTverLRJZXQVjMXlFY4m-ASdCiQJWtoVVBYOUFhHfE3nhECdaOl8xCTcrcfw75AuZXa1ZpIcd0q7m1jGQDd6-HbE3m6UMKiEvD-ZsA68tVs45h0w3ER_pCcfUjRc45Ji1OzEJLezu0RbmoAVOEi4FrxCkB9N_dNn4inD0BhX0axNa4nZH_kfMNUh06081FaBiBq5bNBPQF7lWIYxAhtd2VgsUTZtNxMfHDTllt5fvbCogYngU5-Wj3UM33R_es_aakt_CFndJPmyenP0J7KEBmf4qnlrbxun-jQNV_cbEzgQA_Pt5RIhB5jgofYzT72Ib-lViZUolXpxg8mFQ5BPghqriuaX8eSc85y5rbu-nEvheDkIC7xjTtDBShXMvKKokx6t4d-x_W4sWDuLKsVNn3Veg6mbbl3O74Fr-joEM9unMg-9KCapAuBKmMv4GejOVOIj43i0lm8dsuMslNKsThWpjApE50-VBuySi1f0jD2HNEzjMTt5ZrlNi9bRFzAht46kdc_g4TWLE",
+		"p": "7cl5QebQldiOrfe2Z7E2aP4d1GIuX4b3u-kXUDa73HI4S2u9TooJF8lfsbrtzJIG60iIzio9Pej8R4ss2_1TPC_F7gQS67ST5yY2zuwUH6jwpt8KtVO3NKd9SNfVJse_mZLp7eAeH8CMq4bqkDig8ZI_Sp1LQtWXvhvhju40i0h1ef-MD36wbc26BE1L6tQMAoJIU-pMDawmWMGG3Q5_LxKHb1cxvV8gVfFgsJbAHMPXxcmpCdp1FqeqpvRs3bch3AMxHwjy1SFRK6lhvliS2ZvD7fdupA09PGJ-cNO3S9iHdCvsnyOHg9pMbxxcYW8Io_t1ihk6JYFvZlDIYvukXw",
+		"q": "7rKFf6190MlunvlLSdMvN_E2Xuow35_EThaxQ9e_7FS32LKPy847vFreXzVivVW1IVXrRSDgU8VIEyBQzftliLSriefKYdx2jtd_A84baha4p6CAK9SwfZcVEcIuU2Ul8XcUpbg53_bxUWDv4mYewZW_AqaZ0ZR1Ug14gCGHELvjs7MmWkCkJB6lEenRQZvzwcmA8tWzZ6Dlqb_RyLtxcVPbeSuc3t6YxbB1-pquwXoQLB9-XvgR_4L6vuGZrp1D-C0lzboUPoftkC_hKYb_xY6LPJTsL8LUypATcTrAnD0TBrqj5-Oy_4dMin6OcsXrfdxflxaJqkh7B-kz20oa-Q"
+	}`)
+
+	testKeyRSA_4096_3 = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "ad60df152305985e6bdd8cb1d1f496f766aed097b7464b8b821a09b27f054dcb",
+		"n": "q8mHub9-A7LH2ykklO52CRQ9KYeM9FlHaoLJCMokmwdb8FF8cAMOPDQ4lx7rB107vwX5oHS6T4uhOUH6ppgZnvB083-f5cBfvZPSwvKg1csOPI3Llp-hIO3vnu7v4M-dXRJevmNdTAoHINXP8mJ1KfGL6BuxckxLEOoIX0suSmyudAE_lc6ioMAfwehkH7qTWwllIuReGwxHHYapM0AFIYysJ0z96l-WNkO3ssLm6Q7txpumM_b0d58OkdezBLujEfn0t5H8hsml_LUSE4TfWcGwm3oDBB2q5tsI2_QmVwdV6BcP43Sm8R7EbJvTQq3YXKLlC-Et4-3CDGaMV6z1eFmHBs_FMGzto-kg9MUFockOe0vkfjHj6AafNw7AFyBJRvV3EdMM-O1ysiC5cs45aowbfQIz_9H9RVbR9iMiEjq6nM34bhgfu-FvOmAcliCOJnDlBy2wWe2XyXTLQVkmn1C_w2mitcQAkP-LiktypUwSIbyDqfBai1_imO1SIVzomRLvzSju5qGrcm0ROV6o9zKXCVc546g9la9FUTTPZsI0_s3hcVRVMaF3fa122hE848serjCqNJ0Nb3CYOlVCc6JrkUdeuCj0Y-on6V6aeCFAQxFb1z_p88STsNJkrcNZp87f1JUaSB4POF01dLNQ2HTe7xJCMRmK-fpNuDCOzNM",
+		"e": "AQAB"
+	}`)
+	testKeyRSA_4096_3_Priv = mustLoadJWK(`{
+		"kty": "RSA",
+		"kid": "ad60df152305985e6bdd8cb1d1f496f766aed097b7464b8b821a09b27f054dcb",
+		"n": "q8mHub9-A7LH2ykklO52CRQ9KYeM9FlHaoLJCMokmwdb8FF8cAMOPDQ4lx7rB107vwX5oHS6T4uhOUH6ppgZnvB083-f5cBfvZPSwvKg1csOPI3Llp-hIO3vnu7v4M-dXRJevmNdTAoHINXP8mJ1KfGL6BuxckxLEOoIX0suSmyudAE_lc6ioMAfwehkH7qTWwllIuReGwxHHYapM0AFIYysJ0z96l-WNkO3ssLm6Q7txpumM_b0d58OkdezBLujEfn0t5H8hsml_LUSE4TfWcGwm3oDBB2q5tsI2_QmVwdV6BcP43Sm8R7EbJvTQq3YXKLlC-Et4-3CDGaMV6z1eFmHBs_FMGzto-kg9MUFockOe0vkfjHj6AafNw7AFyBJRvV3EdMM-O1ysiC5cs45aowbfQIz_9H9RVbR9iMiEjq6nM34bhgfu-FvOmAcliCOJnDlBy2wWe2XyXTLQVkmn1C_w2mitcQAkP-LiktypUwSIbyDqfBai1_imO1SIVzomRLvzSju5qGrcm0ROV6o9zKXCVc546g9la9FUTTPZsI0_s3hcVRVMaF3fa122hE848serjCqNJ0Nb3CYOlVCc6JrkUdeuCj0Y-on6V6aeCFAQxFb1z_p88STsNJkrcNZp87f1JUaSB4POF01dLNQ2HTe7xJCMRmK-fpNuDCOzNM",
+		"e": "AQAB",
+		"d": "QFcw8I8aQYRaemlEfEt8BhaAeed9EZ_GscveQ96CK1ZsRuweMU3TrRTaBS_dU1rGH9u7DS_rABQKBIoDuRXKss7Y3sJ0Pvb4ZObSz5VUS_7LjD6HfBi5nr2_O8W-LnNUOyHAPoq0zOAMn221ftEFlPoVLpAAvBB7JRCiph5gbhuak3RMPm2wV4jd3CCQL5oPys8QBCuIW5UTpalkAf_-a_xmFiouB_RZLGXcjaWWGsAuqm5tp5TdJ1h5eoJRWHp2ryrxTzfsXwdzldyzsn_Xr6Rt4y2lp4r9EY4EGW2uVnY25MCOgOCWDkU5yHvselLmcHvKUdK6_11zinV2Jvhuz1FaW10P6lRXxhjuuT4bT7XmY6WkJCUvWf8w_NYrGwDbRvQa6YZcZZWKZ1l2Enkgd_P4qwtrnROQSCe0dJdNG3lSq90lrAwuvb8AhKhpQ8nJSaSQc_h2pa4NJZ-R__9m0_7CRUuEEg9k47AtgdIe09KLSpcACc3W5cBXEw2pL8ihqcf9AVgM0TFQQVrUdIst3YjUiB6r2zLVCRx8KjtT8Pmz6YtMQwDIFTrUopwwai5PEP3QEdxdNF39W1iwkqjA8uw4IlXgZAr4-9s3x617XUL0BQ4TgMpTsEghpV1U8U2HQfYGUKBcAlSqIlAi0MU4QRwcsAJrcoTIi-e34NFMbHE",
+		"p": "xvZ1BKzaRfrmQT2mmbK1MzKdr4amHrx4EJ_4fRsCRBj8MqB92y4Bp0iuO8rZ3iK_yOOgQQTcr4UP5UEACLOqGDPNa92UUpHu1U5XR3MiIY0kGcdovpkkGU8fq78VRpyofc1b9kvZsy4eL0e7W7jApjGu479D_evnpT7lqSOGRl1Z0QHdI3ctKmKw48-AmQWL4BibXTyaXOfRgTHm4AtbrvFshwCZ43QgzrCrwbhZlOfiIIW9wXa_OqqOhV11BLF72qTglecfTgUOxY0W5GWgbwXCVZ8Lwr6cxYKGrKjmny9UNOIjABGsJjZm5egwDNUhhoaEeXXyzXbCKynvUCDxZw",
+		"q": "3Qi1bRzj3TYW3Iw9KSDCuuHWBJRO_3Ke-JLB7PHEXc1hsPwF_XDqXPaBbIUtgKaqNzUehYAQbtHHhJVGqZadTKjsNkO4sO_r5nRoxUpFuGgUEmKNN0QeJa_llHzAZ9JpvUrtoyzVZX-2Em_3aAhtV5pZ6t22YToPDjZIBgqL96MQRCyKsv2FS_dbpoYKdXlG6phzkzKPn3oaWkh-VwmgAsFg7uXdiquQEsXYcOq3-77Umiuke6SBbaHLryLflTzOV7YvY7Kw3s3NycS9MDBESKYXqqX3YKvS25ZFFjYbrVOx5AluLiwajZu2biIPZb9rNbSXE77Hll3tAbmA5syJtQ"
+	}`)
+)
diff --git a/vendor/github.com/coreos/go-oidc/jwks.go b/vendor/github.com/coreos/go-oidc/jwks.go
new file mode 100644
index 0000000000000000000000000000000000000000..4ec6c3f1d6e4747be46470cd6ea79b4574a9541f
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jwks.go
@@ -0,0 +1,199 @@
+package oidc
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"sync"
+	"time"
+
+	"github.com/pquerna/cachecontrol"
+	"golang.org/x/net/context"
+	"golang.org/x/net/context/ctxhttp"
+	jose "gopkg.in/square/go-jose.v2"
+)
+
+// keysExpiryDelta is the allowed clock skew between a client and the OpenID Connect
+// server.
+//
+// When keys expire, they are valid for this amount of time after.
+//
+// If the keys have not expired, and an ID Token claims it was signed by a key not in
+// the cache, if and only if the keys expire in this amount of time, the keys will be
+// updated.
+const keysExpiryDelta = 30 * time.Second
+
+func newRemoteKeySet(ctx context.Context, jwksURL string, now func() time.Time) *remoteKeySet {
+	if now == nil {
+		now = time.Now
+	}
+	return &remoteKeySet{jwksURL: jwksURL, ctx: ctx, now: now}
+}
+
+type remoteKeySet struct {
+	jwksURL string
+	ctx     context.Context
+	now     func() time.Time
+
+	// guard all other fields
+	mu sync.Mutex
+
+	// inflightCtx is the context of the current HTTP request to update the keys.
+	// Its Err() method returns any errors encountered during that attempt.
+	//
+	// If nil, there is no inflight request.
+	inflightCtx context.Context
+
+	// A set of cached keys and their expiry.
+	cachedKeys []jose.JSONWebKey
+	expiry     time.Time
+}
+
+// errContext is a context with a customizable Err() return value.
+type errContext struct {
+	context.Context
+
+	cf  context.CancelFunc
+	err error
+}
+
+func newErrContext(parent context.Context) *errContext {
+	ctx, cancel := context.WithCancel(parent)
+	return &errContext{ctx, cancel, nil}
+}
+
+func (e errContext) Err() error {
+	return e.err
+}
+
+// cancel cancels the errContext causing listeners on Done() to return.
+func (e errContext) cancel(err error) {
+	e.err = err
+	e.cf()
+}
+
+func (r *remoteKeySet) keysWithIDFromCache(keyIDs []string) ([]jose.JSONWebKey, bool) {
+	r.mu.Lock()
+	keys, expiry := r.cachedKeys, r.expiry
+	r.mu.Unlock()
+
+	// Have the keys expired?
+	if expiry.Add(keysExpiryDelta).Before(r.now()) {
+		return nil, false
+	}
+
+	var signingKeys []jose.JSONWebKey
+	for _, key := range keys {
+		if contains(keyIDs, key.KeyID) {
+			signingKeys = append(signingKeys, key)
+		}
+	}
+
+	if len(signingKeys) == 0 {
+		// Are the keys about to expire?
+		if r.now().Add(keysExpiryDelta).After(expiry) {
+			return nil, false
+		}
+	}
+
+	return signingKeys, true
+}
+func (r *remoteKeySet) keysWithID(ctx context.Context, keyIDs []string) ([]jose.JSONWebKey, error) {
+	keys, ok := r.keysWithIDFromCache(keyIDs)
+	if ok {
+		return keys, nil
+	}
+
+	var inflightCtx context.Context
+	func() {
+		r.mu.Lock()
+		defer r.mu.Unlock()
+
+		// If there's not a current inflight request, create one.
+		if r.inflightCtx == nil {
+			// Use the remoteKeySet's context instead of the requests context
+			// because a re-sync is unique to the keys set and will span multiple
+			// requests.
+			errCtx := newErrContext(r.ctx)
+			r.inflightCtx = errCtx
+
+			go func() {
+				// TODO(ericchiang): Upstream Kubernetes request that we recover every time
+				// we spawn a goroutine, because panics in a goroutine will bring down the
+				// entire program. There's no way to recover from another goroutine's panic.
+				//
+				// Most users actually want to let the panic propagate and bring down the
+				// program because it implies some unrecoverable state.
+				//
+				// Add a context key to allow the recover behavior.
+				//
+				// See: https://github.com/coreos/go-oidc/issues/89
+
+				// Sync keys and close inflightCtx when that's done.
+				errCtx.cancel(r.updateKeys(r.inflightCtx))
+
+				r.mu.Lock()
+				defer r.mu.Unlock()
+				r.inflightCtx = nil
+			}()
+		}
+
+		inflightCtx = r.inflightCtx
+	}()
+
+	select {
+	case <-ctx.Done():
+		return nil, ctx.Err()
+	case <-inflightCtx.Done():
+		if err := inflightCtx.Err(); err != nil {
+			return nil, err
+		}
+	}
+
+	// Since we've just updated keys, we don't care about the cache miss.
+	keys, _ = r.keysWithIDFromCache(keyIDs)
+	return keys, nil
+}
+
+func (r *remoteKeySet) updateKeys(ctx context.Context) error {
+	req, err := http.NewRequest("GET", r.jwksURL, nil)
+	if err != nil {
+		return fmt.Errorf("oidc: can't create request: %v", err)
+	}
+
+	resp, err := ctxhttp.Do(ctx, clientFromContext(ctx), req)
+	if err != nil {
+		return fmt.Errorf("oidc: get keys failed %v", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return fmt.Errorf("oidc: read response body: %v", err)
+	}
+	if resp.StatusCode != http.StatusOK {
+		return fmt.Errorf("oidc: get keys failed: %s %s", resp.Status, body)
+	}
+
+	var keySet jose.JSONWebKeySet
+	if err := json.Unmarshal(body, &keySet); err != nil {
+		return fmt.Errorf("oidc: failed to decode keys: %v %s", err, body)
+	}
+
+	// If the server doesn't provide cache control headers, assume the
+	// keys expire immediately.
+	expiry := r.now()
+
+	_, e, err := cachecontrol.CachableResponse(req, resp, cachecontrol.Options{})
+	if err == nil && e.After(expiry) {
+		expiry = e
+	}
+
+	r.mu.Lock()
+	defer r.mu.Unlock()
+	r.cachedKeys = keySet.Keys
+	r.expiry = expiry
+
+	return nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/jwks_test.go b/vendor/github.com/coreos/go-oidc/jwks_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d9735b2d392d08f80b8aaa759315b1e7138a62a8
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jwks_test.go
@@ -0,0 +1,99 @@
+package oidc
+
+import (
+	"encoding/json"
+	"net/http"
+	"net/http/httptest"
+	"reflect"
+	"testing"
+	"time"
+
+	"golang.org/x/net/context"
+	jose "gopkg.in/square/go-jose.v2"
+)
+
+type keyServer struct {
+	keys jose.JSONWebKeySet
+}
+
+func newKeyServer(keys ...jose.JSONWebKey) keyServer {
+	return keyServer{
+		keys: jose.JSONWebKeySet{Keys: keys},
+	}
+}
+
+func (k keyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	if err := json.NewEncoder(w).Encode(k.keys); err != nil {
+		panic(err)
+	}
+}
+
+func TestKeysFormID(t *testing.T) {
+	tests := []struct {
+		name     string
+		keys     []jose.JSONWebKey
+		keyIDs   []string
+		wantKeys []jose.JSONWebKey
+	}{
+		{
+			name: "single key",
+			keys: []jose.JSONWebKey{
+				testKeyRSA_2048_0,
+				testKeyECDSA_256_0,
+			},
+			keyIDs: []string{
+				testKeyRSA_2048_0.KeyID,
+			},
+			wantKeys: []jose.JSONWebKey{
+				testKeyRSA_2048_0,
+			},
+		},
+		{
+			name: "one key id matches",
+			keys: []jose.JSONWebKey{
+				testKeyRSA_2048_0,
+				testKeyECDSA_256_0,
+			},
+			keyIDs: []string{
+				testKeyRSA_2048_0.KeyID,
+				testKeyRSA_2048_1.KeyID,
+			},
+			wantKeys: []jose.JSONWebKey{
+				testKeyRSA_2048_0,
+			},
+		},
+		{
+			name: "no valid keys",
+			keys: []jose.JSONWebKey{
+				testKeyRSA_2048_1,
+				testKeyECDSA_256_0,
+			},
+			keyIDs: []string{
+				testKeyRSA_2048_0.KeyID,
+			},
+		},
+	}
+
+	t0 := time.Now()
+	now := func() time.Time { return t0 }
+
+	for _, test := range tests {
+		func() {
+			ctx, cancel := context.WithCancel(context.Background())
+			defer cancel()
+
+			server := httptest.NewServer(newKeyServer(test.keys...))
+			defer server.Close()
+
+			keySet := newRemoteKeySet(ctx, server.URL, now)
+			gotKeys, err := keySet.keysWithID(ctx, test.keyIDs)
+			if err != nil {
+				t.Errorf("%s: %v", test.name, err)
+				return
+			}
+			if !reflect.DeepEqual(gotKeys, test.wantKeys) {
+				t.Errorf("%s: expected keys=%#v, got=%#v", test.name, test.wantKeys, gotKeys)
+			}
+		}()
+	}
+}
diff --git a/vendor/github.com/coreos/go-oidc/key/doc.go b/vendor/github.com/coreos/go-oidc/key/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..936eec745825c234a628b81bb6b5356d6e4d6e59
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/key/doc.go
@@ -0,0 +1,2 @@
+// Package key is DEPRECATED. Use github.com/coreos/go-oidc instead.
+package key
diff --git a/vendor/github.com/coreos/go-oidc/oauth2/doc.go b/vendor/github.com/coreos/go-oidc/oauth2/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..52eb3085e934dadeaab0149c34c124fd2fcb684a
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oauth2/doc.go
@@ -0,0 +1,2 @@
+// Package oauth2 is DEPRECATED. Use golang.org/x/oauth instead.
+package oauth2
diff --git a/vendor/github.com/coreos/go-oidc/oidc.go b/vendor/github.com/coreos/go-oidc/oidc.go
new file mode 100644
index 0000000000000000000000000000000000000000..378f8f6483320cb06b643852b455513f9f98feec
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc.go
@@ -0,0 +1,286 @@
+// Package oidc implements OpenID Connect client logic for the golang.org/x/oauth2 package.
+package oidc
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"time"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
+	jose "gopkg.in/square/go-jose.v2"
+)
+
+const (
+	// ScopeOpenID is the mandatory scope for all OpenID Connect OAuth2 requests.
+	ScopeOpenID = "openid"
+
+	// ScopeOfflineAccess is an optional scope defined by OpenID Connect for requesting
+	// OAuth2 refresh tokens.
+	//
+	// Support for this scope differs between OpenID Connect providers. For instance
+	// Google rejects it, favoring appending "access_type=offline" as part of the
+	// authorization request instead.
+	//
+	// See: https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
+	ScopeOfflineAccess = "offline_access"
+)
+
+// ClientContext returns a new Context that carries the provided HTTP client.
+//
+// This method sets the same context key used by the golang.org/x/oauth2 package,
+// so the returned context works for that package too.
+//
+//    myClient := &http.Client{}
+//    ctx := oidc.ClientContext(parentContext, myClient)
+//
+//    // This will use the custom client
+//    provider, err := oidc.NewProvider(ctx, "https://accounts.example.com")
+//
+func ClientContext(ctx context.Context, client *http.Client) context.Context {
+	return context.WithValue(ctx, oauth2.HTTPClient, client)
+}
+
+func clientFromContext(ctx context.Context) *http.Client {
+	if client, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok {
+		return client
+	}
+	return http.DefaultClient
+}
+
+// Provider represents an OpenID Connect server's configuration.
+type Provider struct {
+	issuer      string
+	authURL     string
+	tokenURL    string
+	userInfoURL string
+
+	// Raw claims returned by the server.
+	rawClaims []byte
+
+	remoteKeySet *remoteKeySet
+}
+
+type cachedKeys struct {
+	keys   []jose.JSONWebKey
+	expiry time.Time
+}
+
+type providerJSON struct {
+	Issuer      string `json:"issuer"`
+	AuthURL     string `json:"authorization_endpoint"`
+	TokenURL    string `json:"token_endpoint"`
+	JWKSURL     string `json:"jwks_uri"`
+	UserInfoURL string `json:"userinfo_endpoint"`
+}
+
+// NewProvider uses the OpenID Connect discovery mechanism to construct a Provider.
+//
+// The issuer is the URL identifier for the service. For example: "https://accounts.google.com"
+// or "https://login.salesforce.com".
+func NewProvider(ctx context.Context, issuer string) (*Provider, error) {
+	wellKnown := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration"
+	resp, err := clientFromContext(ctx).Get(wellKnown)
+	if err != nil {
+		return nil, err
+	}
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("%s: %s", resp.Status, body)
+	}
+	defer resp.Body.Close()
+	var p providerJSON
+	if err := json.Unmarshal(body, &p); err != nil {
+		return nil, fmt.Errorf("oidc: failed to decode provider discovery object: %v", err)
+	}
+	if p.Issuer != issuer {
+		return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer)
+	}
+	return &Provider{
+		issuer:       p.Issuer,
+		authURL:      p.AuthURL,
+		tokenURL:     p.TokenURL,
+		userInfoURL:  p.UserInfoURL,
+		rawClaims:    body,
+		remoteKeySet: newRemoteKeySet(ctx, p.JWKSURL, time.Now),
+	}, nil
+}
+
+// Claims unmarshals raw fields returned by the server during discovery.
+//
+//    var claims struct {
+//        ScopesSupported []string `json:"scopes_supported"`
+//        ClaimsSupported []string `json:"claims_supported"`
+//    }
+//
+//    if err := provider.Claims(&claims); err != nil {
+//        // handle unmarshaling error
+//    }
+//
+// For a list of fields defined by the OpenID Connect spec see:
+// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
+func (p *Provider) Claims(v interface{}) error {
+	if p.rawClaims == nil {
+		return errors.New("oidc: claims not set")
+	}
+	return json.Unmarshal(p.rawClaims, v)
+}
+
+// Endpoint returns the OAuth2 auth and token endpoints for the given provider.
+func (p *Provider) Endpoint() oauth2.Endpoint {
+	return oauth2.Endpoint{AuthURL: p.authURL, TokenURL: p.tokenURL}
+}
+
+// UserInfo represents the OpenID Connect userinfo claims.
+type UserInfo struct {
+	Subject       string `json:"sub"`
+	Profile       string `json:"profile"`
+	Email         string `json:"email"`
+	EmailVerified bool   `json:"email_verified"`
+
+	claims []byte
+}
+
+// Claims unmarshals the raw JSON object claims into the provided object.
+func (u *UserInfo) Claims(v interface{}) error {
+	if u.claims == nil {
+		return errors.New("oidc: claims not set")
+	}
+	return json.Unmarshal(u.claims, v)
+}
+
+// UserInfo uses the token source to query the provider's user info endpoint.
+func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource) (*UserInfo, error) {
+	if p.userInfoURL == "" {
+		return nil, errors.New("oidc: user info endpoint is not supported by this provider")
+	}
+	resp, err := clientFromContext(ctx).Get(p.userInfoURL)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("%s: %s", resp.Status, body)
+	}
+
+	var userInfo UserInfo
+	if err := json.Unmarshal(body, &userInfo); err != nil {
+		return nil, fmt.Errorf("oidc: failed to decode userinfo: %v", err)
+	}
+	userInfo.claims = body
+	return &userInfo, nil
+}
+
+// IDToken is an OpenID Connect extension that provides a predictable representation
+// of an authorization event.
+//
+// The ID Token only holds fields OpenID Connect requires. To access additional
+// claims returned by the server, use the Claims method.
+type IDToken struct {
+	// The URL of the server which issued this token. This will always be the same
+	// as the URL used for initial discovery.
+	Issuer string
+
+	// The client, or set of clients, that this token is issued for.
+	Audience []string
+
+	// A unique string which identifies the end user.
+	Subject string
+
+	IssuedAt time.Time
+	Expiry   time.Time
+	Nonce    string
+
+	// Raw payload of the id_token.
+	claims []byte
+}
+
+// Claims unmarshals the raw JSON payload of the ID Token into a provided struct.
+//
+//		idToken, err := idTokenVerifier.Verify(rawIDToken)
+//		if err != nil {
+//			// handle error
+//		}
+//		var claims struct {
+//			Email         string `json:"email"`
+//			EmailVerified bool   `json:"email_verified"`
+//		}
+//		if err := idToken.Claims(&claims); err != nil {
+//			// handle error
+//		}
+//
+func (i *IDToken) Claims(v interface{}) error {
+	if i.claims == nil {
+		return errors.New("oidc: claims not set")
+	}
+	return json.Unmarshal(i.claims, v)
+}
+
+type idToken struct {
+	Issuer   string   `json:"iss"`
+	Subject  string   `json:"sub"`
+	Audience audience `json:"aud"`
+	Expiry   jsonTime `json:"exp"`
+	IssuedAt jsonTime `json:"iat"`
+	Nonce    string   `json:"nonce"`
+}
+
+type audience []string
+
+func (a *audience) UnmarshalJSON(b []byte) error {
+	var s string
+	if json.Unmarshal(b, &s) == nil {
+		*a = audience{s}
+		return nil
+	}
+	var auds []string
+	if err := json.Unmarshal(b, &auds); err != nil {
+		return err
+	}
+	*a = audience(auds)
+	return nil
+}
+
+func (a audience) MarshalJSON() ([]byte, error) {
+	if len(a) == 1 {
+		return json.Marshal(a[0])
+	}
+	return json.Marshal([]string(a))
+}
+
+type jsonTime time.Time
+
+func (j *jsonTime) UnmarshalJSON(b []byte) error {
+	var n json.Number
+	if err := json.Unmarshal(b, &n); err != nil {
+		return err
+	}
+	var unix int64
+
+	if t, err := n.Int64(); err == nil {
+		unix = t
+	} else {
+		f, err := n.Float64()
+		if err != nil {
+			return err
+		}
+		unix = int64(f)
+	}
+	*j = jsonTime(time.Unix(unix, 0))
+	return nil
+}
+
+func (j jsonTime) MarshalJSON() ([]byte, error) {
+	return json.Marshal(time.Time(j).Unix())
+}
diff --git a/vendor/github.com/coreos/go-oidc/oidc/doc.go b/vendor/github.com/coreos/go-oidc/oidc/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..196611ec542f7caea31c78c491d8fbcbe8a7d2d7
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc/doc.go
@@ -0,0 +1,2 @@
+// Package oidc is DEPRECATED. Use github.com/coreos/go-oidc instead.
+package oidc
diff --git a/vendor/github.com/coreos/go-oidc/oidc/provider.go b/vendor/github.com/coreos/go-oidc/oidc/provider.go
index ca2838440b3e9123b8edbd1e175ece62139a4428..42197ff1a5f7275272476e1d497d0fa1cf578a50 100644
--- a/vendor/github.com/coreos/go-oidc/oidc/provider.go
+++ b/vendor/github.com/coreos/go-oidc/oidc/provider.go
@@ -567,7 +567,7 @@ func (n *pcsStepNext) step(fn pcsStepFunc) (next pcsStepper) {
 		next = &pcsStepNext{aft: ttl}
 	} else {
 		next = &pcsStepRetry{aft: time.Second}
-		log.Printf("go-oidc: provider config sync falied, retyring in %v: %v", next.after(), err)
+		log.Printf("go-oidc: provider config sync failed, retrying in %v: %v", next.after(), err)
 	}
 	return
 }
@@ -586,7 +586,7 @@ func (r *pcsStepRetry) step(fn pcsStepFunc) (next pcsStepper) {
 		next = &pcsStepNext{aft: ttl}
 	} else {
 		next = &pcsStepRetry{aft: timeutil.ExpBackoff(r.aft, time.Minute)}
-		log.Printf("go-oidc: provider config sync falied, retyring in %v: %v", next.after(), err)
+		log.Printf("go-oidc: provider config sync failed, retrying in %v: %v", next.after(), err)
 	}
 	return
 }
diff --git a/vendor/github.com/coreos/go-oidc/oidc_test.go b/vendor/github.com/coreos/go-oidc/oidc_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7307a8f543176e6655bcea1da1da5bb043a3962e
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc_test.go
@@ -0,0 +1,21 @@
+package oidc
+
+import (
+	"net/http"
+	"testing"
+
+	"golang.org/x/net/context"
+)
+
+func TestClientContext(t *testing.T) {
+	myClient := &http.Client{}
+
+	ctx := ClientContext(context.Background(), myClient)
+
+	gotClient := clientFromContext(ctx)
+
+	// Compare pointer values.
+	if gotClient != myClient {
+		t.Fatal("clientFromContext did not return the value set by ClientContext")
+	}
+}
diff --git a/vendor/github.com/coreos/go-oidc/test b/vendor/github.com/coreos/go-oidc/test
index 9dd606723fc741fe24531e8f58c0cc0c899d6eaf..4b2e39f875f5d257a1d446d54f37bf66bdf75fc3 100755
--- a/vendor/github.com/coreos/go-oidc/test
+++ b/vendor/github.com/coreos/go-oidc/test
@@ -1,62 +1,15 @@
-#!/bin/bash -e
-#
-# Run all tests (not including functional)
-#   ./test
-#   ./test -v
-#
-# Run tests for one package
-#   PKG=./unit ./test
-#   PKG=ssh ./test
-#
+#!/bin/bash
 
-# Invoke ./cover for HTML output
-COVER=${COVER:-"-cover"}
+set -e
 
-RACE=${RACE:-"-race"}
+# Filter out any files with a !golint build tag.
+LINTABLE=$( go list -tags=golint -f '
+  {{- range $i, $file := .GoFiles -}}
+    {{ $file }} {{ end }}
+  {{ range $i, $file := .TestGoFiles -}}
+    {{ $file }} {{ end }}' github.com/coreos/go-oidc )
 
-source ./build
-
-TESTABLE="http jose key oauth2 oidc"
-FORMATTABLE="$TESTABLE"
-
-# user has not provided PKG override
-if [ -z "$PKG" ]; then
-	TEST=$TESTABLE
-	FMT=$FORMATTABLE
-
-# user has provided PKG override
-else
-	# strip out slashes and dots from PKG=./foo/
-	TEST=${PKG//\//}
-	TEST=${TEST//./}
-
-	# only run gofmt on packages provided by user
-	FMT="$TEST"
-fi
-
-# split TEST into an array and prepend repo path to each local package
-split=(${TEST// / })
-TEST=${split[@]/#/github.com/coreos/go-oidc/}
-
-echo "Running tests..."
-go test $RACE ${COVER} $@ ${TEST}
-
-echo "Checking gofmt..."
-fmtRes=$(gofmt -l $FMT)
-if [ -n "${fmtRes}" ]; then
-	echo -e "gofmt checking failed:\n${fmtRes}"
-	exit 255
-fi
-
-if [[ -z "$TRAVIS_GO_VERSION" || "$TRAVIS_GO_VERSION" != "1.4.3" ]]; then
-  echo "Checking govet..."
-  vetRes=$(go vet $TEST)
-  if [ -n "${vetRes}" ]; then
-    echo -e "govet checking failed:\n${vetRes}"
-    exit 255
-  fi
-else
-  echo "Skipping govet (Go 1.4)"
-fi
-
-echo "Success"
+go test -v -i -race github.com/coreos/go-oidc
+go test -v -race github.com/coreos/go-oidc
+golint $LINTABLE
+go vet github.com/coreos/go-oidc
diff --git a/vendor/github.com/coreos/go-oidc/verify.go b/vendor/github.com/coreos/go-oidc/verify.go
new file mode 100644
index 0000000000000000000000000000000000000000..13c0f9347e8a6dd673c649bb6d73871349f6ea28
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/verify.go
@@ -0,0 +1,263 @@
+package oidc
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strings"
+	"time"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
+	jose "gopkg.in/square/go-jose.v2"
+)
+
+// IDTokenVerifier provides verification for ID Tokens.
+type IDTokenVerifier struct {
+	keySet *remoteKeySet
+	config *verificationConfig
+}
+
+// verificationConfig is the unexported configuration for an IDTokenVerifier.
+//
+// Users interact with this struct using a VerificationOption.
+type verificationConfig struct {
+	issuer string
+	// If provided, this value must be in the ID Token audiences.
+	audience string
+	// If not nil, check the expiry of the id token.
+	checkExpiry func() time.Time
+	// If specified, only these sets of algorithms may be used to sign the JWT.
+	requiredAlgs []string
+	// If not nil, don't verify nonce.
+	nonceSource NonceSource
+}
+
+// VerificationOption provides additional checks on ID Tokens.
+type VerificationOption interface {
+	// Unexport this method so other packages can't implement this interface.
+	updateConfig(c *verificationConfig)
+}
+
+// Verifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs.
+//
+// The returned IDTokenVerifier is tied to the Provider's context and its behavior is
+// undefined once the Provider's context is canceled.
+func (p *Provider) Verifier(options ...VerificationOption) *IDTokenVerifier {
+	config := &verificationConfig{issuer: p.issuer}
+	for _, option := range options {
+		option.updateConfig(config)
+	}
+
+	return newVerifier(p.remoteKeySet, config)
+}
+
+func newVerifier(keySet *remoteKeySet, config *verificationConfig) *IDTokenVerifier {
+	// As discussed in the godocs for VerifrySigningAlg, because almost all providers
+	// only support RS256, default to only allowing it.
+	if len(config.requiredAlgs) == 0 {
+		config.requiredAlgs = []string{RS256}
+	}
+
+	return &IDTokenVerifier{
+		keySet: keySet,
+		config: config,
+	}
+}
+
+func parseJWT(p string) ([]byte, error) {
+	parts := strings.Split(p, ".")
+	if len(parts) < 2 {
+		return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts))
+	}
+	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
+	if err != nil {
+		return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err)
+	}
+	return payload, nil
+}
+
+func contains(sli []string, ele string) bool {
+	for _, s := range sli {
+		if s == ele {
+			return true
+		}
+	}
+	return false
+}
+
+// Verify parses a raw ID Token, verifies it's been signed by the provider, preforms
+// any additional checks passed as VerifictionOptions, and returns the payload.
+//
+// See: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
+//
+//    oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
+//    if err != nil {
+//        // handle error
+//    }
+//
+//    // Extract the ID Token from oauth2 token.
+//    rawIDToken, ok := oauth2Token.Extra("id_token").(string)
+//    if !ok {
+//        // handle error
+//    }
+//
+//    token, err := verifier.Verify(ctx, rawIDToken)
+//
+func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDToken, error) {
+	jws, err := jose.ParseSigned(rawIDToken)
+	if err != nil {
+		return nil, fmt.Errorf("oidc: mallformed jwt: %v", err)
+	}
+
+	// Throw out tokens with invalid claims before trying to verify the token. This lets
+	// us do cheap checks before possibly re-syncing keys.
+	payload, err := parseJWT(rawIDToken)
+	if err != nil {
+		return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
+	}
+	var token idToken
+	if err := json.Unmarshal(payload, &token); err != nil {
+		return nil, fmt.Errorf("oidc: failed to unmarshal claims: %v", err)
+	}
+
+	t := &IDToken{
+		Issuer:   token.Issuer,
+		Subject:  token.Subject,
+		Audience: []string(token.Audience),
+		Expiry:   time.Time(token.Expiry),
+		IssuedAt: time.Time(token.IssuedAt),
+		Nonce:    token.Nonce,
+		claims:   payload,
+	}
+
+	// Check issuer.
+	if t.Issuer != v.config.issuer {
+		return nil, fmt.Errorf("oidc: id token issued by a different provider, expected %q got %q", v.config.issuer, t.Issuer)
+	}
+
+	// If a client ID has been provided, make sure it's part of the audience.
+	if v.config.audience != "" {
+		if !contains(t.Audience, v.config.audience) {
+			return nil, fmt.Errorf("oidc: expected audience %q got %q", v.config.audience, t.Audience)
+		}
+	}
+
+	// If a set of required algorithms has been provided, ensure that the signatures use those.
+	var keyIDs, gotAlgs []string
+	for _, sig := range jws.Signatures {
+		if len(v.config.requiredAlgs) == 0 || contains(v.config.requiredAlgs, sig.Header.Algorithm) {
+			keyIDs = append(keyIDs, sig.Header.KeyID)
+		} else {
+			gotAlgs = append(gotAlgs, sig.Header.Algorithm)
+		}
+	}
+	if len(keyIDs) == 0 {
+		return nil, fmt.Errorf("oidc: no signatures use a require algorithm, expected %q got %q", v.config.requiredAlgs, gotAlgs)
+	}
+
+	// Get keys from the remote key set. This may trigger a re-sync.
+	keys, err := v.keySet.keysWithID(ctx, keyIDs)
+	if err != nil {
+		return nil, fmt.Errorf("oidc: get keys for id token: %v", err)
+	}
+	if len(keys) == 0 {
+		return nil, fmt.Errorf("oidc: no keys match signature ID(s) %q", keyIDs)
+	}
+
+	// Try to use a key to validate the signature.
+	var gotPayload []byte
+	for _, key := range keys {
+		if p, err := jws.Verify(&key); err == nil {
+			gotPayload = p
+		}
+	}
+	if len(gotPayload) == 0 {
+		return nil, fmt.Errorf("oidc: failed to verify id token")
+	}
+
+	// Ensure that the payload returned by the square actually matches the payload parsed earlier.
+	if !bytes.Equal(gotPayload, payload) {
+		return nil, errors.New("oidc: internal error, payload parsed did not match previous payload")
+	}
+
+	// Check the nonce after we've verified the token. We don't want to allow unverified
+	// payloads to trigger a nonce lookup.
+	if v.config.nonceSource != nil {
+		if err := v.config.nonceSource.ClaimNonce(t.Nonce); err != nil {
+			return nil, err
+		}
+	}
+
+	return t, nil
+}
+
+// VerifyAudience ensures that an ID Token was issued for the specific client.
+//
+// Note that a verified token may be valid for other clients, as OpenID Connect allows a token to have
+// multiple audiences.
+func VerifyAudience(clientID string) VerificationOption {
+	return clientVerifier{clientID}
+}
+
+type clientVerifier struct {
+	clientID string
+}
+
+func (v clientVerifier) updateConfig(c *verificationConfig) {
+	c.audience = v.clientID
+}
+
+// VerifyExpiry ensures that an ID Token has not expired.
+func VerifyExpiry() VerificationOption {
+	return expiryVerifier{}
+}
+
+type expiryVerifier struct{}
+
+func (v expiryVerifier) updateConfig(c *verificationConfig) {
+	c.checkExpiry = time.Now
+}
+
+// VerifySigningAlg enforces that an ID Token is signed by a specific signing algorithm.
+//
+// Because so many providers only support RS256, if this verifiction option isn't used,
+// the IDTokenVerifier defaults to only allowing RS256.
+func VerifySigningAlg(allowedAlgs ...string) VerificationOption {
+	return algVerifier{allowedAlgs}
+}
+
+type algVerifier struct {
+	algs []string
+}
+
+func (v algVerifier) updateConfig(c *verificationConfig) {
+	c.requiredAlgs = v.algs
+}
+
+// Nonce returns an auth code option which requires the ID Token created by the
+// OpenID Connect provider to contain the specified nonce.
+func Nonce(nonce string) oauth2.AuthCodeOption {
+	return oauth2.SetAuthURLParam("nonce", nonce)
+}
+
+// NonceSource represents a source which can verify a nonce is valid and has not
+// been claimed before.
+type NonceSource interface {
+	ClaimNonce(nonce string) error
+}
+
+// VerifyNonce ensures that the ID Token contains a nonce which can be claimed by the nonce source.
+func VerifyNonce(source NonceSource) VerificationOption {
+	return nonceVerifier{source}
+}
+
+type nonceVerifier struct {
+	nonceSource NonceSource
+}
+
+func (n nonceVerifier) updateConfig(c *verificationConfig) {
+	c.nonceSource = n.nonceSource
+}
diff --git a/vendor/github.com/coreos/go-oidc/verify_test.go b/vendor/github.com/coreos/go-oidc/verify_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..872f75c30d33c44164798e565c9f3bc7b7fdfc20
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/verify_test.go
@@ -0,0 +1,265 @@
+package oidc
+
+import (
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rsa"
+	"encoding/json"
+	"net/http/httptest"
+	"testing"
+	"time"
+
+	"golang.org/x/net/context"
+	jose "gopkg.in/square/go-jose.v2"
+)
+
+func TestVerify(t *testing.T) {
+	tests := []verificationTest{
+		{
+			name: "good token",
+			idToken: idToken{
+				Issuer: "https://foo",
+			},
+			config: verificationConfig{
+				issuer: "https://foo",
+			},
+			signKey: testKeyRSA_2048_0_Priv,
+			pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
+		},
+		{
+			name: "invalid signature",
+			idToken: idToken{
+				Issuer: "https://foo",
+			},
+			config: verificationConfig{
+				issuer: "https://foo",
+			},
+			signKey: testKeyRSA_2048_0_Priv,
+			pubKeys: []jose.JSONWebKey{testKeyRSA_2048_1},
+			wantErr: true,
+		},
+		{
+			name: "invalid issuer",
+			idToken: idToken{
+				Issuer: "https://foo",
+			},
+			config: verificationConfig{
+				issuer: "https://bar",
+			},
+			signKey: testKeyRSA_2048_0_Priv,
+			pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
+			wantErr: true,
+		},
+	}
+	for _, test := range tests {
+		test.run(t)
+	}
+}
+
+func TestVerifyAudience(t *testing.T) {
+	tests := []verificationTest{
+		{
+			name: "good audience",
+			idToken: idToken{
+				Issuer:   "https://foo",
+				Audience: []string{"client1"},
+			},
+			config: verificationConfig{
+				issuer:   "https://foo",
+				audience: "client1",
+			},
+			signKey: testKeyRSA_2048_0_Priv,
+			pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
+		},
+		{
+			name: "mismatched audience",
+			idToken: idToken{
+				Issuer:   "https://foo",
+				Audience: []string{"client2"},
+			},
+			config: verificationConfig{
+				issuer:   "https://foo",
+				audience: "client1",
+			},
+			signKey: testKeyRSA_2048_0_Priv,
+			pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
+			wantErr: true,
+		},
+		{
+			name: "multiple audiences, one matches",
+			idToken: idToken{
+				Issuer:   "https://foo",
+				Audience: []string{"client2", "client1"},
+			},
+			config: verificationConfig{
+				issuer:   "https://foo",
+				audience: "client1",
+			},
+			signKey: testKeyRSA_2048_0_Priv,
+			pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
+		},
+	}
+	for _, test := range tests {
+		test.run(t)
+	}
+}
+
+func TestVerifySigningAlg(t *testing.T) {
+	tests := []verificationTest{
+		{
+			name: "default signing alg",
+			idToken: idToken{
+				Issuer: "https://foo",
+			},
+			config: verificationConfig{
+				issuer: "https://foo",
+			},
+			signKey: testKeyRSA_2048_0_Priv,
+			signAlg: RS256, // By default we only support RS256.
+			pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
+		},
+		{
+			name: "bad signing alg",
+			idToken: idToken{
+				Issuer: "https://foo",
+			},
+			config: verificationConfig{
+				issuer: "https://foo",
+			},
+			signKey: testKeyRSA_2048_0_Priv,
+			signAlg: RS512,
+			pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
+			wantErr: true,
+		},
+		{
+			name: "ecdsa signing",
+			idToken: idToken{
+				Issuer: "https://foo",
+			},
+			config: verificationConfig{
+				issuer:       "https://foo",
+				requiredAlgs: []string{ES384},
+			},
+			signAlg: ES384,
+			signKey: testKeyECDSA_384_0_Priv,
+			pubKeys: []jose.JSONWebKey{testKeyECDSA_384_0},
+		},
+		{
+			name: "one of many supported",
+			idToken: idToken{
+				Issuer: "https://foo",
+			},
+			config: verificationConfig{
+				issuer:       "https://foo",
+				requiredAlgs: []string{RS256, ES384},
+			},
+			signAlg: ES384,
+			signKey: testKeyECDSA_384_0_Priv,
+			pubKeys: []jose.JSONWebKey{testKeyECDSA_384_0},
+		},
+		{
+			name: "not in requiredAlgs",
+			idToken: idToken{
+				Issuer: "https://foo",
+			},
+			config: verificationConfig{
+				issuer:       "https://foo",
+				requiredAlgs: []string{RS256, ES512},
+			},
+			signAlg: ES384,
+			signKey: testKeyECDSA_384_0_Priv,
+			pubKeys: []jose.JSONWebKey{testKeyECDSA_384_0},
+			wantErr: true,
+		},
+	}
+	for _, test := range tests {
+		test.run(t)
+	}
+}
+
+type verificationTest struct {
+	name string
+
+	// ID token claims and a signing key to create the JWT.
+	idToken idToken
+	signKey jose.JSONWebKey
+	// If supplied use this signing algorithm. If not, guess
+	// from the signingKey.
+	signAlg string
+
+	config  verificationConfig
+	pubKeys []jose.JSONWebKey
+
+	wantErr bool
+}
+
+func algForKey(t *testing.T, k jose.JSONWebKey) string {
+	switch key := k.Key.(type) {
+	case *rsa.PrivateKey:
+		return RS256
+	case *ecdsa.PrivateKey:
+		name := key.PublicKey.Params().Name
+		switch name {
+		case elliptic.P256().Params().Name:
+			return ES256
+		case elliptic.P384().Params().Name:
+			return ES384
+		case elliptic.P521().Params().Name:
+			return ES512
+		}
+		t.Fatalf("unsupported ecdsa curve: %s", name)
+	default:
+		t.Fatalf("unsupported key type %T", key)
+	}
+	return ""
+}
+
+func (v verificationTest) run(t *testing.T) {
+	payload, err := json.Marshal(v.idToken)
+	if err != nil {
+		t.Fatal(err)
+	}
+	signingAlg := v.signAlg
+	if signingAlg == "" {
+		signingAlg = algForKey(t, v.signKey)
+	}
+
+	signer, err := jose.NewSigner(jose.SigningKey{
+		Algorithm: jose.SignatureAlgorithm(signingAlg),
+		Key:       &v.signKey,
+	}, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	jws, err := signer.Sign(payload)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	token, err := jws.CompactSerialize()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	t0 := time.Now()
+	now := func() time.Time { return t0 }
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	server := httptest.NewServer(newKeyServer(v.pubKeys...))
+	defer server.Close()
+
+	verifier := newVerifier(newRemoteKeySet(ctx, server.URL, now), &v.config)
+
+	if _, err := verifier.Verify(ctx, token); err != nil {
+		if !v.wantErr {
+			t.Errorf("%s: verify %v", v.name, err)
+		}
+	} else {
+		if v.wantErr {
+			t.Errorf("%s: expected error", v.name)
+		}
+	}
+}