Rename the "pinniped.sts.unrestricted" scope to "pinniped:request-audience".
This is a bit more clear. We're changing this now because it is a non-backwards-compatible change that we can make now since none of this RFC8693 token exchange stuff has been released yet. There is also a small typo fix in some flag usages (s/RF8693/RFC8693/) Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
parent
05127f4cfb
commit
8527c363bb
@ -102,12 +102,12 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
|
||||
f.StringVar(&flags.oidc.issuer, "oidc-issuer", "", "OpenID Connect issuer URL (default: autodiscover)")
|
||||
f.StringVar(&flags.oidc.clientID, "oidc-client-id", "pinniped-cli", "OpenID Connect client ID (default: autodiscover)")
|
||||
f.Uint16Var(&flags.oidc.listenPort, "oidc-listen-port", 0, "TCP port for localhost listener (authorization code flow only)")
|
||||
f.StringSliceVar(&flags.oidc.scopes, "oidc-scopes", []string{oidc.ScopeOfflineAccess, oidc.ScopeOpenID, "pinniped.sts.unrestricted"}, "OpenID Connect scopes to request during login")
|
||||
f.StringSliceVar(&flags.oidc.scopes, "oidc-scopes", []string{oidc.ScopeOfflineAccess, oidc.ScopeOpenID, "pinniped:request-audience"}, "OpenID Connect scopes to request during login")
|
||||
f.BoolVar(&flags.oidc.skipBrowser, "oidc-skip-browser", false, "During OpenID Connect login, skip opening the browser (just print the URL)")
|
||||
f.StringVar(&flags.oidc.sessionCachePath, "oidc-session-cache", "", "Path to OpenID Connect session cache file")
|
||||
f.StringSliceVar(&flags.oidc.caBundlePaths, "oidc-ca-bundle", nil, "Path to TLS certificate authority bundle (PEM format, optional, can be repeated)")
|
||||
f.BoolVar(&flags.oidc.debugSessionCache, "oidc-debug-session-cache", false, "Print debug logs related to the OpenID Connect session cache")
|
||||
f.StringVar(&flags.oidc.requestAudience, "oidc-request-audience", "", "Request a token with an alternate audience using RF8693 token exchange")
|
||||
f.StringVar(&flags.oidc.requestAudience, "oidc-request-audience", "", "Request a token with an alternate audience using RFC8693 token exchange")
|
||||
f.StringVar(&flags.kubeconfigPath, "kubeconfig", os.Getenv("KUBECONFIG"), "Path to kubeconfig file")
|
||||
f.StringVar(&flags.kubeconfigContextOverride, "kubeconfig-context", "", "Kubeconfig context name (default: current active context)")
|
||||
|
||||
|
@ -68,8 +68,8 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
--oidc-client-id string OpenID Connect client ID (default: autodiscover) (default "pinniped-cli")
|
||||
--oidc-issuer string OpenID Connect issuer URL (default: autodiscover)
|
||||
--oidc-listen-port uint16 TCP port for localhost listener (authorization code flow only)
|
||||
--oidc-request-audience string Request a token with an alternate audience using RF8693 token exchange
|
||||
--oidc-scopes strings OpenID Connect scopes to request during login (default [offline_access,openid,pinniped.sts.unrestricted])
|
||||
--oidc-request-audience string Request a token with an alternate audience using RFC8693 token exchange
|
||||
--oidc-scopes strings OpenID Connect scopes to request during login (default [offline_access,openid,pinniped:request-audience])
|
||||
--oidc-session-cache string Path to OpenID Connect session cache file
|
||||
--oidc-skip-browser During OpenID Connect login, skip opening the browser (just print the URL)
|
||||
--static-token string Instead of doing an OIDC-based login, specify a static token
|
||||
@ -415,7 +415,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
|
||||
- --issuer=https://example.com/issuer
|
||||
- --client-id=pinniped-cli
|
||||
- --scopes=offline_access,openid,pinniped.sts.unrestricted
|
||||
- --scopes=offline_access,openid,pinniped:request-audience
|
||||
- --ca-bundle-data=%s
|
||||
- --request-audience=test-audience
|
||||
command: '.../path/to/pinniped'
|
||||
@ -472,7 +472,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
|
||||
- --issuer=https://example.com/issuer
|
||||
- --client-id=pinniped-cli
|
||||
- --scopes=offline_access,openid,pinniped.sts.unrestricted
|
||||
- --scopes=offline_access,openid,pinniped:request-audience
|
||||
- --skip-browser
|
||||
- --listen-port=1234
|
||||
- --ca-bundle-data=%s
|
||||
|
@ -79,13 +79,13 @@ func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command {
|
||||
cmd.Flags().StringVar(&flags.issuer, "issuer", "", "OpenID Connect issuer URL")
|
||||
cmd.Flags().StringVar(&flags.clientID, "client-id", "pinniped-cli", "OpenID Connect client ID")
|
||||
cmd.Flags().Uint16Var(&flags.listenPort, "listen-port", 0, "TCP port for localhost listener (authorization code flow only)")
|
||||
cmd.Flags().StringSliceVar(&flags.scopes, "scopes", []string{oidc.ScopeOfflineAccess, oidc.ScopeOpenID, "pinniped.sts.unrestricted"}, "OIDC scopes to request during login")
|
||||
cmd.Flags().StringSliceVar(&flags.scopes, "scopes", []string{oidc.ScopeOfflineAccess, oidc.ScopeOpenID, "pinniped:request-audience"}, "OIDC scopes to request during login")
|
||||
cmd.Flags().BoolVar(&flags.skipBrowser, "skip-browser", false, "Skip opening the browser (just print the URL)")
|
||||
cmd.Flags().StringVar(&flags.sessionCachePath, "session-cache", filepath.Join(mustGetConfigDir(), "sessions.yaml"), "Path to session cache file")
|
||||
cmd.Flags().StringSliceVar(&flags.caBundlePaths, "ca-bundle", nil, "Path to TLS certificate authority bundle (PEM format, optional, can be repeated)")
|
||||
cmd.Flags().StringSliceVar(&flags.caBundleData, "ca-bundle-data", nil, "Base64 endcoded TLS certificate authority bundle (base64 encoded PEM format, optional, can be repeated)")
|
||||
cmd.Flags().BoolVar(&flags.debugSessionCache, "debug-session-cache", false, "Print debug logs related to the session cache")
|
||||
cmd.Flags().StringVar(&flags.requestAudience, "request-audience", "", "Request a token with an alternate audience using RF8693 token exchange")
|
||||
cmd.Flags().StringVar(&flags.requestAudience, "request-audience", "", "Request a token with an alternate audience using RFC8693 token exchange")
|
||||
cmd.Flags().BoolVar(&flags.conciergeEnabled, "enable-concierge", false, "Exchange the OIDC ID token with the Pinniped concierge during login")
|
||||
cmd.Flags().StringVar(&flags.conciergeNamespace, "concierge-namespace", "pinniped-concierge", "Namespace in which the concierge was installed")
|
||||
cmd.Flags().StringVar(&flags.conciergeAuthenticatorType, "concierge-authenticator-type", "", "Concierge authenticator type (e.g., 'webhook', 'jwt')")
|
||||
|
@ -69,8 +69,8 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
-h, --help help for oidc
|
||||
--issuer string OpenID Connect issuer URL
|
||||
--listen-port uint16 TCP port for localhost listener (authorization code flow only)
|
||||
--request-audience string Request a token with an alternate audience using RF8693 token exchange
|
||||
--scopes strings OIDC scopes to request during login (default [offline_access,openid,pinniped.sts.unrestricted])
|
||||
--request-audience string Request a token with an alternate audience using RFC8693 token exchange
|
||||
--scopes strings OIDC scopes to request during login (default [offline_access,openid,pinniped:request-audience])
|
||||
--session-cache string Path to session cache file (default "` + cfgDir + `/sessions.yaml")
|
||||
--skip-browser Skip opening the browser (just print the URL)
|
||||
`),
|
||||
|
@ -64,8 +64,8 @@ func NewHandler(
|
||||
// at this time, however we will temporarily grant the scope just in case that changes in a future release of fosite.
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, coreosoidc.ScopeOfflineAccess)
|
||||
|
||||
// Grant the Pinniped STS scope if requested.
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, "pinniped.sts.unrestricted")
|
||||
// Grant the pinniped:request-audience scope if requested.
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, "pinniped:request-audience")
|
||||
|
||||
now := time.Now()
|
||||
_, err = oauthHelper.NewAuthorizeResponse(r.Context(), authorizeRequester, &openid.DefaultSession{
|
||||
|
@ -72,10 +72,10 @@ func NewHandler(
|
||||
return httperr.New(http.StatusBadRequest, "error using state downstream auth params")
|
||||
}
|
||||
|
||||
// Automatically grant the openid, offline_access, and Pinniped STS scopes, but only if they were requested.
|
||||
// Automatically grant the openid, offline_access, and pinniped:request-audience scopes, but only if they were requested.
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, coreosoidc.ScopeOpenID)
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, coreosoidc.ScopeOfflineAccess)
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, "pinniped.sts.unrestricted")
|
||||
oidc.GrantScopeIfRequested(authorizeRequester, "pinniped:request-audience")
|
||||
|
||||
token, err := upstreamIDPConfig.ExchangeAuthcodeAndValidateTokens(
|
||||
r.Context(),
|
||||
|
@ -28,7 +28,7 @@ func TestNullStorage_GetClient(t *testing.T) {
|
||||
RedirectURIs: []string{"http://127.0.0.1/callback"},
|
||||
ResponseTypes: []string{"code"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
Scopes: []string{"openid", "offline_access", "profile", "email", "pinniped.sts.unrestricted"},
|
||||
Scopes: []string{"openid", "offline_access", "profile", "email", "pinniped:request-audience"},
|
||||
},
|
||||
TokenEndpointAuthMethod: "none",
|
||||
},
|
||||
|
@ -85,7 +85,7 @@ func PinnipedCLIOIDCClient() *fosite.DefaultOpenIDConnectClient {
|
||||
RedirectURIs: []string{"http://127.0.0.1/callback"},
|
||||
ResponseTypes: []string{"code"},
|
||||
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"},
|
||||
Scopes: []string{coreosoidc.ScopeOpenID, coreosoidc.ScopeOfflineAccess, "profile", "email", "pinniped:request-audience"},
|
||||
},
|
||||
TokenEndpointAuthMethod: "none",
|
||||
}
|
||||
|
@ -633,13 +633,13 @@ func TestTokenExchange(t *testing.T) {
|
||||
successfulAuthCodeExchange := tokenEndpointResponseExpectedValues{
|
||||
wantStatus: http.StatusOK,
|
||||
wantSuccessBodyFields: []string{"id_token", "access_token", "token_type", "expires_in", "scope"},
|
||||
wantRequestedScopes: []string{"openid", "pinniped.sts.unrestricted"},
|
||||
wantGrantedScopes: []string{"openid", "pinniped.sts.unrestricted"},
|
||||
wantRequestedScopes: []string{"openid", "pinniped:request-audience"},
|
||||
wantGrantedScopes: []string{"openid", "pinniped:request-audience"},
|
||||
}
|
||||
|
||||
doValidAuthCodeExchange := authcodeExchangeInputs{
|
||||
modifyAuthRequest: func(authRequest *http.Request) {
|
||||
authRequest.Form.Set("scope", "openid pinniped.sts.unrestricted")
|
||||
authRequest.Form.Set("scope", "openid pinniped:request-audience")
|
||||
},
|
||||
want: successfulAuthCodeExchange,
|
||||
}
|
||||
@ -730,7 +730,7 @@ func TestTokenExchange(t *testing.T) {
|
||||
wantResponseBodyContains: `invalid subject_token`,
|
||||
},
|
||||
{
|
||||
name: "access token missing pinniped.sts.unrestricted scope",
|
||||
name: "access token missing pinniped:request-audience scope",
|
||||
authcodeExchange: authcodeExchangeInputs{
|
||||
modifyAuthRequest: func(authRequest *http.Request) {
|
||||
authRequest.Form.Set("scope", "openid")
|
||||
@ -744,19 +744,19 @@ func TestTokenExchange(t *testing.T) {
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantResponseBodyContains: `missing the \"pinniped.sts.unrestricted\" scope`,
|
||||
wantResponseBodyContains: `missing the \"pinniped:request-audience\" scope`,
|
||||
},
|
||||
{
|
||||
name: "access token missing openid scope",
|
||||
authcodeExchange: authcodeExchangeInputs{
|
||||
modifyAuthRequest: func(authRequest *http.Request) {
|
||||
authRequest.Form.Set("scope", "pinniped.sts.unrestricted")
|
||||
authRequest.Form.Set("scope", "pinniped:request-audience")
|
||||
},
|
||||
want: tokenEndpointResponseExpectedValues{
|
||||
wantStatus: http.StatusOK,
|
||||
wantSuccessBodyFields: []string{"access_token", "token_type", "expires_in", "scope"},
|
||||
wantRequestedScopes: []string{"pinniped.sts.unrestricted"},
|
||||
wantGrantedScopes: []string{"pinniped.sts.unrestricted"},
|
||||
wantRequestedScopes: []string{"pinniped:request-audience"},
|
||||
wantGrantedScopes: []string{"pinniped:request-audience"},
|
||||
},
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
@ -767,7 +767,7 @@ func TestTokenExchange(t *testing.T) {
|
||||
name: "token minting failure",
|
||||
authcodeExchange: authcodeExchangeInputs{
|
||||
modifyAuthRequest: func(authRequest *http.Request) {
|
||||
authRequest.Form.Set("scope", "openid pinniped.sts.unrestricted")
|
||||
authRequest.Form.Set("scope", "openid pinniped:request-audience")
|
||||
},
|
||||
// Fail to fetch a JWK signing key after the authcode exchange has happened.
|
||||
makeOathHelper: makeOauthHelperWithJWTKeyThatWorksOnlyOnce,
|
||||
@ -918,23 +918,23 @@ func TestRefreshGrant(t *testing.T) {
|
||||
{
|
||||
name: "when the refresh request removes a scope which was originally granted from the list of requested scopes then it is granted anyway",
|
||||
authcodeExchange: authcodeExchangeInputs{
|
||||
modifyAuthRequest: func(r *http.Request) { r.Form.Set("scope", "openid offline_access pinniped.sts.unrestricted") },
|
||||
modifyAuthRequest: func(r *http.Request) { r.Form.Set("scope", "openid offline_access pinniped:request-audience") },
|
||||
want: tokenEndpointResponseExpectedValues{
|
||||
wantStatus: http.StatusOK,
|
||||
wantSuccessBodyFields: []string{"id_token", "refresh_token", "access_token", "token_type", "expires_in", "scope"},
|
||||
wantRequestedScopes: []string{"openid", "offline_access", "pinniped.sts.unrestricted"},
|
||||
wantGrantedScopes: []string{"openid", "offline_access", "pinniped.sts.unrestricted"},
|
||||
wantRequestedScopes: []string{"openid", "offline_access", "pinniped:request-audience"},
|
||||
wantGrantedScopes: []string{"openid", "offline_access", "pinniped:request-audience"},
|
||||
},
|
||||
},
|
||||
refreshRequest: refreshRequestInputs{
|
||||
modifyTokenRequest: func(r *http.Request, refreshToken string, accessToken string) {
|
||||
r.Body = happyRefreshRequestBody(refreshToken).WithScope("openid").ReadCloser() // do not ask for "pinniped.sts.unrestricted" again
|
||||
r.Body = happyRefreshRequestBody(refreshToken).WithScope("openid").ReadCloser() // do not ask for "pinniped:request-audience" again
|
||||
},
|
||||
want: tokenEndpointResponseExpectedValues{
|
||||
wantStatus: http.StatusOK,
|
||||
wantSuccessBodyFields: []string{"id_token", "refresh_token", "access_token", "token_type", "expires_in", "scope"},
|
||||
wantRequestedScopes: []string{"openid", "offline_access", "pinniped.sts.unrestricted"},
|
||||
wantGrantedScopes: []string{"openid", "offline_access", "pinniped.sts.unrestricted"},
|
||||
wantRequestedScopes: []string{"openid", "offline_access", "pinniped:request-audience"},
|
||||
wantGrantedScopes: []string{"openid", "offline_access", "pinniped:request-audience"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
@ -1400,8 +1400,8 @@ 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")
|
||||
if strings.Contains(authRequest.Form.Get("scope"), "pinniped:request-audience") {
|
||||
authRequester.GrantScope("pinniped:request-audience")
|
||||
}
|
||||
authResponder, err := oauthHelper.NewAuthorizeResponse(ctx, authRequester, session)
|
||||
require.NoError(t, err)
|
||||
|
@ -18,7 +18,7 @@ import (
|
||||
const (
|
||||
tokenTypeAccessToken = "urn:ietf:params:oauth:token-type:access_token" //nolint: gosec
|
||||
tokenTypeJWT = "urn:ietf:params:oauth:token-type:jwt" //nolint: gosec
|
||||
pinnipedTokenExchangeScope = "pinniped.sts.unrestricted" //nolint: gosec
|
||||
pinnipedTokenExchangeScope = "pinniped:request-audience" //nolint: gosec
|
||||
)
|
||||
|
||||
type stsParams struct {
|
||||
@ -65,7 +65,7 @@ func (t *TokenExchangeHandler) PopulateTokenEndpointResponse(ctx context.Context
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// Require that the incoming access token has the STS and OpenID scopes.
|
||||
// Require that the incoming access token has the pinniped:request-audience and OpenID scopes.
|
||||
if !originalRequester.GetGrantedScopes().Has(pinnipedTokenExchangeScope) {
|
||||
return errors.WithStack(fosite.ErrAccessDenied.WithHintf("missing the %q scope", pinnipedTokenExchangeScope))
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ func WithClient(httpClient *http.Client) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithRequestAudience causes the login flow to perform an additional token exchange using the RFC8693 STS flow.
|
||||
// WithRequestAudience causes the login flow to perform an additional token exchange using the RFC8693 flow.
|
||||
func WithRequestAudience(audience string) Option {
|
||||
return func(h *handlerState) error {
|
||||
h.requestedAudience = audience
|
||||
|
@ -133,7 +133,7 @@ func TestSupervisorLogin(t *testing.T) {
|
||||
ClientID: "pinniped-cli",
|
||||
Endpoint: discovery.Endpoint(),
|
||||
RedirectURL: localCallbackServer.URL,
|
||||
Scopes: []string{"openid", "pinniped.sts.unrestricted", "offline_access"},
|
||||
Scopes: []string{"openid", "pinniped:request-audience", "offline_access"},
|
||||
}
|
||||
|
||||
// Build a valid downstream authorize URL for the supervisor.
|
||||
@ -175,7 +175,7 @@ func TestSupervisorLogin(t *testing.T) {
|
||||
callback := localCallbackServer.waitForCallback(10 * time.Second)
|
||||
t.Logf("got callback request: %s", library.MaskTokens(callback.URL.String()))
|
||||
require.Equal(t, stateParam.String(), callback.URL.Query().Get("state"))
|
||||
require.ElementsMatch(t, []string{"openid", "pinniped.sts.unrestricted", "offline_access"}, strings.Split(callback.URL.Query().Get("scope"), " "))
|
||||
require.ElementsMatch(t, []string{"openid", "pinniped:request-audience", "offline_access"}, strings.Split(callback.URL.Query().Get("scope"), " "))
|
||||
authcode := callback.URL.Query().Get("code")
|
||||
require.NotEmpty(t, authcode)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user