mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-10-16 18:46:26 +08:00
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:
parent
9d7443903a
commit
d792844473
4 changed files with 38 additions and 13 deletions
|
@ -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),
|
||||
});
|
||||
|
|
|
@ -20,6 +20,8 @@ const BASE_CONFIGURATION: MonkeyTypes.Configuration = {
|
|||
endpointsEnabled: false,
|
||||
acceptKeys: false,
|
||||
maxKeysPerUser: 0,
|
||||
apeKeyBytes: 24,
|
||||
apeKeySaltRounds: 5,
|
||||
},
|
||||
enableSavingResults: {
|
||||
enabled: false,
|
||||
|
|
|
@ -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 };
|
||||
|
|
2
backend/types/types.d.ts
vendored
2
backend/types/types.d.ts
vendored
|
@ -18,6 +18,8 @@ declare namespace MonkeyTypes {
|
|||
endpointsEnabled: boolean;
|
||||
acceptKeys: boolean;
|
||||
maxKeysPerUser: number;
|
||||
apeKeyBytes: number;
|
||||
apeKeySaltRounds: number;
|
||||
};
|
||||
enableSavingResults: {
|
||||
enabled: boolean;
|
||||
|
|
Loading…
Add table
Reference in a new issue