From 42b4255e5492300952f6bde915716097974c6df8 Mon Sep 17 00:00:00 2001
From: Angus Lees <gus@inodes.org>
Date: Fri, 7 Jul 2017 11:45:25 +1000
Subject: [PATCH] Anonymously import all auth providers for registration side
 effect

---
 cmd/root.go                                   |   4 +-
 vendor/github.com/coreos/go-oidc/LICENSE      | 202 +++++
 vendor/github.com/coreos/go-oidc/NOTICE       |   5 +
 .../github.com/coreos/go-oidc/http/client.go  |   7 +
 vendor/github.com/coreos/go-oidc/http/doc.go  |   2 +
 vendor/github.com/coreos/go-oidc/http/http.go | 156 ++++
 vendor/github.com/coreos/go-oidc/http/url.go  |  29 +
 .../github.com/coreos/go-oidc/jose/claims.go  | 126 +++
 vendor/github.com/coreos/go-oidc/jose/doc.go  |   2 +
 vendor/github.com/coreos/go-oidc/jose/jose.go | 112 +++
 vendor/github.com/coreos/go-oidc/jose/jwk.go  | 135 +++
 vendor/github.com/coreos/go-oidc/jose/jws.go  |  51 ++
 vendor/github.com/coreos/go-oidc/jose/jwt.go  |  82 ++
 vendor/github.com/coreos/go-oidc/jose/sig.go  |  24 +
 .../github.com/coreos/go-oidc/jose/sig_rsa.go |  67 ++
 vendor/github.com/coreos/go-oidc/key/doc.go   |   2 +
 vendor/github.com/coreos/go-oidc/key/key.go   | 153 ++++
 .../github.com/coreos/go-oidc/key/manager.go  |  99 ++
 vendor/github.com/coreos/go-oidc/key/repo.go  |  55 ++
 .../github.com/coreos/go-oidc/key/rotate.go   | 159 ++++
 vendor/github.com/coreos/go-oidc/key/sync.go  |  91 ++
 .../github.com/coreos/go-oidc/oauth2/doc.go   |   2 +
 .../github.com/coreos/go-oidc/oauth2/error.go |  29 +
 .../coreos/go-oidc/oauth2/oauth2.go           | 416 +++++++++
 .../github.com/coreos/go-oidc/oidc/client.go  | 846 ++++++++++++++++++
 vendor/github.com/coreos/go-oidc/oidc/doc.go  |   2 +
 .../coreos/go-oidc/oidc/identity.go           |  44 +
 .../coreos/go-oidc/oidc/interface.go          |   3 +
 vendor/github.com/coreos/go-oidc/oidc/key.go  |  67 ++
 .../coreos/go-oidc/oidc/provider.go           | 687 ++++++++++++++
 .../coreos/go-oidc/oidc/transport.go          |  88 ++
 vendor/github.com/coreos/go-oidc/oidc/util.go | 109 +++
 .../coreos/go-oidc/oidc/verification.go       | 190 ++++
 vendor/github.com/coreos/pkg/LICENSE          | 202 +++++
 vendor/github.com/coreos/pkg/NOTICE           |   5 +
 vendor/github.com/coreos/pkg/health/README.md |  11 +
 vendor/github.com/coreos/pkg/health/health.go | 127 +++
 .../github.com/coreos/pkg/httputil/README.md  |  13 +
 .../github.com/coreos/pkg/httputil/cookie.go  |  21 +
 vendor/github.com/coreos/pkg/httputil/json.go |  27 +
 .../github.com/coreos/pkg/timeutil/backoff.go |  15 +
 vendor/github.com/jonboulle/clockwork/LICENSE | 201 +++++
 .../github.com/jonboulle/clockwork/README.md  |  69 ++
 .../jonboulle/clockwork/clockwork.go          | 179 ++++
 .../plugin/pkg/client/auth/oidc/OWNERS        |   4 +
 .../plugin/pkg/client/auth/oidc/oidc.go       | 333 +++++++
 .../plugin/pkg/client/auth/plugins.go         |  23 +
 vendor/vendor.json                            |  70 ++
 48 files changed, 5345 insertions(+), 1 deletion(-)
 create mode 100644 vendor/github.com/coreos/go-oidc/LICENSE
 create mode 100644 vendor/github.com/coreos/go-oidc/NOTICE
 create mode 100644 vendor/github.com/coreos/go-oidc/http/client.go
 create mode 100644 vendor/github.com/coreos/go-oidc/http/doc.go
 create mode 100644 vendor/github.com/coreos/go-oidc/http/http.go
 create mode 100644 vendor/github.com/coreos/go-oidc/http/url.go
 create mode 100644 vendor/github.com/coreos/go-oidc/jose/claims.go
 create mode 100644 vendor/github.com/coreos/go-oidc/jose/doc.go
 create mode 100644 vendor/github.com/coreos/go-oidc/jose/jose.go
 create mode 100644 vendor/github.com/coreos/go-oidc/jose/jwk.go
 create mode 100644 vendor/github.com/coreos/go-oidc/jose/jws.go
 create mode 100644 vendor/github.com/coreos/go-oidc/jose/jwt.go
 create mode 100755 vendor/github.com/coreos/go-oidc/jose/sig.go
 create mode 100755 vendor/github.com/coreos/go-oidc/jose/sig_rsa.go
 create mode 100644 vendor/github.com/coreos/go-oidc/key/doc.go
 create mode 100644 vendor/github.com/coreos/go-oidc/key/key.go
 create mode 100644 vendor/github.com/coreos/go-oidc/key/manager.go
 create mode 100644 vendor/github.com/coreos/go-oidc/key/repo.go
 create mode 100644 vendor/github.com/coreos/go-oidc/key/rotate.go
 create mode 100644 vendor/github.com/coreos/go-oidc/key/sync.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oauth2/doc.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oauth2/error.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oauth2/oauth2.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oidc/client.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oidc/doc.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oidc/identity.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oidc/interface.go
 create mode 100755 vendor/github.com/coreos/go-oidc/oidc/key.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oidc/provider.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oidc/transport.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oidc/util.go
 create mode 100644 vendor/github.com/coreos/go-oidc/oidc/verification.go
 create mode 100644 vendor/github.com/coreos/pkg/LICENSE
 create mode 100644 vendor/github.com/coreos/pkg/NOTICE
 create mode 100644 vendor/github.com/coreos/pkg/health/README.md
 create mode 100644 vendor/github.com/coreos/pkg/health/health.go
 create mode 100644 vendor/github.com/coreos/pkg/httputil/README.md
 create mode 100644 vendor/github.com/coreos/pkg/httputil/cookie.go
 create mode 100644 vendor/github.com/coreos/pkg/httputil/json.go
 create mode 100644 vendor/github.com/coreos/pkg/timeutil/backoff.go
 create mode 100644 vendor/github.com/jonboulle/clockwork/LICENSE
 create mode 100644 vendor/github.com/jonboulle/clockwork/README.md
 create mode 100644 vendor/github.com/jonboulle/clockwork/clockwork.go
 create mode 100644 vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/OWNERS
 create mode 100644 vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go
 create mode 100644 vendor/k8s.io/client-go/plugin/pkg/client/auth/plugins.go

