Skip to content
This repository has been archived by the owner on Dec 14, 2024. It is now read-only.

Commit

Permalink
fix: Allow usage of express middlewares
Browse files Browse the repository at this point in the history
  • Loading branch information
amaury1093 committed Oct 19, 2019
1 parent ea23a55 commit a37b8f5
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 129 deletions.
20 changes: 7 additions & 13 deletions api/user.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import { NowRequest, NowResponse } from '@now/node';
import cors from 'micro-cors';
import cors from 'cors';

import { User } from '../models';
import { connectToDatabase, jwt } from '../util';
import { chain, checkJwt, connectToDatabase, WithJwt } from '../util';

/**
* Fetch a user
*/
async function user(req: NowRequest, res: NowResponse): Promise<void> {
if (req.method !== 'GET') {
res.status(200).send('ok');
return;
}

const decoded = await jwt(req, res);

console.log('AAA', decoded);

async function user(
req: NowRequest & WithJwt,
res: NowResponse
): Promise<void> {
if (!process.env.MONGODB_ATLAS_URI) {
throw new Error('MONGODB_ATLAS_URI is not defined');
}
Expand All @@ -34,4 +28,4 @@ async function user(req: NowRequest, res: NowResponse): Promise<void> {
res.status(200).json({ users });
}

export default cors()(user);
export default chain<NowRequest & WithJwt, NowResponse>(cors(), checkJwt)(user);
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@
},
"dependencies": {
"@now/node": "^1.0.1",
"@types/jsonwebtoken": "^8.3.5",
"@types/micro-cors": "^0.1.0",
"@types/cors": "^2.8.6",
"@types/mongodb": "^3.3.6",
"jsonwebtoken": "^8.5.1",
"cors": "^2.8.5",
"express-jwt": "^5.3.1",
"jwks-rsa": "^1.6.0",
"micro-cors": "^0.1.1",
"mongodb": "^3.3.3"
},
"devDependencies": {
Expand Down
49 changes: 49 additions & 0 deletions util/chain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { RequestHandler } from 'express';

type AsyncVoid = void | Promise<void>;

type NowFunction<Req, Res> = (req: Req, res: Res) => AsyncVoid;

/**
* Combine multiple middleware together.
*
* @param middlewares - Functions of form: `function(req, res, next) { ... }`, aka
* express middlewares.
*
* @return - Single combined middleware
*/
function combineMiddleware(middlewares: RequestHandler[]): RequestHandler {
return middlewares.reduce((acc, mid) => {
return function(req, res, next): void {
acc(req, res, err => {
if (err) {
return next(err);
}

mid(req, res, next);
});
};
});
}

/**
* Chain middlewares together, and expose them to be consumed by a `@now/node`
* serverless function.
*
* @param middlewares - Functions of form: `function(req, res, next) { ... }`, aka
* express middlewares.
*/
export function chain<Req, Res>(
...middlewares: RequestHandler[]
): (fn: NowFunction<Req, Res>) => NowFunction<Req, Res> {
return function(fn: NowFunction<Req, Res>): NowFunction<Req, Res> {
return function(req: Req, res: Res): AsyncVoid {
// eslint-disable-next-line
// @ts-ignore Need to cast (and verify everything works) from a
// express.Request to a NowRequest
return combineMiddleware(middlewares)(req, res, () => {
fn(req, res);
});
};
};
}
1 change: 1 addition & 0 deletions util/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './chain';
export * from './db';
export * from './jwt';
115 changes: 31 additions & 84 deletions util/jwt.ts
Original file line number Diff line number Diff line change
@@ -1,92 +1,39 @@
import { NowRequest, NowResponse } from '@now/node';
import { verify, VerifyErrors } from 'jsonwebtoken';
import jwksClient, {
JwksClient,
CertSigningKey,
RsaSigningKey
} from 'jwks-rsa';
import expressJwt from 'express-jwt';
import { expressJwtSecret } from 'jwks-rsa';

/**
* Once the middleware passes, the `req` object will have an `auth0` field with
* the following properties
*/
export interface WithJwt {
auth0: {
iss: string;
sub: string;
aud: string[];
iat: number;
exp: number;
azp: string;
scope: string;
};
}

// Set up Auth0 configuration
const authConfig = {
domain: process.env.AUTH0_DOMAIN,
audience: process.env.AUTH0_API_IDENTIFIER
};

// Define middleware that validates incoming bearer tokens using JWKS from
// auth0 domain
const client: JwksClient = jwksClient({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${authConfig.domain}/.well-known/jwks.json`
// Define middleware that validates incoming bearer tokens
// using JWKS from YOUR_DOMAIN
export const checkJwt = expressJwt({
algorithm: ['RS256'],
audience: authConfig.audience,
issuer: `https://${authConfig.domain}/`,
requestProperty: 'auth0',
secret: expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${authConfig.domain}/.well-known/jwks.json`
})
});

/**
* Verify using getKey callback
* @see https://github.com/auth0/node-jsonwebtoken
*/
function getKey(
header: { kid?: string },
callback: (err: Error | null, result: string | undefined) => void
): void {
console.log('header.kid', header.kid);
if (!header.kid) {
callback(new Error('No `kid` field in header'), undefined);

return;
}

client.getSigningKey(header.kid, function(err, key) {
if (err) {
callback(err, undefined);

return;
}

const signingKey =
(key as CertSigningKey).publicKey || (key as RsaSigningKey).rsaPublicKey;

console.log('signingKey', signingKey);
callback(null, signingKey);
});
}

export async function jwt(
req: NowRequest,
res: NowResponse
): Promise<string | object> {
try {
const bearerToken = req.headers.authorization;

if (!bearerToken) {
throw new Error('Missing Authorization header');
}

const token = bearerToken.replace('Bearer ', '');
console.log('TOKEN', token, authConfig);
const decoded = await new Promise<string | object>((resolve, reject) =>
verify(
token,
getKey,
{
algorithms: ['RS256'],
audience: authConfig.audience,
issuer: `https://${authConfig.domain}/`
},
(err: VerifyErrors, decoded: string | object) => {
if (err) {
return reject(err);
}

resolve(decoded);
}
)
);

return decoded;
} catch (error) {
res.status(401).send(`Invalid token: ${error.message}`);

throw error;
}
}
75 changes: 47 additions & 28 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@
dependencies:
"@types/node" "*"

