Add ape key authentication (#2610)

* Add ape key authentication

* Move ape key config to server config

* Remove full stops

* Fix
This commit is contained in:
Bruce Berrios 2022-03-01 17:43:22 -05:00 committed by GitHub
parent 9d7443903a
commit d792844473
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 13 deletions

View file

@ -6,9 +6,6 @@ import MonkeyError from "../../handlers/error";
import { MonkeyResponse } from "../../handlers/monkey-response";
import { base64UrlEncode } from "../../handlers/misc";
const APE_KEY_BYTES = 48;
const SALT_ROUNDS = parseInt(process.env.APE_KEY_SALT_ROUNDS, 10) || 5;
function cleanApeKey(apeKey: MonkeyTypes.ApeKey): Partial<MonkeyTypes.ApeKey> {
return _.omit(apeKey, "hash");
}
@ -28,7 +25,8 @@ class ApeKeysController {
): Promise<MonkeyResponse> {
const { name, enabled } = req.body;
const { uid } = req.ctx.decodedToken;
const { maxKeysPerUser } = req.ctx.configuration.apeKeys;
const { maxKeysPerUser, apeKeyBytes, apeKeySaltRounds } =
req.ctx.configuration.apeKeys;
const currentNumberOfApeKeys = await ApeKeysDAO.countApeKeysForUser(uid);
@ -39,8 +37,8 @@ class ApeKeysController {
);
}
const apiKey = randomBytes(APE_KEY_BYTES).toString("base64url");
const saltyHash = await hash(apiKey, SALT_ROUNDS);
const apiKey = randomBytes(apeKeyBytes).toString("base64url");
const saltyHash = await hash(apiKey, apeKeySaltRounds);
const apeKey: MonkeyTypes.ApeKey = {
name,
@ -53,7 +51,7 @@ class ApeKeysController {
const apeKeyId = await ApeKeysDAO.addApeKey(uid, apeKey);
return new MonkeyResponse("ApeKey generated", {
apeKey: base64UrlEncode(`${apeKeyId}.${apiKey}`),
apeKey: base64UrlEncode(`${uid}.${apeKeyId}.${apiKey}`),
apeKeyId,
apeKeyDetails: cleanApeKey(apeKey),
});

View file

@ -20,6 +20,8 @@ const BASE_CONFIGURATION: MonkeyTypes.Configuration = {
endpointsEnabled: false,
acceptKeys: false,
maxKeysPerUser: 0,
apeKeyBytes: 24,
apeKeySaltRounds: 5,
},
enableSavingResults: {
enabled: false,

View file

@ -1,5 +1,9 @@
import _ from "lodash";
import { compare } from "bcrypt";
import UsersDAO from "../dao/user";
import MonkeyError from "../handlers/error";
import { verifyIdToken } from "../handlers/auth";
import { base64UrlDecode } from "../handlers/misc";
import { NextFunction, Response, Handler } from "express";
interface RequestAuthenticationOptions {
@ -94,7 +98,7 @@ async function authenticateWithAuthHeader(
throw new MonkeyError(
401,
"Unknown authentication scheme",
`The authentication scheme "${authScheme}" is not implemented.`
`The authentication scheme "${authScheme}" is not implemented`
);
}
@ -137,13 +141,32 @@ async function authenticateWithApeKey(
options: RequestAuthenticationOptions
): Promise<MonkeyTypes.DecodedToken> {
if (!configuration.apeKeys.acceptKeys) {
throw new MonkeyError(403, "ApeKeys are not being accepted at this time.");
}
if (!options.acceptApeKeys) {
throw new MonkeyError(401, "This endpoint does not accept ApeKeys.");
throw new MonkeyError(403, "ApeKeys are not being accepted at this time");
}
throw new MonkeyError(401, "ApeKeys are not implemented.");
if (!options.acceptApeKeys) {
throw new MonkeyError(401, "This endpoint does not accept ApeKeys");
}
try {
const decodedKey = base64UrlDecode(key);
const [uid, keyId, apeKey] = decodedKey.split(".");
const keyOwner = (await UsersDAO.getUser(uid)) as MonkeyTypes.User;
const targetApeKey = _.get(keyOwner.apeKeys, keyId);
const isKeyValid = await compare(apeKey, targetApeKey?.hash);
if (!isKeyValid) {
throw new MonkeyError(400);
}
return {
uid,
email: keyOwner.email,
};
} catch (error) {
throw new MonkeyError(400, "Invalid ApeKey");
}
}
export { authenticateRequest };

View file

@ -18,6 +18,8 @@ declare namespace MonkeyTypes {
endpointsEnabled: boolean;
acceptKeys: boolean;
maxKeysPerUser: number;
apeKeyBytes: number;
apeKeySaltRounds: number;
};
enableSavingResults: {
enabled: boolean;