diff --git a/cmd/root.go b/cmd/root.go
index 08a7579d..152847ce 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -36,10 +36,12 @@ import (
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/client-go/discovery"
 	"k8s.io/client-go/dynamic"
-	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
 	"k8s.io/client-go/tools/clientcmd"
 
 	"github.com/ksonnet/kubecfg/utils"
+
+	// Register auth plugins
+	_ "k8s.io/client-go/plugin/pkg/client/auth"
 )
 
 const (
diff --git a/vendor/github.com/coreos/go-oidc/LICENSE b/vendor/github.com/coreos/go-oidc/LICENSE
new file mode 100644
index 00000000..e06d2081
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/vendor/github.com/coreos/go-oidc/NOTICE b/vendor/github.com/coreos/go-oidc/NOTICE
new file mode 100644
index 00000000..b39ddfa5
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/NOTICE
@@ -0,0 +1,5 @@
+CoreOS Project
+Copyright 2014 CoreOS, Inc
+
+This product includes software developed at CoreOS, Inc.
+(http://www.coreos.com/).
diff --git a/vendor/github.com/coreos/go-oidc/http/client.go b/vendor/github.com/coreos/go-oidc/http/client.go
new file mode 100644
index 00000000..fd079b49
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/http/client.go
@@ -0,0 +1,7 @@
+package http
+
+import "net/http"
+
+type Client interface {
+	Do(*http.Request) (*http.Response, error)
+}
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 00000000..5687e8b8
--- /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/http/http.go b/vendor/github.com/coreos/go-oidc/http/http.go
new file mode 100644
index 00000000..c3f51215
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/http/http.go
@@ -0,0 +1,156 @@
+package http
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"log"
+	"net/http"
+	"net/url"
+	"path"
+	"strconv"
+	"strings"
+	"time"
+)
+
+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("go-oidc: failed to marshal %#v: %v", e, err)
+		code = http.StatusInternalServerError
+		b = []byte(`{"error":"server_error"}`)
+	}
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(code)
+	w.Write(b)
+}
+
+// BasicAuth parses a username and password from the request's
+// Authorization header. This was pulled from golang master:
+// https://codereview.appspot.com/76540043
+func BasicAuth(r *http.Request) (username, password string, ok bool) {
+	auth := r.Header.Get("Authorization")
+	if auth == "" {
+		return
+	}
+
+	if !strings.HasPrefix(auth, "Basic ") {
+		return
+	}
+	c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic "))
+	if err != nil {
+		return
+	}
+	cs := string(c)
+	s := strings.IndexByte(cs, ':')
+	if s < 0 {
+		return
+	}
+	return cs[:s], cs[s+1:], true
+}
+
+func cacheControlMaxAge(hdr string) (time.Duration, bool, error) {
+	for _, field := range strings.Split(hdr, ",") {
+		parts := strings.SplitN(strings.TrimSpace(field), "=", 2)
+		k := strings.ToLower(strings.TrimSpace(parts[0]))
+		if k != "max-age" {
+			continue
+		}
+
+		if len(parts) == 1 {
+			return 0, false, errors.New("max-age has no value")
+		}
+
+		v := strings.TrimSpace(parts[1])
+		if v == "" {
+			return 0, false, errors.New("max-age has empty value")
+		}
+
+		age, err := strconv.Atoi(v)
+		if err != nil {
+			return 0, false, err
+		}
+
+		if age <= 0 {
+			return 0, false, nil
+		}
+
+		return time.Duration(age) * time.Second, true, nil
+	}
+
+	return 0, false, nil
+}
+
+func expires(date, expires string) (time.Duration, bool, error) {
+	if date == "" || expires == "" {
+		return 0, false, nil
+	}
+
+	te, err := time.Parse(time.RFC1123, expires)
+	if err != nil {
+		return 0, false, err
+	}
+
+	td, err := time.Parse(time.RFC1123, date)
+	if err != nil {
+		return 0, false, err
+	}
+
+	ttl := te.Sub(td)
+
+	// headers indicate data already expired, caller should not
+	// have to care about this case
+	if ttl <= 0 {
+		return 0, false, nil
+	}
+
+	return ttl, true, nil
+}
+
+func Cacheable(hdr http.Header) (time.Duration, bool, error) {
+	ttl, ok, err := cacheControlMaxAge(hdr.Get("Cache-Control"))
+	if err != nil || ok {
+		return ttl, ok, err
+	}
+
+	return expires(hdr.Get("Date"), hdr.Get("Expires"))
+}
+
+// MergeQuery appends additional query values to an existing URL.
+func MergeQuery(u url.URL, q url.Values) url.URL {
+	uv := u.Query()
+	for k, vs := range q {
+		for _, v := range vs {
+			uv.Add(k, v)
+		}
+	}
+	u.RawQuery = uv.Encode()
+	return u
+}
+
+// NewResourceLocation appends a resource id to the end of the requested URL path.
+func NewResourceLocation(reqURL *url.URL, id string) string {
+	var u url.URL
+	u = *reqURL
+	u.Path = path.Join(u.Path, id)
+	u.RawQuery = ""
+	u.Fragment = ""
+	return u.String()
+}
+
+// CopyRequest returns a clone of the provided *http.Request.
+// The returned object is a shallow copy of the struct and a
+// deep copy of its Header field.
+func CopyRequest(r *http.Request) *http.Request {
+	r2 := *r
+	r2.Header = make(http.Header)
+	for k, s := range r.Header {
+		r2.Header[k] = s
+	}
+	return &r2
+}
diff --git a/vendor/github.com/coreos/go-oidc/http/url.go b/vendor/github.com/coreos/go-oidc/http/url.go
new file mode 100644
index 00000000..df60eb1a
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/http/url.go
@@ -0,0 +1,29 @@
+package http
+
+import (
+	"errors"
+	"net/url"
+)
+
+// ParseNonEmptyURL checks that a string is a parsable URL which is also not empty
+// since `url.Parse("")` does not return an error. Must contian a scheme and a host.
+func ParseNonEmptyURL(u string) (*url.URL, error) {
+	if u == "" {
+		return nil, errors.New("url is empty")
+	}
+
+	ur, err := url.Parse(u)
+	if err != nil {
+		return nil, err
+	}
+
+	if ur.Scheme == "" {
+		return nil, errors.New("url scheme is empty")
+	}
+
+	if ur.Host == "" {
+		return nil, errors.New("url host is empty")
+	}
+
+	return ur, nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/jose/claims.go b/vendor/github.com/coreos/go-oidc/jose/claims.go
new file mode 100644
index 00000000..8b48bfd2
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose/claims.go
@@ -0,0 +1,126 @@
+package jose
+
+import (
+	"encoding/json"
+	"fmt"
+	"math"
+	"time"
+)
+
+type Claims map[string]interface{}
+
+func (c Claims) Add(name string, value interface{}) {
+	c[name] = value
+}
+
+func (c Claims) StringClaim(name string) (string, bool, error) {
+	cl, ok := c[name]
+	if !ok {
+		return "", false, nil
+	}
+
+	v, ok := cl.(string)
+	if !ok {
+		return "", false, fmt.Errorf("unable to parse claim as string: %v", name)
+	}
+
+	return v, true, nil
+}
+
+func (c Claims) StringsClaim(name string) ([]string, bool, error) {
+	cl, ok := c[name]
+	if !ok {
+		return nil, false, nil
+	}
+
+	if v, ok := cl.([]string); ok {
+		return v, true, nil
+	}
+
+	// When unmarshaled, []string will become []interface{}.
+	if v, ok := cl.([]interface{}); ok {
+		var ret []string
+		for _, vv := range v {
+			str, ok := vv.(string)
+			if !ok {
+				return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name)
+			}
+			ret = append(ret, str)
+		}
+		return ret, true, nil
+	}
+
+	return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name)
+}
+
+func (c Claims) Int64Claim(name string) (int64, bool, error) {
+	cl, ok := c[name]
+	if !ok {
+		return 0, false, nil
+	}
+
+	v, ok := cl.(int64)
+	if !ok {
+		vf, ok := cl.(float64)
+		if !ok {
+			return 0, false, fmt.Errorf("unable to parse claim as int64: %v", name)
+		}
+		v = int64(vf)
+	}
+
+	return v, true, nil
+}
+
+func (c Claims) Float64Claim(name string) (float64, bool, error) {
+	cl, ok := c[name]
+	if !ok {
+		return 0, false, nil
+	}
+
+	v, ok := cl.(float64)
+	if !ok {
+		vi, ok := cl.(int64)
+		if !ok {
+			return 0, false, fmt.Errorf("unable to parse claim as float64: %v", name)
+		}
+		v = float64(vi)
+	}
+
+	return v, true, nil
+}
+
+func (c Claims) TimeClaim(name string) (time.Time, bool, error) {
+	v, ok, err := c.Float64Claim(name)
+	if !ok || err != nil {
+		return time.Time{}, ok, err
+	}
+
+	s := math.Trunc(v)
+	ns := (v - s) * math.Pow(10, 9)
+	return time.Unix(int64(s), int64(ns)).UTC(), true, nil
+}
+
+func decodeClaims(payload []byte) (Claims, error) {
+	var c Claims
+	if err := json.Unmarshal(payload, &c); err != nil {
+		return nil, fmt.Errorf("malformed JWT claims, unable to decode: %v", err)
+	}
+	return c, nil
+}
+
+func marshalClaims(c Claims) ([]byte, error) {
+	b, err := json.Marshal(c)
+	if err != nil {
+		return nil, err
+	}
+	return b, nil
+}
+
+func encodeClaims(c Claims) (string, error) {
+	b, err := marshalClaims(c)
+	if err != nil {
+		return "", err
+	}
+
+	return encodeSegment(b), nil
+}
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 00000000..b5e13217
--- /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/jose.go b/vendor/github.com/coreos/go-oidc/jose/jose.go
new file mode 100644
index 00000000..62099265
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose/jose.go
@@ -0,0 +1,112 @@
+package jose
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"strings"
+)
+
+const (
+	HeaderMediaType    = "typ"
+	HeaderKeyAlgorithm = "alg"
+	HeaderKeyID        = "kid"
+)
+
+const (
+	// Encryption Algorithm Header Parameter Values for JWS
+	// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-6
+	AlgHS256 = "HS256"
+	AlgHS384 = "HS384"
+	AlgHS512 = "HS512"
+	AlgRS256 = "RS256"
+	AlgRS384 = "RS384"
+	AlgRS512 = "RS512"
+	AlgES256 = "ES256"
+	AlgES384 = "ES384"
+	AlgES512 = "ES512"
+	AlgPS256 = "PS256"
+	AlgPS384 = "PS384"
+	AlgPS512 = "PS512"
+	AlgNone  = "none"
+)
+
+const (
+	// Algorithm Header Parameter Values for JWE
+	// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-4.1
+	AlgRSA15            = "RSA1_5"
+	AlgRSAOAEP          = "RSA-OAEP"
+	AlgRSAOAEP256       = "RSA-OAEP-256"
+	AlgA128KW           = "A128KW"
+	AlgA192KW           = "A192KW"
+	AlgA256KW           = "A256KW"
+	AlgDir              = "dir"
+	AlgECDHES           = "ECDH-ES"
+	AlgECDHESA128KW     = "ECDH-ES+A128KW"
+	AlgECDHESA192KW     = "ECDH-ES+A192KW"
+	AlgECDHESA256KW     = "ECDH-ES+A256KW"
+	AlgA128GCMKW        = "A128GCMKW"
+	AlgA192GCMKW        = "A192GCMKW"
+	AlgA256GCMKW        = "A256GCMKW"
+	AlgPBES2HS256A128KW = "PBES2-HS256+A128KW"
+	AlgPBES2HS384A192KW = "PBES2-HS384+A192KW"
+	AlgPBES2HS512A256KW = "PBES2-HS512+A256KW"
+)
+
+const (
+	// Encryption Algorithm Header Parameter Values for JWE
+	// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-22
+	EncA128CBCHS256 = "A128CBC-HS256"
+	EncA128CBCHS384 = "A128CBC-HS384"
+	EncA256CBCHS512 = "A256CBC-HS512"
+	EncA128GCM      = "A128GCM"
+	EncA192GCM      = "A192GCM"
+	EncA256GCM      = "A256GCM"
+)
+
+type JOSEHeader map[string]string
+
+func (j JOSEHeader) Validate() error {
+	if _, exists := j[HeaderKeyAlgorithm]; !exists {
+		return fmt.Errorf("header missing %q parameter", HeaderKeyAlgorithm)
+	}
+
+	return nil
+}
+
+func decodeHeader(seg string) (JOSEHeader, error) {
+	b, err := decodeSegment(seg)
+	if err != nil {
+		return nil, err
+	}
+
+	var h JOSEHeader
+	err = json.Unmarshal(b, &h)
+	if err != nil {
+		return nil, err
+	}
+
+	return h, nil
+}
+
+func encodeHeader(h JOSEHeader) (string, error) {
+	b, err := json.Marshal(h)
+	if err != nil {
+		return "", err
+	}
+
+	return encodeSegment(b), nil
+}
+
+// Decode JWT specific base64url encoding with padding stripped
+func decodeSegment(seg string) ([]byte, error) {
+	if l := len(seg) % 4; l != 0 {
+		seg += strings.Repeat("=", 4-l)
+	}
+	return base64.URLEncoding.DecodeString(seg)
+}
+
+// Encode JWT specific base64url encoding with padding stripped
+func encodeSegment(seg []byte) string {
+	return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=")
+}
diff --git a/vendor/github.com/coreos/go-oidc/jose/jwk.go b/vendor/github.com/coreos/go-oidc/jose/jwk.go
new file mode 100644
index 00000000..119f073f
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose/jwk.go
@@ -0,0 +1,135 @@
+package jose
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/binary"
+	"encoding/json"
+	"math/big"
+	"strings"
+)
+
+// JSON Web Key
+// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-36#page-5
+type JWK struct {
+	ID       string
+	Type     string
+	Alg      string
+	Use      string
+	Exponent int
+	Modulus  *big.Int
+	Secret   []byte
+}
+
+type jwkJSON struct {
+	ID       string `json:"kid"`
+	Type     string `json:"kty"`
+	Alg      string `json:"alg"`
+	Use      string `json:"use"`
+	Exponent string `json:"e"`
+	Modulus  string `json:"n"`
+}
+
+func (j *JWK) MarshalJSON() ([]byte, error) {
+	t := jwkJSON{
+		ID:       j.ID,
+		Type:     j.Type,
+		Alg:      j.Alg,
+		Use:      j.Use,
+		Exponent: encodeExponent(j.Exponent),
+		Modulus:  encodeModulus(j.Modulus),
+	}
+
+	return json.Marshal(&t)
+}
+
+func (j *JWK) UnmarshalJSON(data []byte) error {
+	var t jwkJSON
+	err := json.Unmarshal(data, &t)
+	if err != nil {
+		return err
+	}
+
+	e, err := decodeExponent(t.Exponent)
+	if err != nil {
+		return err
+	}
+
+	n, err := decodeModulus(t.Modulus)
+	if err != nil {
+		return err
+	}
+
+	j.ID = t.ID
+	j.Type = t.Type
+	j.Alg = t.Alg
+	j.Use = t.Use
+	j.Exponent = e
+	j.Modulus = n
+
+	return nil
+}
+
+type JWKSet struct {
+	Keys []JWK `json:"keys"`
+}
+
+func decodeExponent(e string) (int, error) {
+	decE, err := decodeBase64URLPaddingOptional(e)
+	if err != nil {
+		return 0, err
+	}
+	var eBytes []byte
+	if len(decE) < 8 {
+		eBytes = make([]byte, 8-len(decE), 8)
+		eBytes = append(eBytes, decE...)
+	} else {
+		eBytes = decE
+	}
+	eReader := bytes.NewReader(eBytes)
+	var E uint64
+	err = binary.Read(eReader, binary.BigEndian, &E)
+	if err != nil {
+		return 0, err
+	}
+	return int(E), nil
+}
+
+func encodeExponent(e int) string {
+	b := make([]byte, 8)
+	binary.BigEndian.PutUint64(b, uint64(e))
+	var idx int
+	for ; idx < 8; idx++ {
+		if b[idx] != 0x0 {
+			break
+		}
+	}
+	return base64.RawURLEncoding.EncodeToString(b[idx:])
+}
+
+// Turns a URL encoded modulus of a key into a big int.
+func decodeModulus(n string) (*big.Int, error) {
+	decN, err := decodeBase64URLPaddingOptional(n)
+	if err != nil {
+		return nil, err
+	}
+	N := big.NewInt(0)
+	N.SetBytes(decN)
+	return N, nil
+}
+
+func encodeModulus(n *big.Int) string {
+	return base64.RawURLEncoding.EncodeToString(n.Bytes())
+}
+
+// decodeBase64URLPaddingOptional decodes Base64 whether there is padding or not.
+// The stdlib version currently doesn't handle this.
+// We can get rid of this is if this bug:
+//   https://github.com/golang/go/issues/4237
+// ever closes.
+func decodeBase64URLPaddingOptional(e string) ([]byte, error) {
+	if m := len(e) % 4; m != 0 {
+		e += strings.Repeat("=", 4-m)
+	}
+	return base64.URLEncoding.DecodeString(e)
+}
diff --git a/vendor/github.com/coreos/go-oidc/jose/jws.go b/vendor/github.com/coreos/go-oidc/jose/jws.go
new file mode 100644
index 00000000..1049ece8
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose/jws.go
@@ -0,0 +1,51 @@
+package jose
+
+import (
+	"fmt"
+	"strings"
+)
+
+type JWS struct {
+	RawHeader  string
+	Header     JOSEHeader
+	RawPayload string
+	Payload    []byte
+	Signature  []byte
+}
+
+// Given a raw encoded JWS token parses it and verifies the structure.
+func ParseJWS(raw string) (JWS, error) {
+	parts := strings.Split(raw, ".")
+	if len(parts) != 3 {
+		return JWS{}, fmt.Errorf("malformed JWS, only %d segments", len(parts))
+	}
+
+	rawSig := parts[2]
+	jws := JWS{
+		RawHeader:  parts[0],
+		RawPayload: parts[1],
+	}
+
+	header, err := decodeHeader(jws.RawHeader)
+	if err != nil {
+		return JWS{}, fmt.Errorf("malformed JWS, unable to decode header, %s", err)
+	}
+	if err = header.Validate(); err != nil {
+		return JWS{}, fmt.Errorf("malformed JWS, %s", err)
+	}
+	jws.Header = header
+
+	payload, err := decodeSegment(jws.RawPayload)
+	if err != nil {
+		return JWS{}, fmt.Errorf("malformed JWS, unable to decode payload: %s", err)
+	}
+	jws.Payload = payload
+
+	sig, err := decodeSegment(rawSig)
+	if err != nil {
+		return JWS{}, fmt.Errorf("malformed JWS, unable to decode signature: %s", err)
+	}
+	jws.Signature = sig
+
+	return jws, nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/jose/jwt.go b/vendor/github.com/coreos/go-oidc/jose/jwt.go
new file mode 100644
index 00000000..3b3e9634
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose/jwt.go
@@ -0,0 +1,82 @@
+package jose
+
+import "strings"
+
+type JWT JWS
+
+func ParseJWT(token string) (jwt JWT, err error) {
+	jws, err := ParseJWS(token)
+	if err != nil {
+		return
+	}
+
+	return JWT(jws), nil
+}
+
+func NewJWT(header JOSEHeader, claims Claims) (jwt JWT, err error) {
+	jwt = JWT{}
+
+	jwt.Header = header
+	jwt.Header[HeaderMediaType] = "JWT"
+
+	claimBytes, err := marshalClaims(claims)
+	if err != nil {
+		return
+	}
+	jwt.Payload = claimBytes
+
+	eh, err := encodeHeader(header)
+	if err != nil {
+		return
+	}
+	jwt.RawHeader = eh
+
+	ec, err := encodeClaims(claims)
+	if err != nil {
+		return
+	}
+	jwt.RawPayload = ec
+
+	return
+}
+
+func (j *JWT) KeyID() (string, bool) {
+	kID, ok := j.Header[HeaderKeyID]
+	return kID, ok
+}
+
+func (j *JWT) Claims() (Claims, error) {
+	return decodeClaims(j.Payload)
+}
+
+// Encoded data part of the token which may be signed.
+func (j *JWT) Data() string {
+	return strings.Join([]string{j.RawHeader, j.RawPayload}, ".")
+}
+
+// Full encoded JWT token string in format: header.claims.signature
+func (j *JWT) Encode() string {
+	d := j.Data()
+	s := encodeSegment(j.Signature)
+	return strings.Join([]string{d, s}, ".")
+}
+
+func NewSignedJWT(claims Claims, s Signer) (*JWT, error) {
+	header := JOSEHeader{
+		HeaderKeyAlgorithm: s.Alg(),
+		HeaderKeyID:        s.ID(),
+	}
+
+	jwt, err := NewJWT(header, claims)
+	if err != nil {
+		return nil, err
+	}
+
+	sig, err := s.Sign([]byte(jwt.Data()))
+	if err != nil {
+		return nil, err
+	}
+	jwt.Signature = sig
+
+	return &jwt, nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/jose/sig.go b/vendor/github.com/coreos/go-oidc/jose/sig.go
new file mode 100755
index 00000000..7b2b253c
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose/sig.go
@@ -0,0 +1,24 @@
+package jose
+
+import (
+	"fmt"
+)
+
+type Verifier interface {
+	ID() string
+	Alg() string
+	Verify(sig []byte, data []byte) error
+}
+
+type Signer interface {
+	Verifier
+	Sign(data []byte) (sig []byte, err error)
+}
+
+func NewVerifier(jwk JWK) (Verifier, error) {
+	if jwk.Type != "RSA" {
+		return nil, fmt.Errorf("unsupported key type %q", jwk.Type)
+	}
+
+	return NewVerifierRSA(jwk)
+}
diff --git a/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go b/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go
new file mode 100755
index 00000000..004e45dd
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go
@@ -0,0 +1,67 @@
+package jose
+
+import (
+	"crypto"
+	"crypto/rand"
+	"crypto/rsa"
+	"fmt"
+)
+
+type VerifierRSA struct {
+	KeyID     string
+	Hash      crypto.Hash
+	PublicKey rsa.PublicKey
+}
+
+type SignerRSA struct {
+	PrivateKey rsa.PrivateKey
+	VerifierRSA
+}
+
+func NewVerifierRSA(jwk JWK) (*VerifierRSA, error) {
+	if jwk.Alg != "" && jwk.Alg != "RS256" {
+		return nil, fmt.Errorf("unsupported key algorithm %q", jwk.Alg)
+	}
+
+	v := VerifierRSA{
+		KeyID: jwk.ID,
+		PublicKey: rsa.PublicKey{
+			N: jwk.Modulus,
+			E: jwk.Exponent,
+		},
+		Hash: crypto.SHA256,
+	}
+
+	return &v, nil
+}
+
+func NewSignerRSA(kid string, key rsa.PrivateKey) *SignerRSA {
+	return &SignerRSA{
+		PrivateKey: key,
+		VerifierRSA: VerifierRSA{
+			KeyID:     kid,
+			PublicKey: key.PublicKey,
+			Hash:      crypto.SHA256,
+		},
+	}
+}
+
+func (v *VerifierRSA) ID() string {
+	return v.KeyID
+}
+
+func (v *VerifierRSA) Alg() string {
+	return "RS256"
+}
+
+func (v *VerifierRSA) Verify(sig []byte, data []byte) error {
+	h := v.Hash.New()
+	h.Write(data)
+	return rsa.VerifyPKCS1v15(&v.PublicKey, v.Hash, h.Sum(nil), sig)
+}
+
+func (s *SignerRSA) Sign(data []byte) ([]byte, error) {
+	h := s.Hash.New()
+	h.Write(data)
+	return rsa.SignPKCS1v15(rand.Reader, &s.PrivateKey, s.Hash, h.Sum(nil))
+}
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 00000000..936eec74
--- /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/key/key.go b/vendor/github.com/coreos/go-oidc/key/key.go
new file mode 100644
index 00000000..208c1fc1
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/key/key.go
@@ -0,0 +1,153 @@
+package key
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"encoding/hex"
+	"encoding/json"
+	"io"
+	"time"
+
+	"github.com/coreos/go-oidc/jose"
+)
+
+func NewPublicKey(jwk jose.JWK) *PublicKey {
+	return &PublicKey{jwk: jwk}
+}
+
+type PublicKey struct {
+	jwk jose.JWK
+}
+
+func (k *PublicKey) MarshalJSON() ([]byte, error) {
+	return json.Marshal(&k.jwk)
+}
+
+func (k *PublicKey) UnmarshalJSON(data []byte) error {
+	var jwk jose.JWK
+	if err := json.Unmarshal(data, &jwk); err != nil {
+		return err
+	}
+	k.jwk = jwk
+	return nil
+}
+
+func (k *PublicKey) ID() string {
+	return k.jwk.ID
+}
+
+func (k *PublicKey) Verifier() (jose.Verifier, error) {
+	return jose.NewVerifierRSA(k.jwk)
+}
+
+type PrivateKey struct {
+	KeyID      string
+	PrivateKey *rsa.PrivateKey
+}
+
+func (k *PrivateKey) ID() string {
+	return k.KeyID
+}
+
+func (k *PrivateKey) Signer() jose.Signer {
+	return jose.NewSignerRSA(k.ID(), *k.PrivateKey)
+}
+
+func (k *PrivateKey) JWK() jose.JWK {
+	return jose.JWK{
+		ID:       k.KeyID,
+		Type:     "RSA",
+		Alg:      "RS256",
+		Use:      "sig",
+		Exponent: k.PrivateKey.PublicKey.E,
+		Modulus:  k.PrivateKey.PublicKey.N,
+	}
+}
+
+type KeySet interface {
+	ExpiresAt() time.Time
+}
+
+type PublicKeySet struct {
+	keys      []PublicKey
+	index     map[string]*PublicKey
+	expiresAt time.Time
+}
+
+func NewPublicKeySet(jwks []jose.JWK, exp time.Time) *PublicKeySet {
+	keys := make([]PublicKey, len(jwks))
+	index := make(map[string]*PublicKey)
+	for i, jwk := range jwks {
+		keys[i] = *NewPublicKey(jwk)
+		index[keys[i].ID()] = &keys[i]
+	}
+	return &PublicKeySet{
+		keys:      keys,
+		index:     index,
+		expiresAt: exp,
+	}
+}
+
+func (s *PublicKeySet) ExpiresAt() time.Time {
+	return s.expiresAt
+}
+
+func (s *PublicKeySet) Keys() []PublicKey {
+	return s.keys
+}
+
+func (s *PublicKeySet) Key(id string) *PublicKey {
+	return s.index[id]
+}
+
+type PrivateKeySet struct {
+	keys        []*PrivateKey
+	ActiveKeyID string
+	expiresAt   time.Time
+}
+
+func NewPrivateKeySet(keys []*PrivateKey, exp time.Time) *PrivateKeySet {
+	return &PrivateKeySet{
+		keys:        keys,
+		ActiveKeyID: keys[0].ID(),
+		expiresAt:   exp.UTC(),
+	}
+}
+
+func (s *PrivateKeySet) Keys() []*PrivateKey {
+	return s.keys
+}
+
+func (s *PrivateKeySet) ExpiresAt() time.Time {
+	return s.expiresAt
+}
+
+func (s *PrivateKeySet) Active() *PrivateKey {
+	for i, k := range s.keys {
+		if k.ID() == s.ActiveKeyID {
+			return s.keys[i]
+		}
+	}
+
+	return nil
+}
+
+type GeneratePrivateKeyFunc func() (*PrivateKey, error)
+
+func GeneratePrivateKey() (*PrivateKey, error) {
+	pk, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		return nil, err
+	}
+	keyID := make([]byte, 20)
+	if _, err := io.ReadFull(rand.Reader, keyID); err != nil {
+		return nil, err
+	}
+
+	k := PrivateKey{
+		KeyID:      hex.EncodeToString(keyID),
+		PrivateKey: pk,
+	}
+
+	return &k, nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/key/manager.go b/vendor/github.com/coreos/go-oidc/key/manager.go
new file mode 100644
index 00000000..476ab6a8
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/key/manager.go
@@ -0,0 +1,99 @@
+package key
+
+import (
+	"errors"
+	"time"
+
+	"github.com/jonboulle/clockwork"
+
+	"github.com/coreos/go-oidc/jose"
+	"github.com/coreos/pkg/health"
+)
+
+type PrivateKeyManager interface {
+	ExpiresAt() time.Time
+	Signer() (jose.Signer, error)
+	JWKs() ([]jose.JWK, error)
+	PublicKeys() ([]PublicKey, error)
+
+	WritableKeySetRepo
+	health.Checkable
+}
+
+func NewPrivateKeyManager() PrivateKeyManager {
+	return &privateKeyManager{
+		clock: clockwork.NewRealClock(),
+	}
+}
+
+type privateKeyManager struct {
+	keySet *PrivateKeySet
+	clock  clockwork.Clock
+}
+
+func (m *privateKeyManager) ExpiresAt() time.Time {
+	if m.keySet == nil {
+		return m.clock.Now().UTC()
+	}
+
+	return m.keySet.ExpiresAt()
+}
+
+func (m *privateKeyManager) Signer() (jose.Signer, error) {
+	if err := m.Healthy(); err != nil {
+		return nil, err
+	}
+
+	return m.keySet.Active().Signer(), nil
+}
+
+func (m *privateKeyManager) JWKs() ([]jose.JWK, error) {
+	if err := m.Healthy(); err != nil {
+		return nil, err
+	}
+
+	keys := m.keySet.Keys()
+	jwks := make([]jose.JWK, len(keys))
+	for i, k := range keys {
+		jwks[i] = k.JWK()
+	}
+	return jwks, nil
+}
+
+func (m *privateKeyManager) PublicKeys() ([]PublicKey, error) {
+	jwks, err := m.JWKs()
+	if err != nil {
+		return nil, err
+	}
+	keys := make([]PublicKey, len(jwks))
+	for i, jwk := range jwks {
+		keys[i] = *NewPublicKey(jwk)
+	}
+	return keys, nil
+}
+
+func (m *privateKeyManager) Healthy() error {
+	if m.keySet == nil {
+		return errors.New("private key manager uninitialized")
+	}
+
+	if len(m.keySet.Keys()) == 0 {
+		return errors.New("private key manager zero keys")
+	}
+
+	if m.keySet.ExpiresAt().Before(m.clock.Now().UTC()) {
+		return errors.New("private key manager keys expired")
+	}
+
+	return nil
+}
+
+func (m *privateKeyManager) Set(keySet KeySet) error {
+	privKeySet, ok := keySet.(*PrivateKeySet)
+	if !ok {
+		return errors.New("unable to cast to PrivateKeySet")
+	}
+
+	m.keySet = privKeySet
+	return nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/key/repo.go b/vendor/github.com/coreos/go-oidc/key/repo.go
new file mode 100644
index 00000000..1acdeb36
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/key/repo.go
@@ -0,0 +1,55 @@
+package key
+
+import (
+	"errors"
+	"sync"
+)
+
+var ErrorNoKeys = errors.New("no keys found")
+
+type WritableKeySetRepo interface {
+	Set(KeySet) error
+}
+
+type ReadableKeySetRepo interface {
+	Get() (KeySet, error)
+}
+
+type PrivateKeySetRepo interface {
+	WritableKeySetRepo
+	ReadableKeySetRepo
+}
+
+func NewPrivateKeySetRepo() PrivateKeySetRepo {
+	return &memPrivateKeySetRepo{}
+}
+
+type memPrivateKeySetRepo struct {
+	mu  sync.RWMutex
+	pks PrivateKeySet
+}
+
+func (r *memPrivateKeySetRepo) Set(ks KeySet) error {
+	pks, ok := ks.(*PrivateKeySet)
+	if !ok {
+		return errors.New("unable to cast to PrivateKeySet")
+	} else if pks == nil {
+		return errors.New("nil KeySet")
+	}
+
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	r.pks = *pks
+	return nil
+}
+
+func (r *memPrivateKeySetRepo) Get() (KeySet, error) {
+	r.mu.RLock()
+	defer r.mu.RUnlock()
+
+	if r.pks.keys == nil {
+		return nil, ErrorNoKeys
+	}
+	return KeySet(&r.pks), nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/key/rotate.go b/vendor/github.com/coreos/go-oidc/key/rotate.go
new file mode 100644
index 00000000..bc6cdfb1
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/key/rotate.go
@@ -0,0 +1,159 @@
+package key
+
+import (
+	"errors"
+	"log"
+	"time"
+
+	ptime "github.com/coreos/pkg/timeutil"
+	"github.com/jonboulle/clockwork"
+)
+
+var (
+	ErrorPrivateKeysExpired = errors.New("private keys have expired")
+)
+
+func NewPrivateKeyRotator(repo PrivateKeySetRepo, ttl time.Duration) *PrivateKeyRotator {
+	return &PrivateKeyRotator{
+		repo: repo,
+		ttl:  ttl,
+
+		keep:        2,
+		generateKey: GeneratePrivateKey,
+		clock:       clockwork.NewRealClock(),
+	}
+}
+
+type PrivateKeyRotator struct {
+	repo        PrivateKeySetRepo
+	generateKey GeneratePrivateKeyFunc
+	clock       clockwork.Clock
+	keep        int
+	ttl         time.Duration
+}
+
+func (r *PrivateKeyRotator) expiresAt() time.Time {
+	return r.clock.Now().UTC().Add(r.ttl)
+}
+
+func (r *PrivateKeyRotator) Healthy() error {
+	pks, err := r.privateKeySet()
+	if err != nil {
+		return err
+	}
+
+	if r.clock.Now().After(pks.ExpiresAt()) {
+		return ErrorPrivateKeysExpired
+	}
+
+	return nil
+}
+
+func (r *PrivateKeyRotator) privateKeySet() (*PrivateKeySet, error) {
+	ks, err := r.repo.Get()
+	if err != nil {
+		return nil, err
+	}
+
+	pks, ok := ks.(*PrivateKeySet)
+	if !ok {
+		return nil, errors.New("unable to cast to PrivateKeySet")
+	}
+	return pks, nil
+}
+
+func (r *PrivateKeyRotator) nextRotation() (time.Duration, error) {
+	pks, err := r.privateKeySet()
+	if err == ErrorNoKeys {
+		return 0, nil
+	}
+	if err != nil {
+		return 0, err
+	}
+
+	now := r.clock.Now()
+
+	// Ideally, we want to rotate after half the TTL has elapsed.
+	idealRotationTime := pks.ExpiresAt().Add(-r.ttl / 2)
+
+	// If we are past the ideal rotation time, rotate immediatly.
+	return max(0, idealRotationTime.Sub(now)), nil
+}
+
+func max(a, b time.Duration) time.Duration {
+	if a > b {
+		return a
+	}
+	return b
+}
+
+func (r *PrivateKeyRotator) Run() chan struct{} {
+	attempt := func() {
+		k, err := r.generateKey()
+		if err != nil {
+			log.Printf("go-oidc: failed generating signing key: %v", err)
+			return
+		}
+
+		exp := r.expiresAt()
+		if err := rotatePrivateKeys(r.repo, k, r.keep, exp); err != nil {
+			log.Printf("go-oidc: key rotation failed: %v", err)
+			return
+		}
+	}
+
+	stop := make(chan struct{})
+	go func() {
+		for {
+			var nextRotation time.Duration
+			var sleep time.Duration
+			var err error
+			for {
+				if nextRotation, err = r.nextRotation(); err == nil {
+					break
+				}
+				sleep = ptime.ExpBackoff(sleep, time.Minute)
+				log.Printf("go-oidc: error getting nextRotation, retrying in %v: %v", sleep, err)
+				time.Sleep(sleep)
+			}
+
+			select {
+			case <-r.clock.After(nextRotation):
+				attempt()
+			case <-stop:
+				return
+			}
+		}
+	}()
+
+	return stop
+}
+
+func rotatePrivateKeys(repo PrivateKeySetRepo, k *PrivateKey, keep int, exp time.Time) error {
+	ks, err := repo.Get()
+	if err != nil && err != ErrorNoKeys {
+		return err
+	}
+
+	var keys []*PrivateKey
+	if ks != nil {
+		pks, ok := ks.(*PrivateKeySet)
+		if !ok {
+			return errors.New("unable to cast to PrivateKeySet")
+		}
+		keys = pks.Keys()
+	}
+
+	keys = append([]*PrivateKey{k}, keys...)
+	if l := len(keys); l > keep {
+		keys = keys[0:keep]
+	}
+
+	nks := PrivateKeySet{
+		keys:        keys,
+		ActiveKeyID: k.ID(),
+		expiresAt:   exp,
+	}
+
+	return repo.Set(KeySet(&nks))
+}
diff --git a/vendor/github.com/coreos/go-oidc/key/sync.go b/vendor/github.com/coreos/go-oidc/key/sync.go
new file mode 100644
index 00000000..b887f7b5
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/key/sync.go
@@ -0,0 +1,91 @@
+package key
+
+import (
+	"errors"
+	"log"
+	"time"
+
+	"github.com/jonboulle/clockwork"
+
+	"github.com/coreos/pkg/timeutil"
+)
+
+func NewKeySetSyncer(r ReadableKeySetRepo, w WritableKeySetRepo) *KeySetSyncer {
+	return &KeySetSyncer{
+		readable: r,
+		writable: w,
+		clock:    clockwork.NewRealClock(),
+	}
+}
+
+type KeySetSyncer struct {
+	readable ReadableKeySetRepo
+	writable WritableKeySetRepo
+	clock    clockwork.Clock
+}
+
+func (s *KeySetSyncer) Run() chan struct{} {
+	stop := make(chan struct{})
+	go func() {
+		var failing bool
+		var next time.Duration
+		for {
+			exp, err := syncKeySet(s.readable, s.writable, s.clock)
+			if err != nil || exp == 0 {
+				if !failing {
+					failing = true
+					next = time.Second
+				} else {
+					next = timeutil.ExpBackoff(next, time.Minute)
+				}
+				if exp == 0 {
+					log.Printf("Synced to already expired key set, retrying in %v: %v", next, err)
+
+				} else {
+					log.Printf("Failed syncing key set, retrying in %v: %v", next, err)
+				}
+			} else {
+				failing = false
+				next = exp / 2
+			}
+
+			select {
+			case <-s.clock.After(next):
+				continue
+			case <-stop:
+				return
+			}
+		}
+	}()
+
+	return stop
+}
+
+func Sync(r ReadableKeySetRepo, w WritableKeySetRepo) (time.Duration, error) {
+	return syncKeySet(r, w, clockwork.NewRealClock())
+}
+
+// syncKeySet copies the keyset from r to the KeySet at w and returns the duration in which the KeySet will expire.
+// If keyset has already expired, returns a zero duration.
+func syncKeySet(r ReadableKeySetRepo, w WritableKeySetRepo, clock clockwork.Clock) (exp time.Duration, err error) {
+	var ks KeySet
+	ks, err = r.Get()
+	if err != nil {
+		return
+	}
+
+	if ks == nil {
+		err = errors.New("no source KeySet")
+		return
+	}
+
+	if err = w.Set(ks); err != nil {
+		return
+	}
+
+	now := clock.Now()
+	if ks.ExpiresAt().After(now) {
+		exp = ks.ExpiresAt().Sub(now)
+	}
+	return
+}
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 00000000..52eb3085
--- /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/oauth2/error.go b/vendor/github.com/coreos/go-oidc/oauth2/error.go
new file mode 100644
index 00000000..50d89094
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oauth2/error.go
@@ -0,0 +1,29 @@
+package oauth2
+
+const (
+	ErrorAccessDenied            = "access_denied"
+	ErrorInvalidClient           = "invalid_client"
+	ErrorInvalidGrant            = "invalid_grant"
+	ErrorInvalidRequest          = "invalid_request"
+	ErrorServerError             = "server_error"
+	ErrorUnauthorizedClient      = "unauthorized_client"
+	ErrorUnsupportedGrantType    = "unsupported_grant_type"
+	ErrorUnsupportedResponseType = "unsupported_response_type"
+)
+
+type Error struct {
+	Type        string `json:"error"`
+	Description string `json:"error_description,omitempty"`
+	State       string `json:"state,omitempty"`
+}
+
+func (e *Error) Error() string {
+	if e.Description != "" {
+		return e.Type + ": " + e.Description
+	}
+	return e.Type
+}
+
+func NewError(typ string) *Error {
+	return &Error{Type: typ}
+}
diff --git a/vendor/github.com/coreos/go-oidc/oauth2/oauth2.go b/vendor/github.com/coreos/go-oidc/oauth2/oauth2.go
new file mode 100644
index 00000000..72d1d671
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oauth2/oauth2.go
@@ -0,0 +1,416 @@
+package oauth2
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"mime"
+	"net/http"
+	"net/url"
+	"sort"
+	"strconv"
+	"strings"
+
+	phttp "github.com/coreos/go-oidc/http"
+)
+
+// ResponseTypesEqual compares two response_type values. If either
+// contains a space, it is treated as an unordered list. For example,
+// comparing "code id_token" and "id_token code" would evaluate to true.
+func ResponseTypesEqual(r1, r2 string) bool {
+	if !strings.Contains(r1, " ") || !strings.Contains(r2, " ") {
+		// fast route, no split needed
+		return r1 == r2
+	}
+
+	// split, sort, and compare
+	r1Fields := strings.Fields(r1)
+	r2Fields := strings.Fields(r2)
+	if len(r1Fields) != len(r2Fields) {
+		return false
+	}
+	sort.Strings(r1Fields)
+	sort.Strings(r2Fields)
+	for i, r1Field := range r1Fields {
+		if r1Field != r2Fields[i] {
+			return false
+		}
+	}
+	return true
+}
+
+const (
+	// OAuth2.0 response types registered by OIDC.
+	//
+	// See: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#RegistryContents
+	ResponseTypeCode             = "code"
+	ResponseTypeCodeIDToken      = "code id_token"
+	ResponseTypeCodeIDTokenToken = "code id_token token"
+	ResponseTypeIDToken          = "id_token"
+	ResponseTypeIDTokenToken     = "id_token token"
+	ResponseTypeToken            = "token"
+	ResponseTypeNone             = "none"
+)
+
+const (
+	GrantTypeAuthCode     = "authorization_code"
+	GrantTypeClientCreds  = "client_credentials"
+	GrantTypeUserCreds    = "password"
+	GrantTypeImplicit     = "implicit"
+	GrantTypeRefreshToken = "refresh_token"
+
+	AuthMethodClientSecretPost  = "client_secret_post"
+	AuthMethodClientSecretBasic = "client_secret_basic"
+	AuthMethodClientSecretJWT   = "client_secret_jwt"
+	AuthMethodPrivateKeyJWT     = "private_key_jwt"
+)
+
+type Config struct {
+	Credentials ClientCredentials
+	Scope       []string
+	RedirectURL string
+	AuthURL     string
+	TokenURL    string
+
+	// Must be one of the AuthMethodXXX methods above. Right now, only
+	// AuthMethodClientSecretPost and AuthMethodClientSecretBasic are supported.
+	AuthMethod string
+}
+
+type Client struct {
+	hc          phttp.Client
+	creds       ClientCredentials
+	scope       []string
+	authURL     *url.URL
+	redirectURL *url.URL
+	tokenURL    *url.URL
+	authMethod  string
+}
+
+type ClientCredentials struct {
+	ID     string
+	Secret string
+}
+
+func NewClient(hc phttp.Client, cfg Config) (c *Client, err error) {
+	if len(cfg.Credentials.ID) == 0 {
+		err = errors.New("missing client id")
+		return
+	}
+
+	if len(cfg.Credentials.Secret) == 0 {
+		err = errors.New("missing client secret")
+		return
+	}
+
+	if cfg.AuthMethod == "" {
+		cfg.AuthMethod = AuthMethodClientSecretBasic
+	} else if cfg.AuthMethod != AuthMethodClientSecretPost && cfg.AuthMethod != AuthMethodClientSecretBasic {
+		err = fmt.Errorf("auth method %q is not supported", cfg.AuthMethod)
+		return
+	}
+
+	au, err := phttp.ParseNonEmptyURL(cfg.AuthURL)
+	if err != nil {
+		return
+	}
+
+	tu, err := phttp.ParseNonEmptyURL(cfg.TokenURL)
+	if err != nil {
+		return
+	}
+
+	// Allow empty redirect URL in the case where the client
+	// only needs to verify a given token.
+	ru, err := url.Parse(cfg.RedirectURL)
+	if err != nil {
+		return
+	}
+
+	c = &Client{
+		creds:       cfg.Credentials,
+		scope:       cfg.Scope,
+		redirectURL: ru,
+		authURL:     au,
+		tokenURL:    tu,
+		hc:          hc,
+		authMethod:  cfg.AuthMethod,
+	}
+
+	return
+}
+
+// Return the embedded HTTP client
+func (c *Client) HttpClient() phttp.Client {
+	return c.hc
+}
+
+// Generate the url for initial redirect to oauth provider.
+func (c *Client) AuthCodeURL(state, accessType, prompt string) string {
+	v := c.commonURLValues()
+	v.Set("state", state)
+	if strings.ToLower(accessType) == "offline" {
+		v.Set("access_type", "offline")
+	}
+
+	if prompt != "" {
+		v.Set("prompt", prompt)
+	}
+	v.Set("response_type", "code")
+
+	q := v.Encode()
+	u := *c.authURL
+	if u.RawQuery == "" {
+		u.RawQuery = q
+	} else {
+		u.RawQuery += "&" + q
+	}
+	return u.String()
+}
+
+func (c *Client) commonURLValues() url.Values {
+	return url.Values{
+		"redirect_uri": {c.redirectURL.String()},
+		"scope":        {strings.Join(c.scope, " ")},
+		"client_id":    {c.creds.ID},
+	}
+}
+
+func (c *Client) newAuthenticatedRequest(urlToken string, values url.Values) (*http.Request, error) {
+	var req *http.Request
+	var err error
+	switch c.authMethod {
+	case AuthMethodClientSecretPost:
+		values.Set("client_secret", c.creds.Secret)
+		req, err = http.NewRequest("POST", urlToken, strings.NewReader(values.Encode()))
+		if err != nil {
+			return nil, err
+		}
+	case AuthMethodClientSecretBasic:
+		req, err = http.NewRequest("POST", urlToken, strings.NewReader(values.Encode()))
+		if err != nil {
+			return nil, err
+		}
+		encodedID := url.QueryEscape(c.creds.ID)
+		encodedSecret := url.QueryEscape(c.creds.Secret)
+		req.SetBasicAuth(encodedID, encodedSecret)
+	default:
+		panic("misconfigured client: auth method not supported")
+	}
+
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+	return req, nil
+
+}
+
+// ClientCredsToken posts the client id and secret to obtain a token scoped to the OAuth2 client via the "client_credentials" grant type.
+// May not be supported by all OAuth2 servers.
+func (c *Client) ClientCredsToken(scope []string) (result TokenResponse, err error) {
+	v := url.Values{
+		"scope":      {strings.Join(scope, " ")},
+		"grant_type": {GrantTypeClientCreds},
+	}
+
+	req, err := c.newAuthenticatedRequest(c.tokenURL.String(), v)
+	if err != nil {
+		return
+	}
+
+	resp, err := c.hc.Do(req)
+	if err != nil {
+		return
+	}
+	defer resp.Body.Close()
+
+	return parseTokenResponse(resp)
+}
+
+// UserCredsToken posts the username and password to obtain a token scoped to the OAuth2 client via the "password" grant_type
+// May not be supported by all OAuth2 servers.
+func (c *Client) UserCredsToken(username, password string) (result TokenResponse, err error) {
+	v := url.Values{
+		"scope":      {strings.Join(c.scope, " ")},
+		"grant_type": {GrantTypeUserCreds},
+		"username":   {username},
+		"password":   {password},
+	}
+
+	req, err := c.newAuthenticatedRequest(c.tokenURL.String(), v)
+	if err != nil {
+		return
+	}
+
+	resp, err := c.hc.Do(req)
+	if err != nil {
+		return
+	}
+	defer resp.Body.Close()
+
+	return parseTokenResponse(resp)
+}
+
+// RequestToken requests a token from the Token Endpoint with the specified grantType.
+// If 'grantType' == GrantTypeAuthCode, then 'value' should be the authorization code.
+// If 'grantType' == GrantTypeRefreshToken, then 'value' should be the refresh token.
+func (c *Client) RequestToken(grantType, value string) (result TokenResponse, err error) {
+	v := c.commonURLValues()
+
+	v.Set("grant_type", grantType)
+	v.Set("client_secret", c.creds.Secret)
+	switch grantType {
+	case GrantTypeAuthCode:
+		v.Set("code", value)
+	case GrantTypeRefreshToken:
+		v.Set("refresh_token", value)
+	default:
+		err = fmt.Errorf("unsupported grant_type: %v", grantType)
+		return
+	}
+
+	req, err := c.newAuthenticatedRequest(c.tokenURL.String(), v)
+	if err != nil {
+		return
+	}
+
+	resp, err := c.hc.Do(req)
+	if err != nil {
+		return
+	}
+	defer resp.Body.Close()
+
+	return parseTokenResponse(resp)
+}
+
+func parseTokenResponse(resp *http.Response) (result TokenResponse, err error) {
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return
+	}
+	badStatusCode := resp.StatusCode < 200 || resp.StatusCode > 299
+
+	contentType, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
+	if err != nil {
+		return
+	}
+
+	result = TokenResponse{
+		RawBody: body,
+	}
+
+	newError := func(typ, desc, state string) error {
+		if typ == "" {
+			return fmt.Errorf("unrecognized error %s", body)
+		}
+		return &Error{typ, desc, state}
+	}
+
+	if contentType == "application/x-www-form-urlencoded" || contentType == "text/plain" {
+		var vals url.Values
+		vals, err = url.ParseQuery(string(body))
+		if err != nil {
+			return
+		}
+		if error := vals.Get("error"); error != "" || badStatusCode {
+			err = newError(error, vals.Get("error_description"), vals.Get("state"))
+			return
+		}
+		e := vals.Get("expires_in")
+		if e == "" {
+			e = vals.Get("expires")
+		}
+		if e != "" {
+			result.Expires, err = strconv.Atoi(e)
+			if err != nil {
+				return
+			}
+		}
+		result.AccessToken = vals.Get("access_token")
+		result.TokenType = vals.Get("token_type")
+		result.IDToken = vals.Get("id_token")
+		result.RefreshToken = vals.Get("refresh_token")
+		result.Scope = vals.Get("scope")
+	} else {
+		var r struct {
+			AccessToken  string      `json:"access_token"`
+			TokenType    string      `json:"token_type"`
+			IDToken      string      `json:"id_token"`
+			RefreshToken string      `json:"refresh_token"`
+			Scope        string      `json:"scope"`
+			State        string      `json:"state"`
+			ExpiresIn    json.Number `json:"expires_in"` // Azure AD returns string
+			Expires      int         `json:"expires"`
+			Error        string      `json:"error"`
+			Desc         string      `json:"error_description"`
+		}
+		if err = json.Unmarshal(body, &r); err != nil {
+			return
+		}
+		if r.Error != "" || badStatusCode {
+			err = newError(r.Error, r.Desc, r.State)
+			return
+		}
+		result.AccessToken = r.AccessToken
+		result.TokenType = r.TokenType
+		result.IDToken = r.IDToken
+		result.RefreshToken = r.RefreshToken
+		result.Scope = r.Scope
+		if expiresIn, err := r.ExpiresIn.Int64(); err != nil {
+			result.Expires = r.Expires
+		} else {
+			result.Expires = int(expiresIn)
+		}
+	}
+	return
+}
+
+type TokenResponse struct {
+	AccessToken  string
+	TokenType    string
+	Expires      int
+	IDToken      string
+	RefreshToken string // OPTIONAL.
+	Scope        string // OPTIONAL, if identical to the scope requested by the client, otherwise, REQUIRED.
+	RawBody      []byte // In case callers need some other non-standard info from the token response
+}
+
+type AuthCodeRequest struct {
+	ResponseType string
+	ClientID     string
+	RedirectURL  *url.URL
+	Scope        []string
+	State        string
+}
+
+func ParseAuthCodeRequest(q url.Values) (AuthCodeRequest, error) {
+	acr := AuthCodeRequest{
+		ResponseType: q.Get("response_type"),
+		ClientID:     q.Get("client_id"),
+		State:        q.Get("state"),
+		Scope:        make([]string, 0),
+	}
+
+	qs := strings.TrimSpace(q.Get("scope"))
+	if qs != "" {
+		acr.Scope = strings.Split(qs, " ")
+	}
+
+	err := func() error {
+		if acr.ClientID == "" {
+			return NewError(ErrorInvalidRequest)
+		}
+
+		redirectURL := q.Get("redirect_uri")
+		if redirectURL != "" {
+			ru, err := url.Parse(redirectURL)
+			if err != nil {
+				return NewError(ErrorInvalidRequest)
+			}
+			acr.RedirectURL = ru
+		}
+
+		return nil
+	}()
+
+	return acr, err
+}
diff --git a/vendor/github.com/coreos/go-oidc/oidc/client.go b/vendor/github.com/coreos/go-oidc/oidc/client.go
new file mode 100644
index 00000000..7a3cb40f
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc/client.go
@@ -0,0 +1,846 @@
+package oidc
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"net/mail"
+	"net/url"
+	"sync"
+	"time"
+
+	phttp "github.com/coreos/go-oidc/http"
+	"github.com/coreos/go-oidc/jose"
+	"github.com/coreos/go-oidc/key"
+	"github.com/coreos/go-oidc/oauth2"
+)
+
+const (
+	// amount of time that must pass after the last key sync
+	// completes before another attempt may begin
+	keySyncWindow = 5 * time.Second
+)
+
+var (
+	DefaultScope = []string{"openid", "email", "profile"}
+
+	supportedAuthMethods = map[string]struct{}{
+		oauth2.AuthMethodClientSecretBasic: struct{}{},
+		oauth2.AuthMethodClientSecretPost:  struct{}{},
+	}
+)
+
+type ClientCredentials oauth2.ClientCredentials
+
+type ClientIdentity struct {
+	Credentials ClientCredentials
+	Metadata    ClientMetadata
+}
+
+type JWAOptions struct {
+	// SigningAlg specifies an JWA alg for signing JWTs.
+	//
+	// Specifying this field implies different actions depending on the context. It may
+	// require objects be serialized and signed as a JWT instead of plain JSON, or
+	// require an existing JWT object use the specified alg.
+	//
+	// See: http://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
+	SigningAlg string
+	// EncryptionAlg, if provided, specifies that the returned or sent object be stored
+	// (or nested) within a JWT object and encrypted with the provided JWA alg.
+	EncryptionAlg string
+	// EncryptionEnc specifies the JWA enc algorithm to use with EncryptionAlg. If
+	// EncryptionAlg is provided and EncryptionEnc is omitted, this field defaults
+	// to A128CBC-HS256.
+	//
+	// If EncryptionEnc is provided EncryptionAlg must also be specified.
+	EncryptionEnc string
+}
+
+func (opt JWAOptions) valid() error {
+	if opt.EncryptionEnc != "" && opt.EncryptionAlg == "" {
+		return errors.New("encryption encoding provided with no encryption algorithm")
+	}
+	return nil
+}
+
+func (opt JWAOptions) defaults() JWAOptions {
+	if opt.EncryptionAlg != "" && opt.EncryptionEnc == "" {
+		opt.EncryptionEnc = jose.EncA128CBCHS256
+	}
+	return opt
+}
+
+var (
+	// Ensure ClientMetadata satisfies these interfaces.
+	_ json.Marshaler   = &ClientMetadata{}
+	_ json.Unmarshaler = &ClientMetadata{}
+)
+
+// ClientMetadata holds metadata that the authorization server associates
+// with a client identifier. The fields range from human-facing display
+// strings such as client name, to items that impact the security of the
+// protocol, such as the list of valid redirect URIs.
+//
+// See http://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
+//
+// TODO: support language specific claim representations
+// http://openid.net/specs/openid-connect-registration-1_0.html#LanguagesAndScripts
+type ClientMetadata struct {
+	RedirectURIs []url.URL // Required
+
+	// A list of OAuth 2.0 "response_type" values that the client wishes to restrict
+	// itself to. Either "code", "token", or another registered extension.
+	//
+	// If omitted, only "code" will be used.
+	ResponseTypes []string
+	// A list of OAuth 2.0 grant types the client wishes to restrict itself to.
+	// The grant type values used by OIDC are "authorization_code", "implicit",
+	// and "refresh_token".
+	//
+	// If ommitted, only "authorization_code" will be used.
+	GrantTypes []string
+	// "native" or "web". If omitted, "web".
+	ApplicationType string
+
+	// List of email addresses.
+	Contacts []mail.Address
+	// Name of client to be presented to the end-user.
+	ClientName string
+	// URL that references a logo for the Client application.
+	LogoURI *url.URL
+	// URL of the home page of the Client.
+	ClientURI *url.URL
+	// Profile data policies and terms of use to be provided to the end user.
+	PolicyURI         *url.URL
+	TermsOfServiceURI *url.URL
+
+	// URL to or the value of the client's JSON Web Key Set document.
+	JWKSURI *url.URL
+	JWKS    *jose.JWKSet
+
+	// URL referencing a flie with a single JSON array of redirect URIs.
+	SectorIdentifierURI *url.URL
+
+	SubjectType string
+
+	// Options to restrict the JWS alg and enc values used for server responses and requests.
+	IDTokenResponseOptions  JWAOptions
+	UserInfoResponseOptions JWAOptions
+	RequestObjectOptions    JWAOptions
+
+	// Client requested authorization method and signing options for the token endpoint.
+	//
+	// Defaults to "client_secret_basic"
+	TokenEndpointAuthMethod     string
+	TokenEndpointAuthSigningAlg string
+
+	// DefaultMaxAge specifies the maximum amount of time in seconds before an authorized
+	// user must reauthroize.
+	//
+	// If 0, no limitation is placed on the maximum.
+	DefaultMaxAge int64
+	// RequireAuthTime specifies if the auth_time claim in the ID token is required.
+	RequireAuthTime bool
+
+	// Default Authentication Context Class Reference values for authentication requests.
+	DefaultACRValues []string
+
+	// URI that a third party can use to initiate a login by the relaying party.
+	//
+	// See: http://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin
+	InitiateLoginURI *url.URL
+	// Pre-registered request_uri values that may be cached by the server.
+	RequestURIs []url.URL
+}
+
+// Defaults returns a shallow copy of ClientMetadata with default
+// values replacing omitted fields.
+func (m ClientMetadata) Defaults() ClientMetadata {
+	if len(m.ResponseTypes) == 0 {
+		m.ResponseTypes = []string{oauth2.ResponseTypeCode}
+	}
+	if len(m.GrantTypes) == 0 {
+		m.GrantTypes = []string{oauth2.GrantTypeAuthCode}
+	}
+	if m.ApplicationType == "" {
+		m.ApplicationType = "web"
+	}
+	if m.TokenEndpointAuthMethod == "" {
+		m.TokenEndpointAuthMethod = oauth2.AuthMethodClientSecretBasic
+	}
+	m.IDTokenResponseOptions = m.IDTokenResponseOptions.defaults()
+	m.UserInfoResponseOptions = m.UserInfoResponseOptions.defaults()
+	m.RequestObjectOptions = m.RequestObjectOptions.defaults()
+	return m
+}
+
+func (m *ClientMetadata) MarshalJSON() ([]byte, error) {
+	e := m.toEncodableStruct()
+	return json.Marshal(&e)
+}
+
+func (m *ClientMetadata) UnmarshalJSON(data []byte) error {
+	var e encodableClientMetadata
+	if err := json.Unmarshal(data, &e); err != nil {
+		return err
+	}
+	meta, err := e.toStruct()
+	if err != nil {
+		return err
+	}
+	if err := meta.Valid(); err != nil {
+		return err
+	}
+	*m = meta
+	return nil
+}
+
+type encodableClientMetadata struct {
+	RedirectURIs                 []string     `json:"redirect_uris"` // Required
+	ResponseTypes                []string     `json:"response_types,omitempty"`
+	GrantTypes                   []string     `json:"grant_types,omitempty"`
+	ApplicationType              string       `json:"application_type,omitempty"`
+	Contacts                     []string     `json:"contacts,omitempty"`
+	ClientName                   string       `json:"client_name,omitempty"`
+	LogoURI                      string       `json:"logo_uri,omitempty"`
+	ClientURI                    string       `json:"client_uri,omitempty"`
+	PolicyURI                    string       `json:"policy_uri,omitempty"`
+	TermsOfServiceURI            string       `json:"tos_uri,omitempty"`
+	JWKSURI                      string       `json:"jwks_uri,omitempty"`
+	JWKS                         *jose.JWKSet `json:"jwks,omitempty"`
+	SectorIdentifierURI          string       `json:"sector_identifier_uri,omitempty"`
+	SubjectType                  string       `json:"subject_type,omitempty"`
+	IDTokenSignedResponseAlg     string       `json:"id_token_signed_response_alg,omitempty"`
+	IDTokenEncryptedResponseAlg  string       `json:"id_token_encrypted_response_alg,omitempty"`
+	IDTokenEncryptedResponseEnc  string       `json:"id_token_encrypted_response_enc,omitempty"`
+	UserInfoSignedResponseAlg    string       `json:"userinfo_signed_response_alg,omitempty"`
+	UserInfoEncryptedResponseAlg string       `json:"userinfo_encrypted_response_alg,omitempty"`
+	UserInfoEncryptedResponseEnc string       `json:"userinfo_encrypted_response_enc,omitempty"`
+	RequestObjectSigningAlg      string       `json:"request_object_signing_alg,omitempty"`
+	RequestObjectEncryptionAlg   string       `json:"request_object_encryption_alg,omitempty"`
+	RequestObjectEncryptionEnc   string       `json:"request_object_encryption_enc,omitempty"`
+	TokenEndpointAuthMethod      string       `json:"token_endpoint_auth_method,omitempty"`
+	TokenEndpointAuthSigningAlg  string       `json:"token_endpoint_auth_signing_alg,omitempty"`
+	DefaultMaxAge                int64        `json:"default_max_age,omitempty"`
+	RequireAuthTime              bool         `json:"require_auth_time,omitempty"`
+	DefaultACRValues             []string     `json:"default_acr_values,omitempty"`
+	InitiateLoginURI             string       `json:"initiate_login_uri,omitempty"`
+	RequestURIs                  []string     `json:"request_uris,omitempty"`
+}
+
+func (c *encodableClientMetadata) toStruct() (ClientMetadata, error) {
+	p := stickyErrParser{}
+	m := ClientMetadata{
+		RedirectURIs:                p.parseURIs(c.RedirectURIs, "redirect_uris"),
+		ResponseTypes:               c.ResponseTypes,
+		GrantTypes:                  c.GrantTypes,
+		ApplicationType:             c.ApplicationType,
+		Contacts:                    p.parseEmails(c.Contacts, "contacts"),
+		ClientName:                  c.ClientName,
+		LogoURI:                     p.parseURI(c.LogoURI, "logo_uri"),
+		ClientURI:                   p.parseURI(c.ClientURI, "client_uri"),
+		PolicyURI:                   p.parseURI(c.PolicyURI, "policy_uri"),
+		TermsOfServiceURI:           p.parseURI(c.TermsOfServiceURI, "tos_uri"),
+		JWKSURI:                     p.parseURI(c.JWKSURI, "jwks_uri"),
+		JWKS:                        c.JWKS,
+		SectorIdentifierURI:         p.parseURI(c.SectorIdentifierURI, "sector_identifier_uri"),
+		SubjectType:                 c.SubjectType,
+		TokenEndpointAuthMethod:     c.TokenEndpointAuthMethod,
+		TokenEndpointAuthSigningAlg: c.TokenEndpointAuthSigningAlg,
+		DefaultMaxAge:               c.DefaultMaxAge,
+		RequireAuthTime:             c.RequireAuthTime,
+		DefaultACRValues:            c.DefaultACRValues,
+		InitiateLoginURI:            p.parseURI(c.InitiateLoginURI, "initiate_login_uri"),
+		RequestURIs:                 p.parseURIs(c.RequestURIs, "request_uris"),
+		IDTokenResponseOptions: JWAOptions{
+			c.IDTokenSignedResponseAlg,
+			c.IDTokenEncryptedResponseAlg,
+			c.IDTokenEncryptedResponseEnc,
+		},
+		UserInfoResponseOptions: JWAOptions{
+			c.UserInfoSignedResponseAlg,
+			c.UserInfoEncryptedResponseAlg,
+			c.UserInfoEncryptedResponseEnc,
+		},
+		RequestObjectOptions: JWAOptions{
+			c.RequestObjectSigningAlg,
+			c.RequestObjectEncryptionAlg,
+			c.RequestObjectEncryptionEnc,
+		},
+	}
+	if p.firstErr != nil {
+		return ClientMetadata{}, p.firstErr
+	}
+	return m, nil
+}
+
+// stickyErrParser parses URIs and email addresses. Once it encounters
+// a parse error, subsequent calls become no-op.
+type stickyErrParser struct {
+	firstErr error
+}
+
+func (p *stickyErrParser) parseURI(s, field string) *url.URL {
+	if p.firstErr != nil || s == "" {
+		return nil
+	}
+	u, err := url.Parse(s)
+	if err == nil {
+		if u.Host == "" {
+			err = errors.New("no host in URI")
+		} else if u.Scheme != "http" && u.Scheme != "https" {
+			err = errors.New("invalid URI scheme")
+		}
+	}
+	if err != nil {
+		p.firstErr = fmt.Errorf("failed to parse %s: %v", field, err)
+		return nil
+	}
+	return u
+}
+
+func (p *stickyErrParser) parseURIs(s []string, field string) []url.URL {
+	if p.firstErr != nil || len(s) == 0 {
+		return nil
+	}
+	uris := make([]url.URL, len(s))
+	for i, val := range s {
+		if val == "" {
+			p.firstErr = fmt.Errorf("invalid URI in field %s", field)
+			return nil
+		}
+		if u := p.parseURI(val, field); u != nil {
+			uris[i] = *u
+		}
+	}
+	return uris
+}
+
+func (p *stickyErrParser) parseEmails(s []string, field string) []mail.Address {
+	if p.firstErr != nil || len(s) == 0 {
+		return nil
+	}
+	addrs := make([]mail.Address, len(s))
+	for i, addr := range s {
+		if addr == "" {
+			p.firstErr = fmt.Errorf("invalid email in field %s", field)
+			return nil
+		}
+		a, err := mail.ParseAddress(addr)
+		if err != nil {
+			p.firstErr = fmt.Errorf("invalid email in field %s: %v", field, err)
+			return nil
+		}
+		addrs[i] = *a
+	}
+	return addrs
+}
+
+func (m *ClientMetadata) toEncodableStruct() encodableClientMetadata {
+	return encodableClientMetadata{
+		RedirectURIs:                 urisToStrings(m.RedirectURIs),
+		ResponseTypes:                m.ResponseTypes,
+		GrantTypes:                   m.GrantTypes,
+		ApplicationType:              m.ApplicationType,
+		Contacts:                     emailsToStrings(m.Contacts),
+		ClientName:                   m.ClientName,
+		LogoURI:                      uriToString(m.LogoURI),
+		ClientURI:                    uriToString(m.ClientURI),
+		PolicyURI:                    uriToString(m.PolicyURI),
+		TermsOfServiceURI:            uriToString(m.TermsOfServiceURI),
+		JWKSURI:                      uriToString(m.JWKSURI),
+		JWKS:                         m.JWKS,
+		SectorIdentifierURI:          uriToString(m.SectorIdentifierURI),
+		SubjectType:                  m.SubjectType,
+		IDTokenSignedResponseAlg:     m.IDTokenResponseOptions.SigningAlg,
+		IDTokenEncryptedResponseAlg:  m.IDTokenResponseOptions.EncryptionAlg,
+		IDTokenEncryptedResponseEnc:  m.IDTokenResponseOptions.EncryptionEnc,
+		UserInfoSignedResponseAlg:    m.UserInfoResponseOptions.SigningAlg,
+		UserInfoEncryptedResponseAlg: m.UserInfoResponseOptions.EncryptionAlg,
+		UserInfoEncryptedResponseEnc: m.UserInfoResponseOptions.EncryptionEnc,
+		RequestObjectSigningAlg:      m.RequestObjectOptions.SigningAlg,
+		RequestObjectEncryptionAlg:   m.RequestObjectOptions.EncryptionAlg,
+		RequestObjectEncryptionEnc:   m.RequestObjectOptions.EncryptionEnc,
+		TokenEndpointAuthMethod:      m.TokenEndpointAuthMethod,
+		TokenEndpointAuthSigningAlg:  m.TokenEndpointAuthSigningAlg,
+		DefaultMaxAge:                m.DefaultMaxAge,
+		RequireAuthTime:              m.RequireAuthTime,
+		DefaultACRValues:             m.DefaultACRValues,
+		InitiateLoginURI:             uriToString(m.InitiateLoginURI),
+		RequestURIs:                  urisToStrings(m.RequestURIs),
+	}
+}
+
+func uriToString(u *url.URL) string {
+	if u == nil {
+		return ""
+	}
+	return u.String()
+}
+
+func urisToStrings(urls []url.URL) []string {
+	if len(urls) == 0 {
+		return nil
+	}
+	sli := make([]string, len(urls))
+	for i, u := range urls {
+		sli[i] = u.String()
+	}
+	return sli
+}
+
+func emailsToStrings(addrs []mail.Address) []string {
+	if len(addrs) == 0 {
+		return nil
+	}
+	sli := make([]string, len(addrs))
+	for i, addr := range addrs {
+		sli[i] = addr.String()
+	}
+	return sli
+}
+
+// Valid determines if a ClientMetadata conforms with the OIDC specification.
+//
+// Valid is called by UnmarshalJSON.
+//
+// NOTE(ericchiang): For development purposes Valid does not mandate 'https' for
+// URLs fields where the OIDC spec requires it. This may change in future releases
+// of this package. See: https://github.com/coreos/go-oidc/issues/34
+func (m *ClientMetadata) Valid() error {
+	if len(m.RedirectURIs) == 0 {
+		return errors.New("zero redirect URLs")
+	}
+
+	validURI := func(u *url.URL, fieldName string) error {
+		if u.Host == "" {
+			return fmt.Errorf("no host for uri field %s", fieldName)
+		}
+		if u.Scheme != "http" && u.Scheme != "https" {
+			return fmt.Errorf("uri field %s scheme is not http or https", fieldName)
+		}
+		return nil
+	}
+
+	uris := []struct {
+		val  *url.URL
+		name string
+	}{
+		{m.LogoURI, "logo_uri"},
+		{m.ClientURI, "client_uri"},
+		{m.PolicyURI, "policy_uri"},
+		{m.TermsOfServiceURI, "tos_uri"},
+		{m.JWKSURI, "jwks_uri"},
+		{m.SectorIdentifierURI, "sector_identifier_uri"},
+		{m.InitiateLoginURI, "initiate_login_uri"},
+	}
+
+	for _, uri := range uris {
+		if uri.val == nil {
+			continue
+		}
+		if err := validURI(uri.val, uri.name); err != nil {
+			return err
+		}
+	}
+
+	uriLists := []struct {
+		vals []url.URL
+		name string
+	}{
+		{m.RedirectURIs, "redirect_uris"},
+		{m.RequestURIs, "request_uris"},
+	}
+	for _, list := range uriLists {
+		for _, uri := range list.vals {
+			if err := validURI(&uri, list.name); err != nil {
+				return err
+			}
+		}
+	}
+
+	options := []struct {
+		option JWAOptions
+		name   string
+	}{
+		{m.IDTokenResponseOptions, "id_token response"},
+		{m.UserInfoResponseOptions, "userinfo response"},
+		{m.RequestObjectOptions, "request_object"},
+	}
+	for _, option := range options {
+		if err := option.option.valid(); err != nil {
+			return fmt.Errorf("invalid JWA values for %s: %v", option.name, err)
+		}
+	}
+	return nil
+}
+
+type ClientRegistrationResponse struct {
+	ClientID                string // Required
+	ClientSecret            string
+	RegistrationAccessToken string
+	RegistrationClientURI   string
+	// If IsZero is true, unspecified.
+	ClientIDIssuedAt time.Time
+	// Time at which the client_secret will expire.
+	// If IsZero is true, it will not expire.
+	ClientSecretExpiresAt time.Time
+
+	ClientMetadata
+}
+
+type encodableClientRegistrationResponse struct {
+	ClientID                string `json:"client_id"` // Required
+	ClientSecret            string `json:"client_secret,omitempty"`
+	RegistrationAccessToken string `json:"registration_access_token,omitempty"`
+	RegistrationClientURI   string `json:"registration_client_uri,omitempty"`
+	ClientIDIssuedAt        int64  `json:"client_id_issued_at,omitempty"`
+	// Time at which the client_secret will expire, in seconds since the epoch.
+	// If 0 it will not expire.
+	ClientSecretExpiresAt int64 `json:"client_secret_expires_at"` // Required
+
+	encodableClientMetadata
+}
+
+func unixToSec(t time.Time) int64 {
+	if t.IsZero() {
+		return 0
+	}
+	return t.Unix()
+}
+
+func (c *ClientRegistrationResponse) MarshalJSON() ([]byte, error) {
+	e := encodableClientRegistrationResponse{
+		ClientID:                c.ClientID,
+		ClientSecret:            c.ClientSecret,
+		RegistrationAccessToken: c.RegistrationAccessToken,
+		RegistrationClientURI:   c.RegistrationClientURI,
+		ClientIDIssuedAt:        unixToSec(c.ClientIDIssuedAt),
+		ClientSecretExpiresAt:   unixToSec(c.ClientSecretExpiresAt),
+		encodableClientMetadata: c.ClientMetadata.toEncodableStruct(),
+	}
+	return json.Marshal(&e)
+}
+
+func secToUnix(sec int64) time.Time {
+	if sec == 0 {
+		return time.Time{}
+	}
+	return time.Unix(sec, 0)
+}
+
+func (c *ClientRegistrationResponse) UnmarshalJSON(data []byte) error {
+	var e encodableClientRegistrationResponse
+	if err := json.Unmarshal(data, &e); err != nil {
+		return err
+	}
+	if e.ClientID == "" {
+		return errors.New("no client_id in client registration response")
+	}
+	metadata, err := e.encodableClientMetadata.toStruct()
+	if err != nil {
+		return err
+	}
+	*c = ClientRegistrationResponse{
+		ClientID:                e.ClientID,
+		ClientSecret:            e.ClientSecret,
+		RegistrationAccessToken: e.RegistrationAccessToken,
+		RegistrationClientURI:   e.RegistrationClientURI,
+		ClientIDIssuedAt:        secToUnix(e.ClientIDIssuedAt),
+		ClientSecretExpiresAt:   secToUnix(e.ClientSecretExpiresAt),
+		ClientMetadata:          metadata,
+	}
+	return nil
+}
+
+type ClientConfig struct {
+	HTTPClient     phttp.Client
+	Credentials    ClientCredentials
+	Scope          []string
+	RedirectURL    string
+	ProviderConfig ProviderConfig
+	KeySet         key.PublicKeySet
+}
+
+func NewClient(cfg ClientConfig) (*Client, error) {
+	// Allow empty redirect URL in the case where the client
+	// only needs to verify a given token.
+	ru, err := url.Parse(cfg.RedirectURL)
+	if err != nil {
+		return nil, fmt.Errorf("invalid redirect URL: %v", err)
+	}
+
+	c := Client{
+		credentials:    cfg.Credentials,
+		httpClient:     cfg.HTTPClient,
+		scope:          cfg.Scope,
+		redirectURL:    ru.String(),
+		providerConfig: newProviderConfigRepo(cfg.ProviderConfig),
+		keySet:         cfg.KeySet,
+	}
+
+	if c.httpClient == nil {
+		c.httpClient = http.DefaultClient
+	}
+
+	if c.scope == nil {
+		c.scope = make([]string, len(DefaultScope))
+		copy(c.scope, DefaultScope)
+	}
+
+	return &c, nil
+}
+
+type Client struct {
+	httpClient     phttp.Client
+	providerConfig *providerConfigRepo
+	credentials    ClientCredentials
+	redirectURL    string
+	scope          []string
+	keySet         key.PublicKeySet
+	providerSyncer *ProviderConfigSyncer
+
+	keySetSyncMutex sync.RWMutex
+	lastKeySetSync  time.Time
+}
+
+func (c *Client) Healthy() error {
+	now := time.Now().UTC()
+
+	cfg := c.providerConfig.Get()
+
+	if cfg.Empty() {
+		return errors.New("oidc client provider config empty")
+	}
+
+	if !cfg.ExpiresAt.IsZero() && cfg.ExpiresAt.Before(now) {
+		return errors.New("oidc client provider config expired")
+	}
+
+	return nil
+}
+
+func (c *Client) OAuthClient() (*oauth2.Client, error) {
+	cfg := c.providerConfig.Get()
+	authMethod, err := chooseAuthMethod(cfg)
+	if err != nil {
+		return nil, err
+	}
+
+	ocfg := oauth2.Config{
+		Credentials: oauth2.ClientCredentials(c.credentials),
+		RedirectURL: c.redirectURL,
+		AuthURL:     cfg.AuthEndpoint.String(),
+		TokenURL:    cfg.TokenEndpoint.String(),
+		Scope:       c.scope,
+		AuthMethod:  authMethod,
+	}
+
+	return oauth2.NewClient(c.httpClient, ocfg)
+}
+
+func chooseAuthMethod(cfg ProviderConfig) (string, error) {
+	if len(cfg.TokenEndpointAuthMethodsSupported) == 0 {
+		return oauth2.AuthMethodClientSecretBasic, nil
+	}
+
+	for _, authMethod := range cfg.TokenEndpointAuthMethodsSupported {
+		if _, ok := supportedAuthMethods[authMethod]; ok {
+			return authMethod, nil
+		}
+	}
+
+	return "", errors.New("no supported auth methods")
+}
+
+// SyncProviderConfig starts the provider config syncer
+func (c *Client) SyncProviderConfig(discoveryURL string) chan struct{} {
+	r := NewHTTPProviderConfigGetter(c.httpClient, discoveryURL)
+	s := NewProviderConfigSyncer(r, c.providerConfig)
+	stop := s.Run()
+	s.WaitUntilInitialSync()
+	return stop
+}
+
+func (c *Client) maybeSyncKeys() error {
+	tooSoon := func() bool {
+		return time.Now().UTC().Before(c.lastKeySetSync.Add(keySyncWindow))
+	}
+
+	// ignore request to sync keys if a sync operation has been
+	// attempted too recently
+	if tooSoon() {
+		return nil
+	}
+
+	c.keySetSyncMutex.Lock()
+	defer c.keySetSyncMutex.Unlock()
+
+	// check again, as another goroutine may have been holding
+	// the lock while updating the keys
+	if tooSoon() {
+		return nil
+	}
+
+	cfg := c.providerConfig.Get()
+	r := NewRemotePublicKeyRepo(c.httpClient, cfg.KeysEndpoint.String())
+	w := &clientKeyRepo{client: c}
+	_, err := key.Sync(r, w)
+	c.lastKeySetSync = time.Now().UTC()
+
+	return err
+}
+
+type clientKeyRepo struct {
+	client *Client
+}
+
+func (r *clientKeyRepo) Set(ks key.KeySet) error {
+	pks, ok := ks.(*key.PublicKeySet)
+	if !ok {
+		return errors.New("unable to cast to PublicKey")
+	}
+	r.client.keySet = *pks
+	return nil
+}
+
+func (c *Client) ClientCredsToken(scope []string) (jose.JWT, error) {
+	cfg := c.providerConfig.Get()
+
+	if !cfg.SupportsGrantType(oauth2.GrantTypeClientCreds) {
+		return jose.JWT{}, fmt.Errorf("%v grant type is not supported", oauth2.GrantTypeClientCreds)
+	}
+
+	oac, err := c.OAuthClient()
+	if err != nil {
+		return jose.JWT{}, err
+	}
+
+	t, err := oac.ClientCredsToken(scope)
+	if err != nil {
+		return jose.JWT{}, err
+	}
+
+	jwt, err := jose.ParseJWT(t.IDToken)
+	if err != nil {
+		return jose.JWT{}, err
+	}
+
+	return jwt, c.VerifyJWT(jwt)
+}
+
+// ExchangeAuthCode exchanges an OAuth2 auth code for an OIDC JWT ID token.
+func (c *Client) ExchangeAuthCode(code string) (jose.JWT, error) {
+	oac, err := c.OAuthClient()
+	if err != nil {
+		return jose.JWT{}, err
+	}
+
+	t, err := oac.RequestToken(oauth2.GrantTypeAuthCode, code)
+	if err != nil {
+		return jose.JWT{}, err
+	}
+
+	jwt, err := jose.ParseJWT(t.IDToken)
+	if err != nil {
+		return jose.JWT{}, err
+	}
+
+	return jwt, c.VerifyJWT(jwt)
+}
+
+// RefreshToken uses a refresh token to exchange for a new OIDC JWT ID Token.
+func (c *Client) RefreshToken(refreshToken string) (jose.JWT, error) {
+	oac, err := c.OAuthClient()
+	if err != nil {
+		return jose.JWT{}, err
+	}
+
+	t, err := oac.RequestToken(oauth2.GrantTypeRefreshToken, refreshToken)
+	if err != nil {
+		return jose.JWT{}, err
+	}
+
+	jwt, err := jose.ParseJWT(t.IDToken)
+	if err != nil {
+		return jose.JWT{}, err
+	}
+
+	return jwt, c.VerifyJWT(jwt)
+}
+
+func (c *Client) VerifyJWT(jwt jose.JWT) error {
+	var keysFunc func() []key.PublicKey
+	if kID, ok := jwt.KeyID(); ok {
+		keysFunc = c.keysFuncWithID(kID)
+	} else {
+		keysFunc = c.keysFuncAll()
+	}
+
+	v := NewJWTVerifier(
+		c.providerConfig.Get().Issuer.String(),
+		c.credentials.ID,
+		c.maybeSyncKeys, keysFunc)
+
+	return v.Verify(jwt)
+}
+
+// keysFuncWithID returns a function that retrieves at most unexpired
+// public key from the Client that matches the provided ID
+func (c *Client) keysFuncWithID(kID string) func() []key.PublicKey {
+	return func() []key.PublicKey {
+		c.keySetSyncMutex.RLock()
+		defer c.keySetSyncMutex.RUnlock()
+
+		if c.keySet.ExpiresAt().Before(time.Now()) {
+			return []key.PublicKey{}
+		}
+
+		k := c.keySet.Key(kID)
+		if k == nil {
+			return []key.PublicKey{}
+		}
+
+		return []key.PublicKey{*k}
+	}
+}
+
+// keysFuncAll returns a function that retrieves all unexpired public
+// keys from the Client
+func (c *Client) keysFuncAll() func() []key.PublicKey {
+	return func() []key.PublicKey {
+		c.keySetSyncMutex.RLock()
+		defer c.keySetSyncMutex.RUnlock()
+
+		if c.keySet.ExpiresAt().Before(time.Now()) {
+			return []key.PublicKey{}
+		}
+
+		return c.keySet.Keys()
+	}
+}
+
+type providerConfigRepo struct {
+	mu     sync.RWMutex
+	config ProviderConfig // do not access directly, use Get()
+}
+
+func newProviderConfigRepo(pc ProviderConfig) *providerConfigRepo {
+	return &providerConfigRepo{sync.RWMutex{}, pc}
+}
+
+// returns an error to implement ProviderConfigSetter
+func (r *providerConfigRepo) Set(cfg ProviderConfig) error {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+	r.config = cfg
+	return nil
+}
+
+func (r *providerConfigRepo) Get() ProviderConfig {
+	r.mu.RLock()
+	defer r.mu.RUnlock()
+	return r.config
+}
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 00000000..196611ec
--- /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/identity.go b/vendor/github.com/coreos/go-oidc/oidc/identity.go
new file mode 100644
index 00000000..9bfa8e34
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc/identity.go
@@ -0,0 +1,44 @@
+package oidc
+
+import (
+	"errors"
+	"time"
+
+	"github.com/coreos/go-oidc/jose"
+)
+
+type Identity struct {
+	ID        string
+	Name      string
+	Email     string
+	ExpiresAt time.Time
+}
+
+func IdentityFromClaims(claims jose.Claims) (*Identity, error) {
+	if claims == nil {
+		return nil, errors.New("nil claim set")
+	}
+
+	var ident Identity
+	var err error
+	var ok bool
+
+	if ident.ID, ok, err = claims.StringClaim("sub"); err != nil {
+		return nil, err
+	} else if !ok {
+		return nil, errors.New("missing required claim: sub")
+	}
+
+	if ident.Email, _, err = claims.StringClaim("email"); err != nil {
+		return nil, err
+	}
+
+	exp, ok, err := claims.TimeClaim("exp")
+	if err != nil {
+		return nil, err
+	} else if ok {
+		ident.ExpiresAt = exp
+	}
+
+	return &ident, nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/oidc/interface.go b/vendor/github.com/coreos/go-oidc/oidc/interface.go
new file mode 100644
index 00000000..248cac0b
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc/interface.go
@@ -0,0 +1,3 @@
+package oidc
+
+type LoginFunc func(ident Identity, sessionKey string) (redirectURL string, err error)
diff --git a/vendor/github.com/coreos/go-oidc/oidc/key.go b/vendor/github.com/coreos/go-oidc/oidc/key.go
new file mode 100755
index 00000000..82a0f567
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc/key.go
@@ -0,0 +1,67 @@
+package oidc
+
+import (
+	"encoding/json"
+	"errors"
+	"net/http"
+	"time"
+
+	phttp "github.com/coreos/go-oidc/http"
+	"github.com/coreos/go-oidc/jose"
+	"github.com/coreos/go-oidc/key"
+)
+
+// DefaultPublicKeySetTTL is the default TTL set on the PublicKeySet if no
+// Cache-Control header is provided by the JWK Set document endpoint.
+const DefaultPublicKeySetTTL = 24 * time.Hour
+
+// NewRemotePublicKeyRepo is responsible for fetching the JWK Set document.
+func NewRemotePublicKeyRepo(hc phttp.Client, ep string) *remotePublicKeyRepo {
+	return &remotePublicKeyRepo{hc: hc, ep: ep}
+}
+
+type remotePublicKeyRepo struct {
+	hc phttp.Client
+	ep string
+}
+
+// Get returns a PublicKeySet fetched from the JWK Set document endpoint. A TTL
+// is set on the Key Set to avoid it having to be re-retrieved for every
+// encryption event. This TTL is typically controlled by the endpoint returning
+// a Cache-Control header, but defaults to 24 hours if no Cache-Control header
+// is found.
+func (r *remotePublicKeyRepo) Get() (key.KeySet, error) {
+	req, err := http.NewRequest("GET", r.ep, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := r.hc.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	var d struct {
+		Keys []jose.JWK `json:"keys"`
+	}
+	if err := json.NewDecoder(resp.Body).Decode(&d); err != nil {
+		return nil, err
+	}
+
+	if len(d.Keys) == 0 {
+		return nil, errors.New("zero keys in response")
+	}
+
+	ttl, ok, err := phttp.Cacheable(resp.Header)
+	if err != nil {
+		return nil, err
+	}
+	if !ok {
+		ttl = DefaultPublicKeySetTTL
+	}
+
+	exp := time.Now().UTC().Add(ttl)
+	ks := key.NewPublicKeySet(d.Keys, exp)
+	return ks, nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/oidc/provider.go b/vendor/github.com/coreos/go-oidc/oidc/provider.go
new file mode 100644
index 00000000..2afc0da3
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc/provider.go
@@ -0,0 +1,687 @@
+package oidc
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"log"
+	"net/http"
+	"net/url"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/coreos/pkg/timeutil"
+	"github.com/jonboulle/clockwork"
+
+	phttp "github.com/coreos/go-oidc/http"
+	"github.com/coreos/go-oidc/oauth2"
+)
+
+const (
+	// Subject Identifier types defined by the OIDC spec. Specifies if the provider
+	// should provide the same sub claim value to all clients (public) or a unique
+	// value for each client (pairwise).
+	//
+	// See: http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
+	SubjectTypePublic   = "public"
+	SubjectTypePairwise = "pairwise"
+)
+
+var (
+	// Default values for omitted provider config fields.
+	//
+	// Use ProviderConfig's Defaults method to fill a provider config with these values.
+	DefaultGrantTypesSupported               = []string{oauth2.GrantTypeAuthCode, oauth2.GrantTypeImplicit}
+	DefaultResponseModesSupported            = []string{"query", "fragment"}
+	DefaultTokenEndpointAuthMethodsSupported = []string{oauth2.AuthMethodClientSecretBasic}
+	DefaultClaimTypesSupported               = []string{"normal"}
+)
+
+const (
+	MaximumProviderConfigSyncInterval = 24 * time.Hour
+	MinimumProviderConfigSyncInterval = time.Minute
+
+	discoveryConfigPath = "/.well-known/openid-configuration"
+)
+
+// internally configurable for tests
+var minimumProviderConfigSyncInterval = MinimumProviderConfigSyncInterval
+
+var (
+	// Ensure ProviderConfig satisfies these interfaces.
+	_ json.Marshaler   = &ProviderConfig{}
+	_ json.Unmarshaler = &ProviderConfig{}
+)
+
+// ProviderConfig represents the OpenID Provider Metadata specifying what
+// configurations a provider supports.
+//
+// See: http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
+type ProviderConfig struct {
+	Issuer               *url.URL // Required
+	AuthEndpoint         *url.URL // Required
+	TokenEndpoint        *url.URL // Required if grant types other than "implicit" are supported
+	UserInfoEndpoint     *url.URL
+	KeysEndpoint         *url.URL // Required
+	RegistrationEndpoint *url.URL
+	EndSessionEndpoint   *url.URL
+	CheckSessionIFrame   *url.URL
+
+	// Servers MAY choose not to advertise some supported scope values even when this
+	// parameter is used, although those defined in OpenID Core SHOULD be listed, if supported.
+	ScopesSupported []string
+	// OAuth2.0 response types supported.
+	ResponseTypesSupported []string // Required
+	// OAuth2.0 response modes supported.
+	//
+	// If omitted, defaults to DefaultResponseModesSupported.
+	ResponseModesSupported []string
+	// OAuth2.0 grant types supported.
+	//
+	// If omitted, defaults to DefaultGrantTypesSupported.
+	GrantTypesSupported []string
+	ACRValuesSupported  []string
+	// SubjectTypesSupported specifies strategies for providing values for the sub claim.
+	SubjectTypesSupported []string // Required
+
+	// JWA signing and encryption algorith values supported for ID tokens.
+	IDTokenSigningAlgValues    []string // Required
+	IDTokenEncryptionAlgValues []string
+	IDTokenEncryptionEncValues []string
+
+	// JWA signing and encryption algorith values supported for user info responses.
+	UserInfoSigningAlgValues    []string
+	UserInfoEncryptionAlgValues []string
+	UserInfoEncryptionEncValues []string
+
+	// JWA signing and encryption algorith values supported for request objects.
+	ReqObjSigningAlgValues    []string
+	ReqObjEncryptionAlgValues []string
+	ReqObjEncryptionEncValues []string
+
+	TokenEndpointAuthMethodsSupported          []string
+	TokenEndpointAuthSigningAlgValuesSupported []string
+	DisplayValuesSupported                     []string
+	ClaimTypesSupported                        []string
+	ClaimsSupported                            []string
+	ServiceDocs                                *url.URL
+	ClaimsLocalsSupported                      []string
+	UILocalsSupported                          []string
+	ClaimsParameterSupported                   bool
+	RequestParameterSupported                  bool
+	RequestURIParamaterSupported               bool
+	RequireRequestURIRegistration              bool
+
+	Policy         *url.URL
+	TermsOfService *url.URL
+
+	// Not part of the OpenID Provider Metadata
+	ExpiresAt time.Time
+}
+
+// Defaults returns a shallow copy of ProviderConfig with default
+// values replacing omitted fields.
+//
+//     var cfg oidc.ProviderConfig
+//     // Fill provider config with default values for omitted fields.
+//     cfg = cfg.Defaults()
+//
+func (p ProviderConfig) Defaults() ProviderConfig {
+	setDefault := func(val *[]string, defaultVal []string) {
+		if len(*val) == 0 {
+			*val = defaultVal
+		}
+	}
+	setDefault(&p.GrantTypesSupported, DefaultGrantTypesSupported)
+	setDefault(&p.ResponseModesSupported, DefaultResponseModesSupported)
+	setDefault(&p.TokenEndpointAuthMethodsSupported, DefaultTokenEndpointAuthMethodsSupported)
+	setDefault(&p.ClaimTypesSupported, DefaultClaimTypesSupported)
+	return p
+}
+
+func (p *ProviderConfig) MarshalJSON() ([]byte, error) {
+	e := p.toEncodableStruct()
+	return json.Marshal(&e)
+}
+
+func (p *ProviderConfig) UnmarshalJSON(data []byte) error {
+	var e encodableProviderConfig
+	if err := json.Unmarshal(data, &e); err != nil {
+		return err
+	}
+	conf, err := e.toStruct()
+	if err != nil {
+		return err
+	}
+	if err := conf.Valid(); err != nil {
+		return err
+	}
+	*p = conf
+	return nil
+}
+
+type encodableProviderConfig struct {
+	Issuer               string `json:"issuer"`
+	AuthEndpoint         string `json:"authorization_endpoint"`
+	TokenEndpoint        string `json:"token_endpoint"`
+	UserInfoEndpoint     string `json:"userinfo_endpoint,omitempty"`
+	KeysEndpoint         string `json:"jwks_uri"`
+	RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
+	EndSessionEndpoint   string `json:"end_session_endpoint,omitempty"`
+	CheckSessionIFrame   string `json:"check_session_iframe,omitempty"`
+
+	// Use 'omitempty' for all slices as per OIDC spec:
+	// "Claims that return multiple values are represented as JSON arrays.
+	// Claims with zero elements MUST be omitted from the response."
+	// http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse
+
+	ScopesSupported        []string `json:"scopes_supported,omitempty"`
+	ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
+	ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
+	GrantTypesSupported    []string `json:"grant_types_supported,omitempty"`
+	ACRValuesSupported     []string `json:"acr_values_supported,omitempty"`
+	SubjectTypesSupported  []string `json:"subject_types_supported,omitempty"`
+
+	IDTokenSigningAlgValues     []string `json:"id_token_signing_alg_values_supported,omitempty"`
+	IDTokenEncryptionAlgValues  []string `json:"id_token_encryption_alg_values_supported,omitempty"`
+	IDTokenEncryptionEncValues  []string `json:"id_token_encryption_enc_values_supported,omitempty"`
+	UserInfoSigningAlgValues    []string `json:"userinfo_signing_alg_values_supported,omitempty"`
+	UserInfoEncryptionAlgValues []string `json:"userinfo_encryption_alg_values_supported,omitempty"`
+	UserInfoEncryptionEncValues []string `json:"userinfo_encryption_enc_values_supported,omitempty"`
+	ReqObjSigningAlgValues      []string `json:"request_object_signing_alg_values_supported,omitempty"`
+	ReqObjEncryptionAlgValues   []string `json:"request_object_encryption_alg_values_supported,omitempty"`
+	ReqObjEncryptionEncValues   []string `json:"request_object_encryption_enc_values_supported,omitempty"`
+
+	TokenEndpointAuthMethodsSupported          []string `json:"token_endpoint_auth_methods_supported,omitempty"`
+	TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`
+
+	DisplayValuesSupported        []string `json:"display_values_supported,omitempty"`
+	ClaimTypesSupported           []string `json:"claim_types_supported,omitempty"`
+	ClaimsSupported               []string `json:"claims_supported,omitempty"`
+	ServiceDocs                   string   `json:"service_documentation,omitempty"`
+	ClaimsLocalsSupported         []string `json:"claims_locales_supported,omitempty"`
+	UILocalsSupported             []string `json:"ui_locales_supported,omitempty"`
+	ClaimsParameterSupported      bool     `json:"claims_parameter_supported,omitempty"`
+	RequestParameterSupported     bool     `json:"request_parameter_supported,omitempty"`
+	RequestURIParamaterSupported  bool     `json:"request_uri_parameter_supported,omitempty"`
+	RequireRequestURIRegistration bool     `json:"require_request_uri_registration,omitempty"`
+
+	Policy         string `json:"op_policy_uri,omitempty"`
+	TermsOfService string `json:"op_tos_uri,omitempty"`
+}
+
+func (cfg ProviderConfig) toEncodableStruct() encodableProviderConfig {
+	return encodableProviderConfig{
+		Issuer:                                     uriToString(cfg.Issuer),
+		AuthEndpoint:                               uriToString(cfg.AuthEndpoint),
+		TokenEndpoint:                              uriToString(cfg.TokenEndpoint),
+		UserInfoEndpoint:                           uriToString(cfg.UserInfoEndpoint),
+		KeysEndpoint:                               uriToString(cfg.KeysEndpoint),
+		RegistrationEndpoint:                       uriToString(cfg.RegistrationEndpoint),
+		EndSessionEndpoint:                         uriToString(cfg.EndSessionEndpoint),
+		CheckSessionIFrame:                         uriToString(cfg.CheckSessionIFrame),
+		ScopesSupported:                            cfg.ScopesSupported,
+		ResponseTypesSupported:                     cfg.ResponseTypesSupported,
+		ResponseModesSupported:                     cfg.ResponseModesSupported,
+		GrantTypesSupported:                        cfg.GrantTypesSupported,
+		ACRValuesSupported:                         cfg.ACRValuesSupported,
+		SubjectTypesSupported:                      cfg.SubjectTypesSupported,
+		IDTokenSigningAlgValues:                    cfg.IDTokenSigningAlgValues,
+		IDTokenEncryptionAlgValues:                 cfg.IDTokenEncryptionAlgValues,
+		IDTokenEncryptionEncValues:                 cfg.IDTokenEncryptionEncValues,
+		UserInfoSigningAlgValues:                   cfg.UserInfoSigningAlgValues,
+		UserInfoEncryptionAlgValues:                cfg.UserInfoEncryptionAlgValues,
+		UserInfoEncryptionEncValues:                cfg.UserInfoEncryptionEncValues,
+		ReqObjSigningAlgValues:                     cfg.ReqObjSigningAlgValues,
+		ReqObjEncryptionAlgValues:                  cfg.ReqObjEncryptionAlgValues,
+		ReqObjEncryptionEncValues:                  cfg.ReqObjEncryptionEncValues,
+		TokenEndpointAuthMethodsSupported:          cfg.TokenEndpointAuthMethodsSupported,
+		TokenEndpointAuthSigningAlgValuesSupported: cfg.TokenEndpointAuthSigningAlgValuesSupported,
+		DisplayValuesSupported:                     cfg.DisplayValuesSupported,
+		ClaimTypesSupported:                        cfg.ClaimTypesSupported,
+		ClaimsSupported:                            cfg.ClaimsSupported,
+		ServiceDocs:                                uriToString(cfg.ServiceDocs),
+		ClaimsLocalsSupported:                      cfg.ClaimsLocalsSupported,
+		UILocalsSupported:                          cfg.UILocalsSupported,
+		ClaimsParameterSupported:                   cfg.ClaimsParameterSupported,
+		RequestParameterSupported:                  cfg.RequestParameterSupported,
+		RequestURIParamaterSupported:               cfg.RequestURIParamaterSupported,
+		RequireRequestURIRegistration:              cfg.RequireRequestURIRegistration,
+		Policy:         uriToString(cfg.Policy),
+		TermsOfService: uriToString(cfg.TermsOfService),
+	}
+}
+
+func (e encodableProviderConfig) toStruct() (ProviderConfig, error) {
+	p := stickyErrParser{}
+	conf := ProviderConfig{
+		Issuer:                                     p.parseURI(e.Issuer, "issuer"),
+		AuthEndpoint:                               p.parseURI(e.AuthEndpoint, "authorization_endpoint"),
+		TokenEndpoint:                              p.parseURI(e.TokenEndpoint, "token_endpoint"),
+		UserInfoEndpoint:                           p.parseURI(e.UserInfoEndpoint, "userinfo_endpoint"),
+		KeysEndpoint:                               p.parseURI(e.KeysEndpoint, "jwks_uri"),
+		RegistrationEndpoint:                       p.parseURI(e.RegistrationEndpoint, "registration_endpoint"),
+		EndSessionEndpoint:                         p.parseURI(e.EndSessionEndpoint, "end_session_endpoint"),
+		CheckSessionIFrame:                         p.parseURI(e.CheckSessionIFrame, "check_session_iframe"),
+		ScopesSupported:                            e.ScopesSupported,
+		ResponseTypesSupported:                     e.ResponseTypesSupported,
+		ResponseModesSupported:                     e.ResponseModesSupported,
+		GrantTypesSupported:                        e.GrantTypesSupported,
+		ACRValuesSupported:                         e.ACRValuesSupported,
+		SubjectTypesSupported:                      e.SubjectTypesSupported,
+		IDTokenSigningAlgValues:                    e.IDTokenSigningAlgValues,
+		IDTokenEncryptionAlgValues:                 e.IDTokenEncryptionAlgValues,
+		IDTokenEncryptionEncValues:                 e.IDTokenEncryptionEncValues,
+		UserInfoSigningAlgValues:                   e.UserInfoSigningAlgValues,
+		UserInfoEncryptionAlgValues:                e.UserInfoEncryptionAlgValues,
+		UserInfoEncryptionEncValues:                e.UserInfoEncryptionEncValues,
+		ReqObjSigningAlgValues:                     e.ReqObjSigningAlgValues,
+		ReqObjEncryptionAlgValues:                  e.ReqObjEncryptionAlgValues,
+		ReqObjEncryptionEncValues:                  e.ReqObjEncryptionEncValues,
+		TokenEndpointAuthMethodsSupported:          e.TokenEndpointAuthMethodsSupported,
+		TokenEndpointAuthSigningAlgValuesSupported: e.TokenEndpointAuthSigningAlgValuesSupported,
+		DisplayValuesSupported:                     e.DisplayValuesSupported,
+		ClaimTypesSupported:                        e.ClaimTypesSupported,
+		ClaimsSupported:                            e.ClaimsSupported,
+		ServiceDocs:                                p.parseURI(e.ServiceDocs, "service_documentation"),
+		ClaimsLocalsSupported:                      e.ClaimsLocalsSupported,
+		UILocalsSupported:                          e.UILocalsSupported,
+		ClaimsParameterSupported:                   e.ClaimsParameterSupported,
+		RequestParameterSupported:                  e.RequestParameterSupported,
+		RequestURIParamaterSupported:               e.RequestURIParamaterSupported,
+		RequireRequestURIRegistration:              e.RequireRequestURIRegistration,
+		Policy:         p.parseURI(e.Policy, "op_policy-uri"),
+		TermsOfService: p.parseURI(e.TermsOfService, "op_tos_uri"),
+	}
+	if p.firstErr != nil {
+		return ProviderConfig{}, p.firstErr
+	}
+	return conf, nil
+}
+
+// Empty returns if a ProviderConfig holds no information.
+//
+// This case generally indicates a ProviderConfigGetter has experienced an error
+// and has nothing to report.
+func (p ProviderConfig) Empty() bool {
+	return p.Issuer == nil
+}
+
+func contains(sli []string, ele string) bool {
+	for _, s := range sli {
+		if s == ele {
+			return true
+		}
+	}
+	return false
+}
+
+// Valid determines if a ProviderConfig conforms with the OIDC specification.
+// If Valid returns successfully it guarantees required field are non-nil and
+// URLs are well formed.
+//
+// Valid is called by UnmarshalJSON.
+//
+// NOTE(ericchiang): For development purposes Valid does not mandate 'https' for
+// URLs fields where the OIDC spec requires it. This may change in future releases
+// of this package. See: https://github.com/coreos/go-oidc/issues/34
+func (p ProviderConfig) Valid() error {
+	grantTypes := p.GrantTypesSupported
+	if len(grantTypes) == 0 {
+		grantTypes = DefaultGrantTypesSupported
+	}
+	implicitOnly := true
+	for _, grantType := range grantTypes {
+		if grantType != oauth2.GrantTypeImplicit {
+			implicitOnly = false
+			break
+		}
+	}
+
+	if len(p.SubjectTypesSupported) == 0 {
+		return errors.New("missing required field subject_types_supported")
+	}
+	if len(p.IDTokenSigningAlgValues) == 0 {
+		return errors.New("missing required field id_token_signing_alg_values_supported")
+	}
+
+	if len(p.ScopesSupported) != 0 && !contains(p.ScopesSupported, "openid") {
+		return errors.New("scoped_supported must be unspecified or include 'openid'")
+	}
+
+	if !contains(p.IDTokenSigningAlgValues, "RS256") {
+		return errors.New("id_token_signing_alg_values_supported must include 'RS256'")
+	}
+
+	uris := []struct {
+		val      *url.URL
+		name     string
+		required bool
+	}{
+		{p.Issuer, "issuer", true},
+		{p.AuthEndpoint, "authorization_endpoint", true},
+		{p.TokenEndpoint, "token_endpoint", !implicitOnly},
+		{p.UserInfoEndpoint, "userinfo_endpoint", false},
+		{p.KeysEndpoint, "jwks_uri", true},
+		{p.RegistrationEndpoint, "registration_endpoint", false},
+		{p.EndSessionEndpoint, "end_session_endpoint", false},
+		{p.CheckSessionIFrame, "check_session_iframe", false},
+		{p.ServiceDocs, "service_documentation", false},
+		{p.Policy, "op_policy_uri", false},
+		{p.TermsOfService, "op_tos_uri", false},
+	}
+
+	for _, uri := range uris {
+		if uri.val == nil {
+			if !uri.required {
+				continue
+			}
+			return fmt.Errorf("empty value for required uri field %s", uri.name)
+		}
+		if uri.val.Host == "" {
+			return fmt.Errorf("no host for uri field %s", uri.name)
+		}
+		if uri.val.Scheme != "http" && uri.val.Scheme != "https" {
+			return fmt.Errorf("uri field %s schemeis not http or https", uri.name)
+		}
+	}
+	return nil
+}
+
+// Supports determines if provider supports a client given their respective metadata.
+func (p ProviderConfig) Supports(c ClientMetadata) error {
+	if err := p.Valid(); err != nil {
+		return fmt.Errorf("invalid provider config: %v", err)
+	}
+	if err := c.Valid(); err != nil {
+		return fmt.Errorf("invalid client config: %v", err)
+	}
+
+	// Fill default values for omitted fields
+	c = c.Defaults()
+	p = p.Defaults()
+
+	// Do the supported values list the requested one?
+	supports := []struct {
+		supported []string
+		requested string
+		name      string
+	}{
+		{p.IDTokenSigningAlgValues, c.IDTokenResponseOptions.SigningAlg, "id_token_signed_response_alg"},
+		{p.IDTokenEncryptionAlgValues, c.IDTokenResponseOptions.EncryptionAlg, "id_token_encryption_response_alg"},
+		{p.IDTokenEncryptionEncValues, c.IDTokenResponseOptions.EncryptionEnc, "id_token_encryption_response_enc"},
+		{p.UserInfoSigningAlgValues, c.UserInfoResponseOptions.SigningAlg, "userinfo_signed_response_alg"},
+		{p.UserInfoEncryptionAlgValues, c.UserInfoResponseOptions.EncryptionAlg, "userinfo_encryption_response_alg"},
+		{p.UserInfoEncryptionEncValues, c.UserInfoResponseOptions.EncryptionEnc, "userinfo_encryption_response_enc"},
+		{p.ReqObjSigningAlgValues, c.RequestObjectOptions.SigningAlg, "request_object_signing_alg"},
+		{p.ReqObjEncryptionAlgValues, c.RequestObjectOptions.EncryptionAlg, "request_object_encryption_alg"},
+		{p.ReqObjEncryptionEncValues, c.RequestObjectOptions.EncryptionEnc, "request_object_encryption_enc"},
+	}
+	for _, field := range supports {
+		if field.requested == "" {
+			continue
+		}
+		if !contains(field.supported, field.requested) {
+			return fmt.Errorf("provider does not support requested value for field %s", field.name)
+		}
+	}
+
+	stringsEqual := func(s1, s2 string) bool { return s1 == s2 }
+
+	// For lists, are the list of requested values a subset of the supported ones?
+	supportsAll := []struct {
+		supported []string
+		requested []string
+		name      string
+		// OAuth2.0 response_type can be space separated lists where order doesn't matter.
+		// For example "id_token token" is the same as "token id_token"
+		// Support a custom compare method.
+		comp func(s1, s2 string) bool
+	}{
+		{p.GrantTypesSupported, c.GrantTypes, "grant_types", stringsEqual},
+		{p.ResponseTypesSupported, c.ResponseTypes, "response_type", oauth2.ResponseTypesEqual},
+	}
+	for _, field := range supportsAll {
+	requestLoop:
+		for _, req := range field.requested {
+			for _, sup := range field.supported {
+				if field.comp(req, sup) {
+					continue requestLoop
+				}
+			}
+			return fmt.Errorf("provider does not support requested value for field %s", field.name)
+		}
+	}
+
+	// TODO(ericchiang): Are there more checks we feel comfortable with begin strict about?
+
+	return nil
+}
+
+func (p ProviderConfig) SupportsGrantType(grantType string) bool {
+	var supported []string
+	if len(p.GrantTypesSupported) == 0 {
+		supported = DefaultGrantTypesSupported
+	} else {
+		supported = p.GrantTypesSupported
+	}
+
+	for _, t := range supported {
+		if t == grantType {
+			return true
+		}
+	}
+	return false
+}
+
+type ProviderConfigGetter interface {
+	Get() (ProviderConfig, error)
+}
+
+type ProviderConfigSetter interface {
+	Set(ProviderConfig) error
+}
+
+type ProviderConfigSyncer struct {
+	from  ProviderConfigGetter
+	to    ProviderConfigSetter
+	clock clockwork.Clock
+
+	initialSyncDone bool
+	initialSyncWait sync.WaitGroup
+}
+
+func NewProviderConfigSyncer(from ProviderConfigGetter, to ProviderConfigSetter) *ProviderConfigSyncer {
+	return &ProviderConfigSyncer{
+		from:  from,
+		to:    to,
+		clock: clockwork.NewRealClock(),
+	}
+}
+
+func (s *ProviderConfigSyncer) Run() chan struct{} {
+	stop := make(chan struct{})
+
+	var next pcsStepper
+	next = &pcsStepNext{aft: time.Duration(0)}
+
+	s.initialSyncWait.Add(1)
+	go func() {
+		for {
+			select {
+			case <-s.clock.After(next.after()):
+				next = next.step(s.sync)
+			case <-stop:
+				return
+			}
+		}
+	}()
+
+	return stop
+}
+
+func (s *ProviderConfigSyncer) WaitUntilInitialSync() {
+	s.initialSyncWait.Wait()
+}
+
+func (s *ProviderConfigSyncer) sync() (time.Duration, error) {
+	cfg, err := s.from.Get()
+	if err != nil {
+		return 0, err
+	}
+
+	if err = s.to.Set(cfg); err != nil {
+		return 0, fmt.Errorf("error setting provider config: %v", err)
+	}
+
+	if !s.initialSyncDone {
+		s.initialSyncWait.Done()
+		s.initialSyncDone = true
+	}
+
+	return nextSyncAfter(cfg.ExpiresAt, s.clock), nil
+}
+
+type pcsStepFunc func() (time.Duration, error)
+
+type pcsStepper interface {
+	after() time.Duration
+	step(pcsStepFunc) pcsStepper
+}
+
+type pcsStepNext struct {
+	aft time.Duration
+}
+
+func (n *pcsStepNext) after() time.Duration {
+	return n.aft
+}
+
+func (n *pcsStepNext) step(fn pcsStepFunc) (next pcsStepper) {
+	ttl, err := fn()
+	if err == nil {
+		next = &pcsStepNext{aft: ttl}
+	} else {
+		next = &pcsStepRetry{aft: time.Second}
+		log.Printf("go-oidc: provider config sync failed, retrying in %v: %v", next.after(), err)
+	}
+	return
+}
+
+type pcsStepRetry struct {
+	aft time.Duration
+}
+
+func (r *pcsStepRetry) after() time.Duration {
+	return r.aft
+}
+
+func (r *pcsStepRetry) step(fn pcsStepFunc) (next pcsStepper) {
+	ttl, err := fn()
+	if err == nil {
+		next = &pcsStepNext{aft: ttl}
+	} else {
+		next = &pcsStepRetry{aft: timeutil.ExpBackoff(r.aft, time.Minute)}
+		log.Printf("go-oidc: provider config sync failed, retrying in %v: %v", next.after(), err)
+	}
+	return
+}
+
+func nextSyncAfter(exp time.Time, clock clockwork.Clock) time.Duration {
+	if exp.IsZero() {
+		return MaximumProviderConfigSyncInterval
+	}
+
+	t := exp.Sub(clock.Now()) / 2
+	if t > MaximumProviderConfigSyncInterval {
+		t = MaximumProviderConfigSyncInterval
+	} else if t < minimumProviderConfigSyncInterval {
+		t = minimumProviderConfigSyncInterval
+	}
+
+	return t
+}
+
+type httpProviderConfigGetter struct {
+	hc        phttp.Client
+	issuerURL string
+	clock     clockwork.Clock
+}
+
+func NewHTTPProviderConfigGetter(hc phttp.Client, issuerURL string) *httpProviderConfigGetter {
+	return &httpProviderConfigGetter{
+		hc:        hc,
+		issuerURL: issuerURL,
+		clock:     clockwork.NewRealClock(),
+	}
+}
+
+func (r *httpProviderConfigGetter) Get() (cfg ProviderConfig, err error) {
+	// If the Issuer value contains a path component, any terminating / MUST be removed before
+	// appending /.well-known/openid-configuration.
+	// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest
+	discoveryURL := strings.TrimSuffix(r.issuerURL, "/") + discoveryConfigPath
+	req, err := http.NewRequest("GET", discoveryURL, nil)
+	if err != nil {
+		return
+	}
+
+	resp, err := r.hc.Do(req)
+	if err != nil {
+		return
+	}
+	defer resp.Body.Close()
+
+	if err = json.NewDecoder(resp.Body).Decode(&cfg); err != nil {
+		return
+	}
+
+	var ttl time.Duration
+	var ok bool
+	ttl, ok, err = phttp.Cacheable(resp.Header)
+	if err != nil {
+		return
+	} else if ok {
+		cfg.ExpiresAt = r.clock.Now().UTC().Add(ttl)
+	}
+
+	// The issuer value returned MUST be identical to the Issuer URL that was directly used to retrieve the configuration information.
+	// http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation
+	if !urlEqual(cfg.Issuer.String(), r.issuerURL) {
+		err = fmt.Errorf(`"issuer" in config (%v) does not match provided issuer URL (%v)`, cfg.Issuer, r.issuerURL)
+		return
+	}
+
+	return
+}
+
+func FetchProviderConfig(hc phttp.Client, issuerURL string) (ProviderConfig, error) {
+	if hc == nil {
+		hc = http.DefaultClient
+	}
+
+	g := NewHTTPProviderConfigGetter(hc, issuerURL)
+	return g.Get()
+}
+
+func WaitForProviderConfig(hc phttp.Client, issuerURL string) (pcfg ProviderConfig) {
+	return waitForProviderConfig(hc, issuerURL, clockwork.NewRealClock())
+}
+
+func waitForProviderConfig(hc phttp.Client, issuerURL string, clock clockwork.Clock) (pcfg ProviderConfig) {
+	var sleep time.Duration
+	var err error
+	for {
+		pcfg, err = FetchProviderConfig(hc, issuerURL)
+		if err == nil {
+			break
+		}
+
+		sleep = timeutil.ExpBackoff(sleep, time.Minute)
+		fmt.Printf("Failed fetching provider config, trying again in %v: %v\n", sleep, err)
+		time.Sleep(sleep)
+	}
+
+	return
+}
diff --git a/vendor/github.com/coreos/go-oidc/oidc/transport.go b/vendor/github.com/coreos/go-oidc/oidc/transport.go
new file mode 100644
index 00000000..61c926d7
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc/transport.go
@@ -0,0 +1,88 @@
+package oidc
+
+import (
+	"fmt"
+	"net/http"
+	"sync"
+
+	phttp "github.com/coreos/go-oidc/http"
+	"github.com/coreos/go-oidc/jose"
+)
+
+type TokenRefresher interface {
+	// Verify checks if the provided token is currently valid or not.
+	Verify(jose.JWT) error
+
+	// Refresh attempts to authenticate and retrieve a new token.
+	Refresh() (jose.JWT, error)
+}
+
+type ClientCredsTokenRefresher struct {
+	Issuer     string
+	OIDCClient *Client
+}
+
+func (c *ClientCredsTokenRefresher) Verify(jwt jose.JWT) (err error) {
+	_, err = VerifyClientClaims(jwt, c.Issuer)
+	return
+}
+
+func (c *ClientCredsTokenRefresher) Refresh() (jwt jose.JWT, err error) {
+	if err = c.OIDCClient.Healthy(); err != nil {
+		err = fmt.Errorf("unable to authenticate, unhealthy OIDC client: %v", err)
+		return
+	}
+
+	jwt, err = c.OIDCClient.ClientCredsToken([]string{"openid"})
+	if err != nil {
+		err = fmt.Errorf("unable to verify auth code with issuer: %v", err)
+		return
+	}
+
+	return
+}
+
+type AuthenticatedTransport struct {
+	TokenRefresher
+	http.RoundTripper
+
+	mu  sync.Mutex
+	jwt jose.JWT
+}
+
+func (t *AuthenticatedTransport) verifiedJWT() (jose.JWT, error) {
+	t.mu.Lock()
+	defer t.mu.Unlock()
+
+	if t.TokenRefresher.Verify(t.jwt) == nil {
+		return t.jwt, nil
+	}
+
+	jwt, err := t.TokenRefresher.Refresh()
+	if err != nil {
+		return jose.JWT{}, fmt.Errorf("unable to acquire valid JWT: %v", err)
+	}
+
+	t.jwt = jwt
+	return t.jwt, nil
+}
+
+// SetJWT sets the JWT held by the Transport.
+// This is useful for cases in which you want to set an initial JWT.
+func (t *AuthenticatedTransport) SetJWT(jwt jose.JWT) {
+	t.mu.Lock()
+	defer t.mu.Unlock()
+
+	t.jwt = jwt
+}
+
+func (t *AuthenticatedTransport) RoundTrip(r *http.Request) (*http.Response, error) {
+	jwt, err := t.verifiedJWT()
+	if err != nil {
+		return nil, err
+	}
+
+	req := phttp.CopyRequest(r)
+	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", jwt.Encode()))
+	return t.RoundTripper.RoundTrip(req)
+}
diff --git a/vendor/github.com/coreos/go-oidc/oidc/util.go b/vendor/github.com/coreos/go-oidc/oidc/util.go
new file mode 100644
index 00000000..f2a5a195
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc/util.go
@@ -0,0 +1,109 @@
+package oidc
+
+import (
+	"crypto/rand"
+	"encoding/base64"
+	"errors"
+	"fmt"
+	"net"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+
+	"github.com/coreos/go-oidc/jose"
+)
+
+// RequestTokenExtractor funcs extract a raw encoded token from a request.
+type RequestTokenExtractor func(r *http.Request) (string, error)
+
+// ExtractBearerToken is a RequestTokenExtractor which extracts a bearer token from a request's
+// Authorization header.
+func ExtractBearerToken(r *http.Request) (string, error) {
+	ah := r.Header.Get("Authorization")
+	if ah == "" {
+		return "", errors.New("missing Authorization header")
+	}
+
+	if len(ah) <= 6 || strings.ToUpper(ah[0:6]) != "BEARER" {
+		return "", errors.New("should be a bearer token")
+	}
+
+	val := ah[7:]
+	if len(val) == 0 {
+		return "", errors.New("bearer token is empty")
+	}
+
+	return val, nil
+}
+
+// CookieTokenExtractor returns a RequestTokenExtractor which extracts a token from the named cookie in a request.
+func CookieTokenExtractor(cookieName string) RequestTokenExtractor {
+	return func(r *http.Request) (string, error) {
+		ck, err := r.Cookie(cookieName)
+		if err != nil {
+			return "", fmt.Errorf("token cookie not found in request: %v", err)
+		}
+
+		if ck.Value == "" {
+			return "", errors.New("token cookie found but is empty")
+		}
+
+		return ck.Value, nil
+	}
+}
+
+func NewClaims(iss, sub string, aud interface{}, iat, exp time.Time) jose.Claims {
+	return jose.Claims{
+		// required
+		"iss": iss,
+		"sub": sub,
+		"aud": aud,
+		"iat": iat.Unix(),
+		"exp": exp.Unix(),
+	}
+}
+
+func GenClientID(hostport string) (string, error) {
+	b, err := randBytes(32)
+	if err != nil {
+		return "", err
+	}
+
+	var host string
+	if strings.Contains(hostport, ":") {
+		host, _, err = net.SplitHostPort(hostport)
+		if err != nil {
+			return "", err
+		}
+	} else {
+		host = hostport
+	}
+
+	return fmt.Sprintf("%s@%s", base64.URLEncoding.EncodeToString(b), host), nil
+}
+
+func randBytes(n int) ([]byte, error) {
+	b := make([]byte, n)
+	got, err := rand.Read(b)
+	if err != nil {
+		return nil, err
+	} else if n != got {
+		return nil, errors.New("unable to generate enough random data")
+	}
+	return b, nil
+}
+
+// urlEqual checks two urls for equality using only the host and path portions.
+func urlEqual(url1, url2 string) bool {
+	u1, err := url.Parse(url1)
+	if err != nil {
+		return false
+	}
+	u2, err := url.Parse(url2)
+	if err != nil {
+		return false
+	}
+
+	return strings.ToLower(u1.Host+u1.Path) == strings.ToLower(u2.Host+u2.Path)
+}
diff --git a/vendor/github.com/coreos/go-oidc/oidc/verification.go b/vendor/github.com/coreos/go-oidc/oidc/verification.go
new file mode 100644
index 00000000..d9c6afa6
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/oidc/verification.go
@@ -0,0 +1,190 @@
+package oidc
+
+import (
+	"errors"
+	"fmt"
+	"time"
+
+	"github.com/jonboulle/clockwork"
+
+	"github.com/coreos/go-oidc/jose"
+	"github.com/coreos/go-oidc/key"
+)
+
+func VerifySignature(jwt jose.JWT, keys []key.PublicKey) (bool, error) {
+	jwtBytes := []byte(jwt.Data())
+	for _, k := range keys {
+		v, err := k.Verifier()
+		if err != nil {
+			return false, err
+		}
+		if v.Verify(jwt.Signature, jwtBytes) == nil {
+			return true, nil
+		}
+	}
+	return false, nil
+}
+
+// containsString returns true if the given string(needle) is found
+// in the string array(haystack).
+func containsString(needle string, haystack []string) bool {
+	for _, v := range haystack {
+		if v == needle {
+			return true
+		}
+	}
+	return false
+}
+
+// Verify claims in accordance with OIDC spec
+// http://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation
+func VerifyClaims(jwt jose.JWT, issuer, clientID string) error {
+	now := time.Now().UTC()
+
+	claims, err := jwt.Claims()
+	if err != nil {
+		return err
+	}
+
+	ident, err := IdentityFromClaims(claims)
+	if err != nil {
+		return err
+	}
+
+	if ident.ExpiresAt.Before(now) {
+		return errors.New("token is expired")
+	}
+
+	// iss REQUIRED. Issuer Identifier for the Issuer of the response.
+	// The iss value is a case sensitive URL using the https scheme that contains scheme,
+	// host, and optionally, port number and path components and no query or fragment components.
+	if iss, exists := claims["iss"].(string); exists {
+		if !urlEqual(iss, issuer) {
+			return fmt.Errorf("invalid claim value: 'iss'. expected=%s, found=%s.", issuer, iss)
+		}
+	} else {
+		return errors.New("missing claim: 'iss'")
+	}
+
+	// iat REQUIRED. Time at which the JWT was issued.
+	// Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z
+	// as measured in UTC until the date/time.
+	if _, exists := claims["iat"].(float64); !exists {
+		return errors.New("missing claim: 'iat'")
+	}
+
+	// aud REQUIRED. Audience(s) that this ID Token is intended for.
+	// It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value.
+	// It MAY also contain identifiers for other audiences. In the general case, the aud
+	// value is an array of case sensitive strings. In the common special case when there
+	// is one audience, the aud value MAY be a single case sensitive string.
+	if aud, ok, err := claims.StringClaim("aud"); err == nil && ok {
+		if aud != clientID {
+			return fmt.Errorf("invalid claims, 'aud' claim and 'client_id' do not match, aud=%s, client_id=%s", aud, clientID)
+		}
+	} else if aud, ok, err := claims.StringsClaim("aud"); err == nil && ok {
+		if !containsString(clientID, aud) {
+			return fmt.Errorf("invalid claims, cannot find 'client_id' in 'aud' claim, aud=%v, client_id=%s", aud, clientID)
+		}
+	} else {
+		return errors.New("invalid claim value: 'aud' is required, and should be either string or string array")
+	}
+
+	return nil
+}
+
+// VerifyClientClaims verifies all the required claims are valid for a "client credentials" JWT.
+// Returns the client ID if valid, or an error if invalid.
+func VerifyClientClaims(jwt jose.JWT, issuer string) (string, error) {
+	claims, err := jwt.Claims()
+	if err != nil {
+		return "", fmt.Errorf("failed to parse JWT claims: %v", err)
+	}
+
+	iss, ok, err := claims.StringClaim("iss")
+	if err != nil {
+		return "", fmt.Errorf("failed to parse 'iss' claim: %v", err)
+	} else if !ok {
+		return "", errors.New("missing required 'iss' claim")
+	} else if !urlEqual(iss, issuer) {
+		return "", fmt.Errorf("'iss' claim does not match expected issuer, iss=%s", iss)
+	}
+
+	sub, ok, err := claims.StringClaim("sub")
+	if err != nil {
+		return "", fmt.Errorf("failed to parse 'sub' claim: %v", err)
+	} else if !ok {
+		return "", errors.New("missing required 'sub' claim")
+	}
+
+	if aud, ok, err := claims.StringClaim("aud"); err == nil && ok {
+		if aud != sub {
+			return "", fmt.Errorf("invalid claims, 'aud' claim and 'sub' claim do not match, aud=%s, sub=%s", aud, sub)
+		}
+	} else if aud, ok, err := claims.StringsClaim("aud"); err == nil && ok {
+		if !containsString(sub, aud) {
+			return "", fmt.Errorf("invalid claims, cannot find 'sud' in 'aud' claim, aud=%v, sub=%s", aud, sub)
+		}
+	} else {
+		return "", errors.New("invalid claim value: 'aud' is required, and should be either string or string array")
+	}
+
+	now := time.Now().UTC()
+	exp, ok, err := claims.TimeClaim("exp")
+	if err != nil {
+		return "", fmt.Errorf("failed to parse 'exp' claim: %v", err)
+	} else if !ok {
+		return "", errors.New("missing required 'exp' claim")
+	} else if exp.Before(now) {
+		return "", fmt.Errorf("token already expired at: %v", exp)
+	}
+
+	return sub, nil
+}
+
+type JWTVerifier struct {
+	issuer   string
+	clientID string
+	syncFunc func() error
+	keysFunc func() []key.PublicKey
+	clock    clockwork.Clock
+}
+
+func NewJWTVerifier(issuer, clientID string, syncFunc func() error, keysFunc func() []key.PublicKey) JWTVerifier {
+	return JWTVerifier{
+		issuer:   issuer,
+		clientID: clientID,
+		syncFunc: syncFunc,
+		keysFunc: keysFunc,
+		clock:    clockwork.NewRealClock(),
+	}
+}
+
+func (v *JWTVerifier) Verify(jwt jose.JWT) error {
+	// Verify claims before verifying the signature. This is an optimization to throw out
+	// tokens we know are invalid without undergoing an expensive signature check and
+	// possibly a re-sync event.
+	if err := VerifyClaims(jwt, v.issuer, v.clientID); err != nil {
+		return fmt.Errorf("oidc: JWT claims invalid: %v", err)
+	}
+
+	ok, err := VerifySignature(jwt, v.keysFunc())
+	if err != nil {
+		return fmt.Errorf("oidc: JWT signature verification failed: %v", err)
+	} else if ok {
+		return nil
+	}
+
+	if err = v.syncFunc(); err != nil {
+		return fmt.Errorf("oidc: failed syncing KeySet: %v", err)
+	}
+
+	ok, err = VerifySignature(jwt, v.keysFunc())
+	if err != nil {
+		return fmt.Errorf("oidc: JWT signature verification failed: %v", err)
+	} else if !ok {
+		return errors.New("oidc: unable to verify JWT signature: no matching keys")
+	}
+
+	return nil
+}
diff --git a/vendor/github.com/coreos/pkg/LICENSE b/vendor/github.com/coreos/pkg/LICENSE
new file mode 100644
index 00000000..e06d2081
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/vendor/github.com/coreos/pkg/NOTICE b/vendor/github.com/coreos/pkg/NOTICE
new file mode 100644
index 00000000..b39ddfa5
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/NOTICE
@@ -0,0 +1,5 @@
+CoreOS Project
+Copyright 2014 CoreOS, Inc
+
+This product includes software developed at CoreOS, Inc.
+(http://www.coreos.com/).
diff --git a/vendor/github.com/coreos/pkg/health/README.md b/vendor/github.com/coreos/pkg/health/README.md
new file mode 100644
index 00000000..5ec34c21
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/health/README.md
@@ -0,0 +1,11 @@
+health
+====
+
+A simple framework for implementing an HTTP health check endpoint on servers.
+
+Users implement their `health.Checkable` types, and create a `health.Checker`, from which they can get an `http.HandlerFunc` using `health.Checker.MakeHealthHandlerFunc`.
+
+### Documentation
+
+For more details, visit the docs on [gopkgdoc](http://godoc.org/github.com/coreos/pkg/health)
+
diff --git a/vendor/github.com/coreos/pkg/health/health.go b/vendor/github.com/coreos/pkg/health/health.go
new file mode 100644
index 00000000..a1c3610f
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/health/health.go
@@ -0,0 +1,127 @@
+package health
+
+import (
+	"expvar"
+	"fmt"
+	"log"
+	"net/http"
+
+	"github.com/coreos/pkg/httputil"
+)
+
+// Checkables should return nil when the thing they are checking is healthy, and an error otherwise.
+type Checkable interface {
+	Healthy() error
+}
+
+// Checker provides a way to make an endpoint which can be probed for system health.
+type Checker struct {
+	// Checks are the Checkables to be checked when probing.
+	Checks []Checkable
+
+	// Unhealthyhandler is called when one or more of the checks are unhealthy.
+	// If not provided DefaultUnhealthyHandler is called.
+	UnhealthyHandler UnhealthyHandler
+
+	// HealthyHandler is called when all checks are healthy.
+	// If not provided, DefaultHealthyHandler is called.
+	HealthyHandler http.HandlerFunc
+}
+
+func (c Checker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	unhealthyHandler := c.UnhealthyHandler
+	if unhealthyHandler == nil {
+		unhealthyHandler = DefaultUnhealthyHandler
+	}
+
+	successHandler := c.HealthyHandler
+	if successHandler == nil {
+		successHandler = DefaultHealthyHandler
+	}
+
+	if r.Method != "GET" {
+		w.Header().Set("Allow", "GET")
+		w.WriteHeader(http.StatusMethodNotAllowed)
+		return
+	}
+
+	if err := Check(c.Checks); err != nil {
+		unhealthyHandler(w, r, err)
+		return
+	}
+
+	successHandler(w, r)
+}
+
+type UnhealthyHandler func(w http.ResponseWriter, r *http.Request, err error)
+
+type StatusResponse struct {
+	Status  string                 `json:"status"`
+	Details *StatusResponseDetails `json:"details,omitempty"`
+}
+
+type StatusResponseDetails struct {
+	Code    int    `json:"code,omitempty"`
+	Message string `json:"message,omitempty"`
+}
+
+func Check(checks []Checkable) (err error) {
+	errs := []error{}
+	for _, c := range checks {
+		if e := c.Healthy(); e != nil {
+			errs = append(errs, e)
+		}
+	}
+
+	switch len(errs) {
+	case 0:
+		err = nil
+	case 1:
+		err = errs[0]
+	default:
+		err = fmt.Errorf("multiple health check failure: %v", errs)
+	}
+
+	return
+}
+
+func DefaultHealthyHandler(w http.ResponseWriter, r *http.Request) {
+	err := httputil.WriteJSONResponse(w, http.StatusOK, StatusResponse{
+		Status: "ok",
+	})
+	if err != nil {
+		// TODO(bobbyrullo): replace with logging from new logging pkg,
+		// once it lands.
+		log.Printf("Failed to write JSON response: %v", err)
+	}
+}
+
+func DefaultUnhealthyHandler(w http.ResponseWriter, r *http.Request, err error) {
+	writeErr := httputil.WriteJSONResponse(w, http.StatusInternalServerError, StatusResponse{
+		Status: "error",
+		Details: &StatusResponseDetails{
+			Code:    http.StatusInternalServerError,
+			Message: err.Error(),
+		},
+	})
+	if writeErr != nil {
+		// TODO(bobbyrullo): replace with logging from new logging pkg,
+		// once it lands.
+		log.Printf("Failed to write JSON response: %v", err)
+	}
+}
+
+// ExpvarHandler is copied from https://golang.org/src/expvar/expvar.go, where it's sadly unexported.
+func ExpvarHandler(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json; charset=utf-8")
+	fmt.Fprintf(w, "{\n")
+	first := true
+	expvar.Do(func(kv expvar.KeyValue) {
+		if !first {
+			fmt.Fprintf(w, ",\n")
+		}
+		first = false
+		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
+	})
+	fmt.Fprintf(w, "\n}\n")
+}
diff --git a/vendor/github.com/coreos/pkg/httputil/README.md b/vendor/github.com/coreos/pkg/httputil/README.md
new file mode 100644
index 00000000..44fa751c
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/httputil/README.md
@@ -0,0 +1,13 @@
+httputil
+====
+
+Common code for dealing with HTTP.
+
+Includes:
+
+* Code for returning JSON responses.
+
+### Documentation
+
+Visit the docs on [gopkgdoc](http://godoc.org/github.com/coreos/pkg/httputil)
+
diff --git a/vendor/github.com/coreos/pkg/httputil/cookie.go b/vendor/github.com/coreos/pkg/httputil/cookie.go
new file mode 100644
index 00000000..c37a37bb
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/httputil/cookie.go
@@ -0,0 +1,21 @@
+package httputil
+
+import (
+	"net/http"
+	"time"
+)
+
+// DeleteCookies effectively deletes all named cookies
+// by wiping all data and setting to expire immediately.
+func DeleteCookies(w http.ResponseWriter, cookieNames ...string) {
+	for _, n := range cookieNames {
+		c := &http.Cookie{
+			Name:    n,
+			Value:   "",
+			Path:    "/",
+			MaxAge:  -1,
+			Expires: time.Time{},
+		}
+		http.SetCookie(w, c)
+	}
+}
diff --git a/vendor/github.com/coreos/pkg/httputil/json.go b/vendor/github.com/coreos/pkg/httputil/json.go
new file mode 100644
index 00000000..0b092350
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/httputil/json.go
@@ -0,0 +1,27 @@
+package httputil
+
+import (
+	"encoding/json"
+	"net/http"
+)
+
+const (
+	JSONContentType = "application/json"
+)
+
+func WriteJSONResponse(w http.ResponseWriter, code int, resp interface{}) error {
+	enc, err := json.Marshal(resp)
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		return err
+	}
+
+	w.Header().Set("Content-Type", JSONContentType)
+	w.WriteHeader(code)
+
+	_, err = w.Write(enc)
+	if err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/vendor/github.com/coreos/pkg/timeutil/backoff.go b/vendor/github.com/coreos/pkg/timeutil/backoff.go
new file mode 100644
index 00000000..b34fb496
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/timeutil/backoff.go
@@ -0,0 +1,15 @@
+package timeutil
+
+import (
+	"time"
+)
+
+func ExpBackoff(prev, max time.Duration) time.Duration {
+	if prev == 0 {
+		return time.Second
+	}
+	if prev > max/2 {
+		return max
+	}
+	return 2 * prev
+}
diff --git a/vendor/github.com/jonboulle/clockwork/LICENSE b/vendor/github.com/jonboulle/clockwork/LICENSE
new file mode 100644
index 00000000..5c304d1a
--- /dev/null
+++ b/vendor/github.com/jonboulle/clockwork/LICENSE
@@ -0,0 +1,201 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/github.com/jonboulle/clockwork/README.md b/vendor/github.com/jonboulle/clockwork/README.md
new file mode 100644
index 00000000..47bc136b
--- /dev/null
+++ b/vendor/github.com/jonboulle/clockwork/README.md
@@ -0,0 +1,69 @@
+clockwork
+=========
+
+[![Build Status](https://travis-ci.org/jonboulle/clockwork.png?branch=master)](https://travis-ci.org/jonboulle/clockwork)
+[![godoc](https://godoc.org/github.com/jonboulle/clockwork?status.svg)](http://godoc.org/github.com/jonboulle/clockwork)
+
+a simple fake clock for golang
+
+# Usage
+
+Replace uses of the `time` package with the `clockwork.Clock` interface instead.
+
+For example, instead of using `time.Sleep` directly:
+
+```
+func my_func() {
+	time.Sleep(3 * time.Second)
+	do_something()
+}
+```
+
+inject a clock and use its `Sleep` method instead:
+
+```
+func my_func(clock clockwork.Clock) {
+	clock.Sleep(3 * time.Second)
+	do_something()
+}
+```
+
+Now you can easily test `my_func` with a `FakeClock`:
+
+```
+func TestMyFunc(t *testing.T) {
+	c := clockwork.NewFakeClock()
+
+	// Start our sleepy function
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		my_func(c)
+		wg.Done()
+	}()
+
+	// Ensure we wait until my_func is sleeping
+	c.BlockUntil(1)
+
+	assert_state()
+
+	// Advance the FakeClock forward in time
+	c.Advance(3 * time.Second)
+
+	// Wait until the function completes
+	wg.Wait()
+
+	assert_state()
+}
+```
+
+and in production builds, simply inject the real clock instead:
+```
+my_func(clockwork.NewRealClock())
+```
+
+See [example_test.go](example_test.go) for a full example.
+
+# Credits
+
+clockwork is inspired by @wickman's [threaded fake clock](https://gist.github.com/wickman/3840816), and the [Golang playground](http://blog.golang.org/playground#Faking time)
diff --git a/vendor/github.com/jonboulle/clockwork/clockwork.go b/vendor/github.com/jonboulle/clockwork/clockwork.go
new file mode 100644
index 00000000..999fddd5
--- /dev/null
+++ b/vendor/github.com/jonboulle/clockwork/clockwork.go
@@ -0,0 +1,179 @@
+package clockwork
+
+import (
+	"sync"
+	"time"
+)
+
+// Clock provides an interface that packages can use instead of directly
+// using the time module, so that chronology-related behavior can be tested
+type Clock interface {
+	After(d time.Duration) <-chan time.Time
+	Sleep(d time.Duration)
+	Now() time.Time
+	Since(t time.Time) time.Duration
+}
+
+// FakeClock provides an interface for a clock which can be
+// manually advanced through time
+type FakeClock interface {
+	Clock
+	// Advance advances the FakeClock to a new point in time, ensuring any existing
+	// sleepers are notified appropriately before returning
+	Advance(d time.Duration)
+	// BlockUntil will block until the FakeClock has the given number of
+	// sleepers (callers of Sleep or After)
+	BlockUntil(n int)
+}
+
+// NewRealClock returns a Clock which simply delegates calls to the actual time
+// package; it should be used by packages in production.
+func NewRealClock() Clock {
+	return &realClock{}
+}
+
+// NewFakeClock returns a FakeClock implementation which can be
+// manually advanced through time for testing. The initial time of the
+// FakeClock will be an arbitrary non-zero time.
+func NewFakeClock() FakeClock {
+	// use a fixture that does not fulfill Time.IsZero()
+	return NewFakeClockAt(time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC))
+}
+
+// NewFakeClockAt returns a FakeClock initialised at the given time.Time.
+func NewFakeClockAt(t time.Time) FakeClock {
+	return &fakeClock{
+		time: t,
+	}
+}
+
+type realClock struct{}
+
+func (rc *realClock) After(d time.Duration) <-chan time.Time {
+	return time.After(d)
+}
+
+func (rc *realClock) Sleep(d time.Duration) {
+	time.Sleep(d)
+}
+
+func (rc *realClock) Now() time.Time {
+	return time.Now()
+}
+
+func (rc *realClock) Since(t time.Time) time.Duration {
+	return rc.Now().Sub(t)
+}
+
+type fakeClock struct {
+	sleepers []*sleeper
+	blockers []*blocker
+	time     time.Time
+
+	l sync.RWMutex
+}
+
+// sleeper represents a caller of After or Sleep
+type sleeper struct {
+	until time.Time
+	done  chan time.Time
+}
+
+// blocker represents a caller of BlockUntil
+type blocker struct {
+	count int
+	ch    chan struct{}
+}
+
+// After mimics time.After; it waits for the given duration to elapse on the
+// fakeClock, then sends the current time on the returned channel.
+func (fc *fakeClock) After(d time.Duration) <-chan time.Time {
+	fc.l.Lock()
+	defer fc.l.Unlock()
+	now := fc.time
+	done := make(chan time.Time, 1)
+	if d.Nanoseconds() == 0 {
+		// special case - trigger immediately
+		done <- now
+	} else {
+		// otherwise, add to the set of sleepers
+		s := &sleeper{
+			until: now.Add(d),
+			done:  done,
+		}
+		fc.sleepers = append(fc.sleepers, s)
+		// and notify any blockers
+		fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers))
+	}
+	return done
+}
+
+// notifyBlockers notifies all the blockers waiting until the
+// given number of sleepers are waiting on the fakeClock. It
+// returns an updated slice of blockers (i.e. those still waiting)
+func notifyBlockers(blockers []*blocker, count int) (newBlockers []*blocker) {
+	for _, b := range blockers {
+		if b.count == count {
+			close(b.ch)
+		} else {
+			newBlockers = append(newBlockers, b)
+		}
+	}
+	return
+}
+
+// Sleep blocks until the given duration has passed on the fakeClock
+func (fc *fakeClock) Sleep(d time.Duration) {
+	<-fc.After(d)
+}
+
+// Time returns the current time of the fakeClock
+func (fc *fakeClock) Now() time.Time {
+	fc.l.RLock()
+	t := fc.time
+	fc.l.RUnlock()
+	return t
+}
+
+// Since returns the duration that has passed since the given time on the fakeClock
+func (fc *fakeClock) Since(t time.Time) time.Duration {
+	return fc.Now().Sub(t)
+}
+
+// Advance advances fakeClock to a new point in time, ensuring channels from any
+// previous invocations of After are notified appropriately before returning
+func (fc *fakeClock) Advance(d time.Duration) {
+	fc.l.Lock()
+	defer fc.l.Unlock()
+	end := fc.time.Add(d)
+	var newSleepers []*sleeper
+	for _, s := range fc.sleepers {
+		if end.Sub(s.until) >= 0 {
+			s.done <- end
+		} else {
+			newSleepers = append(newSleepers, s)
+		}
+	}
+	fc.sleepers = newSleepers
+	fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers))
+	fc.time = end
+}
+
+// BlockUntil will block until the fakeClock has the given number of sleepers
+// (callers of Sleep or After)
+func (fc *fakeClock) BlockUntil(n int) {
+	fc.l.Lock()
+	// Fast path: current number of sleepers is what we're looking for
+	if len(fc.sleepers) == n {
+		fc.l.Unlock()
+		return
+	}
+	// Otherwise, set up a new blocker
+	b := &blocker{
+		count: n,
+		ch:    make(chan struct{}),
+	}
+	fc.blockers = append(fc.blockers, b)
+	fc.l.Unlock()
+	<-b.ch
+}
diff --git a/vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/OWNERS b/vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/OWNERS
new file mode 100644
index 00000000..d2210cb8
--- /dev/null
+++ b/vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/OWNERS
@@ -0,0 +1,4 @@
+approvers:
+- ericchiang
+reviewers:
+- ericchiang
diff --git a/vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go b/vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go
new file mode 100644
index 00000000..ca5b72cb
--- /dev/null
+++ b/vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go
@@ -0,0 +1,333 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package oidc
+
+import (
+	"encoding/base64"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/coreos/go-oidc/jose"
+	"github.com/coreos/go-oidc/oauth2"
+	"github.com/coreos/go-oidc/oidc"
+	"github.com/golang/glog"
+
+	restclient "k8s.io/client-go/rest"
+)
+
+const (
+	cfgIssuerUrl                = "idp-issuer-url"
+	cfgClientID                 = "client-id"
+	cfgClientSecret             = "client-secret"
+	cfgCertificateAuthority     = "idp-certificate-authority"
+	cfgCertificateAuthorityData = "idp-certificate-authority-data"
+	cfgExtraScopes              = "extra-scopes"
+	cfgIDToken                  = "id-token"
+	cfgRefreshToken             = "refresh-token"
+)
+
+func init() {
+	if err := restclient.RegisterAuthProviderPlugin("oidc", newOIDCAuthProvider); err != nil {
+		glog.Fatalf("Failed to register oidc auth plugin: %v", err)
+	}
+}
+
+// expiryDelta determines how earlier a token should be considered
+// expired than its actual expiration time. It is used to avoid late
+// expirations due to client-server time mismatches.
+//
+// NOTE(ericchiang): this is take from golang.org/x/oauth2
+const expiryDelta = 10 * time.Second
+
+var cache = newClientCache()
+
+// Like TLS transports, keep a cache of OIDC clients indexed by issuer URL.
+type clientCache struct {
+	mu    sync.RWMutex
+	cache map[cacheKey]*oidcAuthProvider
+}
+
+func newClientCache() *clientCache {
+	return &clientCache{cache: make(map[cacheKey]*oidcAuthProvider)}
+}
+
+type cacheKey struct {
+	// Canonical issuer URL string of the provider.
+	issuerURL string
+
+	clientID     string
+	clientSecret string
+
+	// Don't use CA as cache key because we only add a cache entry if we can connect
+	// to the issuer in the first place. A valid CA is a prerequisite.
+}
+
+func (c *clientCache) getClient(issuer, clientID, clientSecret string) (*oidcAuthProvider, bool) {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
+	client, ok := c.cache[cacheKey{issuer, clientID, clientSecret}]
+	return client, ok
+}
+
+// setClient attempts to put the client in the cache but may return any clients
+// with the same keys set before. This is so there's only ever one client for a provider.
+func (c *clientCache) setClient(issuer, clientID, clientSecret string, client *oidcAuthProvider) *oidcAuthProvider {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	key := cacheKey{issuer, clientID, clientSecret}
+
+	// If another client has already initialized a client for the given provider we want
+	// to use that client instead of the one we're trying to set. This is so all transports
+	// share a client and can coordinate around the same mutex when refreshing and writing
+	// to the kubeconfig.
+	if oldClient, ok := c.cache[key]; ok {
+		return oldClient
+	}
+
+	c.cache[key] = client
+	return client
+}
+
+func newOIDCAuthProvider(_ string, cfg map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) {
+	issuer := cfg[cfgIssuerUrl]
+	if issuer == "" {
+		return nil, fmt.Errorf("Must provide %s", cfgIssuerUrl)
+	}
+
+	clientID := cfg[cfgClientID]
+	if clientID == "" {
+		return nil, fmt.Errorf("Must provide %s", cfgClientID)
+	}
+
+	clientSecret := cfg[cfgClientSecret]
+	if clientSecret == "" {
+		return nil, fmt.Errorf("Must provide %s", cfgClientSecret)
+	}
+
+	// Check cache for existing provider.
+	if provider, ok := cache.getClient(issuer, clientID, clientSecret); ok {
+		return provider, nil
+	}
+
+	var certAuthData []byte
+	var err error
+	if cfg[cfgCertificateAuthorityData] != "" {
+		certAuthData, err = base64.StdEncoding.DecodeString(cfg[cfgCertificateAuthorityData])
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	clientConfig := restclient.Config{
+		TLSClientConfig: restclient.TLSClientConfig{
+			CAFile: cfg[cfgCertificateAuthority],
+			CAData: certAuthData,
+		},
+	}
+
+	trans, err := restclient.TransportFor(&clientConfig)
+	if err != nil {
+		return nil, err
+	}
+	hc := &http.Client{Transport: trans}
+
+	providerCfg, err := oidc.FetchProviderConfig(hc, issuer)
+	if err != nil {
+		return nil, fmt.Errorf("error fetching provider config: %v", err)
+	}
+
+	scopes := strings.Split(cfg[cfgExtraScopes], ",")
+	oidcCfg := oidc.ClientConfig{
+		HTTPClient: hc,
+		Credentials: oidc.ClientCredentials{
+			ID:     clientID,
+			Secret: clientSecret,
+		},
+		ProviderConfig: providerCfg,
+		Scope:          append(scopes, oidc.DefaultScope...),
+	}
+	client, err := oidc.NewClient(oidcCfg)
+	if err != nil {
+		return nil, fmt.Errorf("error creating OIDC Client: %v", err)
+	}
+
+	provider := &oidcAuthProvider{
+		client:    &oidcClient{client},
+		cfg:       cfg,
+		persister: persister,
+		now:       time.Now,
+	}
+
+	return cache.setClient(issuer, clientID, clientSecret, provider), nil
+}
+
+type oidcAuthProvider struct {
+	// Interface rather than a raw *oidc.Client for testing.
+	client OIDCClient
+
+	// Stubbed out for testing.
+	now func() time.Time
+
+	// Mutex guards persisting to the kubeconfig file and allows synchronized
+	// updates to the in-memory config. It also ensures concurrent calls to
+	// the RoundTripper only trigger a single refresh request.
+	mu        sync.Mutex
+	cfg       map[string]string
+	persister restclient.AuthProviderConfigPersister
+}
+
+func (p *oidcAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper {
+	return &roundTripper{
+		wrapped:  rt,
+		provider: p,
+	}
+}
+
+func (p *oidcAuthProvider) Login() error {
+	return errors.New("not yet implemented")
+}
+
+type OIDCClient interface {
+	refreshToken(rt string) (oauth2.TokenResponse, error)
+	verifyJWT(jwt *jose.JWT) error
+}
+
+type roundTripper struct {
+	provider *oidcAuthProvider
+	wrapped  http.RoundTripper
+}
+
+func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+	token, err := r.provider.idToken()
+	if err != nil {
+		return nil, err
+	}
+
+	// shallow copy of the struct
+	r2 := new(http.Request)
+	*r2 = *req
+	// deep copy of the Header so we don't modify the original
+	// request's Header (as per RoundTripper contract).
+	r2.Header = make(http.Header)
+	for k, s := range req.Header {
+		r2.Header[k] = s
+	}
+	r2.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
+
+	return r.wrapped.RoundTrip(r2)
+}
+
+func (p *oidcAuthProvider) idToken() (string, error) {
+	p.mu.Lock()
+	defer p.mu.Unlock()
+
+	if idToken, ok := p.cfg[cfgIDToken]; ok && len(idToken) > 0 {
+		valid, err := verifyJWTExpiry(p.now(), idToken)
+		if err != nil {
+			return "", err
+		}
+		if valid {
+			// If the cached id token is still valid use it.
+			return idToken, nil
+		}
+	}
+
+	// Try to request a new token using the refresh token.
+	rt, ok := p.cfg[cfgRefreshToken]
+	if !ok || len(rt) == 0 {
+		return "", errors.New("No valid id-token, and cannot refresh without refresh-token")
+	}
+
+	tokens, err := p.client.refreshToken(rt)
+	if err != nil {
+		return "", fmt.Errorf("could not refresh token: %v", err)
+	}
+	jwt, err := jose.ParseJWT(tokens.IDToken)
+	if err != nil {
+		return "", err
+	}
+
+	if err := p.client.verifyJWT(&jwt); err != nil {
+		return "", err
+	}
+
+	// Create a new config to persist.
+	newCfg := make(map[string]string)
+	for key, val := range p.cfg {
+		newCfg[key] = val
+	}
+
+	if tokens.RefreshToken != "" && tokens.RefreshToken != rt {
+		newCfg[cfgRefreshToken] = tokens.RefreshToken
+	}
+
+	newCfg[cfgIDToken] = tokens.IDToken
+	if err = p.persister.Persist(newCfg); err != nil {
+		return "", fmt.Errorf("could not perist new tokens: %v", err)
+	}
+
+	// Update the in memory config to reflect the on disk one.
+	p.cfg = newCfg
+
+	return tokens.IDToken, nil
+}
+
+// oidcClient is the real implementation of the OIDCClient interface, which is
+// used for testing.
+type oidcClient struct {
+	client *oidc.Client
+}
+
+func (o *oidcClient) refreshToken(rt string) (oauth2.TokenResponse, error) {
+	oac, err := o.client.OAuthClient()
+	if err != nil {
+		return oauth2.TokenResponse{}, err
+	}
+
+	return oac.RequestToken(oauth2.GrantTypeRefreshToken, rt)
+}
+
+func (o *oidcClient) verifyJWT(jwt *jose.JWT) error {
+	return o.client.VerifyJWT(*jwt)
+}
+
+func verifyJWTExpiry(now time.Time, s string) (valid bool, err error) {
+	jwt, err := jose.ParseJWT(s)
+	if err != nil {
+		return false, fmt.Errorf("invalid %q", cfgIDToken)
+	}
+	claims, err := jwt.Claims()
+	if err != nil {
+		return false, err
+	}
+
+	exp, ok, err := claims.TimeClaim("exp")
+	switch {
+	case err != nil:
+		return false, fmt.Errorf("failed to parse 'exp' claim: %v", err)
+	case !ok:
+		return false, errors.New("missing required 'exp' claim")
+	case exp.After(now.Add(expiryDelta)):
+		return true, nil
+	}
+
+	return false, nil
+}
diff --git a/vendor/k8s.io/client-go/plugin/pkg/client/auth/plugins.go b/vendor/k8s.io/client-go/plugin/pkg/client/auth/plugins.go
new file mode 100644
index 00000000..5cb2375f
--- /dev/null
+++ b/vendor/k8s.io/client-go/plugin/pkg/client/auth/plugins.go
@@ -0,0 +1,23 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package auth
+
+import (
+	// Initialize all known client auth plugins.
+	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
+	_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
+)
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 86fcc9d3..b2de91fe 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -32,6 +32,54 @@
 			"revision": "bbf7a2afc14f93e1e0a5c06df524fbd75e5031e5",
 			"revisionTime": "2017-03-24T14:02:28Z"
 		},
+		{
+			"checksumSHA1": "vM0AXBjMyPUVF7sLxjXX0to78l0=",
+			"path": "github.com/coreos/go-oidc/http",
+			"revision": "d68c0e2fef598f5bbf15edd34905f4bf551a54ec",
+			"revisionTime": "2017-06-16T01:39:05Z"
+		},
+		{
+			"checksumSHA1": "4tTcNh86OwCSkCD6WeA6ajRYqDA=",
+			"path": "github.com/coreos/go-oidc/jose",
+			"revision": "d68c0e2fef598f5bbf15edd34905f4bf551a54ec",
+			"revisionTime": "2017-06-16T01:39:05Z"
+		},
+		{
+			"checksumSHA1": "r1oC4B8T20BNrRjyhtVE0Wbavt4=",
+			"path": "github.com/coreos/go-oidc/key",
+			"revision": "d68c0e2fef598f5bbf15edd34905f4bf551a54ec",
+			"revisionTime": "2017-06-16T01:39:05Z"
+		},
+		{
+			"checksumSHA1": "VDZdX1BgnrZ1pV5hZ98xOsN7Bbg=",
+			"path": "github.com/coreos/go-oidc/oauth2",
+			"revision": "d68c0e2fef598f5bbf15edd34905f4bf551a54ec",
+			"revisionTime": "2017-06-16T01:39:05Z"
+		},
+		{
+			"checksumSHA1": "JRmqnLrrYZWzHAeG6iu85hTuqlM=",
+			"path": "github.com/coreos/go-oidc/oidc",
+			"revision": "d68c0e2fef598f5bbf15edd34905f4bf551a54ec",
+			"revisionTime": "2017-06-16T01:39:05Z"
+		},
+		{
+			"checksumSHA1": "vhHfyZLaugP3bKfJjnZe2+5XaHc=",
+			"path": "github.com/coreos/pkg/health",
+			"revision": "8dbaa491b063ed47e2474b5363de0c0db91cf9f2",
+			"revisionTime": "2017-04-20T17:29:56Z"
+		},
+		{
+			"checksumSHA1": "gRHhz6qleTtTUH/UIXwGeNxBFR8=",
+			"path": "github.com/coreos/pkg/httputil",
+			"revision": "8dbaa491b063ed47e2474b5363de0c0db91cf9f2",
+			"revisionTime": "2017-04-20T17:29:56Z"
+		},
+		{
+			"checksumSHA1": "etBdQ0LN6ojGunfvUt6B5C3FNrQ=",
+			"path": "github.com/coreos/pkg/timeutil",
+			"revision": "8dbaa491b063ed47e2474b5363de0c0db91cf9f2",
+			"revisionTime": "2017-04-20T17:29:56Z"
+		},
 		{
 			"checksumSHA1": "dvabztWVQX8f6oMLRyv4dLH+TGY=",
 			"path": "github.com/davecgh/go-spew/spew",
@@ -148,6 +196,12 @@
 			"revision": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75",
 			"revisionTime": "2014-10-17T20:07:13Z"
 		},
+		{
+			"checksumSHA1": "SGc5vSs9tXhrGJ5ncymDyMvTg24=",
+			"path": "github.com/jonboulle/clockwork",
+			"revision": "bcac9884e7502bb2b474c0339d889cb981a2f27f",
+			"revisionTime": "2016-09-07T12:20:59Z"
+		},
 		{
 			"checksumSHA1": "+NJzbj9fa71GPyEhyYcB2urBiXY=",
 			"path": "github.com/juju/ratelimit",
@@ -716,12 +770,28 @@
 			"version": "v3.0",
 			"versionExact": "v3.0.0-beta.0"
 		},
+		{
+			"checksumSHA1": "drjIIoy0trL0gWi/jxi7Nj/Xi/c=",
+			"path": "k8s.io/client-go/plugin/pkg/client/auth",
+			"revision": "3627aeb7d4f6ade38f995d2c923e459146493c7e",
+			"revisionTime": "2017-03-31T20:34:26Z",
+			"version": "v3.0",
+			"versionExact": "v3.0.0-beta.0"
+		},
 		{
 			"checksumSHA1": "2BDblVF8oc5+YR6c625SC0JnnUo=",
 			"path": "k8s.io/client-go/plugin/pkg/client/auth/gcp",
 			"revision": "6daa6a29b8b866679a5be039ad0c8149de1cd60a",
 			"revisionTime": "2017-07-05T23:52:01Z"
 		},
+		{
+			"checksumSHA1": "V2STSOlLArfXkmK9sD9xHHa7lFs=",
+			"path": "k8s.io/client-go/plugin/pkg/client/auth/oidc",
+			"revision": "3627aeb7d4f6ade38f995d2c923e459146493c7e",
+			"revisionTime": "2017-03-31T20:34:26Z",
+			"version": "v3.0",
+			"versionExact": "v3.0.0-beta.0"
+		},
 		{
 			"checksumSHA1": "WKKCrNPVWbJYm1im1DA9145z6xI=",
 			"path": "k8s.io/client-go/rest",
-- 
GitLab