Use middleware to validate the API requests (#792)

* Update

* Validate the API routes in the middleware

* Validate the setuplink token by calling the API

* Tweaks

* unAuthorizedResponse method now accept a message

* Update middleware

* Cleanup

* Update message

* Removed CheckSession - Not needed anymore

* Updates to API authentication middleware

* Updates to API authentication middleware

* Remove the use of checkSession from retraced admin APIs

Co-authored-by: Deepak Prabhakara <deepak@boxyhq.com>
This commit is contained in:
Kiran K 2023-01-07 05:07:08 +05:30 committed by GitHub
parent b4c98be9f4
commit eb80fb5297
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 80 additions and 69 deletions

View File

@ -1,7 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
import type { SAMLFederationApp } from '@boxyhq/saml-jackson';
import { strings } from '@lib/strings';
@ -101,4 +100,4 @@ const handleDELETE = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { checkSession } from '@lib/middleware';
import jackson from '@lib/jackson';
import { strings } from '@lib/strings';
@ -65,4 +64,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
return res.json({ data: apps });
};
export default checkSession(handler);
export default handler;

View File

@ -1,7 +1,5 @@
import Cors from 'cors';
import { NextApiRequest, NextApiResponse } from 'next';
import { getToken } from 'next-auth/jwt';
import { sessionName } from './constants';
// Initializing the cors middleware
const corsFunction = Cors({
@ -25,19 +23,3 @@ function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: any) {
export async function cors(req: NextApiRequest, res: NextApiResponse) {
return await runMiddleware(req, res, corsFunction);
}
export const checkSession = (handler) => async (req: NextApiRequest, res: NextApiResponse) => {
const token = await getToken({
req,
cookieName: sessionName,
});
if (token) {
// Signed in
return handler(req, res);
} else {
// Not Signed in
res.status(401);
}
res.end();
};

View File

@ -1,17 +1,69 @@
// eslint-disable-next-line
import type { NextRequest } from 'next/server';
// eslint-disable-next-line
import { NextResponse } from 'next/server';
import { validateApiKey, extractAuthToken } from '@lib/auth';
import { getToken } from 'next-auth/jwt';
import { sessionName } from '@lib/constants';
import micromatch from 'micromatch';
export function middleware(req: NextRequest) {
const pathname = req.nextUrl.pathname;
// Add API routes that don't require authentication
const unAuthenticatedApiRoutes = [
'/api/health',
'/api/hello',
'/api/auth/**',
'/api/federated-saml/**',
'/api/logout/**',
'/api/oauth/**',
'/api/scim/v2.0/**',
'/api/well-known/**',
'/api/setup/**',
];
if (pathname.startsWith('/api/v1')) {
if (!validateApiKey(extractAuthToken(req))) {
return NextResponse.rewrite(new URL('/api/v1/unauthenticated', req.nextUrl));
}
export async function middleware(req: NextRequest) {
const { pathname } = req.nextUrl;
// Bypass routes that don't require authentication
if (micromatch.isMatch(pathname, unAuthenticatedApiRoutes)) {
return NextResponse.next();
}
return NextResponse.next();
// Validate API routes `/api/admin/**`
if (micromatch.isMatch(pathname, '/api/admin/**')) {
const adminToken = await getToken({
req,
cookieName: sessionName,
});
if (!adminToken) {
return sendUnAuthorizedResponse({ message: 'Unauthorized' });
}
return NextResponse.next();
}
// Validate API routes `/api/v1/**`
if (micromatch.isMatch(pathname, '/api/v1/**')) {
if (!validateApiKey(extractAuthToken(req))) {
return sendUnAuthorizedResponse({ message: 'Unauthorized' });
}
return NextResponse.next();
}
// By default, deny access to all other routes
return sendUnAuthorizedResponse({ message: 'Unauthorized' });
}
// Send 401 response for unauthenticated requests
const sendUnAuthorizedResponse = async (error: { message: string }) => {
const response = JSON.stringify({ error });
return new NextResponse(response, {
status: 401,
headers: { 'content-type': 'application/json' },
});
};
// Limit the middleware to specific '/api/*' routes
export const config = {
matcher: ['/api/:path*'],
};

View File

@ -1,7 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -34,4 +33,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -6,7 +6,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
switch (method) {
case 'GET':
return handleGET(req, res);
return await handleGET(req, res);
default:
res.setHeader('Allow', 'GET');
res.status(405).json({ error: { message: `Method ${method} Not Allowed` } });

View File

@ -2,7 +2,6 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { strategyChecker } from '@lib/utils';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -118,4 +117,4 @@ const handleDELETE = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -36,4 +35,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(200).json({ data: event });
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -64,4 +63,4 @@ const handleDELETE = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(200).json({ data: null });
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -39,4 +38,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -42,4 +41,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
export const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -58,4 +57,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -39,4 +38,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -42,4 +41,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,7 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import type { DirectoryType } from '@boxyhq/saml-jackson';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -59,4 +58,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -23,4 +22,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
return res.json({ data: providers });
};
export default checkSession(handler);
export default handler;

View File

@ -3,7 +3,6 @@ import axios from 'axios';
import { getToken } from '@lib/retraced';
import { retracedOptions } from '@lib/env';
import { checkSession } from '@lib/middleware';
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req;
@ -46,4 +45,4 @@ const getGroups = async (req: NextApiRequest, res: NextApiResponse) => {
});
};
export default checkSession(handler);
export default handler;

View File

@ -4,7 +4,6 @@ import axios from 'axios';
import type { Project } from 'types/retraced';
import { getToken } from '@lib/retraced';
import { retracedOptions } from '@lib/env';
import { checkSession } from '@lib/middleware';
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req;
@ -41,4 +40,4 @@ const getProject = async (req: NextApiRequest, res: NextApiResponse) => {
});
};
export default checkSession(handler);
export default handler;

View File

@ -2,7 +2,6 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import * as Retraced from '@retracedhq/retraced';
import { retracedOptions } from '@lib/env';
import { checkSession } from '@lib/middleware';
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req;
@ -41,4 +40,4 @@ const getViewerToken = async (req: NextApiRequest, res: NextApiResponse) => {
});
};
export default checkSession(handler);
export default handler;

View File

@ -4,7 +4,6 @@ import axios from 'axios';
import type { Project } from 'types/retraced';
import { getToken } from '@lib/retraced';
import { retracedOptions } from '@lib/env';
import { checkSession } from '@lib/middleware';
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req;
@ -74,4 +73,4 @@ const getProjects = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
import { checkSession } from '@lib/middleware';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
@ -84,4 +83,4 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
}
};
export default checkSession(handler);
export default handler;

View File

@ -1,5 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
return res.status(401).json({ data: null, error: { message: 'Unauthorized' } });
}