agmission/Development/server/model/api_key.js

33 lines
1.7 KiB
JavaScript

'use strict';
const mongoose = require('mongoose'), Schema = mongoose.Schema;
const { ApiKeyServices } = require('../helpers/constants');
/**
* ApiKey model — stores hashed API keys for external data-export consumers.
*
* Flow:
* 1. POST /api/keys → generate random key, store prefix (first 8 chars) + bcrypt hash, return plain key ONCE.
* 2. Subsequent requests supply X-API-Key header → middleware does prefix lookup + bcrypt.compare.
* 3. On match, middleware sets req.uid = key.owner so all existing ownership filters work unchanged.
*
* Security notes:
* - Plain key is NEVER stored. Only the bcrypt hash is persisted.
* - Key prefix (first 8 chars) is stored in clear text for efficient DB lookup before comparing hashes.
* - lastUsedAt is updated async (fire-and-forget) to avoid adding latency to the request path.
*/
const schema = new Schema({
owner: { type: Schema.Types.ObjectId, ref: 'User', required: true, index: true },
label: { type: String, required: true, trim: true, maxlength: 100 },
prefix: { type: String, required: true, index: true, maxlength: 8 }, // first 8 chars of plain key, stored for O(1) candidate lookup
keyHash: { type: String, required: true }, // bcrypt hash of the full plain key
service: { type: String, enum: Object.values(ApiKeyServices), default: ApiKeyServices.DATA_EXPORT, index: true }, // which service/integration this key grants access to
active: { type: Boolean, default: true, index: true },
managedBy: { type: String, enum: ['owner', 'admin'], default: 'owner' },
createdAt: { type: Date, default: Date.now },
lastUsedAt: { type: Date },
requestCount: { type: Number, default: 0 }
});
module.exports = mongoose.model('ApiKey', schema);