Add check for grant type in tokenexchangehandler,
- also started writing a test for the tokenexchangehandler, skipping for now Signed-off-by: Ryan Richard <rrichard@vmware.com>
This commit is contained in:
parent
ef3f837800
commit
f103c02408
@ -27,7 +27,7 @@ func TestNullStorage_GetClient(t *testing.T) {
|
||||
Public: true,
|
||||
RedirectURIs: []string{"http://127.0.0.1/callback"},
|
||||
ResponseTypes: []string{"code"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
Scopes: []string{"openid", "offline_access", "profile", "email"},
|
||||
},
|
||||
TokenEndpointAuthMethod: "none",
|
||||
|
@ -84,8 +84,8 @@ func PinnipedCLIOIDCClient() *fosite.DefaultOpenIDConnectClient {
|
||||
Public: true,
|
||||
RedirectURIs: []string{"http://127.0.0.1/callback"},
|
||||
ResponseTypes: []string{"code"},
|
||||
GrantTypes: []string{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
Scopes: []string{coreosoidc.ScopeOpenID, coreosoidc.ScopeOfflineAccess, "profile", "email"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
Scopes: []string{coreosoidc.ScopeOpenID, coreosoidc.ScopeOfflineAccess, "profile", "email", "pinniped.sts.unrestricted"},
|
||||
},
|
||||
TokenEndpointAuthMethod: "none",
|
||||
}
|
||||
@ -126,12 +126,12 @@ func FositeOauth2Helper(
|
||||
OpenIDConnectTokenStrategy: newDynamicOpenIDConnectECDSAStrategy(oauthConfig, jwksProvider),
|
||||
},
|
||||
nil, // hasher, defaults to using BCrypt when nil. Used for hashing client secrets.
|
||||
TokenExchangeFactory,
|
||||
compose.OAuth2AuthorizeExplicitFactory,
|
||||
// compose.OAuth2RefreshTokenGrantFactory,
|
||||
compose.OpenIDConnectExplicitFactory,
|
||||
// compose.OpenIDConnectRefreshFactory,
|
||||
compose.OAuth2PKCEFactory,
|
||||
TokenExchangeFactory,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -206,6 +206,19 @@ var (
|
||||
"redirect_uri": {goodRedirectURI},
|
||||
},
|
||||
}
|
||||
|
||||
happyTokenExchangeRequest = func(audience string, subjectToken string) *http.Request {
|
||||
return &http.Request{
|
||||
Form: url.Values{
|
||||
"grant_type": {"urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
"audience": {audience},
|
||||
"subject_token": {subjectToken},
|
||||
"subject_token_type": {"urn:ietf:params:oauth:token-type:access_token"},
|
||||
"requested_token_type": {"urn:ietf:params:oauth:token-type:jwt"},
|
||||
"client_id": {goodClient},
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
type authcodeExchangeInputs struct {
|
||||
@ -550,6 +563,55 @@ func TestTokenEndpointWhenAuthcodeIsUsedTwice(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenExchange(t *testing.T) {
|
||||
// TODO write this test
|
||||
t.Skip()
|
||||
tests := []struct {
|
||||
name string
|
||||
authcodeExchange authcodeExchangeInputs
|
||||
wantStatus int
|
||||
requestedAudience string
|
||||
modifyTokenExchangeRequest func(r *http.Request)
|
||||
}{
|
||||
{
|
||||
name: "token exchange happy path",
|
||||
authcodeExchange: authcodeExchangeInputs{
|
||||
modifyAuthRequest: func(authRequest *http.Request) {
|
||||
authRequest.Form.Set("scope", "openid pinniped.sts.unrestricted")
|
||||
},
|
||||
wantStatus: http.StatusOK,
|
||||
wantBodyFields: []string{"id_token", "access_token", "token_type", "expires_in", "scope"},
|
||||
wantRequestedScopes: []string{"openid", "pinniped.sts.unrestricted"},
|
||||
},
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
subject, rsp, _, _, _ := exchangeAuthcodeForTokens(t, test.authcodeExchange)
|
||||
var parsedResponseBody map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(rsp.Body.Bytes(), &parsedResponseBody))
|
||||
|
||||
request := happyTokenExchangeRequest("foo-cluster", parsedResponseBody["access_token"].(string))
|
||||
|
||||
req := httptest.NewRequest("POST", "/path/shouldn't/matter", body(request.Form).ReadCloser())
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
if test.modifyTokenExchangeRequest != nil {
|
||||
test.modifyTokenExchangeRequest(req)
|
||||
}
|
||||
rsp = httptest.NewRecorder()
|
||||
|
||||
subject.ServeHTTP(rsp, req)
|
||||
t.Logf("response: %#v", rsp)
|
||||
t.Logf("response body: %q", rsp.Body.String())
|
||||
|
||||
require.Equal(t, test.wantStatus, rsp.Code)
|
||||
testutil.RequireEqualContentType(t, rsp.Header().Get("Content-Type"), "application/json")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func exchangeAuthcodeForTokens(t *testing.T, test authcodeExchangeInputs) (
|
||||
subject http.Handler,
|
||||
rsp *httptest.ResponseRecorder,
|
||||
@ -758,6 +820,9 @@ func simulateAuthEndpointHavingAlreadyRun(t *testing.T, authRequest *http.Reques
|
||||
if strings.Contains(authRequest.Form.Get("scope"), "offline_access") {
|
||||
authRequester.GrantScope("offline_access")
|
||||
}
|
||||
if strings.Contains(authRequest.Form.Get("scope"), "pinniped.sts.unrestricted") {
|
||||
authRequester.GrantScope("pinniped.sts.unrestricted")
|
||||
}
|
||||
authResponder, err := oauthHelper.NewAuthorizeResponse(ctx, authRequester, session)
|
||||
require.NoError(t, err)
|
||||
return authResponder
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oidc
|
||||
|
||||
import (
|
||||
@ -35,6 +38,9 @@ func (t *TokenExchangeHandler) HandleTokenEndpointRequest(ctx context.Context, r
|
||||
}
|
||||
|
||||
func (t *TokenExchangeHandler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder) error {
|
||||
if !(requester.GetGrantTypes().ExactOne("urn:ietf:params:oauth:grant-type:token-exchange")) {
|
||||
return errors.WithStack(fosite.ErrUnknownRequest)
|
||||
}
|
||||
params := requester.GetRequestForm()
|
||||
accessToken := params.Get("subject_token")
|
||||
if err := t.accessTokenStrategy.ValidateAccessToken(ctx, requester, accessToken); err != nil {
|
||||
@ -49,7 +55,7 @@ func (t *TokenExchangeHandler) PopulateTokenEndpointResponse(ctx context.Context
|
||||
return errors.WithStack(fosite.ErrScopeNotGranted)
|
||||
}
|
||||
// TODO check the other requester fields
|
||||
scopedDownRequester := fosite.NewAccessRequest(requester.GetSession())
|
||||
scopedDownRequester := fosite.NewAccessRequest(accessTokenSession.GetSession())
|
||||
scopedDownRequester.GrantedAudience = []string{params.Get("audience")}
|
||||
newToken, err := t.idTokenStrategy.GenerateIDToken(ctx, scopedDownRequester)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user