Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 40 additions & 37 deletions packages/next/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,54 @@
import { NextApiResponse, NextApiRequest } from 'next';
import { JwtVerifier, getTokenFromHeader } from '@serverless-jwt/jwt-verifier';
import { NextJwtVerifierOptions, IApiRoute, NextAuthenticatedApiRequest } from './types';
import { NextApiResponse, NextApiRequest, NextApiHandler } from 'next';
import { JwtVerifier, getTokenFromHeader, JwtVerifierError } from '@serverless-jwt/jwt-verifier';
import { NextJwtVerifierOptions, NextAuthenticatedApiRequest } from './types';

/**
* Middleware to validate a token and set the user context.
*/
const validateJWT = (verifier: JwtVerifier, options: NextJwtVerifierOptions) => {
return (apiRoute: IApiRoute): IApiRoute => async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
if (!req) {
throw new Error('Request is not available');
}

if (!res) {
throw new Error('Response is not available');
}

let claims;
let accessToken;

try {
accessToken = getTokenFromHeader(req.headers.authorization as string);
claims = await verifier.verifyAccessToken(accessToken);
} catch (err) {
if (typeof options.handleError !== 'undefined' && options.handleError !== null) {
return options.handleError(res, err);
return (handler: NextApiHandler): NextApiHandler => {
return async (req: NextApiRequest, res: NextApiResponse) => {
if (!req) {
throw new Error('Request is not available');
}

return res.status(401).json({
error: err.code,
error_description: err.message
});
}

// Expose the identity in the client context.
const authenticatedRequest = req as NextAuthenticatedApiRequest;
authenticatedRequest.identityContext = {
token: accessToken as string,
claims: claims as Record<string, unknown>
};
if (!res) {
throw new Error('Response is not available');
}

let claims;
let accessToken;

try {
accessToken = getTokenFromHeader(req.headers.authorization as string);
claims = await verifier.verifyAccessToken(accessToken);
} catch (err) {
if (typeof options.handleError !== 'undefined' && options.handleError !== null) {
return options.handleError(res, err as JwtVerifierError);
}

const verifyError = err as JwtVerifierError;
return res.status(401).json({
error: verifyError.code,
error_description: verifyError.message
});
}

// Expose the identity in the client context.
const authenticatedRequest = req as NextAuthenticatedApiRequest;
authenticatedRequest.identityContext = {
token: accessToken as string,
claims: claims as Record<string, unknown>
};

// Continue.
return apiRoute(req, res);
// Continue.
return handler(req, res);
};
};
};

export interface NextJwtVerifier {
(apiRoute: IApiRoute): (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
interface NextJwtVerifier {
(handler: NextApiHandler): NextApiHandler;
}

/**
Expand Down
4 changes: 0 additions & 4 deletions packages/next/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ export interface NextJwtVerifierOptions extends JwtVerifierOptions {
handleError?(res: NextApiResponse, err: Error | JwtVerifierError): Promise<void>;
}

export interface IApiRoute {
(req: NextApiRequest, res: NextApiResponse): Promise<void>;
}

export interface IdentityContext {
/**
* The token that was provided.
Expand Down