Merge pull request #1238 from samirachoadi/feature/add_adfs_provider
Added ADFS Provider
This commit is contained in:
		
						commit
						a296936a0f
					
				|  | @ -7,6 +7,7 @@ | ||||||
| ## Breaking Changes | ## Breaking Changes | ||||||
| 
 | 
 | ||||||
| ## Changes since v7.1.3 | ## Changes since v7.1.3 | ||||||
|  | - [#1238](https://github.com/oauth2-proxy/oauth2-proxy/pull/1238) Added ADFS provider (@samirachoadi) | ||||||
| - [#1227](https://github.com/oauth2-proxy/oauth2-proxy/pull/1227) Fix Refresh Session not working for multiple cookies (@rishi1111) | - [#1227](https://github.com/oauth2-proxy/oauth2-proxy/pull/1227) Fix Refresh Session not working for multiple cookies (@rishi1111) | ||||||
| - [#1063](https://github.com/oauth2-proxy/oauth2-proxy/pull/1063) Add Redis lock feature to lock persistent sessions (@Bibob7) | - [#1063](https://github.com/oauth2-proxy/oauth2-proxy/pull/1063) Add Redis lock feature to lock persistent sessions (@Bibob7) | ||||||
| - [#1108](https://github.com/oauth2-proxy/oauth2-proxy/pull/1108) Add alternative ways to generate cookie secrets to docs (@JoelSpeed) | - [#1108](https://github.com/oauth2-proxy/oauth2-proxy/pull/1108) Add alternative ways to generate cookie secrets to docs (@JoelSpeed) | ||||||
|  |  | ||||||
|  | @ -101,6 +101,16 @@ You must remove these options before starting OAuth2 Proxy with `--alpha-config` | ||||||
| ## Configuration Reference | ## Configuration Reference | ||||||
| <!--- THIS FILE IS AUTOGENERATED!!! DO NOT EDIT!!! --> | <!--- THIS FILE IS AUTOGENERATED!!! DO NOT EDIT!!! --> | ||||||
| 
 | 
 | ||||||
|  | ### ADFSOptions | ||||||
|  | 
 | ||||||
|  | (**Appears on:** [Provider](#provider)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | | Field | Type | Description | | ||||||
|  | | ----- | ---- | ----------- | | ||||||
|  | | `skipScope` | _bool_ | Skip adding the scope parameter in login request<br/>Default value is 'false' | | ||||||
|  | 
 | ||||||
| ### AlphaOptions | ### AlphaOptions | ||||||
| 
 | 
 | ||||||
| AlphaOptions contains alpha structured configuration options. | AlphaOptions contains alpha structured configuration options. | ||||||
|  | @ -284,6 +294,7 @@ Provider holds all configuration for a single provider | ||||||
| | `clientSecretFile` | _string_ | ClientSecretFile is the name of the file<br/>containing the OAuth Client Secret, it will be used if ClientSecret is not set. | | | `clientSecretFile` | _string_ | ClientSecretFile is the name of the file<br/>containing the OAuth Client Secret, it will be used if ClientSecret is not set. | | ||||||
| | `keycloakConfig` | _[KeycloakOptions](#keycloakoptions)_ | KeycloakConfig holds all configurations for Keycloak provider. | | | `keycloakConfig` | _[KeycloakOptions](#keycloakoptions)_ | KeycloakConfig holds all configurations for Keycloak provider. | | ||||||
| | `azureConfig` | _[AzureOptions](#azureoptions)_ | AzureConfig holds all configurations for Azure provider. | | | `azureConfig` | _[AzureOptions](#azureoptions)_ | AzureConfig holds all configurations for Azure provider. | | ||||||
|  | | `ADFSConfig` | _[ADFSOptions](#adfsoptions)_ | ADFSConfig holds all configurations for ADFS provider. | | ||||||
| | `bitbucketConfig` | _[BitbucketOptions](#bitbucketoptions)_ | BitbucketConfig holds all configurations for Bitbucket provider. | | | `bitbucketConfig` | _[BitbucketOptions](#bitbucketoptions)_ | BitbucketConfig holds all configurations for Bitbucket provider. | | ||||||
| | `githubConfig` | _[GitHubOptions](#githuboptions)_ | GitHubConfig holds all configurations for GitHubC provider. | | | `githubConfig` | _[GitHubOptions](#githuboptions)_ | GitHubConfig holds all configurations for GitHubC provider. | | ||||||
| | `gitlabConfig` | _[GitLabOptions](#gitlaboptions)_ | GitLabConfig holds all configurations for GitLab provider. | | | `gitlabConfig` | _[GitLabOptions](#gitlaboptions)_ | GitLabConfig holds all configurations for GitLab provider. | | ||||||
|  | @ -297,7 +308,7 @@ Provider holds all configuration for a single provider | ||||||
| | `loginURL` | _string_ | LoginURL is the authentication endpoint | | | `loginURL` | _string_ | LoginURL is the authentication endpoint | | ||||||
| | `redeemURL` | _string_ | RedeemURL is the token redemption endpoint | | | `redeemURL` | _string_ | RedeemURL is the token redemption endpoint | | ||||||
| | `profileURL` | _string_ | ProfileURL is the profile access endpoint | | | `profileURL` | _string_ | ProfileURL is the profile access endpoint | | ||||||
| | `resource` | _string_ | ProtectedResource is the resource that is protected (Azure AD only) | | | `resource` | _string_ | ProtectedResource is the resource that is protected (Azure AD and ADFS only) | | ||||||
| | `validateURL` | _string_ | ValidateURL is the access token validation endpoint | | | `validateURL` | _string_ | ValidateURL is the access token validation endpoint | | ||||||
| | `scope` | _string_ | Scope is the OAuth scope specification | | | `scope` | _string_ | Scope is the OAuth scope specification | | ||||||
| | `prompt` | _string_ | Prompt is OIDC prompt | | | `prompt` | _string_ | Prompt is OIDC prompt | | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ Valid providers are : | ||||||
| 
 | 
 | ||||||
| - [Google](#google-auth-provider) _default_ | - [Google](#google-auth-provider) _default_ | ||||||
| - [Azure](#azure-auth-provider) | - [Azure](#azure-auth-provider) | ||||||
|  | - [ADFS](#adfs-auth-provider) | ||||||
| - [Facebook](#facebook-auth-provider) | - [Facebook](#facebook-auth-provider) | ||||||
| - [GitHub](#github-auth-provider) | - [GitHub](#github-auth-provider) | ||||||
| - [Keycloak](#keycloak-auth-provider) | - [Keycloak](#keycloak-auth-provider) | ||||||
|  | @ -88,6 +89,21 @@ Note: The user is checked against the group members list on initial authenticati | ||||||
| 
 | 
 | ||||||
| Note: When using the Azure Auth provider with nginx and the cookie session store you may find the cookie is too large and doesn't get passed through correctly. Increasing the proxy_buffer_size in nginx or implementing the [redis session storage](sessions.md#redis-storage) should resolve this. | Note: When using the Azure Auth provider with nginx and the cookie session store you may find the cookie is too large and doesn't get passed through correctly. Increasing the proxy_buffer_size in nginx or implementing the [redis session storage](sessions.md#redis-storage) should resolve this. | ||||||
| 
 | 
 | ||||||
|  | ### ADFS Auth Provider | ||||||
|  | 
 | ||||||
|  | 1. Open the ADFS administration console on your Windows Server and add a new Application Group | ||||||
|  | 2. Provide a name for the integration, select Server Application from the Standalone applications section and click Next | ||||||
|  | 3. Follow the wizard to get the client-id, client-secret and configure the application credentials | ||||||
|  | 4. Configure the proxy with | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  |    --provider=adfs | ||||||
|  |    --client-id=<application ID from step 3> | ||||||
|  |    --client-secret=<value from step 3> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Note: When using the ADFS Auth provider with nginx and the cookie session store you may find the cookie is too large and doesn't get passed through correctly. Increasing the proxy_buffer_size in nginx or implementing the [redis session storage](sessions.md#redis-storage) should resolve this. | ||||||
|  | 
 | ||||||
| ### Facebook Auth Provider | ### Facebook Auth Provider | ||||||
| 
 | 
 | ||||||
| 1.  Create a new FB App from <https://developers.facebook.com/> | 1.  Create a new FB App from <https://developers.facebook.com/> | ||||||
|  |  | ||||||
|  | @ -21,6 +21,8 @@ type Provider struct { | ||||||
| 	KeycloakConfig KeycloakOptions `json:"keycloakConfig,omitempty"` | 	KeycloakConfig KeycloakOptions `json:"keycloakConfig,omitempty"` | ||||||
| 	// AzureConfig holds all configurations for Azure provider.
 | 	// AzureConfig holds all configurations for Azure provider.
 | ||||||
| 	AzureConfig AzureOptions `json:"azureConfig,omitempty"` | 	AzureConfig AzureOptions `json:"azureConfig,omitempty"` | ||||||
|  | 	// ADFSConfig holds all configurations for ADFS provider.
 | ||||||
|  | 	ADFSConfig ADFSOptions `json:"ADFSConfig,omitempty"` | ||||||
| 	// BitbucketConfig holds all configurations for Bitbucket provider.
 | 	// BitbucketConfig holds all configurations for Bitbucket provider.
 | ||||||
| 	BitbucketConfig BitbucketOptions `json:"bitbucketConfig,omitempty"` | 	BitbucketConfig BitbucketOptions `json:"bitbucketConfig,omitempty"` | ||||||
| 	// GitHubConfig holds all configurations for GitHubC provider.
 | 	// GitHubConfig holds all configurations for GitHubC provider.
 | ||||||
|  | @ -55,7 +57,7 @@ type Provider struct { | ||||||
| 	RedeemURL string `json:"redeemURL,omitempty"` | 	RedeemURL string `json:"redeemURL,omitempty"` | ||||||
| 	// ProfileURL is the profile access endpoint
 | 	// ProfileURL is the profile access endpoint
 | ||||||
| 	ProfileURL string `json:"profileURL,omitempty"` | 	ProfileURL string `json:"profileURL,omitempty"` | ||||||
| 	// ProtectedResource is the resource that is protected (Azure AD only)
 | 	// ProtectedResource is the resource that is protected (Azure AD and ADFS only)
 | ||||||
| 	ProtectedResource string `json:"resource,omitempty"` | 	ProtectedResource string `json:"resource,omitempty"` | ||||||
| 	// ValidateURL is the access token validation endpoint
 | 	// ValidateURL is the access token validation endpoint
 | ||||||
| 	ValidateURL string `json:"validateURL,omitempty"` | 	ValidateURL string `json:"validateURL,omitempty"` | ||||||
|  | @ -84,6 +86,12 @@ type AzureOptions struct { | ||||||
| 	Tenant string `json:"tenant,omitempty"` | 	Tenant string `json:"tenant,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type ADFSOptions struct { | ||||||
|  | 	// Skip adding the scope parameter in login request
 | ||||||
|  | 	// Default value is 'false'
 | ||||||
|  | 	SkipScope bool `json:"skipScope,omitempty"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type BitbucketOptions struct { | type BitbucketOptions struct { | ||||||
| 	// Team sets restrict logins to members of this team
 | 	// Team sets restrict logins to members of this team
 | ||||||
| 	Team string `json:"team,omitempty"` | 	Team string `json:"team,omitempty"` | ||||||
|  |  | ||||||
|  | @ -236,6 +236,8 @@ func parseProviderInfo(o *options.Options, msgs []string) []string { | ||||||
| 	switch p := o.GetProvider().(type) { | 	switch p := o.GetProvider().(type) { | ||||||
| 	case *providers.AzureProvider: | 	case *providers.AzureProvider: | ||||||
| 		p.Configure(o.Providers[0].AzureConfig.Tenant) | 		p.Configure(o.Providers[0].AzureConfig.Tenant) | ||||||
|  | 	case *providers.ADFSProvider: | ||||||
|  | 		p.Configure(o.Providers[0].ADFSConfig.SkipScope) | ||||||
| 	case *providers.GitHubProvider: | 	case *providers.GitHubProvider: | ||||||
| 		p.SetOrgTeam(o.Providers[0].GitHubConfig.Org, o.Providers[0].GitHubConfig.Team) | 		p.SetOrgTeam(o.Providers[0].GitHubConfig.Org, o.Providers[0].GitHubConfig.Team) | ||||||
| 		p.SetRepo(o.Providers[0].GitHubConfig.Repo, o.Providers[0].GitHubConfig.Token) | 		p.SetRepo(o.Providers[0].GitHubConfig.Repo, o.Providers[0].GitHubConfig.Token) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,100 @@ | ||||||
|  | package providers | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/url" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // ADFSProvider represents an ADFS based Identity Provider
 | ||||||
|  | type ADFSProvider struct { | ||||||
|  | 	*OIDCProvider | ||||||
|  | 	SkipScope bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var _ Provider = (*ADFSProvider)(nil) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	ADFSProviderName = "ADFS" | ||||||
|  | 	ADFSDefaultScope = "openid email profile" | ||||||
|  | 	ADFSSkipScope    = false | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // NewADFSProvider initiates a new ADFSProvider
 | ||||||
|  | func NewADFSProvider(p *ProviderData) *ADFSProvider { | ||||||
|  | 
 | ||||||
|  | 	p.setProviderDefaults(providerDefaults{ | ||||||
|  | 		name:  ADFSProviderName, | ||||||
|  | 		scope: ADFSDefaultScope, | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	if p.ProtectedResource != nil && p.ProtectedResource.String() != "" { | ||||||
|  | 		resource := p.ProtectedResource.String() | ||||||
|  | 		if !strings.HasSuffix(resource, "/") { | ||||||
|  | 			resource += "/" | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if p.Scope != "" && !strings.HasPrefix(p.Scope, resource) { | ||||||
|  | 			p.Scope = resource + p.Scope | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return &ADFSProvider{ | ||||||
|  | 		OIDCProvider: &OIDCProvider{ | ||||||
|  | 			ProviderData: p, | ||||||
|  | 			SkipNonce:    true, | ||||||
|  | 		}, | ||||||
|  | 		SkipScope: ADFSSkipScope, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Configure defaults the ADFSProvider configuration options
 | ||||||
|  | func (p *ADFSProvider) Configure(skipScope bool) { | ||||||
|  | 	p.SkipScope = skipScope | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetLoginURL Override to double encode the state parameter. If not query params are lost
 | ||||||
|  | // More info here: https://docs.microsoft.com/en-us/powerapps/maker/portals/configure/configure-saml2-settings
 | ||||||
|  | func (p *ADFSProvider) GetLoginURL(redirectURI, state, nonce string) string { | ||||||
|  | 	extraParams := url.Values{} | ||||||
|  | 	if !p.SkipNonce { | ||||||
|  | 		extraParams.Add("nonce", nonce) | ||||||
|  | 	} | ||||||
|  | 	loginURL := makeLoginURL(p.Data(), redirectURI, url.QueryEscape(state), extraParams) | ||||||
|  | 	if p.SkipScope { | ||||||
|  | 		q := loginURL.Query() | ||||||
|  | 		q.Del("scope") | ||||||
|  | 		loginURL.RawQuery = q.Encode() | ||||||
|  | 	} | ||||||
|  | 	return loginURL.String() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // EnrichSession to add email
 | ||||||
|  | func (p *ADFSProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error { | ||||||
|  | 	if s.Email != "" { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	idToken, err := p.Verifier.Verify(ctx, s.IDToken) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	p.EmailClaim = "upn" | ||||||
|  | 	c, err := p.getClaims(idToken) | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("couldn't extract claims from id_token (%v)", err) | ||||||
|  | 	} | ||||||
|  | 	s.Email = c.Email | ||||||
|  | 
 | ||||||
|  | 	if s.Email == "" { | ||||||
|  | 		err = errors.New("email not set") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | @ -0,0 +1,205 @@ | ||||||
|  | package providers | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"net/url" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/coreos/go-oidc" | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" | ||||||
|  | 	. "github.com/onsi/ginkgo" | ||||||
|  | 	. "github.com/onsi/ginkgo/extensions/table" | ||||||
|  | 	. "github.com/onsi/gomega" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type fakeADFSJwks struct{} | ||||||
|  | 
 | ||||||
|  | func (fakeADFSJwks) VerifySignature(_ context.Context, jwt string) (payload []byte, err error) { | ||||||
|  | 	decodeString, err := base64.RawURLEncoding.DecodeString(strings.Split(jwt, ".")[1]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return decodeString, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func testADFSProvider(hostname string) *ADFSProvider { | ||||||
|  | 
 | ||||||
|  | 	o := oidc.NewVerifier( | ||||||
|  | 		"https://issuer.example.com", | ||||||
|  | 		fakeADFSJwks{}, | ||||||
|  | 		&oidc.Config{ClientID: "https://test.myapp.com"}, | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	p := NewADFSProvider(&ProviderData{ | ||||||
|  | 		ProviderName: "", | ||||||
|  | 		LoginURL:     &url.URL{}, | ||||||
|  | 		RedeemURL:    &url.URL{}, | ||||||
|  | 		ProfileURL:   &url.URL{}, | ||||||
|  | 		ValidateURL:  &url.URL{}, | ||||||
|  | 		Scope:        "", | ||||||
|  | 		Verifier:     o, | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	if hostname != "" { | ||||||
|  | 		updateURL(p.Data().LoginURL, hostname) | ||||||
|  | 		updateURL(p.Data().RedeemURL, hostname) | ||||||
|  | 		updateURL(p.Data().ProfileURL, hostname) | ||||||
|  | 		updateURL(p.Data().ValidateURL, hostname) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func testADFSBackend() *httptest.Server { | ||||||
|  | 
 | ||||||
|  | 	authResponse := ` | ||||||
|  | 		{ | ||||||
|  | 			"access_token": "my_access_token", | ||||||
|  | 			"id_token": "my_id_token", | ||||||
|  | 			"refresh_token": "my_refresh_token"  | ||||||
|  | 		 } | ||||||
|  | 	` | ||||||
|  | 	userInfo := ` | ||||||
|  | 		{ | ||||||
|  | 			"email": "samiracho@email.com" | ||||||
|  | 		} | ||||||
|  | 	` | ||||||
|  | 
 | ||||||
|  | 	refreshResponse := `{ "access_token": "new_some_access_token", "refresh_token": "new_some_refresh_token", "expires_in": "32693148245", "id_token": "new_some_id_token" }` | ||||||
|  | 
 | ||||||
|  | 	authHeader := "Bearer adfs_access_token" | ||||||
|  | 
 | ||||||
|  | 	return httptest.NewServer(http.HandlerFunc( | ||||||
|  | 		func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 			switch r.URL.Path { | ||||||
|  | 			case "/adfs/oauth2/authorize": | ||||||
|  | 				w.WriteHeader(200) | ||||||
|  | 				w.Write([]byte(authResponse)) | ||||||
|  | 			case "/adfs/oauth2/refresh": | ||||||
|  | 				w.WriteHeader(200) | ||||||
|  | 				w.Write([]byte(refreshResponse)) | ||||||
|  | 			case "/adfs/oauth2/userinfo": | ||||||
|  | 				if r.Header["Authorization"][0] == authHeader { | ||||||
|  | 					w.WriteHeader(200) | ||||||
|  | 					w.Write([]byte(userInfo)) | ||||||
|  | 				} else { | ||||||
|  | 					w.WriteHeader(401) | ||||||
|  | 				} | ||||||
|  | 			default: | ||||||
|  | 				w.WriteHeader(200) | ||||||
|  | 			} | ||||||
|  | 		})) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var _ = Describe("ADFS Provider Tests", func() { | ||||||
|  | 	var p *ADFSProvider | ||||||
|  | 	var b *httptest.Server | ||||||
|  | 
 | ||||||
|  | 	BeforeEach(func() { | ||||||
|  | 		b = testADFSBackend() | ||||||
|  | 
 | ||||||
|  | 		bURL, err := url.Parse(b.URL) | ||||||
|  | 		Expect(err).To(BeNil()) | ||||||
|  | 
 | ||||||
|  | 		p = testADFSProvider(bURL.Host) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	AfterEach(func() { | ||||||
|  | 		b.Close() | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("New Provider Init", func() { | ||||||
|  | 		It("uses defaults", func() { | ||||||
|  | 			providerData := NewADFSProvider(&ProviderData{}).Data() | ||||||
|  | 			Expect(providerData.ProviderName).To(Equal("ADFS")) | ||||||
|  | 			Expect(providerData.Scope).To(Equal("openid email profile")) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("with bad token", func() { | ||||||
|  | 		It("should trigger an error", func() { | ||||||
|  | 			session := &sessions.SessionState{AccessToken: "unexpected_adfs_access_token", IDToken: "malformed_token"} | ||||||
|  | 			err := p.EnrichSession(context.Background(), session) | ||||||
|  | 			Expect(err).NotTo(BeNil()) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("with valid token", func() { | ||||||
|  | 		It("should not throw an error", func() { | ||||||
|  | 			p.EmailClaim = "email" | ||||||
|  | 			rawIDToken, _ := newSignedTestIDToken(defaultIDToken) | ||||||
|  | 			idToken, err := p.Verifier.Verify(context.Background(), rawIDToken) | ||||||
|  | 			Expect(err).To(BeNil()) | ||||||
|  | 			session, err := p.buildSessionFromClaims(idToken) | ||||||
|  | 			session.IDToken = rawIDToken | ||||||
|  | 			Expect(err).To(BeNil()) | ||||||
|  | 			err = p.EnrichSession(context.Background(), session) | ||||||
|  | 			Expect(session.Email).To(Equal("janed@me.com")) | ||||||
|  | 			Expect(err).To(BeNil()) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("with skipScope enabled", func() { | ||||||
|  | 		It("should not include parameter scope", func() { | ||||||
|  | 			resource, _ := url.Parse("http://example.com") | ||||||
|  | 			p := NewADFSProvider(&ProviderData{ | ||||||
|  | 				ProtectedResource: resource, | ||||||
|  | 				Scope:             "", | ||||||
|  | 			}) | ||||||
|  | 			p.SkipScope = true | ||||||
|  | 
 | ||||||
|  | 			result := p.GetLoginURL("https://example.com/adfs/oauth2/", "", "") | ||||||
|  | 			Expect(result).NotTo(ContainSubstring("scope=")) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("With resource parameter", func() { | ||||||
|  | 		type scopeTableInput struct { | ||||||
|  | 			resource      string | ||||||
|  | 			scope         string | ||||||
|  | 			expectedScope string | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		DescribeTable("should return expected results", | ||||||
|  | 			func(in scopeTableInput) { | ||||||
|  | 				resource, _ := url.Parse(in.resource) | ||||||
|  | 				p := NewADFSProvider(&ProviderData{ | ||||||
|  | 					ProtectedResource: resource, | ||||||
|  | 					Scope:             in.scope, | ||||||
|  | 				}) | ||||||
|  | 
 | ||||||
|  | 				Expect(p.Data().Scope).To(Equal(in.expectedScope)) | ||||||
|  | 				result := p.GetLoginURL("https://example.com/adfs/oauth2/", "", "") | ||||||
|  | 				Expect(result).To(ContainSubstring("scope=" + url.QueryEscape(in.expectedScope))) | ||||||
|  | 			}, | ||||||
|  | 			Entry("should add slash", scopeTableInput{ | ||||||
|  | 				resource:      "http://resource.com", | ||||||
|  | 				scope:         "openid", | ||||||
|  | 				expectedScope: "http://resource.com/openid", | ||||||
|  | 			}), | ||||||
|  | 			Entry("shouldn't add extra slash", scopeTableInput{ | ||||||
|  | 				resource:      "http://resource.com/", | ||||||
|  | 				scope:         "openid", | ||||||
|  | 				expectedScope: "http://resource.com/openid", | ||||||
|  | 			}), | ||||||
|  | 			Entry("should add default scopes with resource", scopeTableInput{ | ||||||
|  | 				resource:      "http://resource.com/", | ||||||
|  | 				scope:         "", | ||||||
|  | 				expectedScope: "http://resource.com/openid email profile", | ||||||
|  | 			}), | ||||||
|  | 			Entry("should add default scopes", scopeTableInput{ | ||||||
|  | 				resource:      "", | ||||||
|  | 				scope:         "", | ||||||
|  | 				expectedScope: "openid email profile", | ||||||
|  | 			}), | ||||||
|  | 			Entry("shouldn't add resource if already in scopes", scopeTableInput{ | ||||||
|  | 				resource:      "http://resource.com", | ||||||
|  | 				scope:         "http://resource.com/openid", | ||||||
|  | 				expectedScope: "http://resource.com/openid", | ||||||
|  | 			}), | ||||||
|  | 		) | ||||||
|  | 	}) | ||||||
|  | }) | ||||||
|  | @ -33,6 +33,8 @@ func New(provider string, p *ProviderData) Provider { | ||||||
| 		return NewKeycloakProvider(p) | 		return NewKeycloakProvider(p) | ||||||
| 	case "azure": | 	case "azure": | ||||||
| 		return NewAzureProvider(p) | 		return NewAzureProvider(p) | ||||||
|  | 	case "adfs": | ||||||
|  | 		return NewADFSProvider(p) | ||||||
| 	case "gitlab": | 	case "gitlab": | ||||||
| 		return NewGitLabProvider(p) | 		return NewGitLabProvider(p) | ||||||
| 	case "oidc": | 	case "oidc": | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue