@powersync/service-core 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.probes/.gitkeep +0 -0
- package/CHANGELOG.md +13 -0
- package/LICENSE +67 -0
- package/README.md +3 -0
- package/dist/api/api-index.d.ts +2 -0
- package/dist/api/api-index.js +3 -0
- package/dist/api/api-index.js.map +1 -0
- package/dist/api/diagnostics.d.ts +21 -0
- package/dist/api/diagnostics.js +183 -0
- package/dist/api/diagnostics.js.map +1 -0
- package/dist/api/schema.d.ts +5 -0
- package/dist/api/schema.js +88 -0
- package/dist/api/schema.js.map +1 -0
- package/dist/auth/CachedKeyCollector.d.ts +46 -0
- package/dist/auth/CachedKeyCollector.js +116 -0
- package/dist/auth/CachedKeyCollector.js.map +1 -0
- package/dist/auth/CompoundKeyCollector.d.ts +8 -0
- package/dist/auth/CompoundKeyCollector.js +23 -0
- package/dist/auth/CompoundKeyCollector.js.map +1 -0
- package/dist/auth/JwtPayload.d.ts +10 -0
- package/dist/auth/JwtPayload.js +2 -0
- package/dist/auth/JwtPayload.js.map +1 -0
- package/dist/auth/KeyCollector.d.ts +24 -0
- package/dist/auth/KeyCollector.js +2 -0
- package/dist/auth/KeyCollector.js.map +1 -0
- package/dist/auth/KeySpec.d.ts +26 -0
- package/dist/auth/KeySpec.js +49 -0
- package/dist/auth/KeySpec.js.map +1 -0
- package/dist/auth/KeyStore.d.ts +39 -0
- package/dist/auth/KeyStore.js +131 -0
- package/dist/auth/KeyStore.js.map +1 -0
- package/dist/auth/LeakyBucket.d.ts +39 -0
- package/dist/auth/LeakyBucket.js +57 -0
- package/dist/auth/LeakyBucket.js.map +1 -0
- package/dist/auth/RemoteJWKSCollector.d.ts +24 -0
- package/dist/auth/RemoteJWKSCollector.js +106 -0
- package/dist/auth/RemoteJWKSCollector.js.map +1 -0
- package/dist/auth/StaticKeyCollector.d.ts +14 -0
- package/dist/auth/StaticKeyCollector.js +19 -0
- package/dist/auth/StaticKeyCollector.js.map +1 -0
- package/dist/auth/SupabaseKeyCollector.d.ts +22 -0
- package/dist/auth/SupabaseKeyCollector.js +61 -0
- package/dist/auth/SupabaseKeyCollector.js.map +1 -0
- package/dist/auth/auth-index.d.ts +10 -0
- package/dist/auth/auth-index.js +11 -0
- package/dist/auth/auth-index.js.map +1 -0
- package/dist/db/db-index.d.ts +1 -0
- package/dist/db/db-index.js +2 -0
- package/dist/db/db-index.js.map +1 -0
- package/dist/db/mongo.d.ts +29 -0
- package/dist/db/mongo.js +65 -0
- package/dist/db/mongo.js.map +1 -0
- package/dist/entry/cli-entry.d.ts +15 -0
- package/dist/entry/cli-entry.js +36 -0
- package/dist/entry/cli-entry.js.map +1 -0
- package/dist/entry/commands/config-command.d.ts +10 -0
- package/dist/entry/commands/config-command.js +21 -0
- package/dist/entry/commands/config-command.js.map +1 -0
- package/dist/entry/commands/migrate-action.d.ts +2 -0
- package/dist/entry/commands/migrate-action.js +18 -0
- package/dist/entry/commands/migrate-action.js.map +1 -0
- package/dist/entry/commands/start-action.d.ts +3 -0
- package/dist/entry/commands/start-action.js +15 -0
- package/dist/entry/commands/start-action.js.map +1 -0
- package/dist/entry/commands/teardown-action.d.ts +2 -0
- package/dist/entry/commands/teardown-action.js +17 -0
- package/dist/entry/commands/teardown-action.js.map +1 -0
- package/dist/entry/entry-index.d.ts +5 -0
- package/dist/entry/entry-index.js +6 -0
- package/dist/entry/entry-index.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/metrics.d.ts +16 -0
- package/dist/metrics/metrics.js +139 -0
- package/dist/metrics/metrics.js.map +1 -0
- package/dist/migrations/db/migrations/1684951997326-init.d.ts +3 -0
- package/dist/migrations/db/migrations/1684951997326-init.js +31 -0
- package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -0
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.d.ts +2 -0
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +5 -0
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +1 -0
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.d.ts +3 -0
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +54 -0
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -0
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.d.ts +3 -0
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js +27 -0
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -0
- package/dist/migrations/db/store.d.ts +3 -0
- package/dist/migrations/db/store.js +10 -0
- package/dist/migrations/db/store.js.map +1 -0
- package/dist/migrations/migrations.d.ts +10 -0
- package/dist/migrations/migrations.js +94 -0
- package/dist/migrations/migrations.js.map +1 -0
- package/dist/replication/ErrorRateLimiter.d.ts +17 -0
- package/dist/replication/ErrorRateLimiter.js +42 -0
- package/dist/replication/ErrorRateLimiter.js.map +1 -0
- package/dist/replication/PgRelation.d.ts +16 -0
- package/dist/replication/PgRelation.js +26 -0
- package/dist/replication/PgRelation.js.map +1 -0
- package/dist/replication/WalConnection.d.ts +34 -0
- package/dist/replication/WalConnection.js +190 -0
- package/dist/replication/WalConnection.js.map +1 -0
- package/dist/replication/WalStream.d.ts +58 -0
- package/dist/replication/WalStream.js +517 -0
- package/dist/replication/WalStream.js.map +1 -0
- package/dist/replication/WalStreamManager.d.ts +30 -0
- package/dist/replication/WalStreamManager.js +199 -0
- package/dist/replication/WalStreamManager.js.map +1 -0
- package/dist/replication/WalStreamRunner.d.ts +38 -0
- package/dist/replication/WalStreamRunner.js +155 -0
- package/dist/replication/WalStreamRunner.js.map +1 -0
- package/dist/replication/replication-index.d.ts +7 -0
- package/dist/replication/replication-index.js +8 -0
- package/dist/replication/replication-index.js.map +1 -0
- package/dist/replication/util.d.ts +9 -0
- package/dist/replication/util.js +62 -0
- package/dist/replication/util.js.map +1 -0
- package/dist/routes/admin.d.ts +7 -0
- package/dist/routes/admin.js +192 -0
- package/dist/routes/admin.js.map +1 -0
- package/dist/routes/auth.d.ts +58 -0
- package/dist/routes/auth.js +182 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/checkpointing.d.ts +3 -0
- package/dist/routes/checkpointing.js +30 -0
- package/dist/routes/checkpointing.js.map +1 -0
- package/dist/routes/dev.d.ts +6 -0
- package/dist/routes/dev.js +163 -0
- package/dist/routes/dev.js.map +1 -0
- package/dist/routes/route-generators.d.ts +15 -0
- package/dist/routes/route-generators.js +32 -0
- package/dist/routes/route-generators.js.map +1 -0
- package/dist/routes/router-socket.d.ts +10 -0
- package/dist/routes/router-socket.js +5 -0
- package/dist/routes/router-socket.js.map +1 -0
- package/dist/routes/router.d.ts +13 -0
- package/dist/routes/router.js +2 -0
- package/dist/routes/router.js.map +1 -0
- package/dist/routes/routes-index.d.ts +4 -0
- package/dist/routes/routes-index.js +5 -0
- package/dist/routes/routes-index.js.map +1 -0
- package/dist/routes/socket-route.d.ts +2 -0
- package/dist/routes/socket-route.js +119 -0
- package/dist/routes/socket-route.js.map +1 -0
- package/dist/routes/sync-rules.d.ts +6 -0
- package/dist/routes/sync-rules.js +182 -0
- package/dist/routes/sync-rules.js.map +1 -0
- package/dist/routes/sync-stream.d.ts +5 -0
- package/dist/routes/sync-stream.js +74 -0
- package/dist/routes/sync-stream.js.map +1 -0
- package/dist/runner/teardown.d.ts +2 -0
- package/dist/runner/teardown.js +79 -0
- package/dist/runner/teardown.js.map +1 -0
- package/dist/storage/BucketStorage.d.ts +298 -0
- package/dist/storage/BucketStorage.js +25 -0
- package/dist/storage/BucketStorage.js.map +1 -0
- package/dist/storage/MongoBucketStorage.d.ts +51 -0
- package/dist/storage/MongoBucketStorage.js +388 -0
- package/dist/storage/MongoBucketStorage.js.map +1 -0
- package/dist/storage/SourceTable.d.ts +39 -0
- package/dist/storage/SourceTable.js +50 -0
- package/dist/storage/SourceTable.js.map +1 -0
- package/dist/storage/mongo/MongoBucketBatch.d.ts +48 -0
- package/dist/storage/mongo/MongoBucketBatch.js +584 -0
- package/dist/storage/mongo/MongoBucketBatch.js.map +1 -0
- package/dist/storage/mongo/MongoIdSequence.d.ts +12 -0
- package/dist/storage/mongo/MongoIdSequence.js +21 -0
- package/dist/storage/mongo/MongoIdSequence.js.map +1 -0
- package/dist/storage/mongo/MongoPersistedSyncRules.d.ts +9 -0
- package/dist/storage/mongo/MongoPersistedSyncRules.js +9 -0
- package/dist/storage/mongo/MongoPersistedSyncRules.js.map +1 -0
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.d.ts +20 -0
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js +26 -0
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js.map +1 -0
- package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +27 -0
- package/dist/storage/mongo/MongoSyncBucketStorage.js +379 -0
- package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -0
- package/dist/storage/mongo/MongoSyncRulesLock.d.ts +16 -0
- package/dist/storage/mongo/MongoSyncRulesLock.js +65 -0
- package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -0
- package/dist/storage/mongo/OperationBatch.d.ts +26 -0
- package/dist/storage/mongo/OperationBatch.js +101 -0
- package/dist/storage/mongo/OperationBatch.js.map +1 -0
- package/dist/storage/mongo/PersistedBatch.d.ts +42 -0
- package/dist/storage/mongo/PersistedBatch.js +200 -0
- package/dist/storage/mongo/PersistedBatch.js.map +1 -0
- package/dist/storage/mongo/db.d.ts +23 -0
- package/dist/storage/mongo/db.js +34 -0
- package/dist/storage/mongo/db.js.map +1 -0
- package/dist/storage/mongo/models.d.ts +137 -0
- package/dist/storage/mongo/models.js +27 -0
- package/dist/storage/mongo/models.js.map +1 -0
- package/dist/storage/mongo/util.d.ts +26 -0
- package/dist/storage/mongo/util.js +81 -0
- package/dist/storage/mongo/util.js.map +1 -0
- package/dist/storage/storage-index.d.ts +14 -0
- package/dist/storage/storage-index.js +15 -0
- package/dist/storage/storage-index.js.map +1 -0
- package/dist/sync/BroadcastIterable.d.ts +38 -0
- package/dist/sync/BroadcastIterable.js +153 -0
- package/dist/sync/BroadcastIterable.js.map +1 -0
- package/dist/sync/LastValueSink.d.ts +25 -0
- package/dist/sync/LastValueSink.js +84 -0
- package/dist/sync/LastValueSink.js.map +1 -0
- package/dist/sync/merge.d.ts +39 -0
- package/dist/sync/merge.js +175 -0
- package/dist/sync/merge.js.map +1 -0
- package/dist/sync/safeRace.d.ts +1 -0
- package/dist/sync/safeRace.js +91 -0
- package/dist/sync/safeRace.js.map +1 -0
- package/dist/sync/sync-index.d.ts +6 -0
- package/dist/sync/sync-index.js +7 -0
- package/dist/sync/sync-index.js.map +1 -0
- package/dist/sync/sync.d.ts +18 -0
- package/dist/sync/sync.js +248 -0
- package/dist/sync/sync.js.map +1 -0
- package/dist/sync/util.d.ts +26 -0
- package/dist/sync/util.js +73 -0
- package/dist/sync/util.js.map +1 -0
- package/dist/system/CorePowerSyncSystem.d.ts +18 -0
- package/dist/system/CorePowerSyncSystem.js +28 -0
- package/dist/system/CorePowerSyncSystem.js.map +1 -0
- package/dist/util/Mutex.d.ts +47 -0
- package/dist/util/Mutex.js +132 -0
- package/dist/util/Mutex.js.map +1 -0
- package/dist/util/PgManager.d.ts +24 -0
- package/dist/util/PgManager.js +55 -0
- package/dist/util/PgManager.js.map +1 -0
- package/dist/util/alerting.d.ts +4 -0
- package/dist/util/alerting.js +14 -0
- package/dist/util/alerting.js.map +1 -0
- package/dist/util/config/collectors/config-collector.d.ts +29 -0
- package/dist/util/config/collectors/config-collector.js +116 -0
- package/dist/util/config/collectors/config-collector.js.map +1 -0
- package/dist/util/config/collectors/impl/base64-config-collector.d.ts +6 -0
- package/dist/util/config/collectors/impl/base64-config-collector.js +15 -0
- package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -0
- package/dist/util/config/collectors/impl/fallback-config-collector.d.ts +11 -0
- package/dist/util/config/collectors/impl/fallback-config-collector.js +19 -0
- package/dist/util/config/collectors/impl/fallback-config-collector.js.map +1 -0
- package/dist/util/config/collectors/impl/filesystem-config-collector.d.ts +6 -0
- package/dist/util/config/collectors/impl/filesystem-config-collector.js +35 -0
- package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -0
- package/dist/util/config/compound-config-collector.d.ts +32 -0
- package/dist/util/config/compound-config-collector.js +126 -0
- package/dist/util/config/compound-config-collector.js.map +1 -0
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.d.ts +7 -0
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js +17 -0
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -0
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.d.ts +7 -0
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +21 -0
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -0
- package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.d.ts +7 -0
- package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js +17 -0
- package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -0
- package/dist/util/config/sync-rules/sync-collector.d.ts +6 -0
- package/dist/util/config/sync-rules/sync-collector.js +3 -0
- package/dist/util/config/sync-rules/sync-collector.js.map +1 -0
- package/dist/util/config/types.d.ts +53 -0
- package/dist/util/config/types.js +7 -0
- package/dist/util/config/types.js.map +1 -0
- package/dist/util/config.d.ts +7 -0
- package/dist/util/config.js +35 -0
- package/dist/util/config.js.map +1 -0
- package/dist/util/env.d.ts +10 -0
- package/dist/util/env.js +25 -0
- package/dist/util/env.js.map +1 -0
- package/dist/util/memory-tracking.d.ts +7 -0
- package/dist/util/memory-tracking.js +58 -0
- package/dist/util/memory-tracking.js.map +1 -0
- package/dist/util/migration_lib.d.ts +11 -0
- package/dist/util/migration_lib.js +64 -0
- package/dist/util/migration_lib.js.map +1 -0
- package/dist/util/pgwire_utils.d.ts +24 -0
- package/dist/util/pgwire_utils.js +117 -0
- package/dist/util/pgwire_utils.js.map +1 -0
- package/dist/util/populate_test_data.d.ts +8 -0
- package/dist/util/populate_test_data.js +65 -0
- package/dist/util/populate_test_data.js.map +1 -0
- package/dist/util/protocol-types.d.ts +178 -0
- package/dist/util/protocol-types.js +38 -0
- package/dist/util/protocol-types.js.map +1 -0
- package/dist/util/secs.d.ts +2 -0
- package/dist/util/secs.js +49 -0
- package/dist/util/secs.js.map +1 -0
- package/dist/util/util-index.d.ts +22 -0
- package/dist/util/util-index.js +23 -0
- package/dist/util/util-index.js.map +1 -0
- package/dist/util/utils.d.ts +14 -0
- package/dist/util/utils.js +75 -0
- package/dist/util/utils.js.map +1 -0
- package/package.json +55 -0
- package/src/api/api-index.ts +2 -0
- package/src/api/diagnostics.ts +221 -0
- package/src/api/schema.ts +99 -0
- package/src/auth/CachedKeyCollector.ts +132 -0
- package/src/auth/CompoundKeyCollector.ts +33 -0
- package/src/auth/JwtPayload.ts +11 -0
- package/src/auth/KeyCollector.ts +27 -0
- package/src/auth/KeySpec.ts +67 -0
- package/src/auth/KeyStore.ts +156 -0
- package/src/auth/LeakyBucket.ts +66 -0
- package/src/auth/RemoteJWKSCollector.ts +130 -0
- package/src/auth/StaticKeyCollector.ts +21 -0
- package/src/auth/SupabaseKeyCollector.ts +67 -0
- package/src/auth/auth-index.ts +10 -0
- package/src/db/db-index.ts +1 -0
- package/src/db/mongo.ts +72 -0
- package/src/entry/cli-entry.ts +41 -0
- package/src/entry/commands/config-command.ts +36 -0
- package/src/entry/commands/migrate-action.ts +25 -0
- package/src/entry/commands/start-action.ts +24 -0
- package/src/entry/commands/teardown-action.ts +23 -0
- package/src/entry/entry-index.ts +5 -0
- package/src/index.ts +37 -0
- package/src/metrics/metrics.ts +169 -0
- package/src/migrations/db/migrations/1684951997326-init.ts +33 -0
- package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +5 -0
- package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +99 -0
- package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +32 -0
- package/src/migrations/db/store.ts +11 -0
- package/src/migrations/migrations.ts +122 -0
- package/src/replication/ErrorRateLimiter.ts +49 -0
- package/src/replication/PgRelation.ts +42 -0
- package/src/replication/WalConnection.ts +227 -0
- package/src/replication/WalStream.ts +626 -0
- package/src/replication/WalStreamManager.ts +214 -0
- package/src/replication/WalStreamRunner.ts +180 -0
- package/src/replication/replication-index.ts +7 -0
- package/src/replication/util.ts +76 -0
- package/src/routes/admin.ts +229 -0
- package/src/routes/auth.ts +209 -0
- package/src/routes/checkpointing.ts +38 -0
- package/src/routes/dev.ts +194 -0
- package/src/routes/route-generators.ts +39 -0
- package/src/routes/router-socket.ts +13 -0
- package/src/routes/router.ts +17 -0
- package/src/routes/routes-index.ts +5 -0
- package/src/routes/socket-route.ts +131 -0
- package/src/routes/sync-rules.ts +210 -0
- package/src/routes/sync-stream.ts +92 -0
- package/src/runner/teardown.ts +91 -0
- package/src/storage/BucketStorage.ts +386 -0
- package/src/storage/MongoBucketStorage.ts +493 -0
- package/src/storage/SourceTable.ts +60 -0
- package/src/storage/mongo/MongoBucketBatch.ts +756 -0
- package/src/storage/mongo/MongoIdSequence.ts +24 -0
- package/src/storage/mongo/MongoPersistedSyncRules.ts +16 -0
- package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +47 -0
- package/src/storage/mongo/MongoSyncBucketStorage.ts +517 -0
- package/src/storage/mongo/MongoSyncRulesLock.ts +81 -0
- package/src/storage/mongo/OperationBatch.ts +115 -0
- package/src/storage/mongo/PersistedBatch.ts +245 -0
- package/src/storage/mongo/db.ts +69 -0
- package/src/storage/mongo/models.ts +157 -0
- package/src/storage/mongo/util.ts +88 -0
- package/src/storage/storage-index.ts +15 -0
- package/src/sync/BroadcastIterable.ts +161 -0
- package/src/sync/LastValueSink.ts +100 -0
- package/src/sync/merge.ts +200 -0
- package/src/sync/safeRace.ts +99 -0
- package/src/sync/sync-index.ts +6 -0
- package/src/sync/sync.ts +312 -0
- package/src/sync/util.ts +98 -0
- package/src/system/CorePowerSyncSystem.ts +43 -0
- package/src/util/Mutex.ts +159 -0
- package/src/util/PgManager.ts +64 -0
- package/src/util/alerting.ts +17 -0
- package/src/util/config/collectors/config-collector.ts +141 -0
- package/src/util/config/collectors/impl/base64-config-collector.ts +18 -0
- package/src/util/config/collectors/impl/fallback-config-collector.ts +22 -0
- package/src/util/config/collectors/impl/filesystem-config-collector.ts +41 -0
- package/src/util/config/compound-config-collector.ts +171 -0
- package/src/util/config/sync-rules/impl/base64-sync-rules-collector.ts +21 -0
- package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +26 -0
- package/src/util/config/sync-rules/impl/inline-sync-rules-collector.ts +21 -0
- package/src/util/config/sync-rules/sync-collector.ts +8 -0
- package/src/util/config/types.ts +60 -0
- package/src/util/config.ts +39 -0
- package/src/util/env.ts +28 -0
- package/src/util/memory-tracking.ts +67 -0
- package/src/util/migration_lib.ts +79 -0
- package/src/util/pgwire_utils.ts +139 -0
- package/src/util/populate_test_data.ts +78 -0
- package/src/util/protocol-types.ts +223 -0
- package/src/util/secs.ts +54 -0
- package/src/util/util-index.ts +25 -0
- package/src/util/utils.ts +102 -0
- package/test/src/__snapshots__/pg_test.test.ts.snap +256 -0
- package/test/src/__snapshots__/sync.test.ts.snap +235 -0
- package/test/src/auth.test.ts +340 -0
- package/test/src/broadcast_iterable.test.ts +156 -0
- package/test/src/data_storage.test.ts +1176 -0
- package/test/src/env.ts +8 -0
- package/test/src/large_batch.test.ts +194 -0
- package/test/src/merge_iterable.test.ts +355 -0
- package/test/src/pg_test.test.ts +432 -0
- package/test/src/schema_changes.test.ts +545 -0
- package/test/src/slow_tests.test.ts +257 -0
- package/test/src/sql_functions.test.ts +254 -0
- package/test/src/sql_operators.test.ts +132 -0
- package/test/src/sync.test.ts +293 -0
- package/test/src/sync_rules.test.ts +1051 -0
- package/test/src/util.ts +67 -0
- package/test/src/validation.test.ts +63 -0
- package/test/src/wal_stream.test.ts +310 -0
- package/test/src/wal_stream_utils.ts +147 -0
- package/test/tsconfig.json +20 -0
- package/tsconfig.json +20 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
import { KeySpec } from './KeySpec.js';
|
|
3
|
+
export interface KeyCollector {
|
|
4
|
+
/**
|
|
5
|
+
* Fetch keys for this collector.
|
|
6
|
+
*
|
|
7
|
+
* If a partial result is available, return keys and errors array.
|
|
8
|
+
* These errors are not retried, and previous keys not cached.
|
|
9
|
+
*
|
|
10
|
+
* If the request fails completely, throw an error. These errors are retried.
|
|
11
|
+
* In that case, previous keys may be cached and used.
|
|
12
|
+
*/
|
|
13
|
+
getKeys(): Promise<KeyResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Indicates that no matching key was found.
|
|
16
|
+
*
|
|
17
|
+
* The collector may use this as a hint to reload keys, although this is not a requirement.
|
|
18
|
+
*/
|
|
19
|
+
noKeyFound?: () => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export interface KeyResult {
|
|
22
|
+
errors: jose.errors.JOSEError[];
|
|
23
|
+
keys: KeySpec[];
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyCollector.js","sourceRoot":"","sources":["../../src/auth/KeyCollector.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
export declare const HS_ALGORITHMS: string[];
|
|
3
|
+
export declare const RSA_ALGORITHMS: string[];
|
|
4
|
+
export declare const SUPPORTED_ALGORITHMS: string[];
|
|
5
|
+
export interface KeyOptions {
|
|
6
|
+
/**
|
|
7
|
+
* If configured, JWTs verified by this key must have one of these audiences
|
|
8
|
+
* in the `aud` claim, instead of the default.
|
|
9
|
+
*/
|
|
10
|
+
requiresAudience?: string[];
|
|
11
|
+
/**
|
|
12
|
+
* If configured, JWTs verified by this key can have a maximum lifetime up to
|
|
13
|
+
* this value, instead of the default.
|
|
14
|
+
*/
|
|
15
|
+
maxLifetimeSeconds?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class KeySpec {
|
|
18
|
+
key: jose.KeyLike;
|
|
19
|
+
source: jose.JWK;
|
|
20
|
+
options: KeyOptions;
|
|
21
|
+
static importKey(key: jose.JWK, options?: KeyOptions): Promise<KeySpec>;
|
|
22
|
+
constructor(source: jose.JWK, key: jose.KeyLike, options?: KeyOptions);
|
|
23
|
+
get kid(): string | undefined;
|
|
24
|
+
matchesAlgorithm(jwtAlg: string): boolean;
|
|
25
|
+
isValidSignature(token: string): Promise<boolean>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
export const HS_ALGORITHMS = ['HS256', 'HS384', 'HS512'];
|
|
3
|
+
export const RSA_ALGORITHMS = ['RS256', 'RS384', 'RS512'];
|
|
4
|
+
export const SUPPORTED_ALGORITHMS = [...HS_ALGORITHMS, ...RSA_ALGORITHMS];
|
|
5
|
+
export class KeySpec {
|
|
6
|
+
static async importKey(key, options) {
|
|
7
|
+
const parsed = (await jose.importJWK(key));
|
|
8
|
+
return new KeySpec(key, parsed, options);
|
|
9
|
+
}
|
|
10
|
+
constructor(source, key, options) {
|
|
11
|
+
this.source = source;
|
|
12
|
+
this.key = key;
|
|
13
|
+
this.options = options ?? {};
|
|
14
|
+
}
|
|
15
|
+
get kid() {
|
|
16
|
+
return this.source.kid;
|
|
17
|
+
}
|
|
18
|
+
matchesAlgorithm(jwtAlg) {
|
|
19
|
+
if (this.source.alg) {
|
|
20
|
+
return jwtAlg == this.source.alg;
|
|
21
|
+
}
|
|
22
|
+
else if (this.source.kty == 'RSA') {
|
|
23
|
+
return RSA_ALGORITHMS.includes(jwtAlg);
|
|
24
|
+
}
|
|
25
|
+
else if (this.source.kty == 'oct') {
|
|
26
|
+
return HS_ALGORITHMS.includes(jwtAlg);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// We don't support 'ec' yet
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async isValidSignature(token) {
|
|
34
|
+
try {
|
|
35
|
+
await jose.compactVerify(token, this.key);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
if (e.code == 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Token format error most likely
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=KeySpec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeySpec.js","sourceRoot":"","sources":["../../src/auth/KeySpec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,cAAc,CAAC,CAAC;AAgB1E,MAAM,OAAO,OAAO;IAKlB,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAa,EAAE,OAAoB;QACxD,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAiB,CAAC;QAC3D,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,YAAY,MAAgB,EAAE,GAAiB,EAAE,OAAoB;QACnE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;IACzB,CAAC;IAED,gBAAgB,CAAC,MAAc;QAC7B,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;YACpB,OAAO,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;QACnC,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC;YACpC,OAAO,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC;YACpC,OAAO,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,4BAA4B;YAC5B,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,KAAa;QAClC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YAC1C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,CAAC,IAAI,IAAI,uCAAuC,EAAE,CAAC;gBACtD,OAAO,KAAK,CAAC;YACf,CAAC;iBAAM,CAAC;gBACN,iCAAiC;gBACjC,MAAM,CAAC,CAAC;YACV,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { KeyCollector } from './KeyCollector.js';
|
|
2
|
+
import { JwtPayload } from './JwtPayload.js';
|
|
3
|
+
/**
|
|
4
|
+
* KeyStore to get keys and verify tokens.
|
|
5
|
+
*
|
|
6
|
+
*
|
|
7
|
+
* Similar to micro_auth's KeyStore, but with different caching and error handling.
|
|
8
|
+
*
|
|
9
|
+
* We generally assume that:
|
|
10
|
+
* 1. If we have a key kid matching a JWT kid, that is the correct key.
|
|
11
|
+
* We don't look for other keys, even if there are algorithm or other issues.
|
|
12
|
+
* 2. Otherwise, iterate through "wildcard" keys and look for a matching signature.
|
|
13
|
+
* Wildcard keys are any key defined without a kid.
|
|
14
|
+
*
|
|
15
|
+
* # Security considerations
|
|
16
|
+
*
|
|
17
|
+
* Some places for security holes:
|
|
18
|
+
* 1. Signature verification not done correctly: We rely on jose.jwtVerify() to do this correctly.
|
|
19
|
+
* 2. Using a key that has been revoked - see CachedKeyCollector's refresh strategy.
|
|
20
|
+
* 3. Using a key for the wrong purpose (e.g. key.use != 'sig'). Checked in RemoteJWKSCollector.
|
|
21
|
+
* 4. Not checking all attributes, e.g. a JWT trusted by the global firebase key, but has the wrong aud. Correct aud must be configured.
|
|
22
|
+
* 5. Using the incorrect algorithm, e.g. 'none', or using public key as a shared key.
|
|
23
|
+
* We check the algorithm for each JWT against the matching key's configured algorithm or algorithm family.
|
|
24
|
+
*
|
|
25
|
+
* # Errors
|
|
26
|
+
*
|
|
27
|
+
* If we have a matching kid, we can generally get a detailed error (e.g. signature verification failed, invalid algorithm, etc).
|
|
28
|
+
* If we don't have a matching kid, we'll generally just get an error "Could not find an appropriate key...".
|
|
29
|
+
*/
|
|
30
|
+
export declare class KeyStore {
|
|
31
|
+
private collector;
|
|
32
|
+
constructor(collector: KeyCollector);
|
|
33
|
+
verifyJwt(token: string, options: {
|
|
34
|
+
defaultAudiences: string[];
|
|
35
|
+
maxAge: string;
|
|
36
|
+
}): Promise<JwtPayload>;
|
|
37
|
+
private verifyInternal;
|
|
38
|
+
private getCachedKey;
|
|
39
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
import secs from '../util/secs.js';
|
|
3
|
+
import { SUPPORTED_ALGORITHMS } from './KeySpec.js';
|
|
4
|
+
import * as micro from '@journeyapps-platform/micro';
|
|
5
|
+
/**
|
|
6
|
+
* KeyStore to get keys and verify tokens.
|
|
7
|
+
*
|
|
8
|
+
*
|
|
9
|
+
* Similar to micro_auth's KeyStore, but with different caching and error handling.
|
|
10
|
+
*
|
|
11
|
+
* We generally assume that:
|
|
12
|
+
* 1. If we have a key kid matching a JWT kid, that is the correct key.
|
|
13
|
+
* We don't look for other keys, even if there are algorithm or other issues.
|
|
14
|
+
* 2. Otherwise, iterate through "wildcard" keys and look for a matching signature.
|
|
15
|
+
* Wildcard keys are any key defined without a kid.
|
|
16
|
+
*
|
|
17
|
+
* # Security considerations
|
|
18
|
+
*
|
|
19
|
+
* Some places for security holes:
|
|
20
|
+
* 1. Signature verification not done correctly: We rely on jose.jwtVerify() to do this correctly.
|
|
21
|
+
* 2. Using a key that has been revoked - see CachedKeyCollector's refresh strategy.
|
|
22
|
+
* 3. Using a key for the wrong purpose (e.g. key.use != 'sig'). Checked in RemoteJWKSCollector.
|
|
23
|
+
* 4. Not checking all attributes, e.g. a JWT trusted by the global firebase key, but has the wrong aud. Correct aud must be configured.
|
|
24
|
+
* 5. Using the incorrect algorithm, e.g. 'none', or using public key as a shared key.
|
|
25
|
+
* We check the algorithm for each JWT against the matching key's configured algorithm or algorithm family.
|
|
26
|
+
*
|
|
27
|
+
* # Errors
|
|
28
|
+
*
|
|
29
|
+
* If we have a matching kid, we can generally get a detailed error (e.g. signature verification failed, invalid algorithm, etc).
|
|
30
|
+
* If we don't have a matching kid, we'll generally just get an error "Could not find an appropriate key...".
|
|
31
|
+
*/
|
|
32
|
+
export class KeyStore {
|
|
33
|
+
constructor(collector) {
|
|
34
|
+
this.collector = collector;
|
|
35
|
+
}
|
|
36
|
+
async verifyJwt(token, options) {
|
|
37
|
+
const { result, keyOptions } = await this.verifyInternal(token, {
|
|
38
|
+
// audience is not checked here, since we vary the allowed audience based on the key
|
|
39
|
+
// audience: options.defaultAudiences,
|
|
40
|
+
clockTolerance: 60,
|
|
41
|
+
// More specific algorithm checking is done when selecting the key to use.
|
|
42
|
+
algorithms: SUPPORTED_ALGORITHMS,
|
|
43
|
+
requiredClaims: ['aud', 'sub', 'iat', 'exp']
|
|
44
|
+
});
|
|
45
|
+
let audiences = options.defaultAudiences;
|
|
46
|
+
if (keyOptions.requiresAudience) {
|
|
47
|
+
// Replace the audience, don't add
|
|
48
|
+
audiences = keyOptions.requiresAudience;
|
|
49
|
+
}
|
|
50
|
+
const tokenPayload = result.payload;
|
|
51
|
+
let aud = tokenPayload.aud;
|
|
52
|
+
if (!Array.isArray(aud)) {
|
|
53
|
+
aud = [aud];
|
|
54
|
+
}
|
|
55
|
+
if (!aud.some((a) => {
|
|
56
|
+
return audiences.includes(a);
|
|
57
|
+
})) {
|
|
58
|
+
throw new jose.errors.JWTClaimValidationFailed('unexpected "aud" claim value', 'aud', 'check_failed');
|
|
59
|
+
}
|
|
60
|
+
const tokenDuration = tokenPayload.exp - tokenPayload.iat;
|
|
61
|
+
// Implement our own maxAge validation, that rejects the token immediately if expiration
|
|
62
|
+
// is too far into the future.
|
|
63
|
+
const maxAge = keyOptions.maxLifetimeSeconds ?? secs(options.maxAge);
|
|
64
|
+
if (tokenDuration > maxAge) {
|
|
65
|
+
throw new jose.errors.JWTInvalid(`Token must expire in a maximum of ${maxAge} seconds, got ${tokenDuration}`);
|
|
66
|
+
}
|
|
67
|
+
const parameters = tokenPayload.parameters;
|
|
68
|
+
if (parameters != null && (Array.isArray(parameters) || typeof parameters != 'object')) {
|
|
69
|
+
throw new jose.errors.JWTInvalid('parameters must be an object');
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
...tokenPayload,
|
|
73
|
+
parameters: {
|
|
74
|
+
user_id: tokenPayload.sub,
|
|
75
|
+
...parameters
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async verifyInternal(token, options) {
|
|
80
|
+
let keyOptions = undefined;
|
|
81
|
+
const result = await jose.jwtVerify(token, async (header) => {
|
|
82
|
+
let key = await this.getCachedKey(token, header);
|
|
83
|
+
keyOptions = key.options;
|
|
84
|
+
return key.key;
|
|
85
|
+
}, options);
|
|
86
|
+
return { result, keyOptions: keyOptions };
|
|
87
|
+
}
|
|
88
|
+
async getCachedKey(token, header) {
|
|
89
|
+
const kid = header.kid;
|
|
90
|
+
const { keys, errors } = await this.collector.getKeys();
|
|
91
|
+
if (kid) {
|
|
92
|
+
// key has kid: JWK with exact kid, or JWK without kid
|
|
93
|
+
// key without kid: JWK without kid only
|
|
94
|
+
for (let key of keys) {
|
|
95
|
+
if (key.kid == kid) {
|
|
96
|
+
if (!key.matchesAlgorithm(header.alg)) {
|
|
97
|
+
throw new jose.errors.JOSEAlgNotAllowed(`Unexpected token algorithm ${header.alg}`);
|
|
98
|
+
}
|
|
99
|
+
return key;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (let key of keys) {
|
|
104
|
+
// Checks signature and algorithm
|
|
105
|
+
if (key.kid != null) {
|
|
106
|
+
// Not a wildcard key
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (!key.matchesAlgorithm(header.alg)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (await key.isValidSignature(token)) {
|
|
113
|
+
return key;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (errors.length > 0) {
|
|
117
|
+
throw errors[0];
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// No key found
|
|
121
|
+
// Trigger refresh of the keys - might be ready by the next request.
|
|
122
|
+
this.collector.noKeyFound?.().catch((e) => {
|
|
123
|
+
// Typically this error would be stored on the collector.
|
|
124
|
+
// This is just a last resort error handling.
|
|
125
|
+
micro.logger.error(`Failed to refresh keys`, e);
|
|
126
|
+
});
|
|
127
|
+
throw new jose.errors.JOSEError('Could not find an appropriate key in the keystore. The key is missing or no key matched the token KID');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=KeyStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyStore.js","sourceRoot":"","sources":["../../src/auth/KeyStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variation on a leaky bucket rate limiter.
|
|
3
|
+
*
|
|
4
|
+
* The base is a leaky bucket:
|
|
5
|
+
* * The bucket is filled at a certain rate
|
|
6
|
+
* * The bucket has a max capacity
|
|
7
|
+
*
|
|
8
|
+
* This gives an initial burst capacity, then continues
|
|
9
|
+
* at the refill rate when the burst capacity is depleted.
|
|
10
|
+
*
|
|
11
|
+
* This variation introduces an additional quadratic delay
|
|
12
|
+
* during the initial burst, which prevents the burst capacity
|
|
13
|
+
* from being consumed immediately. The steady-state rate
|
|
14
|
+
* is still the same.
|
|
15
|
+
*
|
|
16
|
+
*
|
|
17
|
+
* Steady state rate: x requests / ms.
|
|
18
|
+
* Steady state period: 1 / rate
|
|
19
|
+
*
|
|
20
|
+
* capacity: number of requests before steady state
|
|
21
|
+
*
|
|
22
|
+
* capacity == max_capacity: no delay between requests
|
|
23
|
+
* capacity = 0: delay = Steady state period
|
|
24
|
+
*
|
|
25
|
+
* variable_delay = (max_capacity - capacity)^2 / max_capacity^2 * period
|
|
26
|
+
*/
|
|
27
|
+
export declare class LeakyBucket {
|
|
28
|
+
private capacity;
|
|
29
|
+
private lastRequest;
|
|
30
|
+
private maxCapacity;
|
|
31
|
+
private periodMs;
|
|
32
|
+
lastGrantedRequest: number;
|
|
33
|
+
constructor(options: {
|
|
34
|
+
maxCapacity: number;
|
|
35
|
+
periodMs: number;
|
|
36
|
+
});
|
|
37
|
+
allowed(): boolean;
|
|
38
|
+
reset(): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variation on a leaky bucket rate limiter.
|
|
3
|
+
*
|
|
4
|
+
* The base is a leaky bucket:
|
|
5
|
+
* * The bucket is filled at a certain rate
|
|
6
|
+
* * The bucket has a max capacity
|
|
7
|
+
*
|
|
8
|
+
* This gives an initial burst capacity, then continues
|
|
9
|
+
* at the refill rate when the burst capacity is depleted.
|
|
10
|
+
*
|
|
11
|
+
* This variation introduces an additional quadratic delay
|
|
12
|
+
* during the initial burst, which prevents the burst capacity
|
|
13
|
+
* from being consumed immediately. The steady-state rate
|
|
14
|
+
* is still the same.
|
|
15
|
+
*
|
|
16
|
+
*
|
|
17
|
+
* Steady state rate: x requests / ms.
|
|
18
|
+
* Steady state period: 1 / rate
|
|
19
|
+
*
|
|
20
|
+
* capacity: number of requests before steady state
|
|
21
|
+
*
|
|
22
|
+
* capacity == max_capacity: no delay between requests
|
|
23
|
+
* capacity = 0: delay = Steady state period
|
|
24
|
+
*
|
|
25
|
+
* variable_delay = (max_capacity - capacity)^2 / max_capacity^2 * period
|
|
26
|
+
*/
|
|
27
|
+
export class LeakyBucket {
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.capacity = options.maxCapacity;
|
|
30
|
+
this.maxCapacity = options.maxCapacity;
|
|
31
|
+
this.periodMs = options.periodMs;
|
|
32
|
+
this.lastRequest = 0;
|
|
33
|
+
this.lastGrantedRequest = 0;
|
|
34
|
+
}
|
|
35
|
+
allowed() {
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const elapsed = now - this.lastRequest;
|
|
38
|
+
const leaked = elapsed / this.periodMs;
|
|
39
|
+
this.capacity = Math.min(this.capacity + leaked, this.maxCapacity);
|
|
40
|
+
this.lastRequest = now;
|
|
41
|
+
const capacityUsed = this.maxCapacity - this.capacity;
|
|
42
|
+
const variableDelay = ((capacityUsed * capacityUsed) / (this.maxCapacity * this.maxCapacity)) * this.periodMs;
|
|
43
|
+
if (this.capacity >= 1 && variableDelay <= now - this.lastGrantedRequest) {
|
|
44
|
+
this.capacity -= 1;
|
|
45
|
+
this.lastGrantedRequest = now;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
reset() {
|
|
53
|
+
this.capacity = this.maxCapacity;
|
|
54
|
+
this.lastGrantedRequest = 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=LeakyBucket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LeakyBucket.js","sourceRoot":"","sources":["../../src/auth/LeakyBucket.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,OAAO,WAAW;IAQtB,YAAY,OAAkD;QAC5D,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC;QACvC,MAAM,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;QACvC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACnE,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QAEvB,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC;QACtD,MAAM,aAAa,GAAG,CAAC,CAAC,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QAE9G,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,aAAa,IAAI,GAAG,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACzE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,IAAI,CAAC,kBAAkB,GAAG,GAAG,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;aAAM,CAAC;YACN,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC;QACjC,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;IAC9B,CAAC;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
+
import * as https from 'https';
|
|
4
|
+
import * as http from 'http';
|
|
5
|
+
import { KeyCollector, KeyResult } from './KeyCollector.js';
|
|
6
|
+
export type RemoteJWKSCollectorOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Blocks IP Ranges from the BLOCKED_IP_RANGES array
|
|
9
|
+
*/
|
|
10
|
+
block_local_ip?: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Set of keys fetched from JWKS URI.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RemoteJWKSCollector implements KeyCollector {
|
|
16
|
+
protected options?: RemoteJWKSCollectorOptions | undefined;
|
|
17
|
+
private url;
|
|
18
|
+
constructor(url: string, options?: RemoteJWKSCollectorOptions | undefined);
|
|
19
|
+
getKeys(): Promise<KeyResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve IP, and check that it is in an allowed range.
|
|
22
|
+
*/
|
|
23
|
+
resolveAgent(): Promise<http.Agent | https.Agent>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as https from 'https';
|
|
2
|
+
import * as http from 'http';
|
|
3
|
+
import * as dns from 'dns/promises';
|
|
4
|
+
import ip from 'ipaddr.js';
|
|
5
|
+
import * as jose from 'jose';
|
|
6
|
+
import * as net from 'net';
|
|
7
|
+
import fetch from 'node-fetch';
|
|
8
|
+
import { KeySpec } from './KeySpec.js';
|
|
9
|
+
/**
|
|
10
|
+
* Set of keys fetched from JWKS URI.
|
|
11
|
+
*/
|
|
12
|
+
export class RemoteJWKSCollector {
|
|
13
|
+
constructor(url, options) {
|
|
14
|
+
this.options = options;
|
|
15
|
+
try {
|
|
16
|
+
this.url = new URL(url);
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
throw new Error(`Invalid jwks_uri: ${url}`);
|
|
20
|
+
}
|
|
21
|
+
// We do support http here for self-hosting use cases.
|
|
22
|
+
// Management service restricts this to https for hosted versions.
|
|
23
|
+
if (this.url.protocol != 'https:' && this.url.protocol != 'http:') {
|
|
24
|
+
throw new Error(`Only http(s) is supported for jwks_uri, got: ${url}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async getKeys() {
|
|
28
|
+
const abortController = new AbortController();
|
|
29
|
+
const timeout = setTimeout(() => {
|
|
30
|
+
abortController.abort();
|
|
31
|
+
}, 30000);
|
|
32
|
+
const res = await fetch(this.url, {
|
|
33
|
+
method: 'GET',
|
|
34
|
+
headers: {
|
|
35
|
+
Accept: 'application/json'
|
|
36
|
+
},
|
|
37
|
+
signal: abortController.signal,
|
|
38
|
+
agent: await this.resolveAgent()
|
|
39
|
+
});
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
throw new jose.errors.JWKSInvalid(`JWKS request failed with ${res.statusText}`);
|
|
42
|
+
}
|
|
43
|
+
const data = (await res.json());
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
// https://github.com/panva/jose/blob/358e864a0cccf1e0f9928a959f91f18f3f06a7de/src/jwks/local.ts#L36
|
|
46
|
+
if (data.keys == null ||
|
|
47
|
+
!Array.isArray(data.keys) ||
|
|
48
|
+
!data.keys.every((key) => typeof key == 'object' && !Array.isArray(key))) {
|
|
49
|
+
return { keys: [], errors: [new jose.errors.JWKSInvalid(`No keys in found in JWKS response`)] };
|
|
50
|
+
}
|
|
51
|
+
let keys = [];
|
|
52
|
+
for (let keyData of data.keys) {
|
|
53
|
+
if (keyData.kty != 'RSA') {
|
|
54
|
+
// Only RSA public keys supported here
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (typeof keyData.use == 'string') {
|
|
58
|
+
if (keyData.use != 'sig') {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (Array.isArray(keyData.key_ops)) {
|
|
63
|
+
if (!keyData.key_ops.includes('verify')) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const key = await KeySpec.importKey(keyData);
|
|
68
|
+
keys.push(key);
|
|
69
|
+
}
|
|
70
|
+
return { keys: keys, errors: [] };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve IP, and check that it is in an allowed range.
|
|
74
|
+
*/
|
|
75
|
+
async resolveAgent() {
|
|
76
|
+
const hostname = this.url.hostname;
|
|
77
|
+
let resolved_ip;
|
|
78
|
+
if (net.isIPv6(hostname)) {
|
|
79
|
+
throw new Error('IPv6 not supported yet');
|
|
80
|
+
}
|
|
81
|
+
else if (net.isIPv4(hostname)) {
|
|
82
|
+
// All good
|
|
83
|
+
resolved_ip = hostname;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
resolved_ip = (await dns.resolve4(hostname))[0];
|
|
87
|
+
}
|
|
88
|
+
const parsed = ip.parse(resolved_ip);
|
|
89
|
+
if (parsed.kind() != 'ipv4' || (this.options?.block_local_ip && parsed.range() !== 'unicast')) {
|
|
90
|
+
// Do not connect to any reserved IPs, including loopback and private ranges
|
|
91
|
+
throw new Error(`IPs in this range are not supported: ${resolved_ip}`);
|
|
92
|
+
}
|
|
93
|
+
const options = {
|
|
94
|
+
// This is the host that the agent connects to
|
|
95
|
+
host: resolved_ip
|
|
96
|
+
};
|
|
97
|
+
switch (this.url.protocol) {
|
|
98
|
+
case 'http:':
|
|
99
|
+
return new http.Agent(options);
|
|
100
|
+
case 'https:':
|
|
101
|
+
return new https.Agent(options);
|
|
102
|
+
}
|
|
103
|
+
throw new Error('http or or https is required for protocol');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=RemoteJWKSCollector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RemoteJWKSCollector.js","sourceRoot":"","sources":["../../src/auth/RemoteJWKSCollector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
import { KeySpec } from './KeySpec.js';
|
|
3
|
+
import { KeyCollector, KeyResult } from './KeyCollector.js';
|
|
4
|
+
/**
|
|
5
|
+
* Set of static keys.
|
|
6
|
+
*
|
|
7
|
+
* A key can be added both with and without a kid, in case wildcard matching is desired.
|
|
8
|
+
*/
|
|
9
|
+
export declare class StaticKeyCollector implements KeyCollector {
|
|
10
|
+
private keys;
|
|
11
|
+
static importKeys(keys: jose.JWK[]): Promise<StaticKeyCollector>;
|
|
12
|
+
constructor(keys: KeySpec[]);
|
|
13
|
+
getKeys(): Promise<KeyResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { KeySpec } from './KeySpec.js';
|
|
2
|
+
/**
|
|
3
|
+
* Set of static keys.
|
|
4
|
+
*
|
|
5
|
+
* A key can be added both with and without a kid, in case wildcard matching is desired.
|
|
6
|
+
*/
|
|
7
|
+
export class StaticKeyCollector {
|
|
8
|
+
static async importKeys(keys) {
|
|
9
|
+
const parsedKeys = await Promise.all(keys.map((key) => KeySpec.importKey(key)));
|
|
10
|
+
return new StaticKeyCollector(parsedKeys);
|
|
11
|
+
}
|
|
12
|
+
constructor(keys) {
|
|
13
|
+
this.keys = keys;
|
|
14
|
+
}
|
|
15
|
+
async getKeys() {
|
|
16
|
+
return { keys: this.keys, errors: [] };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=StaticKeyCollector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StaticKeyCollector.js","sourceRoot":"","sources":["../../src/auth/StaticKeyCollector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IAC7B,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAgB;QACtC,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAChF,OAAO,IAAI,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;IAED,YAAoB,IAAe;QAAf,SAAI,GAAJ,IAAI,CAAW;IAAG,CAAC;IAEvC,KAAK,CAAC,OAAO;QACX,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACzC,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
import { KeyCollector } from './KeyCollector.js';
|
|
3
|
+
import { KeySpec } from './KeySpec.js';
|
|
4
|
+
import { ResolvedConnection } from '../util/config/types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Fetches key from the Supabase database.
|
|
7
|
+
*
|
|
8
|
+
* Unfortunately, despite the JWTs containing a kid, we have no way to lookup that kid
|
|
9
|
+
* before receiving a valid token.
|
|
10
|
+
*/
|
|
11
|
+
export declare class SupabaseKeyCollector implements KeyCollector {
|
|
12
|
+
private pool;
|
|
13
|
+
private keyOptions;
|
|
14
|
+
constructor(connection: ResolvedConnection);
|
|
15
|
+
getKeys(): Promise<{
|
|
16
|
+
keys: never[];
|
|
17
|
+
errors: jose.errors.JWKSNoMatchingKey[];
|
|
18
|
+
} | {
|
|
19
|
+
keys: KeySpec[];
|
|
20
|
+
errors: never[];
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
import { connectPgWirePool, pgwireRows } from '@powersync/service-jpgwire';
|
|
3
|
+
import { KeySpec } from './KeySpec.js';
|
|
4
|
+
import { retriedQuery } from '../util/pgwire_utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Fetches key from the Supabase database.
|
|
7
|
+
*
|
|
8
|
+
* Unfortunately, despite the JWTs containing a kid, we have no way to lookup that kid
|
|
9
|
+
* before receiving a valid token.
|
|
10
|
+
*/
|
|
11
|
+
export class SupabaseKeyCollector {
|
|
12
|
+
constructor(connection) {
|
|
13
|
+
this.keyOptions = {
|
|
14
|
+
requiresAudience: ['authenticated'],
|
|
15
|
+
maxLifetimeSeconds: 86400 * 7 + 1200 // 1 week + 20 minutes margin
|
|
16
|
+
};
|
|
17
|
+
this.pool = connectPgWirePool(connection, {
|
|
18
|
+
// To avoid overloading the source database with open connections,
|
|
19
|
+
// limit to a single connection, and close the connection shortly
|
|
20
|
+
// after using it.
|
|
21
|
+
idleTimeout: 5000,
|
|
22
|
+
maxSize: 1
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async getKeys() {
|
|
26
|
+
let row;
|
|
27
|
+
try {
|
|
28
|
+
const rows = pgwireRows(await retriedQuery(this.pool, `SELECT current_setting('app.settings.jwt_secret') as jwt_secret`));
|
|
29
|
+
row = rows[0];
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
if (e.message?.includes('unrecognized configuration parameter')) {
|
|
33
|
+
throw new jose.errors.JOSEError(`Generate a new JWT secret on Supabase. Cause: ${e.message}`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw e;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const secret = row?.jwt_secret;
|
|
40
|
+
if (secret == null) {
|
|
41
|
+
return {
|
|
42
|
+
keys: [],
|
|
43
|
+
errors: [new jose.errors.JWKSNoMatchingKey()]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const key = {
|
|
48
|
+
kty: 'oct',
|
|
49
|
+
alg: 'HS256',
|
|
50
|
+
// While the secret is valid base64, the base64-encoded form is the secret value.
|
|
51
|
+
k: Buffer.from(secret, 'utf8').toString('base64url')
|
|
52
|
+
};
|
|
53
|
+
const imported = await KeySpec.importKey(key, this.keyOptions);
|
|
54
|
+
return {
|
|
55
|
+
keys: [imported],
|
|
56
|
+
errors: []
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=SupabaseKeyCollector.js.map
|