add the IDP display name to the downstream ID token's sub claim

To make the subject of the downstream ID token more unique when
there are multiple IDPs. It is possible to define two IDPs in a
FederationDomain using the same identity provider CR, in which
case the only thing that would make the subject claim different
is adding the IDP display name into the values of the subject claim.
This commit is contained in:
Ryan Richard 2023-08-30 15:13:42 -07:00
parent 28210ab14d
commit e2bdab9e2d
15 changed files with 372 additions and 294 deletions

View File

@ -246,7 +246,7 @@ if [[ "$use_oidc_upstream" == "yes" ]]; then
fd_idps="${fd_idps}$( fd_idps="${fd_idps}$(
cat <<EOF cat <<EOF
- displayName: "My OIDC IDP" - displayName: "My OIDC IDP 🚀"
objectRef: objectRef:
apiGroup: idp.supervisor.pinniped.dev apiGroup: idp.supervisor.pinniped.dev
kind: OIDCIdentityProvider kind: OIDCIdentityProvider
@ -272,7 +272,7 @@ if [[ "$use_ldap_upstream" == "yes" ]]; then
fd_idps="${fd_idps}$( fd_idps="${fd_idps}$(
cat <<EOF cat <<EOF
- displayName: "My LDAP IDP" - displayName: "My LDAP IDP 🚀"
objectRef: objectRef:
apiGroup: idp.supervisor.pinniped.dev apiGroup: idp.supervisor.pinniped.dev
kind: LDAPIdentityProvider kind: LDAPIdentityProvider

View File

@ -41,6 +41,8 @@ const (
emailVerifiedClaimInvalidFormatErr = constable.Error("email_verified claim in upstream ID token has invalid format") emailVerifiedClaimInvalidFormatErr = constable.Error("email_verified claim in upstream ID token has invalid format")
emailVerifiedClaimFalseErr = constable.Error("email_verified claim in upstream ID token has false value") emailVerifiedClaimFalseErr = constable.Error("email_verified claim in upstream ID token has false value")
idTransformUnexpectedErr = constable.Error("configured identity transformation or policy resulted in unexpected error") idTransformUnexpectedErr = constable.Error("configured identity transformation or policy resulted in unexpected error")
idpNameSubjectQueryParam = "idpName"
) )
// MakeDownstreamSession creates a downstream OIDC session. // MakeDownstreamSession creates a downstream OIDC session.
@ -213,8 +215,9 @@ func AutoApproveScopes(authorizeRequester fosite.AuthorizeRequester) {
func GetDownstreamIdentityFromUpstreamIDToken( func GetDownstreamIdentityFromUpstreamIDToken(
upstreamIDPConfig upstreamprovider.UpstreamOIDCIdentityProviderI, upstreamIDPConfig upstreamprovider.UpstreamOIDCIdentityProviderI,
idTokenClaims map[string]interface{}, idTokenClaims map[string]interface{},
idpDisplayName string,
) (string, string, []string, error) { ) (string, string, []string, error) {
subject, username, err := getSubjectAndUsernameFromUpstreamIDToken(upstreamIDPConfig, idTokenClaims) subject, username, err := getSubjectAndUsernameFromUpstreamIDToken(upstreamIDPConfig, idTokenClaims, idpDisplayName)
if err != nil { if err != nil {
return "", "", nil, err return "", "", nil, err
} }
@ -275,6 +278,7 @@ func ApplyIdentityTransformations(
func getSubjectAndUsernameFromUpstreamIDToken( func getSubjectAndUsernameFromUpstreamIDToken(
upstreamIDPConfig upstreamprovider.UpstreamOIDCIdentityProviderI, upstreamIDPConfig upstreamprovider.UpstreamOIDCIdentityProviderI,
idTokenClaims map[string]interface{}, idTokenClaims map[string]interface{},
idpDisplayName string,
) (string, string, error) { ) (string, string, error) {
// The spec says the "sub" claim is only unique per issuer, // The spec says the "sub" claim is only unique per issuer,
// so we will prepend the issuer string to make it globally unique. // so we will prepend the issuer string to make it globally unique.
@ -286,11 +290,11 @@ func getSubjectAndUsernameFromUpstreamIDToken(
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
subject := downstreamSubjectFromUpstreamOIDC(upstreamIssuer, upstreamSubject) subject := downstreamSubjectFromUpstreamOIDC(upstreamIssuer, upstreamSubject, idpDisplayName)
usernameClaimName := upstreamIDPConfig.GetUsernameClaim() usernameClaimName := upstreamIDPConfig.GetUsernameClaim()
if usernameClaimName == "" { if usernameClaimName == "" {
return subject, subject, nil return subject, downstreamUsernameFromUpstreamOIDCSubject(upstreamIssuer, upstreamSubject), nil
} }
// If the upstream username claim is configured to be the special "email" claim and the upstream "email_verified" // If the upstream username claim is configured to be the special "email" claim and the upstream "email_verified"
@ -358,20 +362,34 @@ func ExtractStringClaimValue(claimName string, upstreamIDPName string, idTokenCl
return valueAsString, nil return valueAsString, nil
} }
func DownstreamSubjectFromUpstreamLDAP(ldapUpstream upstreamprovider.UpstreamLDAPIdentityProviderI, authenticateResponse *authenticators.Response) string { func DownstreamSubjectFromUpstreamLDAP(
ldapUpstream upstreamprovider.UpstreamLDAPIdentityProviderI,
authenticateResponse *authenticators.Response,
idpDisplayName string,
) string {
ldapURL := *ldapUpstream.GetURL() ldapURL := *ldapUpstream.GetURL()
return DownstreamLDAPSubject(authenticateResponse.User.GetUID(), ldapURL) return DownstreamLDAPSubject(authenticateResponse.User.GetUID(), ldapURL, idpDisplayName)
} }
func DownstreamLDAPSubject(uid string, ldapURL url.URL) string { func DownstreamLDAPSubject(uid string, ldapURL url.URL, idpDisplayName string) string {
q := ldapURL.Query() q := ldapURL.Query()
q.Set(idpNameSubjectQueryParam, idpDisplayName)
q.Set(oidcapi.IDTokenClaimSubject, uid) q.Set(oidcapi.IDTokenClaimSubject, uid)
ldapURL.RawQuery = q.Encode() ldapURL.RawQuery = q.Encode()
return ldapURL.String() return ldapURL.String()
} }
func downstreamSubjectFromUpstreamOIDC(upstreamIssuerAsString string, upstreamSubject string) string { func downstreamSubjectFromUpstreamOIDC(upstreamIssuerAsString string, upstreamSubject string, idpDisplayName string) string {
return fmt.Sprintf("%s?%s=%s", upstreamIssuerAsString, oidcapi.IDTokenClaimSubject, url.QueryEscape(upstreamSubject)) return fmt.Sprintf("%s?%s=%s&%s=%s", upstreamIssuerAsString,
idpNameSubjectQueryParam, url.QueryEscape(idpDisplayName),
oidcapi.IDTokenClaimSubject, url.QueryEscape(upstreamSubject),
)
}
func downstreamUsernameFromUpstreamOIDCSubject(upstreamIssuerAsString string, upstreamSubject string) string {
return fmt.Sprintf("%s?%s=%s", upstreamIssuerAsString,
oidcapi.IDTokenClaimSubject, url.QueryEscape(upstreamSubject),
)
} }
// GetGroupsFromUpstreamIDToken returns mapped group names coerced into a slice of strings. // GetGroupsFromUpstreamIDToken returns mapped group names coerced into a slice of strings.

View File

@ -5,6 +5,7 @@ package downstreamsession
import ( import (
"context" "context"
"net/url"
"testing" "testing"
"time" "time"
@ -156,3 +157,118 @@ func TestApplyIdentityTransformations(t *testing.T) {
}) })
} }
} }
func TestDownstreamLDAPSubject(t *testing.T) {
tests := []struct {
name string
uid string
ldapURL string
idpDisplayName string
wantSubject string
}{
{
name: "simple display name",
uid: "some uid",
ldapURL: "ldaps://server.example.com:1234",
idpDisplayName: "simpleName",
wantSubject: "ldaps://server.example.com:1234?idpName=simpleName&sub=some+uid",
},
{
name: "interesting display name",
uid: "some uid",
ldapURL: "ldaps://server.example.com:1234",
idpDisplayName: "this is a 👍 display name that 🦭 can handle",
wantSubject: "ldaps://server.example.com:1234?idpName=this+is+a+%F0%9F%91%8D+display+name+that+%F0%9F%A6%AD+can+handle&sub=some+uid",
},
{
name: "url already has query",
uid: "some uid",
ldapURL: "ldaps://server.example.com:1234?a=1&b=%F0%9F%A6%AD",
idpDisplayName: "some name",
wantSubject: "ldaps://server.example.com:1234?a=1&b=%F0%9F%A6%AD&idpName=some+name&sub=some+uid",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
url, err := url.Parse(test.ldapURL)
require.NoError(t, err)
actual := DownstreamLDAPSubject(test.uid, *url, test.idpDisplayName)
require.Equal(t, test.wantSubject, actual)
})
}
}
func TestDownstreamSubjectFromUpstreamOIDC(t *testing.T) {
tests := []struct {
name string
upstreamIssuerAsString string
upstreamSubject string
idpDisplayName string
wantSubject string
}{
{
name: "simple display name",
upstreamIssuerAsString: "https://server.example.com:1234/path",
upstreamSubject: "some subject",
idpDisplayName: "simpleName",
wantSubject: "https://server.example.com:1234/path?idpName=simpleName&sub=some+subject",
},
{
name: "interesting display name",
upstreamIssuerAsString: "https://server.example.com:1234/path",
upstreamSubject: "some subject",
idpDisplayName: "this is a 👍 display name that 🦭 can handle",
wantSubject: "https://server.example.com:1234/path?idpName=this+is+a+%F0%9F%91%8D+display+name+that+%F0%9F%A6%AD+can+handle&sub=some+subject",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
actual := downstreamSubjectFromUpstreamOIDC(test.upstreamIssuerAsString, test.upstreamSubject, test.idpDisplayName)
require.Equal(t, test.wantSubject, actual)
})
}
}
func TestDownstreamUsernameFromUpstreamOIDCSubject(t *testing.T) {
tests := []struct {
name string
upstreamIssuerAsString string
upstreamSubject string
wantSubject string
}{
{
name: "simple upstreamSubject",
upstreamIssuerAsString: "https://server.example.com:1234/path",
upstreamSubject: "some subject",
wantSubject: "https://server.example.com:1234/path?sub=some+subject",
},
{
name: "interesting upstreamSubject",
upstreamIssuerAsString: "https://server.example.com:1234/path",
upstreamSubject: "this is a 👍 subject that 🦭 can handle",
wantSubject: "https://server.example.com:1234/path?sub=this+is+a+%F0%9F%91%8D+subject+that+%F0%9F%A6%AD+can+handle",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
actual := downstreamUsernameFromUpstreamOIDCSubject(test.upstreamIssuerAsString, test.upstreamSubject)
require.Equal(t, test.wantSubject, actual)
})
}
}

View File

@ -108,6 +108,7 @@ func NewHandler(
oauthHelperWithStorage, oauthHelperWithStorage,
oidcUpstream.Provider, oidcUpstream.Provider,
oidcUpstream.Transforms, oidcUpstream.Transforms,
oidcUpstream.DisplayName,
idpNameQueryParamValue, idpNameQueryParamValue,
) )
} }
@ -130,6 +131,7 @@ func NewHandler(
ldapUpstream.Provider, ldapUpstream.Provider,
ldapUpstream.SessionProviderType, ldapUpstream.SessionProviderType,
ldapUpstream.Transforms, ldapUpstream.Transforms,
ldapUpstream.DisplayName,
idpNameQueryParamValue, idpNameQueryParamValue,
) )
} }
@ -158,6 +160,7 @@ func handleAuthRequestForLDAPUpstreamCLIFlow(
ldapUpstream upstreamprovider.UpstreamLDAPIdentityProviderI, ldapUpstream upstreamprovider.UpstreamLDAPIdentityProviderI,
idpType psession.ProviderType, idpType psession.ProviderType,
identityTransforms *idtransform.TransformationPipeline, identityTransforms *idtransform.TransformationPipeline,
idpDisplayName string,
idpNameQueryParamValue string, idpNameQueryParamValue string,
) error { ) error {
authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper, true) authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper, true)
@ -187,7 +190,7 @@ func handleAuthRequestForLDAPUpstreamCLIFlow(
return nil return nil
} }
subject := downstreamsession.DownstreamSubjectFromUpstreamLDAP(ldapUpstream, authenticateResponse) subject := downstreamsession.DownstreamSubjectFromUpstreamLDAP(ldapUpstream, authenticateResponse, idpDisplayName)
upstreamUsername := authenticateResponse.User.GetName() upstreamUsername := authenticateResponse.User.GetName()
upstreamGroups := authenticateResponse.User.GetGroups() upstreamGroups := authenticateResponse.User.GetGroups()
@ -251,6 +254,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
oauthHelper fosite.OAuth2Provider, oauthHelper fosite.OAuth2Provider,
oidcUpstream upstreamprovider.UpstreamOIDCIdentityProviderI, oidcUpstream upstreamprovider.UpstreamOIDCIdentityProviderI,
identityTransforms *idtransform.TransformationPipeline, identityTransforms *idtransform.TransformationPipeline,
idpDisplayName string,
idpNameQueryParamValue string, idpNameQueryParamValue string,
) error { ) error {
authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper, true) authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper, true)
@ -291,7 +295,9 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
return nil return nil
} }
subject, upstreamUsername, upstreamGroups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(oidcUpstream, token.IDToken.Claims) subject, upstreamUsername, upstreamGroups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(
oidcUpstream, token.IDToken.Claims, idpDisplayName,
)
if err != nil { if err != nil {
// Return a user-friendly error for this case which is entirely within our control. // Return a user-friendly error for this case which is entirely within our control.
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester, oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,

View File

@ -833,7 +833,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -856,7 +856,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: transformationUsernamePrefix + oidcUpstreamUsername, wantDownstreamIDTokenUsername: transformationUsernamePrefix + oidcUpstreamUsername,
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, oidcUpstreamGroupMembership), wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, oidcUpstreamGroupMembership),
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -905,7 +905,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -936,7 +936,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -958,7 +958,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -980,7 +980,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: transformationUsernamePrefix + happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: transformationUsernamePrefix + happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, happyLDAPGroups), wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, happyLDAPGroups),
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1019,7 +1019,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1211,7 +1211,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1234,7 +1234,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1257,7 +1257,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1414,7 +1414,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: downstreamRedirectURIWithDifferentPort + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyState, wantRedirectLocationRegexp: downstreamRedirectURIWithDifferentPort + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyState,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1437,7 +1437,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: downstreamRedirectURIWithDifferentPort + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyState, wantRedirectLocationRegexp: downstreamRedirectURIWithDifferentPort + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyState,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1478,7 +1478,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1500,7 +1500,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1535,7 +1535,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -2668,7 +2668,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyState, // username and groups scopes were not requested, but are granted anyway for backwards compatibility wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyState, // username and groups scopes were not requested, but are granted anyway for backwards compatibility
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, // username scope was not requested, but is granted anyway for backwards compatibility wantDownstreamIDTokenUsername: oidcUpstreamUsername, // username scope was not requested, but is granted anyway for backwards compatibility
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, // groups scope was not requested, but is granted anyway for backwards compatibility wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, // groups scope was not requested, but is granted anyway for backwards compatibility
wantDownstreamRequestedScopes: []string{"email"}, // only email was requested wantDownstreamRequestedScopes: []string{"email"}, // only email was requested
@ -2690,7 +2690,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyState, // username and groups scopes were not requested, but are granted anyway for backwards compatibility wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyState, // username and groups scopes were not requested, but are granted anyway for backwards compatibility
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, // username scope was not requested, but is granted anyway for backwards compatibility wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, // username scope was not requested, but is granted anyway for backwards compatibility
wantDownstreamIDTokenGroups: happyLDAPGroups, // groups scope was not requested, but is granted anyway for backwards compatibility wantDownstreamIDTokenGroups: happyLDAPGroups, // groups scope was not requested, but is granted anyway for backwards compatibility
wantDownstreamRequestedScopes: []string{"email"}, // only email was requested wantDownstreamRequestedScopes: []string{"email"}, // only email was requested
@ -2714,7 +2714,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenGroups: []string{}, wantDownstreamIDTokenGroups: []string{},
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -2745,7 +2745,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: "joe@whitehouse.gov", wantDownstreamIDTokenUsername: "joe@whitehouse.gov",
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -2777,7 +2777,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: "joe@whitehouse.gov", wantDownstreamIDTokenUsername: "joe@whitehouse.gov",
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -2810,7 +2810,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: "joe", wantDownstreamIDTokenUsername: "joe",
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -2875,7 +2875,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamSubject, wantDownstreamIDTokenUsername: oidcUpstreamSubject,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -2905,7 +2905,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: []string{"notAnArrayGroup1 notAnArrayGroup2"}, wantDownstreamIDTokenGroups: []string{"notAnArrayGroup1 notAnArrayGroup2"},
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -2935,7 +2935,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: []string{"group1", "group2"}, wantDownstreamIDTokenGroups: []string{"group1", "group2"},
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -2979,7 +2979,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
wantStatus: http.StatusFound, wantStatus: http.StatusFound,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: []string{}, wantDownstreamIDTokenGroups: []string{},
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,

View File

@ -70,19 +70,25 @@ func NewHandler(
return httperr.New(http.StatusBadGateway, "error exchanging and validating upstream tokens") return httperr.New(http.StatusBadGateway, "error exchanging and validating upstream tokens")
} }
subject, upstreamUsername, upstreamGroups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(upstreamIDPConfig, token.IDToken.Claims) subject, upstreamUsername, upstreamGroups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(
upstreamIDPConfig, token.IDToken.Claims, resolvedOIDCIdentityProvider.DisplayName,
)
if err != nil { if err != nil {
return httperr.Wrap(http.StatusUnprocessableEntity, err.Error(), err) return httperr.Wrap(http.StatusUnprocessableEntity, err.Error(), err)
} }
username, groups, err := downstreamsession.ApplyIdentityTransformations(r.Context(), resolvedOIDCIdentityProvider.Transforms, upstreamUsername, upstreamGroups) username, groups, err := downstreamsession.ApplyIdentityTransformations(
r.Context(), resolvedOIDCIdentityProvider.Transforms, upstreamUsername, upstreamGroups,
)
if err != nil { if err != nil {
return httperr.Wrap(http.StatusUnprocessableEntity, err.Error(), err) return httperr.Wrap(http.StatusUnprocessableEntity, err.Error(), err)
} }
additionalClaims := downstreamsession.MapAdditionalClaimsFromUpstreamIDToken(upstreamIDPConfig, token.IDToken.Claims) additionalClaims := downstreamsession.MapAdditionalClaimsFromUpstreamIDToken(upstreamIDPConfig, token.IDToken.Claims)
customSessionData, err := downstreamsession.MakeDownstreamOIDCCustomSessionData(upstreamIDPConfig, token, username, upstreamUsername, upstreamGroups) customSessionData, err := downstreamsession.MakeDownstreamOIDCCustomSessionData(
upstreamIDPConfig, token, username, upstreamUsername, upstreamGroups,
)
if err != nil { if err != nil {
return httperr.Wrap(http.StatusUnprocessableEntity, err.Error(), err) return httperr.Wrap(http.StatusUnprocessableEntity, err.Error(), err)
} }

View File

