mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 14:28:20 +00:00
feat(fips): fips validated JWT's
This commit is contained in:
@@ -47,3 +47,4 @@ cli/detect/config/gitleaks.toml:gcp-api-key:582
|
|||||||
backend/src/services/smtp/smtp-service.ts:generic-api-key:79
|
backend/src/services/smtp/smtp-service.ts:generic-api-key:79
|
||||||
frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/CloudflarePagesSyncFields.tsx:cloudflare-api-key:7
|
frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/CloudflarePagesSyncFields.tsx:cloudflare-api-key:7
|
||||||
docs/integrations/app-connections/zabbix.mdx:generic-api-key:91
|
docs/integrations/app-connections/zabbix.mdx:generic-api-key:91
|
||||||
|
docs/integrations/app-connections/bitbucket.mdx:generic-api-key:123
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
FROM node:20-slim
|
FROM node:20-slim
|
||||||
|
|
||||||
|
# ? Setup a test SoftHSM module. In production a real HSM is used.
|
||||||
|
|
||||||
|
ARG SOFTHSM2_VERSION=2.5.0
|
||||||
|
|
||||||
|
ENV SOFTHSM2_VERSION=${SOFTHSM2_VERSION} \
|
||||||
|
SOFTHSM2_SOURCES=/tmp/softhsm2
|
||||||
|
|
||||||
# Install build dependencies including python3 (required for pkcs11js and partially TDS driver)
|
# Install build dependencies including python3 (required for pkcs11js and partially TDS driver)
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
build-essential \
|
build-essential \
|
||||||
@@ -46,8 +53,6 @@ RUN apt-get install -y opensc
|
|||||||
RUN mkdir -p /etc/softhsm2/tokens && \
|
RUN mkdir -p /etc/softhsm2/tokens && \
|
||||||
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
||||||
|
|
||||||
|
|
||||||
# Build and install FIPS validated OpenSSL
|
|
||||||
WORKDIR /openssl-build
|
WORKDIR /openssl-build
|
||||||
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
||||||
&& tar -xf openssl-3.1.2.tar.gz \
|
&& tar -xf openssl-3.1.2.tar.gz \
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import "ts-node/register";
|
import "ts-node/register";
|
||||||
|
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import jwt from "jsonwebtoken";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { seedData1 } from "@app/db/seed-data";
|
import { seedData1 } from "@app/db/seed-data";
|
||||||
@@ -83,7 +83,7 @@ export default {
|
|||||||
// @ts-expect-error type
|
// @ts-expect-error type
|
||||||
globalThis.testSuperAdminDAL = superAdminDAL;
|
globalThis.testSuperAdminDAL = superAdminDAL;
|
||||||
// @ts-expect-error type
|
// @ts-expect-error type
|
||||||
globalThis.jwtAuthToken = jwt.sign(
|
globalThis.jwtAuthToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||||
userId: seedData1.id,
|
userId: seedData1.id,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
@@ -62,7 +62,7 @@ export const assumePrivilegeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const assumePrivilegesToken = jwt.sign(
|
const assumePrivilegesToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
tokenVersionId,
|
tokenVersionId,
|
||||||
actorType: targetActorType,
|
actorType: targetActorType,
|
||||||
@@ -82,7 +82,7 @@ export const assumePrivilegeServiceFactory = ({
|
|||||||
tokenVersionId
|
tokenVersionId
|
||||||
) => {
|
) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const decodedToken = jwt.verify(token, appCfg.AUTH_SECRET) as {
|
const decodedToken = crypto.jwt().verify(token, appCfg.AUTH_SECRET) as {
|
||||||
tokenVersionId: string;
|
tokenVersionId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
requesterId: string;
|
requesterId: string;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
|
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||||
@@ -40,7 +41,7 @@ export const GithubProvider = (): TDynamicProviderFns => {
|
|||||||
|
|
||||||
let appJwt: string;
|
let appJwt: string;
|
||||||
try {
|
try {
|
||||||
appJwt = jwt.sign(jwtPayload, privateKey, { algorithm: "RS256" });
|
appJwt = crypto.jwt().sign(jwtPayload, privateKey, { algorithm: "RS256" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
let message = "Failed to sign JWT.";
|
let message = "Failed to sign JWT.";
|
||||||
if (error instanceof jwt.JsonWebTokenError) {
|
if (error instanceof jwt.JsonWebTokenError) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { OrgMembershipStatus, TableName, TLdapConfigsUpdate, TUsers } from "@app/db/schemas";
|
import { OrgMembershipStatus, TableName, TLdapConfigsUpdate, TUsers } from "@app/db/schemas";
|
||||||
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
||||||
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
@@ -536,7 +536,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
const isUserCompleted = Boolean(user.isAccepted);
|
const isUserCompleted = Boolean(user.isAccepted);
|
||||||
const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
|
const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
|
||||||
|
|
||||||
const providerAuthToken = jwt.sign(
|
const providerAuthToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { Issuer, Issuer as OpenIdIssuer, Strategy as OpenIdStrategy, TokenSet } from "openid-client";
|
import { Issuer, Issuer as OpenIdIssuer, Strategy as OpenIdStrategy, TokenSet } from "openid-client";
|
||||||
|
|
||||||
import { OrgMembershipStatus, TableName, TUsers } from "@app/db/schemas";
|
import { OrgMembershipStatus, TableName, TUsers } from "@app/db/schemas";
|
||||||
@@ -13,6 +12,7 @@ import { TLicenseServiceFactory } from "@app/ee/services/license/license-service
|
|||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError, OidcAuthError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError, OidcAuthError } from "@app/lib/errors";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
import { ActorType, AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
@@ -406,7 +406,7 @@ export const oidcConfigServiceFactory = ({
|
|||||||
|
|
||||||
const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
|
const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
|
||||||
const isUserCompleted = Boolean(user.isAccepted);
|
const isUserCompleted = Boolean(user.isAccepted);
|
||||||
const providerAuthToken = jwt.sign(
|
const providerAuthToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { OrgMembershipStatus, TableName, TSamlConfigs, TSamlConfigsUpdate, TUsers } from "@app/db/schemas";
|
import { OrgMembershipStatus, TableName, TSamlConfigs, TSamlConfigsUpdate, TUsers } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
@@ -419,7 +419,7 @@ export const samlConfigServiceFactory = ({
|
|||||||
|
|
||||||
const isUserCompleted = Boolean(user.isAccepted);
|
const isUserCompleted = Boolean(user.isAccepted);
|
||||||
const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
|
const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
|
||||||
const providerAuthToken = jwt.sign(
|
const providerAuthToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { scimPatch } from "scim-patch";
|
import { scimPatch } from "scim-patch";
|
||||||
|
|
||||||
import { OrgMembershipRole, OrgMembershipStatus, TableName, TGroups, TOrgMemberships, TUsers } from "@app/db/schemas";
|
import { OrgMembershipRole, OrgMembershipStatus, TableName, TGroups, TOrgMemberships, TUsers } from "@app/db/schemas";
|
||||||
@@ -10,6 +9,7 @@ import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-grou
|
|||||||
import { TScimDALFactory } from "@app/ee/services/scim/scim-dal";
|
import { TScimDALFactory } from "@app/ee/services/scim/scim-dal";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, NotFoundError, ScimRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, ScimRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TExternalGroupOrgRoleMappingDALFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-dal";
|
import { TExternalGroupOrgRoleMappingDALFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-dal";
|
||||||
@@ -137,7 +137,7 @@ export const scimServiceFactory = ({
|
|||||||
ttlDays
|
ttlDays
|
||||||
});
|
});
|
||||||
|
|
||||||
const scimToken = jwt.sign(
|
const scimToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
scimTokenId: scimTokenData.id,
|
scimTokenId: scimTokenData.id,
|
||||||
authTokenType: AuthTokenType.SCIM_TOKEN
|
authTokenType: AuthTokenType.SCIM_TOKEN
|
||||||
|
|||||||
+2
-3
@@ -1,8 +1,7 @@
|
|||||||
import crypto from "crypto";
|
|
||||||
|
|
||||||
import { TSecretScanningV2DALFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-dal";
|
import { TSecretScanningV2DALFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-dal";
|
||||||
import { SecretScanningDataSource } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
import { SecretScanningDataSource } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
||||||
import { TSecretScanningV2QueueServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-queue";
|
import { TSecretScanningV2QueueServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-queue";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
@@ -67,7 +66,7 @@ export const bitbucketSecretScanningService = (
|
|||||||
|
|
||||||
const credentials = JSON.parse(decryptedCredentials.toString()) as TBitbucketDataSourceCredentials;
|
const credentials = JSON.parse(decryptedCredentials.toString()) as TBitbucketDataSourceCredentials;
|
||||||
|
|
||||||
const hmac = crypto.createHmac("sha256", credentials.webhookSecret);
|
const hmac = crypto.rawCrypto.createHmac("sha256", credentials.webhookSecret);
|
||||||
hmac.update(bodyString);
|
hmac.update(bodyString);
|
||||||
const calculatedSignature = hmac.digest("hex");
|
const calculatedSignature = hmac.digest("hex");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import crypto from "crypto";
|
||||||
|
|
||||||
|
import { SecretEncryptionAlgo } from "@app/db/schemas";
|
||||||
|
import { CryptographyError } from "@app/lib/errors";
|
||||||
|
|
||||||
|
export const asymmetricFipsValidated = () => {
|
||||||
|
const generateKeyPair = () => {
|
||||||
|
const { publicKey, privateKey } = crypto.generateKeyPairSync("x25519");
|
||||||
|
|
||||||
|
return {
|
||||||
|
publicKey: publicKey.export({ type: "spki", format: "der" }).toString("base64"),
|
||||||
|
privateKey: privateKey.export({ type: "pkcs8", format: "der" }).toString("base64")
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const encryptAsymmetric = (data: string, publicKey: string, privateKey: string) => {
|
||||||
|
const pubKeyObj = crypto.createPublicKey({
|
||||||
|
key: Buffer.from(publicKey, "base64"),
|
||||||
|
type: "spki",
|
||||||
|
format: "der"
|
||||||
|
});
|
||||||
|
|
||||||
|
const privKeyObj = crypto.createPrivateKey({
|
||||||
|
key: Buffer.from(privateKey, "base64"),
|
||||||
|
type: "pkcs8",
|
||||||
|
format: "der"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate shared secret using x25519 curve
|
||||||
|
const sharedSecret = crypto.diffieHellman({
|
||||||
|
privateKey: privKeyObj,
|
||||||
|
publicKey: pubKeyObj
|
||||||
|
});
|
||||||
|
|
||||||
|
const nonce = crypto.randomBytes(24);
|
||||||
|
|
||||||
|
// Derive 32-byte key from shared secret
|
||||||
|
const key = crypto.createHash("sha256").update(sharedSecret).digest();
|
||||||
|
|
||||||
|
// Use first 12 bytes of nonce as IV for AES-GCM
|
||||||
|
const iv = nonce.subarray(0, 12);
|
||||||
|
|
||||||
|
// Encrypt with AES-256-GCM
|
||||||
|
const cipher = crypto.createCipheriv(SecretEncryptionAlgo.AES_256_GCM, key, iv);
|
||||||
|
|
||||||
|
const ciphertext = cipher.update(data, "utf8");
|
||||||
|
cipher.final();
|
||||||
|
|
||||||
|
const authTag = cipher.getAuthTag();
|
||||||
|
|
||||||
|
// Combine ciphertext and auth tag
|
||||||
|
const combined = Buffer.concat([ciphertext, authTag]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
ciphertext: combined.toString("base64"),
|
||||||
|
nonce: nonce.toString("base64")
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const decryptAsymmetric = ({
|
||||||
|
ciphertext,
|
||||||
|
nonce,
|
||||||
|
publicKey,
|
||||||
|
privateKey
|
||||||
|
}: {
|
||||||
|
ciphertext: string;
|
||||||
|
nonce: string;
|
||||||
|
publicKey: string;
|
||||||
|
privateKey: string;
|
||||||
|
}) => {
|
||||||
|
// Convert base64 keys back to key objects
|
||||||
|
const pubKeyObj = crypto.createPublicKey({
|
||||||
|
key: Buffer.from(publicKey, "base64"),
|
||||||
|
type: "spki",
|
||||||
|
format: "der"
|
||||||
|
});
|
||||||
|
|
||||||
|
const privKeyObj = crypto.createPrivateKey({
|
||||||
|
key: Buffer.from(privateKey, "base64"),
|
||||||
|
type: "pkcs8",
|
||||||
|
format: "der"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate same shared secret
|
||||||
|
const sharedSecret = crypto.diffieHellman({
|
||||||
|
privateKey: privKeyObj,
|
||||||
|
publicKey: pubKeyObj
|
||||||
|
});
|
||||||
|
|
||||||
|
const nonceBuffer = Buffer.from(nonce, "base64");
|
||||||
|
const combinedBuffer = Buffer.from(ciphertext, "base64");
|
||||||
|
|
||||||
|
// Split ciphertext and auth tag (last 16 bytes for GCM)
|
||||||
|
const actualCiphertext = combinedBuffer.subarray(0, -16);
|
||||||
|
const authTag = combinedBuffer.subarray(-16);
|
||||||
|
|
||||||
|
// Derive same 32-byte key
|
||||||
|
const key = crypto.createHash("sha256").update(sharedSecret).digest();
|
||||||
|
|
||||||
|
// Use first 12 bytes of nonce as IV
|
||||||
|
const iv = nonceBuffer.subarray(0, 12);
|
||||||
|
|
||||||
|
// Decrypt
|
||||||
|
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
||||||
|
decipher.setAuthTag(authTag);
|
||||||
|
|
||||||
|
const plaintext = decipher.update(actualCiphertext);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const final = decipher.final();
|
||||||
|
return Buffer.concat([plaintext, final]).toString("utf8");
|
||||||
|
} catch (error) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message: "Invalid ciphertext or keys"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
generateKeyPair,
|
||||||
|
encryptAsymmetric,
|
||||||
|
decryptAsymmetric
|
||||||
|
};
|
||||||
|
};
|
||||||
+54
-271
@@ -4,6 +4,7 @@ import crypto, { subtle } from "node:crypto";
|
|||||||
|
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import cryptoJs from "crypto-js";
|
import cryptoJs from "crypto-js";
|
||||||
|
import jwtDep from "jsonwebtoken";
|
||||||
import nacl from "tweetnacl";
|
import nacl from "tweetnacl";
|
||||||
import naclUtils from "tweetnacl-util";
|
import naclUtils from "tweetnacl-util";
|
||||||
|
|
||||||
@@ -12,277 +13,30 @@ import { TLicenseServiceFactory } from "@app/ee/services/license/license-service
|
|||||||
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
|
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
|
||||||
|
|
||||||
import { isBase64 } from "../base64";
|
import { isBase64 } from "../../base64";
|
||||||
import { getConfig } from "../config/env";
|
import { getConfig } from "../../config/env";
|
||||||
import { CryptographyError } from "../errors";
|
import { CryptographyError } from "../../errors";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../../logger";
|
||||||
|
import { asymmetricFipsValidated } from "./asymmetric-fips";
|
||||||
enum DigestType {
|
import { hasherFipsValidated } from "./hash-fips";
|
||||||
Hex = "hex",
|
import { jwtFipsValidated } from "./jwt-fips";
|
||||||
Base64 = "base64"
|
import {
|
||||||
}
|
DigestType,
|
||||||
|
JWTPayload,
|
||||||
export enum SymmetricKeySize {
|
JWTSecretOrKey,
|
||||||
Bits128 = "128-bits",
|
JWTSignOptions,
|
||||||
Bits256 = "256-bits"
|
JWTVerifyOptions,
|
||||||
}
|
SymmetricKeySize,
|
||||||
|
TDecryptAsymmetricInput,
|
||||||
type TDecryptSymmetricInput =
|
TDecryptSymmetricInput,
|
||||||
| {
|
TEncryptSymmetricInput
|
||||||
ciphertext: string;
|
} from "./types";
|
||||||
iv: string;
|
|
||||||
tag: string;
|
|
||||||
key: string | Buffer; // can be hex encoded or buffer
|
|
||||||
keySize: SymmetricKeySize.Bits128;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
ciphertext: string;
|
|
||||||
iv: string;
|
|
||||||
tag: string;
|
|
||||||
key: string; // must be base64 encoded
|
|
||||||
keySize: SymmetricKeySize.Bits256;
|
|
||||||
};
|
|
||||||
|
|
||||||
type TEncryptSymmetricInput =
|
|
||||||
| {
|
|
||||||
plaintext: string;
|
|
||||||
key: string;
|
|
||||||
keySize: SymmetricKeySize.Bits256;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
plaintext: string;
|
|
||||||
key: string | Buffer;
|
|
||||||
keySize: SymmetricKeySize.Bits128;
|
|
||||||
};
|
|
||||||
|
|
||||||
type TDecryptAsymmetricInput = {
|
|
||||||
ciphertext: string;
|
|
||||||
nonce: string;
|
|
||||||
publicKey: string;
|
|
||||||
privateKey: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const bytesToBits = (bytes: number) => bytes * 8;
|
const bytesToBits = (bytes: number) => bytes * 8;
|
||||||
|
|
||||||
const IV_BYTES_SIZE = 12;
|
const IV_BYTES_SIZE = 12;
|
||||||
const BLOCK_SIZE_BYTES_16 = 16;
|
const BLOCK_SIZE_BYTES_16 = 16;
|
||||||
|
|
||||||
const hasherFipsValidated = () => {
|
|
||||||
const keySize = 32;
|
|
||||||
|
|
||||||
// For the salt when using pkdf2, we do salt rounds^6. If the salt rounds are 10, this will result in 10^6 = 1.000.000 iterations.
|
|
||||||
// The reason for this is because pbkdf2 is not as compute intense as bcrypt, making it faster to brute-force.
|
|
||||||
// From my testing, doing salt rounds^6 brings the computational power required to a little more than bcrypt.
|
|
||||||
// OWASP recommends a minimum of 600.000 iterations for pbkdf2, so 1.000.000 is more than enough.
|
|
||||||
// Ref: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
|
|
||||||
const MIN_COST_FACTOR = 10;
|
|
||||||
const MAX_COST_FACTOR = 20; // Iterations scales polynomial (costFactor^6), so we need an upper bound
|
|
||||||
|
|
||||||
const $calculateIterations = (costFactor: number) => {
|
|
||||||
return Math.round(costFactor ** 6);
|
|
||||||
};
|
|
||||||
|
|
||||||
const $hashPassword = (password: Buffer, salt: Buffer, iterations: number, keyLength: number) => {
|
|
||||||
return new Promise<Buffer>((resolve, reject) => {
|
|
||||||
crypto.pbkdf2(password, salt, iterations, keyLength, "sha256", (err, derivedKey) => {
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
resolve(derivedKey);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const $validatePassword = async (
|
|
||||||
inputPassword: Buffer,
|
|
||||||
storedHash: Buffer,
|
|
||||||
salt: Buffer,
|
|
||||||
iterations: number,
|
|
||||||
keyLength: number
|
|
||||||
) => {
|
|
||||||
const computedHash = await $hashPassword(inputPassword, salt, iterations, keyLength);
|
|
||||||
|
|
||||||
return crypto.timingSafeEqual(computedHash, storedHash);
|
|
||||||
};
|
|
||||||
|
|
||||||
const hash = async (password: string, costFactor: number) => {
|
|
||||||
// Strict input validation
|
|
||||||
if (typeof password !== "string" || password.length === 0) {
|
|
||||||
throw new CryptographyError({
|
|
||||||
message: "Invalid input, password must be a non-empty string"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Number.isInteger(costFactor)) {
|
|
||||||
throw new CryptographyError({
|
|
||||||
message: "Invalid cost factor, must be an integer"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (costFactor < MIN_COST_FACTOR || costFactor > MAX_COST_FACTOR) {
|
|
||||||
throw new CryptographyError({
|
|
||||||
message: `Invalid cost factor, must be between ${MIN_COST_FACTOR} and ${MAX_COST_FACTOR}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const iterations = $calculateIterations(costFactor);
|
|
||||||
|
|
||||||
const salt = crypto.randomBytes(16);
|
|
||||||
const derivedKey = await $hashPassword(Buffer.from(password), salt, iterations, keySize);
|
|
||||||
|
|
||||||
const combined = Buffer.concat([salt, derivedKey]);
|
|
||||||
return `$v1$${costFactor}$${combined.toString("base64")}`; // Store original costFactor!
|
|
||||||
};
|
|
||||||
|
|
||||||
const compare = async (password: string, hashedPassword: string) => {
|
|
||||||
try {
|
|
||||||
if (!hashedPassword?.startsWith("$v1$")) return false;
|
|
||||||
|
|
||||||
const parts = hashedPassword.split("$");
|
|
||||||
if (parts.length !== 4) return false;
|
|
||||||
|
|
||||||
const [, , storedCostFactor, combined] = parts;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!Number.isInteger(Number(storedCostFactor)) ||
|
|
||||||
Number(storedCostFactor) < MIN_COST_FACTOR ||
|
|
||||||
Number(storedCostFactor) > MAX_COST_FACTOR
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const combinedBuffer = Buffer.from(combined, "base64");
|
|
||||||
const salt = combinedBuffer.subarray(0, 16);
|
|
||||||
const storedHash = combinedBuffer.subarray(16);
|
|
||||||
|
|
||||||
const iterations = $calculateIterations(Number(storedCostFactor));
|
|
||||||
|
|
||||||
const isMatch = await $validatePassword(Buffer.from(password), storedHash, salt, iterations, keySize);
|
|
||||||
|
|
||||||
return isMatch;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
hash,
|
|
||||||
compare
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateAsymmetricKeyPairFipsValidated = () => {
|
|
||||||
const { publicKey, privateKey } = crypto.generateKeyPairSync("x25519");
|
|
||||||
|
|
||||||
return {
|
|
||||||
publicKey: publicKey.export({ type: "spki", format: "der" }).toString("base64"),
|
|
||||||
privateKey: privateKey.export({ type: "pkcs8", format: "der" }).toString("base64")
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const encryptAsymmetricFipsValidated = (data: string, publicKey: string, privateKey: string) => {
|
|
||||||
const pubKeyObj = crypto.createPublicKey({
|
|
||||||
key: Buffer.from(publicKey, "base64"),
|
|
||||||
type: "spki",
|
|
||||||
format: "der"
|
|
||||||
});
|
|
||||||
|
|
||||||
const privKeyObj = crypto.createPrivateKey({
|
|
||||||
key: Buffer.from(privateKey, "base64"),
|
|
||||||
type: "pkcs8",
|
|
||||||
format: "der"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Generate shared secret using x25519 curve
|
|
||||||
const sharedSecret = crypto.diffieHellman({
|
|
||||||
privateKey: privKeyObj,
|
|
||||||
publicKey: pubKeyObj
|
|
||||||
});
|
|
||||||
|
|
||||||
const nonce = crypto.randomBytes(24);
|
|
||||||
|
|
||||||
// Derive 32-byte key from shared secret
|
|
||||||
const key = crypto.createHash("sha256").update(sharedSecret).digest();
|
|
||||||
|
|
||||||
// Use first 12 bytes of nonce as IV for AES-GCM
|
|
||||||
const iv = nonce.subarray(0, 12);
|
|
||||||
|
|
||||||
// Encrypt with AES-256-GCM
|
|
||||||
const cipher = crypto.createCipheriv(SecretEncryptionAlgo.AES_256_GCM, key, iv);
|
|
||||||
|
|
||||||
const ciphertext = cipher.update(data, "utf8");
|
|
||||||
cipher.final();
|
|
||||||
|
|
||||||
const authTag = cipher.getAuthTag();
|
|
||||||
|
|
||||||
// Combine ciphertext and auth tag
|
|
||||||
const combined = Buffer.concat([ciphertext, authTag]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
ciphertext: combined.toString("base64"),
|
|
||||||
nonce: nonce.toString("base64")
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const decryptAsymmetricFipsValidated = ({
|
|
||||||
ciphertext,
|
|
||||||
nonce,
|
|
||||||
publicKey,
|
|
||||||
privateKey
|
|
||||||
}: {
|
|
||||||
ciphertext: string;
|
|
||||||
nonce: string;
|
|
||||||
publicKey: string;
|
|
||||||
privateKey: string;
|
|
||||||
}) => {
|
|
||||||
// Convert base64 keys back to key objects
|
|
||||||
const pubKeyObj = crypto.createPublicKey({
|
|
||||||
key: Buffer.from(publicKey, "base64"),
|
|
||||||
type: "spki",
|
|
||||||
format: "der"
|
|
||||||
});
|
|
||||||
|
|
||||||
const privKeyObj = crypto.createPrivateKey({
|
|
||||||
key: Buffer.from(privateKey, "base64"),
|
|
||||||
type: "pkcs8",
|
|
||||||
format: "der"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Generate same shared secret
|
|
||||||
const sharedSecret = crypto.diffieHellman({
|
|
||||||
privateKey: privKeyObj,
|
|
||||||
publicKey: pubKeyObj
|
|
||||||
});
|
|
||||||
|
|
||||||
const nonceBuffer = Buffer.from(nonce, "base64");
|
|
||||||
const combinedBuffer = Buffer.from(ciphertext, "base64");
|
|
||||||
|
|
||||||
// Split ciphertext and auth tag (last 16 bytes for GCM)
|
|
||||||
const actualCiphertext = combinedBuffer.subarray(0, -16);
|
|
||||||
const authTag = combinedBuffer.subarray(-16);
|
|
||||||
|
|
||||||
// Derive same 32-byte key
|
|
||||||
const key = crypto.createHash("sha256").update(sharedSecret).digest();
|
|
||||||
|
|
||||||
// Use first 12 bytes of nonce as IV
|
|
||||||
const iv = nonceBuffer.subarray(0, 12);
|
|
||||||
|
|
||||||
// Decrypt
|
|
||||||
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
||||||
decipher.setAuthTag(authTag);
|
|
||||||
|
|
||||||
const plaintext = decipher.update(actualCiphertext);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const final = decipher.final();
|
|
||||||
return Buffer.concat([plaintext, final]).toString("utf8");
|
|
||||||
} catch (error) {
|
|
||||||
throw new CryptographyError({
|
|
||||||
message: "Invalid ciphertext or keys"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateAsymmetricKeyPairNoFipsValidation = () => {
|
const generateAsymmetricKeyPairNoFipsValidation = () => {
|
||||||
const pair = nacl.box.keyPair();
|
const pair = nacl.box.keyPair();
|
||||||
|
|
||||||
@@ -447,21 +201,21 @@ const cryptographyFactory = () => {
|
|||||||
const asymmetric = () => {
|
const asymmetric = () => {
|
||||||
const generateKeyPair = () => {
|
const generateKeyPair = () => {
|
||||||
if (isFipsModeEnabled()) {
|
if (isFipsModeEnabled()) {
|
||||||
return generateAsymmetricKeyPairFipsValidated();
|
return asymmetricFipsValidated().generateKeyPair();
|
||||||
}
|
}
|
||||||
return generateAsymmetricKeyPairNoFipsValidation();
|
return generateAsymmetricKeyPairNoFipsValidation();
|
||||||
};
|
};
|
||||||
|
|
||||||
const encrypt = (data: string, publicKey: string, privateKey: string) => {
|
const encrypt = (data: string, publicKey: string, privateKey: string) => {
|
||||||
if (isFipsModeEnabled()) {
|
if (isFipsModeEnabled()) {
|
||||||
return encryptAsymmetricFipsValidated(data, publicKey, privateKey);
|
return asymmetricFipsValidated().encryptAsymmetric(data, publicKey, privateKey);
|
||||||
}
|
}
|
||||||
return encryptAsymmetricNoFipsValidation(data, publicKey, privateKey);
|
return encryptAsymmetricNoFipsValidation(data, publicKey, privateKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
const decrypt = ({ ciphertext, nonce, publicKey, privateKey }: TDecryptAsymmetricInput) => {
|
const decrypt = ({ ciphertext, nonce, publicKey, privateKey }: TDecryptAsymmetricInput) => {
|
||||||
if (isFipsModeEnabled()) {
|
if (isFipsModeEnabled()) {
|
||||||
return decryptAsymmetricFipsValidated({ ciphertext, nonce, publicKey, privateKey });
|
return asymmetricFipsValidated().decryptAsymmetric({ ciphertext, nonce, publicKey, privateKey });
|
||||||
}
|
}
|
||||||
return decryptAsymmetricNoFipsValidation({ ciphertext, nonce, publicKey, privateKey });
|
return decryptAsymmetricNoFipsValidation({ ciphertext, nonce, publicKey, privateKey });
|
||||||
};
|
};
|
||||||
@@ -633,13 +387,44 @@ const cryptographyFactory = () => {
|
|||||||
compareHash
|
compareHash
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
const jwt = () => {
|
||||||
|
$checkIsInitialized();
|
||||||
|
|
||||||
|
const sign = (payload: JWTPayload, secretOrKey: JWTSecretOrKey, options: JWTSignOptions = {}) => {
|
||||||
|
if (isFipsModeEnabled()) {
|
||||||
|
return jwtFipsValidated().sign(payload, secretOrKey, options);
|
||||||
|
}
|
||||||
|
return jwtDep.sign(payload, secretOrKey, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const verify = (token: string, secretOrKey: JWTSecretOrKey, options: JWTVerifyOptions = {}) => {
|
||||||
|
if (isFipsModeEnabled()) {
|
||||||
|
return jwtFipsValidated().verify(token, secretOrKey, options);
|
||||||
|
}
|
||||||
|
return jwtDep.verify(token, secretOrKey, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const decode = (token: string, options: { complete?: boolean } = {}) => {
|
||||||
|
if (isFipsModeEnabled()) {
|
||||||
|
return jwtFipsValidated().decode(token, options);
|
||||||
|
}
|
||||||
|
return jwtDep.decode(token, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
sign,
|
||||||
|
verify,
|
||||||
|
decode
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initialize,
|
initialize,
|
||||||
isFipsModeEnabled,
|
isFipsModeEnabled,
|
||||||
hashing,
|
|
||||||
verifyFipsLicense,
|
verifyFipsLicense,
|
||||||
|
hashing,
|
||||||
encryption,
|
encryption,
|
||||||
|
jwt,
|
||||||
randomBytes: crypto.randomBytes,
|
randomBytes: crypto.randomBytes,
|
||||||
randomInt: crypto.randomInt,
|
randomInt: crypto.randomInt,
|
||||||
rawCrypto: {
|
rawCrypto: {
|
||||||
@@ -674,6 +459,4 @@ const cryptographyFactory = () => {
|
|||||||
|
|
||||||
const factoryInstance = cryptographyFactory();
|
const factoryInstance = cryptographyFactory();
|
||||||
|
|
||||||
export type TCryptographyFactory = ReturnType<typeof cryptographyFactory>;
|
|
||||||
|
|
||||||
export { factoryInstance as crypto, DigestType };
|
export { factoryInstance as crypto, DigestType };
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import crypto from "crypto";
|
||||||
|
|
||||||
|
import { CryptographyError } from "@app/lib/errors";
|
||||||
|
|
||||||
|
export const hasherFipsValidated = () => {
|
||||||
|
const keySize = 32;
|
||||||
|
|
||||||
|
// For the salt when using pkdf2, we do salt rounds^6. If the salt rounds are 10, this will result in 10^6 = 1.000.000 iterations.
|
||||||
|
// The reason for this is because pbkdf2 is not as compute intense as bcrypt, making it faster to brute-force.
|
||||||
|
// From my testing, doing salt rounds^6 brings the computational power required to a little more than bcrypt.
|
||||||
|
// OWASP recommends a minimum of 600.000 iterations for pbkdf2, so 1.000.000 is more than enough.
|
||||||
|
// Ref: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
|
||||||
|
const MIN_COST_FACTOR = 10;
|
||||||
|
const MAX_COST_FACTOR = 20; // Iterations scales polynomial (costFactor^6), so we need an upper bound
|
||||||
|
|
||||||
|
const $calculateIterations = (costFactor: number) => {
|
||||||
|
return Math.round(costFactor ** 6);
|
||||||
|
};
|
||||||
|
|
||||||
|
const $hashPassword = (password: Buffer, salt: Buffer, iterations: number, keyLength: number) => {
|
||||||
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
|
crypto.pbkdf2(password, salt, iterations, keyLength, "sha256", (err, derivedKey) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
resolve(derivedKey);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const $validatePassword = async (
|
||||||
|
inputPassword: Buffer,
|
||||||
|
storedHash: Buffer,
|
||||||
|
salt: Buffer,
|
||||||
|
iterations: number,
|
||||||
|
keyLength: number
|
||||||
|
) => {
|
||||||
|
const computedHash = await $hashPassword(inputPassword, salt, iterations, keyLength);
|
||||||
|
|
||||||
|
return crypto.timingSafeEqual(computedHash, storedHash);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hash = async (password: string, costFactor: number) => {
|
||||||
|
// Strict input validation
|
||||||
|
if (typeof password !== "string" || password.length === 0) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message: "Invalid input, password must be a non-empty string"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Number.isInteger(costFactor)) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message: "Invalid cost factor, must be an integer"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (costFactor < MIN_COST_FACTOR || costFactor > MAX_COST_FACTOR) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message: `Invalid cost factor, must be between ${MIN_COST_FACTOR} and ${MAX_COST_FACTOR}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const iterations = $calculateIterations(costFactor);
|
||||||
|
|
||||||
|
const salt = crypto.randomBytes(16);
|
||||||
|
const derivedKey = await $hashPassword(Buffer.from(password), salt, iterations, keySize);
|
||||||
|
|
||||||
|
const combined = Buffer.concat([salt, derivedKey]);
|
||||||
|
return `$v1$${costFactor}$${combined.toString("base64")}`; // Store original costFactor!
|
||||||
|
};
|
||||||
|
|
||||||
|
const compare = async (password: string, hashedPassword: string) => {
|
||||||
|
try {
|
||||||
|
if (!hashedPassword?.startsWith("$v1$")) return false;
|
||||||
|
|
||||||
|
const parts = hashedPassword.split("$");
|
||||||
|
if (parts.length !== 4) return false;
|
||||||
|
|
||||||
|
const [, , storedCostFactor, combined] = parts;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Number.isInteger(Number(storedCostFactor)) ||
|
||||||
|
Number(storedCostFactor) < MIN_COST_FACTOR ||
|
||||||
|
Number(storedCostFactor) > MAX_COST_FACTOR
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const combinedBuffer = Buffer.from(combined, "base64");
|
||||||
|
const salt = combinedBuffer.subarray(0, 16);
|
||||||
|
const storedHash = combinedBuffer.subarray(16);
|
||||||
|
|
||||||
|
const iterations = $calculateIterations(Number(storedCostFactor));
|
||||||
|
|
||||||
|
const isMatch = await $validatePassword(Buffer.from(password), storedHash, salt, iterations, keySize);
|
||||||
|
|
||||||
|
return isMatch;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
hash,
|
||||||
|
compare
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export { crypto } from "./crypto";
|
||||||
|
export {
|
||||||
|
DigestType,
|
||||||
|
SymmetricKeySize,
|
||||||
|
TDecryptAsymmetricInput,
|
||||||
|
TDecryptSymmetricInput,
|
||||||
|
TEncryptedWithRootEncryptionKey,
|
||||||
|
TEncryptSymmetricInput
|
||||||
|
} from "./types";
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
import crypto from "crypto";
|
||||||
|
import RE2 from "re2";
|
||||||
|
|
||||||
|
import { Algorithm, CompleteJWTPayload, JWTPayload, JWTSecretOrKey, JWTSignOptions, JWTVerifyOptions } from "./types";
|
||||||
|
|
||||||
|
export const jwtFipsValidated = () => {
|
||||||
|
const $base64urlEncode = (str: string) => Buffer.from(str).toString("base64url");
|
||||||
|
const $isRSAAlgorithm = (algorithm: string) => algorithm.startsWith("RS");
|
||||||
|
|
||||||
|
const $parseTimeToSeconds = (timeStr: string | number) => {
|
||||||
|
if (typeof timeStr === "number") {
|
||||||
|
return timeStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = new RE2(/^(\d+)([smhd])$/).exec(timeStr);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error(`Invalid time format: ${timeStr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = parseInt(match[1], 10);
|
||||||
|
const unit = match[2];
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case "s":
|
||||||
|
return value;
|
||||||
|
case "m":
|
||||||
|
return value * 60;
|
||||||
|
case "h":
|
||||||
|
return value * 60 * 60;
|
||||||
|
case "d":
|
||||||
|
return value * 60 * 60 * 24;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown time unit: ${unit}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const $getHashAlgorithm = (algorithm: string) => {
|
||||||
|
switch (algorithm) {
|
||||||
|
case "HS256":
|
||||||
|
case "RS256":
|
||||||
|
return "sha256";
|
||||||
|
case "HS384":
|
||||||
|
case "RS384":
|
||||||
|
return "sha384";
|
||||||
|
case "HS512":
|
||||||
|
case "RS512":
|
||||||
|
return "sha512";
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported algorithm: ${algorithm}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign a JWT token
|
||||||
|
*/
|
||||||
|
const sign = (payload: JWTPayload, secretOrPrivateKey: JWTSecretOrKey, options: JWTSignOptions = {}) => {
|
||||||
|
const algorithm = options.algorithm || "HS256";
|
||||||
|
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
// Create header
|
||||||
|
const header = {
|
||||||
|
alg: algorithm,
|
||||||
|
typ: "JWT",
|
||||||
|
...(options.keyid && { kid: options.keyid })
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create payload with timestamps
|
||||||
|
const finalPayload = {
|
||||||
|
...payload,
|
||||||
|
iat: now,
|
||||||
|
...(options.expiresIn !== undefined && { exp: now + $parseTimeToSeconds(options.expiresIn) })
|
||||||
|
};
|
||||||
|
|
||||||
|
// Encode header and payload
|
||||||
|
const encodedHeader = $base64urlEncode(JSON.stringify(header));
|
||||||
|
const encodedPayload = $base64urlEncode(JSON.stringify(finalPayload));
|
||||||
|
|
||||||
|
// Create signature
|
||||||
|
const data = `${encodedHeader}.${encodedPayload}`;
|
||||||
|
const hashAlgorithm = $getHashAlgorithm(algorithm);
|
||||||
|
let signature: string;
|
||||||
|
|
||||||
|
if ($isRSAAlgorithm(algorithm)) {
|
||||||
|
let privateKey: crypto.KeyLike | { key: string | Buffer };
|
||||||
|
|
||||||
|
if (typeof secretOrPrivateKey === "string") {
|
||||||
|
try {
|
||||||
|
// Try to create a proper private key object
|
||||||
|
privateKey = crypto.createPrivateKey(secretOrPrivateKey);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error("Invalid JWT private key");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
privateKey = secretOrPrivateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
const signatureBuffer = crypto.sign(hashAlgorithm, Buffer.from(data), privateKey);
|
||||||
|
signature = signatureBuffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
||||||
|
} else {
|
||||||
|
// HMAC signing
|
||||||
|
if (typeof secretOrPrivateKey !== "string") {
|
||||||
|
throw new Error("HMAC algorithms require a string secret");
|
||||||
|
}
|
||||||
|
signature = crypto
|
||||||
|
.createHmac(hashAlgorithm, secretOrPrivateKey)
|
||||||
|
.update(data)
|
||||||
|
.digest("base64")
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${data}.${signature}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const verify = (token: string, secretOrKey: JWTSecretOrKey, options: JWTVerifyOptions = {}) => {
|
||||||
|
const parts = token.split(".");
|
||||||
|
if (parts.length !== 3) {
|
||||||
|
throw new Error("Invalid JWT format");
|
||||||
|
}
|
||||||
|
|
||||||
|
const [encodedHeader, encodedPayload, signature] = parts;
|
||||||
|
|
||||||
|
// Decode header
|
||||||
|
const headerJson = Buffer.from(encodedHeader, "base64").toString();
|
||||||
|
const header = JSON.parse(headerJson) as { alg: string; typ: string; kid?: string };
|
||||||
|
|
||||||
|
if (!header.alg || header.typ !== "JWT") {
|
||||||
|
throw new Error("Invalid JWT header");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the actual key from different input types
|
||||||
|
let keyString: string;
|
||||||
|
if (Buffer.isBuffer(secretOrKey)) {
|
||||||
|
keyString = secretOrKey.toString();
|
||||||
|
} else if (typeof secretOrKey === "object" && "key" in secretOrKey) {
|
||||||
|
keyString = Buffer.isBuffer(secretOrKey.key) ? secretOrKey.key.toString() : secretOrKey.key;
|
||||||
|
} else {
|
||||||
|
keyString = secretOrKey as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify signature
|
||||||
|
const data = `${encodedHeader}.${encodedPayload}`;
|
||||||
|
const hashAlgorithm = $getHashAlgorithm(header.alg);
|
||||||
|
let isValidSignature: boolean;
|
||||||
|
|
||||||
|
if ($isRSAAlgorithm(header.alg)) {
|
||||||
|
// For RSA, handle both private and public keys
|
||||||
|
let verificationKey: crypto.KeyLike;
|
||||||
|
|
||||||
|
// Clean up the key format
|
||||||
|
const cleanKey = keyString.replace(/\\n/g, "\n");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// If it's a private key, extract the public key for verification
|
||||||
|
if (cleanKey.includes("PRIVATE KEY")) {
|
||||||
|
const privateKeyObj = crypto.createPrivateKey(cleanKey);
|
||||||
|
verificationKey = crypto.createPublicKey(privateKeyObj);
|
||||||
|
} else {
|
||||||
|
// It's already a public key
|
||||||
|
verificationKey = crypto.createPublicKey(cleanKey);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error("Invalid JWT signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert base64url signature back to buffer
|
||||||
|
const signatureBuffer = Buffer.from(
|
||||||
|
signature.replace(/-/g, "+").replace(/_/g, "/") + "==".slice(0, (4 - (signature.length % 4)) % 4),
|
||||||
|
"base64"
|
||||||
|
);
|
||||||
|
isValidSignature = crypto.verify(hashAlgorithm, Buffer.from(data), verificationKey, signatureBuffer);
|
||||||
|
} else {
|
||||||
|
// HMAC verification
|
||||||
|
const expectedSignature = crypto
|
||||||
|
.createHmac(hashAlgorithm, keyString)
|
||||||
|
.update(data)
|
||||||
|
.digest("base64")
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=/g, "");
|
||||||
|
isValidSignature = signature === expectedSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidSignature) {
|
||||||
|
throw new Error("Invalid JWT signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode payload
|
||||||
|
const payloadJson = Buffer.from(encodedPayload, "base64").toString();
|
||||||
|
const payload = JSON.parse(payloadJson) as JWTPayload & {
|
||||||
|
aud?: string | string[];
|
||||||
|
iss?: string;
|
||||||
|
sub?: string;
|
||||||
|
nbf?: number;
|
||||||
|
jti?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const clockTolerance = options.clockTolerance || 0;
|
||||||
|
|
||||||
|
// Check expiration
|
||||||
|
if (!options.ignoreExpiration && payload.exp && now - clockTolerance > payload.exp) {
|
||||||
|
throw new Error("JWT token has expired");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check not before
|
||||||
|
if (!options.ignoreNotBefore && payload.nbf && now + clockTolerance < payload.nbf) {
|
||||||
|
throw new Error("JWT not active");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check audience
|
||||||
|
if (options.audience) {
|
||||||
|
const audiences = Array.isArray(options.audience) ? options.audience : [options.audience];
|
||||||
|
const tokenAudiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
|
||||||
|
const hasValidAudience = audiences.some((aud) => tokenAudiences.includes(aud));
|
||||||
|
if (!hasValidAudience) {
|
||||||
|
throw new Error("JWT audience invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check issuer
|
||||||
|
if (options.issuer) {
|
||||||
|
const issuers = Array.isArray(options.issuer) ? options.issuer : [options.issuer];
|
||||||
|
if (!payload.iss || !issuers.includes(payload.iss)) {
|
||||||
|
throw new Error("JWT issuer invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check subject
|
||||||
|
if (options.subject && payload.sub !== options.subject) {
|
||||||
|
throw new Error("JWT subject invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check JWT ID
|
||||||
|
if (options.jwtid && payload.jti !== options.jwtid) {
|
||||||
|
throw new Error("JWT ID invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check max age
|
||||||
|
if (options.maxAge && payload.iat) {
|
||||||
|
const maxAgeSeconds = typeof options.maxAge === "string" ? $parseTimeToSeconds(options.maxAge) : options.maxAge;
|
||||||
|
if (now - payload.iat > maxAgeSeconds) {
|
||||||
|
throw new Error("JWT max age exceeded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check algorithms
|
||||||
|
if (options.algorithms && !options.algorithms.includes(header.alg as Algorithm)) {
|
||||||
|
throw new Error(`Algorithm not allowed: ${header.alg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
};
|
||||||
|
|
||||||
|
const decode = (token: string, options: { complete?: boolean } = {}): JWTPayload | CompleteJWTPayload => {
|
||||||
|
const parts = token.split(".");
|
||||||
|
if (parts.length !== 3) {
|
||||||
|
throw new Error("Invalid JWT format");
|
||||||
|
}
|
||||||
|
|
||||||
|
const [encodedHeader, encodedPayload] = parts;
|
||||||
|
|
||||||
|
// Decode header
|
||||||
|
const headerJson = Buffer.from(encodedHeader, "base64").toString();
|
||||||
|
const header = JSON.parse(headerJson) as Record<string, unknown>;
|
||||||
|
|
||||||
|
// Decode payload
|
||||||
|
const payloadJson = Buffer.from(encodedPayload, "base64").toString();
|
||||||
|
const payload = JSON.parse(payloadJson) as Record<string, unknown>;
|
||||||
|
|
||||||
|
// Return complete token info or just payload
|
||||||
|
if (options.complete) {
|
||||||
|
return {
|
||||||
|
header,
|
||||||
|
payload,
|
||||||
|
signature: parts[2]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload as JWTPayload;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
sign,
|
||||||
|
verify,
|
||||||
|
decode
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { KeyObject } from "crypto";
|
||||||
|
|
||||||
|
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
|
||||||
|
|
||||||
|
export enum DigestType {
|
||||||
|
Hex = "hex",
|
||||||
|
Base64 = "base64"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SymmetricKeySize {
|
||||||
|
Bits128 = "128-bits",
|
||||||
|
Bits256 = "256-bits"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TDecryptSymmetricInput =
|
||||||
|
| {
|
||||||
|
ciphertext: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
key: string | Buffer; // can be hex encoded or buffer
|
||||||
|
keySize: SymmetricKeySize.Bits128;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
ciphertext: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
key: string; // must be base64 encoded
|
||||||
|
keySize: SymmetricKeySize.Bits256;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TEncryptSymmetricInput =
|
||||||
|
| {
|
||||||
|
plaintext: string;
|
||||||
|
key: string;
|
||||||
|
keySize: SymmetricKeySize.Bits256;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
plaintext: string;
|
||||||
|
key: string | Buffer;
|
||||||
|
keySize: SymmetricKeySize.Bits128;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TDecryptAsymmetricInput = {
|
||||||
|
ciphertext: string;
|
||||||
|
nonce: string;
|
||||||
|
publicKey: string;
|
||||||
|
privateKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TEncryptedWithRootEncryptionKey = {
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
ciphertext: string;
|
||||||
|
algorithm: SecretEncryptionAlgo;
|
||||||
|
encoding: SecretKeyEncoding;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface JWTPayload {
|
||||||
|
iat?: number;
|
||||||
|
exp?: number;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CompleteJWTPayload {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
header: any;
|
||||||
|
payload: JWTPayload;
|
||||||
|
signature: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Algorithm = "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS512";
|
||||||
|
|
||||||
|
export interface JWTSignOptions {
|
||||||
|
algorithm?: Algorithm | undefined;
|
||||||
|
keyid?: string | undefined;
|
||||||
|
expiresIn?: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type JWTSecretOrKey = string | Buffer | KeyObject | { key: string | Buffer; passphrase: string };
|
||||||
|
|
||||||
|
export interface JWTVerifyOptions {
|
||||||
|
algorithms?: Algorithm[] | undefined;
|
||||||
|
audience?: string | string[];
|
||||||
|
issuer?: string | string[];
|
||||||
|
subject?: string;
|
||||||
|
ignoreExpiration?: boolean;
|
||||||
|
ignoreNotBefore?: boolean;
|
||||||
|
clockTolerance?: number;
|
||||||
|
maxAge?: string | number;
|
||||||
|
jwtid?: string;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export { crypto, SymmetricKeySize } from "./cryptography";
|
export { crypto, SymmetricKeySize, TEncryptedWithRootEncryptionKey } from "./cryptography";
|
||||||
export { buildSecretBlindIndexFromName } from "./encryption";
|
export { buildSecretBlindIndexFromName } from "./encryption";
|
||||||
export {
|
export {
|
||||||
decryptIntegrationAuths,
|
decryptIntegrationAuths,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { requestContext } from "@fastify/request-context";
|
import { requestContext } from "@fastify/request-context";
|
||||||
import { FastifyRequest } from "fastify";
|
import { FastifyRequest } from "fastify";
|
||||||
import fp from "fastify-plugin";
|
import fp from "fastify-plugin";
|
||||||
import jwt, { JwtPayload } from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { TServiceTokens, TUsers } from "@app/db/schemas";
|
import { TServiceTokens, TUsers } from "@app/db/schemas";
|
||||||
import { TScimTokenJwtPayload } from "@app/ee/services/scim/scim-types";
|
import { TScimTokenJwtPayload } from "@app/ee/services/scim/scim-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
|
import { JWTPayload } from "@app/lib/crypto/cryptography/types";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
||||||
@@ -72,7 +73,7 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
|
|||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedToken = jwt.verify(authTokenValue, jwtSecret) as JwtPayload;
|
const decodedToken = crypto.jwt().verify(authTokenValue, jwtSecret) as JWTPayload;
|
||||||
|
|
||||||
switch (decodedToken.authTokenType) {
|
switch (decodedToken.authTokenType) {
|
||||||
case AuthTokenType.ACCESS_TOKEN:
|
case AuthTokenType.ACCESS_TOKEN:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { getMinExpiresIn } from "@app/lib/fn";
|
import { getMinExpiresIn } from "@app/lib/fn";
|
||||||
import { authRateLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { authRateLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@@ -93,7 +93,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = jwt.sign(
|
const token = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authMethod: decodedToken.authMethod,
|
authMethod: decodedToken.authMethod,
|
||||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { mfaRateLimit } from "@app/server/config/rateLimiter";
|
import { mfaRateLimit } from "@app/server/config/rateLimiter";
|
||||||
import { AuthModeMfaJwtTokenPayload, AuthTokenType, MfaMethod } from "@app/services/auth/auth-type";
|
import { AuthModeMfaJwtTokenPayload, AuthTokenType, MfaMethod } from "@app/services/auth/auth-type";
|
||||||
@@ -23,7 +23,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedToken = jwt.verify(token, cfg.AUTH_SECRET) as AuthModeMfaJwtTokenPayload;
|
const decodedToken = crypto.jwt().verify(token, cfg.AUTH_SECRET) as AuthModeMfaJwtTokenPayload;
|
||||||
if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN) throw new Error("Unauthorized access");
|
if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN) throw new Error("Unauthorized access");
|
||||||
|
|
||||||
const user = await server.store.user.findById(decodedToken.userId);
|
const user = await server.store.user.findById(decodedToken.userId);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TAuthTokens, TAuthTokenSessions } from "@app/db/schemas";
|
import { TAuthTokens, TAuthTokenSessions } from "@app/db/schemas";
|
||||||
@@ -160,7 +159,7 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, orgMembershipDAL }: TAu
|
|||||||
message: "Failed to find refresh token"
|
message: "Failed to find refresh token"
|
||||||
});
|
});
|
||||||
|
|
||||||
const decodedToken = jwt.verify(refreshToken, appCfg.AUTH_SECRET) as AuthModeRefreshJwtTokenPayload;
|
const decodedToken = crypto.jwt().verify(refreshToken, appCfg.AUTH_SECRET) as AuthModeRefreshJwtTokenPayload;
|
||||||
|
|
||||||
if (decodedToken.authTokenType !== AuthTokenType.REFRESH_TOKEN)
|
if (decodedToken.authTokenType !== AuthTokenType.REFRESH_TOKEN)
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
|
|
||||||
import { AuthModeProviderJwtTokenPayload, AuthModeProviderSignUpTokenPayload, AuthTokenType } from "./auth-type";
|
import { AuthModeProviderJwtTokenPayload, AuthModeProviderSignUpTokenPayload, AuthTokenType } from "./auth-type";
|
||||||
@@ -8,7 +7,7 @@ import { AuthModeProviderJwtTokenPayload, AuthModeProviderSignUpTokenPayload, Au
|
|||||||
export const validateProviderAuthToken = (providerToken: string, username?: string) => {
|
export const validateProviderAuthToken = (providerToken: string, username?: string) => {
|
||||||
if (!providerToken) throw new UnauthorizedError();
|
if (!providerToken) throw new UnauthorizedError();
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const decodedToken = jwt.verify(providerToken, appCfg.AUTH_SECRET) as AuthModeProviderJwtTokenPayload;
|
const decodedToken = crypto.jwt().verify(providerToken, appCfg.AUTH_SECRET) as AuthModeProviderJwtTokenPayload;
|
||||||
|
|
||||||
if (decodedToken.authTokenType !== AuthTokenType.PROVIDER_TOKEN) throw new UnauthorizedError();
|
if (decodedToken.authTokenType !== AuthTokenType.PROVIDER_TOKEN) throw new UnauthorizedError();
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ export const validateSignUpAuthorization = (token: string, userId: string, valid
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedToken = jwt.verify(AUTH_TOKEN_VALUE, appCfg.AUTH_SECRET) as AuthModeProviderSignUpTokenPayload;
|
const decodedToken = crypto.jwt().verify(AUTH_TOKEN_VALUE, appCfg.AUTH_SECRET) as AuthModeProviderSignUpTokenPayload;
|
||||||
if (!validate) return decodedToken;
|
if (!validate) return decodedToken;
|
||||||
|
|
||||||
if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw new UnauthorizedError();
|
if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw new UnauthorizedError();
|
||||||
@@ -64,7 +63,7 @@ export const validatePasswordResetAuthorization = (token?: string) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedToken = jwt.verify(AUTH_TOKEN_VALUE, appCfg.AUTH_SECRET) as AuthModeProviderSignUpTokenPayload;
|
const decodedToken = crypto.jwt().verify(AUTH_TOKEN_VALUE, appCfg.AUTH_SECRET) as AuthModeProviderSignUpTokenPayload;
|
||||||
|
|
||||||
if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) {
|
if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) {
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { OrgMembershipRole, OrgMembershipStatus, TableName, TUsers, UserDeviceSchema } from "@app/db/schemas";
|
import { OrgMembershipRole, OrgMembershipStatus, TableName, TUsers, UserDeviceSchema } from "@app/db/schemas";
|
||||||
@@ -6,8 +5,7 @@ import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/a
|
|||||||
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
|
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
|
import { crypto, generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
|
||||||
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||||
import { BadRequestError, DatabaseError, ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, DatabaseError, ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { getMinExpiresIn, removeTrailingSlash } from "@app/lib/fn";
|
import { getMinExpiresIn, removeTrailingSlash } from "@app/lib/fn";
|
||||||
@@ -156,7 +154,7 @@ export const authLoginServiceFactory = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authMethod,
|
authMethod,
|
||||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||||
@@ -171,7 +169,7 @@ export const authLoginServiceFactory = ({
|
|||||||
{ expiresIn: tokenSessionExpiresIn }
|
{ expiresIn: tokenSessionExpiresIn }
|
||||||
);
|
);
|
||||||
|
|
||||||
const refreshToken = jwt.sign(
|
const refreshToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authMethod,
|
authMethod,
|
||||||
authTokenType: AuthTokenType.REFRESH_TOKEN,
|
authTokenType: AuthTokenType.REFRESH_TOKEN,
|
||||||
@@ -390,7 +388,7 @@ export const authLoginServiceFactory = ({
|
|||||||
authJwtToken = authJwtToken.replace("Bearer ", ""); // remove bearer from token
|
authJwtToken = authJwtToken.replace("Bearer ", ""); // remove bearer from token
|
||||||
|
|
||||||
// The decoded JWT token, which contains the auth method.
|
// The decoded JWT token, which contains the auth method.
|
||||||
const decodedToken = jwt.verify(authJwtToken, cfg.AUTH_SECRET) as AuthModeJwtTokenPayload;
|
const decodedToken = crypto.jwt().verify(authJwtToken, cfg.AUTH_SECRET) as AuthModeJwtTokenPayload;
|
||||||
if (!decodedToken.authMethod) throw new UnauthorizedError({ name: "Auth method not found on existing token" });
|
if (!decodedToken.authMethod) throw new UnauthorizedError({ name: "Auth method not found on existing token" });
|
||||||
|
|
||||||
const user = await userDAL.findUserEncKeyByUserId(decodedToken.userId);
|
const user = await userDAL.findUserEncKeyByUserId(decodedToken.userId);
|
||||||
@@ -415,7 +413,7 @@ export const authLoginServiceFactory = ({
|
|||||||
if (shouldCheckMfa && (!decodedToken.isMfaVerified || decodedToken.mfaMethod !== mfaMethod)) {
|
if (shouldCheckMfa && (!decodedToken.isMfaVerified || decodedToken.mfaMethod !== mfaMethod)) {
|
||||||
enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd);
|
enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd);
|
||||||
|
|
||||||
const mfaToken = jwt.sign(
|
const mfaToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authMethod: decodedToken.authMethod,
|
authMethod: decodedToken.authMethod,
|
||||||
authTokenType: AuthTokenType.MFA_TOKEN,
|
authTokenType: AuthTokenType.MFA_TOKEN,
|
||||||
@@ -626,7 +624,7 @@ export const authLoginServiceFactory = ({
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedToken = jwt.verify(mfaJwtToken, getConfig().AUTH_SECRET) as AuthModeMfaJwtTokenPayload;
|
const decodedToken = crypto.jwt().verify(mfaJwtToken, getConfig().AUTH_SECRET) as AuthModeMfaJwtTokenPayload;
|
||||||
|
|
||||||
const userEnc = await userDAL.findUserEncKeyByUserId(userId);
|
const userEnc = await userDAL.findUserEncKeyByUserId(userId);
|
||||||
if (!userEnc) throw new Error("Failed to authenticate user");
|
if (!userEnc) throw new Error("Failed to authenticate user");
|
||||||
@@ -776,7 +774,7 @@ export const authLoginServiceFactory = ({
|
|||||||
|
|
||||||
const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
|
const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
|
||||||
const isUserCompleted = user.isAccepted;
|
const isUserCompleted = user.isAccepted;
|
||||||
const providerAuthToken = jwt.sign(
|
const providerAuthToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
|
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
|
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
|
||||||
@@ -174,7 +172,7 @@ export const authPaswordServiceFactory = ({
|
|||||||
code
|
code
|
||||||
});
|
});
|
||||||
|
|
||||||
const token = jwt.sign(
|
const token = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.SIGNUP_TOKEN,
|
authTokenType: AuthTokenType.SIGNUP_TOKEN,
|
||||||
userId: user.id
|
userId: user.id
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { OrgMembershipStatus, SecretKeyEncoding, TableName } from "@app/db/schemas";
|
import { OrgMembershipStatus, SecretKeyEncoding, TableName } from "@app/db/schemas";
|
||||||
import { convertPendingGroupAdditionsToGroupMemberships } from "@app/ee/services/group/group-fns";
|
import { convertPendingGroupAdditionsToGroupMemberships } from "@app/ee/services/group/group-fns";
|
||||||
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||||
@@ -131,7 +129,7 @@ export const authSignupServiceFactory = ({
|
|||||||
await userDAL.updateById(user.id, { isEmailVerified: true });
|
await userDAL.updateById(user.id, { isEmailVerified: true });
|
||||||
|
|
||||||
// generate jwt token this is a temporary token
|
// generate jwt token this is a temporary token
|
||||||
const jwtToken = jwt.sign(
|
const jwtToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.SIGNUP_TOKEN,
|
authTokenType: AuthTokenType.SIGNUP_TOKEN,
|
||||||
userId: user.id.toString()
|
userId: user.id.toString()
|
||||||
@@ -364,7 +362,7 @@ export const authSignupServiceFactory = ({
|
|||||||
});
|
});
|
||||||
if (!tokenSession) throw new Error("Failed to create token");
|
if (!tokenSession) throw new Error("Failed to create token");
|
||||||
|
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authMethod: authMethod || AuthMethod.EMAIL,
|
authMethod: authMethod || AuthMethod.EMAIL,
|
||||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||||
@@ -377,7 +375,7 @@ export const authSignupServiceFactory = ({
|
|||||||
{ expiresIn: tokenSessionExpiresIn }
|
{ expiresIn: tokenSessionExpiresIn }
|
||||||
);
|
);
|
||||||
|
|
||||||
const refreshToken = jwt.sign(
|
const refreshToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authMethod: authMethod || AuthMethod.EMAIL,
|
authMethod: authMethod || AuthMethod.EMAIL,
|
||||||
authTokenType: AuthTokenType.REFRESH_TOKEN,
|
authTokenType: AuthTokenType.REFRESH_TOKEN,
|
||||||
@@ -551,7 +549,7 @@ export const authSignupServiceFactory = ({
|
|||||||
});
|
});
|
||||||
if (!tokenSession) throw new Error("Failed to create token");
|
if (!tokenSession) throw new Error("Failed to create token");
|
||||||
|
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authMethod: AuthMethod.EMAIL,
|
authMethod: AuthMethod.EMAIL,
|
||||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||||
@@ -563,7 +561,7 @@ export const authSignupServiceFactory = ({
|
|||||||
{ expiresIn: appCfg.JWT_SIGNUP_LIFETIME }
|
{ expiresIn: appCfg.JWT_SIGNUP_LIFETIME }
|
||||||
);
|
);
|
||||||
|
|
||||||
const refreshToken = jwt.sign(
|
const refreshToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authMethod: AuthMethod.EMAIL,
|
authMethod: AuthMethod.EMAIL,
|
||||||
authTokenType: AuthTokenType.REFRESH_TOKEN,
|
authTokenType: AuthTokenType.REFRESH_TOKEN,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import jwt, { JwtPayload } from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { IdentityAuthMethod, TableName, TIdentityAccessTokens } from "@app/db/schemas";
|
import { IdentityAuthMethod, TableName, TIdentityAccessTokens } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
|
import { JWTPayload } from "@app/lib/crypto/cryptography/types";
|
||||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip";
|
import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip";
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ export const identityAccessTokenServiceFactory = ({
|
|||||||
const renewAccessToken = async ({ accessToken }: TRenewAccessTokenDTO) => {
|
const renewAccessToken = async ({ accessToken }: TRenewAccessTokenDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
const decodedToken = jwt.verify(accessToken, appCfg.AUTH_SECRET) as TIdentityAccessTokenJwtPayload;
|
const decodedToken = crypto.jwt().verify(accessToken, appCfg.AUTH_SECRET) as TIdentityAccessTokenJwtPayload;
|
||||||
if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) {
|
if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) {
|
||||||
throw new BadRequestError({ message: "Only identity access tokens can be renewed" });
|
throw new BadRequestError({ message: "Only identity access tokens can be renewed" });
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ export const identityAccessTokenServiceFactory = ({
|
|||||||
expiresIn = undefined;
|
expiresIn = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renewedToken = jwt.sign(
|
const renewedToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: decodedToken.identityId,
|
identityId: decodedToken.identityId,
|
||||||
clientSecretId: decodedToken.clientSecretId,
|
clientSecretId: decodedToken.clientSecretId,
|
||||||
@@ -162,7 +162,7 @@ export const identityAccessTokenServiceFactory = ({
|
|||||||
const revokeAccessToken = async (accessToken: string) => {
|
const revokeAccessToken = async (accessToken: string) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
const decodedToken = jwt.verify(accessToken, appCfg.AUTH_SECRET) as JwtPayload & {
|
const decodedToken = crypto.jwt().verify(accessToken, appCfg.AUTH_SECRET) as JWTPayload & {
|
||||||
identityAccessTokenId: string;
|
identityAccessTokenId: string;
|
||||||
};
|
};
|
||||||
if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) {
|
if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
@@ -103,7 +103,7 @@ export const identityAliCloudAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityAliCloudAuth.identityId,
|
identityId: identityAliCloudAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
import RE2 from "re2";
|
import RE2 from "re2";
|
||||||
|
|
||||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
} from "@app/ee/services/permission/permission-fns";
|
} from "@app/ee/services/permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ export const identityAwsAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityAwsAuth.identityId,
|
identityId: identityAwsAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { UnauthorizedError } from "@app/lib/errors";
|
import { UnauthorizedError } from "@app/lib/errors";
|
||||||
|
|
||||||
import { TAzureAuthJwtPayload, TAzureJwksUriResponse, TDecodedAzureAuthJwt } from "./identity-azure-auth-types";
|
import { TAzureAuthJwtPayload, TAzureJwksUriResponse, TDecodedAzureAuthJwt } from "./identity-azure-auth-types";
|
||||||
@@ -16,7 +16,7 @@ export const validateAzureIdentity = async ({
|
|||||||
}) => {
|
}) => {
|
||||||
const jwksUri = `https://login.microsoftonline.com/${tenantId}/discovery/keys`;
|
const jwksUri = `https://login.microsoftonline.com/${tenantId}/discovery/keys`;
|
||||||
|
|
||||||
const decodedJwt = jwt.decode(azureJwt, { complete: true }) as TDecodedAzureAuthJwt;
|
const decodedJwt = crypto.jwt().decode(azureJwt, { complete: true }) as TDecodedAzureAuthJwt;
|
||||||
|
|
||||||
const { kid } = decodedJwt.header;
|
const { kid } = decodedJwt.header;
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ export const validateAzureIdentity = async ({
|
|||||||
resource = resource.slice(0, -1);
|
resource = resource.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jwt.verify(azureJwt, publicKey, {
|
return crypto.jwt().verify(azureJwt, publicKey, {
|
||||||
audience: resource,
|
audience: resource,
|
||||||
issuer: `https://sts.windows.net/${tenantId}/`
|
issuer: `https://sts.windows.net/${tenantId}/`
|
||||||
}) as TAzureAuthJwtPayload;
|
}) as TAzureAuthJwtPayload;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
} from "@app/ee/services/permission/permission-fns";
|
} from "@app/ee/services/permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ export const identityAzureAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityAzureAuth.identityId,
|
identityId: identityAzureAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { OAuth2Client } from "google-auth-library";
|
import { OAuth2Client } from "google-auth-library";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { UnauthorizedError } from "@app/lib/errors";
|
import { UnauthorizedError } from "@app/lib/errors";
|
||||||
|
|
||||||
import { TDecodedGcpIamAuthJwt, TGcpIdTokenPayload } from "./identity-gcp-auth-types";
|
import { TDecodedGcpIamAuthJwt, TGcpIdTokenPayload } from "./identity-gcp-auth-types";
|
||||||
@@ -48,7 +48,7 @@ export const validateIamIdentity = async ({
|
|||||||
identityId: string;
|
identityId: string;
|
||||||
jwt: string;
|
jwt: string;
|
||||||
}) => {
|
}) => {
|
||||||
const decodedJwt = jwt.decode(serviceAccountJwt, { complete: true }) as TDecodedGcpIamAuthJwt;
|
const decodedJwt = crypto.jwt().decode(serviceAccountJwt, { complete: true }) as TDecodedGcpIamAuthJwt;
|
||||||
const { sub, aud } = decodedJwt.payload;
|
const { sub, aud } = decodedJwt.payload;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -61,7 +61,7 @@ export const validateIamIdentity = async ({
|
|||||||
|
|
||||||
const publicKey = data[decodedJwt.header.kid];
|
const publicKey = data[decodedJwt.header.kid];
|
||||||
|
|
||||||
jwt.verify(serviceAccountJwt, publicKey, {
|
crypto.jwt().verify(serviceAccountJwt, publicKey, {
|
||||||
algorithms: ["RS256"]
|
algorithms: ["RS256"]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
} from "@app/ee/services/permission/permission-fns";
|
} from "@app/ee/services/permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ export const identityGcpAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityGcpAuth.identityId,
|
identityId: identityGcpAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import {
|
|||||||
} from "@app/ee/services/permission/permission-fns";
|
} from "@app/ee/services/permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
|
import { CompleteJWTPayload } from "@app/lib/crypto/cryptography/types";
|
||||||
import {
|
import {
|
||||||
BadRequestError,
|
BadRequestError,
|
||||||
ForbiddenRequestError,
|
ForbiddenRequestError,
|
||||||
@@ -79,7 +81,7 @@ export const identityJwtAuthServiceFactory = ({
|
|||||||
orgId: identityMembershipOrg.orgId
|
orgId: identityMembershipOrg.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
const decodedToken = jwt.decode(jwtValue, { complete: true });
|
const decodedToken = crypto.jwt().decode(jwtValue, { complete: true }) as CompleteJWTPayload;
|
||||||
if (!decodedToken) {
|
if (!decodedToken) {
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: "Invalid JWT"
|
message: "Invalid JWT"
|
||||||
@@ -106,11 +108,11 @@ export const identityJwtAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { kid } = decodedToken.header;
|
const { kid } = decodedToken.header as { kid: string };
|
||||||
const jwtSigningKey = await client.getSigningKey(kid);
|
const jwtSigningKey = await client.getSigningKey(kid);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tokenData = jwt.verify(jwtValue, jwtSigningKey.getPublicKey()) as Record<string, string>;
|
tokenData = crypto.jwt().verify(jwtValue, jwtSigningKey.getPublicKey()) as Record<string, string>;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof jwt.JsonWebTokenError) {
|
if (error instanceof jwt.JsonWebTokenError) {
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
@@ -129,7 +131,7 @@ export const identityJwtAuthServiceFactory = ({
|
|||||||
let isMatchAnyKey = false;
|
let isMatchAnyKey = false;
|
||||||
for (const publicKey of decryptedPublicKeys) {
|
for (const publicKey of decryptedPublicKeys) {
|
||||||
try {
|
try {
|
||||||
tokenData = jwt.verify(jwtValue, publicKey) as Record<string, string>;
|
tokenData = crypto.jwt().verify(jwtValue, publicKey) as Record<string, string>;
|
||||||
isMatchAnyKey = true;
|
isMatchAnyKey = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof jwt.JsonWebTokenError) {
|
if (error instanceof jwt.JsonWebTokenError) {
|
||||||
@@ -225,7 +227,7 @@ export const identityJwtAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityJwtAuth.identityId,
|
identityId: identityJwtAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import axios, { AxiosError } from "axios";
|
import axios, { AxiosError } from "axios";
|
||||||
import https from "https";
|
import https from "https";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
import RE2 from "re2";
|
import RE2 from "re2";
|
||||||
|
|
||||||
import { IdentityAuthMethod, TIdentityKubernetesAuthsUpdate } from "@app/db/schemas";
|
import { IdentityAuthMethod, TIdentityKubernetesAuthsUpdate } from "@app/db/schemas";
|
||||||
@@ -19,6 +18,7 @@ import {
|
|||||||
} from "@app/ee/services/permission/permission-fns";
|
} from "@app/ee/services/permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { GatewayHttpProxyActions, GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
import { GatewayHttpProxyActions, GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
@@ -396,7 +396,7 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityKubernetesAuth.identityId,
|
identityId: identityKubernetesAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||||
import { testLDAPConfig } from "@app/ee/services/ldap-config/ldap-fns";
|
import { testLDAPConfig } from "@app/ee/services/ldap-config/ldap-fns";
|
||||||
@@ -12,6 +11,7 @@ import {
|
|||||||
} from "@app/ee/services/permission/permission-fns";
|
} from "@app/ee/services/permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ export const identityLdapAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityLdapAuth.identityId,
|
identityId: identityLdapAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
import RE2 from "re2";
|
import RE2 from "re2";
|
||||||
|
|
||||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||||
@@ -14,6 +13,7 @@ import {
|
|||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
@@ -107,7 +107,7 @@ export const identityOciAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityOciAuth.identityId,
|
identityId: identityOciAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
} from "@app/ee/services/permission/permission-fns";
|
} from "@app/ee/services/permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
|
import { CompleteJWTPayload } from "@app/lib/crypto/cryptography/types";
|
||||||
import {
|
import {
|
||||||
BadRequestError,
|
BadRequestError,
|
||||||
ForbiddenRequestError,
|
ForbiddenRequestError,
|
||||||
@@ -93,7 +95,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
);
|
);
|
||||||
const jwksUri = discoveryDoc.jwks_uri;
|
const jwksUri = discoveryDoc.jwks_uri;
|
||||||
|
|
||||||
const decodedToken = jwt.decode(oidcJwt, { complete: true });
|
const decodedToken = crypto.jwt().decode(oidcJwt, { complete: true }) as CompleteJWTPayload;
|
||||||
if (!decodedToken) {
|
if (!decodedToken) {
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: "Invalid JWT"
|
message: "Invalid JWT"
|
||||||
@@ -105,12 +107,12 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
requestAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined
|
requestAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
const { kid } = decodedToken.header;
|
const { kid } = decodedToken.header as { kid: string };
|
||||||
const oidcSigningKey = await client.getSigningKey(kid);
|
const oidcSigningKey = await client.getSigningKey(kid);
|
||||||
|
|
||||||
let tokenData: Record<string, string>;
|
let tokenData: Record<string, string>;
|
||||||
try {
|
try {
|
||||||
tokenData = jwt.verify(oidcJwt, oidcSigningKey.getPublicKey(), {
|
tokenData = crypto.jwt().verify(oidcJwt, oidcSigningKey.getPublicKey(), {
|
||||||
issuer: identityOidcAuth.boundIssuer
|
issuer: identityOidcAuth.boundIssuer
|
||||||
}) as Record<string, string>;
|
}) as Record<string, string>;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -193,7 +195,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityOidcAuth.identityId,
|
identityId: identityOidcAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -135,7 +134,7 @@ export const identityTlsCertAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityTlsCertAuth.identityId,
|
identityId: identityTlsCertAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { IdentityAuthMethod, TableName } from "@app/db/schemas";
|
import { IdentityAuthMethod, TableName } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
} from "@app/ee/services/permission/permission-fns";
|
} from "@app/ee/services/permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
@@ -362,7 +362,7 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityTokenAuth.identityId,
|
identityId: identityTokenAuth.identityId,
|
||||||
identityAccessTokenId: identityAccessToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -147,7 +146,7 @@ export const identityUaServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = jwt.sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityUa.identityId,
|
identityId: identityUa.identityId,
|
||||||
clientSecretId: validClientSecretInfo.id,
|
clientSecretId: validClientSecretInfo.id,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, ForbiddenRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
|
|
||||||
import { Integrations, IntegrationUrls } from "./integration-list";
|
import { Integrations, IntegrationUrls } from "./integration-list";
|
||||||
@@ -718,7 +717,7 @@ const exchangeRefreshGCPSecretManager = async ({
|
|||||||
exp: Math.floor(Date.now() / 1000) + 3600
|
exp: Math.floor(Date.now() / 1000) + 3600
|
||||||
};
|
};
|
||||||
|
|
||||||
const token = jwt.sign(payload, serviceAccount.private_key, { algorithm: "RS256" });
|
const token = crypto.jwt().sign(payload, serviceAccount.private_key, { algorithm: "RS256" });
|
||||||
|
|
||||||
const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = await request.post(
|
const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = await request.post(
|
||||||
IntegrationUrls.GCP_TOKEN_URL,
|
IntegrationUrls.GCP_TOKEN_URL,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable class-methods-use-this */
|
/* eslint-disable class-methods-use-this */
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { TeamsActivityHandler, TurnContext } from "botbuilder";
|
import { TeamsActivityHandler, TurnContext } from "botbuilder";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TNotification, TriggerFeature } from "@app/lib/workflow-integrations/types";
|
import { TNotification, TriggerFeature } from "@app/lib/workflow-integrations/types";
|
||||||
@@ -71,7 +71,7 @@ export const verifyTenantFromCode = async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Verify application token
|
// Verify application token
|
||||||
const { tid: tenantIdFromApplicationAccessToken } = jwt.decode(applicationAccessToken) as { tid: string };
|
const { tid: tenantIdFromApplicationAccessToken } = crypto.jwt().decode(applicationAccessToken) as { tid: string };
|
||||||
|
|
||||||
if (tenantIdFromApplicationAccessToken !== tenantId) {
|
if (tenantIdFromApplicationAccessToken !== tenantId) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -80,7 +80,9 @@ export const verifyTenantFromCode = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify user authorization token
|
// Verify user authorization token
|
||||||
const { tid: tenantIdFromAuthorizationAccessToken } = jwt.decode(authorizationAccessToken) as { tid: string };
|
const { tid: tenantIdFromAuthorizationAccessToken } = crypto.jwt().decode(authorizationAccessToken) as {
|
||||||
|
tid: string;
|
||||||
|
};
|
||||||
|
|
||||||
if (tenantIdFromAuthorizationAccessToken !== tenantId) {
|
if (tenantIdFromAuthorizationAccessToken !== tenantId) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -596,7 +595,7 @@ export const orgServiceFactory = ({
|
|||||||
const cfg = getConfig();
|
const cfg = getConfig();
|
||||||
const authToken = authorizationHeader.replace("Bearer ", "");
|
const authToken = authorizationHeader.replace("Bearer ", "");
|
||||||
|
|
||||||
const decodedToken = jwt.verify(authToken, cfg.AUTH_SECRET) as AuthModeJwtTokenPayload;
|
const decodedToken = crypto.jwt().verify(authToken, cfg.AUTH_SECRET) as AuthModeJwtTokenPayload;
|
||||||
if (!decodedToken.authMethod) throw new UnauthorizedError({ name: "Auth method not found on existing token" });
|
if (!decodedToken.authMethod) throw new UnauthorizedError({ name: "Auth method not found on existing token" });
|
||||||
|
|
||||||
const response = await orgDAL.transaction(async (tx) => {
|
const response = await orgDAL.transaction(async (tx) => {
|
||||||
@@ -1304,7 +1303,7 @@ export const orgServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const token = jwt.sign(
|
const token = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.SIGNUP_TOKEN,
|
authTokenType: AuthTokenType.SIGNUP_TOKEN,
|
||||||
userId: user.id
|
userId: user.id
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { CronJob } from "cron";
|
import { CronJob } from "cron";
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
import { IdentityAuthMethod, OrgMembershipRole, TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
import { IdentityAuthMethod, OrgMembershipRole, TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -670,7 +669,7 @@ export const superAdminServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
const generatedAccessToken = jwt.sign(
|
const generatedAccessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: newIdentity.id,
|
identityId: newIdentity.id,
|
||||||
identityAccessTokenId: newToken.id,
|
identityAccessTokenId: newToken.id,
|
||||||
|
|||||||
Reference in New Issue
Block a user