Add Legal Office seal and complete Azure CDN deployment

- Add Legal Office of the Master seal (SVG design with Maltese Cross, scales of justice, legal scroll)
- Create legal-office-manifest-template.json for Legal Office credentials
- Update SEAL_MAPPING.md and DESIGN_GUIDE.md with Legal Office seal documentation
- Complete Azure CDN infrastructure deployment:
  - Resource group, storage account, and container created
  - 17 PNG seal files uploaded to Azure Blob Storage
  - All manifest templates updated with Azure URLs
  - Configuration files generated (azure-cdn-config.env)
- Add comprehensive Azure CDN setup scripts and documentation
- Fix manifest URL generation to prevent double slashes
- Verify all seals accessible via HTTPS
This commit is contained in:
defiQUG
2025-11-12 22:03:42 -08:00
parent 8649ad4124
commit 92cc41d26d
258 changed files with 16021 additions and 1260 deletions

View File

@@ -49,6 +49,16 @@ const envSchema = z.object({
ENTRA_CLIENT_ID: z.string().optional(),
ENTRA_CLIENT_SECRET: z.string().optional(),
ENTRA_CREDENTIAL_MANIFEST_ID: z.string().optional(),
ENTRA_MANIFESTS: z.string().optional(), // JSON object mapping manifest names to IDs
// Entra Rate Limiting
ENTRA_RATE_LIMIT_ISSUANCE: z.string().optional(),
ENTRA_RATE_LIMIT_VERIFICATION: z.string().optional(),
ENTRA_RATE_LIMIT_STATUS_CHECK: z.string().optional(),
ENTRA_RATE_LIMIT_GLOBAL: z.string().optional(),
// Credential Display/Images
ENTRA_CREDENTIAL_LOGO_URI: z.string().url().optional(),
ENTRA_CREDENTIAL_BG_COLOR: z.string().optional(),
ENTRA_CREDENTIAL_TEXT_COLOR: z.string().optional(),
// Credential Rate Limiting
CREDENTIAL_RATE_LIMIT_PER_USER: z.string().optional(),

View File

@@ -10,6 +10,7 @@ export * from './middleware';
export * from './validation';
export * from './auth';
export * from './rate-limit-credential';
export * from './rate-limit-entra';
export * from './authorization';
export * from './compliance';
export * from './retry';

View File

@@ -0,0 +1,144 @@
/**
* Entra VerifiedID API rate limiting
* Specific rate limits for Entra VerifiedID endpoints to prevent API quota exhaustion
*/
import { FastifyInstance, FastifyRequest } from 'fastify';
import fastifyRateLimit from '@fastify/rate-limit';
import { getEnv } from './env';
export interface EntraRateLimitConfig {
issuance?: {
max: number;
timeWindow: string | number;
};
verification?: {
max: number;
timeWindow: string | number;
};
statusCheck?: {
max: number;
timeWindow: string | number;
};
global?: {
max: number;
timeWindow: string | number;
};
}
/**
* Register Entra VerifiedID-specific rate limiting
*/
export async function registerEntraRateLimit(
server: FastifyInstance,
config?: EntraRateLimitConfig
): Promise<void> {
const env = getEnv();
// Default configuration - conservative limits to avoid API quota issues
const defaultConfig: Required<EntraRateLimitConfig> = {
issuance: {
max: parseInt(env.ENTRA_RATE_LIMIT_ISSUANCE || '10', 10),
timeWindow: '1 minute',
},
verification: {
max: parseInt(env.ENTRA_RATE_LIMIT_VERIFICATION || '20', 10),
timeWindow: '1 minute',
},
statusCheck: {
max: parseInt(env.ENTRA_RATE_LIMIT_STATUS_CHECK || '30', 10),
timeWindow: '1 minute',
},
global: {
max: parseInt(env.ENTRA_RATE_LIMIT_GLOBAL || '50', 10),
timeWindow: '1 minute',
},
};
const finalConfig = { ...defaultConfig, ...config };
// Global Entra API rate limit
await server.register(fastifyRateLimit, {
max: finalConfig.global.max,
timeWindow: finalConfig.global.timeWindow,
keyGenerator: (request: FastifyRequest) => {
// Rate limit by IP for Entra endpoints
return `entra:global:${request.ip}`;
},
errorResponseBuilder: (_request, context) => {
return {
error: {
code: 'ENTRA_RATE_LIMIT_EXCEEDED',
message: `Entra API rate limit exceeded, retry in ${Math.ceil(context.ttl / 1000)} seconds`,
},
};
},
skipOnError: false,
});
// Issuance-specific rate limit
await server.register(fastifyRateLimit, {
max: finalConfig.issuance.max,
timeWindow: finalConfig.issuance.timeWindow,
keyGenerator: (request: FastifyRequest) => {
const userId = (request as any).user?.id || 'anonymous';
return `entra:issuance:${userId}:${request.ip}`;
},
errorResponseBuilder: (_request, context) => {
return {
error: {
code: 'ENTRA_ISSUANCE_RATE_LIMIT_EXCEEDED',
message: `Entra issuance rate limit exceeded, retry in ${Math.ceil(context.ttl / 1000)} seconds`,
},
};
},
skipOnError: false,
});
// Verification-specific rate limit
await server.register(fastifyRateLimit, {
max: finalConfig.verification.max,
timeWindow: finalConfig.verification.timeWindow,
keyGenerator: (request: FastifyRequest) => {
return `entra:verification:${request.ip}`;
},
errorResponseBuilder: (_request, context) => {
return {
error: {
code: 'ENTRA_VERIFICATION_RATE_LIMIT_EXCEEDED',
message: `Entra verification rate limit exceeded, retry in ${Math.ceil(context.ttl / 1000)} seconds`,
},
};
},
skipOnError: false,
});
// Status check-specific rate limit
await server.register(fastifyRateLimit, {
max: finalConfig.statusCheck.max,
timeWindow: finalConfig.statusCheck.timeWindow,
keyGenerator: (request: FastifyRequest) => {
const requestId = (request.params as any)?.requestId || (request.query as any)?.requestId || 'unknown';
return `entra:status:${requestId}`;
},
errorResponseBuilder: (_request, context) => {
return {
error: {
code: 'ENTRA_STATUS_CHECK_RATE_LIMIT_EXCEEDED',
message: `Entra status check rate limit exceeded, retry in ${Math.ceil(context.ttl / 1000)} seconds`,
},
};
},
skipOnError: false,
});
}
/**
* Create Entra rate limit plugin
*/
export function createEntraRateLimitPlugin(config?: EntraRateLimitConfig) {
return async function (server: FastifyInstance) {
await registerEntraRateLimit(server, config);
};
}