@ -223,7 +223,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusOK, wantStatus: http.StatusOK,
wantContentType: "text/html;charset=UTF-8", wantContentType: "text/html;charset=UTF-8",
wantBodyFormResponseRegexp: `<code id="manual-auth-code">(.+)</code>`, wantBodyFormResponseRegexp: `<code id="manual-auth-code">(.+)</code>`,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -262,7 +262,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusOK, wantStatus: http.StatusOK,
wantContentType: "text/html;charset=UTF-8", wantContentType: "text/html;charset=UTF-8",
wantBodyFormResponseRegexp: `<code id="manual-auth-code">(.+)</code>`, wantBodyFormResponseRegexp: `<code id="manual-auth-code">(.+)</code>`,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -290,7 +290,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -315,7 +315,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -339,7 +339,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -373,7 +373,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusOK, wantStatus: http.StatusOK,
wantContentType: "text/html;charset=UTF-8", wantContentType: "text/html;charset=UTF-8",
wantBodyFormResponseRegexp: `<code id="manual-auth-code">(.+)</code>`, wantBodyFormResponseRegexp: `<code id="manual-auth-code">(.+)</code>`,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamRequestedScopes: []string{"openid"}, wantDownstreamRequestedScopes: []string{"openid"},
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
@ -398,7 +398,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -437,7 +437,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenUsername: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenGroups: []string{}, wantDownstreamIDTokenGroups: []string{},
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -467,7 +467,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: "joe@whitehouse.gov", wantDownstreamIDTokenUsername: "joe@whitehouse.gov",
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -499,7 +499,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: "joe@whitehouse.gov", wantDownstreamIDTokenUsername: "joe@whitehouse.gov",
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -532,7 +532,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, // succeed despite `email_verified=false` because we're not using the email claim for anything wantStatus: http.StatusSeeOther, // succeed despite `email_verified=false` because we're not using the email claim for anything
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: "joe", wantDownstreamIDTokenUsername: "joe",
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -667,7 +667,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamSubject, wantDownstreamIDTokenUsername: oidcUpstreamSubject,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -697,7 +697,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: []string{"notAnArrayGroup1 notAnArrayGroup2"}, wantDownstreamIDTokenGroups: []string{"notAnArrayGroup1 notAnArrayGroup2"},
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -727,7 +727,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: []string{"group1", "group2"}, wantDownstreamIDTokenGroups: []string{"group1", "group2"},
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -761,7 +761,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: "", // username scope was not requested wantDownstreamIDTokenUsername: "", // username scope was not requested
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: []string{"openid", "groups", "offline_access"}, wantDownstreamRequestedScopes: []string{"openid", "groups", "offline_access"},
@ -791,7 +791,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: nil, // groups scope was not requested wantDownstreamIDTokenGroups: nil, // groups scope was not requested
wantDownstreamRequestedScopes: []string{"openid", "username", "offline_access"}, wantDownstreamRequestedScopes: []string{"openid", "username", "offline_access"},
@ -834,7 +834,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: "", // username scope was not requested wantDownstreamIDTokenUsername: "", // username scope was not requested
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamRequestedScopes: []string{"openid", "groups", "offline_access"}, wantDownstreamRequestedScopes: []string{"openid", "groups", "offline_access"},
@ -877,7 +877,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: nil, // groups scope was not requested wantDownstreamIDTokenGroups: nil, // groups scope was not requested
wantDownstreamRequestedScopes: []string{"openid", "username", "offline_access"}, wantDownstreamRequestedScopes: []string{"openid", "username", "offline_access"},
@ -902,7 +902,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: transformationUsernamePrefix + oidcUpstreamUsername, wantDownstreamIDTokenUsername: transformationUsernamePrefix + oidcUpstreamUsername,
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, oidcUpstreamGroupMembership), wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, oidcUpstreamGroupMembership),
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -1158,7 +1158,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamRequestedScopes: []string{"profile", "email", "username", "groups"}, wantDownstreamRequestedScopes: []string{"profile", "email", "username", "groups"},
wantDownstreamGrantedScopes: []string{"username", "groups"}, wantDownstreamGrantedScopes: []string{"username", "groups"},
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
@ -1188,7 +1188,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamRequestedScopes: []string{"profile", "email"}, wantDownstreamRequestedScopes: []string{"profile", "email"},
// username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility // username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility
wantDownstreamGrantedScopes: []string{"username", "groups"}, wantDownstreamGrantedScopes: []string{"username", "groups"},
@ -1217,7 +1217,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username\+groups&state=` + happyDownstreamState,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "username", "groups"}, wantDownstreamRequestedScopes: []string{"openid", "offline_access", "username", "groups"},
wantDownstreamGrantedScopes: []string{"openid", "offline_access", "username", "groups"}, wantDownstreamGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
@ -1315,7 +1315,7 @@ func TestCallbackEndpoint(t *testing.T) {
wantStatus: http.StatusSeeOther, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
wantDownstreamGrantedScopes: happyDownstreamScopesGranted, wantDownstreamGrantedScopes: happyDownstreamScopesGranted,

View File

@ -63,7 +63,9 @@ func NewPostHandler(issuerURL string, upstreamIDPs federationdomainproviders.Fed
} }
// Attempt to authenticate the user with the upstream IDP. // Attempt to authenticate the user with the upstream IDP.
authenticateResponse, authenticated, err := ldapUpstream.Provider.AuthenticateUser(r.Context(), submittedUsername, submittedPassword, authorizeRequester.GetGrantedScopes()) authenticateResponse, authenticated, err := ldapUpstream.Provider.AuthenticateUser(
r.Context(), submittedUsername, submittedPassword, authorizeRequester.GetGrantedScopes(),
)
if err != nil { if err != nil {
plog.WarningErr("unexpected error during upstream LDAP authentication", err, "upstreamName", ldapUpstream.Provider.GetName()) plog.WarningErr("unexpected error during upstream LDAP authentication", err, "upstreamName", ldapUpstream.Provider.GetName())
// There was some problem during authentication with the upstream, aside from bad username/password. // There was some problem during authentication with the upstream, aside from bad username/password.
@ -80,11 +82,15 @@ func NewPostHandler(issuerURL string, upstreamIDPs federationdomainproviders.Fed
// Now the upstream IDP has authenticated the user, so now we're back into the regular OIDC authcode flow steps. // Now the upstream IDP has authenticated the user, so now we're back into the regular OIDC authcode flow steps.
// Both success and error responses from this point onwards should look like the usual fosite redirect // Both success and error responses from this point onwards should look like the usual fosite redirect
// responses, and a happy redirect response will include a downstream authcode. // responses, and a happy redirect response will include a downstream authcode.
subject := downstreamsession.DownstreamSubjectFromUpstreamLDAP(ldapUpstream.Provider, authenticateResponse) subject := downstreamsession.DownstreamSubjectFromUpstreamLDAP(
ldapUpstream.Provider, authenticateResponse, ldapUpstream.DisplayName,
)
upstreamUsername := authenticateResponse.User.GetName() upstreamUsername := authenticateResponse.User.GetName()
upstreamGroups := authenticateResponse.User.GetGroups() upstreamGroups := authenticateResponse.User.GetGroups()
username, groups, err := downstreamsession.ApplyIdentityTransformations(r.Context(), ldapUpstream.Transforms, upstreamUsername, upstreamGroups) username, groups, err := downstreamsession.ApplyIdentityTransformations(
r.Context(), ldapUpstream.Transforms, upstreamUsername, upstreamGroups,
)
if err != nil { if err != nil {
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester, oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
fosite.ErrAccessDenied.WithHintf("Reason: %s.", err.Error()), false, fosite.ErrAccessDenied.WithHintf("Reason: %s.", err.Error()), false,
@ -92,7 +98,8 @@ func NewPostHandler(issuerURL string, upstreamIDPs federationdomainproviders.Fed
return nil return nil
} }
customSessionData := downstreamsession.MakeDownstreamLDAPOrADCustomSessionData(ldapUpstream.Provider, ldapUpstream.SessionProviderType, authenticateResponse, username, upstreamUsername, upstreamGroups) customSessionData := downstreamsession.MakeDownstreamLDAPOrADCustomSessionData(
ldapUpstream.Provider, ldapUpstream.SessionProviderType, authenticateResponse, username, upstreamUsername, upstreamGroups)
openIDSession := downstreamsession.MakeDownstreamSession(subject, username, groups, openIDSession := downstreamsession.MakeDownstreamSession(subject, username, groups,
authorizeRequester.GetGrantedScopes(), authorizeRequester.GetClient().GetID(), customSessionData, map[string]interface{}{}) authorizeRequester.GetGrantedScopes(), authorizeRequester.GetClient().GetID(), customSessionData, map[string]interface{}{})
oidc.PerformAuthcodeRedirect(r, w, oauthHelper, authorizeRequester, openIDSession, false) oidc.PerformAuthcodeRedirect(r, w, oauthHelper, authorizeRequester, openIDSession, false)

View File

@ -338,7 +338,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -361,7 +361,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: transformationUsernamePrefix + happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: transformationUsernamePrefix + happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, happyLDAPGroups), wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, happyLDAPGroups),
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -390,7 +390,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -413,7 +413,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -436,7 +436,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: transformationUsernamePrefix + happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: transformationUsernamePrefix + happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, happyLDAPGroups), wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, happyLDAPGroups),
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -465,7 +465,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -490,7 +490,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyFormResponseRegexp: `(?s)<html.*<script>.*To finish logging in, paste this authorization code` + wantBodyFormResponseRegexp: `(?s)<html.*<script>.*To finish logging in, paste this authorization code` +
`.*<form>.*<code id="manual-auth-code">(.+)</code>.*</html>`, // "(?s)" means match "." across newlines `.*<form>.*<code id="manual-auth-code">(.+)</code>.*</html>`, // "(?s)" means match "." across newlines
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -515,7 +515,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: "http://127.0.0.1:4242/callback" + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: "http://127.0.0.1:4242/callback" + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyDownstreamState,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -541,7 +541,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: "http://127.0.0.1:4242/callback" + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: "http://127.0.0.1:4242/callback" + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyDownstreamState,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: happyDownstreamScopesRequested, wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
@ -567,7 +567,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantBodyString: "", wantBodyString: "",
// username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility // username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+pinniped%3Arequest-audience\+username\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+pinniped%3Arequest-audience\+username\+groups&state=` + happyDownstreamState,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "pinniped:request-audience"}, wantDownstreamRequestedScopes: []string{"openid", "offline_access", "pinniped:request-audience"},
@ -593,7 +593,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+pinniped%3Arequest-audience&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+pinniped%3Arequest-audience&state=` + happyDownstreamState,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: "", // username scope was not requested, so there should be no username in the ID token wantDownstreamIDTokenUsername: "", // username scope was not requested, so there should be no username in the ID token
wantDownstreamIDTokenGroups: []string{}, // groups scope was not requested, so there should be no groups in the ID token wantDownstreamIDTokenGroups: []string{}, // groups scope was not requested, so there should be no groups in the ID token
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "pinniped:request-audience"}, wantDownstreamRequestedScopes: []string{"openid", "offline_access", "pinniped:request-audience"},
@ -627,7 +627,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: "", // username scope was not requested, so there should be no username in the ID token wantDownstreamIDTokenUsername: "", // username scope was not requested, so there should be no username in the ID token
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "groups"}, wantDownstreamRequestedScopes: []string{"openid", "offline_access", "groups"},
@ -661,7 +661,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyString: "", wantBodyString: "",
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: []string{}, // groups scope was not requested, so there should be no groups in the ID token wantDownstreamIDTokenGroups: []string{}, // groups scope was not requested, so there should be no groups in the ID token
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "username"}, wantDownstreamRequestedScopes: []string{"openid", "offline_access", "username"},
@ -691,7 +691,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantBodyString: "", wantBodyString: "",
// username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility // username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: []string{"email"}, // only email was requested wantDownstreamRequestedScopes: []string{"email"}, // only email was requested
@ -719,7 +719,7 @@ func TestPostLoginEndpoint(t *testing.T) {
wantBodyString: "", wantBodyString: "",
// username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility // username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyDownstreamState,
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID, wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
wantDownstreamIDTokenGroups: happyLDAPGroups, wantDownstreamIDTokenGroups: happyLDAPGroups,
wantDownstreamRequestedScopes: []string{"openid"}, wantDownstreamRequestedScopes: []string{"openid"},

View File

@ -394,7 +394,7 @@ func upstreamLDAPRefresh(
Groups: oldUntransformedGroups, Groups: oldUntransformedGroups,
AdditionalAttributes: additionalAttributes, AdditionalAttributes: additionalAttributes,
GrantedScopes: grantedScopes, GrantedScopes: grantedScopes,
}) }, p.DisplayName)
if err != nil { if err != nil {
return errUpstreamRefreshError().WithHint( return errUpstreamRefreshError().WithHint(
"Upstream refresh failed.").WithTrace(err). "Upstream refresh failed.").WithTrace(err).

View File

@ -122,5 +122,5 @@ type UpstreamLDAPIdentityProviderI interface {
authenticators.UserAuthenticator authenticators.UserAuthenticator
// PerformRefresh performs a refresh against the upstream LDAP identity provider // PerformRefresh performs a refresh against the upstream LDAP identity provider
PerformRefresh(ctx context.Context, storedRefreshAttributes RefreshAttributes) (groups []string, err error) PerformRefresh(ctx context.Context, storedRefreshAttributes RefreshAttributes, idpDisplayName string) (groups []string, err error)
} }

View File

@ -220,7 +220,7 @@ func (u *TestUpstreamLDAPIdentityProvider) GetURL() *url.URL {
return u.URL return u.URL
} }
func (u *TestUpstreamLDAPIdentityProvider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes) ([]string, error) { func (u *TestUpstreamLDAPIdentityProvider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes, idpDisplayName string) ([]string, error) {
if u.performRefreshArgs == nil { if u.performRefreshArgs == nil {
u.performRefreshArgs = make([]*PerformRefreshArgs, 0) u.performRefreshArgs = make([]*PerformRefreshArgs, 0)
} }

View File

@ -188,7 +188,7 @@ func closeAndLogError(conn Conn, doingWhat string) {
} }
} }
func (p *Provider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes) ([]string, error) { func (p *Provider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes, idpDisplayName string) ([]string, error) {
t := trace.FromContext(ctx).Nest("slow ldap refresh attempt", trace.Field{Key: "providerName", Value: p.GetName()}) t := trace.FromContext(ctx).Nest("slow ldap refresh attempt", trace.Field{Key: "providerName", Value: p.GetName()})
defer t.LogIfLong(500 * time.Millisecond) // to help users debug slow LDAP searches defer t.LogIfLong(500 * time.Millisecond) // to help users debug slow LDAP searches
userDN := storedRefreshAttributes.DN userDN := storedRefreshAttributes.DN
@ -237,7 +237,7 @@ func (p *Provider) PerformRefresh(ctx context.Context, storedRefreshAttributes u
if err != nil { if err != nil {
return nil, err return nil, err
} }
newSubject := downstreamsession.DownstreamLDAPSubject(newUID, *p.GetURL()) newSubject := downstreamsession.DownstreamLDAPSubject(newUID, *p.GetURL(), idpDisplayName)
if newSubject != storedRefreshAttributes.Subject { if newSubject != storedRefreshAttributes.Subject {
return nil, fmt.Errorf(`searching for user %q produced a different subject than the previous value. expected: %q, actual: %q`, userDN, storedRefreshAttributes.Subject, newSubject) return nil, fmt.Errorf(`searching for user %q produced a different subject than the previous value. expected: %q, actual: %q`, userDN, storedRefreshAttributes.Subject, newSubject)
} }

View File

@ -36,6 +36,7 @@ const (
testHost = "ldap.example.com:8443" testHost = "ldap.example.com:8443"
testBindUsername = "cn=some-bind-username,dc=pinniped,dc=dev" testBindUsername = "cn=some-bind-username,dc=pinniped,dc=dev"
testBindPassword = "some-bind-password" testBindPassword = "some-bind-password"
testUpstreamName = "some-upstream-idp-name"
testUpstreamUsername = "some-upstream-username" testUpstreamUsername = "some-upstream-username"
testUpstreamPassword = "some-upstream-password" testUpstreamPassword = "some-upstream-password"
testUserSearchBase = "some-upstream-user-base-dn" testUserSearchBase = "some-upstream-user-base-dn"
@ -2046,7 +2047,7 @@ func TestUpstreamRefresh(t *testing.T) {
}, nil).Times(1) }, nil).Times(1)
conn.EXPECT().Close().Times(1) conn.EXPECT().Close().Times(1)
}, },
wantErr: "searching for user \"some-upstream-user-dn\" produced a different subject than the previous value. expected: \"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&sub=c29tZS11cHN0cmVhbS11aWQtdmFsdWU\", actual: \"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&sub=d3JvbmctdWlk\"", wantErr: "searching for user \"some-upstream-user-dn\" produced a different subject than the previous value. expected: \"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&idpName=some-upstream-idp-name&sub=c29tZS11cHN0cmVhbS11aWQtdmFsdWU\", actual: \"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&idpName=some-upstream-idp-name&sub=d3JvbmctdWlk\"",
}, },
{ {
name: "search result has wrong username", name: "search result has wrong username",
@ -2279,14 +2280,17 @@ func TestUpstreamRefresh(t *testing.T) {
} }
initialPwdLastSetEncoded := base64.RawURLEncoding.EncodeToString([]byte("132801740800000000")) initialPwdLastSetEncoded := base64.RawURLEncoding.EncodeToString([]byte("132801740800000000"))
ldapProvider := New(*tt.providerConfig) ldapProvider := New(*tt.providerConfig)
subject := "ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&sub=c29tZS11cHN0cmVhbS11aWQtdmFsdWU" subject := fmt.Sprintf(
"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&idpName=%s&sub=c29tZS11cHN0cmVhbS11aWQtdmFsdWU",
testUpstreamName,
)
groups, err := ldapProvider.PerformRefresh(context.Background(), upstreamprovider.RefreshAttributes{ groups, err := ldapProvider.PerformRefresh(context.Background(), upstreamprovider.RefreshAttributes{
Username: testUserSearchResultUsernameAttributeValue, Username: testUserSearchResultUsernameAttributeValue,
Subject: subject, Subject: subject,
DN: tt.refreshUserDN, DN: tt.refreshUserDN,
AdditionalAttributes: map[string]string{pwdLastSetAttribute: initialPwdLastSetEncoded}, AdditionalAttributes: map[string]string{pwdLastSetAttribute: initialPwdLastSetEncoded},
GrantedScopes: tt.grantedScopes, GrantedScopes: tt.grantedScopes,
}) }, testUpstreamName)
if tt.wantErr != "" { if tt.wantErr != "" {
require.Error(t, err) require.Error(t, err)
require.Equal(t, tt.wantErr, err.Error()) require.Equal(t, tt.wantErr, err.Error())

View File

@ -162,6 +162,49 @@ func TestSupervisorLogin_Browser(t *testing.T) {
return ldapIDP, secret return ldapIDP, secret
} }
// The downstream ID token Subject should include the upstream user ID after the upstream issuer name
// and IDP display name.
expectedIDTokenSubjectRegexForUpstreamOIDC := "^" +
regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?idpName=test-upstream-oidc-idp-") + `[\w]+` +
regexp.QuoteMeta("&sub=") + ".+" +
"$"
// The downstream ID token Subject should be the Host URL, plus the user search base, plus the IDP display name,
// plus value pulled from the requested UserSearch.Attributes.UID attribute.
expectedIDTokenSubjectRegexForUpstreamLDAP := "^" +
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamLDAP.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)) +
regexp.QuoteMeta("&idpName=test-upstream-ldap-idp-") + `[\w]+` +
regexp.QuoteMeta("&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue))) +
"$"
// Some of the LDAP tests below use the StartTLS server and port.
expectedIDTokenSubjectRegexForUpstreamLDAPStartTLSHost := "^" +
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamLDAP.StartTLSOnlyHost+"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)) +
regexp.QuoteMeta("&idpName=test-upstream-ldap-idp-") + `[\w]+` +
regexp.QuoteMeta("&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue))) +
"$"
// The downstream ID token Subject should be in the the same format as LDAP above, but with AD-specific values.
expectedIDTokenSubjectRegexForUpstreamAD := "^" +
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)) +
regexp.QuoteMeta("&idpName=test-upstream-ad-idp-") + `[\w]+` +
regexp.QuoteMeta("&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue) +
"$"
// Sometimes the AD tests below use a different search base.
expectedIDTokenSubjectRegexForUpstreamADWithCustomUserSearchBase := "^" +
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.UserSearchBase)) +
regexp.QuoteMeta("&idpName=test-upstream-ad-idp-") + `[\w]+` +
regexp.QuoteMeta("&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue) +
"$"
// Sometimes the AD tests below create a user dynamically so we can't know the UID up front.
expectedIDTokenSubjectRegexForUpstreamADWithUnkownUID := "^" +
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)) +
regexp.QuoteMeta("&idpName=test-upstream-ad-idp-") + `[\w]+` +
regexp.QuoteMeta("&sub=") + ".+" +
"$"
// These tests attempt to exercise the entire login and refresh flow of the Supervisor for various cases. // These tests attempt to exercise the entire login and refresh flow of the Supervisor for various cases.
// They do not use the Pinniped CLI as the client, which allows them to exercise the Supervisor as an // They do not use the Pinniped CLI as the client, which allows them to exercise the Supervisor as an
// OIDC provider in ways that the CLI might not use. Similar tests exist using the CLI in e2e_test.go. // OIDC provider in ways that the CLI might not use. Similar tests exist using the CLI in e2e_test.go.
@ -275,8 +318,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
pinnipedSessionData := pinnipedSession.Custom pinnipedSessionData := pinnipedSession.Custom
pinnipedSessionData.OIDC.UpstreamIssuer = "wrong-issuer" pinnipedSessionData.OIDC.UpstreamIssuer = "wrong-issuer"
}, },
// the ID token Subject should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
// the ID token Username should include the upstream user ID after the upstream issuer name // the ID token Username should include the upstream user ID after the upstream issuer name
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
}, },
@ -299,7 +341,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
customSessionData := pinnipedSession.Custom customSessionData := pinnipedSession.Custom
customSessionData.Username = "some-incorrect-username" customSessionData.Username = "some-incorrect-username"
}, },
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+", wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups, wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string { editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string {
@ -346,7 +388,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
customSessionData := pinnipedSession.Custom customSessionData := pinnipedSession.Custom
customSessionData.Username = "some-incorrect-username" customSessionData.Username = "some-incorrect-username"
}, },
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+", wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups, wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
}, },
@ -375,8 +417,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
require.NotEmpty(t, customSessionData.OIDC.UpstreamRefreshToken) require.NotEmpty(t, customSessionData.OIDC.UpstreamRefreshToken)
customSessionData.OIDC.UpstreamRefreshToken = "invalid-updated-refresh-token" customSessionData.OIDC.UpstreamRefreshToken = "invalid-updated-refresh-token"
}, },
// the ID token Subject should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
// the ID token Username should include the upstream user ID after the upstream issuer name // the ID token Username should include the upstream user ID after the upstream issuer name
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
}, },
@ -406,8 +447,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
false, false,
) )
}, },
// the ID token Subject should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
// the ID token Username should include the upstream user ID after the upstream issuer name // the ID token Username should include the upstream user ID after the upstream issuer name
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
wantDownstreamIDTokenAdditionalClaims: wantGroupsInAdditionalClaimsIfGroupsExist(map[string]interface{}{ wantDownstreamIDTokenAdditionalClaims: wantGroupsInAdditionalClaimsIfGroupsExist(map[string]interface{}{
@ -432,8 +472,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
return testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseReady).Name return testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseReady).Name
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
// the ID token Subject should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
// the ID token Username should include the upstream user ID after the upstream issuer name // the ID token Username should include the upstream user ID after the upstream issuer name
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
wantDownstreamIDTokenAdditionalClaims: wantGroupsInAdditionalClaimsIfGroupsExist(map[string]interface{}{ wantDownstreamIDTokenAdditionalClaims: wantGroupsInAdditionalClaimsIfGroupsExist(map[string]interface{}{
@ -472,12 +511,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
fositeSessionData := pinnipedSession.Fosite fositeSessionData := pinnipedSession.Fosite
fositeSessionData.Claims.Subject = "not-right" fositeSessionData.Claims.Subject = "not-right"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -504,12 +538,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
false, false,
) )
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -534,12 +563,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
false, false,
) )
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -563,7 +587,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
downstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access"}, downstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access"},
wantDownstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access", "username", "groups"}, wantDownstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access", "username", "groups"},
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+", wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups, wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
}, },
@ -580,12 +604,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -635,12 +654,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAPWithBadCredentialsAndThenGoodCredentials, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAPWithBadCredentialsAndThenGoodCredentials,
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -684,12 +698,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
fositeSessionData := pinnipedSession.Fosite fositeSessionData := pinnipedSession.Fosite
fositeSessionData.Claims.Subject = "not-right" fositeSessionData.Claims.Subject = "not-right"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -739,12 +748,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
fositeSessionData := pinnipedSession.Fosite fositeSessionData := pinnipedSession.Fosite
fositeSessionData.Claims.Subject = "not-right" fositeSessionData.Claims.Subject = "not-right"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -778,12 +782,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
require.NotEmpty(t, customSessionData.LDAP.UserDN) require.NotEmpty(t, customSessionData.LDAP.UserDN)
customSessionData.Username = "not-the-same" customSessionData.Username = "not-the-same"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAPStartTLSHost,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.StartTLSOnlyHost+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserDN) + "$" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserDN) + "$" },
wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs, wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs,
@ -852,12 +851,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
require.NotEmpty(t, customSessionData.LDAP.UserDN) require.NotEmpty(t, customSessionData.LDAP.UserDN)
customSessionData.LDAP.UserDN = "cn=not-a-user,dc=pinniped,dc=dev" customSessionData.LDAP.UserDN = "cn=not-a-user,dc=pinniped,dc=dev"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -923,12 +917,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
require.NotEmpty(t, customSessionData.LDAP.UserDN) require.NotEmpty(t, customSessionData.LDAP.UserDN)
customSessionData.LDAP.UserDN = "cn=not-a-user,dc=pinniped,dc=dev" customSessionData.LDAP.UserDN = "cn=not-a-user,dc=pinniped,dc=dev"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -957,12 +946,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN) require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
customSessionData.Username = "not-the-same" customSessionData.Username = "not-the-same"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
@ -1007,12 +991,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
fositeSessionData := pinnipedSession.Fosite fositeSessionData := pinnipedSession.Fosite
fositeSessionData.Claims.Subject = "not-right" fositeSessionData.Claims.Subject = "not-right"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithCustomUserSearchBase,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.UserSearchBase)+
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeValue) + "$"
@ -1055,12 +1034,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
false, false,
) )
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithCustomUserSearchBase,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.UserSearchBase)+
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeValue) + "$"
@ -1112,12 +1086,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN) require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
customSessionData.ActiveDirectory.UserDN = "cn=not-a-user,dc=pinniped,dc=dev" customSessionData.ActiveDirectory.UserDN = "cn=not-a-user,dc=pinniped,dc=dev"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
@ -1184,12 +1153,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN) require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
customSessionData.ActiveDirectory.UserDN = "cn=not-a-user,dc=pinniped,dc=dev" customSessionData.ActiveDirectory.UserDN = "cn=not-a-user,dc=pinniped,dc=dev"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
@ -1218,8 +1182,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
breakRefreshSessionData: func(t *testing.T, sessionData *psession.PinnipedSession, _, username string) { breakRefreshSessionData: func(t *testing.T, sessionData *psession.PinnipedSession, _, username string) {
testlib.ChangeADTestUserPassword(t, env, username) testlib.ChangeADTestUserPassword(t, env, username)
}, },
// we can't know the subject ahead of time because we created a new user and don't know their uid, wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithUnkownUID,
// so skip wantDownstreamIDTokenSubjectToMatch
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(username string) string { wantDownstreamIDTokenUsernameToMatch: func(username string) string {
return "^" + regexp.QuoteMeta(username+"@"+env.SupervisorUpstreamActiveDirectory.Domain) + "$" return "^" + regexp.QuoteMeta(username+"@"+env.SupervisorUpstreamActiveDirectory.Domain) + "$"
@ -1248,8 +1211,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
breakRefreshSessionData: func(t *testing.T, sessionData *psession.PinnipedSession, _, username string) { breakRefreshSessionData: func(t *testing.T, sessionData *psession.PinnipedSession, _, username string) {
testlib.DeactivateADTestUser(t, env, username) testlib.DeactivateADTestUser(t, env, username)
}, },
// we can't know the subject ahead of time because we created a new user and don't know their uid, wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithUnkownUID,
// so skip wantDownstreamIDTokenSubjectToMatch
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(username string) string { wantDownstreamIDTokenUsernameToMatch: func(username string) string {
return "^" + regexp.QuoteMeta(username+"@"+env.SupervisorUpstreamActiveDirectory.Domain) + "$" return "^" + regexp.QuoteMeta(username+"@"+env.SupervisorUpstreamActiveDirectory.Domain) + "$"
@ -1278,8 +1240,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
breakRefreshSessionData: func(t *testing.T, sessionData *psession.PinnipedSession, _, username string) { breakRefreshSessionData: func(t *testing.T, sessionData *psession.PinnipedSession, _, username string) {
testlib.LockADTestUser(t, env, username) testlib.LockADTestUser(t, env, username)
}, },
// we can't know the subject ahead of time because we created a new user and don't know their uid, wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithUnkownUID,
// so skip wantDownstreamIDTokenSubjectToMatch
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(username string) string { wantDownstreamIDTokenUsernameToMatch: func(username string) string {
return "^" + regexp.QuoteMeta(username+"@"+env.SupervisorUpstreamActiveDirectory.Domain) + "$" return "^" + regexp.QuoteMeta(username+"@"+env.SupervisorUpstreamActiveDirectory.Domain) + "$"
@ -1337,12 +1298,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
time.Sleep(10 * time.Second) // wait for controllers to pick up the change time.Sleep(10 * time.Second) // wait for controllers to pick up the change
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -1381,12 +1337,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
time.Sleep(10 * time.Second) // wait for controllers to pick up the change time.Sleep(10 * time.Second) // wait for controllers to pick up the change
return []string{} return []string{}
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -1401,8 +1352,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
requestTokenExchangeAud: "contains-disallowed-substring.pinniped.dev-something", // .pinniped.dev substring is not allowed requestTokenExchangeAud: "contains-disallowed-substring.pinniped.dev-something", // .pinniped.dev substring is not allowed
// the ID token Subject should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
// the ID token Username should include the upstream user ID after the upstream issuer name // the ID token Username should include the upstream user ID after the upstream issuer name
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
wantTokenExchangeResponse: func(t *testing.T, status int, body string) { wantTokenExchangeResponse: func(t *testing.T, status int, body string) {
@ -1422,8 +1372,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
requestTokenExchangeAud: "client.oauth.pinniped.dev-client-name", // OIDC dynamic client name is not allowed requestTokenExchangeAud: "client.oauth.pinniped.dev-client-name", // OIDC dynamic client name is not allowed
// the ID token Subject should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
// the ID token Username should include the upstream user ID after the upstream issuer name // the ID token Username should include the upstream user ID after the upstream issuer name
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
wantTokenExchangeResponse: func(t *testing.T, status int, body string) { wantTokenExchangeResponse: func(t *testing.T, status int, body string) {
@ -1443,8 +1392,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
requestTokenExchangeAud: "pinniped-cli", // pinniped-cli is not allowed requestTokenExchangeAud: "pinniped-cli", // pinniped-cli is not allowed
// the ID token Subject should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
// the ID token Username should include the upstream user ID after the upstream issuer name // the ID token Username should include the upstream user ID after the upstream issuer name
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
wantTokenExchangeResponse: func(t *testing.T, status int, body string) { wantTokenExchangeResponse: func(t *testing.T, status int, body string) {
@ -1478,8 +1426,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
}, configv1alpha1.OIDCClientPhaseReady) }, configv1alpha1.OIDCClientPhaseReady)
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
// the ID token Subject should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups, wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
}, },
@ -1511,8 +1458,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
}, configv1alpha1.OIDCClientPhaseReady) }, configv1alpha1.OIDCClientPhaseReady)
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
// the ID token Subject should include the upstream user ID after the upstream issuer name wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" }, wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups, wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
wantDownstreamIDTokenAdditionalClaims: wantGroupsInAdditionalClaimsIfGroupsExist(map[string]interface{}{ wantDownstreamIDTokenAdditionalClaims: wantGroupsInAdditionalClaimsIfGroupsExist(map[string]interface{}{
@ -1540,12 +1486,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -1693,12 +1634,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
}, },
downstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access", "username"}, // do not request (or expect) groups downstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access", "username"}, // do not request (or expect) groups
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -1726,12 +1662,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
}, },
downstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access", "groups"}, // do not request (or expect) username downstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access", "groups"}, // do not request (or expect) username
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "" // username should not exist as a claim since we did not request it return "" // username should not exist as a claim since we did not request it
}, },
@ -1765,12 +1696,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
}, },
downstreamScopes: []string{"openid", "offline_access"}, // do not request (or expect) pinniped:request-audience or username or groups downstreamScopes: []string{"openid", "offline_access"}, // do not request (or expect) pinniped:request-audience or username or groups
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
) + "$",
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "" // username should not exist as a claim since we did not request it return "" // username should not exist as a claim since we did not request it
}, },
@ -1803,12 +1729,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
env.SupervisorUpstreamActiveDirectory.TestUserPassword // password to present to server during login env.SupervisorUpstreamActiveDirectory.TestUserPassword // password to present to server during login
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$" return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
@ -1879,7 +1800,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
}, displayName }, displayName
}, },
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+", wantDownstreamIDTokenSubjectToMatch: "^" +
regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer) +
regexp.QuoteMeta("?idpName="+url.QueryEscape("my oidc idp")) +
regexp.QuoteMeta("&sub=") + ".+" +
"$",
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamOIDC.Username) + "$" return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamOIDC.Username) + "$"
}, },
@ -1958,12 +1883,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
// compared to what they chose during the initial login, by changing what they decided during the initial login. // compared to what they chose during the initial login, by changing what they decided during the initial login.
customSessionData.Username = "some-different-downstream-username" customSessionData.Username = "some-different-downstream-username"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: "^" +
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta( regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamLDAP.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)) +
"ldaps://"+env.SupervisorUpstreamLDAP.Host+ regexp.QuoteMeta("&idpName="+url.QueryEscape("my ldap idp")) +
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+ regexp.QuoteMeta("&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue))) +
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)), "$",
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute and then transformed // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute and then transformed
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -2023,12 +1947,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
// compared to what they chose during the initial login, by changing what they decided during the initial login. // compared to what they chose during the initial login, by changing what they decided during the initial login.
customSessionData.Username = "some-different-downstream-username" customSessionData.Username = "some-different-downstream-username"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: "^" +
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta( regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamLDAP.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)) +
"ldaps://"+env.SupervisorUpstreamLDAP.Host+ regexp.QuoteMeta("&idpName="+url.QueryEscape("my ldap idp")) +
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+ regexp.QuoteMeta("&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue))) +
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)), "$",
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute and then transformed // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute and then transformed
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
@ -2093,12 +2016,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
// compared to what they chose during the initial login, by changing what they decided during the initial login. // compared to what they chose during the initial login, by changing what they decided during the initial login.
customSessionData.Username = "some-different-downstream-username" customSessionData.Username = "some-different-downstream-username"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: "^" +
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta( regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)) +
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+ regexp.QuoteMeta("&idpName="+url.QueryEscape("my ad idp")) +
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+ regexp.QuoteMeta("&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue) +
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue, "$",
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$" return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
@ -2160,12 +2082,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
// compared to what they chose during the initial login, by changing what they decided during the initial login. // compared to what they chose during the initial login, by changing what they decided during the initial login.
customSessionData.Username = "some-different-downstream-username" customSessionData.Username = "some-different-downstream-username"
}, },
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: "^" +
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta( regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)) +
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+ regexp.QuoteMeta("&idpName="+url.QueryEscape("my ad idp")) +
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+ regexp.QuoteMeta("&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue) +
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue, "$",
) + "$",
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$" return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"