Issue: The client assertion could not be signed due to the absence of client JWKs for Zitadel and OpenID Client integration

Currently leveraging Zitadel as my Identity Provider, I have set up a project and an API with a key. I am now in the process of acquiring a M2M token using the “JWT bearer token with private key” method, recommended by Zitadel (click here). Utilizing the code snippet below with the openid-client library:

/* tslint:disable:no-console */
import { exportJWK, importPKCS8, SignJWT } from 'jose';
import { Issuer } from 'openid-client';
import NodeRSA from 'node-rsa';
import fs from 'fs';
import path from 'path';

const getToken = async () => {
    const clientId = '27700000000000000';

    // Step 1: Discover the OpenID Connect provider's configuration
    const oidcIssuer = await Issuer.discover('https://xxxxxx-xxxxxx.zitadel.cloud');

    // Step 2: Create a JWK from the private key
    const keyJson = JSON.parse(fs.readFileSync(path.join(__dirname, '27700000000000001.json'), 'utf8'));
    const rsa = new NodeRSA(keyJson.key);
    const key = await importPKCS8(rsa.exportKey('pkcs8-private-pem'), 'RSA256');
    const jwk = await exportJWK(key);

    // Step 3: Create a new client with the JWK
    const client = new oidcIssuer.Client({
            client_id: clientId,
            token_endpoint_auth_method: 'private_key_jwt',
        }, {
            keys: [jwk]
        }
    );

    // Step 4: Generate a JWT signed with the private key
    const jwt = await new SignJWT({
        iss: clientId,
        sub: clientId,
        aud: oidcIssuer.metadata.token_endpoint
    })
        .setIssuedAt()
        .setExpirationTime('5m')
        .setProtectedHeader({
            alg: 'RS256',
            kid: keyJson.keyId
        })
        .sign(key);

    // Step 5: Exchange the JWT for an access token
    const tokenSet = await client.grant({
        grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
    }, {
        clientAssertionPayload: {
            client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
            client_assertion: jwt
        }
    });

    console.log('Access Token:', tokenSet.access_token);
};

getToken();

Everything progresses smoothly until step 5. However, upon execution, I encounter the following error:

OPError: invalid_request (assertion missing)

Despite providing the assertion in step 5, I cannot identify the missing element. What am I overlooking?

Answer №1

To ensure control over the POST payload, it is important to use node-fetch effectively. Below is an example of how this can be implemented:

