Merge pull request #696 from ericchiang/switch-go-oidc-client

*: switch oidc client to github.com/coreos/go-oidc
This commit is contained in:
Eric Chiang
2016-11-22 13:42:28 -08:00
committed by GitHub
196 changed files with 12044 additions and 18663 deletions
+4 -4
View File
@@ -17,7 +17,7 @@ import (
"strings"
"time"
"github.com/ericchiang/oidc"
"github.com/coreos/go-oidc"
"github.com/spf13/cobra"
"golang.org/x/net/context"
"golang.org/x/oauth2"
@@ -173,7 +173,7 @@ func cmd() *cobra.Command {
}
a.provider = provider
a.verifier = provider.NewVerifier(a.ctx, oidc.VerifyAudience(a.clientID))
a.verifier = provider.Verifier(oidc.VerifyAudience(a.clientID))
http.HandleFunc("/", a.handleIndex)
http.HandleFunc("/login", a.handleLogin)
@@ -269,7 +269,7 @@ func (a *app) handleCallback(w http.ResponseWriter, r *http.Request) {
RefreshToken: refresh,
Expiry: time.Now().Add(-time.Hour),
}
token, err = oauth2Config.TokenSource(a.ctx, t).Token()
token, err = oauth2Config.TokenSource(r.Context(), t).Token()
default:
http.Error(w, fmt.Sprintf("no code in request: %q", r.Form), http.StatusBadRequest)
return
@@ -286,7 +286,7 @@ func (a *app) handleCallback(w http.ResponseWriter, r *http.Request) {
return
}
idToken, err := a.verifier.Verify(rawIDToken)
idToken, err := a.verifier.Verify(r.Context(), rawIDToken)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to verify ID token: %v", err), http.StatusInternalServerError)
return
+4 -4
View File
@@ -6,7 +6,7 @@ import (
"fmt"
"net/http"
"github.com/ericchiang/oidc"
"github.com/coreos/go-oidc"
"golang.org/x/net/context"
"golang.org/x/oauth2"
@@ -51,7 +51,7 @@ func (c *Config) Open() (conn connector.Connector, err error) {
Scopes: scopes,
RedirectURL: c.RedirectURI,
},
verifier: provider.NewVerifier(ctx,
verifier: provider.Verifier(
oidc.VerifyExpiry(),
oidc.VerifyAudience(clientID),
),
@@ -99,7 +99,7 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
if errType := q.Get("error"); errType != "" {
return identity, &oauth2Error{errType, q.Get("error_description")}
}
token, err := c.oauth2Config.Exchange(c.ctx, q.Get("code"))
token, err := c.oauth2Config.Exchange(r.Context(), q.Get("code"))
if err != nil {
return identity, fmt.Errorf("oidc: failed to get token: %v", err)
}
@@ -108,7 +108,7 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
if !ok {
return identity, errors.New("oidc: no id_token in token response")
}
idToken, err := c.verifier.Verify(rawIDToken)
idToken, err := c.verifier.Verify(r.Context(), rawIDToken)
if err != nil {
return identity, fmt.Errorf("oidc: failed to verify ID Token: %v", err)
}
Generated
+6 -10
View File
@@ -1,12 +1,12 @@
hash: bc7fa6bbfddcb39710064a9d6dab090d49bd88486571cae12c68c4b70093e716
updated: 2016-11-03T14:31:37.323302694-07:00
hash: c3530f2a60a64c2efc4c3ac499fcd15f79de2a532715ba2b9841c1d404942b2e
updated: 2016-11-17T15:18:56.701287533-08:00
imports:
- name: github.com/cockroachdb/cockroach-go
version: 31611c0501c812f437d4861d87d117053967c955
subpackages:
- crdb
- name: github.com/ericchiang/oidc
version: 1907f0e61549f9081f26bdf269f11603496c9dee
- name: github.com/coreos/go-oidc
version: 5a7f09ab5787e846efa7f56f4a08b6d6926d08c4
- name: github.com/ghodss/yaml
version: bea76d6a4713e18b7f5321a2b020738552def3ea
- name: github.com/go-sql-driver/mysql
@@ -52,6 +52,7 @@ imports:
version: 6a513affb38dc9788b449d59ffed099b8de18fa0
subpackages:
- context
- context/ctxhttp
- http2
- http2/hpack
- internal/timeseries
@@ -87,13 +88,8 @@ imports:
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
- name: gopkg.in/ldap.v2
version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73
- name: gopkg.in/square/go-jose.v1
version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc
subpackages:
- cipher
- json
- name: gopkg.in/square/go-jose.v2
version: f209f41628247c56938cb20ef51d589ddad6c30b
version: 8c5257b2f658f86d174ae68c6a592eaf6a9608d9
subpackages:
- cipher
- json
+9 -7
View File
@@ -17,7 +17,7 @@ import:
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
- package: gopkg.in/square/go-jose.v2
version: f209f41628247c56938cb20ef51d589ddad6c30b
version: v2.0.0
subpackages:
- cipher
- json
@@ -26,6 +26,7 @@ import:
version: 6a513affb38dc9788b449d59ffed099b8de18fa0
subpackages:
- context
- context/ctxhttp
- http2
- http2/hpack
- internal/timeseries
@@ -49,16 +50,17 @@ import:
subpackages:
- bcrypt
- package: github.com/ericchiang/oidc
version: 1907f0e61549f9081f26bdf269f11603496c9dee
- package: github.com/coreos/go-oidc
version: 5a7f09ab5787e846efa7f56f4a08b6d6926d08c4
- package: github.com/pquerna/cachecontrol
version: c97913dcbd76de40b051a9b4cd827f7eaeb7a868
- package: gopkg.in/square/go-jose.v1
version: v1.0.2
- package: golang.org/x/oauth2
version: 08c8d727d2392d18286f9f88ad775ad98f09ab33
# Not actually imported but glide detects it. Consider adding subpackages to
# the oauth2 package to eliminate.
subpackages: []
# The oauth2 package only imports the appengine code when it's given a
# specific build tags, but glide detects it anyway.
#
# https://github.com/golang/oauth2/blob/d5040cdd/client_appengine.go
- package: google.golang.org/appengine
version: 267c27e7492265b84fc6719503b14a1e17975d79
subpackages:
+24 -19
View File
@@ -3,6 +3,7 @@ package server
import (
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
@@ -17,7 +18,7 @@ import (
"testing"
"time"
"github.com/ericchiang/oidc"
oidc "github.com/coreos/go-oidc"
"github.com/kylelemons/godebug/pretty"
"golang.org/x/crypto/bcrypt"
"golang.org/x/net/context"
@@ -117,17 +118,21 @@ func TestDiscovery(t *testing.T) {
if err != nil {
t.Fatalf("failed to get provider: %v", err)
}
required := []struct {
name, val string
}{
{"issuer", p.Issuer},
{"authorization_endpoint", p.AuthURL},
{"token_endpoint", p.TokenURL},
{"jwks_uri", p.JWKSURL},
var got map[string]*json.RawMessage
if err := p.Claims(&got); err != nil {
t.Fatalf("failed to decode claims: %v", err)
}
required := []string{
"issuer",
"authorization_endpoint",
"token_endpoint",
"jwks_uri",
}
for _, field := range required {
if field.val == "" {
t.Errorf("server discovery is missing required field %q", field.name)
if _, ok := got[field]; !ok {
t.Errorf("server discovery is missing required field %q", field)
}
}
}
@@ -169,7 +174,7 @@ func TestOAuth2CodeFlow(t *testing.T) {
if !ok {
return fmt.Errorf("no id token found")
}
if _, err := p.NewVerifier(ctx).Verify(idToken); err != nil {
if _, err := p.Verifier().Verify(ctx, idToken); err != nil {
return fmt.Errorf("failed to verify id token: %v", err)
}
return nil
@@ -192,7 +197,7 @@ func TestOAuth2CodeFlow(t *testing.T) {
if !ok {
return fmt.Errorf("no id token found")
}
idToken, err := p.NewVerifier(ctx).Verify(rawIDToken)
idToken, err := p.Verifier().Verify(ctx, rawIDToken)
if err != nil {
return fmt.Errorf("failed to verify id token: %v", err)
}
@@ -230,7 +235,7 @@ func TestOAuth2CodeFlow(t *testing.T) {
v.Add("grant_type", "refresh_token")
v.Add("refresh_token", token.RefreshToken)
v.Add("scope", strings.Join(requestedScopes, " "))
resp, err := http.PostForm(p.TokenURL, v)
resp, err := http.PostForm(p.Endpoint().TokenURL, v)
if err != nil {
return err
}
@@ -258,7 +263,7 @@ func TestOAuth2CodeFlow(t *testing.T) {
// Since we support that client we choose to be more relaxed about
// scope parsing, disregarding extra whitespace.
v.Add("scope", " "+strings.Join(requestedScopes, " "))
resp, err := http.PostForm(p.TokenURL, v)
resp, err := http.PostForm(p.Endpoint().TokenURL, v)
if err != nil {
return err
}
@@ -284,7 +289,7 @@ func TestOAuth2CodeFlow(t *testing.T) {
v.Add("refresh_token", token.RefreshToken)
// Request a scope that wasn't requestd initially.
v.Add("scope", "oidc email profile")
resp, err := http.PostForm(p.TokenURL, v)
resp, err := http.PostForm(p.Endpoint().TokenURL, v)
if err != nil {
return err
}
@@ -335,7 +340,7 @@ func TestOAuth2CodeFlow(t *testing.T) {
if !ok {
return fmt.Errorf("no id_token in refreshed token")
}
idToken, err := p.NewVerifier(ctx).Verify(rawIDToken)
idToken, err := p.Verifier().Verify(ctx, rawIDToken)
if err != nil {
return fmt.Errorf("failed to verify id token: %v", err)
}
@@ -547,7 +552,7 @@ func TestOAuth2ImplicitFlow(t *testing.T) {
src := &nonceSource{nonce: nonce}
idTokenVerifier := p.NewVerifier(ctx, oidc.VerifyAudience(client.ID), oidc.VerifyNonce(src))
idTokenVerifier := p.Verifier(oidc.VerifyAudience(client.ID), oidc.VerifyNonce(src))
oauth2Config = &oauth2.Config{
ClientID: client.ID,
@@ -569,7 +574,7 @@ func TestOAuth2ImplicitFlow(t *testing.T) {
if idToken == "" {
return errors.New("no id_token in fragment")
}
if _, err := idTokenVerifier.Verify(idToken); err != nil {
if _, err := idTokenVerifier.Verify(ctx, idToken); err != nil {
return fmt.Errorf("failed to verify id_token: %v", err)
}
return nil
@@ -664,7 +669,7 @@ func TestCrossClientScopes(t *testing.T) {
t.Errorf("no id token found: %v", err)
return
}
idToken, err := p.NewVerifier(ctx).Verify(rawIDToken)
idToken, err := p.Verifier().Verify(ctx, rawIDToken)
if err != nil {
t.Errorf("failed to parse ID Token: %v", err)
return
+2
View File
@@ -0,0 +1,2 @@
/bin
/gopath
+16
View File
@@ -0,0 +1,16 @@
language: go
go:
- 1.7.3
- 1.6.3
install:
- go get -v -t github.com/coreos/go-oidc
- go get golang.org/x/tools/cmd/cover
- go get github.com/golang/lint/golint
script:
- ./test
notifications:
email: false
+71
View File
@@ -0,0 +1,71 @@
# How to Contribute
CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via
GitHub pull requests. This document outlines some of the conventions on
development workflow, commit message formatting, contact points and other
resources to make it easier to get your contribution accepted.
# Certificate of Origin
By contributing to this project you agree to the Developer Certificate of
Origin (DCO). This document was created by the Linux Kernel community and is a
simple statement that you, as a contributor, have the legal right to make the
contribution. See the [DCO](DCO) file for details.
# Email and Chat
The project currently uses the general CoreOS email list and IRC channel:
- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev)
- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
are very busy and read the mailing lists.
## Getting Started
- Fork the repository on GitHub
- Read the [README](README.md) for build and test instructions
- Play with the project, submit bugs, submit patches!
## Contribution Flow
This is a rough outline of what a contributor's workflow looks like:
- Create a topic branch from where you want to base your work (usually master).
- Make commits of logical units.
- Make sure your commit messages are in the proper format (see below).
- Push your changes to a topic branch in your fork of the repository.
- Make sure the tests pass, and add any new tests as appropriate.
- Submit a pull request to the original repository.
Thanks for your contributions!
### Format of the Commit Message
We follow a rough convention for commit messages that is designed to answer two
questions: what changed and why. The subject line should feature the what and
the body of the commit should describe the why.
```
scripts: add the test-cluster command
this uses tmux to setup a test cluster that you can easily kill and
start for debugging.
Fixes #38
```
The format can be described more formally as follows:
```
<subsystem>: <what changed>
<BLANK LINE>
<why this change was made>
<BLANK LINE>
<footer>
```
The first line is the subject and should be no longer than 70 characters, the
second line is always blank, and other lines should be wrapped at 80 characters.
This allows the message to be easier to read on GitHub as well as in various
git tools.
+36
View File
@@ -0,0 +1,36 @@
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
+3
View File
@@ -0,0 +1,3 @@
Bobby Rullo <bobby.rullo@coreos.com> (@bobbyrullo)
Ed Rooth <ed.rooth@coreos.com> (@sym3tri)
Eric Chiang <eric.chiang@coreos.com> (@ericchiang)
+5
View File
@@ -0,0 +1,5 @@
CoreOS Project
Copyright 2014 CoreOS, Inc
This product includes software developed at CoreOS, Inc.
(http://www.coreos.com/).
+72
View File
@@ -0,0 +1,72 @@
# go-oidc
[![GoDoc](https://godoc.org/github.com/coreos/go-oidc?status.svg)](https://godoc.org/github.com/coreos/go-oidc)
[![Build Status](https://travis-ci.org/coreos/go-oidc.png?branch=master)](https://travis-ci.org/coreos/go-oidc)
## OpenID Connect support for Go
This package enables OpenID Connect support for the [golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2) package.
```go
provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
if err != nil {
// handle error
}
// Configure an OpenID Connect aware OAuth2 client.
oauth2Config := oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURL: redirectURL,
// Discovery returns the OAuth2 endpoints.
Endpoint: provider.Endpoint(),
// "openid" is a required scope for OpenID Connect flows.
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
```
OAuth2 redirects are unchanged.
```go
func handleRedirect(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
}
```
The on responses, the provider can be used to verify ID Tokens.
```go
var verifier = provider.Verifier()
func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
// Verify state and errors.
oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
if err != nil {
// handle error
}
// Extract the ID Token from OAuth2 token.
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok {
// handle missing token
}
// Parse and verify ID Token payload.
idToken, err := verifier.Verify(ctx, rawIDToken)
if err != nil {
// handle error
}
// Extract custom claims
var claims struct {
Email string `json:"email"`
Verified bool `json:"email_verified"`
}
if err := idToken.Claims(&claims); err != nil {
// handle error
}
}
```
+21
View File
@@ -0,0 +1,21 @@
# Examples
These are example uses of the oidc package. Each requires a Google account and the client ID and secret of a registered OAuth2 application. To create one:
1. Visit your [Google Developer Console][google-developer-console].
2. Click "Credentials" on the left column.
3. Click the "Create credentials" button followed by "OAuth client ID".
4. Select "Web application" and add "http://127.0.0.1:5556/auth/google/callback" as an authorized redirect URI.
5. Click create and add the printed client ID and secret to your environment using the following variables:
```
GOOGLE_OAUTH2_CLIENT_ID
GOOGLE_OAUTH2_CLIENT_SECRET
```
Finally run the examples using the Go tool and navigate to http://127.0.0.1:5556.
```
go run ./examples/idtoken/app.go
```
[google-developer-console]: https://console.developers.google.com/apis/dashboard
@@ -9,7 +9,7 @@ import (
"net/http"
"os"
"github.com/ericchiang/oidc"
oidc "github.com/coreos/go-oidc"
"golang.org/x/net/context"
"golang.org/x/oauth2"
@@ -27,7 +27,7 @@ func main() {
if err != nil {
log.Fatal(err)
}
verifier := provider.NewVerifier(ctx)
verifier := provider.Verifier()
config := oauth2.Config{
ClientID: clientID,
@@ -59,7 +59,7 @@ func main() {
http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError)
return
}
idToken, err := verifier.Verify(rawIDToken)
idToken, err := verifier.Verify(ctx, rawIDToken)
if err != nil {
http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
return
@@ -10,7 +10,7 @@ import (
"net/http"
"os"
"github.com/ericchiang/oidc"
oidc "github.com/coreos/go-oidc"
"golang.org/x/net/context"
"golang.org/x/oauth2"
@@ -42,7 +42,7 @@ func main() {
}
// Use the nonce source to create a custom ID Token verifier.
nonceEnabledVerifier := provider.NewVerifier(ctx, oidc.VerifyNonce(nonceSource{}))
nonceEnabledVerifier := provider.Verifier(oidc.VerifyNonce(nonceSource{}))
config := oauth2.Config{
ClientID: clientID,
@@ -76,7 +76,7 @@ func main() {
return
}
// Verify the ID Token signature and nonce.
idToken, err := nonceEnabledVerifier.Verify(rawIDToken)
idToken, err := nonceEnabledVerifier.Verify(ctx, rawIDToken)
if err != nil {
http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
return
@@ -9,7 +9,7 @@ import (
"net/http"
"os"
"github.com/ericchiang/oidc"
oidc "github.com/coreos/go-oidc"
"golang.org/x/net/context"
"golang.org/x/oauth2"
+150
View File
@@ -0,0 +1,150 @@
// +build ignore
// This file is used to generate keys for tests.
package main
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"text/template"
jose "gopkg.in/square/go-jose.v2"
)
type key struct {
name string
new func() (crypto.Signer, error)
}
var keys = []key{
{
"ECDSA_256", func() (crypto.Signer, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
},
},
{
"ECDSA_384", func() (crypto.Signer, error) {
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
},
},
{
"ECDSA_521", func() (crypto.Signer, error) {
return ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
},
},
{
"RSA_1024", func() (crypto.Signer, error) {
return rsa.GenerateKey(rand.Reader, 1024)
},
},
{
"RSA_2048", func() (crypto.Signer, error) {
return rsa.GenerateKey(rand.Reader, 2048)
},
},
{
"RSA_4096", func() (crypto.Signer, error) {
return rsa.GenerateKey(rand.Reader, 4096)
},
},
}
func newJWK(k key, prefix, ident string) (privBytes, pubBytes []byte, err error) {
priv, err := k.new()
if err != nil {
return nil, nil, fmt.Errorf("generate %s: %v", k.name, err)
}
pub := priv.Public()
privKey := &jose.JSONWebKey{Key: priv}
thumbprint, err := privKey.Thumbprint(crypto.SHA256)
if err != nil {
return nil, nil, fmt.Errorf("computing thumbprint: %v", err)
}
keyID := hex.EncodeToString(thumbprint)
privKey.KeyID = keyID
pubKey := &jose.JSONWebKey{Key: pub, KeyID: keyID}
privBytes, err = json.MarshalIndent(privKey, prefix, ident)
if err != nil {
return
}
pubBytes, err = json.MarshalIndent(pubKey, prefix, ident)
return
}
type keyData struct {
Name string
Priv string
Pub string
}
var tmpl = template.Must(template.New("").Parse(`// +build !golint
// This file contains statically created JWKs for tests created by gen.go
package oidc
import (
"encoding/json"
jose "gopkg.in/square/go-jose.v2"
)
func mustLoadJWK(s string) jose.JSONWebKey {
var jwk jose.JSONWebKey
if err := json.Unmarshal([]byte(s), &jwk); err != nil {
panic(err)
}
return jwk
}
var (
{{- range $i, $key := .Keys }}
testKey{{ $key.Name }} = mustLoadJWK(` + "`" + `{{ $key.Pub }}` + "`" + `)
testKey{{ $key.Name }}_Priv = mustLoadJWK(` + "`" + `{{ $key.Priv }}` + "`" + `)
{{ end -}}
)
`))
func main() {
var tmplData struct {
Keys []keyData
}
for _, k := range keys {
for i := 0; i < 4; i++ {
log.Printf("generating %s", k.name)
priv, pub, err := newJWK(k, "\t", "\t")
if err != nil {
log.Fatal(err)
}
name := fmt.Sprintf("%s_%d", k.name, i)
tmplData.Keys = append(tmplData.Keys, keyData{
Name: name,
Priv: string(priv),
Pub: string(pub),
})
}
}
buff := new(bytes.Buffer)
if err := tmpl.Execute(buff, tmplData); err != nil {
log.Fatalf("excuting template: %v", err)
}
if err := ioutil.WriteFile("jose_test.go", buff.Bytes(), 0644); err != nil {
log.Fatal(err)
}
}
+7
View File
@@ -0,0 +1,7 @@
package http
import "net/http"
type Client interface {
Do(*http.Request) (*http.Response, error)
}
+2
View File
@@ -0,0 +1,2 @@
// Package http is DEPRECATED. Use net/http instead.
package http

Some files were not shown because too many files have changed in this diff Show More