From 690e8c2e0989f11b8f270f8612395845c67e5e17 Mon Sep 17 00:00:00 2001 From: Stevche Radevski Date: Tue, 27 Feb 2024 11:37:32 +0100 Subject: [PATCH] feat(api-key): Allow revoking in the future, and enforce the secret key (#6484) Since there is quite a bit of code here already, I'll do the middleware changes in a separate PR --- .../__tests__/api-key/admin/api-key.spec.ts | 25 ++ .../__tests__/api-key-module-service.spec.ts | 69 +++++ .../src/services/api-key-module-service.ts | 265 ++++++++++++------ packages/api-key/src/types/index.ts | 5 +- .../src/api-key/steps/revoke-api-keys.ts | 3 +- .../src/api-key/steps/update-api-keys.ts | 4 +- .../src/api-key/workflows/revoke-api-keys.ts | 2 +- .../src/api-key/workflows/update-api-keys.ts | 2 +- .../admin/api-keys/[id]/revoke/route.ts | 1 + .../src/api-v2/admin/api-keys/[id]/route.ts | 2 +- .../src/api-v2/admin/api-keys/validators.ts | 7 +- .../types/src/api-key/mutations/api-key.ts | 10 +- packages/types/src/api-key/service.ts | 82 ++++-- 13 files changed, 357 insertions(+), 120 deletions(-) diff --git a/integration-tests/plugins/__tests__/api-key/admin/api-key.spec.ts b/integration-tests/plugins/__tests__/api-key/admin/api-key.spec.ts index 7c6c257581..ebc9a315cd 100644 --- a/integration-tests/plugins/__tests__/api-key/admin/api-key.spec.ts +++ b/integration-tests/plugins/__tests__/api-key/admin/api-key.spec.ts @@ -108,4 +108,29 @@ describe("API Keys - Admin", () => { expect(deleted.status).toEqual(200) expect(listedApiKeys.data.apiKeys).toHaveLength(0) }) + + it.skip("can use a secret api key for authentication", async () => { + const api = useApi() as any + const created = await api.post( + `/admin/api-keys`, + { + title: "Test Secret Key", + type: ApiKeyType.SECRET, + }, + adminHeaders + ) + + const createdRegion = await api.post( + `/admin/regions`, + { + name: "Test Region", + currency_code: "usd", + countries: ["us", "ca"], + }, + { headers: { Authorization: `Bearer ${created.token}` } } + ) + + expect(createdRegion.status).toEqual(200) + expect(createdRegion.data.region.name).toEqual("Test Region") + }) }) diff --git a/packages/api-key/integration-tests/__tests__/api-key-module-service.spec.ts b/packages/api-key/integration-tests/__tests__/api-key-module-service.spec.ts index a86002c9a4..d89a2d3783 100644 --- a/packages/api-key/integration-tests/__tests__/api-key-module-service.spec.ts +++ b/packages/api-key/integration-tests/__tests__/api-key-module-service.spec.ts @@ -137,6 +137,27 @@ moduleIntegrationTestRunner({ ) }) + it("should be able to revoke a key in the future", async function () { + const now = Date.parse("2021-01-01T00:00:00Z") + const hourInSec = 3600 + jest.useFakeTimers().setSystemTime(now) + + const createdKey = await service.create(createSecretKeyFixture) + const revokedKey = await service.revoke(createdKey.id, { + revoked_by: "test", + revoke_in: hourInSec, + }) + + expect(revokedKey).toEqual( + expect.objectContaining({ + revoked_by: "test", + revoked_at: new Date(now + hourInSec * 1000), + }) + ) + + jest.useRealTimers() + }) + it("should do nothing if the revokal list is empty", async function () { const firstApiKey = await service.create(createSecretKeyFixture) let revokedKeys = await service.revoke([]) @@ -204,6 +225,54 @@ moduleIntegrationTestRunner({ }) }) + describe("authenticating with API keys", () => { + it("should authenticate a secret key successfully", async function () { + const createdApiKey = await service.create(createSecretKeyFixture) + const authenticated = await service.authenticate(createdApiKey.token) + + expect(authenticated).toBeTruthy() + expect(authenticated.title).toEqual(createSecretKeyFixture.title) + }) + it("should authenticate with a token to be revoked in the future", async function () { + const createdApiKey = await service.create(createSecretKeyFixture) + + // We simulate setting the revoked_at in the future here + jest.useFakeTimers().setSystemTime(new Date().setFullYear(3000)) + await service.revoke(createdApiKey.id, { + revoked_by: "test", + }) + jest.useRealTimers() + + const authenticated = await service.authenticate(createdApiKey.token) + expect(authenticated).toBeTruthy() + expect(authenticated.title).toEqual(createdApiKey.title) + }) + + it("should not authenticate a publishable key", async function () { + const createdApiKey = await service.create( + createPublishableKeyFixture + ) + const authenticated = await service.authenticate(createdApiKey.token) + + expect(authenticated).toBeFalsy() + }) + it("should not authenticate with a non-existent token", async function () { + const createdApiKey = await service.create(createSecretKeyFixture) + const authenticated = await service.authenticate("some-token") + + expect(authenticated).toBeFalsy() + }) + it("should not authenticate with a revoked token", async function () { + const createdApiKey = await service.create(createSecretKeyFixture) + await service.revoke(createdApiKey.id, { + revoked_by: "test", + }) + const authenticated = await service.authenticate(createdApiKey.token) + + expect(authenticated).toBeFalsy() + }) + }) + describe("retrieving API keys", () => { it("should successfully return all existing api keys", async function () { await service.create([ diff --git a/packages/api-key/src/services/api-key-module-service.ts b/packages/api-key/src/services/api-key-module-service.ts index 73f19acd11..9a2123ccb0 100644 --- a/packages/api-key/src/services/api-key-module-service.ts +++ b/packages/api-key/src/services/api-key-module-service.ts @@ -13,7 +13,12 @@ import { } from "@medusajs/types" import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" import { ApiKey } from "@models" -import { CreateApiKeyDTO, TokenDTO } from "@types" +import { + CreateApiKeyDTO, + RevokeApiKeyInput, + TokenDTO, + UpdateApiKeyInput, +} from "@types" import { ApiKeyType, InjectManager, @@ -23,6 +28,7 @@ import { ModulesSdkUtils, isObject, isString, + promiseAll, } from "@medusajs/utils" const scrypt = util.promisify(crypto.scrypt) @@ -132,61 +138,111 @@ export default class ApiKeyModuleService return [createdApiKeys, generatedTokens] } - async update( - selector: FilterableApiKeyProps, - data: Omit, + async upsert( + data: ApiKeyTypes.UpsertApiKeyDTO[], sharedContext?: Context ): Promise + async upsert( + data: ApiKeyTypes.UpsertApiKeyDTO, + sharedContext?: Context + ): Promise + + @InjectManager("baseRepository_") + async upsert( + data: ApiKeyTypes.UpsertApiKeyDTO | ApiKeyTypes.UpsertApiKeyDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + const input = Array.isArray(data) ? data : [data] + const forUpdate = input.filter( + (apiKey): apiKey is UpdateApiKeyInput => !!apiKey.id + ) + const forCreate = input.filter( + (apiKey): apiKey is ApiKeyTypes.CreateApiKeyDTO => !apiKey.id + ) + + const operations: Promise[] = [] + + if (forCreate.length) { + const op = async () => { + const [createdApiKeys, generatedTokens] = await this.create_( + forCreate, + sharedContext + ) + const serializedResponse = await this.baseRepository_.serialize< + ApiKeyTypes.ApiKeyDTO[] + >(createdApiKeys, { + populate: true, + }) + + return serializedResponse.map( + (key) => + ({ + ...key, + token: + generatedTokens.find((t) => t.hashedToken === key.token) + ?.rawToken ?? key.token, + salt: undefined, + } as ApiKeyTypes.ApiKeyDTO) + ) + } + + operations.push(op()) + } + + if (forUpdate.length) { + const op = async () => { + const updateResp = await this.update_(forUpdate, sharedContext) + return await this.baseRepository_.serialize( + updateResp + ) + } + + operations.push(op()) + } + + const result = (await promiseAll(operations)).flat() + return Array.isArray(data) ? result : result[0] + } + async update( id: string, - data: Omit, + data: ApiKeyTypes.UpdateApiKeyDTO, sharedContext?: Context ): Promise async update( - data: ApiKeyTypes.UpdateApiKeyDTO[] + selector: FilterableApiKeyProps, + data: ApiKeyTypes.UpdateApiKeyDTO, + sharedContext?: Context ): Promise + @InjectManager("baseRepository_") async update( - idOrSelectorOrData: - | string - | FilterableApiKeyProps - | ApiKeyTypes.UpdateApiKeyDTO[], - data?: Omit, + idOrSelector: string | FilterableApiKeyProps, + data: ApiKeyTypes.UpdateApiKeyDTO, @MedusaContext() sharedContext: Context = {} ): Promise { - const updatedApiKeys = await this.update_( - idOrSelectorOrData, + let normalizedInput = await this.normalizeUpdateInput_( + idOrSelector, data, sharedContext ) + const updatedApiKeys = await this.update_(normalizedInput, sharedContext) + const serializedResponse = await this.baseRepository_.serialize< ApiKeyTypes.ApiKeyDTO[] >(updatedApiKeys.map(omitToken), { populate: true, }) - return isString(idOrSelectorOrData) - ? serializedResponse[0] - : serializedResponse + return isString(idOrSelector) ? serializedResponse[0] : serializedResponse } @InjectTransactionManager("baseRepository_") protected async update_( - idOrSelectorOrData: - | string - | FilterableApiKeyProps - | ApiKeyTypes.UpdateApiKeyDTO[], - data?: Omit, + normalizedInput: UpdateApiKeyInput[], @MedusaContext() sharedContext: Context = {} ): Promise { - const normalizedInput = - await this.normalizeUpdateInput_( - idOrSelectorOrData, - data, - sharedContext - ) - const updateRequest = normalizedInput.map((k) => ({ id: k.id, title: k.title, @@ -258,33 +314,28 @@ export default class ApiKeyModuleService ] } - async revoke( - selector: FilterableApiKeyProps, - data: Omit, - sharedContext?: Context - ): Promise async revoke( id: string, - data: Omit, + data: ApiKeyTypes.RevokeApiKeyDTO, sharedContext?: Context ): Promise async revoke( - data: ApiKeyTypes.RevokeApiKeyDTO[] + selector: FilterableApiKeyProps, + data: ApiKeyTypes.RevokeApiKeyDTO, + sharedContext?: Context ): Promise @InjectManager("baseRepository_") async revoke( - idOrSelectorOrData: - | string - | FilterableApiKeyProps - | ApiKeyTypes.RevokeApiKeyDTO[], - data?: Omit, + idOrSelector: string | FilterableApiKeyProps, + data: ApiKeyTypes.RevokeApiKeyDTO, @MedusaContext() sharedContext: Context = {} ): Promise { - const revokedApiKeys = await this.revoke_( - idOrSelectorOrData, + const normalizedInput = await this.normalizeUpdateInput_( + idOrSelector, data, sharedContext ) + const revokedApiKeys = await this.revoke_(normalizedInput, sharedContext) const serializedResponse = await this.baseRepository_.serialize< ApiKeyTypes.ApiKeyDTO[] @@ -292,34 +343,28 @@ export default class ApiKeyModuleService populate: true, }) - return isString(idOrSelectorOrData) - ? serializedResponse[0] - : serializedResponse + return isString(idOrSelector) ? serializedResponse[0] : serializedResponse } @InjectTransactionManager("baseRepository_") async revoke_( - idOrSelectorOrData: - | string - | FilterableApiKeyProps - | ApiKeyTypes.RevokeApiKeyDTO[], - data?: Omit, + normalizedInput: RevokeApiKeyInput[], @MedusaContext() sharedContext: Context = {} ): Promise { - const normalizedInput = - await this.normalizeUpdateInput_( - idOrSelectorOrData, - data, - sharedContext - ) - await this.validateRevokeApiKeys_(normalizedInput) - const updateRequest = normalizedInput.map((k) => ({ - id: k.id, - revoked_at: new Date(), - revoked_by: k.revoked_by, - })) + const updateRequest = normalizedInput.map((k) => { + const revokedAt = new Date() + if (k.revoke_in && k.revoke_in > 0) { + revokedAt.setSeconds(revokedAt.getSeconds() + k.revoke_in) + } + + return { + id: k.id, + revoked_at: revokedAt, + revoked_by: k.revoked_by, + } + }) const revokedApiKeys = await this.apiKeyService_.update( updateRequest, @@ -329,13 +374,63 @@ export default class ApiKeyModuleService return revokedApiKeys } - // TODO: Implement - @InjectTransactionManager("baseRepository_") - authenticate( - id: string, + @InjectManager("baseRepository_") + async authenticate( + token: string, @MedusaContext() sharedContext: Context = {} - ): Promise { - return Promise.resolve(false) + ): Promise { + const result = await this.authenticate_(token, sharedContext) + if (!result) { + return false + } + + const serialized = + await this.baseRepository_.serialize(result, { + populate: true, + }) + + return serialized + } + + @InjectTransactionManager("baseRepository_") + protected async authenticate_( + token: string, + @MedusaContext() sharedContext: Context = {} + ): Promise { + // Since we only allow up to 2 active tokens, getitng the list and checking each token isn't an issue. + // We can always filter on the redacted key if we add support for an arbitrary number of tokens. + const secretKeys = await this.apiKeyService_.list( + { + type: ApiKeyType.SECRET, + // If the revoke date is set in the future, it means the key is still valid. + $or: [ + { revoked_at: { $eq: null } }, + { revoked_at: { $gt: new Date() } }, + ], + }, + { take: null }, + sharedContext + ) + + const matches = await promiseAll( + secretKeys.map(async (dbKey) => { + const hashedInput = await ApiKeyModuleService.calculateHash( + token, + dbKey.salt + ) + if (hashedInput === dbKey.token) { + return dbKey + } + + return undefined + }) + ) + + const matchedKeys = matches.filter((match) => !!match) + if (!matchedKeys.length) { + return false + } + return matchedKeys[0]! } protected async validateCreateApiKeys_( @@ -359,9 +454,12 @@ export default class ApiKeyModuleService const dbSecretKeys = await this.apiKeyService_.list( { type: ApiKeyType.SECRET, - revoked_at: null, + $or: [ + { revoked_at: { $eq: null } }, + { revoked_at: { $gt: new Date() } }, + ], }, - {}, + { take: null }, sharedContext ) @@ -374,22 +472,18 @@ export default class ApiKeyModuleService } protected async normalizeUpdateInput_( - idOrSelectorOrData: string | FilterableApiKeyProps | T[], - data?: Omit, + idOrSelector: string | FilterableApiKeyProps, + data: Omit, sharedContext: Context = {} ): Promise { let normalizedInput: T[] = [] - if (isString(idOrSelectorOrData)) { - normalizedInput = [{ id: idOrSelectorOrData, ...data } as T] + if (isString(idOrSelector)) { + normalizedInput = [{ id: idOrSelector, ...data } as T] } - if (Array.isArray(idOrSelectorOrData)) { - normalizedInput = idOrSelectorOrData - } - - if (isObject(idOrSelectorOrData)) { + if (isObject(idOrSelector)) { const apiKeys = await this.apiKeyService_.list( - idOrSelectorOrData, + idOrSelector, {}, sharedContext ) @@ -407,7 +501,7 @@ export default class ApiKeyModuleService } protected async validateRevokeApiKeys_( - data: ApiKeyTypes.RevokeApiKeyDTO[], + data: RevokeApiKeyInput[], sharedContext: Context = {} ): Promise { if (!data.length) { @@ -461,7 +555,7 @@ export default class ApiKeyModuleService protected static async generateSecretKey(): Promise { const token = "sk_" + crypto.randomBytes(32).toString("hex") const salt = crypto.randomBytes(16).toString("hex") - const hashed = ((await scrypt(token, salt, 64)) as Buffer).toString("hex") + const hashed = await this.calculateHash(token, salt) return { rawToken: token, @@ -470,6 +564,13 @@ export default class ApiKeyModuleService redacted: redactKey(token), } } + + protected static async calculateHash( + token: string, + salt: string + ): Promise { + return ((await scrypt(token, salt, 64)) as Buffer).toString("hex") + } } // We are mutating the object here as what microORM relies on non-enumerable fields for serialization, among other things. diff --git a/packages/api-key/src/types/index.ts b/packages/api-key/src/types/index.ts index 2655904953..f3b2746996 100644 --- a/packages/api-key/src/types/index.ts +++ b/packages/api-key/src/types/index.ts @@ -1,4 +1,4 @@ -import { ApiKeyType } from "@medusajs/types" +import { ApiKeyType, RevokeApiKeyDTO, UpdateApiKeyDTO } from "@medusajs/types" import { IEventBusModuleService, Logger } from "@medusajs/types" export type InitializeModuleInjectableDependencies = { @@ -21,3 +21,6 @@ export type TokenDTO = { salt: string redacted: string } + +export type UpdateApiKeyInput = UpdateApiKeyDTO & { id: string } +export type RevokeApiKeyInput = RevokeApiKeyDTO & { id: string } diff --git a/packages/core-flows/src/api-key/steps/revoke-api-keys.ts b/packages/core-flows/src/api-key/steps/revoke-api-keys.ts index a234b7e522..6917eed6c5 100644 --- a/packages/core-flows/src/api-key/steps/revoke-api-keys.ts +++ b/packages/core-flows/src/api-key/steps/revoke-api-keys.ts @@ -4,12 +4,11 @@ import { IApiKeyModuleService, RevokeApiKeyDTO, } from "@medusajs/types" -import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" type RevokeApiKeysStepInput = { selector: FilterableApiKeyProps - revoke: Omit + revoke: RevokeApiKeyDTO } export const revokeApiKeysStepId = "revoke-api-keys" diff --git a/packages/core-flows/src/api-key/steps/update-api-keys.ts b/packages/core-flows/src/api-key/steps/update-api-keys.ts index 7394a27f32..6b5f7227ba 100644 --- a/packages/core-flows/src/api-key/steps/update-api-keys.ts +++ b/packages/core-flows/src/api-key/steps/update-api-keys.ts @@ -9,7 +9,7 @@ import { StepResponse, createStep } from "@medusajs/workflows-sdk" type UpdateApiKeysStepInput = { selector: FilterableApiKeyProps - update: Omit + update: UpdateApiKeyDTO } export const updateApiKeysStepId = "update-api-keys" @@ -41,7 +41,7 @@ export const updateApiKeysStep = createStep( ModuleRegistrationName.API_KEY ) - await service.update( + await service.upsert( prevData.map((r) => ({ id: r.id, title: r.title, diff --git a/packages/core-flows/src/api-key/workflows/revoke-api-keys.ts b/packages/core-flows/src/api-key/workflows/revoke-api-keys.ts index 6c1d4be0da..79baf41992 100644 --- a/packages/core-flows/src/api-key/workflows/revoke-api-keys.ts +++ b/packages/core-flows/src/api-key/workflows/revoke-api-keys.ts @@ -8,7 +8,7 @@ import { revokeApiKeysStep } from "../steps" type RevokeApiKeysStepInput = { selector: FilterableApiKeyProps - revoke: Omit + revoke: RevokeApiKeyDTO } type WorkflowInput = RevokeApiKeysStepInput diff --git a/packages/core-flows/src/api-key/workflows/update-api-keys.ts b/packages/core-flows/src/api-key/workflows/update-api-keys.ts index 452658a7e6..9f42051c4e 100644 --- a/packages/core-flows/src/api-key/workflows/update-api-keys.ts +++ b/packages/core-flows/src/api-key/workflows/update-api-keys.ts @@ -8,7 +8,7 @@ import { updateApiKeysStep } from "../steps" type UpdateApiKeysStepInput = { selector: FilterableApiKeyProps - update: Omit + update: UpdateApiKeyDTO } type WorkflowInput = UpdateApiKeysStepInput diff --git a/packages/medusa/src/api-v2/admin/api-keys/[id]/revoke/route.ts b/packages/medusa/src/api-v2/admin/api-keys/[id]/revoke/route.ts index 2fda8cf08b..d572e8d826 100644 --- a/packages/medusa/src/api-v2/admin/api-keys/[id]/revoke/route.ts +++ b/packages/medusa/src/api-v2/admin/api-keys/[id]/revoke/route.ts @@ -16,6 +16,7 @@ export const POST = async ( input: { selector: { id: req.params.id }, revoke: { + ...(req.validatedBody as Omit), revoked_by: req.auth.actor_id, } as RevokeApiKeyDTO, }, diff --git a/packages/medusa/src/api-v2/admin/api-keys/[id]/route.ts b/packages/medusa/src/api-v2/admin/api-keys/[id]/route.ts index 16283aed1a..46eb8644da 100644 --- a/packages/medusa/src/api-v2/admin/api-keys/[id]/route.ts +++ b/packages/medusa/src/api-v2/admin/api-keys/[id]/route.ts @@ -37,7 +37,7 @@ export const POST = async ( const { result, errors } = await updateApiKeysWorkflow(req.scope).run({ input: { selector: { id: req.params.id }, - update: req.validatedBody, + update: req.validatedBody as UpdateApiKeyDTO, }, throwOnError: false, }) diff --git a/packages/medusa/src/api-v2/admin/api-keys/validators.ts b/packages/medusa/src/api-v2/admin/api-keys/validators.ts index 7e54b6aa21..a2381d3ddb 100644 --- a/packages/medusa/src/api-v2/admin/api-keys/validators.ts +++ b/packages/medusa/src/api-v2/admin/api-keys/validators.ts @@ -3,6 +3,7 @@ import { Type } from "class-transformer" import { IsArray, IsEnum, + IsNumber, IsOptional, IsString, ValidateNested, @@ -65,6 +66,10 @@ export class AdminPostApiKeysApiKeyReq { title: string } -export class AdminRevokeApiKeysApiKeyReq {} +export class AdminRevokeApiKeysApiKeyReq { + @IsOptional() + @IsNumber() + revoke_in?: number +} export class AdminDeleteApiKeysApiKeyReq {} diff --git a/packages/types/src/api-key/mutations/api-key.ts b/packages/types/src/api-key/mutations/api-key.ts index aece40bf26..168f9bf166 100644 --- a/packages/types/src/api-key/mutations/api-key.ts +++ b/packages/types/src/api-key/mutations/api-key.ts @@ -7,12 +7,18 @@ export interface CreateApiKeyDTO { // We could add revoked_at as a parameter (or expires_at that gets mapped to revoked_at internally) in order to support expiring tokens } +export interface UpsertApiKeyDTO { + id?: string + title?: string + type?: ApiKeyType + created_by?: string +} + export interface UpdateApiKeyDTO { - id: string title?: string } export interface RevokeApiKeyDTO { - id: string revoked_by: string + revoke_in?: number // Seconds after which the token should be considered revoked } diff --git a/packages/types/src/api-key/service.ts b/packages/types/src/api-key/service.ts index 0b4c9fe992..ee9df48df8 100644 --- a/packages/types/src/api-key/service.ts +++ b/packages/types/src/api-key/service.ts @@ -2,7 +2,12 @@ import { IModuleService } from "../modules-sdk" import { ApiKeyDTO, FilterableApiKeyProps } from "./common" import { FindConfig } from "../common" import { Context } from "../shared-context" -import { CreateApiKeyDTO, RevokeApiKeyDTO, UpdateApiKeyDTO } from "./mutations" +import { + CreateApiKeyDTO, + RevokeApiKeyDTO, + UpdateApiKeyDTO, + UpsertApiKeyDTO, +} from "./mutations" export interface IApiKeyModuleService extends IModuleService { /** @@ -14,32 +19,57 @@ export interface IApiKeyModuleService extends IModuleService { create(data: CreateApiKeyDTO, sharedContext?: Context): Promise /** - * Update an api key - * @param selector - * @param data - * @param sharedContext + * This method updates existing API keys, or creates new ones if they don't exist. + * + * @param {UpsertApiKeyDTO[]} data - The attributes to update or create for each API key. + * @returns {Promise} The updated and created API keys. + * + * @example + * {example-code} */ - update( - selector: FilterableApiKeyProps, - data: Omit, - sharedContext?: Context - ): Promise + upsert(data: UpsertApiKeyDTO[], sharedContext?: Context): Promise + /** - * Update an api key - * @param id - * @param data - * @param sharedContext + * This method updates an existing API key, or creates a new one if it doesn't exist. + * + * @param {UpsertApiKeyDTO} data - The attributes to update or create for the API key. + * @returns {Promise} The updated or created API key. + * + * @example + * {example-code} + */ + upsert(data: UpsertApiKeyDTO, sharedContext?: Context): Promise + + /** + * This method updates an existing API key. + * + * @param {string} id - The API key's ID. + * @param {UpdateApiKeyDTO} data - The details to update in the API key. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The updated API key. */ update( id: string, - data: Omit, + data: UpdateApiKeyDTO, sharedContext?: Context ): Promise + /** - * Update an api key - * @param data + * This method updates existing API keys. + * + * @param {FilterableApiKeyProps} selector - The filters to specify which API keys should be updated. + * @param {UpdateApiKeyDTO} data - The details to update in the API keys. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The updated API keys. + * + * @example + * {example-code} */ - update(data: UpdateApiKeyDTO[]): Promise + update( + selector: FilterableApiKeyProps, + data: UpdateApiKeyDTO, + sharedContext?: Context + ): Promise /** * Delete an api key @@ -93,7 +123,7 @@ export interface IApiKeyModuleService extends IModuleService { */ revoke( selector: FilterableApiKeyProps, - data: Omit, + data: RevokeApiKeyDTO, sharedContext?: Context ): Promise /** @@ -104,19 +134,17 @@ export interface IApiKeyModuleService extends IModuleService { */ revoke( id: string, - data: Omit, + data: RevokeApiKeyDTO, sharedContext?: Context ): Promise - /** - * Revokes an api key - * @param data - */ - revoke(data: RevokeApiKeyDTO[]): Promise /** * Check the validity of an api key - * @param id + * @param token * @param sharedContext */ - authenticate(id: string, sharedContext?: Context): Promise + authenticate( + token: string, + sharedContext?: Context + ): Promise }