615 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			615 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
package providers
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"crypto/rand"
 | 
						|
	"crypto/rsa"
 | 
						|
	"encoding/base64"
 | 
						|
	"encoding/json"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"net/url"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
 | 
						|
	"github.com/stretchr/testify/assert"
 | 
						|
 | 
						|
	"github.com/coreos/go-oidc/v3/oidc"
 | 
						|
	"github.com/golang-jwt/jwt"
 | 
						|
	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
 | 
						|
	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
 | 
						|
	internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
 | 
						|
	. "github.com/onsi/gomega"
 | 
						|
	"golang.org/x/oauth2"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	idToken      = "eyJfoobar123.eyJbaz987.IDToken"
 | 
						|
	accessToken  = "eyJfoobar123.eyJbaz987.AccessToken"
 | 
						|
	refreshToken = "eyJfoobar123.eyJbaz987.RefreshToken"
 | 
						|
 | 
						|
	oidcIssuer   = "https://issuer.example.com"
 | 
						|
	oidcClientID = "https://test.myapp.com"
 | 
						|
	oidcSecret   = "SuperSecret123456789"
 | 
						|
	oidcNonce    = "abcde12345edcba09876abcde12345ff"
 | 
						|
 | 
						|
	failureTokenID = "this-id-fails-verification"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	verified   = true
 | 
						|
	unverified = false
 | 
						|
 | 
						|
	standardClaims = jwt.StandardClaims{
 | 
						|
		Audience:  oidcClientID,
 | 
						|
		ExpiresAt: time.Now().Add(time.Duration(5) * time.Minute).Unix(),
 | 
						|
		Id:        "id-some-id",
 | 
						|
		IssuedAt:  time.Now().Unix(),
 | 
						|
		Issuer:    oidcIssuer,
 | 
						|
		NotBefore: 0,
 | 
						|
		Subject:   "123456789",
 | 
						|
	}
 | 
						|
 | 
						|
	defaultIDToken = idTokenClaims{
 | 
						|
		Name:           "Jane Dobbs",
 | 
						|
		Email:          "janed@me.com",
 | 
						|
		Phone:          "+4798765432",
 | 
						|
		Picture:        "http://mugbook.com/janed/me.jpg",
 | 
						|
		Groups:         []string{"test:a", "test:b"},
 | 
						|
		Roles:          []string{"test:c", "test:d"},
 | 
						|
		Verified:       &verified,
 | 
						|
		Nonce:          encryption.HashNonce([]byte(oidcNonce)),
 | 
						|
		StandardClaims: standardClaims,
 | 
						|
	}
 | 
						|
 | 
						|
	numericGroupsIDToken = idTokenClaims{
 | 
						|
		Name:           "Jane Dobbs",
 | 
						|
		Email:          "janed@me.com",
 | 
						|
		Phone:          "+4798765432",
 | 
						|
		Picture:        "http://mugbook.com/janed/me.jpg",
 | 
						|
		Groups:         []interface{}{1, 2, 3},
 | 
						|
		Roles:          []string{"test:c", "test:d"},
 | 
						|
		Verified:       &verified,
 | 
						|
		Nonce:          encryption.HashNonce([]byte(oidcNonce)),
 | 
						|
		StandardClaims: standardClaims,
 | 
						|
	}
 | 
						|
 | 
						|
	complexGroupsIDToken = idTokenClaims{
 | 
						|
		Name:    "Complex Claim",
 | 
						|
		Email:   "complex@claims.com",
 | 
						|
		Phone:   "+5439871234",
 | 
						|
		Picture: "http://mugbook.com/complex/claims.jpg",
 | 
						|
		Groups: []interface{}{
 | 
						|
			map[string]interface{}{
 | 
						|
				"groupId": "Admin Group Id",
 | 
						|
				"roles":   []string{"Admin"},
 | 
						|
			},
 | 
						|
			12345,
 | 
						|
			"Just::A::String",
 | 
						|
		},
 | 
						|
		Roles:          []string{"test:simple", "test:roles"},
 | 
						|
		Verified:       &verified,
 | 
						|
		StandardClaims: standardClaims,
 | 
						|
	}
 | 
						|
 | 
						|
	unverifiedIDToken = idTokenClaims{
 | 
						|
		Name:           "Mystery Man",
 | 
						|
		Email:          "unverified@email.com",
 | 
						|
		Phone:          "+4025205729",
 | 
						|
		Picture:        "http://mugbook.com/unverified/email.jpg",
 | 
						|
		Groups:         []string{"test:a", "test:b"},
 | 
						|
		Roles:          []string{"test:c", "test:d"},
 | 
						|
		Verified:       &unverified,
 | 
						|
		StandardClaims: standardClaims,
 | 
						|
	}
 | 
						|
 | 
						|
	minimalIDToken = idTokenClaims{
 | 
						|
		StandardClaims: standardClaims,
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
type idTokenClaims struct {
 | 
						|
	Name     string      `json:"preferred_username,omitempty"`
 | 
						|
	Email    string      `json:"email,omitempty"`
 | 
						|
	Phone    string      `json:"phone_number,omitempty"`
 | 
						|
	Picture  string      `json:"picture,omitempty"`
 | 
						|
	Groups   interface{} `json:"groups,omitempty"`
 | 
						|
	Roles    interface{} `json:"roles,omitempty"`
 | 
						|
	Verified *bool       `json:"email_verified,omitempty"`
 | 
						|
	Nonce    string      `json:"nonce,omitempty"`
 | 
						|
	jwt.StandardClaims
 | 
						|
}
 | 
						|
 | 
						|
type mockJWKS struct{}
 | 
						|
 | 
						|
func (mockJWKS) VerifySignature(_ context.Context, jwt string) ([]byte, error) {
 | 
						|
	decoded, err := base64.RawURLEncoding.DecodeString(strings.Split(jwt, ".")[1])
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	tokenClaims := &idTokenClaims{}
 | 
						|
	err = json.Unmarshal(decoded, tokenClaims)
 | 
						|
	if err != nil || tokenClaims.Id == failureTokenID {
 | 
						|
		return nil, fmt.Errorf("the validation failed for subject [%v]", tokenClaims.Subject)
 | 
						|
	}
 | 
						|
 | 
						|
	return decoded, nil
 | 
						|
}
 | 
						|
 | 
						|
func newSignedTestIDToken(tokenClaims idTokenClaims) (string, error) {
 | 
						|
	key, _ := rsa.GenerateKey(rand.Reader, 2048)
 | 
						|
	standardClaims := jwt.NewWithClaims(jwt.SigningMethodRS256, tokenClaims)
 | 
						|
	return standardClaims.SignedString(key)
 | 
						|
}
 | 
						|
 | 
						|
func newTestOauth2Token() *oauth2.Token {
 | 
						|
	return &oauth2.Token{
 | 
						|
		AccessToken:  accessToken,
 | 
						|
		TokenType:    "Bearer",
 | 
						|
		RefreshToken: refreshToken,
 | 
						|
		Expiry:       time.Time{}.Add(time.Duration(5) * time.Second),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestProviderData_verifyIDToken(t *testing.T) {
 | 
						|
	failureIDToken := defaultIDToken
 | 
						|
	failureIDToken.Id = failureTokenID
 | 
						|
 | 
						|
	testCases := map[string]struct {
 | 
						|
		IDToken       *idTokenClaims
 | 
						|
		Verifier      bool
 | 
						|
		ExpectIDToken bool
 | 
						|
		ExpectedError error
 | 
						|
	}{
 | 
						|
		"Valid ID Token": {
 | 
						|
			IDToken:       &defaultIDToken,
 | 
						|
			Verifier:      true,
 | 
						|
			ExpectIDToken: true,
 | 
						|
			ExpectedError: nil,
 | 
						|
		},
 | 
						|
		"Invalid ID Token": {
 | 
						|
			IDToken:       &failureIDToken,
 | 
						|
			Verifier:      true,
 | 
						|
			ExpectIDToken: false,
 | 
						|
			ExpectedError: errors.New("failed to verify token: failed to verify signature: " +
 | 
						|
				"the validation failed for subject [123456789]"),
 | 
						|
		},
 | 
						|
		"Missing ID Token": {
 | 
						|
			IDToken:       nil,
 | 
						|
			Verifier:      true,
 | 
						|
			ExpectIDToken: false,
 | 
						|
			ExpectedError: ErrMissingIDToken,
 | 
						|
		},
 | 
						|
		"OIDC Verifier not Configured": {
 | 
						|
			IDToken:       &defaultIDToken,
 | 
						|
			Verifier:      false,
 | 
						|
			ExpectIDToken: false,
 | 
						|
			ExpectedError: ErrMissingOIDCVerifier,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for testName, tc := range testCases {
 | 
						|
		t.Run(testName, func(t *testing.T) {
 | 
						|
			g := NewWithT(t)
 | 
						|
 | 
						|
			token := newTestOauth2Token()
 | 
						|
			if tc.IDToken != nil {
 | 
						|
				idToken, err := newSignedTestIDToken(*tc.IDToken)
 | 
						|
				g.Expect(err).ToNot(HaveOccurred())
 | 
						|
				token = token.WithExtra(map[string]interface{}{
 | 
						|
					"id_token": idToken,
 | 
						|
				})
 | 
						|
			}
 | 
						|
 | 
						|
			provider := &ProviderData{}
 | 
						|
			if tc.Verifier {
 | 
						|
				verificationOptions := internaloidc.IDTokenVerificationOptions{
 | 
						|
					AudienceClaims: []string{"aud"},
 | 
						|
					ClientID:       oidcClientID,
 | 
						|
				}
 | 
						|
				provider.Verifier = internaloidc.NewVerifier(oidc.NewVerifier(
 | 
						|
					oidcIssuer,
 | 
						|
					mockJWKS{},
 | 
						|
					&oidc.Config{ClientID: oidcClientID},
 | 
						|
				), verificationOptions)
 | 
						|
			}
 | 
						|
			verified, err := provider.verifyIDToken(context.Background(), token)
 | 
						|
			if err != nil {
 | 
						|
				g.Expect(err).To(Equal(tc.ExpectedError))
 | 
						|
			}
 | 
						|
 | 
						|
			if tc.ExpectIDToken {
 | 
						|
				g.Expect(verified).ToNot(BeNil())
 | 
						|
				g.Expect(*verified).To(BeAssignableToTypeOf(oidc.IDToken{}))
 | 
						|
			} else {
 | 
						|
				g.Expect(verified).To(BeNil())
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestProviderData_buildSessionFromClaims(t *testing.T) {
 | 
						|
	testCases := map[string]struct {
 | 
						|
		IDToken         idTokenClaims
 | 
						|
		AllowUnverified bool
 | 
						|
		UserClaim       string
 | 
						|
		EmailClaim      string
 | 
						|
		GroupsClaim     string
 | 
						|
		ExpectedError   error
 | 
						|
		ExpectedSession *sessions.SessionState
 | 
						|
	}{
 | 
						|
		"Standard": {
 | 
						|
			IDToken:         defaultIDToken,
 | 
						|
			AllowUnverified: false,
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "123456789",
 | 
						|
				Email:             "janed@me.com",
 | 
						|
				Groups:            []string{"test:a", "test:b"},
 | 
						|
				PreferredUsername: "Jane Dobbs",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Unverified Denied": {
 | 
						|
			IDToken:         unverifiedIDToken,
 | 
						|
			AllowUnverified: false,
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			ExpectedError:   errors.New("email in id_token (unverified@email.com) isn't verified"),
 | 
						|
		},
 | 
						|
		"Unverified Allowed": {
 | 
						|
			IDToken:         unverifiedIDToken,
 | 
						|
			AllowUnverified: true,
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "123456789",
 | 
						|
				Email:             "unverified@email.com",
 | 
						|
				Groups:            []string{"test:a", "test:b"},
 | 
						|
				PreferredUsername: "Mystery Man",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Complex Groups": {
 | 
						|
			IDToken:         complexGroupsIDToken,
 | 
						|
			AllowUnverified: true,
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:  "123456789",
 | 
						|
				Email: "complex@claims.com",
 | 
						|
				Groups: []string{
 | 
						|
					"{\"groupId\":\"Admin Group Id\",\"roles\":[\"Admin\"]}",
 | 
						|
					"12345",
 | 
						|
					"Just::A::String",
 | 
						|
				},
 | 
						|
				PreferredUsername: "Complex Claim",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"User Claim Switched": {
 | 
						|
			IDToken:         defaultIDToken,
 | 
						|
			AllowUnverified: true,
 | 
						|
			UserClaim:       "phone_number",
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "+4798765432",
 | 
						|
				Email:             "janed@me.com",
 | 
						|
				Groups:            []string{"test:a", "test:b"},
 | 
						|
				PreferredUsername: "Jane Dobbs",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"User Claim switched to non string": {
 | 
						|
			IDToken:         defaultIDToken,
 | 
						|
			AllowUnverified: true,
 | 
						|
			UserClaim:       "roles",
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "[\"test:c\",\"test:d\"]",
 | 
						|
				Email:             "janed@me.com",
 | 
						|
				Groups:            []string{"test:a", "test:b"},
 | 
						|
				PreferredUsername: "Jane Dobbs",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Email Claim Switched": {
 | 
						|
			IDToken:         unverifiedIDToken,
 | 
						|
			AllowUnverified: true,
 | 
						|
			EmailClaim:      "phone_number",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "123456789",
 | 
						|
				Email:             "+4025205729",
 | 
						|
				Groups:            []string{"test:a", "test:b"},
 | 
						|
				PreferredUsername: "Mystery Man",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Email Claim Switched to Non String": {
 | 
						|
			IDToken:         unverifiedIDToken,
 | 
						|
			AllowUnverified: true,
 | 
						|
			EmailClaim:      "roles",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "123456789",
 | 
						|
				Email:             "[\"test:c\",\"test:d\"]",
 | 
						|
				Groups:            []string{"test:a", "test:b"},
 | 
						|
				PreferredUsername: "Mystery Man",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Email Claim Non Existent": {
 | 
						|
			IDToken:         unverifiedIDToken,
 | 
						|
			AllowUnverified: true,
 | 
						|
			EmailClaim:      "aksjdfhjksadh",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "123456789",
 | 
						|
				Email:             "",
 | 
						|
				Groups:            []string{"test:a", "test:b"},
 | 
						|
				PreferredUsername: "Mystery Man",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Groups Claim Switched": {
 | 
						|
			IDToken:         defaultIDToken,
 | 
						|
			AllowUnverified: false,
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "roles",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "123456789",
 | 
						|
				Email:             "janed@me.com",
 | 
						|
				Groups:            []string{"test:c", "test:d"},
 | 
						|
				PreferredUsername: "Jane Dobbs",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Groups Claim Non Existent": {
 | 
						|
			IDToken:         defaultIDToken,
 | 
						|
			AllowUnverified: false,
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "alskdjfsalkdjf",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "123456789",
 | 
						|
				Email:             "janed@me.com",
 | 
						|
				Groups:            nil,
 | 
						|
				PreferredUsername: "Jane Dobbs",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Groups Claim Numeric values": {
 | 
						|
			IDToken:         numericGroupsIDToken,
 | 
						|
			AllowUnverified: false,
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "groups",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "123456789",
 | 
						|
				Email:             "janed@me.com",
 | 
						|
				Groups:            []string{"1", "2", "3"},
 | 
						|
				PreferredUsername: "Jane Dobbs",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Groups Claim string values": {
 | 
						|
			IDToken:         defaultIDToken,
 | 
						|
			AllowUnverified: false,
 | 
						|
			EmailClaim:      "email",
 | 
						|
			GroupsClaim:     "email",
 | 
						|
			UserClaim:       "sub",
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:              "123456789",
 | 
						|
				Email:             "janed@me.com",
 | 
						|
				Groups:            []string{"janed@me.com"},
 | 
						|
				PreferredUsername: "Jane Dobbs",
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for testName, tc := range testCases {
 | 
						|
		t.Run(testName, func(t *testing.T) {
 | 
						|
			g := NewWithT(t)
 | 
						|
 | 
						|
			verificationOptions := internaloidc.IDTokenVerificationOptions{
 | 
						|
				AudienceClaims: []string{"aud"},
 | 
						|
				ClientID:       oidcClientID,
 | 
						|
			}
 | 
						|
			provider := &ProviderData{
 | 
						|
				Verifier: internaloidc.NewVerifier(oidc.NewVerifier(
 | 
						|
					oidcIssuer,
 | 
						|
					mockJWKS{},
 | 
						|
					&oidc.Config{ClientID: oidcClientID},
 | 
						|
				), verificationOptions),
 | 
						|
			}
 | 
						|
			provider.AllowUnverifiedEmail = tc.AllowUnverified
 | 
						|
			provider.UserClaim = tc.UserClaim
 | 
						|
			provider.EmailClaim = tc.EmailClaim
 | 
						|
			provider.GroupsClaim = tc.GroupsClaim
 | 
						|
 | 
						|
			rawIDToken, err := newSignedTestIDToken(tc.IDToken)
 | 
						|
			g.Expect(err).ToNot(HaveOccurred())
 | 
						|
 | 
						|
			ss, err := provider.buildSessionFromClaims(rawIDToken, "")
 | 
						|
			if err != nil {
 | 
						|
				g.Expect(err).To(Equal(tc.ExpectedError))
 | 
						|
			}
 | 
						|
			if ss != nil {
 | 
						|
				g.Expect(ss).To(Equal(tc.ExpectedSession))
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestProviderData_checkNonce(t *testing.T) {
 | 
						|
	testCases := map[string]struct {
 | 
						|
		Session       *sessions.SessionState
 | 
						|
		IDToken       idTokenClaims
 | 
						|
		ExpectedError error
 | 
						|
	}{
 | 
						|
		"Nonces match": {
 | 
						|
			Session: &sessions.SessionState{
 | 
						|
				Nonce: []byte(oidcNonce),
 | 
						|
			},
 | 
						|
			IDToken:       defaultIDToken,
 | 
						|
			ExpectedError: nil,
 | 
						|
		},
 | 
						|
		"Nonces do not match": {
 | 
						|
			Session: &sessions.SessionState{
 | 
						|
				Nonce: []byte("WrongWrongWrong"),
 | 
						|
			},
 | 
						|
			IDToken:       defaultIDToken,
 | 
						|
			ExpectedError: errors.New("id_token nonce claim does not match the session nonce"),
 | 
						|
		},
 | 
						|
 | 
						|
		"Missing nonce claim": {
 | 
						|
			Session: &sessions.SessionState{
 | 
						|
				Nonce: []byte(oidcNonce),
 | 
						|
			},
 | 
						|
			IDToken:       minimalIDToken,
 | 
						|
			ExpectedError: errors.New("id_token nonce claim does not match the session nonce"),
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for testName, tc := range testCases {
 | 
						|
		t.Run(testName, func(t *testing.T) {
 | 
						|
			g := NewWithT(t)
 | 
						|
 | 
						|
			// Ensure that the ID token in the session is valid (signed and contains a nonce)
 | 
						|
			// as the nonce claim is extracted to compare with the session nonce
 | 
						|
			rawIDToken, err := newSignedTestIDToken(tc.IDToken)
 | 
						|
			g.Expect(err).ToNot(HaveOccurred())
 | 
						|
			tc.Session.IDToken = rawIDToken
 | 
						|
 | 
						|
			verificationOptions := internaloidc.IDTokenVerificationOptions{
 | 
						|
				AudienceClaims: []string{"aud"},
 | 
						|
				ClientID:       oidcClientID,
 | 
						|
			}
 | 
						|
			provider := &ProviderData{
 | 
						|
				Verifier: internaloidc.NewVerifier(oidc.NewVerifier(
 | 
						|
					oidcIssuer,
 | 
						|
					mockJWKS{},
 | 
						|
					&oidc.Config{ClientID: oidcClientID},
 | 
						|
				), verificationOptions),
 | 
						|
			}
 | 
						|
 | 
						|
			if err := provider.checkNonce(tc.Session); err != nil {
 | 
						|
				g.Expect(err).To(Equal(tc.ExpectedError))
 | 
						|
			} else {
 | 
						|
				g.Expect(err).ToNot(HaveOccurred())
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestProviderData_loginURLParameters(t *testing.T) {
 | 
						|
 | 
						|
	testCases := []struct {
 | 
						|
		name      string
 | 
						|
		overrides url.Values
 | 
						|
		has       url.Values
 | 
						|
		notHas    []string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:      "no overrides",
 | 
						|
			overrides: url.Values{},
 | 
						|
			has: url.Values{
 | 
						|
				"fixed":             {"fixed-value"},
 | 
						|
				"enum_with_default": {"default-value"},
 | 
						|
				"free_with_default": {"default-value"},
 | 
						|
			},
 | 
						|
			notHas: []string{"enum_no_default", "free_no_default"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:      "attempt to override fixed value",
 | 
						|
			overrides: url.Values{"fixed": {"another-value"}},
 | 
						|
			has: url.Values{
 | 
						|
				"fixed":             {"fixed-value"},
 | 
						|
				"enum_with_default": {"default-value"},
 | 
						|
				"free_with_default": {"default-value"},
 | 
						|
			},
 | 
						|
			notHas: []string{"enum_no_default", "free_no_default"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "set one allowed and one forbidden enum",
 | 
						|
			overrides: url.Values{
 | 
						|
				"enum_no_default": {"allowed1", "not-allowed"},
 | 
						|
			},
 | 
						|
			has: url.Values{
 | 
						|
				"fixed":             {"fixed-value"},
 | 
						|
				"enum_with_default": {"default-value"},
 | 
						|
				"free_with_default": {"default-value"},
 | 
						|
				"enum_no_default":   {"allowed1"},
 | 
						|
			},
 | 
						|
			notHas: []string{"free_no_default"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:      "replace default value",
 | 
						|
			overrides: url.Values{"free_with_default": {"something-else"}},
 | 
						|
			has: url.Values{
 | 
						|
				"fixed":             {"fixed-value"},
 | 
						|
				"enum_with_default": {"default-value"},
 | 
						|
				"free_with_default": {"something-else"},
 | 
						|
			},
 | 
						|
			notHas: []string{"enum_no_default", "free_no_default"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:      "set free text value",
 | 
						|
			overrides: url.Values{"free_no_default": {"some-value"}},
 | 
						|
			has: url.Values{
 | 
						|
				"fixed":             {"fixed-value"},
 | 
						|
				"enum_with_default": {"default-value"},
 | 
						|
				"free_with_default": {"default-value"},
 | 
						|
				"free_no_default":   {"some-value"},
 | 
						|
			},
 | 
						|
			notHas: []string{"enum_no_default"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:      "attempt to set unapproved parameter",
 | 
						|
			overrides: url.Values{"malicious_value": {"evil"}},
 | 
						|
			has: url.Values{
 | 
						|
				"fixed":             {"fixed-value"},
 | 
						|
				"enum_with_default": {"default-value"},
 | 
						|
				"free_with_default": {"default-value"},
 | 
						|
			},
 | 
						|
			notHas: []string{"enum_no_default", "free_no_default"},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	// fixed list of two allowed values
 | 
						|
	allowed1 := "allowed1"
 | 
						|
	allowed2 := "allowed2"
 | 
						|
	allowEnum := []options.URLParameterRule{
 | 
						|
		{Value: &allowed1},
 | 
						|
		{Value: &allowed2},
 | 
						|
	}
 | 
						|
	// regex that will allow anything
 | 
						|
	anything := "^.*$"
 | 
						|
	allowAnything := []options.URLParameterRule{
 | 
						|
		{Pattern: &anything},
 | 
						|
	}
 | 
						|
	for _, tc := range testCases {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			// set up LoginURLParameters for testing
 | 
						|
			data := ProviderData{}
 | 
						|
			data.compileLoginParams([]options.LoginURLParameter{
 | 
						|
				{Name: "fixed", Default: []string{"fixed-value"}},
 | 
						|
				{Name: "enum_with_default", Default: []string{"default-value"}, Allow: allowEnum},
 | 
						|
				{Name: "enum_no_default", Allow: allowEnum},
 | 
						|
				{Name: "free_with_default", Default: []string{"default-value"}, Allow: allowAnything},
 | 
						|
				{Name: "free_no_default", Allow: allowAnything},
 | 
						|
			})
 | 
						|
 | 
						|
			redirectParams := data.LoginURLParams(tc.overrides)
 | 
						|
			for _, k := range tc.notHas {
 | 
						|
				assert.NotContains(t, redirectParams, k)
 | 
						|
			}
 | 
						|
			for k, vs := range tc.has {
 | 
						|
				actualVals := redirectParams[k]
 | 
						|
				assert.ElementsMatch(t, vs, actualVals)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 |