@filen/aws4-express
Version:
Express middleware handlers for validation AWS Signature V4
184 lines (183 loc) • 7.4 kB
TypeScript
import { BinaryLike, KeyObject } from 'crypto';
import querystring from 'querystring';
import { NextFunction, Request, Response } from 'express';
export type Dictionary = Record<string, string | string[] | undefined>;
/**
* Middleware configuration
*/
export interface AwsVerifyOptions {
/**
* Callback for secretKey. You have to provide process to get proper secret or return undefined secret.
*
* @param message { AwsIncomingMessage }
* @param req { Request }
* @param res { Response }
* @param next { NextFunction }
* @returns { Promise<string | undefined> | string | undefined } - Should return secretKey on incoming parameters - but if secret is missing which it will be normal case when someone want to guess - you should return undefined;
*/
secretKey: (message: AwsIncomingMessage, req: Request, res: Response, next: NextFunction) => Promise<string | undefined> | string | undefined;
/**
* Callback for changes in incoming headers before it goes through parse process. Help to more sophisticated changes to preserve proper headers.
*
* @param headers { Dictionary }
* @returns { Promise<Dictionary> | Dictionary } - Should return fixed incoming headers
*/
headers?: (headers: Dictionary) => Promise<Dictionary> | Dictionary;
/**
* You can skip aws signature validation.
*
* @param req { Request }
* @returns { Promise<boolean> | boolean } If return false will skip aws validation and go to next middleware.
*/
enabled?: (req: Request) => Promise<boolean> | boolean;
/**
* Custom response on header missing. Validation stops here. Default value `onMissingHeaders: () => {
res.status(400).send('Required headers are missing');
},`
*
* @param req { Request }
* @param res { Response }
* @param next { NextFunction }
* @returns { Promise<void> | void }
*/
onMissingHeaders?: (req: Request, res: Response, next: NextFunction) => Promise<void> | void;
/**
* Custom response on signature mismatch. Validation stops here. Default value `onSignatureMismatch: () => {
res.status(401).send('The signature does not match');
},`
*
* @param req { Request }
* @param res { Response }
* @param next { NextFunction }
* @returns { Promise<void> | void }
*/
onSignatureMismatch?: (req: Request, res: Response, next: NextFunction) => Promise<void> | void;
/**
* Custom response on exired time signature. Validation stops here. Default value `onExpired: () => {
res.status(401).send('Request is expired');
},`
*
* @param req { Request }
* @param res { Response }
* @param next { NextFunction }
* @returns { Promise<void> | void }
*/
onExpired?: (req: Request, res: Response, next: NextFunction) => Promise<void> | void;
/**
* Custom callback before standard parser comes. On false validation stops here. Default value `onBeforeParse: () => true,`
*
* @param req { Request }
* @param res { Response }
* @param next { NextFunction }
* @returns { Promise<boolean> | boolean } Should return true if you need to let parse request further.
*/
onBeforeParse?: (req: Request, res: Response, next: NextFunction) => Promise<boolean> | boolean;
/**
* Custom callback after standard parser done. On false validation stops here. Default value `onAfterParse: () => true,`
*
* @param message { AwsIncomingMessage }
* @param req { Request }
* @param res { Response }
* @param next { NextFunction }
* @returns { Promise<boolean> | boolean } Should return true if you need to let parse request further.
*/
onAfterParse?: (message: AwsIncomingMessage, req: Request, res: Response, next: NextFunction) => Promise<boolean> | boolean;
/**
* Last callback after when validation signature is done. You can even stop here process. Default value `onSuccess: () => next()`. Dont forget to return next or next(error) or your validation hangs here.
*
* @param req { Request }
* @param res { Response }
* @param next { NextFunction }
* @returns { Promise<void> | void }
*/
onSuccess?: (message: AwsIncomingMessage | undefined, req: Request, res: Response, next: NextFunction) => Promise<void> | void;
}
/**
* Parsed Incomming Message
*/
export interface AwsIncomingMessage {
/**
* Incoming authorization headers string. Required.
*/
authorization: string;
/**
* DateTime from incoming header. Required.
*/
xAmzDate: string;
/**
* Additional header to set message exiration time even if signature message is valid. Optional.
*/
xAmzExpires?: number;
/**
* Sha256 + formated hex for body. Empty body has own bodyHash. If there is no need to sign body for performance reason you can put UNSIGNED-PAYLOAD in request headers['x-amz-content-sha256'].
*/
bodyHash: string;
/**
* Request path: /..
*/
path: string;
/**
* Query params as key value dictionary;
*/
query?: Dictionary;
/**
* Http method.
*/
method: string;
/**
* accessKey used to sign this message.
*/
accessKey: string;
/**
* Date used in authorization header.
*/
date: string;
/**
* Region used in authorization header. Here can be any value.
*/
region: string;
/**
* Service used in authorization header. Here can be any value.
*/
service: string;
/**
* For aws4 will be aws4_request. Here can be any value.
*/
requestType: string;
/**
* List of signed headers separated with semicolon.
*/
signedHeaders: string;
/**
* Formated encoded header paris.
*/
canonicalHeaders: string;
}
export declare class AwsSignature {
protected message?: AwsIncomingMessage;
protected options?: AwsVerifyOptions;
private secretKey?;
constructor();
verify: (options: AwsVerifyOptions) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
protected parse: (req: Request, res: Response, next: NextFunction) => Promise<boolean | void>;
protected authHeader: () => string;
protected credentialString: () => string;
protected signature: () => string;
protected stringToSign: () => string;
protected canonicalString: () => string;
protected parsePath: (url: string) => {
path: string;
query: querystring.ParsedUrlQuery | undefined;
};
protected canonicalQueryString: () => string;
protected canonicalURI: () => string;
protected trimAll: (header: string | string[] | undefined) => string | undefined;
protected encodeRfc3986: (urlEncodedString: string) => string;
protected encodeRfc3986Full: (str: string) => string;
protected hmacHex: (secretKey: BinaryLike | KeyObject, data: string) => string;
protected hmac: (secretKey: BinaryLike | KeyObject, data: string) => Buffer;
protected hash: (data: string) => string;
protected hashBuffer: (data: Buffer) => string;
protected expires: (dateTime: string, expires: number | undefined) => boolean;
}
export declare const awsVerify: (options: AwsVerifyOptions) => (req: Request, res: Response, next: NextFunction) => Promise<void>;