feat(rp): client credentials grant (#494)
This change adds Client Credentials grant to the Relying Party. As specified in [RFC 6749, section 4.4](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4)
This commit is contained in:
parent
4d05eade5e
commit
fe3e02b80a
2 changed files with 49 additions and 1 deletions
|
@ -323,6 +323,31 @@ func RunAuthorizationCodeFlow(t *testing.T, opServer *httptest.Server, clientID,
|
||||||
return provider, tokens
|
return provider, tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClientCredentials(t *testing.T) {
|
||||||
|
targetURL := "http://local-site"
|
||||||
|
exampleStorage := storage.NewStorage(storage.NewUserStore(targetURL))
|
||||||
|
var dh deferredHandler
|
||||||
|
opServer := httptest.NewServer(&dh)
|
||||||
|
defer opServer.Close()
|
||||||
|
t.Logf("auth server at %s", opServer.URL)
|
||||||
|
dh.Handler = exampleop.SetupServer(opServer.URL, exampleStorage, Logger, true)
|
||||||
|
|
||||||
|
provider, err := rp.NewRelyingPartyOIDC(
|
||||||
|
CTX,
|
||||||
|
opServer.URL,
|
||||||
|
"sid1",
|
||||||
|
"verysecret",
|
||||||
|
targetURL,
|
||||||
|
[]string{"openid"},
|
||||||
|
)
|
||||||
|
require.NoError(t, err, "new rp")
|
||||||
|
|
||||||
|
token, err := rp.ClientCredentials(CTX, provider, nil)
|
||||||
|
require.NoError(t, err, "ClientCredentials call")
|
||||||
|
require.NotNil(t, token)
|
||||||
|
assert.NotEmpty(t, token.AccessToken)
|
||||||
|
}
|
||||||
|
|
||||||
func TestErrorFromPromptNone(t *testing.T) {
|
func TestErrorFromPromptNone(t *testing.T) {
|
||||||
jar, err := cookiejar.New(nil)
|
jar, err := cookiejar.New(nil)
|
||||||
require.NoError(t, err, "create cookie jar")
|
require.NoError(t, err, "create cookie jar")
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
"golang.org/x/exp/slog"
|
"golang.org/x/exp/slog"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/clientcredentials"
|
||||||
|
|
||||||
"github.com/zitadel/oidc/v3/pkg/client"
|
"github.com/zitadel/oidc/v3/pkg/client"
|
||||||
httphelper "github.com/zitadel/oidc/v3/pkg/http"
|
httphelper "github.com/zitadel/oidc/v3/pkg/http"
|
||||||
|
@ -416,12 +417,34 @@ func CodeExchange[C oidc.IDClaims](ctx context.Context, code string, rp RelyingP
|
||||||
return verifyTokenResponse[C](ctx, token, rp)
|
return verifyTokenResponse[C](ctx, token, rp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientCredentials requests an access token using the `client_credentials` grant,
|
||||||
|
// as defined in [RFC 6749, section 4.4].
|
||||||
|
//
|
||||||
|
// As there is no user associated to the request an ID Token can never be returned.
|
||||||
|
// Client Credentials are undefined in OpenID Connect and is a pure OAuth2 grant.
|
||||||
|
// Furthermore the server SHOULD NOT return a refresh token.
|
||||||
|
//
|
||||||
|
// [RFC 6749, section 4.4]: https://datatracker.ietf.org/doc/html/rfc6749#section-4.4
|
||||||
|
func ClientCredentials(ctx context.Context, rp RelyingParty, endpointParams url.Values) (token *oauth2.Token, err error) {
|
||||||
|
ctx = logCtxWithRPData(ctx, rp, "function", "ClientCredentials")
|
||||||
|
ctx = context.WithValue(ctx, oauth2.HTTPClient, rp.HttpClient())
|
||||||
|
config := clientcredentials.Config{
|
||||||
|
ClientID: rp.OAuthConfig().ClientID,
|
||||||
|
ClientSecret: rp.OAuthConfig().ClientSecret,
|
||||||
|
TokenURL: rp.OAuthConfig().Endpoint.TokenURL,
|
||||||
|
Scopes: rp.OAuthConfig().Scopes,
|
||||||
|
EndpointParams: endpointParams,
|
||||||
|
AuthStyle: rp.OAuthConfig().Endpoint.AuthStyle,
|
||||||
|
}
|
||||||
|
return config.Token(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
type CodeExchangeCallback[C oidc.IDClaims] func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens[C], state string, rp RelyingParty)
|
type CodeExchangeCallback[C oidc.IDClaims] func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens[C], state string, rp RelyingParty)
|
||||||
|
|
||||||
// CodeExchangeHandler extends the `CodeExchange` method with a http handler
|
// CodeExchangeHandler extends the `CodeExchange` method with a http handler
|
||||||
// including cookie handling for secure `state` transfer
|
// including cookie handling for secure `state` transfer
|
||||||
// and optional PKCE code verifier checking.
|
// and optional PKCE code verifier checking.
|
||||||
// Custom paramaters can optionally be set to the token URL.
|
// Custom parameters can optionally be set to the token URL.
|
||||||
func CodeExchangeHandler[C oidc.IDClaims](callback CodeExchangeCallback[C], rp RelyingParty, urlParam ...URLParamOpt) http.HandlerFunc {
|
func CodeExchangeHandler[C oidc.IDClaims](callback CodeExchangeCallback[C], rp RelyingParty, urlParam ...URLParamOpt) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
state, err := tryReadStateCookie(w, r, rp)
|
state, err := tryReadStateCookie(w, r, rp)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue