Skip to content
This repository has been archived by the owner on Jan 24, 2019. It is now read-only.

Commit

Permalink
Added OIDC support for UserInfo Endpoint Email Verification
Browse files Browse the repository at this point in the history
* Current OIDC implementation asserts that user email check must come
  from JWT token claims.  OIDC specification also allows for source
  of user email to be fetched from userinfo profile endpoint.
  http://openid.net/specs/openid-connect-core-1_0.html#UserInfo

* Modified current code to search for email in JWT token claims
  first, and then fall back to requesting email from userinfo
  endpoint.

* Modified URL encoding to use '%20' instead of '+' for encoding
  spaces, which is more proper.  Some OpenID servers do not accept
  '+' encoding for the http query arg "scope" (e.g. bad:
  "openid+profile+email" good: "openid%20profilie%20email")
  • Loading branch information
dcwangmit01 committed Oct 20, 2017
1 parent b7f9438 commit e17f3e3
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 3 deletions.
37 changes: 35 additions & 2 deletions providers/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ package providers

import (
"context"
"errors"
"fmt"
"net/http"
"time"

"golang.org/x/oauth2"

oidc "github.com/coreos/go-oidc"

"github.com/bitly/oauth2_proxy/api"
)

type OIDCProvider struct {
Expand Down Expand Up @@ -57,10 +61,10 @@ func (p *OIDCProvider) Redeem(redirectURL, code string) (s *SessionState, err er
}

if claims.Email == "" {
return nil, fmt.Errorf("id_token did not contain an email")
fmt.Errorf("id_token did not contain an email. Falling back on userprofile")
}
if claims.Verified != nil && !*claims.Verified {
return nil, fmt.Errorf("email in id_token (%s) isn't verified", claims.Email)
return nil, fmt.Errorf("id_token failed verification")
}

s = &SessionState{
Expand All @@ -83,3 +87,32 @@ func (p *OIDCProvider) RefreshSessionIfNeeded(s *SessionState) (bool, error) {
fmt.Printf("refreshed access token %s (expired on %s)\n", s, origExpiration)
return false, nil
}

func getOIDCHeader(access_token string) http.Header {
header := make(http.Header)
header.Set("Accept", "application/json")
header.Set("Authorization", fmt.Sprintf("Bearer %s", access_token))
return header
}

func (p *OIDCProvider) GetEmailAddress(s *SessionState) (string, error) {
if s.AccessToken == "" {
return "", errors.New("missing access token")
}
req, err := http.NewRequest("GET", p.ProfileURL.String(), nil)
if err != nil {
return "", err
}
req.Header = getOIDCHeader(s.AccessToken)

json, err := api.Request(req)
if err != nil {
return "", err
}

email, err := json.Get("email").String()
if err != nil {
return "", err
}
return email, nil
}
8 changes: 7 additions & 1 deletion providers/provider_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
"strings"

"github.com/bitly/oauth2_proxy/cookie"
)
Expand Down Expand Up @@ -89,7 +90,12 @@ func (p *ProviderData) GetLoginURL(redirectURI, state string) string {
params.Set("response_type", "code")
params.Add("state", state)
a.RawQuery = params.Encode()
return a.String()

// Default golang libs Encode space( ) as plus (+) instead of '%20'
// which is more proper. Some OpenID servers do not accept this
// encoding. Since there is no way to override the library, replace
// '+' characters with '%20' after the encoding.
return strings.Replace(a.String(), "+", "%20", -1)
}

// CookieForSession serializes a session state for storage in a cookie
Expand Down

0 comments on commit e17f3e3

Please sign in to comment.