@unito/integration-sdk
Version:
Integration SDK
84 lines (70 loc) • 2.55 kB
text/typescript
import { Request, Response, NextFunction } from 'express';
import { BadRequestError } from '../httpErrors.js';
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Express {
interface Locals {
credentials: Credentials;
}
}
}
/**
* A single rate-limit layer specification: the same `{ reqPerPeriod, periodMs }` knobs a
* keuf layer takes. Both fields are required — an override entry fully defines a layer, so
* what is set is exactly what runs (no partial merge, no hidden defaults).
*/
export type RateLimitValue = { reqPerPeriod: number; periodMs: number };
/**
* Per-credential rate-limit override, keyed by an integration-defined layer name (e.g. an
* endpoint type or tier). Each entry fully defines that layer; layers with no entry fall back
* to the integration's own default. Each integration narrows the keys it recognizes.
*/
export type RateLimitOverride = Record<string, RateLimitValue>;
/**
* The credentials object passed to every handler function.
*
* It contains the parsed credentials payload associated with the request through the X-Unito-Credentials header.
*/
export type Credentials = {
/**
* The access token for the provider.
*/
accessToken?: string;
/**
* The id of the unito credential record.
*
* This is not available on the initial call to /me before the creation of the credential.
*/
unitoCredentialId?: string;
/**
* The id of the unito user record.
*/
unitoUserId?: string;
/**
* Per-credential rate-limit override, injected by platformServer from the credential's
* `rateLimitOverride` column. See {@link RateLimitOverride}.
*/
rateLimitOverride?: RateLimitOverride;
[keys: string]: unknown;
};
const CREDENTIALS_HEADER = 'X-Unito-Credentials';
function extractCredentials(req: Request, res: Response, next: NextFunction) {
const credentialsHeader = req.header(CREDENTIALS_HEADER);
if (credentialsHeader) {
let credentials: Credentials;
try {
credentials = JSON.parse(Buffer.from(credentialsHeader, 'base64').toString('utf8'));
} catch {
throw new BadRequestError(`Malformed HTTP header ${CREDENTIALS_HEADER}`);
}
res.locals.credentials = credentials;
if (credentials.unitoCredentialId) {
res.locals.logger?.decorate({ credentialId: credentials.unitoCredentialId });
}
if (credentials.unitoUserId) {
res.locals.logger?.decorate({ userId: credentials.unitoUserId });
}
}
next();
}
export default extractCredentials;