const formData = new URLSearchParams();
formData.append('grant_type', 'client_credentials');
formData.append('client_assertion', assertion);
formData.append('client_assertion_type', 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer');

const response = await fetch(tokenEndpointUrl, {
    method: 'POST',
    headers: {
      'content-type': 'application/x-www-form-urlencoded',
      'accept': 'application/json',
    },
    body: formData,
});

If utilizing a library, ensure the ability to track the raw HTTP request for validation purposes. This will help in confirming that the request aligns with the expected format.

It's important to note that private_key_jwt serves as a token endpoint authentication method. It is advised to test it initially in a straightforward flow like client_credentials to verify the POST functionality. This is typically the recommended M2M flow.

Additionally, the grant

urn:ietf:params:oauth:grant-type:jwt-bearer
signifies a user assertion and signifies the presence of a user. This grant is commonly used for exchanging an access token from an alternate identity system.

Register the public key (or the JWKS URI location you provide) in the authorization server for your trusted client. Avoid including it in the request to prevent potential security risks that could enable unauthorized access by malicious clients.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implementing Class-based Dependency Injection in Express

Incorporating Express into a TypeScript project has presented me with a particular scenario Here is my route file: ... import findAllUsersFactory from "src/factory/FindAllUsers"; routes.get("/users", findAllUsersFactory().handle); ... ...

What is an improved method for defining a TypeScript type to store API method invocations?

Currently, I am exploring ways to enhance either GenericActionables or Items in a way that eliminates the need to manually add the API method name as a GenericActionables<"nameOfNewMethod"> to the Actionables type every time. Any suggesti ...

Ensuring the Accuracy of String Literal Types using class-validator

Consider the following type definition: export type BranchOperatorRole = 'none' | 'seller' | 'operator' | 'administrator'; Which Class-Validator decorator should I use to ensure that a property matches one of these ...

The RxJS observable fails to initiate the subscribe function following the mergeMap operation

I am attempting to organize my dataset in my Angular application using the RxJS operators and split it into multiple streams. However, I am facing difficulties making this work properly. Inside my SignalRService, I have set up a SignalR trigger in the cons ...

ngx-emoji mart - The error message "Type 'string' is not assignable" is being displayed

While working on a project involving the @ctrl/ngx-emoji-mart package, I encountered a perplexing issue. The code functioned flawlessly in Stackblitz but when I attempted to run it on my local system, an error surfaced: Type 'string' is not assig ...

Guide for adding an OnClick event to a MatTable row:

I am looking to add functionality for clicking on a specific row to view details of that user. For instance, when I click on the row for "user1", I want to be able to see all the information related to "user1". Here is the HTML code snippet: <table ma ...

How to troubleshoot: trying to assign '{ source: string; }' to type 'string' is not valid

I'm encountering an issue that seems like it should have a simple solution, but I can't seem to find any explanations on how to fix it. I'm working with Next.js and Typescript to create a component that displays an image and uses hooks to ma ...

What is the timing for the execution of top-level non-export code in TypeScript?

I am currently puzzled about the execution of code in files. Let's say we have a file1.ts with the following content: export interface myInterface {} export function myFunction() {} export const myConst: {} // ... and more exports // top-level non- ...

Are generic constraints leading to type inference selecting the incorrect candidate?

TypeScript Version: 2.6.0-dev.20170826 and 2.4.2 I'm questioning whether I've encountered a TypeScript inference bug or limitation, or if my code is simply incorrect. If the code is valid and it's an issue with type inference, I will repor ...

Deploy the Angular standalone component numerous times across a single page using Bootstrap

Edit After receiving input from Andrew, I have decided to adjust my strategy: Replacing survey-angular with the survey-angular-ui package Implementing a widget approach similar to the one outlined in this example Developing a single module that encompass ...

Encountering a 404 error while attempting to test a contact form on a Next.js website using a local server

Trying to test a contact form in Next.js where the data is logged but not sent to the API due to an error. "POST http://localhost:3000/app/(pages)/api/contact/route.tsx 404 (Not Found)" Troubleshooting to identify the issue. [directory setup] ...

The RC-dock library's 'DockLayout' is not compatible with JSX components. The instance type 'DockLayout' is not a valid JSX element and cannot be used as such

Despite encountering similar questions, none of the provided answers seem to address the issue within my codebase. My project utilizes React 17, Mui v5, and TS v4. I attempted to integrate a basic component from an external package called rc-dock. I simply ...

Instantiate the component array upon object instantiation

I'm currently in the process of learning Angular 2, so please bear with me if this question seems trivial. I am attempting to create a dynamic form that can be bound to a model. However, I am encountering an issue where I am unable to initialize my ar ...

Guide on dynamically applying a CSS rule to an HTML element using programming techniques

Currently working with Angular 6 and Typescript, I am facing a unique challenge. My task involves adding a specific CSS rule to the host of a component that I am currently developing. Unfortunately, applying this rule systematically is not an option. Inste ...

Tips for testing FormGroupDirective within a component

I am facing difficulties in testing a component with FormGroupDirective in the viewProviders section. I am unable to create a mock of the parent and set an empty formGroup. The component code is as follows: @Component({ (...) viewProviders: [ ...

When utilizing Angular, the mat-datepicker is displayed underneath the modal, even after attempting to modify the z-index

I am encountering a problem with a mat-datepicker displaying below a modal in my Angular application. Here are the key details: Html: <div class="col-12"> <mat-form-field appearance="fill"> <mat-label>Start Date ...

Error encountered in spyOn TS when passing array iteration instead of a string

Instead of repeating test cases with minor adjustments, I have implemented an Array and iterated through it. However, I am encountering a TS error in test when passed from the Array instead of as a string testLink Error: No overload matches this call. ...

When comparing two identical strings, the result is not true

Currently, I am conducting a comparison between the value of a checkbox and the values within an array of strings. The process involves printing out each comparison for analysis, as shown below: checkSkillLevel(index: number, level: string){ console.log(t ...

Error in displaying dialogues upon clicking

Currently experimenting with creating a dialog modal using the tutorial found at https://github.com/gopinav/Angular-Material-Tutorial/tree/master/material-demo/src/app, specifically referring to the dialog-example and dialog folder. However, upon testing ...

Setting up Emotion js in a React TypeScript project using Vite 4

Currently, I am in the process of transitioning from Webpack to Vite for my React Typescript application. I have been attempting to integrate Emotion js into the project. "@vitejs/plugin-react": "^4.0.1", "vite": "^4.3.9 ...