feat: add API key expiry options (#7064)
* feat: add API key expiry options * review
This commit is contained in:
@@ -83,6 +83,10 @@ export default function auth(options: AuthenticationOptions = {}) {
|
||||
throw AuthenticationError("Invalid API key");
|
||||
}
|
||||
|
||||
if (apiKey.expiresAt && apiKey.expiresAt < new Date()) {
|
||||
throw AuthenticationError("Invalid API key");
|
||||
}
|
||||
|
||||
user = await User.findByPk(apiKey.userId, {
|
||||
include: [
|
||||
{
|
||||
|
||||
15
server/migrations/20240617030911-add-apikey-expiry.js
Normal file
15
server/migrations/20240617030911-add-apikey-expiry.js
Normal file
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.addColumn("apiKeys", "expiresAt", {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: true,
|
||||
});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
await queryInterface.removeColumn("apiKeys", "expiresAt");
|
||||
},
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
BeforeValidate,
|
||||
BelongsTo,
|
||||
ForeignKey,
|
||||
IsDate,
|
||||
} from "sequelize-typescript";
|
||||
import { ApiKeyValidation } from "@shared/validations";
|
||||
import User from "./User";
|
||||
@@ -34,6 +35,10 @@ class ApiKey extends ParanoidModel<
|
||||
@Column
|
||||
secret: string;
|
||||
|
||||
@IsDate
|
||||
@Column
|
||||
expiresAt: Date | null;
|
||||
|
||||
// hooks
|
||||
|
||||
@BeforeValidate
|
||||
|
||||
@@ -7,5 +7,6 @@ export default function presentApiKey(key: ApiKey) {
|
||||
secret: key.secret,
|
||||
createdAt: key.createdAt,
|
||||
updatedAt: key.updatedAt,
|
||||
expiresAt: key.expiresAt,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,25 @@ import { getTestServer } from "@server/test/support";
|
||||
const server = getTestServer();
|
||||
|
||||
describe("#apiKeys.create", () => {
|
||||
it("should allow creating an api key", async () => {
|
||||
it("should allow creating an api key with expiry", async () => {
|
||||
const now = new Date();
|
||||
const user = await buildUser();
|
||||
|
||||
const res = await server.post("/api/apiKeys.create", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
name: "My API Key",
|
||||
expiresAt: now.toISOString(),
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.name).toEqual("My API Key");
|
||||
expect(body.data.expiresAt).toEqual(now.toISOString());
|
||||
});
|
||||
|
||||
it("should allow creating an api key without expiry", async () => {
|
||||
const user = await buildUser();
|
||||
|
||||
const res = await server.post("/api/apiKeys.create", {
|
||||
@@ -17,6 +35,7 @@ describe("#apiKeys.create", () => {
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.name).toEqual("My API Key");
|
||||
expect(body.data.expiresAt).toBeNull();
|
||||
});
|
||||
|
||||
it("should require authentication", async () => {
|
||||
@@ -27,10 +46,12 @@ describe("#apiKeys.create", () => {
|
||||
|
||||
describe("#apiKeys.list", () => {
|
||||
it("should return api keys of a user", async () => {
|
||||
const now = new Date();
|
||||
const user = await buildUser();
|
||||
await buildApiKey({
|
||||
name: "My API Key",
|
||||
userId: user.id,
|
||||
expiresAt: now,
|
||||
});
|
||||
|
||||
const res = await server.post("/api/apiKeys.list", {
|
||||
@@ -42,6 +63,7 @@ describe("#apiKeys.list", () => {
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data[0].name).toEqual("My API Key");
|
||||
expect(body.data[0].expiresAt).toEqual(now.toISOString());
|
||||
});
|
||||
|
||||
it("should require authentication", async () => {
|
||||
|
||||
@@ -18,7 +18,7 @@ router.post(
|
||||
validate(T.APIKeysCreateSchema),
|
||||
transaction(),
|
||||
async (ctx: APIContext<T.APIKeysCreateReq>) => {
|
||||
const { name } = ctx.input.body;
|
||||
const { name, expiresAt } = ctx.input.body;
|
||||
const { user } = ctx.state.auth;
|
||||
const { transaction } = ctx.state;
|
||||
|
||||
@@ -27,6 +27,7 @@ router.post(
|
||||
{
|
||||
name,
|
||||
userId: user.id,
|
||||
expiresAt,
|
||||
},
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
@@ -5,6 +5,8 @@ export const APIKeysCreateSchema = BaseSchema.extend({
|
||||
body: z.object({
|
||||
/** API Key name */
|
||||
name: z.string(),
|
||||
/** API Key expiry date */
|
||||
expiresAt: z.coerce.date().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user