"@types/cors@^2.8.6":
version "2.8.6"
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.6.tgz#cfaab33c49c15b1ded32f235111ce9123009bd02"
integrity sha512-invOmosX0DqbpA+cE2yoHGUlF/blyf7nB0OGYBBiH27crcVm5NmFaZkLP4Ta1hGaesckCi5lVLlydNJCxkTOSg==
dependencies:
"@types/express" "*"

"@types/eslint-visitor-keys@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
Expand Down Expand Up @@ -115,27 +122,6 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==

"@types/jsonwebtoken@^8.3.5":
version "8.3.5"
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.3.5.tgz#ff9be1151a844095df1ff5f723651298c2c07659"
integrity sha512-VGM1gb+LwsQ5EPevvbvdnKncajBdYqNcrvixBif1BsiDQiSF1q+j4bBTvKC6Bt9n2kqNSx+yNTY2TVJ360E7EQ==
dependencies:
"@types/node" "*"

"@types/micro-cors@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@types/micro-cors/-/micro-cors-0.1.0.tgz#4ee6da45a80373230c6c4ceaba80fa05523db2e1"
integrity sha512-82Ug+VjbCLLN/cNbvRmIr49PA15HrRH+VcAUoDIZeMnWGQCJx2vmSYZy/S7DvqrmEoOZquwuxbs5w/8dfMSbcQ==
dependencies:
"@types/micro" "*"

"@types/micro@*":
version "7.3.3"
resolved "https://registry.yarnpkg.com/@types/micro/-/micro-7.3.3.tgz#31ead8df18ac10d58b7be1186d4b2d977b13a938"
integrity sha512-I3n3QYT7lqAxkyAoTZyg1yrvo38BxW/7ZafLAXZF/zZQOnAnQzg6j9XOuSmUEL5GGVFKWw4iqM+ZLnqb2154TA==
dependencies:
"@types/node" "*"

"@types/mime@*":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
Expand Down Expand Up @@ -290,6 +276,11 @@ astral-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==

async@^1.5.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=

asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
Expand Down Expand Up @@ -427,6 +418,14 @@ core-util-is@1.0.2:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=

cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"

cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
Expand Down Expand Up @@ -670,6 +669,21 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==

express-jwt@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-5.3.1.tgz#66f05c7dddb5409c037346a98b88965bb10ea4ae"
integrity sha512-1C9RNq0wMp/JvsH/qZMlg3SIPvKu14YkZ4YYv7gJQ1Vq+Dv8LH9tLKenS5vMNth45gTlEUGx+ycp9IHIlaHP/g==
dependencies:
async "^1.5.0"
express-unless "^0.3.0"
jsonwebtoken "^8.1.0"
lodash.set "^4.0.0"

express-unless@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-0.3.1.tgz#2557c146e75beb903e2d247f9b5ba01452696e20"
integrity sha1-JVfBRudb65A+LSR/m1ugFFJpbiA=

extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
Expand Down Expand Up @@ -1026,7 +1040,7 @@ json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=

jsonwebtoken@^8.5.1:
jsonwebtoken@^8.1.0, jsonwebtoken@^8.5.1:
version "8.5.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
Expand Down Expand Up @@ -1143,6 +1157,11 @@ lodash.once@^4.0.0:
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=

lodash.set@^4.0.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=

lodash.unescape@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
Expand Down Expand Up @@ -1183,11 +1202,6 @@ memory-pager@^1.0.2:
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==

micro-cors@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/micro-cors/-/micro-cors-0.1.1.tgz#af7a480182c114ffd1ada84ad9dffc52bb4f4054"
integrity sha512-6WqIahA5sbQR1Gjexp1VuWGFDKbZZleJb/gy1khNGk18a6iN1FdTcr3Q8twaxkV5H94RjxIBjirYbWCehpMBFw==

mime-db@1.40.0:
version "1.40.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
Expand Down Expand Up @@ -1270,7 +1284,7 @@ oauth-sign@~0.9.0:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==

object-assign@^4.1.1:
object-assign@^4, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
Expand Down Expand Up @@ -1775,6 +1789,11 @@ v8-compile-cache@^2.0.3:
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==

vary@^1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=

verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
Expand Down

0 comments on commit a37b8f5

Please sign in to comment.