Compare commits

...

9 Commits

Author SHA1 Message Date
standardci
6dfb2be4a2 chore(release): publish new version
- @standardnotes/auth-server@1.73.0
2022-12-28 13:09:57 +00:00
Karol Sójko
d81cbad550 Merge pull request #381 from standardnotes/authenticator_registration
feat(auth): add generating authencator registration options
2022-12-28 14:08:02 +01:00
Karol Sójko
51ad06b303 feat(auth): add generating authencator registration options 2022-12-28 13:56:06 +01:00
standardci
27048ad95c chore(release): publish new version
- @standardnotes/auth-server@1.72.0
2022-12-28 11:42:03 +00:00
Karol Sójko
fa9bf0b448 feat(auth): add authenticator challenges model 2022-12-28 12:40:13 +01:00
standardci
305190b64e chore(release): publish new version
- @standardnotes/auth-server@1.71.1
2022-12-28 11:27:35 +00:00
Karol Sójko
98e3d18335 fix(auth): credential id field type 2022-12-28 12:25:36 +01:00
standardci
72e398956b chore(release): publish new version
- @standardnotes/auth-server@1.71.0
2022-12-28 10:39:38 +00:00
Karol Sójko
1e69a13a97 feat(auth): add authenticators model 2022-12-28 11:37:06 +01:00
51 changed files with 1123 additions and 3 deletions

259
.pnp.cjs generated
View File

