diff --git a/packages/transport/src/modules/index.ts b/packages/transport/src/modules/index.ts index 17a5f5d36..ea5556ef3 100644 --- a/packages/transport/src/modules/index.ts +++ b/packages/transport/src/modules/index.ts @@ -103,6 +103,7 @@ export * from "./tokens/backbone/BackboneGetTokens"; export * from "./tokens/backbone/BackbonePostTokens"; export * from "./tokens/backbone/TokenClient"; export * from "./tokens/local/EmptyToken"; +export * from "./tokens/local/SendEmptyTokenParameters"; export * from "./tokens/local/SendTokenParameters"; export * from "./tokens/local/Token"; export * from "./tokens/TokenController"; diff --git a/packages/transport/src/modules/tokens/TokenController.ts b/packages/transport/src/modules/tokens/TokenController.ts index 26dd347a2..29aa473de 100644 --- a/packages/transport/src/modules/tokens/TokenController.ts +++ b/packages/transport/src/modules/tokens/TokenController.ts @@ -1,6 +1,6 @@ import { ISerializable, Serializable } from "@js-soft/ts-serval"; import { log } from "@js-soft/ts-utils"; -import { CoreAddress, CoreDate, CoreId } from "@nmshd/core-types"; +import { CoreAddress, CoreDate, CoreId, Random, RandomCharacterRange } from "@nmshd/core-types"; import { CoreBuffer, CryptoCipher, CryptoSecretKey } from "@nmshd/crypto"; import { CoreCrypto, TransportCoreErrors } from "../../core"; import { DbCollectionName } from "../../core/DbCollectionName"; @@ -10,6 +10,8 @@ import { AccountController } from "../accounts/AccountController"; import { SynchronizedCollection } from "../sync/SynchronizedCollection"; import { BackboneGetTokensResponse } from "./backbone/BackboneGetTokens"; import { TokenClient } from "./backbone/TokenClient"; +import { EmptyToken } from "./local/EmptyToken"; +import { ISendEmptyTokenParameters, SendEmptyTokenParameters } from "./local/SendEmptyTokenParameters"; import { ISendTokenParameters, SendTokenParameters } from "./local/SendTokenParameters"; import { Token } from "./local/Token"; import { IUpdateTokenContentParameters, UpdateTokenContentParameters } from "./local/UpdateTokenContentParameters"; @@ -87,6 +89,27 @@ export class TokenController extends TransportController { return token; } + public async sendEmptyToken(parameters: ISendEmptyTokenParameters): Promise { + const input = SendEmptyTokenParameters.from(parameters); + const secretKey = await CoreCrypto.generateSecretKey(); + + const password = await Random.string(16, RandomCharacterRange.Alphanumeric + RandomCharacterRange.SpecialCharacters); + const salt = await CoreCrypto.random(16); + const hashedPassword = (await CoreCrypto.deriveHashOutOfPassword(password, salt)).toBase64(); + const passwordProtection = PasswordProtection.from({ password, passwordType: "pw", salt }); + + const response = (await this.client.createEmptyToken({ password: hashedPassword, expiresAt: input.expiresAt.toISOString() })).value; + + const token = EmptyToken.from({ + id: CoreId.from(response.id), + secretKey: secretKey, + expiresAt: input.expiresAt, + passwordProtection + }); + + return token; + } + @log() public async setTokenMetadata(idOrToken: CoreId | Token, metadata: ISerializable): Promise { const id = idOrToken instanceof CoreId ? idOrToken.toString() : idOrToken.id.toString(); diff --git a/packages/transport/src/modules/tokens/backbone/TokenClient.ts b/packages/transport/src/modules/tokens/backbone/TokenClient.ts index 11d1b2bc0..77ba189d6 100644 --- a/packages/transport/src/modules/tokens/backbone/TokenClient.ts +++ b/packages/transport/src/modules/tokens/backbone/TokenClient.ts @@ -9,6 +9,10 @@ export class TokenClient extends RESTClientAuthenticate { return await this.post("/api/v2/Tokens", token); } + public async createEmptyToken(request: Omit): Promise> { + return await this.post("/api/v2/Tokens", request); + } + public async updateTokenContent(request: BackboneUpdateTokenContentRequest): Promise> { return await this.post(`/api/v2/Tokens/${request.id}/UpdateContent`, request); } diff --git a/packages/transport/src/modules/tokens/local/SendEmptyTokenParameters.ts b/packages/transport/src/modules/tokens/local/SendEmptyTokenParameters.ts new file mode 100644 index 000000000..282b6d1d2 --- /dev/null +++ b/packages/transport/src/modules/tokens/local/SendEmptyTokenParameters.ts @@ -0,0 +1,17 @@ +import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; +import { CoreDate, ICoreDate } from "@nmshd/core-types"; + +export interface ISendEmptyTokenParameters extends ISerializable { + expiresAt: ICoreDate; +} + +@type("SendEmptyTokenParameters") +export class SendEmptyTokenParameters extends Serializable implements ISendEmptyTokenParameters { + @validate() + @serialize() + public expiresAt: CoreDate; + + public static from(value: ISendEmptyTokenParameters): SendEmptyTokenParameters { + return this.fromAny(value); + } +} diff --git a/packages/transport/src/modules/tokens/local/UpdateTokenContentParameters.ts b/packages/transport/src/modules/tokens/local/UpdateTokenContentParameters.ts index 576ce4d0f..8a4551501 100644 --- a/packages/transport/src/modules/tokens/local/UpdateTokenContentParameters.ts +++ b/packages/transport/src/modules/tokens/local/UpdateTokenContentParameters.ts @@ -9,7 +9,7 @@ export interface IUpdateTokenContentParameters extends ISerializable { passwordProtection: ISharedPasswordProtection; } -@type("SendTokenParameters") +@type("UpdateTokenContentParameters") export class UpdateTokenContentParameters extends Serializable implements IUpdateTokenContentParameters { @validate() @serialize() diff --git a/packages/transport/test/modules/tokens/TokenController.test.ts b/packages/transport/test/modules/tokens/TokenController.test.ts index 82c6886b3..a635cfbbc 100644 --- a/packages/transport/test/modules/tokens/TokenController.test.ts +++ b/packages/transport/test/modules/tokens/TokenController.test.ts @@ -67,6 +67,15 @@ describe("TokenController", function () { expect((receivedToken.content as any).content).toBe((sentToken.content as any).content); }); + test("should send an empty token", async function () { + const expiresAt = CoreDate.utc().add({ hours: 1 }); + const sentToken = await sender.tokens.sendEmptyToken({ + expiresAt + }); + + expect(sentToken).toBeDefined(); + }); + test("should get the stored token", async function () { const sentToken = await sender.tokens.getToken(tempId1); const receivedToken = await recipient.tokens.getToken(tempId1);