git.iko.soy/typeable/oauth2-proxy
on master help sign in
Move provider URLs to package level vars
commitd4dd34a65a3392e63fb74429a537072fb124db51 authorJoel Speed <joel.speed@hotmail.co.uk> dateMon 25 May 2020, 13:08 (6 years ago) parent9643a0b
23 files changed+541−315
CHANGELOG.md+1 providers/azure.go+64−29 providers/azure_test.go+13−19 providers/bitbucket.go+40−24 providers/bitbucket_test.go+13−13 providers/digitalocean.go+38−24 providers/digitalocean_test.go+12−13 providers/facebook.go+39−25 providers/facebook_test.go+20 providers/github.go+40−25 providers/github_test.go+12−11 providers/gitlab.go+7−2 providers/google.go+39−21 providers/google_test.go+12−13 providers/internal_util_test.go+3 providers/keycloak.go+37−23 providers/keycloak_test.go+15−2 providers/linkedin.go+38−21 providers/linkedin_test.go+12−13 providers/logingov.go+36−24 providers/logingov_test.go+12−12 providers/nextcloud.go+3−1 providers/provider_data.go+35
CHANGELOG.md+1
@@ -11,6 +11,7 @@
1111
1212## Changes since v6.0.0
1313
14+- [#561](https://github.com/oauth2-proxy/oauth2-proxy/pull/561) Refactor provider URLs to package level vars (@JoelSpeed)
1415- [#682](https://github.com/oauth2-proxy/oauth2-proxy/pull/682) Refactor persistent session store session ticket management (@NickMeves)
1516- [#688](https://github.com/oauth2-proxy/oauth2-proxy/pull/688) Refactor session loading to make use of middleware pattern (@JoelSpeed)
1617- [#593](https://github.com/oauth2-proxy/oauth2-proxy/pull/593) Integrate upstream package with OAuth2 Proxy (@JoelSpeed)
providers/azure.go+64−29
@@ -23,49 +23,84 @@ type AzureProvider struct {
2323
2424var _ Provider = (*AzureProvider)(nil)
2525
26+const (
27+ azureProviderName = "Azure"
28+ azureDefaultScope = "openid"
29+)
30+
31+var (
32+ // Default Login URL for Azure.
33+ // Pre-parsed URL of https://login.microsoftonline.com/common/oauth2/authorize.
34+ azureDefaultLoginURL = &url.URL{
35+ Scheme: "https",
36+ Host: "login.microsoftonline.com",
37+ Path: "/common/oauth2/authorize",
38+ }
39+
40+ // Default Redeem URL for Azure.
41+ // Pre-parsed URL of https://login.microsoftonline.com/common/oauth2/token.
42+ azureDefaultRedeemURL = &url.URL{
43+ Scheme: "https",
44+ Host: "login.microsoftonline.com",
45+ Path: "/common/oauth2/token",
46+ }
47+
48+ // Default Profile URL for Azure.
49+ // Pre-parsed URL of https://graph.microsoft.com/v1.0/me.
50+ azureDefaultProfileURL = &url.URL{
51+ Scheme: "https",
52+ Host: "graph.microsoft.com",
53+ Path: "/v1.0/me",
54+ }
55+
56+ // Default ProtectedResource URL for Azure.
57+ // Pre-parsed URL of https://graph.microsoft.com.
58+ azureDefaultProtectResourceURL = &url.URL{
59+ Scheme: "https",
60+ Host: "graph.microsoft.com",
61+ }
62+)
63+
2664// NewAzureProvider initiates a new AzureProvider
2765func NewAzureProvider(p *ProviderData) *AzureProvider {
28− p.ProviderName = "Azure"
66+ p.setProviderDefaults(providerDefaults{
67+ name: azureProviderName,
68+ loginURL: azureDefaultLoginURL,
69+ redeemURL: azureDefaultRedeemURL,
70+ profileURL: azureDefaultProfileURL,
71+ validateURL: nil,
72+ scope: azureDefaultScope,
73+ })
2974
30− if p.ProfileURL == nil || p.ProfileURL.String() == "" {
31− p.ProfileURL = &url.URL{
32− Scheme: "https",
33− Host: "graph.microsoft.com",
34− Path: "/v1.0/me",
35− }
36− }
3775 if p.ProtectedResource == nil || p.ProtectedResource.String() == "" {
38− p.ProtectedResource = &url.URL{
39− Scheme: "https",
40− Host: "graph.microsoft.com",
41− }
42− }
43− if p.Scope == "" {
44− p.Scope = "openid"
76+ p.ProtectedResource = azureDefaultProtectResourceURL
4577 }
4678
47− return &AzureProvider{ProviderData: p}
79+ return &AzureProvider{
80+ ProviderData: p,
81+ Tenant: "common",
82+ }
4883}
4984
5085// Configure defaults the AzureProvider configuration options
5186func (p *AzureProvider) Configure(tenant string) {
52− p.Tenant = tenant
53− if tenant == "" {
54− p.Tenant = "common"
87+ if tenant == "" || tenant == "common" {
88+ // tenant is empty or default, remain on the default "common" tenant
89+ return
5590 }
5691
57− if p.LoginURL == nil || p.LoginURL.String() == "" {
58− p.LoginURL = &url.URL{
59− Scheme: "https",
60− Host: "login.microsoftonline.com",
61− Path: "/" + p.Tenant + "/oauth2/authorize"}
62− }
63− if p.RedeemURL == nil || p.RedeemURL.String() == "" {
64− p.RedeemURL = &url.URL{
92+ // Specific tennant specified, override the Login and RedeemURLs
93+ p.Tenant = tenant
94+ overrideTenantURL(p.LoginURL, azureDefaultLoginURL, tenant, "authorize")
95+ overrideTenantURL(p.RedeemURL, azureDefaultRedeemURL, tenant, "token")
96+}
97+
98+func overrideTenantURL(current, defaultURL *url.URL, tenant, path string) {
99+ if current == nil || current.String() == "" || current.String() == defaultURL.String() {
100+ *current = url.URL{
65101 Scheme: "https",
66102 Host: "login.microsoftonline.com",
67− Path: "/" + p.Tenant + "/oauth2/token",
68− }
103+ Path: "/" + tenant + "/oauth2/" + path}
69104 }
70105}
71106
providers/azure_test.go+13−19
@@ -8,6 +8,7 @@ import (
88 "testing"
99 "time"
1010
11+ . "github.com/onsi/gomega"
1112 "github.com/stretchr/testify/assert"
1213)
1314
@@ -32,23 +33,17 @@ func testAzureProvider(hostname string) *AzureProvider {
3233 return p
3334}
3435
35−func TestAzureProviderDefaults(t *testing.T) {
36− p := testAzureProvider("")
37− assert.NotEqual(t, nil, p)
38− p.Configure("")
39− assert.Equal(t, "Azure", p.Data().ProviderName)
40− assert.Equal(t, "common", p.Tenant)
41− assert.Equal(t, "https://login.microsoftonline.com/common/oauth2/authorize",
42− p.Data().LoginURL.String())
43− assert.Equal(t, "https://login.microsoftonline.com/common/oauth2/token",
44− p.Data().RedeemURL.String())
45− assert.Equal(t, "https://graph.microsoft.com/v1.0/me",
46− p.Data().ProfileURL.String())
47− assert.Equal(t, "https://graph.microsoft.com",
48− p.Data().ProtectedResource.String())
49− assert.Equal(t, "",
50− p.Data().ValidateURL.String())
51− assert.Equal(t, "openid", p.Data().Scope)
36+func TestNewAzureProvider(t *testing.T) {
37+ g := NewWithT(t)
38+
39+ // Test that defaults are set when calling for a new provider with nothing set
40+ providerData := NewAzureProvider(&ProviderData{}).Data()
41+ g.Expect(providerData.ProviderName).To(Equal("Azure"))
42+ g.Expect(providerData.LoginURL.String()).To(Equal("https://login.microsoftonline.com/common/oauth2/authorize"))
43+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://login.microsoftonline.com/common/oauth2/token"))
44+ g.Expect(providerData.ProfileURL.String()).To(Equal("https://graph.microsoft.com/v1.0/me"))
45+ g.Expect(providerData.ValidateURL.String()).To(Equal(""))
46+ g.Expect(providerData.Scope).To(Equal("openid"))
5247}
5348
5449func TestAzureProviderOverrides(t *testing.T) {
@@ -102,8 +97,7 @@ func TestAzureSetTenant(t *testing.T) {
10297 p.Data().ProfileURL.String())
10398 assert.Equal(t, "https://graph.microsoft.com",
10499 p.Data().ProtectedResource.String())
105− assert.Equal(t, "",
106− p.Data().ValidateURL.String())
100+ assert.Equal(t, "", p.Data().ValidateURL.String())
107101 assert.Equal(t, "openid", p.Data().Scope)
108102}
109103
providers/bitbucket.go+40−24
@@ -19,33 +19,49 @@ type BitbucketProvider struct {
1919
2020var _ Provider = (*BitbucketProvider)(nil)
2121
22−// NewBitbucketProvider initiates a new BitbucketProvider
23−func NewBitbucketProvider(p *ProviderData) *BitbucketProvider {
24− p.ProviderName = "Bitbucket"
25− if p.LoginURL == nil || p.LoginURL.String() == "" {
26− p.LoginURL = &url.URL{
27− Scheme: "https",
28− Host: "bitbucket.org",
29− Path: "/site/oauth2/authorize",
30− }
31− }
32− if p.RedeemURL == nil || p.RedeemURL.String() == "" {
33− p.RedeemURL = &url.URL{
34− Scheme: "https",
35− Host: "bitbucket.org",
36− Path: "/site/oauth2/access_token",
37− }
22+const (
23+ bitbucketProviderName = "Bitbucket"
24+ bitbucketDefaultScope = "email"
25+)
26+
27+var (
28+ // Default Login URL for Bitbucket.
29+ // Pre-parsed URL of https://bitbucket.org/site/oauth2/authorize.
30+ bitbucketDefaultLoginURL = &url.URL{
31+ Scheme: "https",
32+ Host: "bitbucket.org",
33+ Path: "/site/oauth2/authorize",
3834 }
39− if p.ValidateURL == nil || p.ValidateURL.String() == "" {
40− p.ValidateURL = &url.URL{
41− Scheme: "https",
42− Host: "api.bitbucket.org",
43− Path: "/2.0/user/emails",
44− }
35+
36+ // Default Redeem URL for Bitbucket.
37+ // Pre-parsed URL of https://bitbucket.org/site/oauth2/access_token.
38+ bitbucketDefaultRedeemURL = &url.URL{
39+ Scheme: "https",
40+ Host: "bitbucket.org",
41+ Path: "/site/oauth2/access_token",
4542 }
46− if p.Scope == "" {
47− p.Scope = "email"
43+
44+ // Default Validation URL for Bitbucket.
45+ // This simply returns the email of the authenticated user.
46+ // Bitbucket does not have a Profile URL to use.
47+ // Pre-parsed URL of https://api.bitbucket.org/2.0/user/emails.
48+ bitbucketDefaultValidateURL = &url.URL{
49+ Scheme: "https",
50+ Host: "api.bitbucket.org",
51+ Path: "/2.0/user/emails",
4852 }
53+)
54+
55+// NewBitbucketProvider initiates a new BitbucketProvider
56+func NewBitbucketProvider(p *ProviderData) *BitbucketProvider {
57+ p.setProviderDefaults(providerDefaults{
58+ name: bitbucketProviderName,
59+ loginURL: bitbucketDefaultLoginURL,
60+ redeemURL: bitbucketDefaultRedeemURL,
61+ profileURL: nil,
62+ validateURL: bitbucketDefaultValidateURL,
63+ scope: bitbucketDefaultScope,
64+ })
4965 return &BitbucketProvider{ProviderData: p}
5066}
5167
providers/bitbucket_test.go+13−13
@@ -8,9 +8,9 @@ import (
88 "net/url"
99 "testing"
1010
11− "github.com/stretchr/testify/assert"
12−
1311 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
12+ . "github.com/onsi/gomega"
13+ "github.com/stretchr/testify/assert"
1414)
1515
1616func testBitbucketProvider(hostname, team string, repository string) *BitbucketProvider {
@@ -61,17 +61,17 @@ func testBitbucketBackend(payload string) *httptest.Server {
6161 }))
6262}
6363
64−func TestBitbucketProviderDefaults(t *testing.T) {
65− p := testBitbucketProvider("", "", "")
66− assert.NotEqual(t, nil, p)
67− assert.Equal(t, "Bitbucket", p.Data().ProviderName)
68− assert.Equal(t, "https://bitbucket.org/site/oauth2/authorize",
69− p.Data().LoginURL.String())
70− assert.Equal(t, "https://bitbucket.org/site/oauth2/access_token",
71− p.Data().RedeemURL.String())
72− assert.Equal(t, "https://api.bitbucket.org/2.0/user/emails",
73− p.Data().ValidateURL.String())
74− assert.Equal(t, "email", p.Data().Scope)
64+func TestNewBitbucketProvider(t *testing.T) {
65+ g := NewWithT(t)
66+
67+ // Test that defaults are set when calling for a new provider with nothing set
68+ providerData := NewBitbucketProvider(&ProviderData{}).Data()
69+ g.Expect(providerData.ProviderName).To(Equal("Bitbucket"))
70+ g.Expect(providerData.LoginURL.String()).To(Equal("https://bitbucket.org/site/oauth2/authorize"))
71+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://bitbucket.org/site/oauth2/access_token"))
72+ g.Expect(providerData.ProfileURL.String()).To(Equal(""))
73+ g.Expect(providerData.ValidateURL.String()).To(Equal("https://api.bitbucket.org/2.0/user/emails"))
74+ g.Expect(providerData.Scope).To(Equal("email"))
7575}
7676
7777func TestBitbucketProviderScopeAdjustForTeam(t *testing.T) {
providers/digitalocean.go+38−24
@@ -18,33 +18,47 @@ type DigitalOceanProvider struct {
1818
1919var _ Provider = (*DigitalOceanProvider)(nil)
2020
21−// NewDigitalOceanProvider initiates a new DigitalOceanProvider
22−func NewDigitalOceanProvider(p *ProviderData) *DigitalOceanProvider {
23− p.ProviderName = "DigitalOcean"
24− if p.LoginURL.String() == "" {
25− p.LoginURL = &url.URL{Scheme: "https",
26− Host: "cloud.digitalocean.com",
27− Path: "/v1/oauth/authorize",
28− }
29− }
30− if p.RedeemURL.String() == "" {
31− p.RedeemURL = &url.URL{Scheme: "https",
32− Host: "cloud.digitalocean.com",
33− Path: "/v1/oauth/token",
34− }
35− }
36− if p.ProfileURL.String() == "" {
37− p.ProfileURL = &url.URL{Scheme: "https",
38− Host: "api.digitalocean.com",
39− Path: "/v2/account",
40− }
21+const (
22+ digitalOceanProviderName = "DigitalOcean"
23+ digitalOceanDefaultScope = "read"
24+)
25+
26+var (
27+ // Default Login URL for DigitalOcean.
28+ // Pre-parsed URL of https://cloud.digitalocean.com/v1/oauth/authorize.
29+ digitalOceanDefaultLoginURL = &url.URL{
30+ Scheme: "https",
31+ Host: "cloud.digitalocean.com",
32+ Path: "/v1/oauth/authorize",
4133 }
42− if p.ValidateURL.String() == "" {
43− p.ValidateURL = p.ProfileURL
34+
35+ // Default Redeem URL for DigitalOcean.
36+ // Pre-parsed URL of https://cloud.digitalocean.com/v1/oauth/token.
37+ digitalOceanDefaultRedeemURL = &url.URL{
38+ Scheme: "https",
39+ Host: "cloud.digitalocean.com",
40+ Path: "/v1/oauth/token",
4441 }
45− if p.Scope == "" {
46− p.Scope = "read"
42+
43+ // Default Profile URL for DigitalOcean.
44+ // Pre-parsed URL of https://cloud.digitalocean.com/v2/account.
45+ digitalOceanDefaultProfileURL = &url.URL{
46+ Scheme: "https",
47+ Host: "api.digitalocean.com",
48+ Path: "/v2/account",
4749 }
50+)
51+
52+// NewDigitalOceanProvider initiates a new DigitalOceanProvider
53+func NewDigitalOceanProvider(p *ProviderData) *DigitalOceanProvider {
54+ p.setProviderDefaults(providerDefaults{
55+ name: digitalOceanProviderName,
56+ loginURL: digitalOceanDefaultLoginURL,
57+ redeemURL: digitalOceanDefaultRedeemURL,
58+ profileURL: digitalOceanDefaultProfileURL,
59+ validateURL: digitalOceanDefaultProfileURL,
60+ scope: digitalOceanDefaultScope,
61+ })
4862 return &DigitalOceanProvider{ProviderData: p}
4963}
5064
providers/digitalocean_test.go+12−13
@@ -8,6 +8,7 @@ import (
88 "testing"
99
1010 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
11+ . "github.com/onsi/gomega"
1112 "github.com/stretchr/testify/assert"
1213)
1314
@@ -44,19 +45,17 @@ func testDigitalOceanBackend(payload string) *httptest.Server {
4445 }))
4546}
4647
47−func TestDigitalOceanProviderDefaults(t *testing.T) {
48− p := testDigitalOceanProvider("")
49− assert.NotEqual(t, nil, p)
50− assert.Equal(t, "DigitalOcean", p.Data().ProviderName)
51− assert.Equal(t, "https://cloud.digitalocean.com/v1/oauth/authorize",
52− p.Data().LoginURL.String())
53− assert.Equal(t, "https://cloud.digitalocean.com/v1/oauth/token",
54− p.Data().RedeemURL.String())
55− assert.Equal(t, "https://api.digitalocean.com/v2/account",
56− p.Data().ProfileURL.String())
57− assert.Equal(t, "https://api.digitalocean.com/v2/account",
58− p.Data().ValidateURL.String())
59− assert.Equal(t, "read", p.Data().Scope)
48+func TestNewDigitalOceanProvider(t *testing.T) {
49+ g := NewWithT(t)
50+
51+ // Test that defaults are set when calling for a new provider with nothing set
52+ providerData := NewDigitalOceanProvider(&ProviderData{}).Data()
53+ g.Expect(providerData.ProviderName).To(Equal("DigitalOcean"))
54+ g.Expect(providerData.LoginURL.String()).To(Equal("https://cloud.digitalocean.com/v1/oauth/authorize"))
55+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://cloud.digitalocean.com/v1/oauth/token"))
56+ g.Expect(providerData.ProfileURL.String()).To(Equal("https://api.digitalocean.com/v2/account"))
57+ g.Expect(providerData.ValidateURL.String()).To(Equal("https://api.digitalocean.com/v2/account"))
58+ g.Expect(providerData.Scope).To(Equal("read"))
6059}
6160
6261func TestDigitalOceanProviderOverrides(t *testing.T) {
providers/facebook.go+39−25
@@ -18,34 +18,48 @@ type FacebookProvider struct {
1818
1919var _ Provider = (*FacebookProvider)(nil)
2020
21−// NewFacebookProvider initiates a new FacebookProvider
22−func NewFacebookProvider(p *ProviderData) *FacebookProvider {
23− p.ProviderName = "Facebook"
24− if p.LoginURL.String() == "" {
25− p.LoginURL = &url.URL{Scheme: "https",
26− Host: "www.facebook.com",
27− Path: "/v2.5/dialog/oauth",
28− // ?granted_scopes=true
29− }
30− }
31− if p.RedeemURL.String() == "" {
32− p.RedeemURL = &url.URL{Scheme: "https",
33− Host: "graph.facebook.com",
34− Path: "/v2.5/oauth/access_token",
35− }
36− }
37− if p.ProfileURL.String() == "" {
38− p.ProfileURL = &url.URL{Scheme: "https",
39− Host: "graph.facebook.com",
40− Path: "/v2.5/me",
41− }
21+const (
22+ facebookProviderName = "Facebook"
23+ facebookDefaultScope = "public_profile email"
24+)
25+
26+var (
27+ // Default Login URL for Facebook.
28+ // Pre-parsed URL of https://www.facebook.com/v2.5/dialog/oauth.
29+ facebookDefaultLoginURL = &url.URL{
30+ Scheme: "https",
31+ Host: "www.facebook.com",
32+ Path: "/v2.5/dialog/oauth",
33+ // ?granted_scopes=true
4234 }
43− if p.ValidateURL.String() == "" {
44− p.ValidateURL = p.ProfileURL
35+
36+ // Default Redeem URL for Facebook.
37+ // Pre-parsed URL of https://graph.facebook.com/v2.5/oauth/access_token.
38+ facebookDefaultRedeemURL = &url.URL{
39+ Scheme: "https",
40+ Host: "graph.facebook.com",
41+ Path: "/v2.5/oauth/access_token",
4542 }
46− if p.Scope == "" {
47− p.Scope = "public_profile email"
43+
44+ // Default Profile URL for Facebook.
45+ // Pre-parsed URL of https://graph.facebook.com/v2.5/me.
46+ facebookDefaultProfileURL = &url.URL{
47+ Scheme: "https",
48+ Host: "graph.facebook.com",
49+ Path: "/v2.5/me",
4850 }
51+)
52+
53+// NewFacebookProvider initiates a new FacebookProvider
54+func NewFacebookProvider(p *ProviderData) *FacebookProvider {
55+ p.setProviderDefaults(providerDefaults{
56+ name: facebookProviderName,
57+ loginURL: facebookDefaultLoginURL,
58+ redeemURL: facebookDefaultRedeemURL,
59+ profileURL: facebookDefaultProfileURL,
60+ validateURL: facebookDefaultProfileURL,
61+ scope: facebookDefaultScope,
62+ })
4963 return &FacebookProvider{ProviderData: p}
5064}
5165
providers/facebook_test.gonew file+20
@@ -0,0 +1,20 @@
1+package providers
2+
3+import (
4+ "testing"
5+
6+ . "github.com/onsi/gomega"
7+)
8+
9+func TestNewFacebookProvider(t *testing.T) {
10+ g := NewWithT(t)
11+
12+ // Test that defaults are set when calling for a new provider with nothing set
13+ providerData := NewFacebookProvider(&ProviderData{}).Data()
14+ g.Expect(providerData.ProviderName).To(Equal("Facebook"))
15+ g.Expect(providerData.LoginURL.String()).To(Equal("https://www.facebook.com/v2.5/dialog/oauth"))
16+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://graph.facebook.com/v2.5/oauth/access_token"))
17+ g.Expect(providerData.ProfileURL.String()).To(Equal("https://graph.facebook.com/v2.5/me"))
18+ g.Expect(providerData.ValidateURL.String()).To(Equal("https://graph.facebook.com/v2.5/me"))
19+ g.Expect(providerData.Scope).To(Equal("public_profile email"))
20+}
providers/github.go+40−25
@@ -28,34 +28,49 @@ type GitHubProvider struct {
2828
2929var _ Provider = (*GitHubProvider)(nil)
3030
31−// NewGitHubProvider initiates a new GitHubProvider
32−func NewGitHubProvider(p *ProviderData) *GitHubProvider {
33− p.ProviderName = "GitHub"
34− if p.LoginURL == nil || p.LoginURL.String() == "" {
35− p.LoginURL = &url.URL{
36− Scheme: "https",
37− Host: "github.com",
38− Path: "/login/oauth/authorize",
39− }
40− }
41− if p.RedeemURL == nil || p.RedeemURL.String() == "" {
42− p.RedeemURL = &url.URL{
43− Scheme: "https",
44− Host: "github.com",
45− Path: "/login/oauth/access_token",
46− }
31+const (
32+ githubProviderName = "GitHub"
33+ githubDefaultScope = "user:email"
34+)
35+
36+var (
37+ // Default Login URL for GitHub.
38+ // Pre-parsed URL of https://github.org/login/oauth/authorize.
39+ githubDefaultLoginURL = &url.URL{
40+ Scheme: "https",
41+ Host: "github.com",
42+ Path: "/login/oauth/authorize",
4743 }
48− // ValidationURL is the API Base URL
49− if p.ValidateURL == nil || p.ValidateURL.String() == "" {
50− p.ValidateURL = &url.URL{
51− Scheme: "https",
52− Host: "api.github.com",
53− Path: "/",
54− }
44+
45+ // Default Redeem URL for GitHub.
46+ // Pre-parsed URL of https://github.org/login/oauth/access_token.
47+ githubDefaultRedeemURL = &url.URL{
48+ Scheme: "https",
49+ Host: "github.com",
50+ Path: "/login/oauth/access_token",
5551 }
56− if p.Scope == "" {
57− p.Scope = "user:email"
52+
53+ // Default Validation URL for GitHub.
54+ // ValidationURL is the API Base URL.
55+ // Other API requests are based off of this (eg to fetch users/groups).
56+ // Pre-parsed URL of https://api.github.com/.
57+ githubDefaultValidateURL = &url.URL{
58+ Scheme: "https",
59+ Host: "api.github.com",
60+ Path: "/",
5861 }
62+)
63+
64+// NewGitHubProvider initiates a new GitHubProvider
65+func NewGitHubProvider(p *ProviderData) *GitHubProvider {
66+ p.setProviderDefaults(providerDefaults{
67+ name: githubProviderName,
68+ loginURL: githubDefaultLoginURL,
69+ redeemURL: githubDefaultRedeemURL,
70+ profileURL: nil,
71+ validateURL: githubDefaultValidateURL,
72+ scope: githubDefaultScope,
73+ })
5974 return &GitHubProvider{ProviderData: p}
6075}
6176
providers/github_test.go+12−11
@@ -8,6 +8,7 @@ import (
88 "testing"
99
1010 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
11+ . "github.com/onsi/gomega"
1112 "github.com/stretchr/testify/assert"
1213)
1314
@@ -66,17 +67,17 @@ func testGitHubBackend(payloads map[string][]string) *httptest.Server {
6667 }))
6768}
6869
69−func TestGitHubProviderDefaults(t *testing.T) {
70− p := testGitHubProvider("")
71− assert.NotEqual(t, nil, p)
72− assert.Equal(t, "GitHub", p.Data().ProviderName)
73− assert.Equal(t, "https://github.com/login/oauth/authorize",
74− p.Data().LoginURL.String())
75− assert.Equal(t, "https://github.com/login/oauth/access_token",
76− p.Data().RedeemURL.String())
77− assert.Equal(t, "https://api.github.com/",
78− p.Data().ValidateURL.String())
79− assert.Equal(t, "user:email", p.Data().Scope)
70+func TestNewGitHubProvider(t *testing.T) {
71+ g := NewWithT(t)
72+
73+ // Test that defaults are set when calling for a new provider with nothing set
74+ providerData := NewGitHubProvider(&ProviderData{}).Data()
75+ g.Expect(providerData.ProviderName).To(Equal("GitHub"))
76+ g.Expect(providerData.LoginURL.String()).To(Equal("https://github.com/login/oauth/authorize"))
77+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://github.com/login/oauth/access_token"))
78+ g.Expect(providerData.ProfileURL.String()).To(Equal(""))
79+ g.Expect(providerData.ValidateURL.String()).To(Equal("https://api.github.com/"))
80+ g.Expect(providerData.Scope).To(Equal("user:email"))
8081}
8182
8283func TestGitHubProviderOverrides(t *testing.T) {
providers/gitlab.go+7−2
@@ -25,12 +25,17 @@ type GitLabProvider struct {
2525
2626var _ Provider = (*GitLabProvider)(nil)
2727
28+const (
29+ gitlabProviderName = "GitLab"
30+ gitlabDefaultScope = "openid email"
31+)
32+
2833// NewGitLabProvider initiates a new GitLabProvider
2934func NewGitLabProvider(p *ProviderData) *GitLabProvider {
30− p.ProviderName = "GitLab"
35+ p.ProviderName = gitlabProviderName
3136
3237 if p.Scope == "" {
33− p.Scope = "openid email"
38+ p.Scope = gitlabDefaultScope
3439 }
3540
3641 return &GitLabProvider{ProviderData: p}
providers/google.go+39−21
@@ -39,31 +39,49 @@ type claims struct {
3939 EmailVerified bool `json:"email_verified"`
4040}
4141
42−// NewGoogleProvider initiates a new GoogleProvider
43−func NewGoogleProvider(p *ProviderData) *GoogleProvider {
44− p.ProviderName = "Google"
45− if p.LoginURL.String() == "" {
46− p.LoginURL = &url.URL{Scheme: "https",
47− Host: "accounts.google.com",
48− Path: "/o/oauth2/auth",
49− // to get a refresh token. see https://developers.google.com/identity/protocols/OAuth2WebServer#offline
50− RawQuery: "access_type=offline",
51− }
52− }
53− if p.RedeemURL.String() == "" {
54− p.RedeemURL = &url.URL{Scheme: "https",
55− Host: "www.googleapis.com",
56− Path: "/oauth2/v3/token"}
42+const (
43+ googleProviderName = "Google"
44+ googleDefaultScope = "profile email"
45+)
46+
47+var (
48+ // Default Login URL for Google.
49+ // Pre-parsed URL of https://accounts.google.com/o/oauth2/auth?access_type=offline.
50+ googleDefaultLoginURL = &url.URL{
51+ Scheme: "https",
52+ Host: "accounts.google.com",
53+ Path: "/o/oauth2/auth",
54+ // to get a refresh token. see https://developers.google.com/identity/protocols/OAuth2WebServer#offline
55+ RawQuery: "access_type=offline",
5756 }
58− if p.ValidateURL.String() == "" {
59− p.ValidateURL = &url.URL{Scheme: "https",
60− Host: "www.googleapis.com",
61− Path: "/oauth2/v1/tokeninfo"}
57+
58+ // Default Redeem URL for Google.
59+ // Pre-parsed URL of https://www.googleapis.com/oauth2/v3/token.
60+ googleDefaultRedeemURL = &url.URL{
61+ Scheme: "https",
62+ Host: "www.googleapis.com",
63+ Path: "/oauth2/v3/token",
6264 }
63− if p.Scope == "" {
64− p.Scope = "profile email"
65+
66+ // Default Validation URL for Google.
67+ // Pre-parsed URL of https://www.googleapis.com/oauth2/v1/tokeninfo.
68+ googleDefaultValidateURL = &url.URL{
69+ Scheme: "https",
70+ Host: "www.googleapis.com",
71+ Path: "/oauth2/v1/tokeninfo",
6572 }
73+)
6674
75+// NewGoogleProvider initiates a new GoogleProvider
76+func NewGoogleProvider(p *ProviderData) *GoogleProvider {
77+ p.setProviderDefaults(providerDefaults{
78+ name: googleProviderName,
79+ loginURL: googleDefaultLoginURL,
80+ redeemURL: googleDefaultRedeemURL,
81+ profileURL: nil,
82+ validateURL: googleDefaultValidateURL,
83+ scope: googleDefaultScope,
84+ })
6785 return &GoogleProvider{
6886 ProviderData: p,
6987 // Set a default GroupValidator to just always return valid (true), it will
providers/google_test.go+12−13
@@ -10,8 +10,8 @@ import (
1010 "net/url"
1111 "testing"
1212
13+ . "github.com/onsi/gomega"
1314 "github.com/stretchr/testify/assert"
14−
1515 admin "google.golang.org/api/admin/directory/v1"
1616 option "google.golang.org/api/option"
1717)
@@ -35,18 +35,17 @@ func newGoogleProvider() *GoogleProvider {
3535 Scope: ""})
3636}
3737
38−func TestGoogleProviderDefaults(t *testing.T) {
39− p := newGoogleProvider()
40− assert.NotEqual(t, nil, p)
41− assert.Equal(t, "Google", p.Data().ProviderName)
42− assert.Equal(t, "https://accounts.google.com/o/oauth2/auth?access_type=offline",
43− p.Data().LoginURL.String())
44− assert.Equal(t, "https://www.googleapis.com/oauth2/v3/token",
45− p.Data().RedeemURL.String())
46− assert.Equal(t, "https://www.googleapis.com/oauth2/v1/tokeninfo",
47− p.Data().ValidateURL.String())
48− assert.Equal(t, "", p.Data().ProfileURL.String())
49− assert.Equal(t, "profile email", p.Data().Scope)
38+func TestNewGoogleProvider(t *testing.T) {
39+ g := NewWithT(t)
40+
41+ // Test that defaults are set when calling for a new provider with nothing set
42+ providerData := NewGoogleProvider(&ProviderData{}).Data()
43+ g.Expect(providerData.ProviderName).To(Equal("Google"))
44+ g.Expect(providerData.LoginURL.String()).To(Equal("https://accounts.google.com/o/oauth2/auth?access_type=offline"))
45+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://www.googleapis.com/oauth2/v3/token"))
46+ g.Expect(providerData.ProfileURL.String()).To(Equal(""))
47+ g.Expect(providerData.ValidateURL.String()).To(Equal("https://www.googleapis.com/oauth2/v1/tokeninfo"))
48+ g.Expect(providerData.Scope).To(Equal("profile email"))
5049}
5150
5251func TestGoogleProviderOverrides(t *testing.T) {
providers/internal_util_test.go+3
@@ -13,6 +13,9 @@ import (
1313)
1414
1515func updateURL(url *url.URL, hostname string) {
16+ if url == nil {
17+ return
18+ }
1619 url.Scheme = "http"
1720 url.Host = hostname
1821}
providers/keycloak.go+37−23
@@ -16,32 +16,46 @@ type KeycloakProvider struct {
1616
1717var _ Provider = (*KeycloakProvider)(nil)
1818
19−func NewKeycloakProvider(p *ProviderData) *KeycloakProvider {
20− p.ProviderName = "Keycloak"
21− if p.LoginURL == nil || p.LoginURL.String() == "" {
22− p.LoginURL = &url.URL{
23− Scheme: "https",
24− Host: "keycloak.org",
25− Path: "/oauth/authorize",
26− }
27− }
28− if p.RedeemURL == nil || p.RedeemURL.String() == "" {
29− p.RedeemURL = &url.URL{
30− Scheme: "https",
31− Host: "keycloak.org",
32− Path: "/oauth/token",
33− }
19+const (
20+ keycloakProviderName = "Keycloak"
21+ keycloakDefaultScope = "api"
22+)
23+
24+var (
25+ // Default Login URL for Keycloak.
26+ // Pre-parsed URL of https://keycloak.org/oauth/authorize.
27+ keycloakDefaultLoginURL = &url.URL{
28+ Scheme: "https",
29+ Host: "keycloak.org",
30+ Path: "/oauth/authorize",
3431 }
35− if p.ValidateURL == nil || p.ValidateURL.String() == "" {
36− p.ValidateURL = &url.URL{
37− Scheme: "https",
38− Host: "keycloak.org",
39− Path: "/api/v3/user",
40− }
32+
33+ // Default Redeem URL for Keycloak.
34+ // Pre-parsed URL of ttps://keycloak.org/oauth/token.
35+ keycloakDefaultRedeemURL = &url.URL{
36+ Scheme: "https",
37+ Host: "keycloak.org",
38+ Path: "/oauth/token",
4139 }
42− if p.Scope == "" {
43− p.Scope = "api"
40+
41+ // Default Validation URL for Keycloak.
42+ // Pre-parsed URL of https://keycloak.org/api/v3/user.
43+ keycloakDefaultValidateURL = &url.URL{
44+ Scheme: "https",
45+ Host: "keycloak.org",
46+ Path: "/api/v3/user",
4447 }
48+)
49+
50+func NewKeycloakProvider(p *ProviderData) *KeycloakProvider {
51+ p.setProviderDefaults(providerDefaults{
52+ name: keycloakProviderName,
53+ loginURL: keycloakDefaultLoginURL,
54+ redeemURL: keycloakDefaultRedeemURL,
55+ profileURL: nil,
56+ validateURL: keycloakDefaultValidateURL,
57+ scope: keycloakDefaultScope,
58+ })
4559 return &KeycloakProvider{ProviderData: p}
4660}
4761
providers/keycloak_test.go+15−2
@@ -7,9 +7,9 @@ import (
77 "net/url"
88 "testing"
99
10− "github.com/stretchr/testify/assert"
11−
1210 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
11+ . "github.com/onsi/gomega"
12+ "github.com/stretchr/testify/assert"
1313)
1414
1515func testKeycloakProvider(hostname, group string) *KeycloakProvider {
@@ -65,6 +65,19 @@ func TestKeycloakProviderDefaults(t *testing.T) {
6565 assert.Equal(t, "api", p.Data().Scope)
6666}
6767
68+func TestNewKeycloakProvider(t *testing.T) {
69+ g := NewWithT(t)
70+
71+ // Test that defaults are set when calling for a new provider with nothing set
72+ providerData := NewKeycloakProvider(&ProviderData{}).Data()
73+ g.Expect(providerData.ProviderName).To(Equal("Keycloak"))
74+ g.Expect(providerData.LoginURL.String()).To(Equal("https://keycloak.org/oauth/authorize"))
75+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://keycloak.org/oauth/token"))
76+ g.Expect(providerData.ProfileURL.String()).To(Equal(""))
77+ g.Expect(providerData.ValidateURL.String()).To(Equal("https://keycloak.org/api/v3/user"))
78+ g.Expect(providerData.Scope).To(Equal("api"))
79+}
80+
6881func TestKeycloakProviderOverrides(t *testing.T) {
6982 p := NewKeycloakProvider(
7083 &ProviderData{
providers/linkedin.go+38−21
@@ -18,30 +18,47 @@ type LinkedInProvider struct {
1818
1919var _ Provider = (*LinkedInProvider)(nil)
2020
21−// NewLinkedInProvider initiates a new LinkedInProvider
22−func NewLinkedInProvider(p *ProviderData) *LinkedInProvider {
23− p.ProviderName = "LinkedIn"
24− if p.LoginURL.String() == "" {
25− p.LoginURL = &url.URL{Scheme: "https",
26− Host: "www.linkedin.com",
27− Path: "/uas/oauth2/authorization"}
28− }
29− if p.RedeemURL.String() == "" {
30− p.RedeemURL = &url.URL{Scheme: "https",
31− Host: "www.linkedin.com",
32− Path: "/uas/oauth2/accessToken"}
33− }
34− if p.ProfileURL.String() == "" {
35− p.ProfileURL = &url.URL{Scheme: "https",
36− Host: "www.linkedin.com",
37− Path: "/v1/people/~/email-address"}
21+const (
22+ linkedinProviderName = "LinkedIn"
23+ linkedinDefaultScope = "r_emailaddress r_basicprofile"
24+)
25+
26+var (
27+ // Default Login URL for LinkedIn.
28+ // Pre-parsed URL of https://www.linkedin.com/uas/oauth2/authorization.
29+ linkedinDefaultLoginURL = &url.URL{
30+ Scheme: "https",
31+ Host: "www.linkedin.com",
32+ Path: "/uas/oauth2/authorization",
3833 }
39− if p.ValidateURL.String() == "" {
40− p.ValidateURL = p.ProfileURL
34+
35+ // Default Redeem URL for LinkedIn.
36+ // Pre-parsed URL of https://www.linkedin.com/uas/oauth2/accessToken.
37+ linkedinDefaultRedeemURL = &url.URL{
38+ Scheme: "https",
39+ Host: "www.linkedin.com",
40+ Path: "/uas/oauth2/accessToken",
4141 }
42− if p.Scope == "" {
43− p.Scope = "r_emailaddress r_basicprofile"
42+
43+ // Default Profile URL for LinkedIn.
44+ // Pre-parsed URL of https://www.linkedin.com/v1/people/~/email-address.
45+ linkedinDefaultProfileURL = &url.URL{
46+ Scheme: "https",
47+ Host: "www.linkedin.com",
48+ Path: "/v1/people/~/email-address",
4449 }
50+)
51+
52+// NewLinkedInProvider initiates a new LinkedInProvider
53+func NewLinkedInProvider(p *ProviderData) *LinkedInProvider {
54+ p.setProviderDefaults(providerDefaults{
55+ name: linkedinProviderName,
56+ loginURL: linkedinDefaultLoginURL,
57+ redeemURL: linkedinDefaultRedeemURL,
58+ profileURL: linkedinDefaultProfileURL,
59+ validateURL: linkedinDefaultProfileURL,
60+ scope: linkedinDefaultScope,
61+ })
4562 return &LinkedInProvider{ProviderData: p}
4663}
4764
providers/linkedin_test.go+12−13
@@ -8,6 +8,7 @@ import (
88 "testing"
99
1010 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
11+ . "github.com/onsi/gomega"
1112 "github.com/stretchr/testify/assert"
1213)
1314
@@ -44,19 +45,17 @@ func testLinkedInBackend(payload string) *httptest.Server {
4445 }))
4546}
4647
47−func TestLinkedInProviderDefaults(t *testing.T) {
48− p := testLinkedInProvider("")
49− assert.NotEqual(t, nil, p)
50− assert.Equal(t, "LinkedIn", p.Data().ProviderName)
51− assert.Equal(t, "https://www.linkedin.com/uas/oauth2/authorization",
52− p.Data().LoginURL.String())
53− assert.Equal(t, "https://www.linkedin.com/uas/oauth2/accessToken",
54− p.Data().RedeemURL.String())
55− assert.Equal(t, "https://www.linkedin.com/v1/people/~/email-address",
56− p.Data().ProfileURL.String())
57− assert.Equal(t, "https://www.linkedin.com/v1/people/~/email-address",
58− p.Data().ValidateURL.String())
59− assert.Equal(t, "r_emailaddress r_basicprofile", p.Data().Scope)
48+func TestNewLinkedInProvider(t *testing.T) {
49+ g := NewWithT(t)
50+
51+ // Test that defaults are set when calling for a new provider with nothing set
52+ providerData := NewLinkedInProvider(&ProviderData{}).Data()
53+ g.Expect(providerData.ProviderName).To(Equal("LinkedIn"))
54+ g.Expect(providerData.LoginURL.String()).To(Equal("https://www.linkedin.com/uas/oauth2/authorization"))
55+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://www.linkedin.com/uas/oauth2/accessToken"))
56+ g.Expect(providerData.ProfileURL.String()).To(Equal("https://www.linkedin.com/v1/people/~/email-address"))
57+ g.Expect(providerData.ValidateURL.String()).To(Equal("https://www.linkedin.com/v1/people/~/email-address"))
58+ g.Expect(providerData.Scope).To(Equal("r_emailaddress r_basicprofile"))
6059}
6160
6261func TestLinkedInProviderOverrides(t *testing.T) {
providers/logingov.go+36−24
@@ -43,35 +43,47 @@ func randSeq(n int) string {
4343 return string(b)
4444}
4545
46−// NewLoginGovProvider initiates a new LoginGovProvider
47−func NewLoginGovProvider(p *ProviderData) *LoginGovProvider {
48− p.ProviderName = "login.gov"
46+const (
47+ loginGovProviderName = "login.gov"
48+ loginGovDefaultScope = "email openid"
49+)
4950
50− if p.LoginURL == nil || p.LoginURL.String() == "" {
51− p.LoginURL = &url.URL{
52− Scheme: "https",
53− Host: "secure.login.gov",
54− Path: "/openid_connect/authorize",
55− }
51+var (
52+ // Default Login URL for LoginGov.
53+ // Pre-parsed URL of https://secure.login.gov/openid_connect/authorize.
54+ loginGovDefaultLoginURL = &url.URL{
55+ Scheme: "https",
56+ Host: "secure.login.gov",
57+ Path: "/openid_connect/authorize",
5658 }
57− if p.RedeemURL == nil || p.RedeemURL.String() == "" {
58− p.RedeemURL = &url.URL{
59− Scheme: "https",
60− Host: "secure.login.gov",
61− Path: "/api/openid_connect/token",
62− }
63− }
64− if p.ProfileURL == nil || p.ProfileURL.String() == "" {
65− p.ProfileURL = &url.URL{
66− Scheme: "https",
67− Host: "secure.login.gov",
68− Path: "/api/openid_connect/userinfo",
69− }
59+
60+ // Default Redeem URL for LoginGov.
61+ // Pre-parsed URL of https://secure.login.gov/api/openid_connect/token.
62+ loginGovDefaultRedeemURL = &url.URL{
63+ Scheme: "https",
64+ Host: "secure.login.gov",
65+ Path: "/api/openid_connect/token",
7066 }
71− if p.Scope == "" {
72− p.Scope = "email openid"
67+
68+ // Default Profile URL for LoginGov.
69+ // Pre-parsed URL of https://graph.loginGov.com/v2.5/me.
70+ loginGovDefaultProfileURL = &url.URL{
71+ Scheme: "https",
72+ Host: "secure.login.gov",
73+ Path: "/api/openid_connect/userinfo",
7374 }
75+)
7476
77+// NewLoginGovProvider initiates a new LoginGovProvider
78+func NewLoginGovProvider(p *ProviderData) *LoginGovProvider {
79+ p.setProviderDefaults(providerDefaults{
80+ name: loginGovProviderName,
81+ loginURL: loginGovDefaultLoginURL,
82+ redeemURL: loginGovDefaultRedeemURL,
83+ profileURL: loginGovDefaultProfileURL,
84+ validateURL: nil,
85+ scope: loginGovDefaultScope,
86+ })
7587 return &LoginGovProvider{
7688 ProviderData: p,
7789 Nonce: randSeq(32),
providers/logingov_test.go+12−12
@@ -13,6 +13,7 @@ import (
1313 "time"
1414
1515 "github.com/dgrijalva/jwt-go"
16+ . "github.com/onsi/gomega"
1617 "github.com/stretchr/testify/assert"
1718 "gopkg.in/square/go-jose.v2"
1819)
@@ -65,18 +66,17 @@ func newLoginGovProvider() (l *LoginGovProvider, serverKey *MyKeyData, err error
6566 return
6667}
6768
68−func TestLoginGovProviderDefaults(t *testing.T) {
69− p, _, err := newLoginGovProvider()
70− assert.NotEqual(t, nil, p)
71− assert.NoError(t, err)
72− assert.Equal(t, "login.gov", p.Data().ProviderName)
73− assert.Equal(t, "https://secure.login.gov/openid_connect/authorize",
74− p.Data().LoginURL.String())
75− assert.Equal(t, "https://secure.login.gov/api/openid_connect/token",
76− p.Data().RedeemURL.String())
77− assert.Equal(t, "https://secure.login.gov/api/openid_connect/userinfo",
78− p.Data().ProfileURL.String())
79− assert.Equal(t, "email openid", p.Data().Scope)
69+func TestNewLoginGovProvider(t *testing.T) {
70+ g := NewWithT(t)
71+
72+ // Test that defaults are set when calling for a new provider with nothing set
73+ providerData := NewLoginGovProvider(&ProviderData{}).Data()
74+ g.Expect(providerData.ProviderName).To(Equal("login.gov"))
75+ g.Expect(providerData.LoginURL.String()).To(Equal("https://secure.login.gov/openid_connect/authorize"))
76+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://secure.login.gov/api/openid_connect/token"))
77+ g.Expect(providerData.ProfileURL.String()).To(Equal("https://secure.login.gov/api/openid_connect/userinfo"))
78+ g.Expect(providerData.ValidateURL.String()).To(Equal(""))
79+ g.Expect(providerData.Scope).To(Equal("email openid"))
8080}
8181
8282func TestLoginGovProviderOverrides(t *testing.T) {
providers/nextcloud.go+3−1
@@ -16,9 +16,11 @@ type NextcloudProvider struct {
1616
1717var _ Provider = (*NextcloudProvider)(nil)
1818
19+const nextCloudProviderName = "Nextcloud"
20+
1921// NewNextcloudProvider initiates a new NextcloudProvider
2022func NewNextcloudProvider(p *ProviderData) *NextcloudProvider {
21− p.ProviderName = "Nextcloud"
23+ p.ProviderName = nextCloudProviderName
2224 return &NextcloudProvider{ProviderData: p}
2325}
2426
providers/provider_data.go+35
@@ -44,3 +44,38 @@ func (p *ProviderData) GetClientSecret() (clientSecret string, err error) {
4444 }
4545 return string(fileClientSecret), nil
4646}
47+
48+type providerDefaults struct {
49+ name string
50+ loginURL *url.URL
51+ redeemURL *url.URL
52+ profileURL *url.URL
53+ validateURL *url.URL
54+ scope string
55+}
56+
57+func (p *ProviderData) setProviderDefaults(defaults providerDefaults) {
58+ p.ProviderName = defaults.name
59+ p.LoginURL = defaultURL(p.LoginURL, defaults.loginURL)
60+ p.RedeemURL = defaultURL(p.RedeemURL, defaults.redeemURL)
61+ p.ProfileURL = defaultURL(p.ProfileURL, defaults.profileURL)
62+ p.ValidateURL = defaultURL(p.ValidateURL, defaults.validateURL)
63+
64+ if p.Scope == "" {
65+ p.Scope = defaults.scope
66+ }
67+}
68+
69+// defaultURL will set return a default value if the given value is not set.
70+func defaultURL(u *url.URL, d *url.URL) *url.URL {
71+ if u != nil && u.String() != "" {
72+ // The value is already set
73+ return u
74+ }
75+
76+ // If the default is given, return that
77+ if d != nil {
78+ return d
79+ }
80+ return &url.URL{}
81+}