feat: Refactor JWT signing to use custom method with private key for OAuth flows

This commit is contained in:
Nawaz Dhandala
2026-01-12 13:46:30 +00:00
parent b48b3f31fc
commit 1131b7eea0
2 changed files with 22 additions and 5 deletions

View File

@@ -4,7 +4,7 @@ import GlobalCache from "Common/Server/Infrastructure/GlobalCache";
import { JSONObject } from "Common/Types/JSON";
import URL from "Common/Types/API/URL";
import OAuthProviderType from "Common/Types/Email/OAuthProviderType";
import jwt from "jsonwebtoken";
import JSONWebToken from "Common/Server/Utils/JsonWebToken";
interface OAuthTokenResponse {
access_token: string;
@@ -183,9 +183,10 @@ export default class SMTPOAuthService {
};
// Sign the JWT with the private key
const signedJwt: string = jwt.sign(jwtClaims, config.clientSecret, {
algorithm: "RS256",
});
const signedJwt: string = JSONWebToken.signWithPrivateKey(
jwtClaims,
config.clientSecret,
);
logger.debug(
`Fetching OAuth token from ${config.tokenUrl.toString()} using JWT Bearer`,
@@ -391,7 +392,9 @@ export default class SMTPOAuthService {
);
} catch (error) {
// Log but don't fail if caching fails - token is still valid
logger.warn("Failed to cache OAuth token in Redis:", error);
logger.warn(
`Failed to cache OAuth token in Redis: ${error instanceof Error ? error.message : String(error)}`,
);
}
}

View File

@@ -84,6 +84,20 @@ class JSONWebToken {
});
}
/**
* Sign a JWT with a custom private key using RS256 algorithm.
* Used for OAuth JWT Bearer flows that require asymmetric signing.
*/
@CaptureSpan()
public static signWithPrivateKey(
payload: Record<string, unknown>,
privateKey: string,
): string {
return jwt.sign(payload, privateKey, {
algorithm: "RS256",
});
}
@CaptureSpan()
public static decodeJsonPayload(token: string): JSONObject {
const decodedToken: string = JSON.stringify(