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
|
||||
}
|
||||
|
||||
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) {
|
||||
jar, err := cookiejar.New(nil)
|
||||
require.NoError(t, err, "create cookie jar")
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/zitadel/logging"
|
||||
"golang.org/x/exp/slog"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/clientcredentials"
|
||||
|
||||
"github.com/zitadel/oidc/v3/pkg/client"
|
||||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// CodeExchangeHandler extends the `CodeExchange` method with a http handler
|
||||
// including cookie handling for secure `state` transfer
|
||||
// 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 {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
state, err := tryReadStateCookie(w, r, rp)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue