@friggframework/core 2.0.0-next.11 → 2.0.0-next.110
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/CLAUDE.md +702 -0
- package/README.md +999 -50
- package/admin-scripts/index.js +52 -0
- package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
- package/admin-scripts/repositories/admin-script-execution-repository-interface.js +171 -0
- package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +238 -0
- package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +278 -0
- package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
- package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
- package/application/commands/README.md +451 -0
- package/application/commands/admin-script-commands.js +359 -0
- package/application/commands/credential-commands.js +262 -0
- package/application/commands/entity-commands.js +336 -0
- package/application/commands/integration-commands.js +356 -0
- package/application/commands/integration-mapping-commands.js +25 -0
- package/application/commands/process-commands.js +135 -0
- package/application/commands/report-commands.js +188 -0
- package/application/commands/scheduler-commands.js +263 -0
- package/application/commands/usage-commands.js +56 -0
- package/application/commands/user-commands.js +312 -0
- package/application/index.js +83 -0
- package/artifacts/repositories/artifact-repository-factory.js +19 -0
- package/artifacts/repositories/artifact-repository-interface.js +27 -0
- package/artifacts/repositories/artifact-repository-local.js +42 -0
- package/artifacts/repositories/artifact-repository-s3.js +61 -0
- package/assertions/index.js +0 -3
- package/core/CLAUDE.md +700 -0
- package/core/Worker.js +60 -24
- package/core/create-handler.js +189 -5
- package/core/parameters-to-env.js +257 -0
- package/core/ssm-preload.mjs +34 -0
- package/credential/repositories/credential-active-type.js +32 -0
- package/credential/repositories/credential-repository-documentdb.js +355 -0
- package/credential/repositories/credential-repository-factory.js +54 -0
- package/credential/repositories/credential-repository-interface.js +113 -0
- package/credential/repositories/credential-repository-mongo.js +294 -0
- package/credential/repositories/credential-repository-postgres.js +312 -0
- package/credential/repositories/credential-repository.js +300 -0
- package/credential/use-cases/get-credential-for-user.js +25 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +154 -0
- package/database/documentdb-encryption-service.js +330 -0
- package/database/documentdb-utils.js +192 -0
- package/database/encryption/README.md +839 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +401 -0
- package/database/encryption/field-encryption-service.js +254 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/prisma-encryption-extension.js +230 -0
- package/database/index.js +21 -21
- package/database/prisma.js +182 -0
- package/database/repositories/health-check-repository-documentdb.js +138 -0
- package/database/repositories/health-check-repository-factory.js +48 -0
- package/database/repositories/health-check-repository-interface.js +82 -0
- package/database/repositories/health-check-repository-mongodb.js +89 -0
- package/database/repositories/health-check-repository-postgres.js +82 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-health-use-case.js +29 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +83 -0
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/resolve-migration-via-worker-use-case.js +49 -0
- package/database/use-cases/run-database-migration-use-case.js +139 -0
- package/database/use-cases/test-encryption-use-case.js +253 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +94 -0
- package/database/utils/mongodb-schema-init.js +108 -0
- package/database/utils/prisma-runner.js +491 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
- package/encrypt/Cryptor.js +34 -168
- package/encrypt/index.js +1 -2
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +15 -7
- package/errors/index.js +2 -0
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +5 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +5 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +386 -0
- package/generated/prisma-mongodb/index-browser.js +368 -0
- package/generated/prisma-mongodb/index.d.ts +27065 -0
- package/generated/prisma-mongodb/index.js +411 -0
- package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
- package/generated/prisma-mongodb/runtime/edge.js +35 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
- package/generated/prisma-mongodb/runtime/library.js +147 -0
- package/generated/prisma-mongodb/runtime/react-native.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-mongodb/schema.prisma +455 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +393 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +5 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +5 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +407 -0
- package/generated/prisma-postgresql/index-browser.js +389 -0
- package/generated/prisma-postgresql/index.d.ts +29904 -0
- package/generated/prisma-postgresql/index.js +432 -0
- package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
- package/generated/prisma-postgresql/runtime/edge.js +35 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
- package/generated/prisma-postgresql/runtime/library.js +147 -0
- package/generated/prisma-postgresql/runtime/react-native.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-postgresql/schema.prisma +436 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +414 -0
- package/handlers/WEBHOOKS.md +653 -0
- package/handlers/app-definition-loader.js +67 -0
- package/handlers/app-handler-helpers.js +3 -3
- package/handlers/backend-utils.js +271 -42
- package/handlers/database-migration-handler.js +227 -0
- package/handlers/integration-event-dispatcher.js +68 -0
- package/handlers/middleware/admin-auth.js +73 -0
- package/handlers/routers/HEALTHCHECK.md +342 -0
- package/handlers/routers/auth.js +4 -15
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +329 -0
- package/handlers/routers/health.js +518 -0
- package/handlers/routers/integration-defined-routers.js +85 -10
- package/handlers/routers/integration-webhook-routers.js +80 -0
- package/handlers/routers/user.js +27 -5
- package/handlers/routers/websocket.js +5 -3
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
- package/handlers/workers/db-migration.js +427 -0
- package/handlers/workers/dlq-processor.js +63 -0
- package/handlers/workers/integration-defined-workers.js +13 -7
- package/index.js +102 -46
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
- package/infrastructure/scheduler/index.js +33 -0
- package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
- package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
- package/integrations/EXTENSIONS.md +240 -0
- package/integrations/WEBHOOK-QUICKSTART.md +151 -0
- package/integrations/extension.js +254 -0
- package/integrations/index.js +20 -10
- package/integrations/integration-base.js +730 -74
- package/integrations/integration-router.js +398 -180
- package/integrations/options.js +1 -1
- package/integrations/repositories/config-patch-shared.js +43 -0
- package/integrations/repositories/integration-mapping-repository-documentdb.js +303 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +120 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +183 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +255 -0
- package/integrations/repositories/integration-mapping-repository.js +156 -0
- package/integrations/repositories/integration-repository-documentdb.js +322 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +189 -0
- package/integrations/repositories/integration-repository-mongo.js +450 -0
- package/integrations/repositories/integration-repository-postgres.js +492 -0
- package/integrations/repositories/process-repository-documentdb.js +311 -0
- package/integrations/repositories/process-repository-factory.js +53 -0
- package/integrations/repositories/process-repository-interface.js +136 -0
- package/integrations/repositories/process-repository-mongo.js +262 -0
- package/integrations/repositories/process-repository-postgres.js +380 -0
- package/integrations/repositories/process-update-ops-shared.js +112 -0
- package/integrations/repositories/report-id.js +13 -0
- package/integrations/tests/doubles/config-capturing-integration.js +81 -0
- package/integrations/tests/doubles/dummy-integration-class.js +113 -0
- package/integrations/tests/doubles/test-integration-repository.js +146 -0
- package/integrations/use-cases/create-integration.js +215 -0
- package/integrations/use-cases/create-process.js +130 -0
- package/integrations/use-cases/delete-integration-for-user.js +119 -0
- package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
- package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
- package/integrations/use-cases/get-integration-for-user.js +78 -0
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +83 -0
- package/integrations/use-cases/get-integrations-for-user.js +88 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/get-process.js +89 -0
- package/integrations/use-cases/index.js +19 -0
- package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
- package/integrations/use-cases/load-integration-context.js +71 -0
- package/integrations/use-cases/patch-integration-config.js +39 -0
- package/integrations/use-cases/process-errors.js +28 -0
- package/integrations/use-cases/update-integration-config.js +32 -0
- package/integrations/use-cases/update-integration-messages.js +44 -0
- package/integrations/use-cases/update-integration-status.js +32 -0
- package/integrations/use-cases/update-integration.js +92 -0
- package/integrations/use-cases/update-process-metrics.js +222 -0
- package/integrations/use-cases/update-process-state.js +163 -0
- package/integrations/utils/map-integration-dto.js +37 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/logs/logger.js +0 -4
- package/{module-plugin → modules}/index.js +0 -10
- package/modules/module-factory.js +56 -0
- package/modules/module.js +307 -0
- package/modules/repositories/module-repository-documentdb.js +350 -0
- package/modules/repositories/module-repository-factory.js +40 -0
- package/modules/repositories/module-repository-interface.js +145 -0
- package/modules/repositories/module-repository-mongo.js +436 -0
- package/modules/repositories/module-repository-postgres.js +481 -0
- package/modules/repositories/module-repository.js +369 -0
- package/modules/requester/api-key.js +52 -0
- package/modules/requester/oauth-2.js +555 -0
- package/modules/requester/requester.js +542 -0
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +39 -0
- package/modules/use-cases/get-entities-for-user.js +32 -0
- package/modules/use-cases/get-entity-options-by-id.js +71 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +34 -0
- package/modules/use-cases/get-module.js +74 -0
- package/modules/use-cases/process-authorization-callback.js +243 -0
- package/modules/use-cases/refresh-entity-options.js +72 -0
- package/modules/use-cases/test-module-auth.js +72 -0
- package/modules/utils/map-module-dto.js +18 -0
- package/package.json +92 -50
- package/prisma-mongodb/schema.prisma +455 -0
- package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
- package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
- package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
- package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
- package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
- package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
- package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
- package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
- package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
- package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +26 -0
- package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +436 -0
- package/queues/queuer-util.js +103 -21
- package/reporting/README.md +154 -0
- package/reporting/builtin-reports.js +6 -0
- package/reporting/index.js +13 -0
- package/reporting/report-base.js +49 -0
- package/reporting/reports/integrations-report.js +221 -0
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-documentdb.js +240 -0
- package/syncs/repositories/sync-repository-factory.js +43 -0
- package/syncs/repositories/sync-repository-interface.js +109 -0
- package/syncs/repositories/sync-repository-mongo.js +239 -0
- package/syncs/repositories/sync-repository-postgres.js +319 -0
- package/syncs/sync.js +0 -1
- package/telemetry/README.md +331 -0
- package/telemetry/bind-telemetry-context.js +73 -0
- package/telemetry/canonical-counters.js +52 -0
- package/telemetry/exporters.js +85 -0
- package/telemetry/index.js +26 -0
- package/telemetry/instrument-handler.js +87 -0
- package/telemetry/no-op-telemetry.js +67 -0
- package/telemetry/north-star.js +103 -0
- package/telemetry/otel-telemetry.js +213 -0
- package/telemetry/plugin-subscribers.js +77 -0
- package/telemetry/telemetry-config.js +120 -0
- package/telemetry/telemetry-context.js +40 -0
- package/telemetry/telemetry-event-bus.js +58 -0
- package/telemetry/telemetry-runtime.js +147 -0
- package/telemetry/telemetry-service.js +51 -0
- package/telemetry/usage-rollup-subscriber.js +116 -0
- package/token/repositories/token-repository-documentdb.js +137 -0
- package/token/repositories/token-repository-factory.js +40 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +219 -0
- package/token/repositories/token-repository-postgres.js +264 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/associations/index.d.ts +0 -17
- package/types/core/index.d.ts +12 -4
- package/types/database/index.d.ts +10 -2
- package/types/encrypt/index.d.ts +5 -3
- package/types/integrations/index.d.ts +3 -8
- package/types/module-plugin/index.d.ts +20 -69
- package/types/syncs/index.d.ts +0 -17
- package/usage/README.md +54 -0
- package/usage/index.js +17 -0
- package/usage/repositories/usage-repository-documentdb.js +194 -0
- package/usage/repositories/usage-repository-factory.js +25 -0
- package/usage/repositories/usage-repository-interface.js +37 -0
- package/usage/repositories/usage-repository-prisma.js +146 -0
- package/usage/tracked-metrics.js +38 -0
- package/usage/usage-windows.js +24 -0
- package/user/repositories/user-repository-documentdb.js +458 -0
- package/user/repositories/user-repository-factory.js +52 -0
- package/user/repositories/user-repository-interface.js +214 -0
- package/user/repositories/user-repository-mongo.js +323 -0
- package/user/repositories/user-repository-postgres.js +377 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/use-cases/authenticate-user.js +127 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -0
- package/user/use-cases/create-individual-user.js +61 -0
- package/user/use-cases/create-organization-user.js +47 -0
- package/user/use-cases/create-token-for-user-id.js +30 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-bearer-token.js +77 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +125 -0
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
- package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
- package/websocket/repositories/websocket-connection-repository.js +161 -0
- package/assertions/is-equal.js +0 -17
- package/associations/model.js +0 -54
- package/database/models/IndividualUser.js +0 -76
- package/database/models/OrganizationUser.js +0 -29
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/models/UserModel.js +0 -7
- package/database/models/WebsocketConnection.js +0 -49
- package/database/mongo.js +0 -45
- package/database/mongoose.js +0 -5
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -132
- package/encrypt/encrypt.test.js +0 -1069
- package/encrypt/test-encrypt.js +0 -107
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- package/handlers/routers/middleware/loadUser.js +0 -15
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
- package/integrations/create-frigg-backend.js +0 -31
- package/integrations/integration-factory.js +0 -251
- package/integrations/integration-mapping.js +0 -43
- package/integrations/integration-model.js +0 -46
- package/integrations/integration-user.js +0 -144
- package/integrations/test/integration-base.test.js +0 -144
- package/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- package/module-plugin/auther.js +0 -393
- package/module-plugin/credential.js +0 -22
- package/module-plugin/entity-manager.js +0 -70
- package/module-plugin/entity.js +0 -46
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/requester/api-key.js +0 -36
- package/module-plugin/requester/oauth-2.js +0 -219
- package/module-plugin/requester/requester.js +0 -165
- package/module-plugin/requester/requester.test.js +0 -28
- package/module-plugin/test/auther.test.js +0 -97
- package/syncs/model.js +0 -62
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
const { Requester } = require('./requester');
|
|
2
|
+
const { get } = require('../../assertions');
|
|
3
|
+
const { ModuleConstants } = require('../ModuleConstants');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* OAuth 2.0 Requester - Base class for API modules using OAuth 2.0 authentication.
|
|
7
|
+
*
|
|
8
|
+
* Supports multiple OAuth 2.0 grant types:
|
|
9
|
+
* - `authorization_code` (default): Standard OAuth flow with user consent
|
|
10
|
+
* - `client_credentials`: Server-to-server authentication without user
|
|
11
|
+
* - `password`: Resource Owner Password Credentials grant
|
|
12
|
+
*
|
|
13
|
+
* @extends Requester
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Authorization Code flow (default)
|
|
17
|
+
* const api = new MyApi({ grant_type: 'authorization_code' });
|
|
18
|
+
* const authUrl = api.getAuthorizationUri();
|
|
19
|
+
* // After user authorizes...
|
|
20
|
+
* await api.getTokenFromCode(code);
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Client Credentials flow
|
|
24
|
+
* const api = new MyApi({
|
|
25
|
+
* grant_type: 'client_credentials',
|
|
26
|
+
* client_id: process.env.CLIENT_ID,
|
|
27
|
+
* client_secret: process.env.CLIENT_SECRET,
|
|
28
|
+
* audience: 'https://api.example.com',
|
|
29
|
+
* });
|
|
30
|
+
* await api.getTokenFromClientCredentials();
|
|
31
|
+
*/
|
|
32
|
+
class OAuth2Requester extends Requester {
|
|
33
|
+
static requesterType = ModuleConstants.authType.oauth2;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates an OAuth2Requester instance.
|
|
37
|
+
*
|
|
38
|
+
* @param {Object} params - Configuration parameters
|
|
39
|
+
* @param {string} [params.grant_type='authorization_code'] - OAuth grant type:
|
|
40
|
+
* 'authorization_code', 'client_credentials', or 'password'
|
|
41
|
+
* @param {string} [params.client_id] - OAuth client ID
|
|
42
|
+
* @param {string} [params.client_secret] - OAuth client secret
|
|
43
|
+
* @param {string} [params.redirect_uri] - OAuth redirect URI for authorization code flow
|
|
44
|
+
* @param {string} [params.scope] - OAuth scopes (space-separated)
|
|
45
|
+
* @param {string} [params.authorizationUri] - Authorization endpoint URL
|
|
46
|
+
* @param {string} [params.tokenUri] - Token endpoint URL for exchanging codes/credentials
|
|
47
|
+
* @param {string} [params.baseURL] - Base URL for API requests
|
|
48
|
+
* @param {string} [params.access_token] - Existing access token
|
|
49
|
+
* @param {string} [params.refresh_token] - Existing refresh token
|
|
50
|
+
* @param {Date} [params.accessTokenExpire] - Access token expiration date
|
|
51
|
+
* @param {Date} [params.refreshTokenExpire] - Refresh token expiration date
|
|
52
|
+
* @param {string} [params.audience] - Token audience (for client_credentials)
|
|
53
|
+
* @param {string} [params.username] - Username (for password grant)
|
|
54
|
+
* @param {string} [params.password] - Password (for password grant)
|
|
55
|
+
* @param {string} [params.state] - OAuth state parameter for CSRF protection
|
|
56
|
+
*/
|
|
57
|
+
constructor(params) {
|
|
58
|
+
super(params);
|
|
59
|
+
/** @type {string} Delegate type for token update notifications */
|
|
60
|
+
this.DLGT_TOKEN_UPDATE = 'TOKEN_UPDATE';
|
|
61
|
+
/** @type {string} Delegate type for token deauthorization notifications */
|
|
62
|
+
this.DLGT_TOKEN_DEAUTHORIZED = 'TOKEN_DEAUTHORIZED';
|
|
63
|
+
/**
|
|
64
|
+
* @type {string} Delegate type that asks the Module for the stored
|
|
65
|
+
* credential. The requester can then adopt a concurrent invocation's
|
|
66
|
+
* refresh and does not race it. See _adoptNewerCredential.
|
|
67
|
+
*/
|
|
68
|
+
this.DLGT_CREDENTIAL_RELOAD = 'CREDENTIAL_RELOAD';
|
|
69
|
+
|
|
70
|
+
this.delegateTypes.push(this.DLGT_TOKEN_UPDATE);
|
|
71
|
+
this.delegateTypes.push(this.DLGT_TOKEN_DEAUTHORIZED);
|
|
72
|
+
this.delegateTypes.push(this.DLGT_CREDENTIAL_RELOAD);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Re-read delays after an invalid_grant, in ms. The winner's write
|
|
76
|
+
* can arrive after the loser's rejection (716 ms measured in
|
|
77
|
+
* production). Wait between re-reads before you decide that the
|
|
78
|
+
* credential is dead. Tests can inject other values.
|
|
79
|
+
*/
|
|
80
|
+
this.credentialReloadBackoffMs = params?.credentialReloadBackoffMs ?? [
|
|
81
|
+
500, 1000, 1500,
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
/** @type {string} OAuth grant type */
|
|
85
|
+
this.grant_type = get(params, 'grant_type', 'authorization_code');
|
|
86
|
+
/** @type {string|null} OAuth client ID */
|
|
87
|
+
this.client_id = get(params, 'client_id', null);
|
|
88
|
+
/** @type {string|null} OAuth client secret */
|
|
89
|
+
this.client_secret = get(params, 'client_secret', null);
|
|
90
|
+
/** @type {string|null} OAuth redirect URI */
|
|
91
|
+
this.redirect_uri = get(params, 'redirect_uri', null);
|
|
92
|
+
/** @type {string|null} OAuth scopes */
|
|
93
|
+
this.scope = get(params, 'scope', null);
|
|
94
|
+
/** @type {string|null} Authorization endpoint URL */
|
|
95
|
+
this.authorizationUri = get(params, 'authorizationUri', null);
|
|
96
|
+
/** @type {string|null} Token endpoint URL */
|
|
97
|
+
this.tokenUri = get(params, 'tokenUri', null);
|
|
98
|
+
/** @type {string|null} Base URL for API requests */
|
|
99
|
+
this.baseURL = get(params, 'baseURL', null);
|
|
100
|
+
/** @type {string|null} Current access token */
|
|
101
|
+
this.access_token = get(params, 'access_token', null);
|
|
102
|
+
/** @type {string|null} Current refresh token */
|
|
103
|
+
this.refresh_token = get(params, 'refresh_token', null);
|
|
104
|
+
/** @type {Date|null} Access token expiration */
|
|
105
|
+
this.accessTokenExpire = get(params, 'accessTokenExpire', null);
|
|
106
|
+
/** @type {Date|null} Refresh token expiration */
|
|
107
|
+
this.refreshTokenExpire = get(params, 'refreshTokenExpire', null);
|
|
108
|
+
/** @type {string|null} Token audience */
|
|
109
|
+
this.audience = get(params, 'audience', null);
|
|
110
|
+
/** @type {string|null} Username for password grant */
|
|
111
|
+
this.username = get(params, 'username', null);
|
|
112
|
+
/** @type {string|null} Password for password grant */
|
|
113
|
+
this.password = get(params, 'password', null);
|
|
114
|
+
/** @type {string|null} OAuth state for CSRF protection */
|
|
115
|
+
this.state = get(params, 'state', null);
|
|
116
|
+
|
|
117
|
+
/** @type {boolean} Whether this requester supports token refresh */
|
|
118
|
+
this.isRefreshable = true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Sets OAuth tokens and calculates expiration times.
|
|
123
|
+
* Notifies delegates of token update via DLGT_TOKEN_UPDATE.
|
|
124
|
+
*
|
|
125
|
+
* @param {Object} params - Token response from OAuth server
|
|
126
|
+
* @param {string} params.access_token - The access token
|
|
127
|
+
* @param {string} [params.refresh_token] - The refresh token (if provided)
|
|
128
|
+
* @param {number} [params.expires_in] - Access token lifetime in seconds
|
|
129
|
+
* @param {number} [params.x_refresh_token_expires_in] - Refresh token lifetime in seconds
|
|
130
|
+
* @returns {Promise<void>}
|
|
131
|
+
*/
|
|
132
|
+
async setTokens(params) {
|
|
133
|
+
this.access_token = get(params, 'access_token');
|
|
134
|
+
const newRefreshToken = get(params, 'refresh_token', null);
|
|
135
|
+
if (newRefreshToken !== null) {
|
|
136
|
+
this.refresh_token = newRefreshToken;
|
|
137
|
+
} else {
|
|
138
|
+
if (this.refresh_token) {
|
|
139
|
+
console.log(
|
|
140
|
+
'[Frigg] No refresh_token in response, preserving existing'
|
|
141
|
+
);
|
|
142
|
+
} else {
|
|
143
|
+
console.log(
|
|
144
|
+
'[Frigg] Current refresh_token is null and no new refresh_token in response'
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const accessExpiresIn = get(params, 'expires_in', null);
|
|
149
|
+
const refreshExpiresIn = get(
|
|
150
|
+
params,
|
|
151
|
+
'x_refresh_token_expires_in',
|
|
152
|
+
null
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
this.accessTokenExpire = new Date(Date.now() + accessExpiresIn * 1000);
|
|
156
|
+
if (refreshExpiresIn !== null) {
|
|
157
|
+
this.refreshTokenExpire = new Date(
|
|
158
|
+
Date.now() + refreshExpiresIn * 1000
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await this.notify(this.DLGT_TOKEN_UPDATE);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Gets the OAuth authorization URL for initiating the authorization code flow.
|
|
167
|
+
*
|
|
168
|
+
* @returns {string|null} The authorization URL
|
|
169
|
+
*/
|
|
170
|
+
getAuthorizationUri() {
|
|
171
|
+
return this.authorizationUri;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Returns authorization requirements for this OAuth flow.
|
|
176
|
+
*
|
|
177
|
+
* @returns {{url: string|null, type: string}} Authorization requirements
|
|
178
|
+
*/
|
|
179
|
+
getAuthorizationRequirements() {
|
|
180
|
+
return {
|
|
181
|
+
url: this.getAuthorizationUri(),
|
|
182
|
+
type: 'oauth2',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Exchanges an authorization code for access and refresh tokens.
|
|
188
|
+
* Requires client_id, client_secret, redirect_uri, and tokenUri to be set.
|
|
189
|
+
*
|
|
190
|
+
* @param {string} code - The authorization code from the OAuth callback
|
|
191
|
+
* @returns {Promise<Object>} Token response containing access_token, refresh_token, etc.
|
|
192
|
+
*/
|
|
193
|
+
async getTokenFromCode(code) {
|
|
194
|
+
const params = new URLSearchParams();
|
|
195
|
+
params.append('grant_type', 'authorization_code');
|
|
196
|
+
params.append('client_id', this.client_id);
|
|
197
|
+
params.append('client_secret', this.client_secret);
|
|
198
|
+
params.append('redirect_uri', this.redirect_uri);
|
|
199
|
+
params.append('scope', this.scope);
|
|
200
|
+
params.append('code', code);
|
|
201
|
+
const options = {
|
|
202
|
+
body: params,
|
|
203
|
+
headers: {
|
|
204
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
205
|
+
},
|
|
206
|
+
url: this.tokenUri,
|
|
207
|
+
};
|
|
208
|
+
const response = await this._post(options, false);
|
|
209
|
+
await this.setTokens(response);
|
|
210
|
+
return response;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Exchanges an authorization code for tokens using Basic Auth header.
|
|
215
|
+
* Alternative to getTokenFromCode() for OAuth servers requiring Basic Auth.
|
|
216
|
+
* Override getTokenFromCode() in child class to use this instead.
|
|
217
|
+
*
|
|
218
|
+
* @param {string} code - The authorization code from the OAuth callback
|
|
219
|
+
* @returns {Promise<Object>} Token response containing access_token, refresh_token, etc.
|
|
220
|
+
*/
|
|
221
|
+
async getTokenFromCodeBasicAuthHeader(code) {
|
|
222
|
+
const params = new URLSearchParams();
|
|
223
|
+
params.append('grant_type', 'authorization_code');
|
|
224
|
+
params.append('client_id', this.client_id);
|
|
225
|
+
params.append('redirect_uri', this.redirect_uri);
|
|
226
|
+
params.append('code', code);
|
|
227
|
+
|
|
228
|
+
const options = {
|
|
229
|
+
body: params,
|
|
230
|
+
headers: {
|
|
231
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
232
|
+
Authorization: `Basic ${Buffer.from(
|
|
233
|
+
`${this.client_id}:${this.client_secret}`
|
|
234
|
+
).toString('base64')}`,
|
|
235
|
+
},
|
|
236
|
+
url: this.tokenUri,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const response = await this._post(options, false);
|
|
240
|
+
await this.setTokens(response);
|
|
241
|
+
return response;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Refreshes the access token using the refresh token.
|
|
246
|
+
* Used for authorization_code and password grant types.
|
|
247
|
+
*
|
|
248
|
+
* @param {Object} refreshTokenObject - Object containing refresh_token
|
|
249
|
+
* @param {string} refreshTokenObject.refresh_token - The refresh token
|
|
250
|
+
* @returns {Promise<Object>} New token response
|
|
251
|
+
*/
|
|
252
|
+
async refreshAccessToken(refreshTokenObject) {
|
|
253
|
+
this.access_token = undefined;
|
|
254
|
+
const params = new URLSearchParams();
|
|
255
|
+
params.append('grant_type', 'refresh_token');
|
|
256
|
+
params.append('client_id', this.client_id);
|
|
257
|
+
params.append('client_secret', this.client_secret);
|
|
258
|
+
params.append('refresh_token', refreshTokenObject.refresh_token);
|
|
259
|
+
params.append('redirect_uri', this.redirect_uri);
|
|
260
|
+
|
|
261
|
+
const options = {
|
|
262
|
+
body: params,
|
|
263
|
+
url: this.tokenUri,
|
|
264
|
+
headers: {
|
|
265
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
console.log('[Frigg] Refreshing access token with options');
|
|
269
|
+
const response = await this._post(options, false);
|
|
270
|
+
await this.setTokens(response);
|
|
271
|
+
return response;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Adds OAuth Bearer token to request headers.
|
|
276
|
+
* Clears any existing Authorization header first to prevent stale tokens
|
|
277
|
+
* from being reused after failed refresh attempts.
|
|
278
|
+
*
|
|
279
|
+
* @param {Object} headers - Headers object to modify
|
|
280
|
+
* @returns {Promise<Object>} Headers with Authorization added
|
|
281
|
+
*/
|
|
282
|
+
async addAuthHeaders(headers) {
|
|
283
|
+
delete headers.Authorization;
|
|
284
|
+
if (this.access_token) {
|
|
285
|
+
headers.Authorization = `Bearer ${this.access_token}`;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return headers;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Checks if the requester has valid authentication.
|
|
293
|
+
*
|
|
294
|
+
* @returns {boolean} True if authenticated with valid tokens
|
|
295
|
+
*/
|
|
296
|
+
isAuthenticated() {
|
|
297
|
+
return !!(
|
|
298
|
+
this.access_token !== null &&
|
|
299
|
+
this.refresh_token !== null &&
|
|
300
|
+
this.accessTokenExpire &&
|
|
301
|
+
this.refreshTokenExpire
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Refreshes authentication based on the configured grant type.
|
|
307
|
+
* - For authorization_code/password: Uses refreshAccessToken() with refresh_token
|
|
308
|
+
* - For client_credentials: Uses getTokenFromClientCredentials() to get new token
|
|
309
|
+
*
|
|
310
|
+
* On failure, notifies delegates via DLGT_INVALID_AUTH.
|
|
311
|
+
*
|
|
312
|
+
* @returns {Promise<boolean>} True if refresh succeeded, false if failed
|
|
313
|
+
*/
|
|
314
|
+
async refreshAuth() {
|
|
315
|
+
// The wrapper runs this check before it calls refreshAuth(). Callers
|
|
316
|
+
// that reach this method directly skip the wrapper, so the check runs
|
|
317
|
+
// here for them. The guard keeps it at one read per refresh.
|
|
318
|
+
if (
|
|
319
|
+
!this._isInsideRefreshFlow() &&
|
|
320
|
+
(await this._adoptNewerCredential())
|
|
321
|
+
) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
console.log('[Frigg] Starting token refresh', {
|
|
327
|
+
grant_type: this.grant_type,
|
|
328
|
+
has_refresh_token: !!this.refresh_token,
|
|
329
|
+
has_client_id: !!this.client_id,
|
|
330
|
+
has_client_secret: !!this.client_secret,
|
|
331
|
+
has_token_uri: !!this.tokenUri,
|
|
332
|
+
tokenUri: this.tokenUri,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
if (this.grant_type !== 'client_credentials') {
|
|
336
|
+
await this.refreshAccessToken({
|
|
337
|
+
refresh_token: this.refresh_token,
|
|
338
|
+
});
|
|
339
|
+
} else {
|
|
340
|
+
// getTokenFromClientCredentials() reports a failed token
|
|
341
|
+
// request itself and resolves to undefined. Without this
|
|
342
|
+
// check, the refresh counts as a success with no new token.
|
|
343
|
+
const tokenRes = await this.getTokenFromClientCredentials();
|
|
344
|
+
if (!tokenRes) return false;
|
|
345
|
+
}
|
|
346
|
+
console.log('[Frigg] Token refresh succeeded');
|
|
347
|
+
return true;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
const moduleName = this.delegate?.name ?? 'unknown module';
|
|
350
|
+
console.error(`[Frigg] Token refresh failed for ${moduleName}`, {
|
|
351
|
+
error_message: error?.message,
|
|
352
|
+
error_name: error?.name,
|
|
353
|
+
response_status: error?.response?.status,
|
|
354
|
+
response_data: error?.response?.data,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
if (!this._isDefinitiveAuthRejection(error)) {
|
|
358
|
+
throw this._transportFailureError(error, moduleName);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (await this._adoptNewerCredentialWithBackoff()) return true;
|
|
362
|
+
|
|
363
|
+
// The provider rejected the grant, and the store has nothing
|
|
364
|
+
// newer. The credential is dead.
|
|
365
|
+
this.telemetry?.count?.('frigg.auth.refresh_race_lost', 1, {
|
|
366
|
+
module: this._telemetryModuleLabel(),
|
|
367
|
+
});
|
|
368
|
+
// Send the status only. The refresh body contains the
|
|
369
|
+
// client_secret, and FetchError puts the body in its message
|
|
370
|
+
// outside prod.
|
|
371
|
+
await this.notify(this.DLGT_INVALID_AUTH, {
|
|
372
|
+
statusCode: error?.statusCode,
|
|
373
|
+
});
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* A timeout, a 429, or a 5xx from the token endpoint says nothing about
|
|
380
|
+
* the credential. The error must stay retryable: the caller fails loudly
|
|
381
|
+
* (worker throw → SQS retry → DLQ) and does not flag a healthy
|
|
382
|
+
* credential. This is a fresh Error on purpose. Outside prod, the
|
|
383
|
+
* original message can contain the request body, and the body carries
|
|
384
|
+
* the client_secret.
|
|
385
|
+
*/
|
|
386
|
+
_transportFailureError(error, moduleName) {
|
|
387
|
+
const status =
|
|
388
|
+
error?.statusCode ?? error?.status ?? error?.response?.status;
|
|
389
|
+
const transportError = new Error(
|
|
390
|
+
`[Frigg] Token refresh transport failure for ${moduleName}` +
|
|
391
|
+
(status != null ? ` (status ${status})` : '')
|
|
392
|
+
);
|
|
393
|
+
transportError.statusCode = status;
|
|
394
|
+
transportError.isTokenRefreshTransportFailure = true;
|
|
395
|
+
return transportError;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* A definitive rejection can mean that another invocation consumed this
|
|
400
|
+
* refresh token first. That invocation's write can be unreadable for a
|
|
401
|
+
* short time. Re-read with a bounded backoff before you decide that the
|
|
402
|
+
* credential is dead.
|
|
403
|
+
*/
|
|
404
|
+
async _adoptNewerCredentialWithBackoff() {
|
|
405
|
+
for (const delayMs of this.credentialReloadBackoffMs) {
|
|
406
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
407
|
+
if (await this._adoptNewerCredential()) {
|
|
408
|
+
console.log(
|
|
409
|
+
'[Frigg] Adopted a newer credential after a refresh rejection',
|
|
410
|
+
{ module: this._telemetryModuleLabel() }
|
|
411
|
+
);
|
|
412
|
+
this.telemetry?.count?.('frigg.auth.refresh_race_recovered', 1, {
|
|
413
|
+
module: this._telemetryModuleLabel(),
|
|
414
|
+
});
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* True when the token endpoint refused the grant. RFC 6749 §5.2 sets
|
|
423
|
+
* the status: 400, or 401 for invalid_client. A 429 or a 5xx is never a
|
|
424
|
+
* verdict on the credential. Body markers are only a fallback for SDK
|
|
425
|
+
* errors that have no status code. Production FetchErrors have a
|
|
426
|
+
* sanitized body, so a marker cannot be the primary signal.
|
|
427
|
+
*/
|
|
428
|
+
_isDefinitiveAuthRejection(error) {
|
|
429
|
+
const status =
|
|
430
|
+
error?.statusCode ?? error?.status ?? error?.response?.status;
|
|
431
|
+
if (status !== undefined && status !== null) {
|
|
432
|
+
return status === 400 || status === 401;
|
|
433
|
+
}
|
|
434
|
+
const haystack = [
|
|
435
|
+
error?.message,
|
|
436
|
+
error?.body,
|
|
437
|
+
typeof error?.error === 'string' ? error.error : null,
|
|
438
|
+
error?.response?.data && JSON.stringify(error.response.data),
|
|
439
|
+
]
|
|
440
|
+
.filter(Boolean)
|
|
441
|
+
.join(' ');
|
|
442
|
+
return /\b(invalid_grant|invalid_client)\b/i.test(haystack);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Adopts the credential stored in the database if it is newer than the
|
|
447
|
+
* credential from the instance. The refresh token decides "newer": a
|
|
448
|
+
* provider can rotate it and return an identical access-token string.
|
|
449
|
+
* The reload only reads. A reload failure is not fatal: a database blip
|
|
450
|
+
* must not change the auth behavior.
|
|
451
|
+
*
|
|
452
|
+
* @returns {Promise<boolean>} True if the module adopted a newer
|
|
453
|
+
* credential.
|
|
454
|
+
*/
|
|
455
|
+
async _adoptNewerCredential() {
|
|
456
|
+
let stored = null;
|
|
457
|
+
try {
|
|
458
|
+
stored = await this.notify(this.DLGT_CREDENTIAL_RELOAD);
|
|
459
|
+
} catch (_) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
if (!stored?.refresh_token) return false;
|
|
463
|
+
if (stored.refresh_token === this.refresh_token) return false;
|
|
464
|
+
|
|
465
|
+
if (stored.access_token) {
|
|
466
|
+
this.access_token = stored.access_token;
|
|
467
|
+
}
|
|
468
|
+
this.refresh_token = stored.refresh_token;
|
|
469
|
+
if (stored.accessTokenExpire !== undefined) {
|
|
470
|
+
this.accessTokenExpire = stored.accessTokenExpire;
|
|
471
|
+
}
|
|
472
|
+
if (stored.refreshTokenExpire !== undefined) {
|
|
473
|
+
this.refreshTokenExpire = stored.refreshTokenExpire;
|
|
474
|
+
}
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Obtains tokens using the Resource Owner Password Credentials grant.
|
|
480
|
+
* Requires username and password to be set.
|
|
481
|
+
*
|
|
482
|
+
* @returns {Promise<Object|undefined>} Token response or undefined on error
|
|
483
|
+
*/
|
|
484
|
+
async getTokenFromUsernamePassword() {
|
|
485
|
+
try {
|
|
486
|
+
const url = this.tokenUri;
|
|
487
|
+
|
|
488
|
+
const body = {
|
|
489
|
+
username: this.username,
|
|
490
|
+
password: this.password,
|
|
491
|
+
grant_type: 'password',
|
|
492
|
+
};
|
|
493
|
+
const headers = {
|
|
494
|
+
'Content-Type': 'application/json',
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const tokenRes = await this._post({
|
|
498
|
+
url,
|
|
499
|
+
body,
|
|
500
|
+
headers,
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
await this.setTokens(tokenRes);
|
|
504
|
+
return tokenRes;
|
|
505
|
+
} catch (error) {
|
|
506
|
+
// Status only. This request's body holds the password or client
|
|
507
|
+
// secret, and FetchError embeds the body in its message outside
|
|
508
|
+
// prod, so forwarding the error itself would log the credential.
|
|
509
|
+
await this.notify(this.DLGT_INVALID_AUTH, {
|
|
510
|
+
statusCode: error?.statusCode,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Obtains tokens using the Client Credentials grant.
|
|
517
|
+
* Used for server-to-server authentication without a user context.
|
|
518
|
+
* Requires client_id, client_secret, and optionally audience to be set.
|
|
519
|
+
*
|
|
520
|
+
* @returns {Promise<Object|undefined>} Token response or undefined on error
|
|
521
|
+
*/
|
|
522
|
+
async getTokenFromClientCredentials() {
|
|
523
|
+
try {
|
|
524
|
+
const url = this.tokenUri;
|
|
525
|
+
|
|
526
|
+
const body = {
|
|
527
|
+
audience: this.audience,
|
|
528
|
+
client_id: this.client_id,
|
|
529
|
+
client_secret: this.client_secret,
|
|
530
|
+
grant_type: 'client_credentials',
|
|
531
|
+
};
|
|
532
|
+
const headers = {
|
|
533
|
+
'Content-Type': 'application/json',
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
const tokenRes = await this._post({
|
|
537
|
+
url,
|
|
538
|
+
body,
|
|
539
|
+
headers,
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
await this.setTokens(tokenRes);
|
|
543
|
+
return tokenRes;
|
|
544
|
+
} catch (error) {
|
|
545
|
+
// Status only. This request's body holds the password or client
|
|
546
|
+
// secret, and FetchError embeds the body in its message outside
|
|
547
|
+
// prod, so forwarding the error itself would log the credential.
|
|
548
|
+
await this.notify(this.DLGT_INVALID_AUTH, {
|
|
549
|
+
statusCode: error?.statusCode,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
module.exports = { OAuth2Requester };
|