Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkg/auth/oauth/external/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ func NewProvider(providerName, clientID, clientSecret string) external.Provider
return provider{providerName, clientID, clientSecret}
}

func (p provider) GetTransport() (http.RoundTripper, error) {
return nil, nil
}

// NewConfig implements external/interfaces/Provider.NewConfig
func (p provider) NewConfig() (*osincli.ClientConfig, error) {
config := &osincli.ClientConfig{
Expand Down
112 changes: 18 additions & 94 deletions pkg/auth/oauth/external/google/google.go
Original file line number Diff line number Diff line change
@@ -1,113 +1,37 @@
package google

import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"strings"

"github.com/RangelReale/osincli"
"github.com/golang/glog"

authapi "github.com/openshift/origin/pkg/auth/api"
"github.com/openshift/origin/pkg/auth/oauth/external"
"github.com/openshift/origin/pkg/auth/oauth/external/openid"
)

const (
googleAuthorizeURL = "https://accounts.google.com/o/oauth2/auth"
googleTokenURL = "https://accounts.google.com/o/oauth2/token"
googleOAuthScope = "profile email"
googleTokenURL = "https://www.googleapis.com/oauth2/v3/token"
googleUserInfoURL = "https://www.googleapis.com/oauth2/v3/userinfo"
)

var googleOAuthScopes = []string{"openid", "email", "profile"}

type provider struct {
providerName, clientID, clientSecret string
}

func NewProvider(providerName, clientID, clientSecret string) external.Provider {
return provider{providerName, clientID, clientSecret}
}

// NewConfig implements external/interfaces/Provider.NewConfig
func (p provider) NewConfig() (*osincli.ClientConfig, error) {
config := &osincli.ClientConfig{
ClientId: p.clientID,
ClientSecret: p.clientSecret,
ErrorsInStatusCode: true,
SendClientSecretInParams: true,
AuthorizeUrl: googleAuthorizeURL,
TokenUrl: googleTokenURL,
Scope: googleOAuthScope,
}
return config, nil
}

// AddCustomParameters implements external/interfaces/Provider.AddCustomParameters
func (p provider) AddCustomParameters(req *osincli.AuthorizeRequest) {
req.CustomParameters["include_granted_scopes"] = "true"
req.CustomParameters["access_type"] = "offline"
}

// GetUserIdentity implements external/interfaces/Provider.GetUserIdentity
func (p provider) GetUserIdentity(data *osincli.AccessData) (authapi.UserIdentityInfo, bool, error) {
idToken, ok := data.ResponseData["id_token"].(string)
if !ok {
return nil, false, fmt.Errorf("No id_token returned in %v", data.ResponseData)
}

userdata, err := decodeJWT(idToken)
if err != nil {
return nil, false, err
}

subject, _ := userdata["sub"].(string)
if subject == "" {
return nil, false, errors.New("Could not retrieve Google id")
}

email, _ := userdata["email"].(string)
if email == "" {
return nil, false, errors.New("Could not retrieve Google email")
}

identity := authapi.NewDefaultUserIdentityInfo(p.providerName, subject)
identity.Extra[authapi.IdentityLoginKey] = email
identity.Extra[authapi.IdentityEmailKey] = email
glog.V(4).Infof("identity=%v", identity)

return identity, true, nil
}

// Decode JWT
// http://openid.net/specs/draft-jones-json-web-token-07.html
func decodeJWT(jwt string) (map[string]interface{}, error) {
jwtParts := strings.Split(jwt, ".")
if len(jwtParts) != 3 {
return nil, fmt.Errorf("Invalid JSON Web Token: expected 3 parts, got %d", len(jwtParts))
}

encodedPayload := jwtParts[1]
glog.V(4).Infof("got encodedPayload")
func NewProvider(providerName, clientID, clientSecret string) (external.Provider, error) {
config := openid.Config{
ClientID: clientID,
ClientSecret: clientSecret,

// Re-pad, if needed
if l := len(encodedPayload) % 4; l != 0 {
padding := strings.Repeat("=", 4-l)
encodedPayload += padding
glog.V(4).Infof("added padding: %s\n", padding)
}
AuthorizeURL: googleAuthorizeURL,
TokenURL: googleTokenURL,
UserInfoURL: googleUserInfoURL,

decodedPayload, err := base64.StdEncoding.DecodeString(encodedPayload)
if err != nil {
return nil, fmt.Errorf("Error decoding payload: %v\n", err)
}
glog.V(4).Infof("got decodedPayload")
Scopes: googleOAuthScopes,

var data map[string]interface{}
err = json.Unmarshal([]byte(decodedPayload), &data)
if err != nil {
return nil, fmt.Errorf("Error parsing token: %v\n", err)
IDClaims: []string{"sub"},
PreferredUsernameClaims: []string{"email"},
EmailClaims: []string{"email"},
NameClaims: []string{"name", "email"},
}
glog.V(4).Infof("got id_token data")

return data, nil
return openid.NewProvider(providerName, nil, config)
}
6 changes: 5 additions & 1 deletion pkg/auth/oauth/external/google/google_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@ import (
)

func TestGoogle(t *testing.T) {
_ = external.Provider(NewProvider("google", "clientid", "clientsecret"))
p, err := NewProvider("google", "clientid", "clientsecret")
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
_ = external.Provider(p)
}
8 changes: 7 additions & 1 deletion pkg/auth/oauth/external/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ func NewExternalOAuthRedirector(provider Provider, state State, redirectURL stri
return nil, err
}

transport, err := provider.GetTransport()
if err != nil {
return nil, err
}
client.Transport = transport

return &Handler{
provider: provider,
state: state,
Expand Down Expand Up @@ -90,7 +96,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
accessReq := h.client.NewAccessRequest(osincli.AUTHORIZATION_CODE, authData)
accessData, err := accessReq.GetToken()
if err != nil {
glog.V(4).Infof("Error getting access token:", err)
glog.V(4).Infof("Error getting access token: %v", err)
h.handleError(err, w, req)
return
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/auth/oauth/external/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
type Provider interface {
// NewConfig returns a client information that allows a standard oauth client to communicate with external oauth
NewConfig() (*osincli.ClientConfig, error)
// GetTransport returns the transport to use for server-to-server calls. If nil is returned, http.DefaultTransport is used.
GetTransport() (http.RoundTripper, error)
// AddCustomParameters allows an external oauth provider to provide parameters that are extension to the spec. Some providers require this.
AddCustomParameters(*osincli.AuthorizeRequest)
// GetUserIdentity takes the external oauth token information this and returns the user identity, isAuthenticated, and error
Expand Down
Loading