Add integration test for failed client auth for a dynamic client
This commit is contained in:
parent
e42f5488fa
commit
c12ffad29e
@ -224,10 +224,14 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
|||||||
|
|
||||||
// Want the authorization endpoint to redirect to the callback with this error type.
|
// Want the authorization endpoint to redirect to the callback with this error type.
|
||||||
// The rest of the flow will be skipped since the initial authorization failed.
|
// The rest of the flow will be skipped since the initial authorization failed.
|
||||||
wantErrorType string
|
wantAuthorizationErrorType string
|
||||||
// Want the authorization endpoint to redirect to the callback with this error description.
|
// Want the authorization endpoint to redirect to the callback with this error description.
|
||||||
// Should be used with wantErrorType.
|
// Should be used with wantAuthorizationErrorType.
|
||||||
wantErrorDescription string
|
wantAuthorizationErrorDescription string
|
||||||
|
|
||||||
|
// Optionally want to the authcode exchange at the token endpoint to fail. The rest of the flow will be
|
||||||
|
// skipped since the authcode exchange failed.
|
||||||
|
wantAuthcodeExchangeError string
|
||||||
|
|
||||||
// Optionally make all required assertions about the response of the RFC8693 token exchange for
|
// Optionally make all required assertions about the response of the RFC8693 token exchange for
|
||||||
// the cluster-scoped ID token, given the http response status and response body from the token endpoint.
|
// the cluster-scoped ID token, given the http response status and response body from the token endpoint.
|
||||||
@ -674,8 +678,8 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
wantErrorDescription: "The resource owner or authorization server denied the request. Username/password not accepted by LDAP provider.",
|
wantAuthorizationErrorDescription: "The resource owner or authorization server denied the request. Username/password not accepted by LDAP provider.",
|
||||||
wantErrorType: "access_denied",
|
wantAuthorizationErrorType: "access_denied",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ldap login still works after updating bind secret",
|
name: "ldap login still works after updating bind secret",
|
||||||
@ -1126,8 +1130,8 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
breakRefreshSessionData: nil,
|
breakRefreshSessionData: nil,
|
||||||
wantErrorDescription: "The resource owner or authorization server denied the request. Username/password not accepted by LDAP provider.",
|
wantAuthorizationErrorDescription: "The resource owner or authorization server denied the request. Username/password not accepted by LDAP provider.",
|
||||||
wantErrorType: "access_denied",
|
wantAuthorizationErrorType: "access_denied",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ldap refresh fails when username changes from email as username to dn as username",
|
name: "ldap refresh fails when username changes from email as username to dn as username",
|
||||||
@ -1366,6 +1370,30 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames,
|
wantDownstreamIDTokenGroups: env.SupervisorUpstreamActiveDirectory.TestUserIndirectGroupsSAMAccountPlusDomainNames,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ldap upstream with downstream dynamic client, failed client authentication",
|
||||||
|
maybeSkip: skipLDAPTests,
|
||||||
|
createIDP: func(t *testing.T) string {
|
||||||
|
idp, _ := createLDAPIdentityProvider(t, nil)
|
||||||
|
return idp.Name
|
||||||
|
},
|
||||||
|
createOIDCClient: func(t *testing.T, callbackURL string) (string, string) {
|
||||||
|
clientID, _ := testlib.CreateOIDCClient(t, configv1alpha1.OIDCClientSpec{
|
||||||
|
AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)},
|
||||||
|
AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"},
|
||||||
|
AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"},
|
||||||
|
}, configv1alpha1.PhaseReady)
|
||||||
|
return clientID, "wrong-client-secret"
|
||||||
|
},
|
||||||
|
testUser: func(t *testing.T) (string, string) {
|
||||||
|
// return the username and password of the existing user that we want to use for this test
|
||||||
|
return env.SupervisorUpstreamLDAP.TestUserMailAttributeValue, // username to present to server during login
|
||||||
|
env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login
|
||||||
|
},
|
||||||
|
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
|
||||||
|
wantAuthcodeExchangeError: "oauth2: cannot fetch token: 401 Unauthorized\n" +
|
||||||
|
`Response: {"error":"invalid_client","error_description":"Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)."}`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -1373,7 +1401,8 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tt.maybeSkip(t)
|
tt.maybeSkip(t)
|
||||||
|
|
||||||
testSupervisorLogin(t,
|
testSupervisorLogin(
|
||||||
|
t,
|
||||||
tt.createIDP,
|
tt.createIDP,
|
||||||
tt.requestAuthorization,
|
tt.requestAuthorization,
|
||||||
tt.editRefreshSessionDataWithoutBreaking,
|
tt.editRefreshSessionDataWithoutBreaking,
|
||||||
@ -1386,8 +1415,9 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
|||||||
tt.wantDownstreamIDTokenSubjectToMatch,
|
tt.wantDownstreamIDTokenSubjectToMatch,
|
||||||
tt.wantDownstreamIDTokenUsernameToMatch,
|
tt.wantDownstreamIDTokenUsernameToMatch,
|
||||||
tt.wantDownstreamIDTokenGroups,
|
tt.wantDownstreamIDTokenGroups,
|
||||||
tt.wantErrorDescription,
|
tt.wantAuthorizationErrorType,
|
||||||
tt.wantErrorType,
|
tt.wantAuthorizationErrorDescription,
|
||||||
|
tt.wantAuthcodeExchangeError,
|
||||||
tt.wantTokenExchangeResponse,
|
tt.wantTokenExchangeResponse,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -1516,8 +1546,8 @@ func testSupervisorLogin(
|
|||||||
t *testing.T,
|
t *testing.T,
|
||||||
createIDP func(t *testing.T) string,
|
createIDP func(t *testing.T) string,
|
||||||
requestAuthorization func(t *testing.T, downstreamIssuer string, downstreamAuthorizeURL string, downstreamCallbackURL string, username string, password string, httpClient *http.Client),
|
requestAuthorization func(t *testing.T, downstreamIssuer string, downstreamAuthorizeURL string, downstreamCallbackURL string, username string, password string, httpClient *http.Client),
|
||||||
editRefreshSessionDataWithoutBreaking func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName, username string) []string,
|
editRefreshSessionDataWithoutBreaking func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName string, username string) []string,
|
||||||
breakRefreshSessionData func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName, username string),
|
breakRefreshSessionData func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName string, username string),
|
||||||
testUser func(t *testing.T) (string, string),
|
testUser func(t *testing.T) (string, string),
|
||||||
createOIDCClient func(t *testing.T, callbackURL string) (string, string),
|
createOIDCClient func(t *testing.T, callbackURL string) (string, string),
|
||||||
downstreamScopes []string,
|
downstreamScopes []string,
|
||||||
@ -1526,8 +1556,9 @@ func testSupervisorLogin(
|
|||||||
wantDownstreamIDTokenSubjectToMatch string,
|
wantDownstreamIDTokenSubjectToMatch string,
|
||||||
wantDownstreamIDTokenUsernameToMatch func(username string) string,
|
wantDownstreamIDTokenUsernameToMatch func(username string) string,
|
||||||
wantDownstreamIDTokenGroups []string,
|
wantDownstreamIDTokenGroups []string,
|
||||||
wantErrorDescription string,
|
wantAuthorizationErrorType string,
|
||||||
wantErrorType string,
|
wantAuthorizationErrorDescription string,
|
||||||
|
wantAuthcodeExchangeError string,
|
||||||
wantTokenExchangeResponse func(t *testing.T, status int, body string),
|
wantTokenExchangeResponse func(t *testing.T, status int, body string),
|
||||||
) {
|
) {
|
||||||
env := testlib.IntegrationEnv(t)
|
env := testlib.IntegrationEnv(t)
|
||||||
@ -1693,7 +1724,16 @@ func testSupervisorLogin(
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Logf("got callback request: %s", testlib.MaskTokens(callback.URL.String()))
|
t.Logf("got callback request: %s", testlib.MaskTokens(callback.URL.String()))
|
||||||
if wantErrorType == "" { // nolint:nestif
|
|
||||||
|
if wantAuthorizationErrorType != "" {
|
||||||
|
errorDescription := callback.URL.Query().Get("error_description")
|
||||||
|
errorType := callback.URL.Query().Get("error")
|
||||||
|
require.Equal(t, errorDescription, wantAuthorizationErrorDescription)
|
||||||
|
require.Equal(t, errorType, wantAuthorizationErrorType)
|
||||||
|
// The authorization has failed, so can't continue the login flow, making this the end of the test case.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
require.Equal(t, stateParam.String(), callback.URL.Query().Get("state"))
|
require.Equal(t, stateParam.String(), callback.URL.Query().Get("state"))
|
||||||
require.ElementsMatch(t, downstreamScopes, strings.Split(callback.URL.Query().Get("scope"), " "))
|
require.ElementsMatch(t, downstreamScopes, strings.Split(callback.URL.Query().Get("scope"), " "))
|
||||||
authcode := callback.URL.Query().Get("code")
|
authcode := callback.URL.Query().Get("code")
|
||||||
@ -1704,8 +1744,13 @@ func testSupervisorLogin(
|
|||||||
|
|
||||||
// Call the token endpoint to get tokens.
|
// Call the token endpoint to get tokens.
|
||||||
tokenResponse, err := downstreamOAuth2Config.Exchange(oidcHTTPClientContext, authcode, pkceParam.Verifier())
|
tokenResponse, err := downstreamOAuth2Config.Exchange(oidcHTTPClientContext, authcode, pkceParam.Verifier())
|
||||||
|
if wantAuthcodeExchangeError != "" {
|
||||||
|
require.EqualError(t, err, wantAuthcodeExchangeError)
|
||||||
|
// The authcode exchange has failed, so can't continue the login flow, making this the end of the test case.
|
||||||
|
return
|
||||||
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "username"}
|
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "username"}
|
||||||
if slices.Contains(downstreamScopes, "groups") {
|
if slices.Contains(downstreamScopes, "groups") {
|
||||||
expectedIDTokenClaims = append(expectedIDTokenClaims, "groups")
|
expectedIDTokenClaims = append(expectedIDTokenClaims, "groups")
|
||||||
@ -1798,12 +1843,6 @@ func testSupervisorLogin(
|
|||||||
err.Error(),
|
err.Error(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
errorDescription := callback.URL.Query().Get("error_description")
|
|
||||||
errorType := callback.URL.Query().Get("error")
|
|
||||||
require.Equal(t, errorDescription, wantErrorDescription)
|
|
||||||
require.Equal(t, errorType, wantErrorType)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFositeDataSignature returns the signature of the provided data. The provided data could be an auth code, access
|
// getFositeDataSignature returns the signature of the provided data. The provided data could be an auth code, access
|
||||||
|
Loading…
Reference in New Issue
Block a user