mirror of
https://github.com/hanzoai/sign.git
synced 2026-10-21 01:21:51 +00:00
An audience proves nothing alone: any client IAM authenticates may mint a token for this app's resource. So the bearer is an application's access token whose azp IAM_APP_TEAMS lists, and it acts for exactly the team that list maps it to, as that team's organisation owner. People sign in; the person bearer and the X-Team-Id choice are gone, and so is IAM_ORG. A sign-in's id_token is never a bearer. A webhook carries eSign's identity only to the Hanzo API, minted for it; any other endpoint gets the webhook's own secret and nothing of ours.
108 lines
2.7 KiB
TypeScript
108 lines
2.7 KiB
TypeScript
import type { Team, User } from '@prisma/client';
|
|
import type { TsRestRequest } from '@ts-rest/serverless';
|
|
import type { Logger } from 'pino';
|
|
|
|
import { AppError, AppErrorCode } from '@hanzo/esign-lib/errors/app-error';
|
|
import { getBearer } from '@hanzo/esign-lib/server-only/public-api/get-bearer';
|
|
import type { BaseApiLog, RootApiLog } from '@hanzo/esign-lib/types/api-logs';
|
|
import type { ApiRequestMetadata } from '@hanzo/esign-lib/universal/extract-request-metadata';
|
|
import { extractRequestMetadata } from '@hanzo/esign-lib/universal/extract-request-metadata';
|
|
import { nanoid } from '@hanzo/esign-lib/universal/id';
|
|
import { logger } from '@hanzo/esign-lib/utils/logger';
|
|
|
|
type B = {
|
|
// appRoute: any;
|
|
request: TsRestRequest;
|
|
responseHeaders: Headers;
|
|
};
|
|
|
|
export const authenticatedMiddleware = <
|
|
T extends {
|
|
headers: {
|
|
authorization: string;
|
|
};
|
|
},
|
|
R extends {
|
|
status: number;
|
|
body: unknown;
|
|
},
|
|
>(
|
|
handler: (
|
|
args: T & { req: TsRestRequest },
|
|
user: Pick<User, 'id' | 'email' | 'name' | 'disabled'>,
|
|
team: Team,
|
|
options: { metadata: ApiRequestMetadata; logger: Logger },
|
|
) => Promise<R>,
|
|
) => {
|
|
return async (args: T, { request }: B) => {
|
|
const requestMetadata = extractRequestMetadata(request);
|
|
|
|
const apiLogger = logger.child({
|
|
ipAddress: requestMetadata.ipAddress,
|
|
userAgent: requestMetadata.userAgent,
|
|
requestId: nanoid(),
|
|
} satisfies RootApiLog);
|
|
|
|
const infoToLog: BaseApiLog = {
|
|
auth: 'api',
|
|
source: 'apiV1',
|
|
path: request.url,
|
|
};
|
|
|
|
try {
|
|
const { authorization } = args.headers;
|
|
|
|
// A Hanzo IAM token, as "Authorization: Bearer <token>" or bare.
|
|
const [token] = (authorization || '').split('Bearer ').filter((s) => s.length > 0);
|
|
|
|
if (!token) {
|
|
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
|
message: 'An IAM bearer token was not provided',
|
|
});
|
|
}
|
|
|
|
const bearer = await getBearer({ token });
|
|
|
|
apiLogger.info({
|
|
...infoToLog,
|
|
userId: bearer.user.id,
|
|
bearer: bearer.id,
|
|
} satisfies BaseApiLog);
|
|
|
|
const metadata: ApiRequestMetadata = {
|
|
requestMetadata,
|
|
source: 'apiV1',
|
|
auth: 'api',
|
|
auditUser: { id: null, email: null, name: bearer.id },
|
|
};
|
|
|
|
return await handler(
|
|
{
|
|
...args,
|
|
req: request,
|
|
},
|
|
bearer.user,
|
|
bearer.team,
|
|
{ metadata, logger: apiLogger },
|
|
);
|
|
} catch (err) {
|
|
console.log({ err });
|
|
|
|
apiLogger.info(infoToLog);
|
|
|
|
let message = 'Unauthorized';
|
|
|
|
if (err instanceof AppError) {
|
|
message = err.message;
|
|
}
|
|
|
|
return {
|
|
status: 401,
|
|
body: {
|
|
message,
|
|
},
|
|
} as const;
|
|
}
|
|
};
|
|
};
|