Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
itssimon committed Nov 21, 2023
1 parent 915358c commit 7548dc1
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 128 deletions.
64 changes: 64 additions & 0 deletions src/express/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { NextFunction, Request, Response } from "express";

import { ApitallyClient } from "../common/client";

export const requireApiKey = ({
scopes,
customHeader,
}: {
scopes?: string | string[];
customHeader?: string;
} = {}) => {
return async (req: Request, res: Response, next: NextFunction) => {
let apiKey: string | undefined;

if (!customHeader) {
if (!req.headers.authorization) {
res
.status(401)
.set("WWW-Authenticate", "ApiKey")
.json({ error: "Missing authorization header" });
return;
}
const authorizationParts = req.headers.authorization.split(" ");
if (
authorizationParts.length === 2 &&
authorizationParts[0].toLowerCase() === "apikey"
) {
apiKey = authorizationParts[1];
} else {
res
.status(401)
.set("WWW-Authenticate", "ApiKey")
.json({ error: "Invalid authorization scheme" });
return;
}
} else if (customHeader) {
const customHeaderValue = req.headers[customHeader.toLowerCase()];
if (typeof customHeaderValue === "string") {
apiKey = customHeaderValue;
} else if (Array.isArray(customHeaderValue)) {
apiKey = customHeaderValue[0];
}
}

if (!apiKey) {
res.status(403).json({ error: "Missing API key" });
return;
}

const client = ApitallyClient.getInstance();
const keyInfo = await client.keyRegistry.get(apiKey);
if (!keyInfo) {
res.status(403).json({ error: "Invalid API key" });
return;
}
if (scopes && !keyInfo.hasScopes(scopes)) {
res.status(403).json({ error: "Permission denied" });
return;
}

res.locals.keyInfo = keyInfo;
next();
};
};
3 changes: 2 additions & 1 deletion src/express/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { requireApiKey, useApitally } from "./middleware";
export { requireApiKey } from "./auth";
export { useApitally } from "./middleware";
61 changes: 0 additions & 61 deletions src/express/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,67 +16,6 @@ export const useApitally = (app: Express, config: ApitallyConfig) => {
}, 100);
};

export const requireApiKey = ({
scopes,
customHeader,
}: {
scopes?: string | string[];
customHeader?: string;
} = {}) => {
return async (req: Request, res: Response, next: NextFunction) => {
let apiKey: string | undefined;

if (!customHeader) {
if (!req.headers.authorization) {
res
.status(401)
.set("WWW-Authenticate", "ApiKey")
.json({ error: "Missing authorization header" });
return;
}
const authorizationParts = req.headers.authorization.split(" ");
if (
authorizationParts.length === 2 &&
authorizationParts[0].toLowerCase() === "apikey"
) {
apiKey = authorizationParts[1];
} else {
res
.status(401)
.set("WWW-Authenticate", "ApiKey")
.json({ error: "Invalid authorization scheme" });
return;
}
} else if (customHeader) {
const customHeaderValue = req.headers[customHeader.toLowerCase()];
if (typeof customHeaderValue === "string") {
apiKey = customHeaderValue;
} else if (Array.isArray(customHeaderValue)) {
apiKey = customHeaderValue[0];
}
}

if (!apiKey) {
res.status(403).json({ error: "Missing API key" });
return;
}

const client = ApitallyClient.getInstance();
const keyInfo = await client.keyRegistry.get(apiKey);
if (!keyInfo) {
res.status(403).json({ error: "Invalid API key" });
return;
}
if (scopes && !keyInfo.hasScopes(scopes)) {
res.status(403).json({ error: "Permission denied" });
return;
}

res.locals.keyInfo = keyInfo;
next();
};
};

const getMiddleware = (client: ApitallyClient) => {
const validatorInstalled = getPackageVersion("express-validator") !== null;
const celebrateInstalled = getPackageVersion("celebrate") !== null;
Expand Down
65 changes: 65 additions & 0 deletions src/koa/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import Koa from "koa";

import { ApitallyClient } from "../common/client";

export const requireApiKey = ({
scopes,
customHeader,
}: {
scopes?: string | string[];
customHeader?: string;
} = {}) => {
return async (ctx: Koa.Context, next: Koa.Next) => {
let apiKey: string | undefined;

if (!customHeader) {
if (!ctx.headers.authorization) {
ctx.status = 401;
ctx.set("WWW-Authenticate", "ApiKey");
ctx.body = { error: "Missing authorization header" };
return;
}
const authorizationParts = ctx.headers.authorization.split(" ");
if (
authorizationParts.length === 2 &&
authorizationParts[0].toLowerCase() === "apikey"
) {
apiKey = authorizationParts[1];
} else {
ctx.status = 401;
ctx.set("WWW-Authenticate", "ApiKey");
ctx.body = { error: "Invalid authorization scheme" };
return;
}
} else if (customHeader) {
const customHeaderValue = ctx.headers[customHeader.toLowerCase()];
if (typeof customHeaderValue === "string") {
apiKey = customHeaderValue;
} else if (Array.isArray(customHeaderValue)) {
apiKey = customHeaderValue[0];
}
}

if (!apiKey) {
ctx.status = 403;
ctx.body = { error: "Missing API key" };
return;
}

const client = ApitallyClient.getInstance();
const keyInfo = await client.keyRegistry.get(apiKey);
if (!keyInfo) {
ctx.status = 403;
ctx.body = { error: "Invalid API key" };
return;
}
if (scopes && !keyInfo.hasScopes(scopes)) {
ctx.status = 403;
ctx.body = { error: "Permission denied" };
return;
}

ctx.state.keyInfo = keyInfo;
await next();
};
};
3 changes: 2 additions & 1 deletion src/koa/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { requireApiKey, useApitally } from "./middleware";
export { requireApiKey } from "./auth";
export { useApitally } from "./middleware";
62 changes: 0 additions & 62 deletions src/koa/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,68 +14,6 @@ export const useApitally = (app: Koa, config: ApitallyConfig) => {
}, 100);
};

export const requireApiKey = ({
scopes,
customHeader,
}: {
scopes?: string | string[];
customHeader?: string;
} = {}) => {
return async (ctx: Koa.Context, next: Koa.Next) => {
let apiKey: string | undefined;

if (!customHeader) {
if (!ctx.headers.authorization) {
ctx.status = 401;
ctx.set("WWW-Authenticate", "ApiKey");
ctx.body = { error: "Missing authorization header" };
return;
}
const authorizationParts = ctx.headers.authorization.split(" ");
if (
authorizationParts.length === 2 &&
authorizationParts[0].toLowerCase() === "apikey"
) {
apiKey = authorizationParts[1];
} else {
ctx.status = 401;
ctx.set("WWW-Authenticate", "ApiKey");
ctx.body = { error: "Invalid authorization scheme" };
return;
}
} else if (customHeader) {
const customHeaderValue = ctx.headers[customHeader.toLowerCase()];
if (typeof customHeaderValue === "string") {
apiKey = customHeaderValue;
} else if (Array.isArray(customHeaderValue)) {
apiKey = customHeaderValue[0];
}
}

if (!apiKey) {
ctx.status = 403;
ctx.body = { error: "Missing API key" };
return;
}

const client = ApitallyClient.getInstance();
const keyInfo = await client.keyRegistry.get(apiKey);
if (!keyInfo) {
ctx.status = 403;
ctx.body = { error: "Invalid API key" };
return;
}
if (scopes && !keyInfo.hasScopes(scopes)) {
ctx.status = 403;
ctx.body = { error: "Permission denied" };
return;
}

ctx.state.keyInfo = keyInfo;
await next();
};
};

const getMiddleware = (client: ApitallyClient) => {
return async (ctx: Koa.Context, next: Koa.Next) => {
const startTime = performance.now();
Expand Down
1 change: 0 additions & 1 deletion src/nestjs/middleware.ts → src/nestjs/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { Reflector } from "@nestjs/core";
import { Request, Response } from "express";

import { ApitallyClient } from "../common/client";
export { useApitally } from "../express";

export const Scopes = (...scopes: string[]) => SetMetadata("scopes", scopes);

Expand Down
3 changes: 2 additions & 1 deletion src/nestjs/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { ApitallyApiKeyGuard, Scopes, useApitally } from "./middleware";
export { useApitally } from "../express";
export { ApitallyApiKeyGuard, Scopes } from "./auth";
2 changes: 1 addition & 1 deletion tests/nestjs/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BaseExceptionFilter } from "@nestjs/core";
import request from "supertest";

import { ApitallyClient } from "../../src/common/client";
import { ApitallyApiKeyGuard } from "../../src/nestjs/middleware";
import { ApitallyApiKeyGuard } from "../../src/nestjs/auth";
import { API_KEY, mockApitallyHub } from "../utils";
import { getApp } from "./app";

Expand Down

0 comments on commit 7548dc1

Please sign in to comment.