Begin API Key functionality

This commit is contained in:
Tuan Dang
2023-01-04 18:04:53 +07:00
parent 212ca72c7b
commit ff0b053d12
7 changed files with 123 additions and 6 deletions

View File

@@ -0,0 +1,54 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import crypto from 'crypto';
import bcrypt from 'bcrypt';
import {
APIKeyData
} from '../../models';
import {
SALT_ROUNDS
} from '../../config';
/**
* Create new API key for user with id [req.user._id]
* @param req
* @param res
*/
export const createAPIKey = async (req: Request, res: Response) => {
let apiKey, apiKeyData;
try {
const { name, expiresIn } = req.body;
const secret = crypto.randomBytes(16).toString('hex');
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS);
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
apiKeyData = await new APIKeyData({
name,
expiresAt,
user: req.user._id,
secretHash
});
// return api key data without sensitive data
apiKeyData = await APIKeyData.findById(apiKeyData._id);
if (!apiKeyData) throw new Error('Failed to find API key data');
apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to create service token data'
});
}
return res.status(200).send({
apiKey,
apiKeyData
});
}

View File

@@ -1,7 +1,9 @@
import * as workspaceController from './workspaceController';
import * as serviceTokenDataController from './serviceTokenDataController';
import * as apiKeyDataController from './apiKeyDataController';
export {
workspaceController,
serviceTokenDataController
serviceTokenDataController,
apiKeyDataController
}

View File

@@ -0,0 +1,37 @@
import { Schema, model, Types } from 'mongoose';
export interface IAPIKeyData {
name: string;
user: Types.ObjectId;
expiresAt: Date;
secretHash: string;
}
const apiKeyDataSchema = new Schema<IAPIKeyData>(
{
name: {
type: String,
required: true
},
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
expiresAt: {
type: Date
},
secretHash: {
type: String,
required: true,
select: false
}
},
{
timestamps: true
}
);
const APIKeyData = model<IAPIKeyData>('APIKeyData', apiKeyDataSchema);
export default APIKeyData;

View File

@@ -14,7 +14,8 @@ import Token, { IToken } from './token';
import User, { IUser } from './user';
import UserAction, { IUserAction } from './userAction';
import Workspace, { IWorkspace } from './workspace';
import ServiceTokenData, { IServiceTokenData } from './serviceTokenData ';
import ServiceTokenData, { IServiceTokenData } from './serviceTokenData';
import APIKeyData, { IAPIKeyData } from './apiKeyData';
export {
BackupPrivateKey,
@@ -50,5 +51,7 @@ export {
Workspace,
IWorkspace,
ServiceTokenData,
IServiceTokenData
IServiceTokenData,
APIKeyData,
IAPIKeyData
};

View File

@@ -1,5 +1,4 @@
import { Schema, model, Types } from 'mongoose';
import { ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD } from '../variables';
export interface IServiceTokenData {
name: string;
@@ -38,7 +37,6 @@ const serviceTokenDataSchema = new Schema<IServiceTokenData>(
},
secretHash: {
type: String,
unique: true,
required: true,
select: false
},

View File

@@ -0,0 +1,21 @@
import express from 'express';
const router = express.Router();
import {
requireAuth,
validateRequest
} from '../../middleware';
import { body } from 'express-validator';
import { apiKeyDataController } from '../../controllers/v2';
router.post(
'/',
requireAuth({
acceptedAuthModes: ['jwt']
}),
body('name').exists().trim(),
body('expiresIn'), // measured in ms
validateRequest,
apiKeyDataController.createAPIKey
);
export default router;

View File

@@ -1,9 +1,11 @@
import secret from './secret';
import workspace from './workspace';
import serviceTokenData from './serviceTokenData';
import apiKeyData from './apiKeyData';
export {
secret,
workspace,
serviceTokenData
serviceTokenData,
apiKeyData
}