mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: finished crud endpoints
This commit is contained in:
@@ -14,13 +14,13 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
t.uuid("identityId").notNullable().unique();
|
t.uuid("identityId").notNullable().unique();
|
||||||
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
||||||
t.string("configurationType").notNullable();
|
t.string("configurationType").notNullable();
|
||||||
t.string("jwksUrl");
|
t.string("jwksUrl").notNullable();
|
||||||
t.binary("encryptedJwksCaCert");
|
t.binary("encryptedJwksCaCert").notNullable();
|
||||||
t.binary("encryptedPublicKeys");
|
t.binary("encryptedPublicKeys").notNullable();
|
||||||
t.string("boundIssuer");
|
t.string("boundIssuer").notNullable();
|
||||||
t.string("boundAudiences");
|
t.string("boundAudiences").notNullable();
|
||||||
t.jsonb("boundClaims");
|
t.jsonb("boundClaims").notNullable();
|
||||||
t.string("boundSubject");
|
t.string("boundSubject").notNullable();
|
||||||
t.timestamps(true, true, true);
|
t.timestamps(true, true, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ export const IdentityJwtAuthsSchema = z.object({
|
|||||||
accessTokenTrustedIps: z.unknown(),
|
accessTokenTrustedIps: z.unknown(),
|
||||||
identityId: z.string().uuid(),
|
identityId: z.string().uuid(),
|
||||||
configurationType: z.string(),
|
configurationType: z.string(),
|
||||||
jwksUrl: z.string().nullable().optional(),
|
jwksUrl: z.string(),
|
||||||
encryptedJwksCaCert: zodBuffer.nullable().optional(),
|
encryptedJwksCaCert: zodBuffer,
|
||||||
encryptedPublicKeys: zodBuffer.nullable().optional(),
|
encryptedPublicKeys: zodBuffer,
|
||||||
boundIssuer: z.string().nullable().optional(),
|
boundIssuer: z.string(),
|
||||||
boundAudiences: z.string().nullable().optional(),
|
boundAudiences: z.string(),
|
||||||
boundClaims: z.unknown().nullable().optional(),
|
boundClaims: z.unknown(),
|
||||||
boundSubject: z.string().nullable().optional(),
|
boundSubject: z.string(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -94,6 +94,10 @@ export enum EventType {
|
|||||||
UPDATE_IDENTITY_OIDC_AUTH = "update-identity-oidc-auth",
|
UPDATE_IDENTITY_OIDC_AUTH = "update-identity-oidc-auth",
|
||||||
GET_IDENTITY_OIDC_AUTH = "get-identity-oidc-auth",
|
GET_IDENTITY_OIDC_AUTH = "get-identity-oidc-auth",
|
||||||
REVOKE_IDENTITY_OIDC_AUTH = "revoke-identity-oidc-auth",
|
REVOKE_IDENTITY_OIDC_AUTH = "revoke-identity-oidc-auth",
|
||||||
|
ADD_IDENTITY_JWT_AUTH = "add-identity-jwt-auth",
|
||||||
|
UPDATE_IDENTITY_JWT_AUTH = "update-identity-jwt-auth",
|
||||||
|
GET_IDENTITY_JWT_AUTH = "get-identity-jwt-auth",
|
||||||
|
REVOKE_IDENTITY_JWT_AUTH = "revoke-identity-jwt-auth",
|
||||||
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
||||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
||||||
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
||||||
@@ -895,6 +899,58 @@ interface GetIdentityOidcAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AddIdentityJwtAuthEvent {
|
||||||
|
type: EventType.ADD_IDENTITY_JWT_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
configurationType: string;
|
||||||
|
jwksUrl?: string;
|
||||||
|
jwksCaCert: string;
|
||||||
|
publicKeys: string[];
|
||||||
|
boundIssuer: string;
|
||||||
|
boundAudiences: string;
|
||||||
|
boundClaims: Record<string, string>;
|
||||||
|
boundSubject: string;
|
||||||
|
accessTokenTTL: number;
|
||||||
|
accessTokenMaxTTL: number;
|
||||||
|
accessTokenNumUsesLimit: number;
|
||||||
|
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateIdentityJwtAuthEvent {
|
||||||
|
type: EventType.UPDATE_IDENTITY_JWT_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
configurationType?: string;
|
||||||
|
jwksUrl?: string;
|
||||||
|
jwksCaCert?: string;
|
||||||
|
publicKeys?: string[];
|
||||||
|
boundIssuer?: string;
|
||||||
|
boundAudiences?: string;
|
||||||
|
boundClaims?: Record<string, string>;
|
||||||
|
boundSubject?: string;
|
||||||
|
accessTokenTTL?: number;
|
||||||
|
accessTokenMaxTTL?: number;
|
||||||
|
accessTokenNumUsesLimit?: number;
|
||||||
|
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteIdentityJwtAuthEvent {
|
||||||
|
type: EventType.REVOKE_IDENTITY_JWT_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetIdentityJwtAuthEvent {
|
||||||
|
type: EventType.GET_IDENTITY_JWT_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface CreateEnvironmentEvent {
|
interface CreateEnvironmentEvent {
|
||||||
type: EventType.CREATE_ENVIRONMENT;
|
type: EventType.CREATE_ENVIRONMENT;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -1733,6 +1789,10 @@ export type Event =
|
|||||||
| DeleteIdentityOidcAuthEvent
|
| DeleteIdentityOidcAuthEvent
|
||||||
| UpdateIdentityOidcAuthEvent
|
| UpdateIdentityOidcAuthEvent
|
||||||
| GetIdentityOidcAuthEvent
|
| GetIdentityOidcAuthEvent
|
||||||
|
| AddIdentityJwtAuthEvent
|
||||||
|
| UpdateIdentityJwtAuthEvent
|
||||||
|
| GetIdentityJwtAuthEvent
|
||||||
|
| DeleteIdentityJwtAuthEvent
|
||||||
| CreateEnvironmentEvent
|
| CreateEnvironmentEvent
|
||||||
| GetEnvironmentEvent
|
| GetEnvironmentEvent
|
||||||
| UpdateEnvironmentEvent
|
| UpdateEnvironmentEvent
|
||||||
|
|||||||
@@ -355,14 +355,13 @@ export const JWT_AUTH = {
|
|||||||
},
|
},
|
||||||
ATTACH: {
|
ATTACH: {
|
||||||
identityId: "The ID of the identity to attach the configuration onto.",
|
identityId: "The ID of the identity to attach the configuration onto.",
|
||||||
caCert: "The PEM-encoded CA cert for establishing secure communication with the Identity Provider endpoints.",
|
|
||||||
configurationType: "The configuration for validating JWTs. Must be one of: 'jwks', 'static'",
|
configurationType: "The configuration for validating JWTs. Must be one of: 'jwks', 'static'",
|
||||||
jwksUrl:
|
jwksUrl:
|
||||||
"The URL of the JWKS endpoint. Required if configurationType is 'jwks'. This endpoint must serve JSON Web Key Sets (JWKS) containing the public keys used to verify JWT signatures.",
|
"The URL of the JWKS endpoint. Required if configurationType is 'jwks'. This endpoint must serve JSON Web Key Sets (JWKS) containing the public keys used to verify JWT signatures.",
|
||||||
jwksCaCert: "The PEM-encoded CA certificate for validating the TLS connection to the JWKS endpoint.",
|
jwksCaCert: "The PEM-encoded CA certificate for validating the TLS connection to the JWKS endpoint.",
|
||||||
publicKeys:
|
publicKeys:
|
||||||
"A list of PEM-encoded public keys used to verify JWT signatures. Required if configurationType is 'static'. Each key must be in RSA or ECDSA format and properly PEM-encoded with BEGIN/END markers.",
|
"A list of PEM-encoded public keys used to verify JWT signatures. Required if configurationType is 'static'. Each key must be in RSA or ECDSA format and properly PEM-encoded with BEGIN/END markers.",
|
||||||
boundIssuer: "The unique identifier of the identity provider issuing the JWT.",
|
boundIssuer: "The unique identifier of the JWT provider.",
|
||||||
boundAudiences: "The list of intended recipients.",
|
boundAudiences: "The list of intended recipients.",
|
||||||
boundClaims: "The attributes that should be present in the JWT for it to be valid.",
|
boundClaims: "The attributes that should be present in the JWT for it to be valid.",
|
||||||
boundSubject: "The expected principal that is the subject of the JWT.",
|
boundSubject: "The expected principal that is the subject of the JWT.",
|
||||||
@@ -370,6 +369,29 @@ export const JWT_AUTH = {
|
|||||||
accessTokenTTL: "The lifetime for an access token in seconds.",
|
accessTokenTTL: "The lifetime for an access token in seconds.",
|
||||||
accessTokenMaxTTL: "The maximum lifetime for an access token in seconds.",
|
accessTokenMaxTTL: "The maximum lifetime for an access token in seconds.",
|
||||||
accessTokenNumUsesLimit: "The maximum number of times that an access token can be used."
|
accessTokenNumUsesLimit: "The maximum number of times that an access token can be used."
|
||||||
|
},
|
||||||
|
UPDATE: {
|
||||||
|
identityId: "The ID of the identity to update the auth method for.",
|
||||||
|
configurationType: "The new configuration for validating JWTs. Must be one of: 'jwks', 'static'",
|
||||||
|
jwksUrl:
|
||||||
|
"The new URL of the JWKS endpoint. This endpoint must serve JSON Web Key Sets (JWKS) containing the public keys used to verify JWT signatures.",
|
||||||
|
jwksCaCert: "The new PEM-encoded CA certificate for validating the TLS connection to the JWKS endpoint.",
|
||||||
|
publicKeys:
|
||||||
|
"A new list of PEM-encoded public keys used to verify JWT signatures. Each key must be in RSA or ECDSA format and properly PEM-encoded with BEGIN/END markers.",
|
||||||
|
boundIssuer: "The new unique identifier of the JWT provider.",
|
||||||
|
boundAudiences: "The new list of intended recipients.",
|
||||||
|
boundClaims: "The new attributes that should be present in the JWT for it to be valid.",
|
||||||
|
boundSubject: "The new expected principal that is the subject of the JWT.",
|
||||||
|
accessTokenTrustedIps: "The new IPs or CIDR ranges that access tokens can be used from.",
|
||||||
|
accessTokenTTL: "The new lifetime for an access token in seconds.",
|
||||||
|
accessTokenMaxTTL: "The new maximum lifetime for an access token in seconds.",
|
||||||
|
accessTokenNumUsesLimit: "The new maximum number of times that an access token can be used."
|
||||||
|
},
|
||||||
|
RETRIEVE: {
|
||||||
|
identityId: "The ID of the identity to retrieve the auth method for."
|
||||||
|
},
|
||||||
|
REVOKE: {
|
||||||
|
identityId: "The ID of the identity to revoke the auth method for."
|
||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityJwtAuthsSchema } from "@app/db/schemas";
|
import { IdentityJwtAuthsSchema } from "@app/db/schemas";
|
||||||
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { JWT_AUTH } from "@app/lib/api-docs";
|
import { JWT_AUTH } from "@app/lib/api-docs";
|
||||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||||
import { JwtConfigurationType } from "@app/services/identity-jwt-auth/identity-jwt-auth-types";
|
import { JwtConfigurationType } from "@app/services/identity-jwt-auth/identity-jwt-auth-types";
|
||||||
import {
|
import {
|
||||||
validateJwtAuthAudiencesField,
|
validateJwtAuthAudiencesField,
|
||||||
@@ -16,7 +18,7 @@ const IdentityJwtAuthResponseSchema = IdentityJwtAuthsSchema.omit({
|
|||||||
encryptedPublicKeys: true
|
encryptedPublicKeys: true
|
||||||
}).extend({
|
}).extend({
|
||||||
jwksCaCert: z.string(),
|
jwksCaCert: z.string(),
|
||||||
publicKeys: z.string()
|
publicKeys: z.string().array()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider) => {
|
export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider) => {
|
||||||
@@ -37,43 +39,246 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider)
|
|||||||
params: z.object({
|
params: z.object({
|
||||||
identityId: z.string().trim().describe(JWT_AUTH.ATTACH.identityId)
|
identityId: z.string().trim().describe(JWT_AUTH.ATTACH.identityId)
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z
|
||||||
configurationType: z.nativeEnum(JwtConfigurationType).describe(JWT_AUTH.ATTACH.configurationType),
|
.object({
|
||||||
jwksUrl: z.string().describe(JWT_AUTH.ATTACH.jwksUrl),
|
configurationType: z.nativeEnum(JwtConfigurationType).describe(JWT_AUTH.ATTACH.configurationType),
|
||||||
jwksCaCert: z.string().describe(JWT_AUTH.ATTACH.jwksCaCert),
|
jwksUrl: z.string().trim().default("").describe(JWT_AUTH.ATTACH.jwksUrl),
|
||||||
publicKeys: z.string().array().describe(JWT_AUTH.ATTACH.publicKeys),
|
jwksCaCert: z.string().trim().default("").describe(JWT_AUTH.ATTACH.jwksCaCert),
|
||||||
boundIssuer: z.string().min(1).describe(JWT_AUTH.ATTACH.boundIssuer),
|
publicKeys: z.string().min(1).array().describe(JWT_AUTH.ATTACH.publicKeys),
|
||||||
boundAudiences: validateJwtAuthAudiencesField.describe(JWT_AUTH.ATTACH.boundAudiences),
|
boundIssuer: z.string().trim().default("").describe(JWT_AUTH.ATTACH.boundIssuer),
|
||||||
boundClaims: validateJwtBoundClaimsField.describe(JWT_AUTH.ATTACH.boundClaims),
|
boundAudiences: validateJwtAuthAudiencesField.describe(JWT_AUTH.ATTACH.boundAudiences),
|
||||||
boundSubject: z.string().optional().default("").describe(JWT_AUTH.ATTACH.boundSubject),
|
boundClaims: validateJwtBoundClaimsField.describe(JWT_AUTH.ATTACH.boundClaims),
|
||||||
accessTokenTrustedIps: z
|
boundSubject: z.string().trim().default("").describe(JWT_AUTH.ATTACH.boundSubject),
|
||||||
.object({
|
accessTokenTrustedIps: z
|
||||||
ipAddress: z.string().trim()
|
.object({
|
||||||
})
|
ipAddress: z.string().trim()
|
||||||
.array()
|
})
|
||||||
.min(1)
|
.array()
|
||||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
|
.min(1)
|
||||||
.describe(JWT_AUTH.ATTACH.accessTokenTrustedIps),
|
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
|
||||||
accessTokenTTL: z
|
.describe(JWT_AUTH.ATTACH.accessTokenTrustedIps),
|
||||||
.number()
|
accessTokenTTL: z
|
||||||
.int()
|
.number()
|
||||||
.min(1)
|
.int()
|
||||||
.max(315360000)
|
.min(1)
|
||||||
.refine((value) => value !== 0, {
|
.max(315360000)
|
||||||
message: "accessTokenTTL must have a non zero number"
|
.refine((value) => value !== 0, {
|
||||||
})
|
message: "accessTokenTTL must have a non zero number"
|
||||||
.default(2592000)
|
})
|
||||||
.describe(JWT_AUTH.ATTACH.accessTokenTTL),
|
.default(2592000)
|
||||||
accessTokenMaxTTL: z
|
.describe(JWT_AUTH.ATTACH.accessTokenTTL),
|
||||||
.number()
|
accessTokenMaxTTL: z
|
||||||
.int()
|
.number()
|
||||||
.max(315360000)
|
.int()
|
||||||
.refine((value) => value !== 0, {
|
.max(315360000)
|
||||||
message: "accessTokenMaxTTL must have a non zero number"
|
.refine((value) => value !== 0, {
|
||||||
})
|
message: "accessTokenMaxTTL must have a non zero number"
|
||||||
.default(2592000)
|
})
|
||||||
.describe(JWT_AUTH.ATTACH.accessTokenMaxTTL),
|
.default(2592000)
|
||||||
accessTokenNumUsesLimit: z.number().int().min(0).default(0).describe(JWT_AUTH.ATTACH.accessTokenNumUsesLimit)
|
.describe(JWT_AUTH.ATTACH.accessTokenMaxTTL),
|
||||||
|
accessTokenNumUsesLimit: z.number().int().min(0).default(0).describe(JWT_AUTH.ATTACH.accessTokenNumUsesLimit)
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
if (data.configurationType === JwtConfigurationType.JWKS) {
|
||||||
|
if (!data.jwksUrl) {
|
||||||
|
ctx.addIssue({
|
||||||
|
path: ["jwksUrl"],
|
||||||
|
message: "JWKS url is required",
|
||||||
|
code: z.ZodIssueCode.custom
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (data.configurationType === JwtConfigurationType.STATIC) {
|
||||||
|
if (data.publicKeys.length === 0) {
|
||||||
|
ctx.addIssue({
|
||||||
|
path: ["publicKeys"],
|
||||||
|
message: "public key is required",
|
||||||
|
code: z.ZodIssueCode.custom
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityJwtAuth: IdentityJwtAuthResponseSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityJwtAuth = await server.services.identityJwtAuth.attachJwtAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.body,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: identityJwtAuth.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.ADD_IDENTITY_JWT_AUTH,
|
||||||
|
metadata: {
|
||||||
|
identityId: identityJwtAuth.identityId,
|
||||||
|
configurationType: identityJwtAuth.configurationType,
|
||||||
|
jwksUrl: identityJwtAuth.jwksUrl,
|
||||||
|
jwksCaCert: identityJwtAuth.jwksCaCert,
|
||||||
|
publicKeys: identityJwtAuth.publicKeys,
|
||||||
|
boundIssuer: identityJwtAuth.boundIssuer,
|
||||||
|
boundAudiences: identityJwtAuth.boundAudiences,
|
||||||
|
boundClaims: identityJwtAuth.boundClaims as Record<string, string>,
|
||||||
|
boundSubject: identityJwtAuth.boundSubject,
|
||||||
|
accessTokenTTL: identityJwtAuth.accessTokenTTL,
|
||||||
|
accessTokenMaxTTL: identityJwtAuth.accessTokenMaxTTL,
|
||||||
|
accessTokenTrustedIps: identityJwtAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
|
||||||
|
accessTokenNumUsesLimit: identityJwtAuth.accessTokenNumUsesLimit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
identityJwtAuth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/jwt-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Update JWT Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().trim().describe(JWT_AUTH.UPDATE.identityId)
|
||||||
|
}),
|
||||||
|
body: z
|
||||||
|
.object({
|
||||||
|
configurationType: z.nativeEnum(JwtConfigurationType).describe(JWT_AUTH.UPDATE.configurationType),
|
||||||
|
jwksUrl: z.string().trim().describe(JWT_AUTH.UPDATE.jwksUrl),
|
||||||
|
jwksCaCert: z.string().trim().describe(JWT_AUTH.UPDATE.jwksCaCert),
|
||||||
|
publicKeys: z.string().array().describe(JWT_AUTH.UPDATE.publicKeys),
|
||||||
|
boundIssuer: z.string().trim().describe(JWT_AUTH.UPDATE.boundIssuer),
|
||||||
|
boundAudiences: validateJwtAuthAudiencesField.describe(JWT_AUTH.UPDATE.boundAudiences),
|
||||||
|
boundClaims: validateJwtBoundClaimsField.describe(JWT_AUTH.UPDATE.boundClaims),
|
||||||
|
boundSubject: z.string().trim().describe(JWT_AUTH.UPDATE.boundSubject),
|
||||||
|
accessTokenTrustedIps: z
|
||||||
|
.object({
|
||||||
|
ipAddress: z.string().trim()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.min(1)
|
||||||
|
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
|
||||||
|
.describe(JWT_AUTH.UPDATE.accessTokenTrustedIps),
|
||||||
|
accessTokenTTL: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1)
|
||||||
|
.max(315360000)
|
||||||
|
.refine((value) => value !== 0, {
|
||||||
|
message: "accessTokenTTL must have a non zero number"
|
||||||
|
})
|
||||||
|
.default(2592000)
|
||||||
|
.describe(JWT_AUTH.UPDATE.accessTokenTTL),
|
||||||
|
accessTokenMaxTTL: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.max(315360000)
|
||||||
|
.refine((value) => value !== 0, {
|
||||||
|
message: "accessTokenMaxTTL must have a non zero number"
|
||||||
|
})
|
||||||
|
.default(2592000)
|
||||||
|
.describe(JWT_AUTH.UPDATE.accessTokenMaxTTL),
|
||||||
|
|
||||||
|
accessTokenNumUsesLimit: z.number().int().min(0).default(0).describe(JWT_AUTH.UPDATE.accessTokenNumUsesLimit)
|
||||||
|
})
|
||||||
|
.partial()
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
if (data.configurationType === JwtConfigurationType.JWKS) {
|
||||||
|
if (!data.jwksUrl) {
|
||||||
|
ctx.addIssue({
|
||||||
|
path: ["jwksUrl"],
|
||||||
|
message: "JWKS url is required",
|
||||||
|
code: z.ZodIssueCode.custom
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (data.configurationType === JwtConfigurationType.STATIC) {
|
||||||
|
if (data.publicKeys?.length === 0) {
|
||||||
|
ctx.addIssue({
|
||||||
|
path: ["publicKeys"],
|
||||||
|
message: "public key is required",
|
||||||
|
code: z.ZodIssueCode.custom
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityJwtAuth: IdentityJwtAuthResponseSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityJwtAuth = await server.services.identityJwtAuth.updateJwtAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
...req.body,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: identityJwtAuth.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.UPDATE_IDENTITY_JWT_AUTH,
|
||||||
|
metadata: {
|
||||||
|
identityId: identityJwtAuth.identityId,
|
||||||
|
configurationType: identityJwtAuth.configurationType,
|
||||||
|
jwksUrl: identityJwtAuth.jwksUrl,
|
||||||
|
jwksCaCert: identityJwtAuth.jwksCaCert,
|
||||||
|
publicKeys: identityJwtAuth.publicKeys,
|
||||||
|
boundIssuer: identityJwtAuth.boundIssuer,
|
||||||
|
boundAudiences: identityJwtAuth.boundAudiences,
|
||||||
|
boundClaims: identityJwtAuth.boundClaims as Record<string, string>,
|
||||||
|
boundSubject: identityJwtAuth.boundSubject,
|
||||||
|
accessTokenTTL: identityJwtAuth.accessTokenTTL,
|
||||||
|
accessTokenMaxTTL: identityJwtAuth.accessTokenMaxTTL,
|
||||||
|
accessTokenTrustedIps: identityJwtAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
|
||||||
|
accessTokenNumUsesLimit: identityJwtAuth.accessTokenNumUsesLimit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityJwtAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/jwt-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Retrieve JWT Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().describe(JWT_AUTH.RETRIEVE.identityId)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -81,6 +286,77 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {}
|
handler: async (req) => {
|
||||||
|
const identityJwtAuth = await server.services.identityJwtAuth.getJwtAuth({
|
||||||
|
identityId: req.params.identityId,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: identityJwtAuth.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_IDENTITY_JWT_AUTH,
|
||||||
|
metadata: {
|
||||||
|
identityId: identityJwtAuth.identityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityJwtAuth };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/jwt-auth/identities/:identityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
schema: {
|
||||||
|
description: "Delete JWT Auth configuration on identity",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
identityId: z.string().describe(JWT_AUTH.REVOKE.identityId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
identityJwtAuth: IdentityJwtAuthResponseSchema.omit({
|
||||||
|
publicKeys: true,
|
||||||
|
jwksCaCert: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const identityJwtAuth = await server.services.identityJwtAuth.revokeJwtAuth({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
identityId: req.params.identityId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: identityJwtAuth.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.REVOKE_IDENTITY_JWT_AUTH,
|
||||||
|
metadata: {
|
||||||
|
identityId: identityJwtAuth.identityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { identityJwtAuth };
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
import { IdentityAuthMethod, TIdentityJwtAuthsUpdate } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
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";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
|
|
||||||
|
import { ActorType } from "../auth/auth-type";
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
import { KmsDataKey } from "../kms/kms-types";
|
import { KmsDataKey } from "../kms/kms-types";
|
||||||
import { TIdentityJwtAuthDALFactory } from "./identity-jwt-auth-dal";
|
import { TIdentityJwtAuthDALFactory } from "./identity-jwt-auth-dal";
|
||||||
import { TAttachJwtAuthDTO } from "./identity-jwt-auth-types";
|
import { TAttachJwtAuthDTO, TGetJwtAuthDTO, TRevokeJwtAuthDTO, TUpdateJwtAuthDTO } from "./identity-jwt-auth-types";
|
||||||
|
|
||||||
type TIdentityJwtAuthServiceFactoryDep = {
|
type TIdentityJwtAuthServiceFactoryDep = {
|
||||||
identityJwtAuthDAL: TIdentityJwtAuthDALFactory;
|
identityJwtAuthDAL: TIdentityJwtAuthDALFactory;
|
||||||
@@ -30,6 +32,7 @@ export const identityJwtAuthServiceFactory = ({
|
|||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
|
identityAccessTokenDAL,
|
||||||
kmsService
|
kmsService
|
||||||
}: TIdentityJwtAuthServiceFactoryDep) => {
|
}: TIdentityJwtAuthServiceFactoryDep) => {
|
||||||
const attachJwtAuth = async ({
|
const attachJwtAuth = async ({
|
||||||
@@ -131,7 +134,212 @@ export const identityJwtAuthServiceFactory = ({
|
|||||||
return { ...identityJwtAuth, orgId: identityMembershipOrg.orgId, jwksCaCert, publicKeys };
|
return { ...identityJwtAuth, orgId: identityMembershipOrg.orgId, jwksCaCert, publicKeys };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateJwtAuth = async ({
|
||||||
|
identityId,
|
||||||
|
configurationType,
|
||||||
|
jwksUrl,
|
||||||
|
jwksCaCert,
|
||||||
|
publicKeys,
|
||||||
|
boundIssuer,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actor,
|
||||||
|
actorOrgId
|
||||||
|
}: TUpdateJwtAuthDTO) => {
|
||||||
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
|
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||||
|
|
||||||
|
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to update JWT Auth"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const identityJwtAuth = await identityJwtAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
|
if (
|
||||||
|
(accessTokenMaxTTL || identityJwtAuth.accessTokenMaxTTL) > 0 &&
|
||||||
|
(accessTokenTTL || identityJwtAuth.accessTokenMaxTTL) > (accessTokenMaxTTL || identityJwtAuth.accessTokenMaxTTL)
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityMembershipOrg.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
|
const plan = await licenseService.getPlan(identityMembershipOrg.orgId);
|
||||||
|
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||||
|
if (
|
||||||
|
!plan.ipAllowlisting &&
|
||||||
|
accessTokenTrustedIp.ipAddress !== "0.0.0.0/0" &&
|
||||||
|
accessTokenTrustedIp.ipAddress !== "::/0"
|
||||||
|
)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Failed to add IP access range to access token due to plan restriction. Upgrade plan to add IP access range."
|
||||||
|
});
|
||||||
|
if (!isValidIpOrCidr(accessTokenTrustedIp.ipAddress))
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||||
|
});
|
||||||
|
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateQuery: TIdentityJwtAuthsUpdate = {
|
||||||
|
boundIssuer,
|
||||||
|
configurationType,
|
||||||
|
jwksUrl,
|
||||||
|
boundAudiences,
|
||||||
|
boundClaims,
|
||||||
|
boundSubject,
|
||||||
|
accessTokenMaxTTL,
|
||||||
|
accessTokenTTL,
|
||||||
|
accessTokenNumUsesLimit,
|
||||||
|
accessTokenTrustedIps: reformattedAccessTokenTrustedIps
|
||||||
|
? JSON.stringify(reformattedAccessTokenTrustedIps)
|
||||||
|
: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const { encryptor: orgDataKeyEncryptor, decryptor: orgDataKeyDecryptor } =
|
||||||
|
await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.Organization,
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (jwksCaCert) {
|
||||||
|
const { cipherTextBlob: encryptedJwksCaCert } = orgDataKeyEncryptor({
|
||||||
|
plainText: Buffer.from(jwksCaCert)
|
||||||
|
});
|
||||||
|
|
||||||
|
updateQuery.encryptedJwksCaCert = encryptedJwksCaCert;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publicKeys) {
|
||||||
|
const { cipherTextBlob: encryptedPublicKeys } = orgDataKeyEncryptor({
|
||||||
|
plainText: Buffer.from(publicKeys.join(","))
|
||||||
|
});
|
||||||
|
|
||||||
|
updateQuery.encryptedPublicKeys = encryptedPublicKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedJwtAuth = await identityJwtAuthDAL.updateById(identityJwtAuth.id, updateQuery);
|
||||||
|
const decryptedJwksCaCert = orgDataKeyDecryptor({ cipherTextBlob: updatedJwtAuth.encryptedJwksCaCert }).toString();
|
||||||
|
const decryptedPublicKeys = orgDataKeyDecryptor({ cipherTextBlob: updatedJwtAuth.encryptedPublicKeys })
|
||||||
|
.toString()
|
||||||
|
.split(",");
|
||||||
|
|
||||||
|
return {
|
||||||
|
...updatedJwtAuth,
|
||||||
|
orgId: identityMembershipOrg.orgId,
|
||||||
|
jwksCaCert: decryptedJwksCaCert,
|
||||||
|
publicKeys: decryptedPublicKeys
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getJwtAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetJwtAuthDTO) => {
|
||||||
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
|
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||||
|
|
||||||
|
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "The identity does not have JWT Auth attached"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityMembershipOrg.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
|
const identityJwtAuth = await identityJwtAuthDAL.findOne({ identityId });
|
||||||
|
|
||||||
|
const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.Organization,
|
||||||
|
orgId: actorOrgId
|
||||||
|
});
|
||||||
|
|
||||||
|
const decryptedJwksCaCert = orgDataKeyDecryptor({ cipherTextBlob: identityJwtAuth.encryptedJwksCaCert }).toString();
|
||||||
|
const decryptedPublicKeys = orgDataKeyDecryptor({ cipherTextBlob: identityJwtAuth.encryptedPublicKeys })
|
||||||
|
.toString()
|
||||||
|
.split(",");
|
||||||
|
|
||||||
|
return {
|
||||||
|
...identityJwtAuth,
|
||||||
|
orgId: identityMembershipOrg.orgId,
|
||||||
|
jwksCaCert: decryptedJwksCaCert,
|
||||||
|
publicKeys: decryptedPublicKeys
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const revokeJwtAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TRevokeJwtAuthDTO) => {
|
||||||
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||||
|
if (!identityMembershipOrg) {
|
||||||
|
throw new NotFoundError({ message: "Failed to find identity" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.JWT_AUTH)) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "The identity does not have JWT auth"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityMembershipOrg.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
|
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||||
|
ActorType.IDENTITY,
|
||||||
|
identityMembershipOrg.identityId,
|
||||||
|
identityMembershipOrg.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isAtLeastAsPrivileged(permission, rolePermission)) {
|
||||||
|
throw new ForbiddenRequestError({
|
||||||
|
message: "Failed to revoke JWT auth of identity with more privileged role"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const revokedIdentityJwtAuth = await identityJwtAuthDAL.transaction(async (tx) => {
|
||||||
|
const deletedJwtAuth = await identityJwtAuthDAL.delete({ identityId }, tx);
|
||||||
|
await identityAccessTokenDAL.delete({ identityId, authMethod: IdentityAuthMethod.JWT_AUTH }, tx);
|
||||||
|
|
||||||
|
return { ...deletedJwtAuth?.[0], orgId: identityMembershipOrg.orgId };
|
||||||
|
});
|
||||||
|
|
||||||
|
return revokedIdentityJwtAuth;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attachJwtAuth
|
attachJwtAuth,
|
||||||
|
updateJwtAuth,
|
||||||
|
getJwtAuth,
|
||||||
|
revokeJwtAuth
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,3 +20,27 @@ export type TAttachJwtAuthDTO = {
|
|||||||
accessTokenNumUsesLimit: number;
|
accessTokenNumUsesLimit: number;
|
||||||
accessTokenTrustedIps: { ipAddress: string }[];
|
accessTokenTrustedIps: { ipAddress: string }[];
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TUpdateJwtAuthDTO = {
|
||||||
|
identityId: string;
|
||||||
|
configurationType?: JwtConfigurationType;
|
||||||
|
jwksUrl?: string;
|
||||||
|
jwksCaCert?: string;
|
||||||
|
publicKeys?: string[];
|
||||||
|
boundIssuer?: string;
|
||||||
|
boundAudiences?: string;
|
||||||
|
boundClaims?: Record<string, string>;
|
||||||
|
boundSubject?: string;
|
||||||
|
accessTokenTTL?: number;
|
||||||
|
accessTokenMaxTTL?: number;
|
||||||
|
accessTokenNumUsesLimit?: number;
|
||||||
|
accessTokenTrustedIps?: { ipAddress: string }[];
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TGetJwtAuthDTO = {
|
||||||
|
identityId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TRevokeJwtAuthDTO = {
|
||||||
|
identityId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|||||||
Reference in New Issue
Block a user