From d9a945ebc3a4f5f28d70b7469d212a1c37ad0047 Mon Sep 17 00:00:00 2001 From: Mike Bland Date: Mon, 30 Mar 2015 15:48:30 -0400 Subject: [PATCH] Integrate Provider into Options and OauthProxy --- main.go | 6 ++++++ oauthproxy.go | 38 +++++++------------------------------- options.go | 37 +++++++++++++++++++++++++++++++------ options_test.go | 12 ++++++++++++ 4 files changed, 56 insertions(+), 37 deletions(-) diff --git a/main.go b/main.go index f0d03f4..5250dcd 100644 --- a/main.go +++ b/main.go @@ -50,6 +50,12 @@ func main() { flagSet.Bool("request-logging", true, "Log requests to stdout") + flagSet.String("provider", "", "Oauth provider (defaults to Google)") + flagSet.String("login-url", "", "Authentication endpoint") + flagSet.String("redeem-url", "", "Token redemption endpoint") + flagSet.String("profile-url", "", "Profile access endpoint") + flagSet.String("scope", "", "Oauth scope specification") + flagSet.Parse(os.Args[1:]) if *showVersion { diff --git a/oauthproxy.go b/oauthproxy.go index e5eb541..ca49f74 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -15,8 +15,8 @@ import ( "strings" "time" - "github.com/bitly/go-simplejson" "github.com/bitly/google_auth_proxy/api" + "github.com/bitly/google_auth_proxy/providers" ) const pingPath = "/ping" @@ -34,6 +34,7 @@ type OauthProxy struct { Validator func(string) bool redirectUrl *url.URL // the url to receive requests at + provider providers.Provider oauthRedemptionUrl *url.URL // endpoint to redeem the code oauthLoginUrl *url.URL // to redirect the user to oauthScope string @@ -83,8 +84,6 @@ func setProxyDirector(proxy *httputil.ReverseProxy) { } func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { - login, _ := url.Parse("https://accounts.google.com/o/oauth2/auth") - redeem, _ := url.Parse("https://accounts.google.com/o/oauth2/token") serveMux := http.NewServeMux() for _, u := range opts.proxyUrls { path := u.Path @@ -128,9 +127,10 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { clientID: opts.ClientID, clientSecret: opts.ClientSecret, - oauthScope: "profile email", - oauthRedemptionUrl: redeem, - oauthLoginUrl: login, + oauthScope: opts.provider.Data().Scope, + provider: opts.provider, + oauthRedemptionUrl: opts.provider.Data().RedeemUrl, + oauthLoginUrl: opts.provider.Data().LoginUrl, serveMux: serveMux, redirectUrl: redirectUrl, skipAuthRegex: opts.SkipAuthRegex, @@ -201,23 +201,7 @@ func (p *OauthProxy) redeemCode(host, code string) (string, string, error) { return "", "", err } - idToken, err := json.Get("id_token").String() - if err != nil { - return "", "", err - } - - // id_token is a base64 encode ID token payload - // https://developers.google.com/accounts/docs/OAuth2Login#obtainuserinfo - jwt := strings.Split(idToken, ".") - b, err := jwtDecodeSegment(jwt[1]) - if err != nil { - return "", "", err - } - data, err := simplejson.NewJson(b) - if err != nil { - return "", "", err - } - email, err := data.Get("email").String() + email, err := p.provider.GetEmailAddress(json, access_token) if err != nil { return "", "", err } @@ -225,14 +209,6 @@ func (p *OauthProxy) redeemCode(host, code string) (string, string, error) { return access_token, email, nil } -func jwtDecodeSegment(seg string) ([]byte, error) { - if l := len(seg) % 4; l > 0 { - seg += strings.Repeat("=", 4-l) - } - - return base64.URLEncoding.DecodeString(seg) -} - func (p *OauthProxy) ClearCookie(rw http.ResponseWriter, req *http.Request) { domain := req.Host if h, _, err := net.SplitHostPort(domain); err == nil { diff --git a/options.go b/options.go index eb099ac..85b4c01 100644 --- a/options.go +++ b/options.go @@ -6,6 +6,8 @@ import ( "regexp" "strings" "time" + + "github.com/bitly/google_auth_proxy/providers" ) // Configuration Options that can be set by Command Line Flag, or Config File @@ -33,12 +35,21 @@ type Options struct { PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"` PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header"` + // These options allow for other providers besides Google, with + // potential overrides. + Provider string `flag:"provider" cfg:"provider"` + LoginUrl string `flag:"login-url" cfg:"login_url"` + RedeemUrl string `flag:"redeem-url" cfg:"redeem_url"` + ProfileUrl string `flag:"profile-url" cfg:"profile_url"` + Scope string `flag:"scope" cfg:"scope"` + RequestLogging bool `flag:"request-logging" cfg:"request_logging"` // internal values that are set after config validation redirectUrl *url.URL proxyUrls []*url.URL CompiledRegex []*regexp.Regexp + provider providers.Provider } func NewOptions() *Options { @@ -55,6 +66,15 @@ func NewOptions() *Options { } } +func parseUrl(to_parse string, urltype string, msgs []string) (*url.URL, []string) { + parsed, err := url.Parse(to_parse) + if err != nil { + return nil, append(msgs, fmt.Sprintf( + "error parsing %s-url=%q %s", urltype, to_parse, err)) + } + return parsed, msgs +} + func (o *Options) Validate() error { msgs := make([]string, 0) if len(o.Upstreams) < 1 { @@ -70,12 +90,7 @@ func (o *Options) Validate() error { msgs = append(msgs, "missing setting: client-secret") } - redirectUrl, err := url.Parse(o.RedirectUrl) - if err != nil { - msgs = append(msgs, fmt.Sprintf( - "error parsing redirect-url=%q %s", o.RedirectUrl, err)) - } - o.redirectUrl = redirectUrl + o.redirectUrl, msgs = parseUrl(o.RedirectUrl, "redirect", msgs) for _, u := range o.Upstreams { upstreamUrl, err := url.Parse(u) @@ -98,6 +113,7 @@ func (o *Options) Validate() error { } o.CompiledRegex = append(o.CompiledRegex, CompiledRegex) } + msgs = parseProviderInfo(o, msgs) if len(msgs) != 0 { return fmt.Errorf("Invalid configuration:\n %s", @@ -105,3 +121,12 @@ func (o *Options) Validate() error { } return nil } + +func parseProviderInfo(o *Options, msgs []string) []string { + p := &providers.ProviderData{Scope: o.Scope} + p.LoginUrl, msgs = parseUrl(o.LoginUrl, "login", msgs) + p.RedeemUrl, msgs = parseUrl(o.RedeemUrl, "redeem", msgs) + p.ProfileUrl, msgs = parseUrl(o.ProfileUrl, "profile", msgs) + o.provider = providers.New(o.Provider, p) + return msgs +} diff --git a/options_test.go b/options_test.go index 47c711a..515c1c8 100644 --- a/options_test.go +++ b/options_test.go @@ -90,3 +90,15 @@ func TestCompiledRegexError(t *testing.T) { "unexpected ): `barquux)`"}) assert.Equal(t, expected, err.Error()) } + +func TestDefaultProviderApiSettings(t *testing.T) { + o := testOptions() + assert.Equal(t, nil, o.Validate()) + p := o.provider.Data() + assert.Equal(t, "https://accounts.google.com/o/oauth2/auth", + p.LoginUrl.String()) + assert.Equal(t, "https://accounts.google.com/o/oauth2/token", + p.RedeemUrl.String()) + assert.Equal(t, "", p.ProfileUrl.String()) + assert.Equal(t, "profile email", p.Scope) +}