Monday OAuth - JWT signing key

My Monday.com app is successfully authenticating users as described in the documentation. The resulting JWT token is passed to a .NET API application in the Authorization HTTP header.
I’m having some trouble to decrypt the JWT token.
When I look at the Monday.com app definition on the tab called ‘Basic Information’, there’s a ‘Signing Secret’ value.
Is that signing secret used to sign the JWT token or not?
Since the JWT token is not containing ‘iss’ (issuer) and ‘aud’ (audience) claims, I need to write some custom C# code to decrypt the JWT token in the .NET API.

The OAuth JWT is signed with signing secret(on the basic info page), as are the JWT for automation actions. However, the JWT for the install webhooks are signed with the client secret for some reason.

I would think that there is a .NET library that handles json web tokens, where if you don’t specify the issuer or audience to verify will skip verifying them.

1 Like

Another way of looking at this is:

Use the Signing Secret when the JWT token is:

  • in the Authorization header without being prefixed with Bearer
  • in the URLSearchParams as a parameter named token

Use the Client Secret when the JWT token is:

  • in the URLSearchParams as a parameter named sessionToken
1 Like

The signature validation in my .NET API currently fails. It’s assuming a symmetric security key.
Do we know which algorithm is used by Monday.com to sign the JWT token?

I can bring in my own token validator but then I would have to know how to decode the JWT token with the signature into a set of claims. This is depending on the signing algorithm that is used to create the JWT token in the first place.

Pretty sure it’s HS256.

the first segment of base64 characters in a JWT (split the JWT on .) decodes to JSON which defines the signing algorithm and encryption.

idk about .NET, but here’s a basic example using jsonwebtoken with sveltekit that just returns the content of the JWT:

import { error, json, type RequestEvent } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';
import { logger } from 'path/to/logger';

import jwt from 'jsonwebtoken';

/**
 * GET /path/to/here?token=.....
 */
export async function GET(event: RequestEvent) {
	try {
		const token = event.url.searchParams.get('token');
		const payload = await jwt.verify(token, env.MONDAY_SIGNING_SECRET);
		// display the payload or do something else here...
		return json(payload);
	} catch (err: any) {
		logger.error('jwt catch error ', err);
		throw error(401, err.body?.message || 'Unauthorized. Please try again.');
	}
}

As you can see jsonwebtoken is just using the defaults, so try using the defaults with whatever you’re using in .NET.

I’ve got it working now with some help of .NET folks.
The .NET API is authorizing the user with the incoming JWT token.