@@ -1968,6 +1968,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["@noble/ed25519", [\
["npm:1.7.1", {\
"packageLocation": "./.yarn/cache/@noble-ed25519-npm-1.7.1-177d9beb01-b1aa4b9264.zip/node_modules/@noble/ed25519/",\
"packageDependencies": [\
["@noble/ed25519", "npm:1.7.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["@nodelib/fs.scandir", [\
["npm:2.1.5", {\
"packageLocation": "./.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-5f309a3b37.zip/node_modules/@nodelib/fs.scandir/",\
@@ -2324,6 +2333,44 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["@peculiar/asn1-android", [\
["npm:2.3.3", {\
"packageLocation": "./.yarn/cache/@peculiar-asn1-android-npm-2.3.3-28df67d7a3-0c7cad544e.zip/node_modules/@peculiar/asn1-android/",\
"packageDependencies": [\
["@peculiar/asn1-android", "npm:2.3.3"],\
["@peculiar/asn1-schema", "npm:2.3.3"],\
["asn1js", "npm:3.0.5"],\
["tslib", "npm:2.4.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["@peculiar/asn1-schema", [\
["npm:2.3.3", {\
"packageLocation": "./.yarn/cache/@peculiar-asn1-schema-npm-2.3.3-7c2b9469c4-f584f79d5a.zip/node_modules/@peculiar/asn1-schema/",\
"packageDependencies": [\
["@peculiar/asn1-schema", "npm:2.3.3"],\
["asn1js", "npm:3.0.5"],\
["pvtsutils", "npm:1.3.2"],\
["tslib", "npm:2.4.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["@peculiar/asn1-x509", [\
["npm:2.3.4", {\
"packageLocation": "./.yarn/cache/@peculiar-asn1-x509-npm-2.3.4-a579005836-10a8659980.zip/node_modules/@peculiar/asn1-x509/",\
"packageDependencies": [\
["@peculiar/asn1-x509", "npm:2.3.4"],\
["@peculiar/asn1-schema", "npm:2.3.3"],\
["asn1js", "npm:3.0.5"],\
["ipaddr.js", "npm:2.0.1"],\
["pvtsutils", "npm:1.3.2"],\
["tslib", "npm:2.4.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["@pnpm/network.ca-file", [\
["npm:1.0.1", {\
"packageLocation": "./.yarn/cache/@pnpm-network.ca-file-npm-1.0.1-42bfe40bec-ed952a5574.zip/node_modules/@pnpm/network.ca-file/",\
@@ -2579,6 +2626,25 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["@simplewebauthn/server", [\
["npm:6.2.2", {\
"packageLocation": "./.yarn/cache/@simplewebauthn-server-npm-6.2.2-ca870b05c2-5ffb9b1c15.zip/node_modules/@simplewebauthn/server/",\
"packageDependencies": [\
["@simplewebauthn/server", "npm:6.2.2"],\
["@noble/ed25519", "npm:1.7.1"],\
["@peculiar/asn1-android", "npm:2.3.3"],\
["@peculiar/asn1-schema", "npm:2.3.3"],\
["@peculiar/asn1-x509", "npm:2.3.4"],\
["base64url", "npm:3.0.1"],\
["cbor", "npm:5.2.0"],\
["debug", "virtual:b86a9fb34323a98c6519528ed55faa0d9b44ca8879307c0b29aa384bde47ff59a7d0c9051b31246f14521dfb71ba3c5d6d0b35c29fffc17bf875aa6ad977d9e8#npm:4.3.4"],\
["jsrsasign", "npm:10.6.1"],\
["jwk-to-pem", "npm:2.0.5"],\
["node-fetch", "virtual:25a5f5382d53dbf298bf7a1191760bc2e0a523a619eeb0e667b99a8649e8ad183f9e2e0b45f6fb831b92f4078b61622aa567cf79565f6aa5af9597e3c84864f6#npm:2.6.7"]\
],\
"linkType": "HARD"\
}]\
]],\
["@sinclair/typebox", [\
["npm:0.24.44", {\
"packageLocation": "./.yarn/cache/@sinclair-typebox-npm-0.24.44-38506ddef6-f37b9d28bf.zip/node_modules/@sinclair/typebox/",\
@@ -2743,6 +2809,7 @@ const RAW_RUNTIME_STATE =
["@sentry/node", "npm:7.28.1"],\
["@sentry/profiling-node", "npm:0.0.12"],\
["@sentry/tracing", "npm:7.28.1"],\
["@simplewebauthn/server", "npm:6.2.2"],\
["@standardnotes/api", "npm:1.19.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
@@ -4869,6 +4936,31 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["asn1.js", [\
["npm:5.4.1", {\
"packageLocation": "./.yarn/cache/asn1.js-npm-5.4.1-37c7edbcb0-5c36f81388.zip/node_modules/asn1.js/",\
"packageDependencies": [\
["asn1.js", "npm:5.4.1"],\
["bn.js", "npm:4.12.0"],\
["inherits", "npm:2.0.4"],\
["minimalistic-assert", "npm:1.0.1"],\
["safer-buffer", "npm:2.1.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["asn1js", [\
["npm:3.0.5", {\
"packageLocation": "./.yarn/cache/asn1js-npm-3.0.5-cf5558af33-d0bc57da97.zip/node_modules/asn1js/",\
"packageDependencies": [\
["asn1js", "npm:3.0.5"],\
["pvtsutils", "npm:1.3.2"],\
["pvutils", "npm:1.1.3"],\
["tslib", "npm:2.4.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["async", [\
["npm:3.2.4", {\
"packageLocation": "./.yarn/cache/async-npm-3.2.4-aba13508f9-9719e38d24.zip/node_modules/async/",\
@@ -5082,6 +5174,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["base64url", [\
["npm:3.0.1", {\
"packageLocation": "./.yarn/cache/base64url-npm-3.0.1-4c171c4917-72e1401ffe.zip/node_modules/base64url/",\
"packageDependencies": [\
["base64url", "npm:3.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["bcryptjs", [\
["npm:2.4.3", {\
"packageLocation": "./.yarn/cache/bcryptjs-npm-2.4.3-32de4957eb-bf6a43e9c4.zip/node_modules/bcryptjs/",\
@@ -5100,6 +5201,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["bignumber.js", [\
["npm:9.1.1", {\
"packageLocation": "./.yarn/cache/bignumber.js-npm-9.1.1-5929e8d8dc-e44d008049.zip/node_modules/bignumber.js/",\
"packageDependencies": [\
["bignumber.js", "npm:9.1.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["binary-extensions", [\
["npm:2.2.0", {\
"packageLocation": "./.yarn/cache/binary-extensions-npm-2.2.0-180c33fec7-16cf7c0cfd.zip/node_modules/binary-extensions/",\
@@ -5121,6 +5231,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["bn.js", [\
["npm:4.12.0", {\
"packageLocation": "./.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-bfb4590775.zip/node_modules/bn.js/",\
"packageDependencies": [\
["bn.js", "npm:4.12.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["body-parser", [\
["npm:1.20.1", {\
"packageLocation": "./.yarn/cache/body-parser-npm-1.20.1-759fd14db9-33f202c9d5.zip/node_modules/body-parser/",\
@@ -5188,6 +5307,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["brorand", [\
["npm:1.1.0", {\
"packageLocation": "./.yarn/cache/brorand-npm-1.1.0-ea86634c4b-f736e127fb.zip/node_modules/brorand/",\
"packageDependencies": [\
["brorand", "npm:1.1.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["browserslist", [\
["npm:4.21.1", {\
"packageLocation": "./.yarn/cache/browserslist-npm-4.21.1-930e90b93a-617d624493.zip/node_modules/browserslist/",\
@@ -5423,6 +5551,17 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["cbor", [\
["npm:5.2.0", {\
"packageLocation": "./.yarn/cache/cbor-npm-5.2.0-4f6440587f-d60986b9d0.zip/node_modules/cbor/",\
"packageDependencies": [\
["cbor", "npm:5.2.0"],\
["bignumber.js", "npm:9.1.1"],\
["nofilter", "npm:1.0.4"]\
],\
"linkType": "HARD"\
}]\
]],\
["chalk", [\
["npm:2.4.2", {\
"packageLocation": "./.yarn/cache/chalk-npm-2.4.2-3ea16dd91e-befd2fe888.zip/node_modules/chalk/",\
@@ -6547,6 +6686,22 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["elliptic", [\
["npm:6.5.4", {\
"packageLocation": "./.yarn/cache/elliptic-npm-6.5.4-0ca8204a86-4453b008cf.zip/node_modules/elliptic/",\
"packageDependencies": [\
["elliptic", "npm:6.5.4"],\
["bn.js", "npm:4.12.0"],\
["brorand", "npm:1.1.0"],\
["hash.js", "npm:1.1.7"],\
["hmac-drbg", "npm:1.0.1"],\
["inherits", "npm:2.0.4"],\
["minimalistic-assert", "npm:1.0.1"],\
["minimalistic-crypto-utils", "npm:1.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["emittery", [\
["npm:0.10.2", {\
"packageLocation": "./.yarn/cache/emittery-npm-0.10.2-aac10498b5-c55b286714.zip/node_modules/emittery/",\
@@ -8053,6 +8208,17 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["hash.js", [\
["npm:1.1.7", {\
"packageLocation": "./.yarn/cache/hash.js-npm-1.1.7-f1ad187358-e4266370d1.zip/node_modules/hash.js/",\
"packageDependencies": [\
["hash.js", "npm:1.1.7"],\
["inherits", "npm:2.0.4"],\
["minimalistic-assert", "npm:1.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["helmet", [\
["npm:6.0.0", {\
"packageLocation": "./.yarn/cache/helmet-npm-6.0.0-2285459f57-73b6ba802d.zip/node_modules/helmet/",\
@@ -8071,6 +8237,18 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["hmac-drbg", [\
["npm:1.0.1", {\
"packageLocation": "./.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-4e88d58ffc.zip/node_modules/hmac-drbg/",\
"packageDependencies": [\
["hmac-drbg", "npm:1.0.1"],\
["hash.js", "npm:1.1.7"],\
["minimalistic-assert", "npm:1.0.1"],\
["minimalistic-crypto-utils", "npm:1.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["hosted-git-info", [\
["npm:2.8.9", {\
"packageLocation": "./.yarn/cache/hosted-git-info-npm-2.8.9-62c44fa93f-c24da52f98.zip/node_modules/hosted-git-info/",\
@@ -8451,6 +8629,13 @@ const RAW_RUNTIME_STATE =
["ipaddr.js", "npm:1.9.1"]\
],\
"linkType": "HARD"\
}],\
["npm:2.0.1", {\
"packageLocation": "./.yarn/cache/ipaddr.js-npm-2.0.1-04e97280d7-04ce6c896c.zip/node_modules/ipaddr.js/",\
"packageDependencies": [\
["ipaddr.js", "npm:2.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["is-arguments", [\
@@ -9653,6 +9838,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["jsrsasign", [\
["npm:10.6.1", {\
"packageLocation": "./.yarn/cache/jsrsasign-npm-10.6.1-a8fa295369-e8e9c1b24f.zip/node_modules/jsrsasign/",\
"packageDependencies": [\
["jsrsasign", "npm:10.6.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["jwa", [\
["npm:1.4.1", {\
"packageLocation": "./.yarn/cache/jwa-npm-1.4.1-4f19d6572c-0cc3e68b68.zip/node_modules/jwa/",\
@@ -9665,6 +9859,18 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["jwk-to-pem", [\
["npm:2.0.5", {\
"packageLocation": "./.yarn/cache/jwk-to-pem-npm-2.0.5-aff7d9f125-fced3a75b0.zip/node_modules/jwk-to-pem/",\
"packageDependencies": [\
["jwk-to-pem", "npm:2.0.5"],\
["asn1.js", "npm:5.4.1"],\
["elliptic", "npm:6.5.4"],\
["safe-buffer", "npm:5.2.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["jws", [\
["npm:3.2.2", {\
"packageLocation": "./.yarn/cache/jws-npm-3.2.2-c1ae59c7af-347ed7c334.zip/node_modules/jws/",\
@@ -10278,6 +10484,24 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["minimalistic-assert", [\
["npm:1.0.1", {\
"packageLocation": "./.yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-e2310081d8.zip/node_modules/minimalistic-assert/",\
"packageDependencies": [\
["minimalistic-assert", "npm:1.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["minimalistic-crypto-utils", [\
["npm:1.0.1", {\
"packageLocation": "./.yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-7d909decd2.zip/node_modules/minimalistic-crypto-utils/",\
"packageDependencies": [\
["minimalistic-crypto-utils", "npm:1.0.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["minimatch", [\
["npm:3.1.2", {\
"packageLocation": "./.yarn/cache/minimatch-npm-3.1.2-9405269906-97f5615ee8.zip/node_modules/minimatch/",\
@@ -10697,6 +10921,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["nofilter", [\
["npm:1.0.4", {\
"packageLocation": "./.yarn/cache/nofilter-npm-1.0.4-1cbdc6c03a-9a26874e7d.zip/node_modules/nofilter/",\
"packageDependencies": [\
["nofilter", "npm:1.0.4"]\
],\
"linkType": "HARD"\
}]\
]],\
["nopt", [\
["npm:1.0.10", {\
"packageLocation": "./.yarn/cache/nopt-npm-1.0.10-f3db192976-efa5a9c2c1.zip/node_modules/nopt/",\
@@ -11753,6 +11986,25 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["pvtsutils", [\
["npm:1.3.2", {\
"packageLocation": "./.yarn/cache/pvtsutils-npm-1.3.2-e1483da905-eb22d3df60.zip/node_modules/pvtsutils/",\
"packageDependencies": [\
["pvtsutils", "npm:1.3.2"],\
["tslib", "npm:2.4.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["pvutils", [\
["npm:1.1.3", {\
"packageLocation": "./.yarn/cache/pvutils-npm-1.1.3-da8b07d6cf-0cb4f4878f.zip/node_modules/pvutils/",\
"packageDependencies": [\
["pvutils", "npm:1.1.3"]\
],\
"linkType": "HARD"\
}]\
]],\
["q", [\
["npm:1.5.1", {\
"packageLocation": "./.yarn/cache/q-npm-1.5.1-a28b3cfeaf-276b7e93fc.zip/node_modules/q/",\
@@ -13437,6 +13689,13 @@ const RAW_RUNTIME_STATE =
["tslib", "npm:2.4.0"]\
],\
"linkType": "HARD"\
}],\
["npm:2.4.1", {\
"packageLocation": "./.yarn/cache/tslib-npm-2.4.1-36f0ed04db-a739a21e3f.zip/node_modules/tslib/",\
"packageDependencies": [\
["tslib", "npm:2.4.1"]\
],\
"linkType": "HARD"\
}]\
]],\
["tsutils", [\

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.73.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.72.0...@standardnotes/auth-server@1.73.0) (2022-12-28)
### Features
* **auth:** add generating authencator registration options ([51ad06b](https://github.com/standardnotes/server/commit/51ad06b303d7dc994920818872fdf8bd37fc445c))
# [1.72.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.71.1...@standardnotes/auth-server@1.72.0) (2022-12-28)
### Features
* **auth:** add authenticator challenges model ([fa9bf0b](https://github.com/standardnotes/server/commit/fa9bf0b448acb3f19ab44c4b431ce367dab37b76))
## [1.71.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.71.0...@standardnotes/auth-server@1.71.1) (2022-12-28)
### Bug Fixes
* **auth:** credential id field type ([98e3d18](https://github.com/standardnotes/server/commit/98e3d1833530dcd9e3e34a4c4a6b14a2a01afea1))
# [1.71.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.70.9...@standardnotes/auth-server@1.71.0) (2022-12-28)
### Features
* **auth:** add authenticators model ([1e69a13](https://github.com/standardnotes/server/commit/1e69a13a97c4d9022aa96397cce1b349d3cede89))
## [1.70.9](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.70.8...@standardnotes/auth-server@1.70.9) (2022-12-28)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -0,0 +1,16 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class addAuthenticators1672223738686 implements MigrationInterface {
name = 'addAuthenticators1672223738686'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
'CREATE TABLE `authenticators` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `credential_id` varbinary(1024) NOT NULL, `credential_public_key` blob NOT NULL, `counter` bigint NOT NULL, `credential_device_type` varchar(32) NOT NULL, `credential_backed_up` tinyint NOT NULL, `transports` varchar(255) NULL, `created_at` bigint NOT NULL, `updated_at` bigint NOT NULL, UNIQUE INDEX `credential_id` (`credential_id`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP INDEX `credential_id` ON `authenticators`')
await queryRunner.query('DROP TABLE `authenticators`')
}
}

View File

@@ -0,0 +1,16 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class addAuthenticatorChallenges1672227471677 implements MigrationInterface {
name = 'addAuthenticatorChallenges1672227471677'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
'CREATE TABLE `authenticator_challenges` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `challenge` varchar(255) NOT NULL, `created_at` bigint NOT NULL, INDEX `user_uuid_and_challenge` (`user_uuid`, `challenge`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP INDEX `user_uuid_and_challenge` ON `authenticator_challenges`')
await queryRunner.query('DROP TABLE `authenticator_challenges`')
}
}

View File

@@ -0,0 +1,23 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class fixAuthenticatorDataTypes1672232035280 implements MigrationInterface {
name = 'fixAuthenticatorDataTypes1672232035280'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `authenticators` DROP COLUMN `created_at`')
await queryRunner.query('ALTER TABLE `authenticators` ADD `created_at` datetime NOT NULL')
await queryRunner.query('ALTER TABLE `authenticators` DROP COLUMN `updated_at`')
await queryRunner.query('ALTER TABLE `authenticators` ADD `updated_at` datetime NOT NULL')
await queryRunner.query('ALTER TABLE `authenticator_challenges` DROP COLUMN `created_at`')
await queryRunner.query('ALTER TABLE `authenticator_challenges` ADD `created_at` datetime NOT NULL')
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `authenticator_challenges` DROP COLUMN `created_at`')
await queryRunner.query('ALTER TABLE `authenticator_challenges` ADD `created_at` bigint NOT NULL')
await queryRunner.query('ALTER TABLE `authenticators` DROP COLUMN `updated_at`')
await queryRunner.query('ALTER TABLE `authenticators` ADD `updated_at` bigint NOT NULL')
await queryRunner.query('ALTER TABLE `authenticators` DROP COLUMN `created_at`')
await queryRunner.query('ALTER TABLE `authenticators` ADD `created_at` bigint NOT NULL')
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.70.9",
"version": "1.73.0",
"engines": {
"node": ">=18.0.0 <19.0.0"
},
@@ -36,6 +36,7 @@
"@sentry/node": "^7.28.1",
"@sentry/profiling-node": "^0.0.12",
"@sentry/tracing": "^7.28.1",
"@simplewebauthn/server": "^6.2.2",
"@standardnotes/api": "^1.19.0",
"@standardnotes/common": "workspace:*",
"@standardnotes/domain-core": "workspace:^",

View File

@@ -203,6 +203,17 @@ import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
import { TraceSession } from '../Domain/UseCase/TraceSession/TraceSession'
import { CleanupSessionTraces } from '../Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces'
import { PersistStatistics } from '../Domain/UseCase/PersistStatistics/PersistStatistics'
import { TypeORMAuthenticator } from '../Infra/TypeORM/TypeORMAuthenticator'
import { Authenticator } from '../Domain/Authenticator/Authenticator'
import { AuthenticatorPersistenceMapper } from '../Mapping/AuthenticatorPersistenceMapper'
import { AuthenticatorChallenge } from '../Domain/Authenticator/AuthenticatorChallenge'
import { TypeORMAuthenticatorChallenge } from '../Infra/TypeORM/TypeORMAuthenticatorChallenge'
import { AuthenticatorChallengePersistenceMapper } from '../Mapping/AuthenticatorChallengePersistenceMapper'
import { AuthenticatorRepositoryInterface } from '../Domain/Authenticator/AuthenticatorRepositoryInterface'
import { MySQLAuthenticatorRepository } from '../Infra/MySQL/MySQLAuthenticatorRepository'
import { AuthenticatorChallengeRepositoryInterface } from '../Domain/Authenticator/AuthenticatorChallengeRepositoryInterface'
import { MySQLAuthenticatorChallengeRepository } from '../Infra/MySQL/MySQLAuthenticatorChallengeRepository'
import { GenerateAuthenticatorRegistrationOptions } from '../Domain/UseCase/GenerateAuthenticatorRegistrationOptions/GenerateAuthenticatorRegistrationOptions'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicFormatter = require('@newrelic/winston-enricher')
@@ -280,6 +291,14 @@ export class ContainerConfigLoader {
container
.bind<MapperInterface<SessionTrace, TypeORMSessionTrace>>(TYPES.SessionTracePersistenceMapper)
.toConstantValue(new SessionTracePersistenceMapper())
container
.bind<MapperInterface<Authenticator, TypeORMAuthenticator>>(TYPES.AuthenticatorPersistenceMapper)
.toConstantValue(new AuthenticatorPersistenceMapper())
container
.bind<MapperInterface<AuthenticatorChallenge, TypeORMAuthenticatorChallenge>>(
TYPES.AuthenticatorChallengePersistenceMapper,
)
.toConstantValue(new AuthenticatorChallengePersistenceMapper())
// Controller
container.bind<AuthController>(TYPES.AuthController).to(AuthController)
@@ -316,6 +335,12 @@ export class ContainerConfigLoader {
container
.bind<Repository<TypeORMSessionTrace>>(TYPES.ORMSessionTraceRepository)
.toConstantValue(AppDataSource.getRepository(TypeORMSessionTrace))
container
.bind<Repository<TypeORMAuthenticator>>(TYPES.ORMAuthenticatorRepository)
.toConstantValue(AppDataSource.getRepository(TypeORMAuthenticator))
container
.bind<Repository<TypeORMAuthenticatorChallenge>>(TYPES.ORMAuthenticatorChallengeRepository)
.toConstantValue(AppDataSource.getRepository(TypeORMAuthenticatorChallenge))
// Repositories
container.bind<SessionRepositoryInterface>(TYPES.SessionRepository).to(MySQLSessionRepository)
@@ -355,6 +380,22 @@ export class ContainerConfigLoader {
container.get(TYPES.SessionTracePersistenceMapper),
),
)
container
.bind<AuthenticatorRepositoryInterface>(TYPES.AuthenticatorRepository)
.toConstantValue(
new MySQLAuthenticatorRepository(
container.get(TYPES.ORMAuthenticatorRepository),
container.get(TYPES.AuthenticatorPersistenceMapper),
),
)
container
.bind<AuthenticatorChallengeRepositoryInterface>(TYPES.AuthenticatorChallengeRepository)
.toConstantValue(
new MySQLAuthenticatorChallengeRepository(
container.get(TYPES.ORMAuthenticatorChallengeRepository),
container.get(TYPES.AuthenticatorChallengePersistenceMapper),
),
)
// Middleware
container.bind<AuthMiddleware>(TYPES.AuthMiddleware).to(AuthMiddleware)
@@ -511,6 +552,14 @@ export class ContainerConfigLoader {
container.get(TYPES.Timer),
),
)
container
.bind<GenerateAuthenticatorRegistrationOptions>(TYPES.GenerateAuthenticatorRegistrationOptions)
.toConstantValue(
new GenerateAuthenticatorRegistrationOptions(
container.get(TYPES.AuthenticatorRepository),
container.get(TYPES.AuthenticatorChallengeRepository),
),
)
container
.bind<CleanupSessionTraces>(TYPES.CleanupSessionTraces)

View File

@@ -10,6 +10,8 @@ import { SharedSubscriptionInvitation } from '../Domain/SharedSubscription/Share
import { OfflineUserSubscription } from '../Domain/Subscription/OfflineUserSubscription'
import { UserSubscription } from '../Domain/Subscription/UserSubscription'
import { User } from '../Domain/User/User'
import { TypeORMAuthenticator } from '../Infra/TypeORM/TypeORMAuthenticator'
import { TypeORMAuthenticatorChallenge } from '../Infra/TypeORM/TypeORMAuthenticatorChallenge'
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
import { Env } from './Env'
@@ -58,6 +60,8 @@ export const AppDataSource = new DataSource({
SharedSubscriptionInvitation,
SubscriptionSetting,
TypeORMSessionTrace,
TypeORMAuthenticator,
TypeORMAuthenticatorChallenge,
],
migrations: [env.get('DB_MIGRATIONS_PATH', true) ?? 'dist/migrations/*.js'],
migrationsRun: true,

View File

@@ -5,6 +5,8 @@ const TYPES = {
SQS: Symbol.for('SQS'),
// Mapping
SessionTracePersistenceMapper: Symbol.for('SessionTracePersistenceMapper'),
AuthenticatorChallengePersistenceMapper: Symbol.for('AuthenticatorChallengePersistenceMapper'),
AuthenticatorPersistenceMapper: Symbol.for('AuthenticatorPersistenceMapper'),
// Controller
AuthController: Symbol.for('AuthController'),
SubscriptionInvitesController: Symbol.for('SubscriptionInvitesController'),
@@ -26,6 +28,8 @@ const TYPES = {
SharedSubscriptionInvitationRepository: Symbol.for('SharedSubscriptionInvitationRepository'),
PKCERepository: Symbol.for('PKCERepository'),
SessionTraceRepository: Symbol.for('SessionTraceRepository'),
AuthenticatorRepository: Symbol.for('AuthenticatorRepository'),
AuthenticatorChallengeRepository: Symbol.for('AuthenticatorChallengeRepository'),
// ORM
ORMOfflineSettingRepository: Symbol.for('ORMOfflineSettingRepository'),
ORMOfflineUserSubscriptionRepository: Symbol.for('ORMOfflineUserSubscriptionRepository'),
@@ -38,6 +42,8 @@ const TYPES = {
ORMUserRepository: Symbol.for('ORMUserRepository'),
ORMUserSubscriptionRepository: Symbol.for('ORMUserSubscriptionRepository'),
ORMSessionTraceRepository: Symbol.for('ORMSessionTraceRepository'),
ORMAuthenticatorRepository: Symbol.for('ORMAuthenticatorRepository'),
ORMAuthenticatorChallengeRepository: Symbol.for('ORMAuthenticatorChallengeRepository'),
// Middleware
AuthMiddleware: Symbol.for('AuthMiddleware'),
ApiGatewayAuthMiddleware: Symbol.for('ApiGatewayAuthMiddleware'),
@@ -127,6 +133,7 @@ const TYPES = {
TraceSession: Symbol.for('TraceSession'),
CleanupSessionTraces: Symbol.for('CleanupSessionTraces'),
PersistStatistics: Symbol.for('PersistStatistics'),
GenerateAuthenticatorRegistrationOptions: Symbol.for('GenerateAuthenticatorRegistrationOptions'),
// Handlers
UserRegisteredEventHandler: Symbol.for('UserRegisteredEventHandler'),
AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'),

View File

@@ -0,0 +1,21 @@
import { Dates, Uuid } from '@standardnotes/domain-core'
import { Authenticator } from './Authenticator'
describe('Authenticator', () => {
it('should create an entity', () => {
const entityOrError = Authenticator.create({
counter: 1,
credentialBackedUp: true,
credentialDeviceType: 'singleDevice',
credentialId: Buffer.from('credentialId'),
credentialPublicKey: Buffer.from('credentialPublicKey'),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
dates: Dates.create(new Date(1), new Date(1)).getValue(),
transports: ['usb'],
})
expect(entityOrError.isFailed()).toBeFalsy()
expect(entityOrError.getValue().id).not.toBeNull()
})
})

View File

@@ -0,0 +1,17 @@
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
import { AuthenticatorProps } from './AuthenticatorProps'
export class Authenticator extends Entity<AuthenticatorProps> {
get id(): UniqueEntityId {
return this._id
}
private constructor(props: AuthenticatorProps, id?: UniqueEntityId) {
super(props, id)
}
static create(props: AuthenticatorProps, id?: UniqueEntityId): Result<Authenticator> {
return Result.ok<Authenticator>(new Authenticator(props, id))
}
}

View File

@@ -0,0 +1,16 @@
import { Uuid } from '@standardnotes/domain-core'
import { AuthenticatorChallenge } from './AuthenticatorChallenge'
describe('AuthenticatorChallenge', () => {
it('should create an entity', () => {
const entityOrError = AuthenticatorChallenge.create({
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
createdAt: new Date(1),
challenge: Buffer.from('challenge'),
})
expect(entityOrError.isFailed()).toBeFalsy()
expect(entityOrError.getValue().id).not.toBeNull()
})
})

View File

@@ -0,0 +1,17 @@
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
import { AuthenticatorChallengeProps } from './AuthenticatorChallengeProps'
export class AuthenticatorChallenge extends Entity<AuthenticatorChallengeProps> {
get id(): UniqueEntityId {
return this._id
}
private constructor(props: AuthenticatorChallengeProps, id?: UniqueEntityId) {
super(props, id)
}
static create(props: AuthenticatorChallengeProps, id?: UniqueEntityId): Result<AuthenticatorChallenge> {
return Result.ok<AuthenticatorChallenge>(new AuthenticatorChallenge(props, id))
}
}

View File

@@ -0,0 +1,7 @@
import { Uuid } from '@standardnotes/domain-core'
export interface AuthenticatorChallengeProps {
userUuid: Uuid
challenge: Buffer
createdAt: Date
}

View File

@@ -0,0 +1,5 @@
import { AuthenticatorChallenge } from './AuthenticatorChallenge'
export interface AuthenticatorChallengeRepositoryInterface {
save(authenticatorChallenge: AuthenticatorChallenge): Promise<void>
}

View File

@@ -0,0 +1,12 @@
import { Dates, Uuid } from '@standardnotes/domain-core'
export interface AuthenticatorProps {
userUuid: Uuid
credentialId: Buffer
credentialPublicKey: Buffer
counter: number
credentialDeviceType: string
credentialBackedUp: boolean
transports?: string[]
dates: Dates
}

View File

@@ -0,0 +1,6 @@
import { Uuid } from '@standardnotes/domain-core'
import { Authenticator } from './Authenticator'
export interface AuthenticatorRepositoryInterface {
findByUserUuid(userUuid: Uuid): Promise<Authenticator[]>
}

View File

@@ -0,0 +1,89 @@
import { Dates, Result, Uuid } from '@standardnotes/domain-core'
import { Authenticator } from '../../Authenticator/Authenticator'
import { AuthenticatorChallenge } from '../../Authenticator/AuthenticatorChallenge'
import { AuthenticatorChallengeRepositoryInterface } from '../../Authenticator/AuthenticatorChallengeRepositoryInterface'
import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface'
import { GenerateAuthenticatorRegistrationOptions } from './GenerateAuthenticatorRegistrationOptions'
describe('GenerateAuthenticatorRegistrationOptions', () => {
let authenticatorRepository: AuthenticatorRepositoryInterface
let authenticatorChallengeRepository: AuthenticatorChallengeRepositoryInterface
const createUseCase = () =>
new GenerateAuthenticatorRegistrationOptions(authenticatorRepository, authenticatorChallengeRepository)
beforeEach(() => {
const authenticator = Authenticator.create({
counter: 1,
credentialBackedUp: true,
credentialDeviceType: 'singleDevice',
credentialId: Buffer.from('credentialId'),
credentialPublicKey: Buffer.from('credentialPublicKey'),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
dates: Dates.create(new Date(1), new Date(1)).getValue(),
transports: ['usb'],
}).getValue()
authenticatorRepository = {} as jest.Mocked<AuthenticatorRepositoryInterface>
authenticatorRepository.findByUserUuid = jest.fn().mockReturnValue([authenticator])
authenticatorChallengeRepository = {} as jest.Mocked<AuthenticatorChallengeRepositoryInterface>
authenticatorChallengeRepository.save = jest.fn()
})
it('should return error if userUuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userUuid: 'invalid',
username: 'username',
})
expect(result.isFailed()).toBe(true)
expect(result.getError()).toBe(
'Could not generate authenticator registration options: Given value is not a valid uuid: invalid',
)
})
it('should return error if username is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userUuid: '00000000-0000-0000-0000-000000000000',
username: '',
})
expect(result.isFailed()).toBe(true)
expect(result.getError()).toBe('Could not generate authenticator registration options: Username cannot be empty')
})
it('should return error if authenticator challenge is invalid', async () => {
const mock = jest.spyOn(AuthenticatorChallenge, 'create')
mock.mockReturnValue(Result.fail('Oops'))
const useCase = createUseCase()
const result = await useCase.execute({
userUuid: '00000000-0000-0000-0000-000000000000',
username: 'username',
})
expect(result.isFailed()).toBe(true)
expect(result.getError()).toBe('Could not generate authenticator registration options: Oops')
mock.mockRestore()
})
it('should return registration options', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userUuid: '00000000-0000-0000-0000-000000000000',
username: 'username',
})
expect(result.isFailed()).toBe(false)
expect(authenticatorChallengeRepository.save).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,61 @@
import { Result, UseCaseInterface, Username, Uuid } from '@standardnotes/domain-core'
import { generateRegistrationOptions } from '@simplewebauthn/server'
import { GenerateAuthenticatorRegistrationOptionsDTO } from './GenerateAuthenticatorRegistrationOptionsDTO'
import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface'
import { AuthenticatorChallengeRepositoryInterface } from '../../Authenticator/AuthenticatorChallengeRepositoryInterface'
import { AuthenticatorChallenge } from '../../Authenticator/AuthenticatorChallenge'
export class GenerateAuthenticatorRegistrationOptions implements UseCaseInterface<Record<string, unknown>> {
private readonly RP_NAME = 'Standard Notes'
private readonly RP_ID = 'standardnotes.com'
constructor(
private authenticatorRepository: AuthenticatorRepositoryInterface,
private authenticatorChallengeRepository: AuthenticatorChallengeRepositoryInterface,
) {}
async execute(dto: GenerateAuthenticatorRegistrationOptionsDTO): Promise<Result<Record<string, unknown>>> {
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return Result.fail(`Could not generate authenticator registration options: ${userUuidOrError.getError()}`)
}
const userUuid = userUuidOrError.getValue()
const usernameOrError = Username.create(dto.username)
if (usernameOrError.isFailed()) {
return Result.fail(`Could not generate authenticator registration options: ${usernameOrError.getError()}`)
}
const username = usernameOrError.getValue()
const authenticators = await this.authenticatorRepository.findByUserUuid(userUuid)
const options = generateRegistrationOptions({
rpID: this.RP_ID,
rpName: this.RP_NAME,
userID: userUuid.value,
userName: username.value,
attestationType: 'none',
excludeCredentials: authenticators.map((authenticator) => ({
id: authenticator.props.credentialId,
type: 'public-key',
transports: authenticator.props.transports,
})),
})
const authenticatorChallengeOrError = AuthenticatorChallenge.create({
challenge: options.challenge,
userUuid,
createdAt: new Date(),
})
if (authenticatorChallengeOrError.isFailed()) {
return Result.fail(
`Could not generate authenticator registration options: ${authenticatorChallengeOrError.getError()}`,
)
}
const authenticatorChallenge = authenticatorChallengeOrError.getValue()
await this.authenticatorChallengeRepository.save(authenticatorChallenge)
return Result.ok(options)
}
}

View File

@@ -0,0 +1,4 @@
export interface GenerateAuthenticatorRegistrationOptionsDTO {
userUuid: string
username: string
}

View File

@@ -0,0 +1,20 @@
import { MapperInterface } from '@standardnotes/domain-core'
import { Repository } from 'typeorm'
import { AuthenticatorChallenge } from '../../Domain/Authenticator/AuthenticatorChallenge'
import { AuthenticatorChallengeRepositoryInterface } from '../../Domain/Authenticator/AuthenticatorChallengeRepositoryInterface'
import { TypeORMAuthenticatorChallenge } from '../TypeORM/TypeORMAuthenticatorChallenge'
export class MySQLAuthenticatorChallengeRepository implements AuthenticatorChallengeRepositoryInterface {
constructor(
private ormRepository: Repository<TypeORMAuthenticatorChallenge>,
private mapper: MapperInterface<AuthenticatorChallenge, TypeORMAuthenticatorChallenge>,
) {}
async save(authenticatorChallenge: AuthenticatorChallenge): Promise<void> {
const persistence = this.mapper.toProjection(authenticatorChallenge)
await this.ormRepository.save(persistence)
}
}

View File

@@ -0,0 +1,24 @@
import { MapperInterface, Uuid } from '@standardnotes/domain-core'
import { Repository } from 'typeorm'
import { Authenticator } from '../../Domain/Authenticator/Authenticator'
import { AuthenticatorRepositoryInterface } from '../../Domain/Authenticator/AuthenticatorRepositoryInterface'
import { TypeORMAuthenticator } from '../TypeORM/TypeORMAuthenticator'
export class MySQLAuthenticatorRepository implements AuthenticatorRepositoryInterface {
constructor(
private ormRepository: Repository<TypeORMAuthenticator>,
private mapper: MapperInterface<Authenticator, TypeORMAuthenticator>,
) {}
async findByUserUuid(userUuid: Uuid): Promise<Authenticator[]> {
const typeOrm = await this.ormRepository
.createQueryBuilder('authenticator')
.where('authenticator.user_uuid = :userUuid', {
userUuid: userUuid.value,
})
.getMany()
return typeOrm.map((authenticator) => this.mapper.toDomain(authenticator))
}
}

View File

@@ -0,0 +1,65 @@
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
@Entity({ name: 'authenticators' })
export class TypeORMAuthenticator {
@PrimaryGeneratedColumn('uuid')
declare uuid: string
@Column({
name: 'user_uuid',
length: 36,
})
declare userUuid: string
@Column({
name: 'credential_id',
type: 'varbinary',
length: 1024,
})
@Index('credential_id', { unique: true })
declare credentialId: Buffer
@Column({
name: 'credential_public_key',
type: 'blob',
})
declare credentialPublicKey: Buffer
@Column({
name: 'counter',
type: 'bigint',
})
declare counter: number
@Column({
name: 'credential_device_type',
type: 'varchar',
length: 32,
})
declare credentialDeviceType: string
@Column({
name: 'credential_backed_up',
})
declare credentialBackedUp: boolean
@Column({
name: 'transports',
type: 'varchar',
length: 255,
nullable: true,
})
declare transports: string | null
@Column({
name: 'created_at',
type: 'datetime',
})
declare createdAt: Date
@Column({
name: 'updated_at',
type: 'datetime',
})
declare updatedAt: Date
}

View File

@@ -0,0 +1,27 @@
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
@Entity({ name: 'authenticator_challenges' })
@Index('user_uuid_and_challenge', ['userUuid', 'challenge'])
export class TypeORMAuthenticatorChallenge {
@PrimaryGeneratedColumn('uuid')
declare uuid: string
@Column({
name: 'user_uuid',
length: 36,
})
declare userUuid: string
@Column({
name: 'challenge',
type: 'varchar',
length: 255,
})
declare challenge: Buffer
@Column({
name: 'created_at',
type: 'datetime',
})
declare createdAt: Date
}

View File

@@ -0,0 +1,44 @@
import { MapperInterface, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
import { AuthenticatorChallenge } from '../Domain/Authenticator/AuthenticatorChallenge'
import { TypeORMAuthenticatorChallenge } from '../Infra/TypeORM/TypeORMAuthenticatorChallenge'
export class AuthenticatorChallengePersistenceMapper
implements MapperInterface<AuthenticatorChallenge, TypeORMAuthenticatorChallenge>
{
toDomain(projection: TypeORMAuthenticatorChallenge): AuthenticatorChallenge {
const userUuidOrError = Uuid.create(projection.userUuid)
if (userUuidOrError.isFailed()) {
throw new Error(`Failed to create authenticator challenge from projection: ${userUuidOrError.getError()}`)
}
const userUuid = userUuidOrError.getValue()
const authenticatorChallengeOrError = AuthenticatorChallenge.create(
{
challenge: projection.challenge,
userUuid,
createdAt: projection.createdAt,
},
new UniqueEntityId(projection.uuid),
)
if (authenticatorChallengeOrError.isFailed()) {
throw new Error(
`Failed to create authenticator challenge from projection: ${authenticatorChallengeOrError.getError()}`,
)
}
const authenticatorChallenge = authenticatorChallengeOrError.getValue()
return authenticatorChallenge
}
toProjection(domain: AuthenticatorChallenge): TypeORMAuthenticatorChallenge {
const typeorm = new TypeORMAuthenticatorChallenge()
typeorm.uuid = domain.id.toString()
typeorm.userUuid = domain.props.userUuid.value
typeorm.challenge = domain.props.challenge
typeorm.createdAt = domain.props.createdAt
return typeorm
}
}

View File

@@ -0,0 +1,59 @@
import { Dates, MapperInterface, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
import { Authenticator } from '../Domain/Authenticator/Authenticator'
import { TypeORMAuthenticator } from '../Infra/TypeORM/TypeORMAuthenticator'
export class AuthenticatorPersistenceMapper implements MapperInterface<Authenticator, TypeORMAuthenticator> {
toDomain(projection: TypeORMAuthenticator): Authenticator {
const userUuidOrError = Uuid.create(projection.userUuid)
if (userUuidOrError.isFailed()) {
throw new Error(`Failed to create authenticator from projection: ${userUuidOrError.getError()}`)
}
const userUuid = userUuidOrError.getValue()
const datesOrError = Dates.create(projection.createdAt, projection.updatedAt)
if (datesOrError.isFailed()) {
throw new Error(`Failed to create authenticator from projection: ${datesOrError.getError()}`)
}
const dates = datesOrError.getValue()
const authenticatorOrError = Authenticator.create(
{
userUuid,
counter: projection.counter,
credentialBackedUp: projection.credentialBackedUp,
credentialDeviceType: projection.credentialDeviceType,
credentialId: projection.credentialId,
credentialPublicKey: projection.credentialPublicKey,
dates,
transports: projection.transports ? JSON.parse(projection.transports) : undefined,
},
new UniqueEntityId(projection.uuid),
)
if (authenticatorOrError.isFailed()) {
throw new Error(`Failed to create authenticator from projection: ${authenticatorOrError.getError()}`)
}
const authenticator = authenticatorOrError.getValue()
return authenticator
}
toProjection(domain: Authenticator): TypeORMAuthenticator {
const typeorm = new TypeORMAuthenticator()
typeorm.uuid = domain.id.toString()
typeorm.userUuid = domain.props.userUuid.value
typeorm.credentialId = domain.props.credentialId
typeorm.credentialPublicKey = domain.props.credentialPublicKey
typeorm.counter = domain.props.counter
typeorm.credentialDeviceType = domain.props.credentialDeviceType
typeorm.credentialBackedUp = domain.props.credentialBackedUp
if (domain.props.transports) {
typeorm.transports = JSON.stringify(domain.props.transports)
}
typeorm.createdAt = domain.props.dates.createdAt
typeorm.updatedAt = domain.props.dates.updatedAt
return typeorm
}
}

231
yarn.lock
View File

@@ -1323,6 +1323,13 @@ __metadata:
languageName: node
linkType: hard
"@noble/ed25519@npm:^1.6.1":
version: 1.7.1
resolution: "@noble/ed25519@npm:1.7.1"
checksum: b1aa4b9264c2a26d1905b01493c81c124eb05758c00464fcc9ae92880f9696fc4b525d9ff2a22c859ca753982172ba7b180242a6adb0b8ecc52a944d2173e77c
languageName: node
linkType: hard
"@nodelib/fs.scandir@npm:2.1.5":
version: 2.1.5
resolution: "@nodelib/fs.scandir@npm:2.1.5"
@@ -1614,6 +1621,41 @@ __metadata:
languageName: node
linkType: hard
"@peculiar/asn1-android@npm:^2.1.7":
version: 2.3.3
resolution: "@peculiar/asn1-android@npm:2.3.3"
dependencies:
"@peculiar/asn1-schema": "npm:^2.3.3"
asn1js: "npm:^3.0.5"
tslib: "npm:^2.4.0"
checksum: 0c7cad544efe3a7235888a56baf70ce913b3604d53d2733f606b9904c8af52bad0cd55b7a28276676a707c655ee6a6d30ee6232454a0c568bc488dcbb88b943b
languageName: node
linkType: hard
"@peculiar/asn1-schema@npm:^2.1.7, @peculiar/asn1-schema@npm:^2.3.3":
version: 2.3.3
resolution: "@peculiar/asn1-schema@npm:2.3.3"
dependencies:
asn1js: "npm:^3.0.5"
pvtsutils: "npm:^1.3.2"
tslib: "npm:^2.4.0"
checksum: f584f79d5a3652888452e72591cd5f910b1c4c59a359c7495a82d8f6463e3033a715bdd1954b52bcecb864f21f8844335025e84e782cc843c6649bc3304094ec
languageName: node
linkType: hard
"@peculiar/asn1-x509@npm:^2.1.7":
version: 2.3.4
resolution: "@peculiar/asn1-x509@npm:2.3.4"
dependencies:
"@peculiar/asn1-schema": "npm:^2.3.3"
asn1js: "npm:^3.0.5"
ipaddr.js: "npm:^2.0.1"
pvtsutils: "npm:^1.3.2"
tslib: "npm:^2.4.0"
checksum: 10a865998042fc7dc4fff8de5d7b926ba37962fcf8d5b923c6ba322de388788d8fcf0c235d9298bf32d06f1262adebfe5003f92aaa45108d234303a25175a85b
languageName: node
linkType: hard
"@pnpm/network.ca-file@npm:^1.0.1":
version: 1.0.1
resolution: "@pnpm/network.ca-file@npm:1.0.1"
@@ -1844,6 +1886,24 @@ __metadata:
languageName: node
linkType: hard
"@simplewebauthn/server@npm:^6.2.2":
version: 6.2.2
resolution: "@simplewebauthn/server@npm:6.2.2"
dependencies:
"@noble/ed25519": "npm:^1.6.1"
"@peculiar/asn1-android": "npm:^2.1.7"
"@peculiar/asn1-schema": "npm:^2.1.7"
"@peculiar/asn1-x509": "npm:^2.1.7"
base64url: "npm:^3.0.1"
cbor: "npm:^5.1.0"
debug: "npm:^4.3.2"
jsrsasign: "npm:^10.4.0"
jwk-to-pem: "npm:^2.0.4"
node-fetch: "npm:^2.6.0"
checksum: 5ffb9b1c15a69ca74c80adf70d890ba1e0bba21040653d79dc4a443d505aefe9d085763918e491479b0e64dd32c214c2acdc96d44de0384e542411944d01e09b
languageName: node
linkType: hard
"@sinclair/typebox@npm:^0.24.1":
version: 0.24.44
resolution: "@sinclair/typebox@npm:0.24.44"
@@ -1983,6 +2043,7 @@ __metadata:
"@sentry/node": "npm:^7.28.1"
"@sentry/profiling-node": "npm:^0.0.12"
"@sentry/tracing": "npm:^7.28.1"
"@simplewebauthn/server": "npm:^6.2.2"
"@standardnotes/api": "npm:^1.19.0"
"@standardnotes/common": "workspace:*"
"@standardnotes/domain-core": "workspace:^"
@@ -3644,6 +3705,29 @@ __metadata:
languageName: node
linkType: hard
"asn1.js@npm:^5.3.0":
version: 5.4.1
resolution: "asn1.js@npm:5.4.1"
dependencies:
bn.js: "npm:^4.0.0"
inherits: "npm:^2.0.1"
minimalistic-assert: "npm:^1.0.0"
safer-buffer: "npm:^2.1.0"
checksum: 5c36f81388e344c9417866bd20acd2d4164d2bc2827d4fd0e35714f8701a249b9c6118c70720758fe710a4723d65699c5be1e827f89e9eff1dbd1bfe910300fd
languageName: node
linkType: hard
"asn1js@npm:^3.0.5":
version: 3.0.5
resolution: "asn1js@npm:3.0.5"
dependencies:
pvtsutils: "npm:^1.3.2"
pvutils: "npm:^1.1.3"
tslib: "npm:^2.4.0"
checksum: d0bc57da97696cbf161ad24cb35f9442b59a9e59a30f30e13ba6e2fb9e69f417666d952cb436a9f309a3c88d4d89404493dcc277a5fdb4d0cdbeb03da0303bb1
languageName: node
linkType: hard
"async@npm:^3.2.3, async@npm:^3.2.4":
version: 3.2.4
resolution: "async@npm:3.2.4"
@@ -3784,6 +3868,13 @@ __metadata:
languageName: node
linkType: hard
"base64url@npm:^3.0.1":
version: 3.0.1
resolution: "base64url@npm:3.0.1"
checksum: 72e1401ffe08693524f35bc2912a519bb3bf58f8911f3c4045d8c4bb6b5112187eae2eb45db895f464debe3ff562d46b76305e7cc4de4857365c47f73442c228
languageName: node
linkType: hard
"bcryptjs@npm:2.4.3":
version: 2.4.3
resolution: "bcryptjs@npm:2.4.3"
@@ -3798,6 +3889,13 @@ __metadata:
languageName: node
linkType: hard
"bignumber.js@npm:^9.0.1":
version: 9.1.1
resolution: "bignumber.js@npm:9.1.1"
checksum: e44d00804916c299d01e1a83b435111dbced54c6f165df4a0034a8a0a27182d6698f93f408ec804b3ae80896fd6ad8ad43f37939883dc03ecd04a125742f1483
languageName: node
linkType: hard
"binary-extensions@npm:^2.0.0":
version: 2.2.0
resolution: "binary-extensions@npm:2.2.0"
@@ -3816,6 +3914,13 @@ __metadata:
languageName: node
linkType: hard
"bn.js@npm:^4.0.0, bn.js@npm:^4.11.9":
version: 4.12.0
resolution: "bn.js@npm:4.12.0"
checksum: bfb4590775a29dad10c8d42da5ba7fca9d4f796f6d278cb27f53c6a6272df5e58a3ca58d879487c9584db9e7a8f73ac843117183bebea2d627c1f0db95848ec8
languageName: node
linkType: hard
"body-parser@npm:1.20.1":
version: 1.20.1
resolution: "body-parser@npm:1.20.1"
@@ -3880,6 +3985,13 @@ __metadata:
languageName: node
linkType: hard
"brorand@npm:^1.1.0":
version: 1.1.0
resolution: "brorand@npm:1.1.0"
checksum: f736e127fbac2d704b0b55935c297ec261112b93a178e15170da19c17500d448ebacff3b1edb075821363e8daecc739c062b40e920aa19b8cbed7f4fbe1ff6aa
languageName: node
linkType: hard
"browserslist@npm:^4.20.2":
version: 4.21.1
resolution: "browserslist@npm:4.21.1"
@@ -4093,6 +4205,16 @@ __metadata:
languageName: node
linkType: hard
"cbor@npm:^5.1.0":
version: 5.2.0
resolution: "cbor@npm:5.2.0"
dependencies:
bignumber.js: "npm:^9.0.1"
nofilter: "npm:^1.0.4"
checksum: d60986b9d006dd60e462293629830afe2db89db6a06ee2665ac59618b7a76af5e8cef880e4eb484345c8e978fa8425b1cf1e5f3840b97f39ad9e3d2fa8f28bc6
languageName: node
linkType: hard
"chalk@npm:^2.0.0, chalk@npm:^2.4.2":
version: 2.4.2
resolution: "chalk@npm:2.4.2"
@@ -5038,6 +5160,21 @@ __metadata:
languageName: node
linkType: hard
"elliptic@npm:^6.5.4":
version: 6.5.4
resolution: "elliptic@npm:6.5.4"
dependencies:
bn.js: "npm:^4.11.9"
brorand: "npm:^1.1.0"
hash.js: "npm:^1.0.0"
hmac-drbg: "npm:^1.0.1"
inherits: "npm:^2.0.4"
minimalistic-assert: "npm:^1.0.1"
minimalistic-crypto-utils: "npm:^1.0.1"
checksum: 4453b008cf9e741a87f8e1935398c10124291026e7f2b99a512205a645c59586deaeefbb1e7149574481ec2c4cc7f34efc6ae6ae5bc35a94431be71c0375367e
languageName: node
linkType: hard
"emittery@npm:^0.10.2":
version: 0.10.2
resolution: "emittery@npm:0.10.2"
@@ -6287,6 +6424,16 @@ __metadata:
languageName: node
linkType: hard
"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3":
version: 1.1.7
resolution: "hash.js@npm:1.1.7"
dependencies:
inherits: "npm:^2.0.3"
minimalistic-assert: "npm:^1.0.1"
checksum: e4266370d194fd31ed7bb51f5a943cf4e3b361321ea19a0dfcaab2e21400c3e581d8dec897364ed4530845c2c1b58d44dd6a9b3682cfd5ec02d0ce7bc802f1db
languageName: node
linkType: hard
"helmet@npm:^6.0.0":
version: 6.0.0
resolution: "helmet@npm:6.0.0"
@@ -6301,6 +6448,17 @@ __metadata:
languageName: node
linkType: hard
"hmac-drbg@npm:^1.0.1":
version: 1.0.1
resolution: "hmac-drbg@npm:1.0.1"
dependencies:
hash.js: "npm:^1.0.3"
minimalistic-assert: "npm:^1.0.0"
minimalistic-crypto-utils: "npm:^1.0.1"
checksum: 4e88d58ffc03e027990bbc31c0aa7b90dc4d2b3642ac3a8f3b71e3c43eb03416179ac601f36417312f0375cc382a9e39d80ade1ae239aff188701162bc84226d
languageName: node
linkType: hard
"hosted-git-info@npm:^2.1.4":
version: 2.8.9
resolution: "hosted-git-info@npm:2.8.9"
@@ -6640,6 +6798,13 @@ __metadata:
languageName: node
linkType: hard
"ipaddr.js@npm:^2.0.1":
version: 2.0.1
resolution: "ipaddr.js@npm:2.0.1"
checksum: 04ce6c896c82b163a87d0be70fa1701dda54f1315f27419207c9ea95bea025cacbe6335d5e0c1270657158f60d17eeaa0bbb19b60e230e34532adfca786c6dc7
languageName: node
linkType: hard
"is-arguments@npm:^1.0.4":
version: 1.1.1
resolution: "is-arguments@npm:1.1.1"
@@ -7664,6 +7829,13 @@ __metadata:
languageName: node
linkType: hard
"jsrsasign@npm:^10.4.0":
version: 10.6.1
resolution: "jsrsasign@npm:10.6.1"
checksum: e8e9c1b24f78d506eccd3b0e68e6e9dc8011f2ca4bd5baedc33a3a64b63c5ff7e21d00a73ef95624fd9fd594743cc6dd60b39b17d6938c7a6a712242698bac14
languageName: node
linkType: hard
"jwa@npm:^1.4.1":
version: 1.4.1
resolution: "jwa@npm:1.4.1"
@@ -7675,6 +7847,17 @@ __metadata:
languageName: node
linkType: hard
"jwk-to-pem@npm:^2.0.4":
version: 2.0.5
resolution: "jwk-to-pem@npm:2.0.5"
dependencies:
asn1.js: "npm:^5.3.0"
elliptic: "npm:^6.5.4"
safe-buffer: "npm:^5.0.1"
checksum: fced3a75b0fcc6145e703669e210b670789df5d85dbcbf11d3f97e2bb24954e70e889b69ddb1c6404089ae45c435a9bf09137dfd0290931b49c8ccd09fa5e704
languageName: node
linkType: hard
"jws@npm:^3.2.2":
version: 3.2.2
resolution: "jws@npm:3.2.2"
@@ -8209,6 +8392,20 @@ __metadata:
languageName: node
linkType: hard
"minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1":
version: 1.0.1
resolution: "minimalistic-assert@npm:1.0.1"
checksum: e2310081d82a7f8a6ee7f338d167c82b3eb5378929b9eda3a9eb633cf0f0f19c029b69e6868264447d4f726644b52fdc4dda3bc985793a1aeba9b02b13ca3f8e
languageName: node
linkType: hard
"minimalistic-crypto-utils@npm:^1.0.1":
version: 1.0.1
resolution: "minimalistic-crypto-utils@npm:1.0.1"
checksum: 7d909decd241bd658f941981ce53db4061c834daba807a5082d08fd2a0c488b8ef67904c90af38b70445e0220951a533ae3a181be5724ad342df4d9454286476
languageName: node
linkType: hard
"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
@@ -8501,7 +8698,7 @@ __metadata:
languageName: node
linkType: hard
"node-fetch@npm:^2.6.7":
"node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.7":
version: 2.6.7
resolution: "node-fetch@npm:2.6.7"
dependencies:
@@ -8600,6 +8797,13 @@ __metadata:
languageName: node
linkType: hard
"nofilter@npm:^1.0.4":
version: 1.0.4
resolution: "nofilter@npm:1.0.4"
checksum: 9a26874e7d5505acbee413d33311ed6d934ff2695e021b1ea1c9f83adc02e6e8038ed2702fcbcfc022bcf5a10cae6e79c6ad7ec23ecb512cdbfa7994fef6532f
languageName: node
linkType: hard
"nopt@npm:^5.0.0":
version: 5.0.0
resolution: "nopt@npm:5.0.0"
@@ -9554,6 +9758,22 @@ __metadata:
languageName: node
linkType: hard
"pvtsutils@npm:^1.3.2":
version: 1.3.2
resolution: "pvtsutils@npm:1.3.2"
dependencies:
tslib: "npm:^2.4.0"
checksum: eb22d3df60a341da289c59b2a34e4e55cadc70bd43caeb5fdd14d28e40f54112142e0e8bd5906f3d756e2c38b651599243def3ced6f1169335feb7933e210fa7
languageName: node
linkType: hard
"pvutils@npm:^1.1.3":
version: 1.1.3
resolution: "pvutils@npm:1.1.3"
checksum: 0cb4f4878f535b07ad409df26b4e724582a50cc9ddd71375cc8d79dc79b9d712dcc0cbe52f4385c27abe6887aa88a94c98377746d95f35a88f96ebb8a585814c
languageName: node
linkType: hard
"q@npm:^1.5.1":
version: 1.5.1
resolution: "q@npm:1.5.1"
@@ -10033,7 +10253,7 @@ __metadata:
languageName: node
linkType: hard
"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0":
"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.1.0":
version: 2.1.2
resolution: "safer-buffer@npm:2.1.2"
checksum: d4199666e9e792968c0b88c2c35dd400f56d3eecb9affbcf5207922822eadf30cc06995bae3c5d0a653851bbd40fc0af578bf046bbf734199ce22433ba4da659
@@ -11003,6 +11223,13 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:^2.4.0":
version: 2.4.1
resolution: "tslib@npm:2.4.1"
checksum: a739a21e3ff059e62c7c3c0845333fbc9c081bcc91e015f2d246a31eeaf1fb0e29b10c0913c7d22f692b3203bb469fd60ca5288aa4f27d351a969df2d0714899
languageName: node
linkType: hard
"tsutils@npm:^3.21.0":
version: 3.21.0
resolution: "tsutils@npm:3.21.0"