@ajejoseph22/proxx
Version:
A lightweight HTTPS/HTTP proxy server with bandwidth tracking, basic auth and real-time analytics.
96 lines (81 loc) • 2.62 kB
text/typescript
import bcrypt from "bcrypt";
import express from "express";
import http from "http";
import { IDatabaseService } from "../database-service";
export type AuthResponse = {
isAuthenticated: boolean;
code: number;
message: string;
};
export class AuthService {
private readonly databaseService: IDatabaseService;
private readonly statusCode = {
UNAUTHORIZED: 401,
PROXY_AUTH_REQUIRED: 407,
FORBIDDEN: 403,
OK: 200,
};
constructor(dbService: IDatabaseService) {
this.databaseService = dbService;
}
private async addUser(username: string, password: string): Promise<void> {
const salt = await bcrypt.genSalt(10);
const auth = this.databaseService.getData().auth;
auth[username] = {
passwordHash: await bcrypt.hash(password, salt),
};
await this.databaseService.save({ auth });
}
private async isAuthenticated(authHeader: string): Promise<boolean> {
const [method, credentials] = authHeader.split(" ");
if (method !== "Basic") return false;
const [username, password] = Buffer.from(credentials, "base64")
.toString()
.split(":");
const user = this.databaseService.getData().auth?.[username];
if (!user) return false;
return bcrypt.compare(password, user.passwordHash);
}
private async handleAuth(
authHeader: string | undefined,
isProxyAuth: boolean,
): Promise<AuthResponse> {
if (!authHeader) {
return {
isAuthenticated: false,
code: isProxyAuth
? this.statusCode.PROXY_AUTH_REQUIRED
: this.statusCode.UNAUTHORIZED,
message: "Authentication Required\r\n\r\n",
};
}
const isAuthenticated = await this.isAuthenticated(authHeader);
return {
isAuthenticated,
code: isAuthenticated ? this.statusCode.OK : this.statusCode.FORBIDDEN,
message: `${isAuthenticated ? "OK" : "Invalid credentials"}\r\n\r\n}`,
};
}
async initialize(): Promise<void> {
if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) {
await this.addUser(
process.env.ADMIN_USERNAME,
process.env.ADMIN_PASSWORD,
);
}
}
async proxyAuth(
req: express.Request | http.IncomingMessage,
res?: express.Response,
): Promise<AuthResponse> {
res?.setHeader("Proxy-Authenticate", "Basic");
return this.handleAuth(req.headers["proxy-authorization"], true);
}
async endpointAuth(
req: express.Request,
res: express.Response,
): Promise<AuthResponse> {
res.setHeader("WWW-Authenticate", "Basic");
return this.handleAuth(req.headers["authorization"], false);
}
}