Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does Zitadel support generating access_token using client_secret_jwt for the grant type of client_credentials #655

Open
1 of 2 tasks
idavollen opened this issue Sep 21, 2024 · 1 comment
Assignees
Labels
auth docs Improvements or additions to documentation

Comments

@idavollen
Copy link

idavollen commented Sep 21, 2024

Preflight Checklist

  • I could not find a solution in the existing issues, docs, nor discussions
  • I have joined the ZITADEL chat

Describe the docs your are missing or that are wrong

I can find neither documentation nor code example of testing client_credentials with client_secret_jwt to generate access token for machine-to-machine communication without sending the client secret to the authorization server.

I ran https://github.com/zitadel/oidc/blob/3b64e792ed1c01daf6bb3320a8da4ffa346753c2/example/server/main.go as OP and created a Go client that attempts to create access_token with client_secret_jwt.

But in the end, I've got this error message:
Error getting access token: failed to get access token: {"error":"invalid_client"}

I'm looking forward to your confirmation or shed light on how to use Zitadel OIDC for my requested purpose.


package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/golang-jwt/jwt/v4"
)

// Function to generate a client_secret_jwt
func generateClientSecretJWT(clientID, clientSecret, tokenURL string) (string, error) {
	// Define the claims for the JWT
	claims := jwt.MapClaims{
		"iss": clientID,
		"sub": clientID,
		"aud": tokenURL,
		"exp": time.Now().Add(time.Minute * 5).Unix(), // 5 minutes expiration
		"iat": time.Now().Unix(),
		"jti": "unique-jwt-id", // You can generate this dynamically
	}

	// Create a new JWT token with HS256 signing method
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// Sign the token using the client secret
	signedToken, err := token.SignedString([]byte(clientSecret))
	if err != nil {
		return "", err
	}

	return signedToken, nil
}

// Function to request an access token from the Authorization Server
func requestAccessToken(clientID, clientSecret, tokenURL string) (string, error) {
	// Generate the client_secret_jwt
	clientAssertion, err := generateClientSecretJWT(clientID, clientSecret, tokenURL)
	if err != nil {
		return "", err
	}
	fmt.Println("Generated jwt payload: ", clientAssertion)

	// Prepare the form data for the token request
	data := url.Values{}
	data.Set("grant_type", "client_credentials")
	data.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
	data.Set("client_assertion", clientAssertion)

	// Create a new POST request
	req, err := http.NewRequest("POST", tokenURL, bytes.NewBufferString(data.Encode()))
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	// Send the request
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	// Read and parse the response
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("failed to get access token: %s", string(body))
	}

	// Parse the JSON response to extract the access token
	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return "", err
	}

	accessToken, ok := result["access_token"].(string)
	if !ok {
		return "", fmt.Errorf("access token not found in response")
	}

	return accessToken, nil
}

func main() {
// I also tried api/secret without luck
	clientID := "sid1"
	clientSecret := "everysecret"
	tokenURL := "https://www.myexample.com/oauth/token"
	// Request the access token
	accessToken, err := requestAccessToken(clientID, clientSecret, tokenURL)
	if err != nil {
		fmt.Println("Error getting access token:", err)
		return
	}

	fmt.Println("Access Token:", accessToken)
}

Additional Context

No response

@idavollen idavollen added the docs Improvements or additions to documentation label Sep 21, 2024
@muhlemmer muhlemmer added the auth label Sep 22, 2024
@muhlemmer muhlemmer self-assigned this Sep 25, 2024
@idavollen
Copy link
Author

any progress? Can you confirm that the client_secret_jwt is for the time being not supported? The more complicated private_key_jwt is already supported, why is the easier one, client_secret_jwt is not supported?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth docs Improvements or additions to documentation
Projects
Status: 🧐 Investigating
Development

No branches or pull requests

2 participants