├── .github ├── CODEOWNERS ├── pull_request_template.md └── workflows │ ├── ci.yml │ ├── coana-analysis.yml │ ├── coana-guardrail.yml │ ├── fix-latest.yml │ └── release.yml ├── .gitignore ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── bin └── restore-or-install ├── jest.config.js ├── package.json ├── renovate.json ├── setup-jest.ts ├── src ├── actions │ ├── actions.spec.ts │ ├── actions.ts │ ├── fixtures │ │ ├── authentication-action-context.json │ │ └── user-registration-action-context.json │ ├── interfaces │ │ ├── action.interface.ts │ │ ├── index.ts │ │ └── response-payload.interface.ts │ └── serializers │ │ └── action.serializer.ts ├── audit-logs │ ├── audit-logs.spec.ts │ ├── audit-logs.ts │ ├── interfaces │ │ ├── audit-log-export-options.interface.ts │ │ ├── audit-log-export.interface.ts │ │ ├── create-audit-log-event-options.interface.ts │ │ ├── create-audit-log-schema-options.interface.ts │ │ └── index.ts │ └── serializers │ │ ├── audit-log-export-options.serializer.ts │ │ ├── audit-log-export.serializer.ts │ │ ├── create-audit-log-event-options.serializer.ts │ │ ├── create-audit-log-schema-options.serializer.ts │ │ ├── create-audit-log-schema.serializer.ts │ │ └── index.ts ├── common │ ├── crypto │ │ ├── crypto-provider.spec.ts │ │ ├── crypto-provider.ts │ │ ├── node-crypto-provider.ts │ │ ├── signature-provider.spec.ts │ │ ├── signature-provider.ts │ │ └── subtle-crypto-provider.ts │ ├── exceptions │ │ ├── bad-request.exception.ts │ │ ├── conflict.exception.ts │ │ ├── generic-server.exception.ts │ │ ├── index.ts │ │ ├── no-api-key-provided.exception.ts │ │ ├── not-found.exception.ts │ │ ├── oauth.exception.ts │ │ ├── rate-limit-exceeded.exception.ts │ │ ├── signature-verification.exception.ts │ │ ├── unauthorized.exception.ts │ │ └── unprocessable-entity.exception.ts │ ├── interfaces │ │ ├── app-info.interface.ts │ │ ├── event.interface.ts │ │ ├── get-options.interface.ts │ │ ├── http-client.interface.ts │ │ ├── index.ts │ │ ├── list.interface.ts │ │ ├── pagination-options.interface.ts │ │ ├── post-options.interface.ts │ │ ├── put-options.interface.ts │ │ ├── request-exception.interface.ts │ │ ├── unknown-record.interface.ts │ │ ├── unprocessable-entity-error.interface.ts │ │ ├── workos-options.interface.ts │ │ └── workos-response-error.interface.ts │ ├── iron-session │ │ ├── edge-iron-session-provider.ts │ │ ├── iron-session-provider.ts │ │ └── web-iron-session-provider.ts │ ├── net │ │ ├── fetch-client.spec.ts │ │ ├── fetch-client.ts │ │ ├── http-client.ts │ │ ├── index.ts │ │ ├── node-client.spec.ts │ │ └── node-client.ts │ ├── serializers │ │ ├── event.serializer.ts │ │ ├── index.ts │ │ └── list.serializer.ts │ └── utils │ │ ├── base64.ts │ │ ├── fetch-and-deserialize.ts │ │ ├── fetch-error.ts │ │ ├── pagination.ts │ │ ├── test-utils.ts │ │ ├── unreachable.ts │ │ └── workos-mock-response.ts ├── directory-sync │ ├── directory-sync.spec.ts │ ├── directory-sync.ts │ ├── interfaces │ │ ├── directory-group.interface.ts │ │ ├── directory-user.interface.ts │ │ ├── directory.interface.ts │ │ ├── index.ts │ │ ├── list-directories-options.interface.ts │ │ ├── list-directory-users-options.interface.ts │ │ └── list-groups-options.interface.ts │ ├── serializers │ │ ├── directory-group.serializer.ts │ │ ├── directory-user.serializer.ts │ │ ├── directory.serializer.ts │ │ ├── index.ts │ │ └── list-directories-options.serializer.ts │ └── utils │ │ ├── get-primary-email.spec.ts │ │ └── get-primary-email.ts ├── events │ ├── events.spec.ts │ ├── events.ts │ ├── interfaces │ │ ├── index.ts │ │ └── list-events-options.interface.ts │ └── serializers │ │ ├── index.ts │ │ └── list-event-options.serializer.ts ├── fga │ ├── fga-live-test.spec.ts │ ├── fga.spec.ts │ ├── fga.ts │ ├── interfaces │ │ ├── check-op.enum.ts │ │ ├── check.interface.ts │ │ ├── index.ts │ │ ├── list.interface.ts │ │ ├── query.interface.ts │ │ ├── resource-op.enum.ts │ │ ├── resource.interface.ts │ │ ├── warning.interface.ts │ │ ├── warrant-op.enum.ts │ │ ├── warrant-token.interface.ts │ │ └── warrant.interface.ts │ ├── serializers │ │ ├── batch-write-resources-options.serializer.ts │ │ ├── check-options.serializer.ts │ │ ├── create-resource-options.serializer.ts │ │ ├── delete-resource-options.serializer.ts │ │ ├── index.ts │ │ ├── list-resources-options.serializer.ts │ │ ├── list-warrants-options.serializer.ts │ │ ├── list.serializer.ts │ │ ├── query-options.serializer.ts │ │ ├── query-result.serializer.ts │ │ ├── resource.serializer.ts │ │ ├── warrant-token.serializer.ts │ │ ├── warrant.serializer.ts │ │ └── write-warrant-options.serializer.ts │ └── utils │ │ ├── fetch-and-deserialize-list.ts │ │ ├── fga-paginatable.ts │ │ └── interface-check.ts ├── index.ts ├── index.worker.ts ├── mfa │ ├── interfaces │ │ ├── challenge-factor-options.ts │ │ ├── challenge.interface.ts │ │ ├── enroll-factor-options.ts │ │ ├── factor.interface.ts │ │ ├── index.ts │ │ ├── sms.interface.ts │ │ ├── totp.interface.ts │ │ ├── verify-challenge-options.ts │ │ ├── verify-challenge-response.ts │ │ └── verify-factor-options.ts │ ├── mfa.spec.ts │ ├── mfa.ts │ └── serializers │ │ ├── challenge.serializer.ts │ │ ├── factor.serializer.ts │ │ ├── index.ts │ │ ├── sms.serializer.ts │ │ ├── totp.serializer.ts │ │ └── verify-response.serializer.ts ├── organization-domains │ ├── fixtures │ │ ├── get-organization-domain-pending.json │ │ └── get-organization-domain-verified.json │ ├── interfaces │ │ ├── create-organization-domain-options.interface.ts │ │ ├── index.ts │ │ └── organization-domain.interface.ts │ ├── organization-domains.spec.ts │ ├── organization-domains.ts │ └── serializers │ │ ├── create-organization-domain-options.serializer.ts │ │ └── organization-domain.serializer.ts ├── organizations │ ├── fixtures │ │ ├── clear-stripe-customer-id.json │ │ ├── create-organization-invalid.json │ │ ├── create-organization.json │ │ ├── get-organization.json │ │ ├── list-organization-roles.json │ │ ├── list-organizations.json │ │ ├── set-stripe-customer-id-disabled.json │ │ ├── set-stripe-customer-id.json │ │ └── update-organization.json │ ├── interfaces │ │ ├── create-organization-options.interface.ts │ │ ├── domain-data.interface.ts │ │ ├── index.ts │ │ ├── list-organization-roles-options.interface.ts │ │ ├── list-organizations-options.interface.ts │ │ ├── organization.interface.ts │ │ └── update-organization-options.interface.ts │ ├── organizations.spec.ts │ ├── organizations.ts │ └── serializers │ │ ├── create-organization-options.serializer.ts │ │ ├── index.ts │ │ ├── organization.serializer.spec.ts │ │ ├── organization.serializer.ts │ │ └── update-organization-options.serializer.ts ├── passwordless │ ├── fixtures │ │ └── create-session.json │ ├── interfaces │ │ ├── create-passwordless-session-options.interface.ts │ │ ├── index.ts │ │ ├── passwordless-session.interface.ts │ │ └── send-session-response.interface.ts │ ├── passwordless.spec.ts │ ├── passwordless.ts │ └── serializers │ │ ├── index.ts │ │ └── passwordless-session.serializer.ts ├── portal │ ├── fixtures │ │ ├── generate-link-invalid.json │ │ └── generate-link.json │ ├── interfaces │ │ ├── generate-portal-link-intent.interface.ts │ │ └── index.ts │ ├── portal.spec.ts │ └── portal.ts ├── roles │ ├── interfaces │ │ ├── index.ts │ │ └── role.interface.ts │ └── serializers │ │ └── role.serializer.ts ├── sso │ ├── __snapshots__ │ │ └── sso.spec.ts.snap │ ├── interfaces │ │ ├── authorization-url-options.interface.ts │ │ ├── connection-type.enum.ts │ │ ├── connection.interface.ts │ │ ├── get-profile-and-token-options.interface.ts │ │ ├── get-profile-options.interface.ts │ │ ├── index.ts │ │ ├── list-connections-options.interface.ts │ │ ├── profile-and-token.interface.ts │ │ └── profile.interface.ts │ ├── serializers │ │ ├── connection.serializer.ts │ │ ├── index.ts │ │ ├── list-connections-options.serializer.ts │ │ ├── profile-and-token.serializer.ts │ │ └── profile.serializer.ts │ ├── sso.spec.ts │ └── sso.ts ├── user-management │ ├── __snapshots__ │ │ └── user-management.spec.ts.snap │ ├── fixtures │ │ ├── deactivate-organization-membership.json │ │ ├── email_verification.json │ │ ├── identity.json │ │ ├── invitation.json │ │ ├── list-factors.json │ │ ├── list-invitations.json │ │ ├── list-organization-memberships.json │ │ ├── list-users.json │ │ ├── magic_auth.json │ │ ├── organization-membership.json │ │ ├── password_reset.json │ │ └── user.json │ ├── interfaces │ │ ├── authenticate-with-code-options.interface.ts │ │ ├── authenticate-with-email-verification-options.interface.ts │ │ ├── authenticate-with-magic-auth-options.interface.ts │ │ ├── authenticate-with-options-base.interface.ts │ │ ├── authenticate-with-organization-selection.interface.ts │ │ ├── authenticate-with-password-options.interface.ts │ │ ├── authenticate-with-refresh-token-options.interface.ts │ │ ├── authenticate-with-session-cookie.interface.ts │ │ ├── authenticate-with-totp-options.interface.ts │ │ ├── authentication-event.interface.ts │ │ ├── authentication-radar-risk-detected-event.interface.ts │ │ ├── authentication-response.interface.ts │ │ ├── authorization-url-options.interface.ts │ │ ├── create-magic-auth-options.interface.ts │ │ ├── create-organization-membership-options.interface.ts │ │ ├── create-password-reset-options.interface.ts │ │ ├── create-user-options.interface.ts │ │ ├── email-verification.interface.ts │ │ ├── enroll-auth-factor.interface.ts │ │ ├── factor.interface.ts │ │ ├── identity.interface.ts │ │ ├── impersonator.interface.ts │ │ ├── index.ts │ │ ├── invitation.interface.ts │ │ ├── list-auth-factors-options.interface.ts │ │ ├── list-invitations-options.interface.ts │ │ ├── list-organization-memberships-options.interface.ts │ │ ├── list-users-options.interface.ts │ │ ├── magic-auth.interface.ts │ │ ├── oauth-tokens.interface.ts │ │ ├── organization-membership.interface.ts │ │ ├── password-hash-type.interface.ts │ │ ├── password-reset.interface.ts │ │ ├── refresh-and-seal-session-data.interface.ts │ │ ├── reset-password-options.interface.ts │ │ ├── revoke-session-options.interface.ts │ │ ├── send-invitation-options.interface.ts │ │ ├── send-magic-auth-code-options.interface.ts │ │ ├── send-password-reset-email-options.interface.ts │ │ ├── send-verification-email-options.interface.ts │ │ ├── session-handler-options.interface.ts │ │ ├── session.interface.ts │ │ ├── update-organization-membership-options.interface.ts │ │ ├── update-user-options.interface.ts │ │ ├── update-user-password-options.interface.ts │ │ ├── user.interface.ts │ │ └── verify-email-options.interface.ts │ ├── serializers │ │ ├── authenticate-with-code-options.serializer.ts │ │ ├── authenticate-with-email-verification.serializer.ts │ │ ├── authenticate-with-magic-auth-options.serializer.ts │ │ ├── authenticate-with-organization-selection-options.serializer.ts │ │ ├── authenticate-with-password-options.serializer.ts │ │ ├── authenticate-with-refresh-token.options.serializer.ts │ │ ├── authenticate-with-totp-options.serializer.ts │ │ ├── authentication-event.serializer.ts │ │ ├── authentication-radar-risk-event-serializer.ts │ │ ├── authentication-response.serializer.ts │ │ ├── create-magic-auth-options.serializer.ts │ │ ├── create-organization-membership-options.serializer.ts │ │ ├── create-password-reset-options.serializer.ts │ │ ├── create-user-options.serializer.ts │ │ ├── email-verification.serializer.ts │ │ ├── enroll-auth-factor-options.serializer.ts │ │ ├── factor.serializer.ts │ │ ├── identity.serializer.ts │ │ ├── index.ts │ │ ├── invitation.serializer.ts │ │ ├── list-invitations-options.serializer.ts │ │ ├── list-organization-memberships-options.serializer.ts │ │ ├── list-users-options.serializer.ts │ │ ├── magic-auth.serializer.ts │ │ ├── oauth-tokens.serializer.ts │ │ ├── organization-membership.serializer.ts │ │ ├── password-reset.serializer.ts │ │ ├── reset-password-options.serializer.ts │ │ ├── role.serializer.ts │ │ ├── send-invitation-options.serializer.ts │ │ ├── send-magic-auth-code-options.serializer.ts │ │ ├── send-password-reset-email.serializer.ts │ │ ├── session.serializer.ts │ │ ├── update-organization-membership-options.serializer.ts │ │ ├── update-user-options.serializer.ts │ │ ├── update-user-password-options.serializer.ts │ │ ├── user.serializer.spec.ts │ │ └── user.serializer.ts │ ├── session.spec.ts │ ├── session.ts │ ├── user-management.spec.ts │ └── user-management.ts ├── vault │ ├── interfaces │ │ ├── index.ts │ │ ├── key.interface.ts │ │ ├── key │ │ │ ├── create-data-key.interface.ts │ │ │ └── decrypt-data-key.interface.ts │ │ ├── object.interface.ts │ │ ├── object │ │ │ ├── create-object.interface.ts │ │ │ ├── delete-object.interface.ts │ │ │ ├── list-object-versions.interface.ts │ │ │ ├── list-objects.interface.ts │ │ │ ├── read-object.interface.ts │ │ │ └── update-object.interface.ts │ │ └── secret.interface.ts │ ├── leb.d.ts │ ├── serializers │ │ ├── vault-key.serializer.ts │ │ └── vault-object.serializer.ts │ ├── vault-live-test.spec.ts │ ├── vault.spec.ts │ └── vault.ts ├── webhooks │ ├── fixtures │ │ └── webhook.json │ ├── webhooks.spec.ts │ └── webhooks.ts ├── widgets │ ├── fixtures │ │ ├── get-token-error.json │ │ └── token.json │ ├── interfaces │ │ └── get-token.ts │ ├── widgets.spec.ts │ └── widgets.ts ├── worker.spec.ts ├── workos.spec.ts └── workos.ts ├── tsconfig.json └── tslint.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See GitHub's docs for more details: 2 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 3 | 4 | # TypeScript Team 5 | * @workos/typescript 6 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | ## Documentation 4 | 5 | Does this require changes to the WorkOS Docs? E.g. the [API Reference](https://workos.com/docs/reference) or code snippets need updates. 6 | 7 | ``` 8 | [ ] Yes 9 | ``` 10 | 11 | If yes, link a related docs PR and add a docs maintainer as a reviewer. Their approval is required. 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | pull_request: {} 8 | 9 | defaults: 10 | run: 11 | shell: bash 12 | 13 | jobs: 14 | test: 15 | name: Test Node ${{ matrix.node }} 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | node: [16, 18, 20, 22] 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node }} 25 | 26 | - name: Install Dependencies 27 | run: | 28 | npm install 29 | 30 | - name: Prettier 31 | run: | 32 | npm run prettier 33 | 34 | - name: Lint 35 | run: | 36 | npm run lint 37 | 38 | - name: Build 39 | run: | 40 | npm run build 41 | 42 | - name: Test 43 | run: | 44 | npm run test 45 | -------------------------------------------------------------------------------- /.github/workflows/coana-analysis.yml: -------------------------------------------------------------------------------- 1 | name: Coana Vulnerability Analysis 2 | 3 | on: 4 | schedule: 5 | - cron: '0 3 * * *' # every day at 3 AM 6 | workflow_dispatch: 7 | inputs: 8 | tags: 9 | description: 'Manually run vulnerability analysis' 10 | # Required by the return-dispatch action 11 | distinct_id: 12 | 13 | jobs: 14 | coana-vulnerability-analysis: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | 21 | - name: Run Coana CLI 22 | id: coana-cli 23 | uses: docker://coana/coana:latest@sha256:74144ed0fc9d7da87dcd45ccd12458cc7c25ad23e47eebd7ceb4860ed396d63e 24 | with: 25 | args: | 26 | coana run . \ 27 | --api-key ${{ secrets.COANA_API_KEY }} \ 28 | --repo-url https://github.com/${{github.repository}} 29 | -------------------------------------------------------------------------------- /.github/workflows/fix-latest.yml: -------------------------------------------------------------------------------- 1 | name: Fix @latest tag 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | latest-version: 7 | description: 'The version that should be @latest, e.g. "6.7.0"' 8 | required: true 9 | type: string 10 | 11 | defaults: 12 | run: 13 | shell: bash 14 | 15 | jobs: 16 | test: 17 | name: Update @latest tag 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: actions/setup-node@v4 22 | with: 23 | node-version: 18 24 | registry-url: 'https://registry.npmjs.org' 25 | 26 | - name: Install Dependencies 27 | run: | 28 | npm install 29 | 30 | - name: Tag 31 | env: 32 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 33 | LATEST_VERSION: ${{ inputs.latest-version }} 34 | run: | 35 | echo "Setting @workos-inc/node@latest to $LATEST_VERSION" 36 | npm dist-tag add @workos-inc/node@$LATEST_VERSION latest 37 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | # Support manually pushing a new release 5 | workflow_dispatch: {} 6 | # Trigger when a release or a pre-release is published 7 | release: 8 | types: [published] 9 | 10 | defaults: 11 | run: 12 | shell: bash 13 | 14 | jobs: 15 | test: 16 | name: Publish to NPM 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: 18 23 | registry-url: 'https://registry.npmjs.org' 24 | 25 | - name: Install Dependencies 26 | run: | 27 | npm install 28 | 29 | - name: Run Tests 30 | run: | 31 | npm run test 32 | 33 | - name: Publish 34 | if: ${{ !github.event.release.prerelease }} 35 | env: 36 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 37 | run: | 38 | npm publish --tag latest --access=public 39 | 40 | - name: Publish Pre-Release 41 | if: ${{ github.event.release.prerelease }} 42 | env: 43 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 44 | run: | 45 | # Tags may come prefixed with "v" which we want to remove before putting 46 | # it as the version of this deploy. E.g. this will turn "v7.5.0-beta" to "7.5.0-beta" 47 | CLEAN_TAG=$(echo "${{ github.event.release.tag_name }}" | sed 's/[^0-9]//') 48 | jq ".version=\"${CLEAN_TAG}\"" package.json > package.json.deploy 49 | cp package.json.deploy package.json 50 | 51 | echo "Deploying with package.json" 52 | cat package.json 53 | 54 | npm publish --tag next --access=public 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .npmrc 2 | node_modules 3 | yarn-error.log 4 | lib/ 5 | package-lock.json 6 | .DS_Store 7 | yarn.lock -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | **/*.spec.js 2 | **/*.spec.d.ts 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 14.21.3 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WorkOS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/restore-or-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | KEY="node-modules-$(checksum yarn.lock)" 4 | cache restore $KEY 5 | 6 | if [[ ! `ls -d ./node_modules 2>/dev/null` ]]; then 7 | yarn install 8 | cache store $KEY ./node_modules 9 | fi 10 | 11 | if [[ ! `ls -d ./node_modules/.bin 2>/dev/null` ]]; then 12 | yarn install --check-files 13 | cache store $KEY ./node_modules 14 | fi -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | resetMocks: true, 4 | restoreMocks: true, 5 | verbose: true, 6 | testEnvironment: 'node', 7 | testPathIgnorePatterns: ['/node_modules/', '/dist/'], 8 | roots: ['/src'], 9 | setupFilesAfterEnv: ['./setup-jest.ts'], 10 | transform: { 11 | '^.+\\.ts?$': 'ts-jest', 12 | }, 13 | moduleNameMapper: { 14 | '^jose': require.resolve('jose'), 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageRules": [ 3 | { 4 | "updateTypes": ["minor", "patch", "pin", "digest"], 5 | "automerge": true 6 | } 7 | ], 8 | "extends": [ 9 | "config:base" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /setup-jest.ts: -------------------------------------------------------------------------------- 1 | import { enableFetchMocks } from 'jest-fetch-mock'; 2 | import { Crypto } from '@peculiar/webcrypto'; 3 | import { WorkOS } from './src/workos'; 4 | import { WebIronSessionProvider } from './src/common/iron-session/web-iron-session-provider'; 5 | 6 | enableFetchMocks(); 7 | 8 | // Assign Node's Crypto to global.crypto if it is not already present 9 | if (!global.crypto) { 10 | global.crypto = new Crypto(); 11 | } 12 | 13 | // For tests, we can use the WebIronSessionProvider 14 | WorkOS.prototype.createIronSessionProvider = jest 15 | .fn() 16 | .mockReturnValue(new WebIronSessionProvider()); 17 | -------------------------------------------------------------------------------- /src/actions/fixtures/authentication-action-context.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "01JATCMZJY26PQ59XT9BNT0FNN", 3 | "user": { 4 | "object": "user", 5 | "id": "01JATCHZVEC5EPANDPEZVM68Y9", 6 | "email": "jane@foocorp.com", 7 | "first_name": "Jane", 8 | "last_name": "Doe", 9 | "email_verified": true, 10 | "profile_picture_url": "https://example.com/jane.jpg", 11 | "created_at": "2024-10-22T17:12:50.746Z", 12 | "updated_at": "2024-10-22T17:12:50.746Z" 13 | }, 14 | "ip_address": "50.141.123.10", 15 | "user_agent": "Mozilla/5.0", 16 | "device_fingerprint": "notafingerprint", 17 | "issuer": "test", 18 | "object": "authentication_action_context", 19 | "organization": { 20 | "object": "organization", 21 | "id": "01JATCMZJY26PQ59XT9BNT0FNN", 22 | "name": "Foo Corp", 23 | "allow_profiles_outside_organization": false, 24 | "domains": [], 25 | "lookup_key": "my-key", 26 | "created_at": "2024-10-22T17:12:50.746Z", 27 | "updated_at": "2024-10-22T17:12:50.746Z" 28 | }, 29 | "organization_membership": { 30 | "object": "organization_membership", 31 | "id": "01JATCNVYCHT1SZGENR4QTXKRK", 32 | "user_id": "01JATCHZVEC5EPANDPEZVM68Y9", 33 | "organization_id": "01JATCMZJY26PQ59XT9BNT0FNN", 34 | "role": { 35 | "slug": "member" 36 | }, 37 | "status": "active", 38 | "created_at": "2024-10-22T17:12:50.746Z", 39 | "updated_at": "2024-10-22T17:12:50.746Z" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/actions/fixtures/user-registration-action-context.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "01JATCMZJY26PQ59XT9BNT0FNN", 3 | "user_data": { 4 | "object": "user_data", 5 | "email": "jane@foocorp.com", 6 | "first_name": "Jane", 7 | "last_name": "Doe" 8 | }, 9 | "ip_address": "50.141.123.10", 10 | "user_agent": "Mozilla/5.0", 11 | "device_fingerprint": "notafingerprint", 12 | "object": "user_registration_action_context", 13 | "invitation": { 14 | "object": "invitation", 15 | "id": "01JBVZWH8HJ855YZ5BWHG1WNZN", 16 | "email": "jane@foocorp.com", 17 | "expires_at": "2024-10-22T17:12:50.746Z", 18 | "created_at": "2024-10-21T17:12:50.746Z", 19 | "updated_at": "2024-10-21T17:12:50.746Z", 20 | "accepted_at": "2024-10-22T17:13:50.746Z", 21 | "revoked_at": null, 22 | "organization_id": "01JBW46BTKAA98WZN8826XQ2YP", 23 | "inviter_user_id": "01JBVZWAEPWAE3YYKBVT0AF81F" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/actions/interfaces/action.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Organization, 3 | OrganizationResponse, 4 | } from '../../organizations/interfaces'; 5 | import { 6 | Invitation, 7 | InvitationResponse, 8 | OrganizationMembership, 9 | OrganizationMembershipResponse, 10 | User, 11 | UserResponse, 12 | } from '../../user-management/interfaces'; 13 | 14 | interface AuthenticationActionContext { 15 | id: string; 16 | object: 'authentication_action_context'; 17 | user: User; 18 | organization?: Organization; 19 | organizationMembership?: OrganizationMembership; 20 | ipAddress?: string; 21 | userAgent?: string; 22 | deviceFingerprint?: string; 23 | issuer?: string; 24 | } 25 | 26 | export interface UserData { 27 | object: 'user_data'; 28 | email: string; 29 | firstName: string; 30 | lastName: string; 31 | } 32 | 33 | interface UserRegistrationActionContext { 34 | id: string; 35 | object: 'user_registration_action_context'; 36 | userData: UserData; 37 | invitation?: Invitation; 38 | ipAddress?: string; 39 | userAgent?: string; 40 | deviceFingerprint?: string; 41 | } 42 | 43 | export type ActionContext = 44 | | AuthenticationActionContext 45 | | UserRegistrationActionContext; 46 | 47 | interface AuthenticationActionPayload { 48 | id: string; 49 | object: 'authentication_action_context'; 50 | user: UserResponse; 51 | organization?: OrganizationResponse; 52 | organization_membership?: OrganizationMembershipResponse; 53 | ip_address?: string; 54 | user_agent?: string; 55 | device_fingerprint?: string; 56 | issuer?: string; 57 | } 58 | 59 | export interface UserDataPayload { 60 | object: 'user_data'; 61 | email: string; 62 | first_name: string; 63 | last_name: string; 64 | } 65 | 66 | export interface UserRegistrationActionPayload { 67 | id: string; 68 | object: 'user_registration_action_context'; 69 | user_data: UserDataPayload; 70 | invitation?: InvitationResponse; 71 | ip_address?: string; 72 | user_agent?: string; 73 | device_fingerprint?: string; 74 | } 75 | 76 | export type ActionPayload = 77 | | AuthenticationActionPayload 78 | | UserRegistrationActionPayload; 79 | -------------------------------------------------------------------------------- /src/actions/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './action.interface'; 2 | export * from './response-payload.interface'; 3 | -------------------------------------------------------------------------------- /src/actions/interfaces/response-payload.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ResponsePayload { 2 | timestamp: number; 3 | verdict?: 'Allow' | 'Deny'; 4 | errorMessage?: string; 5 | } 6 | 7 | interface AllowResponseData { 8 | verdict: 'Allow'; 9 | } 10 | 11 | interface DenyResponseData { 12 | verdict: 'Deny'; 13 | errorMessage?: string; 14 | } 15 | 16 | export type AuthenticationActionResponseData = 17 | | (AllowResponseData & { type: 'authentication' }) 18 | | (DenyResponseData & { type: 'authentication' }); 19 | 20 | export type UserRegistrationActionResponseData = 21 | | (AllowResponseData & { type: 'user_registration' }) 22 | | (DenyResponseData & { type: 'user_registration' }); 23 | -------------------------------------------------------------------------------- /src/audit-logs/interfaces/audit-log-export-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface AuditLogExportOptions { 2 | actions?: string[]; 3 | /** 4 | * @deprecated Please use `actorNames` instead. 5 | */ 6 | actors?: string[]; 7 | actorNames?: string[]; 8 | actorIds?: string[]; 9 | organizationId: string; 10 | rangeEnd: Date; 11 | rangeStart: Date; 12 | targets?: string[]; 13 | } 14 | 15 | export interface SerializedAuditLogExportOptions { 16 | actions?: string[]; 17 | actors?: string[]; 18 | actor_names?: string[]; 19 | actor_ids?: string[]; 20 | organization_id: string; 21 | range_end: string; 22 | range_start: string; 23 | targets?: string[]; 24 | } 25 | -------------------------------------------------------------------------------- /src/audit-logs/interfaces/audit-log-export.interface.ts: -------------------------------------------------------------------------------- 1 | export interface AuditLogExport { 2 | object: 'audit_log_export'; 3 | id: string; 4 | state: 'pending' | 'ready' | 'error'; 5 | url?: string; 6 | createdAt: string; 7 | updatedAt: string; 8 | } 9 | 10 | export interface AuditLogExportResponse { 11 | object: 'audit_log_export'; 12 | id: string; 13 | state: 'pending' | 'ready' | 'error'; 14 | url?: string; 15 | created_at: string; 16 | updated_at: string; 17 | } 18 | -------------------------------------------------------------------------------- /src/audit-logs/interfaces/create-audit-log-event-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PostOptions } from '../../common/interfaces'; 2 | 3 | export interface AuditLogActor { 4 | id: string; 5 | name?: string; 6 | type: string; 7 | metadata?: Record; 8 | } 9 | 10 | export interface AuditLogTarget { 11 | id: string; 12 | name?: string; 13 | type: string; 14 | metadata?: Record; 15 | } 16 | 17 | export interface CreateAuditLogEventOptions { 18 | action: string; 19 | version?: number; 20 | occurredAt: Date; 21 | actor: AuditLogActor; 22 | targets: AuditLogTarget[]; 23 | context: { 24 | location: string; 25 | userAgent?: string; 26 | }; 27 | metadata?: Record; 28 | } 29 | 30 | export interface SerializedCreateAuditLogEventOptions { 31 | action: string; 32 | version?: number; 33 | occurred_at: string; 34 | actor: AuditLogActor; 35 | targets: AuditLogTarget[]; 36 | context: { 37 | location: string; 38 | user_agent?: string; 39 | }; 40 | metadata?: Record; 41 | } 42 | 43 | export type CreateAuditLogEventRequestOptions = Pick< 44 | PostOptions, 45 | 'idempotencyKey' 46 | >; 47 | -------------------------------------------------------------------------------- /src/audit-logs/interfaces/create-audit-log-schema-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PostOptions } from '../../common/interfaces'; 2 | 3 | export type AuditLogSchemaMetadata = 4 | | Record 5 | | undefined; 6 | 7 | export interface AuditLogSchema { 8 | object: 'audit_log_schema'; 9 | version: number; 10 | targets: AuditLogTargetSchema[]; 11 | actor: AuditLogActorSchema; 12 | metadata: Record | undefined; 13 | createdAt: string; 14 | } 15 | 16 | export interface AuditLogActorSchema { 17 | metadata: Record; 18 | } 19 | 20 | export interface AuditLogTargetSchema { 21 | type: string; 22 | metadata?: Record | undefined; 23 | } 24 | 25 | export interface CreateAuditLogSchemaOptions { 26 | action: string; 27 | targets: AuditLogTargetSchema[]; 28 | actor?: AuditLogActorSchema; 29 | metadata?: Record; 30 | } 31 | 32 | interface SerializedAuditLogTargetSchema { 33 | type: string; 34 | metadata?: { 35 | type: 'object'; 36 | properties: AuditLogSchemaMetadata; 37 | }; 38 | } 39 | 40 | export interface SerializedCreateAuditLogSchemaOptions { 41 | targets: SerializedAuditLogTargetSchema[]; 42 | actor?: { 43 | metadata: { 44 | type: 'object'; 45 | properties: AuditLogSchemaMetadata; 46 | }; 47 | }; 48 | metadata?: { 49 | type: 'object'; 50 | properties: AuditLogSchemaMetadata; 51 | }; 52 | } 53 | 54 | export interface CreateAuditLogSchemaResponse { 55 | object: 'audit_log_schema'; 56 | version: number; 57 | targets: SerializedAuditLogTargetSchema[]; 58 | actor: { 59 | metadata: { 60 | type: 'object'; 61 | properties: AuditLogSchemaMetadata; 62 | }; 63 | }; 64 | metadata?: { 65 | type: 'object'; 66 | properties: AuditLogSchemaMetadata; 67 | }; 68 | created_at: string; 69 | } 70 | 71 | export type CreateAuditLogSchemaRequestOptions = Pick< 72 | PostOptions, 73 | 'idempotencyKey' 74 | >; 75 | -------------------------------------------------------------------------------- /src/audit-logs/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './audit-log-export-options.interface'; 2 | export * from './audit-log-export.interface'; 3 | export * from './create-audit-log-event-options.interface'; 4 | export * from './create-audit-log-schema-options.interface'; 5 | -------------------------------------------------------------------------------- /src/audit-logs/serializers/audit-log-export-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuditLogExportOptions, 3 | SerializedAuditLogExportOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeAuditLogExportOptions = ( 7 | options: AuditLogExportOptions, 8 | ): SerializedAuditLogExportOptions => ({ 9 | actions: options.actions, 10 | actors: options.actors, 11 | actor_names: options.actorNames, 12 | actor_ids: options.actorIds, 13 | organization_id: options.organizationId, 14 | range_end: options.rangeEnd.toISOString(), 15 | range_start: options.rangeStart.toISOString(), 16 | targets: options.targets, 17 | }); 18 | -------------------------------------------------------------------------------- /src/audit-logs/serializers/audit-log-export.serializer.ts: -------------------------------------------------------------------------------- 1 | import { AuditLogExport, AuditLogExportResponse } from '../interfaces'; 2 | 3 | export const deserializeAuditLogExport = ( 4 | auditLogExport: AuditLogExportResponse, 5 | ): AuditLogExport => ({ 6 | object: auditLogExport.object, 7 | id: auditLogExport.id, 8 | state: auditLogExport.state, 9 | url: auditLogExport.url, 10 | createdAt: auditLogExport.created_at, 11 | updatedAt: auditLogExport.updated_at, 12 | }); 13 | -------------------------------------------------------------------------------- /src/audit-logs/serializers/create-audit-log-event-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateAuditLogEventOptions, 3 | SerializedCreateAuditLogEventOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeCreateAuditLogEventOptions = ( 7 | event: CreateAuditLogEventOptions, 8 | ): SerializedCreateAuditLogEventOptions => ({ 9 | action: event.action, 10 | version: event.version, 11 | occurred_at: event.occurredAt.toISOString(), 12 | actor: event.actor, 13 | targets: event.targets, 14 | context: { 15 | location: event.context.location, 16 | user_agent: event.context.userAgent, 17 | }, 18 | metadata: event.metadata, 19 | }); 20 | -------------------------------------------------------------------------------- /src/audit-logs/serializers/create-audit-log-schema-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuditLogSchemaMetadata, 3 | CreateAuditLogSchemaOptions, 4 | SerializedCreateAuditLogSchemaOptions, 5 | } from '../interfaces'; 6 | 7 | function serializeMetadata( 8 | metadata: Record | undefined, 9 | ) { 10 | if (!metadata) { 11 | return {}; 12 | } 13 | 14 | const serializedMetadata: AuditLogSchemaMetadata = {}; 15 | 16 | Object.keys(metadata).forEach((key) => { 17 | serializedMetadata[key] = { 18 | type: metadata[key] as 'string' | 'number' | 'boolean', 19 | }; 20 | }); 21 | 22 | return serializedMetadata; 23 | } 24 | 25 | export const serializeCreateAuditLogSchemaOptions = ( 26 | schema: CreateAuditLogSchemaOptions, 27 | ): SerializedCreateAuditLogSchemaOptions => ({ 28 | actor: { 29 | metadata: { 30 | type: 'object', 31 | properties: serializeMetadata(schema.actor?.metadata), 32 | }, 33 | }, 34 | targets: schema.targets.map((target) => { 35 | return { 36 | type: target.type, 37 | metadata: target.metadata 38 | ? { 39 | type: 'object', 40 | properties: serializeMetadata(target.metadata), 41 | } 42 | : undefined, 43 | }; 44 | }), 45 | metadata: schema.metadata 46 | ? { 47 | type: 'object', 48 | properties: serializeMetadata(schema.metadata), 49 | } 50 | : undefined, 51 | }); 52 | -------------------------------------------------------------------------------- /src/audit-logs/serializers/create-audit-log-schema.serializer.ts: -------------------------------------------------------------------------------- 1 | import { AuditLogSchema, CreateAuditLogSchemaResponse } from '../interfaces'; 2 | 3 | function deserializeMetadata(metadata: { 4 | properties?: Record; 5 | }): Record { 6 | if (!metadata || !metadata.properties) { 7 | return {}; 8 | } 9 | 10 | const deserializedMetadata: Record = {}; 11 | 12 | Object.keys(metadata.properties).forEach((key) => { 13 | if (metadata.properties) { 14 | deserializedMetadata[key] = metadata.properties[key].type; 15 | } 16 | }); 17 | 18 | return deserializedMetadata; 19 | } 20 | 21 | export const deserializeAuditLogSchema = ( 22 | auditLogSchema: CreateAuditLogSchemaResponse, 23 | ): AuditLogSchema => ({ 24 | object: auditLogSchema.object, 25 | version: auditLogSchema.version, 26 | targets: auditLogSchema.targets.map((target) => { 27 | return { 28 | type: target.type, 29 | metadata: target.metadata 30 | ? deserializeMetadata(target.metadata) 31 | : undefined, 32 | }; 33 | }), 34 | actor: { 35 | metadata: deserializeMetadata(auditLogSchema.actor?.metadata), 36 | }, 37 | metadata: auditLogSchema.metadata 38 | ? deserializeMetadata(auditLogSchema.metadata) 39 | : undefined, 40 | createdAt: auditLogSchema.created_at, 41 | }); 42 | -------------------------------------------------------------------------------- /src/audit-logs/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './audit-log-export.serializer'; 2 | export * from './audit-log-export-options.serializer'; 3 | export * from './create-audit-log-event-options.serializer'; 4 | export * from './create-audit-log-schema-options.serializer'; 5 | export * from './create-audit-log-schema.serializer'; 6 | -------------------------------------------------------------------------------- /src/common/exceptions/bad-request.exception.ts: -------------------------------------------------------------------------------- 1 | import { RequestException } from '../interfaces/request-exception.interface'; 2 | 3 | export class BadRequestException extends Error implements RequestException { 4 | readonly status = 400; 5 | readonly name = 'BadRequestException'; 6 | readonly message: string = 'Bad request'; 7 | readonly code?: string; 8 | readonly errors?: unknown[]; 9 | readonly requestID: string; 10 | 11 | constructor({ 12 | code, 13 | errors, 14 | message, 15 | requestID, 16 | }: { 17 | code?: string; 18 | errors?: unknown[]; 19 | message?: string; 20 | requestID: string; 21 | }) { 22 | super(); 23 | 24 | this.requestID = requestID; 25 | 26 | if (message) { 27 | this.message = message; 28 | } 29 | 30 | if (code) { 31 | this.code = code; 32 | } 33 | 34 | if (errors) { 35 | this.errors = errors; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/common/exceptions/conflict.exception.ts: -------------------------------------------------------------------------------- 1 | import { RequestException } from '../interfaces/request-exception.interface'; 2 | 3 | export class ConflictException extends Error implements RequestException { 4 | readonly status = 409; 5 | readonly name = 'ConflictException'; 6 | readonly requestID: string; 7 | 8 | constructor({ 9 | error, 10 | message, 11 | requestID, 12 | }: { 13 | error?: string; 14 | message?: string; 15 | requestID: string; 16 | }) { 17 | super(); 18 | 19 | this.requestID = requestID; 20 | 21 | if (message) { 22 | this.message = message; 23 | } else if (error) { 24 | this.message = `Error: ${error}`; 25 | } else { 26 | this.message = `An conflict has occurred on the server.`; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/common/exceptions/generic-server.exception.ts: -------------------------------------------------------------------------------- 1 | import { RequestException } from '../interfaces/request-exception.interface'; 2 | 3 | export class GenericServerException extends Error implements RequestException { 4 | readonly name: string = 'GenericServerException'; 5 | readonly message: string = 'The request could not be completed.'; 6 | 7 | constructor( 8 | readonly status: number, 9 | message: string | undefined, 10 | readonly rawData: unknown, 11 | readonly requestID: string, 12 | ) { 13 | super(); 14 | if (message) { 15 | this.message = message; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/common/exceptions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './generic-server.exception'; 2 | export * from './bad-request.exception'; 3 | export * from './no-api-key-provided.exception'; 4 | export * from './not-found.exception'; 5 | export * from './oauth.exception'; 6 | export * from './rate-limit-exceeded.exception'; 7 | export * from './signature-verification.exception'; 8 | export * from './unauthorized.exception'; 9 | export * from './unprocessable-entity.exception'; 10 | -------------------------------------------------------------------------------- /src/common/exceptions/no-api-key-provided.exception.ts: -------------------------------------------------------------------------------- 1 | export class NoApiKeyProvidedException extends Error { 2 | readonly status = 500; 3 | readonly name = 'NoApiKeyProvidedException'; 4 | readonly message = 5 | `Missing API key. Pass it to the constructor (new WorkOS("sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU")) ` + 6 | `or define it in the WORKOS_API_KEY environment variable.`; 7 | } 8 | -------------------------------------------------------------------------------- /src/common/exceptions/not-found.exception.ts: -------------------------------------------------------------------------------- 1 | import { RequestException } from '../interfaces/request-exception.interface'; 2 | 3 | export class NotFoundException extends Error implements RequestException { 4 | readonly status = 404; 5 | readonly name = 'NotFoundException'; 6 | readonly message: string; 7 | readonly code?: string; 8 | readonly requestID: string; 9 | 10 | constructor({ 11 | code, 12 | message, 13 | path, 14 | requestID, 15 | }: { 16 | code?: string; 17 | message?: string; 18 | path: string; 19 | requestID: string; 20 | }) { 21 | super(); 22 | this.code = code; 23 | this.message = 24 | message ?? `The requested path '${path}' could not be found.`; 25 | this.requestID = requestID; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/common/exceptions/oauth.exception.ts: -------------------------------------------------------------------------------- 1 | import { RequestException } from '../interfaces/request-exception.interface'; 2 | 3 | export class OauthException extends Error implements RequestException { 4 | readonly name = 'OauthException'; 5 | 6 | constructor( 7 | readonly status: number, 8 | readonly requestID: string, 9 | readonly error: string | undefined, 10 | readonly errorDescription: string | undefined, 11 | readonly rawData: unknown, 12 | ) { 13 | super(); 14 | if (error && errorDescription) { 15 | this.message = `Error: ${error}\nError Description: ${errorDescription}`; 16 | } else if (error) { 17 | this.message = `Error: ${error}`; 18 | } else { 19 | this.message = `An error has occurred.`; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/common/exceptions/rate-limit-exceeded.exception.ts: -------------------------------------------------------------------------------- 1 | import { GenericServerException } from './generic-server.exception'; 2 | 3 | // Inheriting from `GenericServerException` in order to maintain backwards 4 | // compatibility with what 429 errors would have previously been thrown as. 5 | // 6 | // TODO: Consider making it the base class for all request errors. 7 | export class RateLimitExceededException extends GenericServerException { 8 | readonly name = 'RateLimitExceededException'; 9 | 10 | constructor( 11 | message: string, 12 | requestID: string, 13 | /** 14 | * The number of seconds to wait before retrying the request. 15 | */ 16 | readonly retryAfter: number | null, 17 | ) { 18 | super(429, message, {}, requestID); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/common/exceptions/signature-verification.exception.ts: -------------------------------------------------------------------------------- 1 | export class SignatureVerificationException extends Error { 2 | readonly name = 'SignatureVerificationException'; 3 | 4 | constructor(message: string) { 5 | super(message || 'Signature verification failed.'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/common/exceptions/unauthorized.exception.ts: -------------------------------------------------------------------------------- 1 | import { RequestException } from '../interfaces/request-exception.interface'; 2 | 3 | export class UnauthorizedException extends Error implements RequestException { 4 | readonly status = 401; 5 | readonly name = 'UnauthorizedException'; 6 | readonly message: string; 7 | 8 | constructor(readonly requestID: string) { 9 | super(); 10 | this.message = `Could not authorize the request. Maybe your API key is invalid?`; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/common/exceptions/unprocessable-entity.exception.ts: -------------------------------------------------------------------------------- 1 | import pluralize from 'pluralize'; 2 | 3 | import { UnprocessableEntityError } from '../interfaces'; 4 | import { RequestException } from '../interfaces/request-exception.interface'; 5 | 6 | export class UnprocessableEntityException 7 | extends Error 8 | implements RequestException 9 | { 10 | readonly status = 422; 11 | readonly name = 'UnprocessableEntityException'; 12 | readonly message: string = 'Unprocessable entity'; 13 | readonly code?: string; 14 | readonly requestID: string; 15 | 16 | constructor({ 17 | code, 18 | errors, 19 | message, 20 | requestID, 21 | }: { 22 | code?: string; 23 | errors?: UnprocessableEntityError[]; 24 | message?: string; 25 | requestID: string; 26 | }) { 27 | super(); 28 | 29 | this.requestID = requestID; 30 | 31 | if (message) { 32 | this.message = message; 33 | } 34 | 35 | if (code) { 36 | this.code = code; 37 | } 38 | 39 | if (errors) { 40 | const requirement: string = pluralize('requirement', errors.length); 41 | 42 | this.message = `The following ${requirement} must be met:\n`; 43 | 44 | for (const { code } of errors) { 45 | this.message = this.message.concat(`\t${code}\n`); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/common/interfaces/app-info.interface.ts: -------------------------------------------------------------------------------- 1 | export interface AppInfo { 2 | name: string; 3 | version: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/common/interfaces/get-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface GetOptions { 2 | query?: Record; 3 | accessToken?: string; 4 | warrantToken?: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/common/interfaces/http-client.interface.ts: -------------------------------------------------------------------------------- 1 | export type RequestHeaders = Record; 2 | export type RequestOptions = { 3 | params?: Record; 4 | headers?: RequestHeaders; 5 | }; 6 | export type ResponseHeaderValue = string | string[]; 7 | export type ResponseHeaders = Record; 8 | 9 | export interface HttpClientInterface { 10 | getClientName: () => string; 11 | get(path: string, options: RequestOptions): any; 12 | post( 13 | path: string, 14 | entity: Entity, 15 | options: RequestOptions, 16 | ): any; 17 | put(path: string, entity: Entity, options: RequestOptions): any; 18 | delete(path: string, options: RequestOptions): any; 19 | } 20 | 21 | export interface HttpClientResponseInterface { 22 | getStatusCode: () => number; 23 | getHeaders: () => ResponseHeaders; 24 | getRawResponse: () => unknown; 25 | toJSON: () => Promise | null; 26 | } 27 | -------------------------------------------------------------------------------- /src/common/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './event.interface'; 2 | export * from './get-options.interface'; 3 | export * from './list.interface'; 4 | export * from './post-options.interface'; 5 | export * from './put-options.interface'; 6 | export * from './unprocessable-entity-error.interface'; 7 | export * from './workos-options.interface'; 8 | export * from './workos-response-error.interface'; 9 | export * from './pagination-options.interface'; 10 | export * from './http-client.interface'; 11 | -------------------------------------------------------------------------------- /src/common/interfaces/list.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ListResponse { 2 | readonly object: 'list'; 3 | data: T[]; 4 | list_metadata: { 5 | before?: string; 6 | after?: string; 7 | }; 8 | } 9 | 10 | export interface List { 11 | readonly object: 'list'; 12 | data: T[]; 13 | listMetadata: { 14 | before?: string; 15 | after?: string; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/common/interfaces/pagination-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface PaginationOptions { 2 | limit?: number; 3 | before?: string; 4 | after?: string; 5 | order?: 'asc' | 'desc'; 6 | } 7 | -------------------------------------------------------------------------------- /src/common/interfaces/post-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface PostOptions { 2 | query?: { [key: string]: any }; 3 | idempotencyKey?: string; 4 | warrantToken?: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/common/interfaces/put-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface PutOptions { 2 | query?: { [key: string]: any }; 3 | idempotencyKey?: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/common/interfaces/request-exception.interface.ts: -------------------------------------------------------------------------------- 1 | export interface RequestException { 2 | readonly status: number; 3 | readonly message: string; 4 | readonly requestID: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/common/interfaces/unknown-record.interface.ts: -------------------------------------------------------------------------------- 1 | export type UnknownRecord = Record; 2 | -------------------------------------------------------------------------------- /src/common/interfaces/unprocessable-entity-error.interface.ts: -------------------------------------------------------------------------------- 1 | export interface UnprocessableEntityError { 2 | field: string; 3 | code: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/common/interfaces/workos-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { AppInfo } from './app-info.interface'; 2 | 3 | export interface WorkOSOptions { 4 | apiHostname?: string; 5 | https?: boolean; 6 | port?: number; 7 | config?: RequestInit; 8 | appInfo?: AppInfo; 9 | fetchFn?: typeof fetch; 10 | clientId?: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/common/interfaces/workos-response-error.interface.ts: -------------------------------------------------------------------------------- 1 | import { UnprocessableEntityError } from './unprocessable-entity-error.interface'; 2 | 3 | export interface WorkOSResponseError { 4 | code?: string; 5 | error_description?: string; 6 | error?: string; 7 | errors?: UnprocessableEntityError[]; 8 | message: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/common/iron-session/edge-iron-session-provider.ts: -------------------------------------------------------------------------------- 1 | import { sealData, unsealData } from 'iron-session/edge'; 2 | import { 3 | IronSessionProvider, 4 | SealDataOptions, 5 | UnsealedDataType, 6 | } from './iron-session-provider'; 7 | 8 | /** 9 | * EdgeIronSessionProvider which uses the base iron-session seal/unseal methods. 10 | */ 11 | export class EdgeIronSessionProvider extends IronSessionProvider { 12 | /** @override */ 13 | async sealData(data: unknown, options: SealDataOptions): Promise { 14 | // The iron-session default ttl is 14 days, which can be problematic if the WorkOS session is configured to be > 14 days. 15 | // In that case the session expires and can't be refreshed, so we set the ttl to 0 to set it to the max possible value. 16 | const sealOptions = { 17 | ...options, 18 | ttl: 0, 19 | }; 20 | return sealData(data, sealOptions); 21 | } 22 | 23 | /** @override */ 24 | async unsealData( 25 | seal: string, 26 | options: SealDataOptions, 27 | ): Promise { 28 | return unsealData(seal, options); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/common/iron-session/iron-session-provider.ts: -------------------------------------------------------------------------------- 1 | export type SealDataOptions = { 2 | password: 3 | | string 4 | | { 5 | [id: string]: string; 6 | }; 7 | ttl?: number | undefined; 8 | }; 9 | 10 | export type UnsealedDataType = Record; 11 | 12 | /** 13 | * Interface encapsulating the sealData/unsealData methods for separate iron-session implementations. 14 | * 15 | * This allows for different implementations of the iron-session library to be used in 16 | * worker/edge vs. regular web environments, which is required because of the different crypto APIs available. 17 | * Once we drop support for Node 16 and upgrade to iron-session 8+, we can remove this abstraction as iron-session 8+ 18 | * handles this on its own. 19 | */ 20 | export abstract class IronSessionProvider { 21 | abstract sealData(data: unknown, options: SealDataOptions): Promise; 22 | 23 | abstract unsealData( 24 | seal: string, 25 | options: SealDataOptions, 26 | ): Promise; 27 | } 28 | -------------------------------------------------------------------------------- /src/common/iron-session/web-iron-session-provider.ts: -------------------------------------------------------------------------------- 1 | import { sealData, unsealData } from 'iron-session'; 2 | import { 3 | IronSessionProvider, 4 | SealDataOptions, 5 | UnsealedDataType, 6 | } from './iron-session-provider'; 7 | 8 | /** 9 | * WebIronSessionProvider which uses the base iron-session seal/unseal methods. 10 | */ 11 | export class WebIronSessionProvider extends IronSessionProvider { 12 | /** @override */ 13 | async sealData(data: unknown, options: SealDataOptions): Promise { 14 | // The iron-session default ttl is 14 days, which can be problematic if the WorkOS session is configured to be > 14 days. 15 | // In that case the session expires and can't be refreshed, so we set the ttl to 0 to set it to the max possible value. 16 | const sealOptions = { 17 | ...options, 18 | ttl: 0, 19 | }; 20 | return sealData(data, sealOptions); 21 | } 22 | 23 | /** @override */ 24 | async unsealData( 25 | seal: string, 26 | options: SealDataOptions, 27 | ): Promise { 28 | return unsealData(seal, options); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/common/net/index.ts: -------------------------------------------------------------------------------- 1 | import { FetchHttpClient } from './fetch-client'; 2 | import { HttpClient } from './http-client'; 3 | import { NodeHttpClient } from './node-client'; 4 | 5 | export function createHttpClient( 6 | baseURL: string, 7 | options: RequestInit, 8 | fetchFn?: typeof fetch, 9 | ): HttpClient { 10 | if (typeof fetch !== 'undefined' || typeof fetchFn !== 'undefined') { 11 | return new FetchHttpClient(baseURL, options, fetchFn); 12 | } else { 13 | return new NodeHttpClient(baseURL, options); 14 | } 15 | } 16 | 17 | export * from './fetch-client'; 18 | export * from './node-client'; 19 | export * from './http-client'; 20 | -------------------------------------------------------------------------------- /src/common/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './event.serializer'; 2 | export * from './list.serializer'; 3 | -------------------------------------------------------------------------------- /src/common/serializers/list.serializer.ts: -------------------------------------------------------------------------------- 1 | import { List, ListResponse } from '../interfaces'; 2 | 3 | export const deserializeList = ( 4 | list: ListResponse, 5 | deserializer: (serialized: TSerialized) => TDeserialized, 6 | ): List => ({ 7 | object: 'list', 8 | data: list.data.map(deserializer), 9 | listMetadata: list.list_metadata, 10 | }); 11 | -------------------------------------------------------------------------------- /src/common/utils/base64.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Cross-runtime compatible base64 encoding/decoding utilities 3 | * that work in both Node.js and browser environments 4 | */ 5 | 6 | /** 7 | * Converts a base64 string to a Uint8Array 8 | */ 9 | export function base64ToUint8Array(base64: string): Uint8Array { 10 | // In browsers and modern Node.js 11 | if (typeof atob === 'function') { 12 | const binary = atob(base64); 13 | const bytes = new Uint8Array(binary.length); 14 | for (let i = 0; i < binary.length; i++) { 15 | bytes[i] = binary.charCodeAt(i); 16 | } 17 | return bytes; 18 | } 19 | // Node.js fallback using Buffer 20 | else if (typeof Buffer !== 'undefined') { 21 | return new Uint8Array(Buffer.from(base64, 'base64')); 22 | } 23 | // Fallback implementation if neither is available 24 | else { 25 | throw new Error('No base64 decoding implementation available'); 26 | } 27 | } 28 | 29 | /** 30 | * Converts a Uint8Array to a base64 string 31 | */ 32 | export function uint8ArrayToBase64(bytes: Uint8Array): string { 33 | // In browsers and modern Node.js 34 | if (typeof btoa === 'function') { 35 | let binary = ''; 36 | for (let i = 0; i < bytes.byteLength; i++) { 37 | binary += String.fromCharCode(bytes[i]); 38 | } 39 | return btoa(binary); 40 | } 41 | // Node.js fallback using Buffer 42 | else if (typeof Buffer !== 'undefined') { 43 | return Buffer.from(bytes).toString('base64'); 44 | } 45 | // Fallback implementation if neither is available 46 | else { 47 | throw new Error('No base64 encoding implementation available'); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/common/utils/fetch-and-deserialize.ts: -------------------------------------------------------------------------------- 1 | import { WorkOS } from '../../workos'; 2 | import { 3 | GetOptions, 4 | List, 5 | ListResponse, 6 | PaginationOptions, 7 | } from '../interfaces'; 8 | import { deserializeList } from '../serializers'; 9 | 10 | const setDefaultOptions = (options?: PaginationOptions): PaginationOptions => { 11 | return { 12 | ...options, 13 | order: options?.order || 'desc', 14 | }; 15 | }; 16 | 17 | export const fetchAndDeserialize = async ( 18 | workos: WorkOS, 19 | endpoint: string, 20 | deserializeFn: (data: T) => U, 21 | options?: PaginationOptions, 22 | requestOptions?: GetOptions, 23 | ): Promise> => { 24 | const { data } = await workos.get>(endpoint, { 25 | query: setDefaultOptions(options), 26 | ...requestOptions, 27 | }); 28 | 29 | return deserializeList(data, deserializeFn); 30 | }; 31 | -------------------------------------------------------------------------------- /src/common/utils/fetch-error.ts: -------------------------------------------------------------------------------- 1 | export class FetchError extends Error { 2 | readonly name: string = 'FetchError'; 3 | readonly message: string = 'The request could not be completed.'; 4 | readonly response: { status: number; headers: Headers; data: T }; 5 | 6 | constructor({ 7 | message, 8 | response, 9 | }: { 10 | message: string; 11 | readonly response: FetchError['response']; 12 | }) { 13 | super(message); 14 | this.message = message; 15 | this.response = response; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/common/utils/pagination.ts: -------------------------------------------------------------------------------- 1 | import { List, PaginationOptions } from '../interfaces'; 2 | 3 | export class AutoPaginatable { 4 | readonly object: 'list' = 'list'; 5 | readonly options: PaginationOptions; 6 | 7 | constructor( 8 | protected list: List, 9 | private apiCall: (params: PaginationOptions) => Promise>, 10 | options?: PaginationOptions, 11 | ) { 12 | this.options = { 13 | ...options, 14 | }; 15 | } 16 | 17 | get data(): T[] { 18 | return this.list.data; 19 | } 20 | 21 | get listMetadata() { 22 | return this.list.listMetadata; 23 | } 24 | 25 | private async *generatePages(params: PaginationOptions): AsyncGenerator { 26 | const result = await this.apiCall({ 27 | ...this.options, 28 | limit: 100, 29 | after: params.after, 30 | }); 31 | 32 | yield result.data; 33 | 34 | if (result.listMetadata.after) { 35 | // Delay of 4rps to respect list users rate limits 36 | await new Promise((resolve) => setTimeout(resolve, 250)); 37 | yield* this.generatePages({ after: result.listMetadata.after }); 38 | } 39 | } 40 | 41 | /** 42 | * Automatically paginates over the list of results, returning the complete data set. 43 | * Returns the first result if `options.limit` is passed to the first request. 44 | */ 45 | async autoPagination(): Promise { 46 | if (this.options.limit) { 47 | return this.data; 48 | } 49 | 50 | const results: T[] = []; 51 | 52 | for await (const page of this.generatePages({ 53 | after: this.options.after, 54 | })) { 55 | results.push(...page); 56 | } 57 | 58 | return results; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/common/utils/test-utils.ts: -------------------------------------------------------------------------------- 1 | import fetch, { MockParams } from 'jest-fetch-mock'; 2 | 3 | export function fetchOnce( 4 | response: {} = {}, 5 | { status = 200, headers, ...rest }: MockParams = {}, 6 | ) { 7 | return fetch.once(JSON.stringify(response), { 8 | status, 9 | headers: { 'content-type': 'application/json;charset=UTF-8', ...headers }, 10 | ...rest, 11 | }); 12 | } 13 | 14 | export function fetchURL() { 15 | return fetch.mock.calls[0][0]; 16 | } 17 | 18 | export function fetchSearchParams() { 19 | return Object.fromEntries(new URL(String(fetchURL())).searchParams); 20 | } 21 | 22 | export function fetchHeaders() { 23 | return fetch.mock.calls[0][1]?.headers; 24 | } 25 | 26 | export function fetchMethod() { 27 | return fetch.mock.calls[0][1]?.method; 28 | } 29 | 30 | export function fetchBody({ raw = false } = {}) { 31 | const body = fetch.mock.calls[0][1]?.body; 32 | if (body instanceof URLSearchParams) { 33 | return body.toString(); 34 | } 35 | if (raw) { 36 | return body; 37 | } 38 | return JSON.parse(String(body)); 39 | } 40 | -------------------------------------------------------------------------------- /src/common/utils/unreachable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Indicates that code is unreachable. 3 | * 4 | * This can be used for exhaustiveness checks in situations where the compiler 5 | * would not otherwise check for exhaustiveness. 6 | * 7 | * If the determination that the code is unreachable proves incorrect, an 8 | * exception is thrown. 9 | */ 10 | export const unreachable = ( 11 | condition: never, 12 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions 13 | message = `Entered unreachable code. Received '${condition}'.`, 14 | ): never => { 15 | throw new TypeError(message); 16 | }; 17 | -------------------------------------------------------------------------------- /src/common/utils/workos-mock-response.ts: -------------------------------------------------------------------------------- 1 | export const mockWorkOsResponse = (status: number, data: unknown) => ({ 2 | data, 3 | status, 4 | headers: {}, 5 | statusText: '', 6 | config: {} as any, 7 | }); 8 | -------------------------------------------------------------------------------- /src/directory-sync/interfaces/directory-group.interface.ts: -------------------------------------------------------------------------------- 1 | export interface DirectoryGroup { 2 | id: string; 3 | idpId: string; 4 | directoryId: string; 5 | organizationId: string | null; 6 | name: string; 7 | createdAt: string; 8 | updatedAt: string; 9 | rawAttributes: any; 10 | } 11 | 12 | export interface DirectoryGroupResponse { 13 | id: string; 14 | idp_id: string; 15 | directory_id: string; 16 | organization_id: string | null; 17 | name: string; 18 | created_at: string; 19 | updated_at: string; 20 | raw_attributes: any; 21 | } 22 | -------------------------------------------------------------------------------- /src/directory-sync/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './directory.interface'; 2 | export * from './directory-group.interface'; 3 | export * from './list-directories-options.interface'; 4 | export * from './list-groups-options.interface'; 5 | export * from './list-directory-users-options.interface'; 6 | export * from './directory-user.interface'; 7 | -------------------------------------------------------------------------------- /src/directory-sync/interfaces/list-directories-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; 2 | 3 | export interface ListDirectoriesOptions extends PaginationOptions { 4 | organizationId?: string; 5 | search?: string; 6 | } 7 | 8 | export interface SerializedListDirectoriesOptions extends PaginationOptions { 9 | organization_id?: string; 10 | search?: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/directory-sync/interfaces/list-directory-users-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; 2 | 3 | export interface ListDirectoryUsersOptions extends PaginationOptions { 4 | directory?: string; 5 | group?: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/directory-sync/interfaces/list-groups-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; 2 | 3 | export interface ListDirectoryGroupsOptions extends PaginationOptions { 4 | directory?: string; 5 | user?: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/directory-sync/serializers/directory-group.serializer.ts: -------------------------------------------------------------------------------- 1 | import { DirectoryGroup, DirectoryGroupResponse } from '../interfaces'; 2 | 3 | export const deserializeDirectoryGroup = ( 4 | directoryGroup: DirectoryGroupResponse, 5 | ): DirectoryGroup => ({ 6 | id: directoryGroup.id, 7 | idpId: directoryGroup.idp_id, 8 | directoryId: directoryGroup.directory_id, 9 | organizationId: directoryGroup.organization_id, 10 | name: directoryGroup.name, 11 | createdAt: directoryGroup.created_at, 12 | updatedAt: directoryGroup.updated_at, 13 | rawAttributes: directoryGroup.raw_attributes, 14 | }); 15 | 16 | export const deserializeUpdatedEventDirectoryGroup = ( 17 | directoryGroup: DirectoryGroupResponse & Record<'previous_attributes', any>, 18 | ): DirectoryGroup & Record<'previousAttributes', any> => ({ 19 | id: directoryGroup.id, 20 | idpId: directoryGroup.idp_id, 21 | directoryId: directoryGroup.directory_id, 22 | organizationId: directoryGroup.organization_id, 23 | name: directoryGroup.name, 24 | createdAt: directoryGroup.created_at, 25 | updatedAt: directoryGroup.updated_at, 26 | rawAttributes: directoryGroup.raw_attributes, 27 | previousAttributes: directoryGroup.previous_attributes, 28 | }); 29 | -------------------------------------------------------------------------------- /src/directory-sync/serializers/directory.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directory, 3 | DirectoryResponse, 4 | DirectoryState, 5 | DirectoryStateResponse, 6 | EventDirectory, 7 | EventDirectoryResponse, 8 | } from '../interfaces'; 9 | 10 | export const deserializeDirectory = ( 11 | directory: DirectoryResponse, 12 | ): Directory => ({ 13 | object: directory.object, 14 | id: directory.id, 15 | domain: directory.domain, 16 | externalKey: directory.external_key, 17 | name: directory.name, 18 | organizationId: directory.organization_id, 19 | state: deserializeDirectoryState(directory.state), 20 | type: directory.type, 21 | createdAt: directory.created_at, 22 | updatedAt: directory.updated_at, 23 | }); 24 | 25 | export const deserializeDirectoryState = ( 26 | state: DirectoryStateResponse, 27 | ): DirectoryState => { 28 | if (state === 'linked') { 29 | return 'active'; 30 | } 31 | 32 | if (state === 'unlinked') { 33 | return 'inactive'; 34 | } 35 | 36 | return state; 37 | }; 38 | 39 | export const deserializeEventDirectory = ( 40 | directory: EventDirectoryResponse, 41 | ): EventDirectory => ({ 42 | object: directory.object, 43 | id: directory.id, 44 | externalKey: directory.external_key, 45 | type: directory.type, 46 | state: directory.state, 47 | name: directory.name, 48 | organizationId: directory.organization_id, 49 | domains: directory.domains, 50 | createdAt: directory.created_at, 51 | updatedAt: directory.updated_at, 52 | }); 53 | 54 | export const deserializeDeletedEventDirectory = ( 55 | directory: Omit, 56 | ): Omit => ({ 57 | object: directory.object, 58 | id: directory.id, 59 | type: directory.type, 60 | state: directory.state, 61 | name: directory.name, 62 | organizationId: directory.organization_id, 63 | createdAt: directory.created_at, 64 | updatedAt: directory.updated_at, 65 | }); 66 | -------------------------------------------------------------------------------- /src/directory-sync/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './directory-group.serializer'; 2 | export * from './directory-user.serializer'; 3 | export * from './directory.serializer'; 4 | export * from './list-directories-options.serializer'; 5 | -------------------------------------------------------------------------------- /src/directory-sync/serializers/list-directories-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ListDirectoriesOptions, 3 | SerializedListDirectoriesOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeListDirectoriesOptions = ( 7 | options: ListDirectoriesOptions, 8 | ): SerializedListDirectoriesOptions => ({ 9 | organization_id: options.organizationId, 10 | search: options.search, 11 | limit: options.limit, 12 | before: options.before, 13 | after: options.after, 14 | order: options.order, 15 | }); 16 | -------------------------------------------------------------------------------- /src/directory-sync/utils/get-primary-email.spec.ts: -------------------------------------------------------------------------------- 1 | import { getPrimaryEmail } from './get-primary-email'; 2 | import { DirectoryUser } from '../interfaces'; 3 | 4 | describe('getPrimaryEmail', () => { 5 | const user: DirectoryUser = { 6 | object: 'directory_user', 7 | id: 'user_123', 8 | customAttributes: { 9 | custom: true, 10 | }, 11 | directoryId: 'dir_123', 12 | organizationId: 'org_123', 13 | email: 'jonsnow@workos.com', 14 | emails: [ 15 | { 16 | primary: true, 17 | type: 'type', 18 | value: 'jonsnow@workos.com', 19 | }, 20 | ], 21 | firstName: 'Jon', 22 | idpId: 'idp_foo', 23 | lastName: 'Snow', 24 | rawAttributes: {}, 25 | state: 'active', 26 | username: 'jonsnow', 27 | jobTitle: 'Knight of the Watch', 28 | createdAt: '2021-10-27 15:21:50.640958', 29 | updatedAt: '2021-12-13 12:15:45.531847', 30 | }; 31 | 32 | it(`returns primary email value`, () => { 33 | const primaryEmail = getPrimaryEmail(user); 34 | 35 | expect(primaryEmail).toEqual('jonsnow@workos.com'); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/directory-sync/utils/get-primary-email.ts: -------------------------------------------------------------------------------- 1 | import { DirectoryUser } from '../interfaces/directory-user.interface'; 2 | 3 | export function getPrimaryEmail(user: DirectoryUser): string | undefined { 4 | const primaryEmail = user.emails?.find((email) => email.primary); 5 | return primaryEmail?.value; 6 | } 7 | -------------------------------------------------------------------------------- /src/events/events.ts: -------------------------------------------------------------------------------- 1 | import { WorkOS } from '../workos'; 2 | import { ListEventOptions } from './interfaces'; 3 | import { deserializeEvent, deserializeList } from '../common/serializers'; 4 | import { serializeListEventOptions } from './serializers'; 5 | import { Event, EventResponse, List, ListResponse } from '../common/interfaces'; 6 | 7 | export class Events { 8 | constructor(private readonly workos: WorkOS) {} 9 | 10 | async listEvents(options: ListEventOptions): Promise> { 11 | const { data } = await this.workos.get>( 12 | `/events`, 13 | { 14 | query: options ? serializeListEventOptions(options) : undefined, 15 | }, 16 | ); 17 | 18 | return deserializeList(data, deserializeEvent); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/events/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list-events-options.interface'; 2 | -------------------------------------------------------------------------------- /src/events/interfaces/list-events-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { EventName } from '../../common/interfaces'; 2 | 3 | export interface ListEventOptions { 4 | events: EventName[]; 5 | rangeStart?: string; 6 | rangeEnd?: string; 7 | limit?: number; 8 | after?: string; 9 | organizationId?: string; 10 | } 11 | 12 | export interface SerializedListEventOptions { 13 | events: EventName[]; 14 | range_start?: string; 15 | range_end?: string; 16 | limit?: number; 17 | after?: string; 18 | organization_id?: string; 19 | } 20 | -------------------------------------------------------------------------------- /src/events/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list-event-options.serializer'; 2 | -------------------------------------------------------------------------------- /src/events/serializers/list-event-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { ListEventOptions, SerializedListEventOptions } from '../interfaces'; 2 | 3 | export const serializeListEventOptions = ( 4 | options: ListEventOptions, 5 | ): SerializedListEventOptions => ({ 6 | events: options.events, 7 | organization_id: options.organizationId, 8 | range_start: options.rangeStart, 9 | range_end: options.rangeEnd, 10 | limit: options.limit, 11 | after: options.after, 12 | }); 13 | -------------------------------------------------------------------------------- /src/fga/interfaces/check-op.enum.ts: -------------------------------------------------------------------------------- 1 | export enum CheckOp { 2 | AllOf = 'all_of', 3 | AnyOf = 'any_of', 4 | } 5 | -------------------------------------------------------------------------------- /src/fga/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './check-op.enum'; 2 | export * from './check.interface'; 3 | export * from './query.interface'; 4 | export * from './resource-op.enum'; 5 | export * from './resource.interface'; 6 | export * from './warrant-op.enum'; 7 | export * from './warrant-token.interface'; 8 | export * from './warrant.interface'; 9 | -------------------------------------------------------------------------------- /src/fga/interfaces/list.interface.ts: -------------------------------------------------------------------------------- 1 | import { List, ListResponse } from '../../common/interfaces'; 2 | import { Warning } from './warning.interface'; 3 | 4 | export interface FGAListResponse extends ListResponse { 5 | warnings?: Warning[]; 6 | } 7 | 8 | export interface FGAList extends List { 9 | warnings?: Warning[]; 10 | } 11 | -------------------------------------------------------------------------------- /src/fga/interfaces/query.interface.ts: -------------------------------------------------------------------------------- 1 | import { Warrant, PolicyContext, WarrantResponse } from './warrant.interface'; 2 | import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; 3 | import { GetOptions } from '../../common/interfaces'; 4 | 5 | export interface QueryOptions extends PaginationOptions { 6 | q: string; 7 | context?: PolicyContext; 8 | } 9 | 10 | export interface SerializedQueryOptions extends PaginationOptions { 11 | q: string; 12 | context?: string; 13 | } 14 | 15 | export interface QueryResult { 16 | resourceType: string; 17 | resourceId: string; 18 | relation: string; 19 | warrant: Warrant; 20 | isImplicit: boolean; 21 | meta?: { [key: string]: any }; 22 | } 23 | 24 | export interface QueryResultResponse { 25 | resource_type: string; 26 | resource_id: string; 27 | relation: string; 28 | warrant: WarrantResponse; 29 | is_implicit: boolean; 30 | meta?: Record; 31 | } 32 | 33 | export type QueryRequestOptions = Pick; 34 | -------------------------------------------------------------------------------- /src/fga/interfaces/resource-op.enum.ts: -------------------------------------------------------------------------------- 1 | export enum ResourceOp { 2 | Create = 'create', 3 | Delete = 'delete', 4 | } 5 | -------------------------------------------------------------------------------- /src/fga/interfaces/warning.interface.ts: -------------------------------------------------------------------------------- 1 | export interface BaseWarning { 2 | code: string; 3 | message: string; 4 | } 5 | 6 | export interface MissingContextKeysWarning extends BaseWarning { 7 | code: 'missing_context_keys'; 8 | keys: string[]; 9 | } 10 | 11 | export type Warning = BaseWarning | MissingContextKeysWarning; 12 | -------------------------------------------------------------------------------- /src/fga/interfaces/warrant-op.enum.ts: -------------------------------------------------------------------------------- 1 | export enum WarrantOp { 2 | Create = 'create', 3 | Delete = 'delete', 4 | } 5 | -------------------------------------------------------------------------------- /src/fga/interfaces/warrant-token.interface.ts: -------------------------------------------------------------------------------- 1 | export interface WarrantToken { 2 | warrantToken: string; 3 | } 4 | 5 | export interface WarrantTokenResponse { 6 | warrant_token: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/fga/interfaces/warrant.interface.ts: -------------------------------------------------------------------------------- 1 | import { GetOptions } from '../../common/interfaces'; 2 | import { ResourceInterface, ResourceOptions } from './resource.interface'; 3 | import { WarrantOp } from './warrant-op.enum'; 4 | 5 | export interface ListWarrantsOptions { 6 | resourceType?: string; 7 | resourceId?: string; 8 | relation?: string; 9 | subjectType?: string; 10 | subjectId?: string; 11 | subjectRelation?: string; 12 | limit?: number; 13 | after?: string; 14 | } 15 | 16 | export interface SerializedListWarrantsOptions { 17 | resource_type?: string; 18 | resource_id?: string; 19 | relation?: string; 20 | subject_type?: string; 21 | subject_id?: string; 22 | subject_relation?: string; 23 | limit?: number; 24 | after?: string; 25 | } 26 | 27 | export interface PolicyContext { 28 | [key: string]: any; 29 | } 30 | 31 | export interface Subject { 32 | resourceType: string; 33 | resourceId: string; 34 | relation?: string; 35 | } 36 | 37 | export interface SerializedSubject { 38 | resource_type: string; 39 | resource_id: string; 40 | relation?: string; 41 | } 42 | 43 | export interface Warrant { 44 | resourceType: string; 45 | resourceId: string; 46 | relation: string; 47 | subject: Subject; 48 | policy?: string; 49 | } 50 | 51 | export interface WriteWarrantOptions { 52 | op?: WarrantOp; 53 | resource: ResourceInterface | ResourceOptions; 54 | relation: string; 55 | subject: ResourceInterface | Subject; 56 | policy?: string; 57 | } 58 | 59 | export interface SerializedWriteWarrantOptions { 60 | op?: WarrantOp; 61 | resource_type: string; 62 | resource_id: string; 63 | relation: string; 64 | subject: SerializedSubject; 65 | policy?: string; 66 | } 67 | 68 | export type ListWarrantsRequestOptions = Pick; 69 | 70 | export interface WarrantResponse { 71 | resource_type: string; 72 | resource_id: string; 73 | relation: string; 74 | subject: SerializedSubject; 75 | policy?: string; 76 | } 77 | -------------------------------------------------------------------------------- /src/fga/serializers/batch-write-resources-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BatchWriteResourcesOptions, 3 | CreateResourceOptions, 4 | DeleteResourceOptions, 5 | ResourceOp, 6 | SerializedBatchWriteResourcesOptions, 7 | SerializedCreateResourceOptions, 8 | SerializedDeleteResourceOptions, 9 | } from '../interfaces'; 10 | import { serializeCreateResourceOptions } from './create-resource-options.serializer'; 11 | import { serializeDeleteResourceOptions } from './delete-resource-options.serializer'; 12 | 13 | export const serializeBatchWriteResourcesOptions = ( 14 | options: BatchWriteResourcesOptions, 15 | ): SerializedBatchWriteResourcesOptions => { 16 | let serializedResources: 17 | | SerializedCreateResourceOptions[] 18 | | SerializedDeleteResourceOptions[] = []; 19 | if (options.op === ResourceOp.Create) { 20 | const resources = options.resources as CreateResourceOptions[]; 21 | serializedResources = resources.map((options: CreateResourceOptions) => 22 | serializeCreateResourceOptions(options), 23 | ); 24 | } else if (options.op === ResourceOp.Delete) { 25 | const resources = options.resources as DeleteResourceOptions[]; 26 | serializedResources = resources.map((options: DeleteResourceOptions) => 27 | serializeDeleteResourceOptions(options), 28 | ); 29 | } 30 | 31 | return { 32 | op: options.op, 33 | resources: serializedResources, 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /src/fga/serializers/create-resource-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateResourceOptions, 3 | SerializedCreateResourceOptions, 4 | } from '../interfaces'; 5 | import { isResourceInterface } from '../utils/interface-check'; 6 | 7 | export const serializeCreateResourceOptions = ( 8 | options: CreateResourceOptions, 9 | ): SerializedCreateResourceOptions => ({ 10 | resource_type: isResourceInterface(options.resource) 11 | ? options.resource.getResourceType() 12 | : options.resource.resourceType, 13 | resource_id: isResourceInterface(options.resource) 14 | ? options.resource.getResourceId() 15 | : options.resource.resourceId 16 | ? options.resource.resourceId 17 | : '', 18 | meta: options.meta, 19 | }); 20 | -------------------------------------------------------------------------------- /src/fga/serializers/delete-resource-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DeleteResourceOptions, 3 | SerializedDeleteResourceOptions, 4 | } from '../interfaces'; 5 | import { isResourceInterface } from '../utils/interface-check'; 6 | 7 | export const serializeDeleteResourceOptions = ( 8 | options: DeleteResourceOptions, 9 | ): SerializedDeleteResourceOptions => ({ 10 | resource_type: isResourceInterface(options) 11 | ? options.getResourceType() 12 | : options.resourceType, 13 | resource_id: isResourceInterface(options) 14 | ? options.getResourceId() 15 | : options.resourceId 16 | ? options.resourceId 17 | : '', 18 | }); 19 | -------------------------------------------------------------------------------- /src/fga/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './check-options.serializer'; 2 | export * from './batch-write-resources-options.serializer'; 3 | export * from './create-resource-options.serializer'; 4 | export * from './delete-resource-options.serializer'; 5 | export * from './list-resources-options.serializer'; 6 | export * from './list-warrants-options.serializer'; 7 | export * from './query-options.serializer'; 8 | export * from './query-result.serializer'; 9 | export * from './resource.serializer'; 10 | export * from './warrant-token.serializer'; 11 | export * from './warrant.serializer'; 12 | export * from './write-warrant-options.serializer'; 13 | export * from './list.serializer'; 14 | -------------------------------------------------------------------------------- /src/fga/serializers/list-resources-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ListResourcesOptions, 3 | SerializedListResourcesOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeListResourceOptions = ( 7 | options: ListResourcesOptions, 8 | ): SerializedListResourcesOptions => ({ 9 | resource_type: options.resourceType, 10 | search: options.search, 11 | limit: options.limit, 12 | before: options.before, 13 | after: options.after, 14 | order: options.order, 15 | }); 16 | -------------------------------------------------------------------------------- /src/fga/serializers/list-warrants-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ListWarrantsOptions, 3 | SerializedListWarrantsOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeListWarrantsOptions = ( 7 | options: ListWarrantsOptions, 8 | ): SerializedListWarrantsOptions => ({ 9 | resource_type: options.resourceType, 10 | resource_id: options.resourceId, 11 | relation: options.relation, 12 | subject_type: options.subjectType, 13 | subject_id: options.subjectId, 14 | subject_relation: options.subjectRelation, 15 | limit: options.limit, 16 | after: options.after, 17 | }); 18 | -------------------------------------------------------------------------------- /src/fga/serializers/list.serializer.ts: -------------------------------------------------------------------------------- 1 | import { FGAList } from '../interfaces/list.interface'; 2 | import { ListResponse } from '../../common/interfaces'; 3 | 4 | export const deserializeFGAList = ( 5 | response: ListResponse & { warnings?: any[] }, 6 | deserializeFn: (data: T) => U, 7 | ): FGAList => ({ 8 | object: 'list', 9 | data: response.data.map(deserializeFn), 10 | listMetadata: response.list_metadata, 11 | warnings: response.warnings, 12 | }); 13 | -------------------------------------------------------------------------------- /src/fga/serializers/query-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { QueryOptions, SerializedQueryOptions } from '../interfaces'; 2 | 3 | export const serializeQueryOptions = ( 4 | options: QueryOptions, 5 | ): SerializedQueryOptions => ({ 6 | q: options.q, 7 | context: JSON.stringify(options.context), 8 | limit: options.limit, 9 | before: options.before, 10 | after: options.after, 11 | order: options.order, 12 | }); 13 | -------------------------------------------------------------------------------- /src/fga/serializers/query-result.serializer.ts: -------------------------------------------------------------------------------- 1 | import { QueryResult, QueryResultResponse } from '../interfaces'; 2 | import { Warning } from '../interfaces/warning.interface'; 3 | import { ListResponse } from '../../common/interfaces'; 4 | 5 | export interface QueryResultListResponse 6 | extends ListResponse { 7 | warnings?: Warning[]; 8 | } 9 | 10 | export const deserializeQueryResult = ( 11 | queryResult: QueryResultResponse, 12 | ): QueryResult => ({ 13 | resourceType: queryResult.resource_type, 14 | resourceId: queryResult.resource_id, 15 | relation: queryResult.relation, 16 | warrant: { 17 | resourceType: queryResult.warrant.resource_type, 18 | resourceId: queryResult.warrant.resource_id, 19 | relation: queryResult.warrant.relation, 20 | subject: { 21 | resourceType: queryResult.warrant.subject.resource_type, 22 | resourceId: queryResult.warrant.subject.resource_id, 23 | relation: queryResult.warrant.subject.relation, 24 | }, 25 | }, 26 | isImplicit: queryResult.is_implicit, 27 | meta: queryResult.meta, 28 | }); 29 | -------------------------------------------------------------------------------- /src/fga/serializers/resource.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BatchWriteResourcesResponse, 3 | Resource, 4 | ResourceResponse, 5 | } from '../interfaces'; 6 | 7 | export const deserializeResource = (response: ResourceResponse): Resource => ({ 8 | resourceType: response.resource_type, 9 | resourceId: response.resource_id, 10 | meta: response.meta, 11 | }); 12 | 13 | export const deserializeBatchWriteResourcesResponse = ( 14 | response: BatchWriteResourcesResponse, 15 | ): Resource[] => { 16 | return response.data.map((resource) => deserializeResource(resource)); 17 | }; 18 | -------------------------------------------------------------------------------- /src/fga/serializers/warrant-token.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WarrantToken, 3 | WarrantTokenResponse, 4 | } from '../interfaces/warrant-token.interface'; 5 | 6 | export const deserializeWarrantToken = ( 7 | warrantToken: WarrantTokenResponse, 8 | ): WarrantToken => ({ 9 | warrantToken: warrantToken.warrant_token, 10 | }); 11 | -------------------------------------------------------------------------------- /src/fga/serializers/warrant.serializer.ts: -------------------------------------------------------------------------------- 1 | import { Warrant, WarrantResponse } from '../interfaces'; 2 | 3 | export const deserializeWarrant = (warrant: WarrantResponse): Warrant => ({ 4 | resourceType: warrant.resource_type, 5 | resourceId: warrant.resource_id, 6 | relation: warrant.relation, 7 | subject: { 8 | resourceType: warrant.subject.resource_type, 9 | resourceId: warrant.subject.resource_id, 10 | relation: warrant.subject.relation, 11 | }, 12 | policy: warrant.policy, 13 | }); 14 | -------------------------------------------------------------------------------- /src/fga/serializers/write-warrant-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SerializedWriteWarrantOptions, 3 | WriteWarrantOptions, 4 | } from '../interfaces'; 5 | import { isSubject, isResourceInterface } from '../utils/interface-check'; 6 | 7 | export const serializeWriteWarrantOptions = ( 8 | warrant: WriteWarrantOptions, 9 | ): SerializedWriteWarrantOptions => ({ 10 | op: warrant.op, 11 | resource_type: isResourceInterface(warrant.resource) 12 | ? warrant.resource.getResourceType() 13 | : warrant.resource.resourceType, 14 | resource_id: isResourceInterface(warrant.resource) 15 | ? warrant.resource.getResourceId() 16 | : warrant.resource.resourceId 17 | ? warrant.resource.resourceId 18 | : '', 19 | relation: warrant.relation, 20 | subject: isSubject(warrant.subject) 21 | ? { 22 | resource_type: warrant.subject.resourceType, 23 | resource_id: warrant.subject.resourceId, 24 | } 25 | : { 26 | resource_type: warrant.subject.getResourceType(), 27 | resource_id: warrant.subject.getResourceId(), 28 | }, 29 | policy: warrant.policy, 30 | }); 31 | -------------------------------------------------------------------------------- /src/fga/utils/fetch-and-deserialize-list.ts: -------------------------------------------------------------------------------- 1 | import { WorkOS } from '../../workos'; 2 | import { FGAList } from '../interfaces/list.interface'; 3 | import { QueryRequestOptions } from '../interfaces'; 4 | import { PaginationOptions } from '../../common/interfaces'; 5 | import { ListResponse } from '../../common/interfaces'; 6 | import { deserializeFGAList } from '../serializers/list.serializer'; 7 | 8 | export const fetchAndDeserializeFGAList = async ( 9 | workos: WorkOS, 10 | endpoint: string, 11 | deserializeFn: (data: T) => U, 12 | options?: PaginationOptions, 13 | requestOptions?: QueryRequestOptions, 14 | ): Promise> => { 15 | const { data: response } = await workos.get< 16 | ListResponse & { warnings?: any[] } 17 | >(endpoint, { 18 | query: options, 19 | ...requestOptions, 20 | }); 21 | 22 | return deserializeFGAList(response, deserializeFn); 23 | }; 24 | -------------------------------------------------------------------------------- /src/fga/utils/fga-paginatable.ts: -------------------------------------------------------------------------------- 1 | import { AutoPaginatable } from '../../common/utils/pagination'; 2 | import { FGAList } from '../interfaces/list.interface'; 3 | import { Warning } from '../interfaces/warning.interface'; 4 | import { PaginationOptions } from '../../common/interfaces'; 5 | 6 | export class FgaPaginatable extends AutoPaginatable { 7 | protected override list!: FGAList; 8 | 9 | constructor( 10 | list: FGAList, 11 | apiCall: (params: PaginationOptions) => Promise>, 12 | options?: PaginationOptions, 13 | ) { 14 | super(list, apiCall, options); 15 | } 16 | 17 | get warnings(): Warning[] | undefined { 18 | return this.list.warnings; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/fga/utils/interface-check.ts: -------------------------------------------------------------------------------- 1 | import { Subject, ResourceInterface } from '../interfaces'; 2 | 3 | export function isSubject(resource: any): resource is Subject { 4 | return ( 5 | Object.prototype.hasOwnProperty.call(resource, 'resourceType') && 6 | Object.prototype.hasOwnProperty.call(resource, 'resourceId') 7 | ); 8 | } 9 | 10 | export function isResourceInterface( 11 | resource: unknown, 12 | ): resource is ResourceInterface { 13 | return ( 14 | !!resource && 15 | typeof resource === 'object' && 16 | 'getResouceType' in resource && 17 | 'getResourceId' in resource 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/mfa/interfaces/challenge-factor-options.ts: -------------------------------------------------------------------------------- 1 | export type ChallengeFactorOptions = 2 | | { 3 | authenticationFactorId: string; 4 | } 5 | | { 6 | authenticationFactorId: string; 7 | smsTemplate: string; 8 | }; 9 | -------------------------------------------------------------------------------- /src/mfa/interfaces/challenge.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Challenge { 2 | object: 'authentication_challenge'; 3 | id: string; 4 | createdAt: string; 5 | updatedAt: string; 6 | expiresAt?: string; 7 | code?: string; 8 | authenticationFactorId: string; 9 | } 10 | 11 | export interface ChallengeResponse { 12 | object: 'authentication_challenge'; 13 | id: string; 14 | created_at: string; 15 | updated_at: string; 16 | expires_at?: string; 17 | code?: string; 18 | authentication_factor_id: string; 19 | } 20 | -------------------------------------------------------------------------------- /src/mfa/interfaces/enroll-factor-options.ts: -------------------------------------------------------------------------------- 1 | export type EnrollFactorOptions = 2 | | { 3 | type: 'sms'; 4 | phoneNumber: string; 5 | } 6 | | { 7 | type: 'totp'; 8 | issuer: string; 9 | user: string; 10 | } 11 | | { 12 | type: 'generic_otp'; 13 | }; 14 | -------------------------------------------------------------------------------- /src/mfa/interfaces/factor.interface.ts: -------------------------------------------------------------------------------- 1 | import { Sms, SmsResponse } from './sms.interface'; 2 | import { 3 | Totp, 4 | TotpResponse, 5 | TotpWithSecrets, 6 | TotpWithSecretsResponse, 7 | } from './totp.interface'; 8 | 9 | type FactorType = 'sms' | 'totp' | 'generic_otp'; 10 | 11 | export interface Factor { 12 | object: 'authentication_factor'; 13 | id: string; 14 | createdAt: string; 15 | updatedAt: string; 16 | type: FactorType; 17 | sms?: Sms; 18 | totp?: Totp; 19 | } 20 | 21 | export interface FactorWithSecrets { 22 | object: 'authentication_factor'; 23 | id: string; 24 | createdAt: string; 25 | updatedAt: string; 26 | type: FactorType; 27 | sms?: Sms; 28 | totp?: TotpWithSecrets; 29 | } 30 | 31 | export interface FactorResponse { 32 | object: 'authentication_factor'; 33 | id: string; 34 | created_at: string; 35 | updated_at: string; 36 | type: FactorType; 37 | sms?: SmsResponse; 38 | totp?: TotpResponse; 39 | } 40 | 41 | export interface FactorWithSecretsResponse { 42 | object: 'authentication_factor'; 43 | id: string; 44 | created_at: string; 45 | updated_at: string; 46 | type: FactorType; 47 | sms?: SmsResponse; 48 | totp?: TotpWithSecretsResponse; 49 | } 50 | -------------------------------------------------------------------------------- /src/mfa/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './challenge-factor-options'; 2 | export * from './challenge.interface'; 3 | export * from './enroll-factor-options'; 4 | export * from './factor.interface'; 5 | export * from './sms.interface'; 6 | export * from './totp.interface'; 7 | export * from './verify-challenge-options'; 8 | export * from './verify-challenge-response'; 9 | export * from './verify-factor-options'; 10 | -------------------------------------------------------------------------------- /src/mfa/interfaces/sms.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Sms { 2 | phoneNumber: string; 3 | } 4 | 5 | export interface SmsResponse { 6 | phone_number: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/mfa/interfaces/totp.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Totp { 2 | issuer: string; 3 | user: string; 4 | } 5 | 6 | export interface TotpWithSecrets extends Totp { 7 | qrCode: string; 8 | secret: string; 9 | uri: string; 10 | } 11 | 12 | export interface TotpResponse { 13 | issuer: string; 14 | user: string; 15 | } 16 | 17 | export interface TotpWithSecretsResponse extends TotpResponse { 18 | qr_code: string; 19 | secret: string; 20 | uri: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/mfa/interfaces/verify-challenge-options.ts: -------------------------------------------------------------------------------- 1 | export interface VerifyChallengeOptions { 2 | authenticationChallengeId: string; 3 | code: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/mfa/interfaces/verify-challenge-response.ts: -------------------------------------------------------------------------------- 1 | import { Challenge, ChallengeResponse } from './challenge.interface'; 2 | 3 | export interface VerifyResponse { 4 | challenge: Challenge; 5 | valid: boolean; 6 | } 7 | 8 | export interface VerifyResponseResponse { 9 | challenge: ChallengeResponse; 10 | valid: boolean; 11 | } 12 | -------------------------------------------------------------------------------- /src/mfa/interfaces/verify-factor-options.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @deprecated Please use `VerifyChallengeOptions` instead. 3 | */ 4 | export type VerifyFactorOptions = { 5 | authenticationChallengeId: string; 6 | code: string; 7 | }; 8 | -------------------------------------------------------------------------------- /src/mfa/serializers/challenge.serializer.ts: -------------------------------------------------------------------------------- 1 | import { Challenge, ChallengeResponse } from '../interfaces'; 2 | 3 | export const deserializeChallenge = ( 4 | challenge: ChallengeResponse, 5 | ): Challenge => ({ 6 | object: challenge.object, 7 | id: challenge.id, 8 | createdAt: challenge.created_at, 9 | updatedAt: challenge.updated_at, 10 | expiresAt: challenge.expires_at, 11 | code: challenge.code, 12 | authenticationFactorId: challenge.authentication_factor_id, 13 | }); 14 | -------------------------------------------------------------------------------- /src/mfa/serializers/factor.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Factor, 3 | FactorResponse, 4 | FactorWithSecrets, 5 | FactorWithSecretsResponse, 6 | } from '../interfaces'; 7 | import { deserializeSms } from './sms.serializer'; 8 | import { deserializeTotp, deserializeTotpWithSecrets } from './totp.serializer'; 9 | 10 | export const deserializeFactor = (factor: FactorResponse): Factor => ({ 11 | object: factor.object, 12 | id: factor.id, 13 | createdAt: factor.created_at, 14 | updatedAt: factor.updated_at, 15 | type: factor.type, 16 | ...(factor.sms ? { sms: deserializeSms(factor.sms) } : {}), 17 | ...(factor.totp ? { totp: deserializeTotp(factor.totp) } : {}), 18 | }); 19 | 20 | export const deserializeFactorWithSecrets = ( 21 | factor: FactorWithSecretsResponse, 22 | ): FactorWithSecrets => ({ 23 | object: factor.object, 24 | id: factor.id, 25 | createdAt: factor.created_at, 26 | updatedAt: factor.updated_at, 27 | type: factor.type, 28 | ...(factor.sms ? { sms: deserializeSms(factor.sms) } : {}), 29 | ...(factor.totp ? { totp: deserializeTotpWithSecrets(factor.totp) } : {}), 30 | }); 31 | -------------------------------------------------------------------------------- /src/mfa/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './challenge.serializer'; 2 | export * from './factor.serializer'; 3 | export * from './verify-response.serializer'; 4 | -------------------------------------------------------------------------------- /src/mfa/serializers/sms.serializer.ts: -------------------------------------------------------------------------------- 1 | import { Sms, SmsResponse } from '../interfaces'; 2 | 3 | export const deserializeSms = (sms: SmsResponse): Sms => ({ 4 | phoneNumber: sms.phone_number, 5 | }); 6 | -------------------------------------------------------------------------------- /src/mfa/serializers/totp.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Totp, 3 | TotpResponse, 4 | TotpWithSecretsResponse, 5 | TotpWithSecrets, 6 | } from '../interfaces'; 7 | 8 | export const deserializeTotp = (totp: TotpResponse): Totp => { 9 | return { 10 | issuer: totp.issuer, 11 | user: totp.user, 12 | }; 13 | }; 14 | 15 | export const deserializeTotpWithSecrets = ( 16 | totp: TotpWithSecretsResponse, 17 | ): TotpWithSecrets => { 18 | return { 19 | issuer: totp.issuer, 20 | user: totp.user, 21 | qrCode: totp.qr_code, 22 | secret: totp.secret, 23 | uri: totp.uri, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /src/mfa/serializers/verify-response.serializer.ts: -------------------------------------------------------------------------------- 1 | import { VerifyResponse, VerifyResponseResponse } from '../interfaces'; 2 | import { deserializeChallenge } from './challenge.serializer'; 3 | 4 | export const deserializeVerifyResponse = ( 5 | verifyResponse: VerifyResponseResponse, 6 | ): VerifyResponse => ({ 7 | challenge: deserializeChallenge(verifyResponse.challenge), 8 | valid: verifyResponse.valid, 9 | }); 10 | -------------------------------------------------------------------------------- /src/organization-domains/fixtures/get-organization-domain-pending.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "organization_domain", 3 | "id": "org_domain_01HD50K7EPWCMNPGMKXKKE14XT", 4 | "organization_id": "org_01JR8C1EHCRPV4B4XP4W2B9X1M", 5 | "domain": "workos.com", 6 | "state": "pending", 7 | "verification_token": "F06PGMsZIO0shrveGWuGxgCj7", 8 | "verification_strategy": "dns" 9 | } 10 | -------------------------------------------------------------------------------- /src/organization-domains/fixtures/get-organization-domain-verified.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "organization_domain", 3 | "id": "org_domain_01HCZRAP3TPQ0X0DKJHR32TATG", 4 | "organization_id": "org_01JR8C1EHCRPV4B4XP4W2B9X1M", 5 | "domain": "workos.com", 6 | "state": "verified", 7 | "verification_token": null, 8 | "verification_strategy": "manual" 9 | } 10 | -------------------------------------------------------------------------------- /src/organization-domains/interfaces/create-organization-domain-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreateOrganizationDomainOptions { 2 | domain: string; 3 | organizationId: string; 4 | } 5 | 6 | export interface SerializedCreateOrganizationDomainOptions { 7 | domain: string; 8 | organization_id: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/organization-domains/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-organization-domain-options.interface'; 2 | export * from './organization-domain.interface'; 3 | -------------------------------------------------------------------------------- /src/organization-domains/interfaces/organization-domain.interface.ts: -------------------------------------------------------------------------------- 1 | export enum OrganizationDomainState { 2 | /** 3 | * @deprecated 4 | */ 5 | LegacyVerified = 'legacy_verified', 6 | Verified = 'verified', 7 | Pending = 'pending', 8 | Failed = 'failed', 9 | } 10 | 11 | export enum OrganizationDomainVerificationStrategy { 12 | Dns = 'dns', 13 | Manual = 'manual', 14 | } 15 | 16 | export interface OrganizationDomain { 17 | object: 'organization_domain'; 18 | id: string; 19 | domain: string; 20 | organizationId: string; 21 | state: OrganizationDomainState; 22 | verificationToken?: string; 23 | verificationStrategy: OrganizationDomainVerificationStrategy; 24 | } 25 | 26 | export interface OrganizationDomainResponse { 27 | object: 'organization_domain'; 28 | id: string; 29 | domain: string; 30 | organization_id: string; 31 | state: OrganizationDomainState; 32 | verification_token?: string; 33 | verification_strategy: OrganizationDomainVerificationStrategy; 34 | } 35 | -------------------------------------------------------------------------------- /src/organization-domains/organization-domains.ts: -------------------------------------------------------------------------------- 1 | import { WorkOS } from '../workos'; 2 | import { 3 | CreateOrganizationDomainOptions, 4 | OrganizationDomain, 5 | OrganizationDomainResponse, 6 | } from './interfaces'; 7 | import { serializeCreateOrganizationDomainOptions } from './serializers/create-organization-domain-options.serializer'; 8 | import { deserializeOrganizationDomain } from './serializers/organization-domain.serializer'; 9 | 10 | export class OrganizationDomains { 11 | constructor(private readonly workos: WorkOS) {} 12 | 13 | async get(id: string): Promise { 14 | const { data } = await this.workos.get( 15 | `/organization_domains/${id}`, 16 | ); 17 | 18 | return deserializeOrganizationDomain(data); 19 | } 20 | 21 | async verify(id: string): Promise { 22 | const { data } = await this.workos.post( 23 | `/organization_domains/${id}/verify`, 24 | {}, 25 | ); 26 | 27 | return deserializeOrganizationDomain(data); 28 | } 29 | 30 | async create( 31 | payload: CreateOrganizationDomainOptions, 32 | ): Promise { 33 | const { data } = await this.workos.post( 34 | `/organization_domains`, 35 | serializeCreateOrganizationDomainOptions(payload), 36 | ); 37 | 38 | return deserializeOrganizationDomain(data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/organization-domains/serializers/create-organization-domain-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateOrganizationDomainOptions, 3 | SerializedCreateOrganizationDomainOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeCreateOrganizationDomainOptions = ( 7 | options: CreateOrganizationDomainOptions, 8 | ): SerializedCreateOrganizationDomainOptions => ({ 9 | domain: options.domain, 10 | organization_id: options.organizationId, 11 | }); 12 | -------------------------------------------------------------------------------- /src/organization-domains/serializers/organization-domain.serializer.ts: -------------------------------------------------------------------------------- 1 | import { OrganizationDomain, OrganizationDomainResponse } from '../interfaces'; 2 | 3 | export const deserializeOrganizationDomain = ( 4 | organizationDomain: OrganizationDomainResponse, 5 | ): OrganizationDomain => ({ 6 | object: organizationDomain.object, 7 | id: organizationDomain.id, 8 | domain: organizationDomain.domain, 9 | organizationId: organizationDomain.organization_id, 10 | state: organizationDomain.state, 11 | verificationToken: organizationDomain.verification_token, 12 | verificationStrategy: organizationDomain.verification_strategy, 13 | }); 14 | -------------------------------------------------------------------------------- /src/organizations/fixtures/clear-stripe-customer-id.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Test Organization 3", 3 | "object": "organization", 4 | "id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", 5 | "allow_profiles_outside_organization": false, 6 | "domains": [ 7 | { 8 | "domain": "example.com", 9 | "object": "organization_domain", 10 | "id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/organizations/fixtures/create-organization-invalid.json: -------------------------------------------------------------------------------- 1 | { "message": "An Organization with the domain example.com already exists."} 2 | -------------------------------------------------------------------------------- /src/organizations/fixtures/create-organization.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Test Organization", 3 | "object": "organization", 4 | "id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", 5 | "allow_profiles_outside_organization": false, 6 | "domains": [ 7 | { 8 | "domain": "example.com", 9 | "object": "organization_domain", 10 | "id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/organizations/fixtures/get-organization.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Test Organization 3", 3 | "object": "organization", 4 | "id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", 5 | "allow_profiles_outside_organization": false, 6 | "domains": [ 7 | { 8 | "domain": "example.com", 9 | "object": "organization_domain", 10 | "id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8", 11 | "state": "verified", 12 | "verification_strategy": "dns", 13 | "verification_token": "xB8SeACdKJQP9DP4CahU4YuQZ" 14 | } 15 | ], 16 | "metadata": {} 17 | } 18 | -------------------------------------------------------------------------------- /src/organizations/fixtures/list-organization-roles.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "list", 3 | "data": [ 4 | { 5 | "object": "role", 6 | "id": "role_01EHQMYV6MBK39QC5PZXHY59C5", 7 | "name": "Admin", 8 | "slug": "admin", 9 | "description": null, 10 | "permissions": [ 11 | "posts:create", 12 | "posts:delete" 13 | ], 14 | "type": "EnvironmentRole", 15 | "created_at": "2024-01-01T00:00:00.000Z", 16 | "updated_at": "2024-01-01T00:00:00.000Z" 17 | }, 18 | { 19 | "object": "role", 20 | "id": "role_01EHQMYV6MBK39QC5PZXHY59C3", 21 | "name": "Member", 22 | "slug": "member", 23 | "description": null, 24 | "permissions": [], 25 | "type": "EnvironmentRole", 26 | "created_at": "2024-01-01T00:00:00.000Z", 27 | "updated_at": "2024-01-01T00:00:00.000Z" 28 | }, 29 | { 30 | "object": "role", 31 | "id": "role_01EHQMYV6MBK39QC5PZXHY59C3", 32 | "name": "OrganizationMember", 33 | "slug": "org-member", 34 | "description": null, 35 | "permissions": [ 36 | "posts:read" 37 | ], 38 | "type": "OrganizationRole", 39 | "created_at": "2024-01-01T00:00:00.000Z", 40 | "updated_at": "2024-01-01T00:00:00.000Z" 41 | } 42 | ] 43 | } -------------------------------------------------------------------------------- /src/organizations/fixtures/set-stripe-customer-id-disabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "stripe_customer_id is not enabled for this environment", 3 | "error": "Unprocessable Entity" 4 | } 5 | -------------------------------------------------------------------------------- /src/organizations/fixtures/set-stripe-customer-id.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Test Organization 3", 3 | "object": "organization", 4 | "id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", 5 | "allow_profiles_outside_organization": false, 6 | "domains": [ 7 | { 8 | "domain": "example.com", 9 | "object": "organization_domain", 10 | "id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8" 11 | } 12 | ], 13 | "stripe_customer_id": "cus_MX8J9nfK4lP2Yw" 14 | } 15 | -------------------------------------------------------------------------------- /src/organizations/fixtures/update-organization.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Test Organization 2", 3 | "object": "organization", 4 | "id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", 5 | "allow_profiles_outside_organization": false, 6 | "domains": [ 7 | { 8 | "domain": "example.com", 9 | "object": "organization_domain", 10 | "id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/organizations/interfaces/create-organization-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PostOptions } from '../../common/interfaces'; 2 | import { DomainData } from './domain-data.interface'; 3 | 4 | export interface CreateOrganizationOptions { 5 | name: string; 6 | domainData?: DomainData[]; 7 | externalId?: string | null; 8 | metadata?: Record; 9 | 10 | /** 11 | * @deprecated If you need to allow sign-ins from any email domain, contact support@workos.com. 12 | */ 13 | allowProfilesOutsideOrganization?: boolean; 14 | /** 15 | * @deprecated Use `domain_data` instead. 16 | */ 17 | domains?: string[]; 18 | } 19 | 20 | export interface SerializedCreateOrganizationOptions { 21 | name: string; 22 | domain_data?: DomainData[]; 23 | external_id?: string | null; 24 | metadata?: Record; 25 | 26 | /** 27 | * @deprecated If you need to allow sign-ins from any email domain, contact support@workos.com. 28 | */ 29 | allow_profiles_outside_organization?: boolean; 30 | /** 31 | * @deprecated Use `domain_data` instead. 32 | */ 33 | domains?: string[]; 34 | } 35 | 36 | export interface CreateOrganizationRequestOptions 37 | extends Pick {} 38 | -------------------------------------------------------------------------------- /src/organizations/interfaces/domain-data.interface.ts: -------------------------------------------------------------------------------- 1 | // These are the only possible states to create an organization domain with 2 | export enum DomainDataState { 3 | Verified = 'verified', 4 | Pending = 'pending', 5 | } 6 | 7 | export interface DomainData { 8 | domain: string; 9 | state: DomainDataState; 10 | } 11 | -------------------------------------------------------------------------------- /src/organizations/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-organization-options.interface'; 2 | export * from './domain-data.interface'; 3 | export * from './list-organizations-options.interface'; 4 | export * from './organization.interface'; 5 | export * from './update-organization-options.interface'; 6 | -------------------------------------------------------------------------------- /src/organizations/interfaces/list-organization-roles-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ListOrganizationRolesOptions { 2 | organizationId: string; 3 | } 4 | -------------------------------------------------------------------------------- /src/organizations/interfaces/list-organizations-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; 2 | 3 | export interface ListOrganizationsOptions extends PaginationOptions { 4 | domains?: string[]; 5 | } 6 | -------------------------------------------------------------------------------- /src/organizations/interfaces/organization.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OrganizationDomain, 3 | OrganizationDomainResponse, 4 | } from '../../organization-domains/interfaces/organization-domain.interface'; 5 | 6 | export interface Organization { 7 | object: 'organization'; 8 | id: string; 9 | name: string; 10 | allowProfilesOutsideOrganization: boolean; 11 | domains: OrganizationDomain[]; 12 | stripeCustomerId?: string; 13 | createdAt: string; 14 | updatedAt: string; 15 | externalId: string | null; 16 | metadata: Record; 17 | } 18 | 19 | export interface OrganizationResponse { 20 | object: 'organization'; 21 | id: string; 22 | name: string; 23 | allow_profiles_outside_organization: boolean; 24 | domains: OrganizationDomainResponse[]; 25 | stripe_customer_id?: string; 26 | created_at: string; 27 | updated_at: string; 28 | external_id?: string | null; 29 | metadata?: Record; 30 | } 31 | -------------------------------------------------------------------------------- /src/organizations/interfaces/update-organization-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { DomainData } from './domain-data.interface'; 2 | 3 | export interface UpdateOrganizationOptions { 4 | organization: string; 5 | name?: string; 6 | domainData?: DomainData[]; 7 | stripeCustomerId?: string | null; 8 | externalId?: string | null; 9 | metadata?: Record; 10 | 11 | /** 12 | * @deprecated If you need to allow sign-ins from any email domain, contact support@workos.com. 13 | */ 14 | allowProfilesOutsideOrganization?: boolean; 15 | /** 16 | * @deprecated Use `domain_data` instead. 17 | */ 18 | domains?: string[]; 19 | } 20 | 21 | export interface SerializedUpdateOrganizationOptions { 22 | name?: string; 23 | domain_data?: DomainData[]; 24 | stripe_customer_id?: string | null; 25 | external_id?: string | null; 26 | metadata?: Record; 27 | 28 | /** 29 | * @deprecated If you need to allow sign-ins from any email domain, contact support@workos.com. 30 | */ 31 | allow_profiles_outside_organization?: boolean; 32 | /** 33 | * @deprecated Use `domain_data` instead. 34 | */ 35 | domains?: string[]; 36 | } 37 | -------------------------------------------------------------------------------- /src/organizations/serializers/create-organization-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateOrganizationOptions, 3 | SerializedCreateOrganizationOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeCreateOrganizationOptions = ( 7 | options: CreateOrganizationOptions, 8 | ): SerializedCreateOrganizationOptions => ({ 9 | name: options.name, 10 | allow_profiles_outside_organization: options.allowProfilesOutsideOrganization, 11 | domain_data: options.domainData, 12 | domains: options.domains, 13 | external_id: options.externalId, 14 | metadata: options.metadata, 15 | }); 16 | -------------------------------------------------------------------------------- /src/organizations/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-organization-options.serializer'; 2 | export * from './organization.serializer'; 3 | export * from './update-organization-options.serializer'; 4 | -------------------------------------------------------------------------------- /src/organizations/serializers/organization.serializer.spec.ts: -------------------------------------------------------------------------------- 1 | import { deserializeOrganization } from './organization.serializer'; 2 | import organizationFixture from '../fixtures/get-organization.json'; 3 | 4 | const organizationResponse = { 5 | ...organizationFixture, 6 | object: 'organization' as const, 7 | created_at: new Date().toISOString(), 8 | updated_at: new Date().toISOString(), 9 | domains: [], 10 | }; 11 | 12 | describe('deserializeOrganization', () => { 13 | it('includes metadata if present', () => { 14 | const metadata = { key: 'value' }; 15 | 16 | expect( 17 | deserializeOrganization({ 18 | ...organizationResponse, 19 | metadata, 20 | }), 21 | ).toMatchObject({ 22 | metadata, 23 | }); 24 | }); 25 | 26 | it('coerces missing metadata to empty object', () => { 27 | const { metadata, ...organizationResponseWithoutMetadata } = 28 | organizationResponse; 29 | 30 | expect( 31 | deserializeOrganization(organizationResponseWithoutMetadata), 32 | ).toMatchObject({ 33 | metadata: {}, 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/organizations/serializers/organization.serializer.ts: -------------------------------------------------------------------------------- 1 | import { deserializeOrganizationDomain } from '../../organization-domains/serializers/organization-domain.serializer'; 2 | import { Organization, OrganizationResponse } from '../interfaces'; 3 | 4 | export const deserializeOrganization = ( 5 | organization: OrganizationResponse, 6 | ): Organization => ({ 7 | object: organization.object, 8 | id: organization.id, 9 | name: organization.name, 10 | allowProfilesOutsideOrganization: 11 | organization.allow_profiles_outside_organization, 12 | domains: organization.domains.map(deserializeOrganizationDomain), 13 | ...(typeof organization.stripe_customer_id === 'undefined' 14 | ? undefined 15 | : { stripeCustomerId: organization.stripe_customer_id }), 16 | createdAt: organization.created_at, 17 | updatedAt: organization.updated_at, 18 | externalId: organization.external_id ?? null, 19 | metadata: organization.metadata ?? {}, 20 | }); 21 | -------------------------------------------------------------------------------- /src/organizations/serializers/update-organization-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SerializedUpdateOrganizationOptions, 3 | UpdateOrganizationOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeUpdateOrganizationOptions = ( 7 | options: Omit, 8 | ): SerializedUpdateOrganizationOptions => ({ 9 | name: options.name, 10 | allow_profiles_outside_organization: options.allowProfilesOutsideOrganization, 11 | domain_data: options.domainData, 12 | domains: options.domains, 13 | stripe_customer_id: options.stripeCustomerId, 14 | external_id: options.externalId, 15 | metadata: options.metadata, 16 | }); 17 | -------------------------------------------------------------------------------- /src/passwordless/fixtures/create-session.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "passwordless_session_01EHDAK2BFGWCSZXP9HGZ3VK8C", 3 | "email": "passwordless-session-email@workos.com", 4 | "expires_at": "2020-08-13T05:50:00.000Z", 5 | "link": "https://auth.workos.com/passwordless/token/confirm", 6 | "object": "passwordless_session" 7 | } 8 | -------------------------------------------------------------------------------- /src/passwordless/interfaces/create-passwordless-session-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreatePasswordlessSessionOptions { 2 | type: 'MagicLink'; 3 | email: string; 4 | redirectURI?: string; 5 | state?: string; 6 | connection?: string; 7 | expiresIn?: number; 8 | } 9 | 10 | export interface SerializedCreatePasswordlessSessionOptions { 11 | type: 'MagicLink'; 12 | email: string; 13 | redirect_uri?: string; 14 | state?: string; 15 | connection?: string; 16 | expires_in?: number; 17 | } 18 | -------------------------------------------------------------------------------- /src/passwordless/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './passwordless-session.interface'; 2 | export * from './create-passwordless-session-options.interface'; 3 | export * from './send-session-response.interface'; 4 | -------------------------------------------------------------------------------- /src/passwordless/interfaces/passwordless-session.interface.ts: -------------------------------------------------------------------------------- 1 | export interface PasswordlessSession { 2 | id: string; 3 | email: string; 4 | expiresAt: Date; 5 | link: string; 6 | object: 'passwordless_session'; 7 | } 8 | 9 | export interface PasswordlessSessionResponse { 10 | id: string; 11 | email: string; 12 | expires_at: Date; 13 | link: string; 14 | object: 'passwordless_session'; 15 | } 16 | -------------------------------------------------------------------------------- /src/passwordless/interfaces/send-session-response.interface.ts: -------------------------------------------------------------------------------- 1 | export interface SendSessionResponse { 2 | message?: string; 3 | success?: boolean; 4 | } 5 | -------------------------------------------------------------------------------- /src/passwordless/passwordless.spec.ts: -------------------------------------------------------------------------------- 1 | import fetch from 'jest-fetch-mock'; 2 | import { fetchOnce, fetchURL, fetchBody } from '../common/utils/test-utils'; 3 | 4 | import createSession from './fixtures/create-session.json'; 5 | import { WorkOS } from '../workos'; 6 | 7 | describe('Passwordless', () => { 8 | beforeEach(() => fetch.resetMocks()); 9 | 10 | describe('createSession', () => { 11 | describe('with valid options', () => { 12 | it('creates a passwordless session', async () => { 13 | const email = 'passwordless-session-email@workos.com'; 14 | const redirectURI = 'https://example.com/passwordless/callback'; 15 | 16 | fetchOnce(createSession, { status: 201 }); 17 | 18 | const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); 19 | 20 | const session = await workos.passwordless.createSession({ 21 | type: 'MagicLink', 22 | email, 23 | redirectURI, 24 | }); 25 | 26 | expect(session.email).toEqual(email); 27 | expect(session.object).toEqual('passwordless_session'); 28 | 29 | expect(fetchBody().email).toEqual(email); 30 | expect(fetchBody().redirect_uri).toEqual(redirectURI); 31 | expect(fetchURL()).toContain('/passwordless/sessions'); 32 | }); 33 | }); 34 | }); 35 | 36 | describe('sendEmail', () => { 37 | describe('with a valid session id', () => { 38 | it(`sends a request to send a magic link email`, async () => { 39 | fetchOnce(); 40 | const workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU'); 41 | 42 | const sessionId = 'session_123'; 43 | await workos.passwordless.sendSession(sessionId); 44 | 45 | expect(fetchURL()).toContain( 46 | `/passwordless/sessions/${sessionId}/send`, 47 | ); 48 | }); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /src/passwordless/passwordless.ts: -------------------------------------------------------------------------------- 1 | import { WorkOS } from '../workos'; 2 | import { 3 | CreatePasswordlessSessionOptions, 4 | PasswordlessSession, 5 | PasswordlessSessionResponse, 6 | SendSessionResponse, 7 | SerializedCreatePasswordlessSessionOptions, 8 | } from './interfaces'; 9 | import { deserializePasswordlessSession } from './serializers/passwordless-session.serializer'; 10 | 11 | export class Passwordless { 12 | constructor(private readonly workos: WorkOS) {} 13 | 14 | async createSession({ 15 | redirectURI, 16 | expiresIn, 17 | ...options 18 | }: CreatePasswordlessSessionOptions): Promise { 19 | const { data } = await this.workos.post< 20 | PasswordlessSessionResponse, 21 | SerializedCreatePasswordlessSessionOptions 22 | >('/passwordless/sessions', { 23 | ...options, 24 | redirect_uri: redirectURI, 25 | expires_in: expiresIn, 26 | }); 27 | 28 | return deserializePasswordlessSession(data); 29 | } 30 | 31 | async sendSession(sessionId: string): Promise { 32 | const { data } = await this.workos.post( 33 | `/passwordless/sessions/${sessionId}/send`, 34 | {}, 35 | ); 36 | return data; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/passwordless/serializers/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/workos-node/fdc0e53d32f52d3ce130824803de35adbd42959a/src/passwordless/serializers/index.ts -------------------------------------------------------------------------------- /src/passwordless/serializers/passwordless-session.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PasswordlessSession, 3 | PasswordlessSessionResponse, 4 | } from '../interfaces'; 5 | 6 | export const deserializePasswordlessSession = ( 7 | passwordlessSession: PasswordlessSessionResponse, 8 | ): PasswordlessSession => ({ 9 | id: passwordlessSession.id, 10 | email: passwordlessSession.email, 11 | expiresAt: passwordlessSession.expires_at, 12 | link: passwordlessSession.link, 13 | object: passwordlessSession.object, 14 | }); 15 | -------------------------------------------------------------------------------- /src/portal/fixtures/generate-link-invalid.json: -------------------------------------------------------------------------------- 1 | { "message": "Could not find an organization with the id, bogus-id."} 2 | -------------------------------------------------------------------------------- /src/portal/fixtures/generate-link.json: -------------------------------------------------------------------------------- 1 | { "link": "https://id.workos.com/portal/launch?secret=secret" } 2 | -------------------------------------------------------------------------------- /src/portal/interfaces/generate-portal-link-intent.interface.ts: -------------------------------------------------------------------------------- 1 | export enum GeneratePortalLinkIntent { 2 | AuditLogs = 'audit_logs', 3 | DomainVerification = 'domain_verification', 4 | DSync = 'dsync', 5 | LogStreams = 'log_streams', 6 | SSO = 'sso', 7 | CertificateRenewal = 'certificate_renewal', 8 | } 9 | -------------------------------------------------------------------------------- /src/portal/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export { GeneratePortalLinkIntent } from './generate-portal-link-intent.interface'; 2 | -------------------------------------------------------------------------------- /src/portal/portal.ts: -------------------------------------------------------------------------------- 1 | import { WorkOS } from '../workos'; 2 | import { GeneratePortalLinkIntent } from './interfaces/generate-portal-link-intent.interface'; 3 | 4 | export class Portal { 5 | constructor(private readonly workos: WorkOS) {} 6 | 7 | async generateLink({ 8 | intent, 9 | organization, 10 | returnUrl, 11 | successUrl, 12 | }: { 13 | intent: GeneratePortalLinkIntent; 14 | organization: string; 15 | returnUrl?: string; 16 | successUrl?: string; 17 | }): Promise<{ link: string }> { 18 | const { data } = await this.workos.post('/portal/generate_link', { 19 | intent, 20 | organization, 21 | return_url: returnUrl, 22 | success_url: successUrl, 23 | }); 24 | 25 | return data; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/roles/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './role.interface'; 2 | -------------------------------------------------------------------------------- /src/roles/interfaces/role.interface.ts: -------------------------------------------------------------------------------- 1 | export interface RoleResponse { 2 | slug: string; 3 | } 4 | 5 | export interface RoleEvent { 6 | object: 'role'; 7 | slug: string; 8 | permissions: string[]; 9 | createdAt: string; 10 | updatedAt: string; 11 | } 12 | 13 | export interface RoleEventResponse { 14 | object: 'role'; 15 | slug: string; 16 | permissions: string[]; 17 | created_at: string; 18 | updated_at: string; 19 | } 20 | 21 | export interface ListOrganizationRolesResponse { 22 | object: 'list'; 23 | data: OrganizationRoleResponse[]; 24 | } 25 | 26 | export interface OrganizationRoleResponse { 27 | object: 'role'; 28 | id: string; 29 | name: string; 30 | slug: string; 31 | description: string | null; 32 | permissions: string[]; 33 | type: 'EnvironmentRole' | 'OrganizationRole'; 34 | created_at: string; 35 | updated_at: string; 36 | } 37 | 38 | export interface Role { 39 | object: 'role'; 40 | id: string; 41 | name: string; 42 | slug: string; 43 | description: string | null; 44 | permissions: string[]; 45 | type: 'EnvironmentRole' | 'OrganizationRole'; 46 | createdAt: string; 47 | updatedAt: string; 48 | } 49 | 50 | export interface RoleList { 51 | object: 'list'; 52 | data: Role[]; 53 | } 54 | -------------------------------------------------------------------------------- /src/roles/serializers/role.serializer.ts: -------------------------------------------------------------------------------- 1 | import { OrganizationRoleResponse, Role } from '../interfaces'; 2 | 3 | export const deserializeRole = (role: OrganizationRoleResponse): Role => ({ 4 | object: role.object, 5 | id: role.id, 6 | name: role.name, 7 | slug: role.slug, 8 | description: role.description, 9 | permissions: role.permissions, 10 | type: role.type, 11 | createdAt: role.created_at, 12 | updatedAt: role.updated_at, 13 | }); 14 | -------------------------------------------------------------------------------- /src/sso/interfaces/authorization-url-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface SSOAuthorizationURLOptions { 2 | clientId: string; 3 | connection?: string; 4 | organization?: string; 5 | 6 | /** 7 | * @deprecated Please use `organization` instead. 8 | */ 9 | domain?: string; 10 | domainHint?: string; 11 | loginHint?: string; 12 | provider?: string; 13 | redirectUri: string; 14 | state?: string; 15 | } 16 | 17 | /** 18 | * @deprecated Use SSOAuthorizationURLOptions instead 19 | */ 20 | // tslint:disable-next-line:no-empty-interface 21 | export interface AuthorizationURLOptions extends SSOAuthorizationURLOptions {} 22 | -------------------------------------------------------------------------------- /src/sso/interfaces/connection-type.enum.ts: -------------------------------------------------------------------------------- 1 | export enum ConnectionType { 2 | ADFSSAML = 'ADFSSAML', 3 | AdpOidc = 'AdpOidc', 4 | AppleOAuth = 'AppleOAuth', 5 | Auth0SAML = 'Auth0SAML', 6 | AzureSAML = 'AzureSAML', 7 | CasSAML = 'CasSAML', 8 | ClassLinkSAML = 'ClassLinkSAML', 9 | CloudflareSAML = 'CloudflareSAML', 10 | CyberArkSAML = 'CyberArkSAML', 11 | DuoSAML = 'DuoSAML', 12 | GenericOIDC = 'GenericOIDC', 13 | GenericSAML = 'GenericSAML', 14 | GitHubOAuth = 'GitHubOAuth', 15 | GoogleOAuth = 'GoogleOAuth', 16 | GoogleSAML = 'GoogleSAML', 17 | JumpCloudSAML = 'JumpCloudSAML', 18 | KeycloakSAML = 'KeycloakSAML', 19 | LastPassSAML = 'LastPassSAML', 20 | LoginGovOidc = 'LoginGovOidc', 21 | MagicLink = 'MagicLink', 22 | MicrosoftOAuth = 'MicrosoftOAuth', 23 | MiniOrangeSAML = 'MiniOrangeSAML', 24 | NetIqSAML = 'NetIqSAML', 25 | OktaSAML = 'OktaSAML', 26 | OneLoginSAML = 'OneLoginSAML', 27 | OracleSAML = 'OracleSAML', 28 | PingFederateSAML = 'PingFederateSAML', 29 | PingOneSAML = 'PingOneSAML', 30 | RipplingSAML = 'RipplingSAML', 31 | SalesforceSAML = 'SalesforceSAML', 32 | ShibbolethGenericSAML = 'ShibbolethGenericSAML', 33 | ShibbolethSAML = 'ShibbolethSAML', 34 | SimpleSamlPhpSAML = 'SimpleSamlPhpSAML', 35 | VMwareSAML = 'VMwareSAML', 36 | } 37 | -------------------------------------------------------------------------------- /src/sso/interfaces/connection.interface.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionType } from './connection-type.enum'; 2 | 3 | export interface ConnectionDomain { 4 | object: 'connection_domain'; 5 | id: string; 6 | domain: string; 7 | } 8 | 9 | export interface Connection { 10 | object: 'connection'; 11 | id: string; 12 | organizationId?: string; 13 | name: string; 14 | /** 15 | * @deprecated The connectionType parameter has been deprecated. Please use type. 16 | */ 17 | connectionType: ConnectionType; 18 | state: 'draft' | 'active' | 'inactive' | 'validating'; 19 | domains: ConnectionDomain[]; 20 | type: ConnectionType; 21 | createdAt: string; 22 | updatedAt: string; 23 | } 24 | 25 | export interface ConnectionResponse { 26 | object: 'connection'; 27 | id: string; 28 | organization_id?: string; 29 | name: string; 30 | connection_type: ConnectionType; 31 | state: 'draft' | 'active' | 'inactive' | 'validating'; 32 | domains: ConnectionDomain[]; 33 | created_at: string; 34 | updated_at: string; 35 | } 36 | -------------------------------------------------------------------------------- /src/sso/interfaces/get-profile-and-token-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface GetProfileAndTokenOptions { 2 | clientId: string; 3 | code: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/sso/interfaces/get-profile-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface GetProfileOptions { 2 | accessToken: string; 3 | } 4 | -------------------------------------------------------------------------------- /src/sso/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './authorization-url-options.interface'; 2 | export * from './connection-type.enum'; 3 | export * from './connection.interface'; 4 | export * from './get-profile-options.interface'; 5 | export * from './get-profile-and-token-options.interface'; 6 | export * from './list-connections-options.interface'; 7 | export * from './profile-and-token.interface'; 8 | export * from './profile.interface'; 9 | -------------------------------------------------------------------------------- /src/sso/interfaces/list-connections-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; 2 | import { ConnectionType } from './connection-type.enum'; 3 | 4 | export interface ListConnectionsOptions extends PaginationOptions { 5 | connectionType?: ConnectionType; 6 | domain?: string; 7 | organizationId?: string; 8 | } 9 | 10 | export interface SerializedListConnectionsOptions extends PaginationOptions { 11 | connection_type?: ConnectionType; 12 | domain?: string; 13 | organization_id?: string; 14 | } 15 | -------------------------------------------------------------------------------- /src/sso/interfaces/profile-and-token.interface.ts: -------------------------------------------------------------------------------- 1 | import { UnknownRecord } from '../../common/interfaces/unknown-record.interface'; 2 | import { Profile, ProfileResponse } from './profile.interface'; 3 | 4 | export interface ProfileAndToken { 5 | accessToken: string; 6 | profile: Profile; 7 | } 8 | 9 | export interface ProfileAndTokenResponse< 10 | CustomAttributesType extends UnknownRecord, 11 | > { 12 | access_token: string; 13 | profile: ProfileResponse; 14 | } 15 | -------------------------------------------------------------------------------- /src/sso/interfaces/profile.interface.ts: -------------------------------------------------------------------------------- 1 | import { UnknownRecord } from '../../common/interfaces/unknown-record.interface'; 2 | import { RoleResponse } from '../../roles/interfaces'; 3 | import { ConnectionType } from './connection-type.enum'; 4 | 5 | export interface Profile { 6 | id: string; 7 | idpId: string; 8 | organizationId?: string; 9 | connectionId: string; 10 | connectionType: ConnectionType; 11 | email: string; 12 | firstName?: string; 13 | lastName?: string; 14 | role?: RoleResponse; 15 | groups?: string[]; 16 | customAttributes?: CustomAttributesType; 17 | rawAttributes?: { [key: string]: any }; 18 | } 19 | 20 | export interface ProfileResponse { 21 | id: string; 22 | idp_id: string; 23 | organization_id?: string; 24 | connection_id: string; 25 | connection_type: ConnectionType; 26 | email: string; 27 | first_name?: string; 28 | last_name?: string; 29 | role?: RoleResponse; 30 | groups?: string[]; 31 | custom_attributes?: CustomAttributesType; 32 | raw_attributes?: { [key: string]: any }; 33 | } 34 | -------------------------------------------------------------------------------- /src/sso/serializers/connection.serializer.ts: -------------------------------------------------------------------------------- 1 | import { Connection, ConnectionResponse } from '../interfaces'; 2 | 3 | export const deserializeConnection = ( 4 | connection: ConnectionResponse, 5 | ): Connection => ({ 6 | object: connection.object, 7 | id: connection.id, 8 | organizationId: connection.organization_id, 9 | name: connection.name, 10 | connectionType: connection.connection_type, 11 | type: connection.connection_type, 12 | state: connection.state, 13 | domains: connection.domains, 14 | createdAt: connection.created_at, 15 | updatedAt: connection.updated_at, 16 | }); 17 | -------------------------------------------------------------------------------- /src/sso/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './connection.serializer'; 2 | export * from './list-connections-options.serializer'; 3 | export * from './profile-and-token.serializer'; 4 | export * from './profile.serializer'; 5 | -------------------------------------------------------------------------------- /src/sso/serializers/list-connections-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ListConnectionsOptions, 3 | SerializedListConnectionsOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeListConnectionsOptions = ( 7 | options: ListConnectionsOptions, 8 | ): SerializedListConnectionsOptions => ({ 9 | connection_type: options.connectionType, 10 | domain: options.domain, 11 | organization_id: options.organizationId, 12 | limit: options.limit, 13 | before: options.before, 14 | after: options.after, 15 | order: options.order, 16 | }); 17 | -------------------------------------------------------------------------------- /src/sso/serializers/profile-and-token.serializer.ts: -------------------------------------------------------------------------------- 1 | import { UnknownRecord } from '../../common/interfaces/unknown-record.interface'; 2 | import { ProfileAndToken, ProfileAndTokenResponse } from '../interfaces'; 3 | import { deserializeProfile } from './profile.serializer'; 4 | 5 | export const deserializeProfileAndToken = < 6 | CustomAttributesType extends UnknownRecord, 7 | >( 8 | profileAndToken: ProfileAndTokenResponse, 9 | ): ProfileAndToken => ({ 10 | accessToken: profileAndToken.access_token, 11 | profile: deserializeProfile(profileAndToken.profile), 12 | }); 13 | -------------------------------------------------------------------------------- /src/sso/serializers/profile.serializer.ts: -------------------------------------------------------------------------------- 1 | import { UnknownRecord } from '../../common/interfaces/unknown-record.interface'; 2 | import { Profile, ProfileResponse } from '../interfaces'; 3 | 4 | export const deserializeProfile = ( 5 | profile: ProfileResponse, 6 | ): Profile => ({ 7 | id: profile.id, 8 | idpId: profile.idp_id, 9 | organizationId: profile.organization_id, 10 | connectionId: profile.connection_id, 11 | connectionType: profile.connection_type, 12 | email: profile.email, 13 | firstName: profile.first_name, 14 | lastName: profile.last_name, 15 | role: profile.role, 16 | groups: profile.groups, 17 | customAttributes: profile.custom_attributes, 18 | rawAttributes: profile.raw_attributes, 19 | }); 20 | -------------------------------------------------------------------------------- /src/user-management/fixtures/deactivate-organization-membership.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "organization_membership", 3 | "id": "om_01H5JQDV7R7ATEYZDEG0W5PRYS", 4 | "user_id": "user_01H5JQDV7R7ATEYZDEG0W5PRYS", 5 | "organization_id": "organization_01H5JQDV7R7ATEYZDEG0W5PRYS", 6 | "status": "inactive", 7 | "role": { 8 | "slug": "member" 9 | }, 10 | "created_at": "2023-07-18T02:07:19.911Z", 11 | "updated_at": "2023-07-18T02:07:19.911Z" 12 | } 13 | -------------------------------------------------------------------------------- /src/user-management/fixtures/email_verification.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "email_verification", 3 | "id": "email_verification_01H5JQDV7R7ATEYZDEG0W5PRYS", 4 | "user_id": "user_01H5JQDV7R7ATEYZDEG0W5PRYS", 5 | "email": "dane@workos.com", 6 | "expires_at": "2023-07-18T02:07:19.911Z", 7 | "code": "123456", 8 | "created_at": "2023-07-18T02:07:19.911Z", 9 | "updated_at": "2023-07-18T02:07:19.911Z" 10 | } -------------------------------------------------------------------------------- /src/user-management/fixtures/identity.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "idp_id": "108872335", 4 | "type": "OAuth", 5 | "provider": "GithubOAuth" 6 | }, 7 | { 8 | "idp_id": "111966195055680542408", 9 | "type": "OAuth", 10 | "provider": "GoogleOAuth" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /src/user-management/fixtures/invitation.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "invitation", 3 | "id": "invitation_01H5JQDV7R7ATEYZDEG0W5PRYS", 4 | "email": "dane@workos.com", 5 | "state": "pending", 6 | "accepted_at": "2023-07-18T02:07:19.911Z", 7 | "revoked_at": "2023-07-18T02:07:19.911Z", 8 | "expires_at": "2023-07-18T02:07:19.911Z", 9 | "organization_id": "org_01H5JQDV7R7ATEYZDEG0W5PRYS", 10 | "inviter_user_id": null, 11 | "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", 12 | "accept_invitation_url": "https://your-app.com/invite?invitation_token=Z1uX3RbwcIl5fIGJJJCXXisdI", 13 | "created_at": "2023-07-18T02:07:19.911Z", 14 | "updated_at": "2023-07-18T02:07:19.911Z" 15 | } 16 | -------------------------------------------------------------------------------- /src/user-management/fixtures/list-factors.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "list", 3 | "data": [ 4 | { 5 | "object": "authentication_factor", 6 | "id": "auth_factor_1234", 7 | "created_at": "2022-03-15T20:39:19.892Z", 8 | "updated_at": "2022-03-15T20:39:19.892Z", 9 | "type": "totp", 10 | "totp": { 11 | "issuer": "WorkOS", 12 | "user": "some_user" 13 | } 14 | } 15 | ], 16 | "list_metadata": { 17 | "before": null, 18 | "after": null 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/user-management/fixtures/list-invitations.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "list", 3 | "data": [ 4 | { 5 | "object": "invitation", 6 | "id": "invitation_01H5JQDV7R7ATEYZDEG0W5PRYS", 7 | "email": "dane@workos.com", 8 | "state": "pending", 9 | "accepted_at": "2023-07-18T02:07:19.911Z", 10 | "revoked_at": "2023-07-18T02:07:19.911Z", 11 | "expires_at": "2023-07-18T02:07:19.911Z", 12 | "organization_id": "org_01H5JQDV7R7ATEYZDEG0W5PRYS", 13 | "inviter_user_id": null, 14 | "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", 15 | "accept_invitation_url": "https://your-app.com/invite?invitation_token=Z1uX3RbwcIl5fIGJJJCXXisdI", 16 | "created_at": "2023-07-18T02:07:19.911Z", 17 | "updated_at": "2023-07-18T02:07:19.911Z" 18 | } 19 | ], 20 | "list_metadata": { 21 | "before": null, 22 | "after": null 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/user-management/fixtures/list-organization-memberships.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "list", 3 | "data": [ 4 | { 5 | "object": "organization_membership", 6 | "id": "om_01H5JQDV7R7ATEYZDEG0W5PRYS", 7 | "user_id": "user_01H5JQDV7R7ATEYZDEG0W5PRYS", 8 | "organization_id": "organization_01H5JQDV7R7ATEYZDEG0W5PRYS", 9 | "organization_name": "Example Organization", 10 | "status": "active", 11 | "created_at": "2023-07-18T02:07:19.911Z", 12 | "updated_at": "2023-07-18T02:07:19.911Z" 13 | } 14 | ], 15 | "list_metadata": { 16 | "before": null, 17 | "after": null 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/user-management/fixtures/list-users.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "list", 3 | "data": [ 4 | { 5 | "object": "user", 6 | "id": "user_01H5JQDV7R7ATEYZDEG0W5PRYS", 7 | "email": "test01@example.com", 8 | "first_name": "Test 01", 9 | "last_name": "User", 10 | "created_at": "2023-07-18T02:07:19.911Z", 11 | "updated_at": "2023-07-18T02:07:19.911Z", 12 | "email_verified": true, 13 | "profile_picture_url": "https://example.com/profile_picture.jpg", 14 | "last_sign_in_at": "2023-07-18T02:07:19.911Z" 15 | } 16 | ], 17 | "list_metadata": { 18 | "before": null, 19 | "after": null 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/user-management/fixtures/magic_auth.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "magic_auth", 3 | "id": "magic_auth_01H5JQDV7R7ATEYZDEG0W5PRYS", 4 | "user_id": "user_01H5JQDV7R7ATEYZDEG0W5PRYS", 5 | "email": "dane@workos.com", 6 | "expires_at": "2023-07-18T02:07:19.911Z", 7 | "code": "123456", 8 | "created_at": "2023-07-18T02:07:19.911Z", 9 | "updated_at": "2023-07-18T02:07:19.911Z" 10 | } -------------------------------------------------------------------------------- /src/user-management/fixtures/organization-membership.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "organization_membership", 3 | "id": "om_01H5JQDV7R7ATEYZDEG0W5PRYS", 4 | "user_id": "user_01H5JQDV7R7ATEYZDEG0W5PRYS", 5 | "organization_id": "organization_01H5JQDV7R7ATEYZDEG0W5PRYS", 6 | "status": "active", 7 | "role": { 8 | "slug": "member" 9 | }, 10 | "created_at": "2023-07-18T02:07:19.911Z", 11 | "updated_at": "2023-07-18T02:07:19.911Z" 12 | } 13 | -------------------------------------------------------------------------------- /src/user-management/fixtures/password_reset.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "password_reset", 3 | "id": "password_reset_01H5JQDV7R7ATEYZDEG0W5PRYS", 4 | "user_id": "user_01H5JQDV7R7ATEYZDEG0W5PRYS", 5 | "email": "dane@workos.com", 6 | "password_reset_token": "Z1uX3RbwcIl5fIGJJJCXXisdI", 7 | "password_reset_url": "https://your-app.com/reset-password?token=Z1uX3RbwcIl5fIGJJJCXXisdI", 8 | "expires_at": "2023-07-18T02:07:19.911Z", 9 | "created_at": "2023-07-18T02:07:19.911Z" 10 | } -------------------------------------------------------------------------------- /src/user-management/fixtures/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": "user", 3 | "id": "user_01H5JQDV7R7ATEYZDEG0W5PRYS", 4 | "email": "test01@example.com", 5 | "first_name": "Test 01", 6 | "last_name": "User", 7 | "created_at": "2023-07-18T02:07:19.911Z", 8 | "updated_at": "2023-07-18T02:07:19.911Z", 9 | "email_verified": true, 10 | "profile_picture_url": "https://example.com/profile_picture.jpg", 11 | "last_sign_in_at": "2023-07-18T02:07:19.911Z", 12 | "metadata": { "key": "value" } 13 | } 14 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authenticate-with-code-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateWithOptionsBase, 3 | SerializedAuthenticateWithOptionsBase, 4 | } from './authenticate-with-options-base.interface'; 5 | 6 | export interface AuthenticateWithCodeOptions 7 | extends AuthenticateWithOptionsBase { 8 | codeVerifier?: string; 9 | code: string; 10 | invitationToken?: string; 11 | } 12 | 13 | export interface AuthenticateUserWithCodeCredentials { 14 | clientSecret: string | undefined; 15 | } 16 | 17 | export interface SerializedAuthenticateWithCodeOptions 18 | extends SerializedAuthenticateWithOptionsBase { 19 | grant_type: 'authorization_code'; 20 | code_verifier?: string; 21 | code: string; 22 | invitation_token?: string; 23 | } 24 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authenticate-with-email-verification-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateWithOptionsBase, 3 | SerializedAuthenticateWithOptionsBase, 4 | } from './authenticate-with-options-base.interface'; 5 | 6 | export interface AuthenticateWithEmailVerificationOptions 7 | extends AuthenticateWithOptionsBase { 8 | code: string; 9 | pendingAuthenticationToken: string; 10 | } 11 | 12 | export interface AuthenticateUserWithEmailVerificationCredentials { 13 | clientSecret: string | undefined; 14 | } 15 | 16 | export interface SerializedAuthenticateWithEmailVerificationOptions 17 | extends SerializedAuthenticateWithOptionsBase { 18 | grant_type: 'urn:workos:oauth:grant-type:email-verification:code'; 19 | code: string; 20 | pending_authentication_token: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authenticate-with-magic-auth-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateWithOptionsBase, 3 | SerializedAuthenticateWithOptionsBase, 4 | } from './authenticate-with-options-base.interface'; 5 | 6 | export interface AuthenticateWithMagicAuthOptions 7 | extends AuthenticateWithOptionsBase { 8 | code: string; 9 | email: string; 10 | invitationToken?: string; 11 | linkAuthorizationCode?: string; 12 | } 13 | 14 | export interface AuthenticateUserWithMagicAuthCredentials { 15 | clientSecret: string | undefined; 16 | } 17 | 18 | export interface SerializedAuthenticateWithMagicAuthOptions 19 | extends SerializedAuthenticateWithOptionsBase { 20 | grant_type: 'urn:workos:oauth:grant-type:magic-auth:code'; 21 | code: string; 22 | email: string; 23 | invitation_token?: string; 24 | link_authorization_code?: string; 25 | } 26 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authenticate-with-options-base.interface.ts: -------------------------------------------------------------------------------- 1 | export interface AuthenticateWithSessionOptions { 2 | cookiePassword?: string; 3 | sealSession: boolean; 4 | } 5 | 6 | export interface AuthenticateWithOptionsBase { 7 | clientId: string; 8 | ipAddress?: string; 9 | userAgent?: string; 10 | session?: AuthenticateWithSessionOptions; 11 | } 12 | 13 | export interface SerializedAuthenticateWithOptionsBase { 14 | client_id: string; 15 | client_secret: string | undefined; 16 | ip_address?: string; 17 | user_agent?: string; 18 | } 19 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authenticate-with-organization-selection.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateWithOptionsBase, 3 | SerializedAuthenticateWithOptionsBase, 4 | } from './authenticate-with-options-base.interface'; 5 | 6 | export interface AuthenticateWithOrganizationSelectionOptions 7 | extends AuthenticateWithOptionsBase { 8 | organizationId: string; 9 | pendingAuthenticationToken: string; 10 | } 11 | 12 | export interface AuthenticateUserWithOrganizationSelectionCredentials { 13 | clientSecret: string | undefined; 14 | } 15 | 16 | export interface SerializedAuthenticateWithOrganizationSelectionOptions 17 | extends SerializedAuthenticateWithOptionsBase { 18 | grant_type: 'urn:workos:oauth:grant-type:organization-selection'; 19 | organization_id: string; 20 | pending_authentication_token: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authenticate-with-password-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateWithOptionsBase, 3 | SerializedAuthenticateWithOptionsBase, 4 | } from './authenticate-with-options-base.interface'; 5 | 6 | export interface AuthenticateWithPasswordOptions 7 | extends AuthenticateWithOptionsBase { 8 | email: string; 9 | password: string; 10 | invitationToken?: string; 11 | } 12 | 13 | export interface AuthenticateUserWithPasswordCredentials { 14 | clientSecret: string | undefined; 15 | } 16 | 17 | export interface SerializedAuthenticateWithPasswordOptions 18 | extends SerializedAuthenticateWithOptionsBase { 19 | grant_type: 'password'; 20 | email: string; 21 | password: string; 22 | invitation_token?: string; 23 | } 24 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authenticate-with-refresh-token-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateWithOptionsBase, 3 | SerializedAuthenticateWithOptionsBase, 4 | } from './authenticate-with-options-base.interface'; 5 | 6 | export interface AuthenticateWithRefreshTokenOptions 7 | extends AuthenticateWithOptionsBase { 8 | refreshToken: string; 9 | organizationId?: string; 10 | } 11 | 12 | export interface AuthenticateUserWithRefreshTokenCredentials { 13 | clientSecret: string | undefined; 14 | } 15 | 16 | export interface SerializedAuthenticateWithRefreshTokenOptions 17 | extends SerializedAuthenticateWithOptionsBase { 18 | grant_type: 'refresh_token'; 19 | refresh_token: string; 20 | organization_id: string | undefined; 21 | } 22 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authenticate-with-session-cookie.interface.ts: -------------------------------------------------------------------------------- 1 | import { AuthenticationResponse } from './authentication-response.interface'; 2 | import { Impersonator } from './impersonator.interface'; 3 | import { User } from './user.interface'; 4 | 5 | export interface AuthenticateWithSessionCookieOptions { 6 | sessionData: string; 7 | cookiePassword?: string; 8 | } 9 | 10 | export interface AccessToken { 11 | sid: string; 12 | org_id?: string; 13 | role?: string; 14 | permissions?: string[]; 15 | entitlements?: string[]; 16 | } 17 | 18 | export type SessionCookieData = Pick< 19 | AuthenticationResponse, 20 | 'accessToken' | 'impersonator' | 'organizationId' | 'refreshToken' | 'user' 21 | >; 22 | 23 | export enum AuthenticateWithSessionCookieFailureReason { 24 | INVALID_JWT = 'invalid_jwt', 25 | INVALID_SESSION_COOKIE = 'invalid_session_cookie', 26 | NO_SESSION_COOKIE_PROVIDED = 'no_session_cookie_provided', 27 | } 28 | 29 | export type AuthenticateWithSessionCookieFailedResponse = { 30 | authenticated: false; 31 | reason: AuthenticateWithSessionCookieFailureReason; 32 | }; 33 | 34 | export type AuthenticateWithSessionCookieSuccessResponse = { 35 | authenticated: true; 36 | sessionId: string; 37 | organizationId?: string; 38 | role?: string; 39 | permissions?: string[]; 40 | entitlements?: string[]; 41 | user: User; 42 | impersonator?: Impersonator; 43 | accessToken: string; 44 | }; 45 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authenticate-with-totp-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateWithOptionsBase, 3 | SerializedAuthenticateWithOptionsBase, 4 | } from './authenticate-with-options-base.interface'; 5 | 6 | export interface AuthenticateWithTotpOptions 7 | extends AuthenticateWithOptionsBase { 8 | code: string; 9 | pendingAuthenticationToken: string; 10 | authenticationChallengeId: string; 11 | } 12 | 13 | export interface AuthenticateUserWithTotpCredentials { 14 | clientSecret: string | undefined; 15 | } 16 | 17 | export interface SerializedAuthenticateWithTotpOptions 18 | extends SerializedAuthenticateWithOptionsBase { 19 | grant_type: 'urn:workos:oauth:grant-type:mfa-totp'; 20 | code: string; 21 | pending_authentication_token: string; 22 | authentication_challenge_id: string; 23 | } 24 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authentication-event.interface.ts: -------------------------------------------------------------------------------- 1 | interface AuthenticationEventError { 2 | code: string; 3 | message: string; 4 | } 5 | 6 | type AuthenticationEventType = 7 | | 'sso' 8 | | 'password' 9 | | 'oauth' 10 | | 'mfa' 11 | | 'magic_auth' 12 | | 'email_verification'; 13 | 14 | type AuthenticationEventStatus = 'failed' | 'succeeded'; 15 | 16 | export type AuthenticationEvent = { 17 | email: string | null; 18 | error?: AuthenticationEventError; 19 | ipAddress: string | null; 20 | status: AuthenticationEventStatus; 21 | type: AuthenticationEventType; 22 | userAgent: string | null; 23 | userId: string | null; 24 | }; 25 | 26 | export interface AuthenticationEventResponse { 27 | email: string | null; 28 | error?: AuthenticationEventError; 29 | ip_address: string | null; 30 | status: AuthenticationEventStatus; 31 | type: AuthenticationEventType; 32 | user_agent: string | null; 33 | user_id: string | null; 34 | } 35 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authentication-radar-risk-detected-event.interface.ts: -------------------------------------------------------------------------------- 1 | export type AuthenticationRadarRiskDetectedEventData = { 2 | authMethod: string; 3 | action: 'signup' | 'login'; 4 | control: string | null; 5 | blocklistType: string | null; 6 | ipAddress: string | null; 7 | userAgent: string | null; 8 | userId: string; 9 | email: string; 10 | }; 11 | 12 | export interface AuthenticationRadarRiskDetectedEventResponseData { 13 | auth_method: string; 14 | action: 'signup' | 'login'; 15 | control: string | null; 16 | blocklist_type: string | null; 17 | ip_address: string | null; 18 | user_agent: string | null; 19 | user_id: string; 20 | email: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authentication-response.interface.ts: -------------------------------------------------------------------------------- 1 | import { Impersonator, ImpersonatorResponse } from './impersonator.interface'; 2 | import { OauthTokens, OauthTokensResponse } from './oauth-tokens.interface'; 3 | import { User, UserResponse } from './user.interface'; 4 | 5 | type AuthenticationMethod = 6 | | 'SSO' 7 | | 'Password' 8 | | 'Passkey' 9 | | 'AppleOAuth' 10 | | 'GitHubOAuth' 11 | | 'GoogleOAuth' 12 | | 'MicrosoftOAuth' 13 | | 'MagicAuth' 14 | | 'Impersonation'; 15 | 16 | export interface AuthenticationResponse { 17 | user: User; 18 | organizationId?: string; 19 | accessToken: string; 20 | refreshToken: string; 21 | impersonator?: Impersonator; 22 | authenticationMethod?: AuthenticationMethod; 23 | sealedSession?: string; 24 | oauthTokens?: OauthTokens; 25 | } 26 | 27 | export interface AuthenticationResponseResponse { 28 | user: UserResponse; 29 | organization_id?: string; 30 | access_token: string; 31 | refresh_token: string; 32 | impersonator?: ImpersonatorResponse; 33 | authentication_method?: AuthenticationMethod; 34 | oauth_tokens?: OauthTokensResponse; 35 | } 36 | -------------------------------------------------------------------------------- /src/user-management/interfaces/authorization-url-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface UserManagementAuthorizationURLOptions { 2 | clientId: string; 3 | codeChallenge?: string; 4 | codeChallengeMethod?: 'S256'; 5 | connectionId?: string; 6 | /** 7 | * @deprecated We previously required initiate login endpoints to return the `context` 8 | * query parameter when getting the authorization URL. This is no longer necessary. 9 | */ 10 | context?: string; 11 | organizationId?: string; 12 | domainHint?: string; 13 | loginHint?: string; 14 | provider?: string; 15 | providerScopes?: string[]; 16 | redirectUri: string; 17 | state?: string; 18 | screenHint?: 'sign-up' | 'sign-in'; 19 | } 20 | -------------------------------------------------------------------------------- /src/user-management/interfaces/create-magic-auth-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreateMagicAuthOptions { 2 | email: string; 3 | invitationToken?: string; 4 | } 5 | 6 | export interface SerializedCreateMagicAuthOptions { 7 | email: string; 8 | invitation_token?: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/user-management/interfaces/create-organization-membership-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreateOrganizationMembershipOptions { 2 | organizationId: string; 3 | userId: string; 4 | roleSlug?: string; 5 | } 6 | 7 | export interface SerializedCreateOrganizationMembershipOptions { 8 | organization_id: string; 9 | user_id: string; 10 | role_slug?: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/user-management/interfaces/create-password-reset-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CreatePasswordResetOptions { 2 | email: string; 3 | } 4 | 5 | export interface SerializedCreatePasswordResetOptions { 6 | email: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/user-management/interfaces/create-user-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PasswordHashType } from './password-hash-type.interface'; 2 | 3 | export interface CreateUserOptions { 4 | email: string; 5 | password?: string; 6 | passwordHash?: string; 7 | passwordHashType?: PasswordHashType; 8 | firstName?: string; 9 | lastName?: string; 10 | emailVerified?: boolean; 11 | externalId?: string; 12 | metadata?: Record; 13 | } 14 | 15 | export interface SerializedCreateUserOptions { 16 | email: string; 17 | password?: string; 18 | password_hash?: string; 19 | password_hash_type?: PasswordHashType; 20 | first_name?: string; 21 | last_name?: string; 22 | email_verified?: boolean; 23 | external_id?: string; 24 | metadata?: Record; 25 | } 26 | -------------------------------------------------------------------------------- /src/user-management/interfaces/email-verification.interface.ts: -------------------------------------------------------------------------------- 1 | export interface EmailVerification { 2 | object: 'email_verification'; 3 | id: string; 4 | userId: string; 5 | email: string; 6 | expiresAt: string; 7 | code: string; 8 | createdAt: string; 9 | updatedAt: string; 10 | } 11 | 12 | export interface EmailVerificationEvent { 13 | object: 'email_verification'; 14 | id: string; 15 | userId: string; 16 | email: string; 17 | expiresAt: string; 18 | createdAt: string; 19 | updatedAt: string; 20 | } 21 | 22 | export interface EmailVerificationResponse { 23 | object: 'email_verification'; 24 | id: string; 25 | user_id: string; 26 | email: string; 27 | expires_at: string; 28 | code: string; 29 | created_at: string; 30 | updated_at: string; 31 | } 32 | 33 | export interface EmailVerificationEventResponse { 34 | object: 'email_verification'; 35 | id: string; 36 | user_id: string; 37 | email: string; 38 | expires_at: string; 39 | created_at: string; 40 | updated_at: string; 41 | } 42 | -------------------------------------------------------------------------------- /src/user-management/interfaces/enroll-auth-factor.interface.ts: -------------------------------------------------------------------------------- 1 | export interface EnrollAuthFactorOptions { 2 | userId: string; 3 | type: 'totp'; 4 | totpIssuer?: string; 5 | totpUser?: string; 6 | totpSecret?: string; 7 | } 8 | 9 | export interface SerializedEnrollUserInMfaFactorOptions { 10 | type: 'totp'; 11 | totp_issuer?: string; 12 | totp_user?: string; 13 | totp_secret?: string; 14 | } 15 | -------------------------------------------------------------------------------- /src/user-management/interfaces/factor.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Totp, 3 | TotpResponse, 4 | TotpWithSecrets, 5 | TotpWithSecretsResponse, 6 | } from '../../mfa/interfaces/totp.interface'; 7 | 8 | export interface Factor { 9 | object: 'authentication_factor'; 10 | id: string; 11 | createdAt: string; 12 | updatedAt: string; 13 | type: 'totp'; 14 | totp: Totp; 15 | userId: string; 16 | } 17 | 18 | export interface FactorWithSecrets { 19 | object: 'authentication_factor'; 20 | id: string; 21 | createdAt: string; 22 | updatedAt: string; 23 | type: 'totp'; 24 | totp: TotpWithSecrets; 25 | userId: string; 26 | } 27 | 28 | export interface FactorResponse { 29 | object: 'authentication_factor'; 30 | id: string; 31 | created_at: string; 32 | updated_at: string; 33 | type: 'totp'; 34 | totp: TotpResponse; 35 | user_id: string; 36 | } 37 | 38 | export interface FactorWithSecretsResponse { 39 | object: 'authentication_factor'; 40 | id: string; 41 | created_at: string; 42 | updated_at: string; 43 | type: 'totp'; 44 | totp: TotpWithSecretsResponse; 45 | user_id: string; 46 | } 47 | -------------------------------------------------------------------------------- /src/user-management/interfaces/identity.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Identity { 2 | idpId: string; 3 | type: 'OAuth'; 4 | provider: 'AppleOAuth' | 'GoogleOAuth' | 'GitHubOAuth' | 'MicrosoftOAuth'; 5 | } 6 | 7 | export interface IdentityResponse { 8 | idp_id: string; 9 | type: 'OAuth'; 10 | provider: 'AppleOAuth' | 'GoogleOAuth' | 'GitHubOAuth' | 'MicrosoftOAuth'; 11 | } 12 | -------------------------------------------------------------------------------- /src/user-management/interfaces/impersonator.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Impersonator { 2 | email: string; 3 | reason: string | null; 4 | } 5 | 6 | export interface ImpersonatorResponse { 7 | email: string; 8 | reason: string | null; 9 | } 10 | -------------------------------------------------------------------------------- /src/user-management/interfaces/invitation.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Invitation { 2 | object: 'invitation'; 3 | id: string; 4 | email: string; 5 | state: 'pending' | 'accepted' | 'expired' | 'revoked'; 6 | acceptedAt: string | null; 7 | revokedAt: string | null; 8 | expiresAt: string; 9 | organizationId: string | null; 10 | inviterUserId: string | null; 11 | acceptedUserId: string | null; 12 | token: string; 13 | acceptInvitationUrl: string; 14 | createdAt: string; 15 | updatedAt: string; 16 | } 17 | 18 | export interface InvitationEvent { 19 | object: 'invitation'; 20 | id: string; 21 | email: string; 22 | state: 'pending' | 'accepted' | 'expired' | 'revoked'; 23 | acceptedAt: string | null; 24 | revokedAt: string | null; 25 | expiresAt: string; 26 | organizationId: string | null; 27 | inviterUserId: string | null; 28 | acceptedUserId: string | null; 29 | createdAt: string; 30 | updatedAt: string; 31 | } 32 | 33 | export interface InvitationResponse { 34 | object: 'invitation'; 35 | id: string; 36 | email: string; 37 | state: 'pending' | 'accepted' | 'expired' | 'revoked'; 38 | accepted_at: string | null; 39 | revoked_at: string | null; 40 | expires_at: string; 41 | organization_id: string | null; 42 | inviter_user_id: string | null; 43 | accepted_user_id: string | null; 44 | token: string; 45 | accept_invitation_url: string; 46 | created_at: string; 47 | updated_at: string; 48 | } 49 | 50 | export interface InvitationEventResponse { 51 | object: 'invitation'; 52 | id: string; 53 | email: string; 54 | state: 'pending' | 'accepted' | 'expired' | 'revoked'; 55 | accepted_at: string | null; 56 | revoked_at: string | null; 57 | expires_at: string; 58 | organization_id: string | null; 59 | inviter_user_id: string | null; 60 | accepted_user_id: string | null; 61 | created_at: string; 62 | updated_at: string; 63 | } 64 | -------------------------------------------------------------------------------- /src/user-management/interfaces/list-auth-factors-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PaginationOptions } from '../../common/interfaces'; 2 | 3 | export interface ListAuthFactorsOptions extends PaginationOptions { 4 | userId: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/user-management/interfaces/list-invitations-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PaginationOptions } from '../../common/interfaces'; 2 | 3 | export interface ListInvitationsOptions extends PaginationOptions { 4 | organizationId?: string; 5 | email?: string; 6 | } 7 | 8 | export interface SerializedListInvitationsOptions extends PaginationOptions { 9 | organization_id?: string; 10 | email?: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/user-management/interfaces/list-organization-memberships-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PaginationOptions } from '../../common/interfaces'; 2 | import { OrganizationMembershipStatus } from './organization-membership.interface'; 3 | 4 | export interface ListOrganizationMembershipsOptions extends PaginationOptions { 5 | organizationId?: string; 6 | userId?: string; 7 | statuses?: OrganizationMembershipStatus[]; 8 | } 9 | 10 | export interface SerializedListOrganizationMembershipsOptions 11 | extends PaginationOptions { 12 | organization_id?: string; 13 | user_id?: string; 14 | statuses?: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/user-management/interfaces/list-users-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PaginationOptions } from '../../common/interfaces/pagination-options.interface'; 2 | 3 | export interface ListUsersOptions extends PaginationOptions { 4 | email?: string; 5 | organizationId?: string; 6 | } 7 | 8 | export interface SerializedListUsersOptions extends PaginationOptions { 9 | email?: string; 10 | organization_id?: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/user-management/interfaces/magic-auth.interface.ts: -------------------------------------------------------------------------------- 1 | export interface MagicAuth { 2 | object: 'magic_auth'; 3 | id: string; 4 | userId: string; 5 | email: string; 6 | expiresAt: string; 7 | code: string; 8 | createdAt: string; 9 | updatedAt: string; 10 | } 11 | 12 | export interface MagicAuthEvent { 13 | object: 'magic_auth'; 14 | id: string; 15 | userId: string; 16 | email: string; 17 | expiresAt: string; 18 | createdAt: string; 19 | updatedAt: string; 20 | } 21 | 22 | export interface MagicAuthResponse { 23 | object: 'magic_auth'; 24 | id: string; 25 | user_id: string; 26 | email: string; 27 | expires_at: string; 28 | code: string; 29 | created_at: string; 30 | updated_at: string; 31 | } 32 | 33 | export interface MagicAuthEventResponse { 34 | object: 'magic_auth'; 35 | id: string; 36 | user_id: string; 37 | email: string; 38 | expires_at: string; 39 | created_at: string; 40 | updated_at: string; 41 | } 42 | -------------------------------------------------------------------------------- /src/user-management/interfaces/oauth-tokens.interface.ts: -------------------------------------------------------------------------------- 1 | export interface OauthTokens { 2 | accessToken: string; 3 | refreshToken: string; 4 | expiresAt: number; 5 | scopes: string[]; 6 | } 7 | 8 | export interface OauthTokensResponse { 9 | access_token: string; 10 | refresh_token: string; 11 | expires_at: number; 12 | scopes: string[]; 13 | } 14 | -------------------------------------------------------------------------------- /src/user-management/interfaces/organization-membership.interface.ts: -------------------------------------------------------------------------------- 1 | import { RoleResponse } from '../../roles/interfaces/'; 2 | 3 | export type OrganizationMembershipStatus = 'active' | 'inactive' | 'pending'; 4 | 5 | export interface OrganizationMembership { 6 | object: 'organization_membership'; 7 | id: string; 8 | organizationId: string; 9 | organizationName: string; 10 | status: OrganizationMembershipStatus; 11 | userId: string; 12 | createdAt: string; 13 | updatedAt: string; 14 | role: RoleResponse; 15 | } 16 | 17 | export interface OrganizationMembershipResponse { 18 | object: 'organization_membership'; 19 | id: string; 20 | organization_id: string; 21 | organization_name: string; 22 | status: OrganizationMembershipStatus; 23 | user_id: string; 24 | created_at: string; 25 | updated_at: string; 26 | role: RoleResponse; 27 | } 28 | -------------------------------------------------------------------------------- /src/user-management/interfaces/password-hash-type.interface.ts: -------------------------------------------------------------------------------- 1 | export type PasswordHashType = 'bcrypt' | 'firebase-scrypt' | 'ssha' | 'scrypt'; 2 | -------------------------------------------------------------------------------- /src/user-management/interfaces/password-reset.interface.ts: -------------------------------------------------------------------------------- 1 | export interface PasswordReset { 2 | object: 'password_reset'; 3 | id: string; 4 | userId: string; 5 | email: string; 6 | passwordResetToken: string; 7 | passwordResetUrl: string; 8 | expiresAt: string; 9 | createdAt: string; 10 | } 11 | 12 | export interface PasswordResetEvent { 13 | object: 'password_reset'; 14 | id: string; 15 | userId: string; 16 | email: string; 17 | expiresAt: string; 18 | createdAt: string; 19 | } 20 | 21 | export interface PasswordResetResponse { 22 | object: 'password_reset'; 23 | id: string; 24 | user_id: string; 25 | email: string; 26 | password_reset_token: string; 27 | password_reset_url: string; 28 | expires_at: string; 29 | created_at: string; 30 | } 31 | 32 | export interface PasswordResetEventResponse { 33 | object: 'password_reset'; 34 | id: string; 35 | user_id: string; 36 | email: string; 37 | expires_at: string; 38 | created_at: string; 39 | } 40 | -------------------------------------------------------------------------------- /src/user-management/interfaces/refresh-and-seal-session-data.interface.ts: -------------------------------------------------------------------------------- 1 | import { AuthenticateWithSessionCookieSuccessResponse } from './authenticate-with-session-cookie.interface'; 2 | import { AuthenticationResponse } from './authentication-response.interface'; 3 | 4 | export enum RefreshAndSealSessionDataFailureReason { 5 | /** 6 | * @deprecated To be removed in a future major version. 7 | */ 8 | INVALID_SESSION_COOKE = 'invalid_session_cookie', 9 | INVALID_SESSION_COOKIE = 'invalid_session_cookie', 10 | NO_SESSION_COOKIE_PROVIDED = 'no_session_cookie_provided', 11 | 12 | // API OauthErrors for refresh tokens 13 | INVALID_GRANT = 'invalid_grant', 14 | MFA_ENROLLMENT = 'mfa_enrollment', 15 | SSO_REQUIRED = 'sso_required', 16 | /** 17 | * @deprecated To be removed in a future major version. 18 | */ 19 | ORGANIZATION_NOT_AUTHORIZED = 'organization_not_authorized', 20 | } 21 | 22 | type RefreshSessionFailedResponse = { 23 | authenticated: false; 24 | reason: RefreshAndSealSessionDataFailureReason; 25 | }; 26 | 27 | /** 28 | * @deprecated To be removed in a future major version along with `refreshAndSealSessionData`. 29 | */ 30 | type RefreshAndSealSessionDataSuccessResponse = { 31 | authenticated: true; 32 | session?: AuthenticationResponse; 33 | sealedSession?: string; 34 | }; 35 | 36 | export type RefreshAndSealSessionDataResponse = 37 | | RefreshSessionFailedResponse 38 | | RefreshAndSealSessionDataSuccessResponse; 39 | 40 | type RefreshSessionSuccessResponse = Omit< 41 | AuthenticateWithSessionCookieSuccessResponse, 42 | // accessToken is available in the session object and with session 43 | // helpers isn't necessarily useful to return top level 44 | 'accessToken' 45 | > & { 46 | authenticated: true; 47 | session?: AuthenticationResponse; 48 | sealedSession?: string; 49 | }; 50 | 51 | export type RefreshSessionResponse = 52 | | RefreshSessionFailedResponse 53 | | RefreshSessionSuccessResponse; 54 | -------------------------------------------------------------------------------- /src/user-management/interfaces/reset-password-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ResetPasswordOptions { 2 | token: string; 3 | newPassword: string; 4 | } 5 | 6 | export interface SerializedResetPasswordOptions { 7 | token: string; 8 | new_password: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/user-management/interfaces/revoke-session-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface RevokeSessionOptions { 2 | sessionId: string; 3 | } 4 | 5 | export interface SerializedRevokeSessionOptions { 6 | session_id: string; 7 | } 8 | 9 | export const serializeRevokeSessionOptions = ( 10 | options: RevokeSessionOptions, 11 | ): SerializedRevokeSessionOptions => ({ 12 | session_id: options.sessionId, 13 | }); 14 | -------------------------------------------------------------------------------- /src/user-management/interfaces/send-invitation-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface SendInvitationOptions { 2 | email: string; 3 | organizationId?: string; 4 | expiresInDays?: number; 5 | inviterUserId?: string; 6 | roleSlug?: string; 7 | } 8 | 9 | export interface SerializedSendInvitationOptions { 10 | email: string; 11 | organization_id?: string; 12 | expires_in_days?: number; 13 | inviter_user_id?: string; 14 | role_slug?: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/user-management/interfaces/send-magic-auth-code-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface SendMagicAuthCodeOptions { 2 | email: string; 3 | } 4 | 5 | export interface SerializedSendMagicAuthCodeOptions { 6 | email: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/user-management/interfaces/send-password-reset-email-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface SendPasswordResetEmailOptions { 2 | email: string; 3 | passwordResetUrl: string; 4 | } 5 | 6 | export interface SerializedSendPasswordResetEmailOptions { 7 | email: string; 8 | password_reset_url: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/user-management/interfaces/send-verification-email-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface SendVerificationEmailOptions { 2 | userId: string; 3 | } 4 | -------------------------------------------------------------------------------- /src/user-management/interfaces/session-handler-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface SessionHandlerOptions { 2 | sessionData: string; 3 | cookiePassword?: string; 4 | organizationId?: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/user-management/interfaces/session.interface.ts: -------------------------------------------------------------------------------- 1 | import { Impersonator } from './impersonator.interface'; 2 | 3 | export interface Session { 4 | object: 'session'; 5 | id: string; 6 | userId: string; 7 | ipAddress: string | null; 8 | userAgent: string | null; 9 | organizationId?: string; 10 | impersonator?: Impersonator; 11 | } 12 | 13 | export interface SessionResponse { 14 | object: 'session'; 15 | id: string; 16 | user_id: string; 17 | ip_address: string | null; 18 | user_agent: string | null; 19 | organization_id?: string; 20 | impersonator?: Impersonator; 21 | } 22 | -------------------------------------------------------------------------------- /src/user-management/interfaces/update-organization-membership-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface UpdateOrganizationMembershipOptions { 2 | roleSlug?: string; 3 | } 4 | 5 | export interface SerializedUpdateOrganizationMembershipOptions { 6 | role_slug?: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/user-management/interfaces/update-user-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { PasswordHashType } from './password-hash-type.interface'; 2 | 3 | export interface UpdateUserOptions { 4 | userId: string; 5 | email?: string; 6 | firstName?: string; 7 | lastName?: string; 8 | emailVerified?: boolean; 9 | password?: string; 10 | passwordHash?: string; 11 | passwordHashType?: PasswordHashType; 12 | externalId?: string; 13 | metadata?: Record; 14 | } 15 | 16 | export interface SerializedUpdateUserOptions { 17 | email?: string; 18 | first_name?: string; 19 | last_name?: string; 20 | email_verified?: boolean; 21 | password?: string; 22 | password_hash?: string; 23 | password_hash_type?: PasswordHashType; 24 | external_id?: string; 25 | metadata?: Record; 26 | } 27 | -------------------------------------------------------------------------------- /src/user-management/interfaces/update-user-password-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface UpdateUserPasswordOptions { 2 | userId: string; 3 | password: string; 4 | } 5 | 6 | export interface SerializedUpdateUserPasswordOptions { 7 | password: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/user-management/interfaces/user.interface.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | object: 'user'; 3 | id: string; 4 | email: string; 5 | emailVerified: boolean; 6 | profilePictureUrl: string | null; 7 | firstName: string | null; 8 | lastName: string | null; 9 | lastSignInAt: string | null; 10 | createdAt: string; 11 | updatedAt: string; 12 | externalId: string | null; 13 | metadata: Record; 14 | } 15 | 16 | export interface UserResponse { 17 | object: 'user'; 18 | id: string; 19 | email: string; 20 | email_verified: boolean; 21 | profile_picture_url: string | null; 22 | first_name: string | null; 23 | last_name: string | null; 24 | last_sign_in_at: string | null; 25 | created_at: string; 26 | updated_at: string; 27 | external_id?: string; 28 | metadata?: Record; 29 | } 30 | -------------------------------------------------------------------------------- /src/user-management/interfaces/verify-email-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface VerifyEmailOptions { 2 | code: string; 3 | userId: string; 4 | } 5 | 6 | export interface SerializedVerifyEmailOptions { 7 | code: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/user-management/serializers/authenticate-with-code-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateUserWithCodeCredentials, 3 | AuthenticateWithCodeOptions, 4 | SerializedAuthenticateWithCodeOptions, 5 | } from '../interfaces'; 6 | 7 | export const serializeAuthenticateWithCodeOptions = ( 8 | options: AuthenticateWithCodeOptions & AuthenticateUserWithCodeCredentials, 9 | ): SerializedAuthenticateWithCodeOptions => ({ 10 | grant_type: 'authorization_code', 11 | client_id: options.clientId, 12 | client_secret: options.clientSecret, 13 | code: options.code, 14 | code_verifier: options.codeVerifier, 15 | invitation_token: options.invitationToken, 16 | ip_address: options.ipAddress, 17 | user_agent: options.userAgent, 18 | }); 19 | -------------------------------------------------------------------------------- /src/user-management/serializers/authenticate-with-email-verification.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateUserWithEmailVerificationCredentials, 3 | AuthenticateWithEmailVerificationOptions, 4 | SerializedAuthenticateWithEmailVerificationOptions, 5 | } from '../interfaces/authenticate-with-email-verification-options.interface'; 6 | 7 | export const serializeAuthenticateWithEmailVerificationOptions = ( 8 | options: AuthenticateWithEmailVerificationOptions & 9 | AuthenticateUserWithEmailVerificationCredentials, 10 | ): SerializedAuthenticateWithEmailVerificationOptions => ({ 11 | grant_type: 'urn:workos:oauth:grant-type:email-verification:code', 12 | client_id: options.clientId, 13 | client_secret: options.clientSecret, 14 | pending_authentication_token: options.pendingAuthenticationToken, 15 | code: options.code, 16 | ip_address: options.ipAddress, 17 | user_agent: options.userAgent, 18 | }); 19 | -------------------------------------------------------------------------------- /src/user-management/serializers/authenticate-with-magic-auth-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateUserWithMagicAuthCredentials, 3 | AuthenticateWithMagicAuthOptions, 4 | SerializedAuthenticateWithMagicAuthOptions, 5 | } from '../interfaces'; 6 | 7 | export const serializeAuthenticateWithMagicAuthOptions = ( 8 | options: AuthenticateWithMagicAuthOptions & 9 | AuthenticateUserWithMagicAuthCredentials, 10 | ): SerializedAuthenticateWithMagicAuthOptions => ({ 11 | grant_type: 'urn:workos:oauth:grant-type:magic-auth:code', 12 | client_id: options.clientId, 13 | client_secret: options.clientSecret, 14 | code: options.code, 15 | email: options.email, 16 | invitation_token: options.invitationToken, 17 | link_authorization_code: options.linkAuthorizationCode, 18 | ip_address: options.ipAddress, 19 | user_agent: options.userAgent, 20 | }); 21 | -------------------------------------------------------------------------------- /src/user-management/serializers/authenticate-with-organization-selection-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateUserWithOrganizationSelectionCredentials, 3 | AuthenticateWithOrganizationSelectionOptions, 4 | SerializedAuthenticateWithOrganizationSelectionOptions, 5 | } from '../interfaces/authenticate-with-organization-selection.interface'; 6 | 7 | export const serializeAuthenticateWithOrganizationSelectionOptions = ( 8 | options: AuthenticateWithOrganizationSelectionOptions & 9 | AuthenticateUserWithOrganizationSelectionCredentials, 10 | ): SerializedAuthenticateWithOrganizationSelectionOptions => ({ 11 | grant_type: 'urn:workos:oauth:grant-type:organization-selection', 12 | client_id: options.clientId, 13 | client_secret: options.clientSecret, 14 | pending_authentication_token: options.pendingAuthenticationToken, 15 | organization_id: options.organizationId, 16 | ip_address: options.ipAddress, 17 | user_agent: options.userAgent, 18 | }); 19 | -------------------------------------------------------------------------------- /src/user-management/serializers/authenticate-with-password-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateUserWithPasswordCredentials, 3 | AuthenticateWithPasswordOptions, 4 | SerializedAuthenticateWithPasswordOptions, 5 | } from '../interfaces'; 6 | 7 | export const serializeAuthenticateWithPasswordOptions = ( 8 | options: AuthenticateWithPasswordOptions & 9 | AuthenticateUserWithPasswordCredentials, 10 | ): SerializedAuthenticateWithPasswordOptions => ({ 11 | grant_type: 'password', 12 | client_id: options.clientId, 13 | client_secret: options.clientSecret, 14 | email: options.email, 15 | password: options.password, 16 | invitation_token: options.invitationToken, 17 | ip_address: options.ipAddress, 18 | user_agent: options.userAgent, 19 | }); 20 | -------------------------------------------------------------------------------- /src/user-management/serializers/authenticate-with-refresh-token.options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateUserWithCodeCredentials, 3 | AuthenticateWithRefreshTokenOptions, 4 | SerializedAuthenticateWithRefreshTokenOptions, 5 | } from '../interfaces'; 6 | 7 | export const serializeAuthenticateWithRefreshTokenOptions = ( 8 | options: AuthenticateWithRefreshTokenOptions & 9 | AuthenticateUserWithCodeCredentials, 10 | ): SerializedAuthenticateWithRefreshTokenOptions => ({ 11 | grant_type: 'refresh_token', 12 | client_id: options.clientId, 13 | client_secret: options.clientSecret, 14 | refresh_token: options.refreshToken, 15 | organization_id: options.organizationId, 16 | ip_address: options.ipAddress, 17 | user_agent: options.userAgent, 18 | }); 19 | -------------------------------------------------------------------------------- /src/user-management/serializers/authenticate-with-totp-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticateUserWithTotpCredentials, 3 | AuthenticateWithTotpOptions, 4 | SerializedAuthenticateWithTotpOptions, 5 | } from '../interfaces'; 6 | 7 | export const serializeAuthenticateWithTotpOptions = ( 8 | options: AuthenticateWithTotpOptions & AuthenticateUserWithTotpCredentials, 9 | ): SerializedAuthenticateWithTotpOptions => ({ 10 | grant_type: 'urn:workos:oauth:grant-type:mfa-totp', 11 | client_id: options.clientId, 12 | client_secret: options.clientSecret, 13 | code: options.code, 14 | authentication_challenge_id: options.authenticationChallengeId, 15 | pending_authentication_token: options.pendingAuthenticationToken, 16 | ip_address: options.ipAddress, 17 | user_agent: options.userAgent, 18 | }); 19 | -------------------------------------------------------------------------------- /src/user-management/serializers/authentication-event.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticationEvent, 3 | AuthenticationEventResponse, 4 | } from '../interfaces'; 5 | 6 | export const deserializeAuthenticationEvent = ( 7 | authenticationEvent: AuthenticationEventResponse, 8 | ): AuthenticationEvent => ({ 9 | email: authenticationEvent.email, 10 | error: authenticationEvent.error, 11 | ipAddress: authenticationEvent.ip_address, 12 | status: authenticationEvent.status, 13 | type: authenticationEvent.type, 14 | userAgent: authenticationEvent.user_agent, 15 | userId: authenticationEvent.user_id, 16 | }); 17 | -------------------------------------------------------------------------------- /src/user-management/serializers/authentication-radar-risk-event-serializer.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | AuthenticationRadarRiskDetectedEventData, 3 | AuthenticationRadarRiskDetectedEventResponseData, 4 | } from '../interfaces'; 5 | 6 | export const deserializeAuthenticationRadarRiskDetectedEvent = ( 7 | authenticationRadarRiskDetectedEvent: AuthenticationRadarRiskDetectedEventResponseData, 8 | ): AuthenticationRadarRiskDetectedEventData => ({ 9 | authMethod: authenticationRadarRiskDetectedEvent.auth_method, 10 | action: authenticationRadarRiskDetectedEvent.action, 11 | control: authenticationRadarRiskDetectedEvent.control, 12 | blocklistType: authenticationRadarRiskDetectedEvent.blocklist_type, 13 | ipAddress: authenticationRadarRiskDetectedEvent.ip_address, 14 | userAgent: authenticationRadarRiskDetectedEvent.user_agent, 15 | userId: authenticationRadarRiskDetectedEvent.user_id, 16 | email: authenticationRadarRiskDetectedEvent.email, 17 | }); 18 | -------------------------------------------------------------------------------- /src/user-management/serializers/authentication-response.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AuthenticationResponse, 3 | AuthenticationResponseResponse, 4 | } from '../interfaces'; 5 | import { deserializeOauthTokens } from './oauth-tokens.serializer'; 6 | import { deserializeUser } from './user.serializer'; 7 | 8 | export const deserializeAuthenticationResponse = ( 9 | authenticationResponse: AuthenticationResponseResponse, 10 | ): AuthenticationResponse => { 11 | const { 12 | user, 13 | organization_id, 14 | access_token, 15 | refresh_token, 16 | authentication_method, 17 | impersonator, 18 | oauth_tokens, 19 | ...rest 20 | } = authenticationResponse; 21 | 22 | return { 23 | user: deserializeUser(user), 24 | organizationId: organization_id, 25 | accessToken: access_token, 26 | refreshToken: refresh_token, 27 | impersonator, 28 | authenticationMethod: authentication_method, 29 | oauthTokens: deserializeOauthTokens(oauth_tokens), 30 | ...rest, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /src/user-management/serializers/create-magic-auth-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateMagicAuthOptions, 3 | SerializedCreateMagicAuthOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeCreateMagicAuthOptions = ( 7 | options: CreateMagicAuthOptions, 8 | ): SerializedCreateMagicAuthOptions => ({ 9 | email: options.email, 10 | invitation_token: options.invitationToken, 11 | }); 12 | -------------------------------------------------------------------------------- /src/user-management/serializers/create-organization-membership-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateOrganizationMembershipOptions, 3 | SerializedCreateOrganizationMembershipOptions, 4 | } from '../interfaces/create-organization-membership-options.interface'; 5 | 6 | export const serializeCreateOrganizationMembershipOptions = ( 7 | options: CreateOrganizationMembershipOptions, 8 | ): SerializedCreateOrganizationMembershipOptions => ({ 9 | organization_id: options.organizationId, 10 | user_id: options.userId, 11 | role_slug: options.roleSlug, 12 | }); 13 | -------------------------------------------------------------------------------- /src/user-management/serializers/create-password-reset-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreatePasswordResetOptions, 3 | SerializedCreatePasswordResetOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeCreatePasswordResetOptions = ( 7 | options: CreatePasswordResetOptions, 8 | ): SerializedCreatePasswordResetOptions => ({ 9 | email: options.email, 10 | }); 11 | -------------------------------------------------------------------------------- /src/user-management/serializers/create-user-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { CreateUserOptions, SerializedCreateUserOptions } from '../interfaces'; 2 | 3 | export const serializeCreateUserOptions = ( 4 | options: CreateUserOptions, 5 | ): SerializedCreateUserOptions => ({ 6 | email: options.email, 7 | password: options.password, 8 | password_hash: options.passwordHash, 9 | password_hash_type: options.passwordHashType, 10 | first_name: options.firstName, 11 | last_name: options.lastName, 12 | email_verified: options.emailVerified, 13 | external_id: options.externalId, 14 | metadata: options.metadata, 15 | }); 16 | -------------------------------------------------------------------------------- /src/user-management/serializers/email-verification.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EmailVerification, 3 | EmailVerificationEvent, 4 | EmailVerificationEventResponse, 5 | EmailVerificationResponse, 6 | } from '../interfaces'; 7 | 8 | export const deserializeEmailVerification = ( 9 | emailVerification: EmailVerificationResponse, 10 | ): EmailVerification => ({ 11 | object: emailVerification.object, 12 | id: emailVerification.id, 13 | userId: emailVerification.user_id, 14 | email: emailVerification.email, 15 | expiresAt: emailVerification.expires_at, 16 | code: emailVerification.code, 17 | createdAt: emailVerification.created_at, 18 | updatedAt: emailVerification.updated_at, 19 | }); 20 | 21 | export const deserializeEmailVerificationEvent = ( 22 | emailVerification: EmailVerificationEventResponse, 23 | ): EmailVerificationEvent => ({ 24 | object: emailVerification.object, 25 | id: emailVerification.id, 26 | userId: emailVerification.user_id, 27 | email: emailVerification.email, 28 | expiresAt: emailVerification.expires_at, 29 | createdAt: emailVerification.created_at, 30 | updatedAt: emailVerification.updated_at, 31 | }); 32 | -------------------------------------------------------------------------------- /src/user-management/serializers/enroll-auth-factor-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EnrollAuthFactorOptions, 3 | SerializedEnrollUserInMfaFactorOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeEnrollAuthFactorOptions = ( 7 | options: EnrollAuthFactorOptions, 8 | ): SerializedEnrollUserInMfaFactorOptions => ({ 9 | type: options.type, 10 | totp_issuer: options.totpIssuer, 11 | totp_user: options.totpUser, 12 | totp_secret: options.totpSecret, 13 | }); 14 | -------------------------------------------------------------------------------- /src/user-management/serializers/factor.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Factor, 3 | FactorResponse, 4 | FactorWithSecrets, 5 | FactorWithSecretsResponse, 6 | } from '../interfaces/factor.interface'; 7 | import { 8 | deserializeTotp, 9 | deserializeTotpWithSecrets, 10 | } from '../../mfa/serializers/totp.serializer'; 11 | 12 | export const deserializeFactor = (factor: FactorResponse): Factor => ({ 13 | object: factor.object, 14 | id: factor.id, 15 | createdAt: factor.created_at, 16 | updatedAt: factor.updated_at, 17 | type: factor.type, 18 | totp: deserializeTotp(factor.totp), 19 | userId: factor.user_id, 20 | }); 21 | 22 | export const deserializeFactorWithSecrets = ( 23 | factor: FactorWithSecretsResponse, 24 | ): FactorWithSecrets => ({ 25 | object: factor.object, 26 | id: factor.id, 27 | createdAt: factor.created_at, 28 | updatedAt: factor.updated_at, 29 | type: factor.type, 30 | totp: deserializeTotpWithSecrets(factor.totp), 31 | userId: factor.user_id, 32 | }); 33 | -------------------------------------------------------------------------------- /src/user-management/serializers/identity.serializer.ts: -------------------------------------------------------------------------------- 1 | import { Identity, IdentityResponse } from '../interfaces/identity.interface'; 2 | 3 | export const deserializeIdentities = ( 4 | identities: IdentityResponse[], 5 | ): Identity[] => { 6 | return identities.map((identity) => { 7 | return { 8 | idpId: identity.idp_id, 9 | type: identity.type, 10 | provider: identity.provider, 11 | }; 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /src/user-management/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './authenticate-with-code-options.serializer'; 2 | export * from './authenticate-with-magic-auth-options.serializer'; 3 | export * from './authenticate-with-password-options.serializer'; 4 | export * from './authenticate-with-refresh-token.options.serializer'; 5 | export * from './authenticate-with-totp-options.serializer'; 6 | export * from './authentication-event.serializer'; 7 | export * from './authentication-response.serializer'; 8 | export * from './create-magic-auth-options.serializer'; 9 | export * from './create-password-reset-options.serializer'; 10 | export * from './email-verification.serializer'; 11 | export * from './enroll-auth-factor-options.serializer'; 12 | export * from './factor.serializer'; 13 | export * from './invitation.serializer'; 14 | export * from './magic-auth.serializer'; 15 | export * from './password-reset.serializer'; 16 | export * from './reset-password-options.serializer'; 17 | export * from './send-password-reset-email.serializer'; 18 | export * from './create-user-options.serializer'; 19 | export * from './send-magic-auth-code-options.serializer'; 20 | export * from './update-user-options.serializer'; 21 | export * from './update-user-password-options.serializer'; 22 | export * from './user.serializer'; 23 | -------------------------------------------------------------------------------- /src/user-management/serializers/invitation.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Invitation, 3 | InvitationEvent, 4 | InvitationEventResponse, 5 | InvitationResponse, 6 | } from '../interfaces/invitation.interface'; 7 | 8 | export const deserializeInvitation = ( 9 | invitation: InvitationResponse, 10 | ): Invitation => ({ 11 | object: invitation.object, 12 | id: invitation.id, 13 | email: invitation.email, 14 | state: invitation.state, 15 | acceptedAt: invitation.accepted_at, 16 | revokedAt: invitation.revoked_at, 17 | expiresAt: invitation.expires_at, 18 | organizationId: invitation.organization_id, 19 | inviterUserId: invitation.inviter_user_id, 20 | acceptedUserId: invitation.accepted_user_id, 21 | token: invitation.token, 22 | acceptInvitationUrl: invitation.accept_invitation_url, 23 | createdAt: invitation.created_at, 24 | updatedAt: invitation.updated_at, 25 | }); 26 | 27 | export const deserializeInvitationEvent = ( 28 | invitation: InvitationEventResponse, 29 | ): InvitationEvent => ({ 30 | object: invitation.object, 31 | id: invitation.id, 32 | email: invitation.email, 33 | state: invitation.state, 34 | acceptedAt: invitation.accepted_at, 35 | revokedAt: invitation.revoked_at, 36 | expiresAt: invitation.expires_at, 37 | organizationId: invitation.organization_id, 38 | inviterUserId: invitation.inviter_user_id, 39 | acceptedUserId: invitation.accepted_user_id, 40 | createdAt: invitation.created_at, 41 | updatedAt: invitation.updated_at, 42 | }); 43 | -------------------------------------------------------------------------------- /src/user-management/serializers/list-invitations-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ListInvitationsOptions, 3 | SerializedListInvitationsOptions, 4 | } from '../interfaces/list-invitations-options.interface'; 5 | 6 | export const serializeListInvitationsOptions = ( 7 | options: ListInvitationsOptions, 8 | ): SerializedListInvitationsOptions => ({ 9 | email: options.email, 10 | organization_id: options.organizationId, 11 | limit: options.limit, 12 | before: options.before, 13 | after: options.after, 14 | order: options.order, 15 | }); 16 | -------------------------------------------------------------------------------- /src/user-management/serializers/list-organization-memberships-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ListOrganizationMembershipsOptions, 3 | SerializedListOrganizationMembershipsOptions, 4 | } from '../interfaces/list-organization-memberships-options.interface'; 5 | 6 | export const serializeListOrganizationMembershipsOptions = ( 7 | options: ListOrganizationMembershipsOptions, 8 | ): SerializedListOrganizationMembershipsOptions => ({ 9 | user_id: options.userId, 10 | organization_id: options.organizationId, 11 | statuses: options.statuses?.join(','), 12 | limit: options.limit, 13 | before: options.before, 14 | after: options.after, 15 | order: options.order, 16 | }); 17 | -------------------------------------------------------------------------------- /src/user-management/serializers/list-users-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { ListUsersOptions, SerializedListUsersOptions } from '../interfaces'; 2 | 3 | export const serializeListUsersOptions = ( 4 | options: ListUsersOptions, 5 | ): SerializedListUsersOptions => ({ 6 | email: options.email, 7 | organization_id: options.organizationId, 8 | limit: options.limit, 9 | before: options.before, 10 | after: options.after, 11 | order: options.order, 12 | }); 13 | -------------------------------------------------------------------------------- /src/user-management/serializers/magic-auth.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MagicAuth, 3 | MagicAuthEvent, 4 | MagicAuthEventResponse, 5 | MagicAuthResponse, 6 | } from '../interfaces/magic-auth.interface'; 7 | 8 | export const deserializeMagicAuth = ( 9 | magicAuth: MagicAuthResponse, 10 | ): MagicAuth => ({ 11 | object: magicAuth.object, 12 | id: magicAuth.id, 13 | userId: magicAuth.user_id, 14 | email: magicAuth.email, 15 | expiresAt: magicAuth.expires_at, 16 | code: magicAuth.code, 17 | createdAt: magicAuth.created_at, 18 | updatedAt: magicAuth.updated_at, 19 | }); 20 | 21 | export const deserializeMagicAuthEvent = ( 22 | magicAuth: MagicAuthEventResponse, 23 | ): MagicAuthEvent => ({ 24 | object: magicAuth.object, 25 | id: magicAuth.id, 26 | userId: magicAuth.user_id, 27 | email: magicAuth.email, 28 | expiresAt: magicAuth.expires_at, 29 | createdAt: magicAuth.created_at, 30 | updatedAt: magicAuth.updated_at, 31 | }); 32 | -------------------------------------------------------------------------------- /src/user-management/serializers/oauth-tokens.serializer.ts: -------------------------------------------------------------------------------- 1 | import { OauthTokensResponse, OauthTokens } from '../interfaces'; 2 | 3 | export const deserializeOauthTokens = ( 4 | oauthTokens?: OauthTokensResponse, 5 | ): OauthTokens | undefined => 6 | oauthTokens 7 | ? { 8 | accessToken: oauthTokens.access_token, 9 | refreshToken: oauthTokens.refresh_token, 10 | expiresAt: oauthTokens.expires_at, 11 | scopes: oauthTokens.scopes, 12 | } 13 | : undefined; 14 | -------------------------------------------------------------------------------- /src/user-management/serializers/organization-membership.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OrganizationMembership, 3 | OrganizationMembershipResponse, 4 | } from '../interfaces/organization-membership.interface'; 5 | 6 | export const deserializeOrganizationMembership = ( 7 | organizationMembership: OrganizationMembershipResponse, 8 | ): OrganizationMembership => ({ 9 | object: organizationMembership.object, 10 | id: organizationMembership.id, 11 | userId: organizationMembership.user_id, 12 | organizationId: organizationMembership.organization_id, 13 | organizationName: organizationMembership.organization_name, 14 | status: organizationMembership.status, 15 | createdAt: organizationMembership.created_at, 16 | updatedAt: organizationMembership.updated_at, 17 | role: organizationMembership.role, 18 | }); 19 | -------------------------------------------------------------------------------- /src/user-management/serializers/password-reset.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PasswordReset, 3 | PasswordResetEvent, 4 | PasswordResetEventResponse, 5 | PasswordResetResponse, 6 | } from '../interfaces'; 7 | 8 | export const deserializePasswordReset = ( 9 | passwordReset: PasswordResetResponse, 10 | ): PasswordReset => ({ 11 | object: passwordReset.object, 12 | id: passwordReset.id, 13 | userId: passwordReset.user_id, 14 | email: passwordReset.email, 15 | passwordResetToken: passwordReset.password_reset_token, 16 | passwordResetUrl: passwordReset.password_reset_url, 17 | expiresAt: passwordReset.expires_at, 18 | createdAt: passwordReset.created_at, 19 | }); 20 | 21 | export const deserializePasswordResetEvent = ( 22 | passwordReset: PasswordResetEventResponse, 23 | ): PasswordResetEvent => ({ 24 | object: passwordReset.object, 25 | id: passwordReset.id, 26 | userId: passwordReset.user_id, 27 | email: passwordReset.email, 28 | expiresAt: passwordReset.expires_at, 29 | createdAt: passwordReset.created_at, 30 | }); 31 | -------------------------------------------------------------------------------- /src/user-management/serializers/reset-password-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ResetPasswordOptions, 3 | SerializedResetPasswordOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeResetPasswordOptions = ( 7 | options: ResetPasswordOptions, 8 | ): SerializedResetPasswordOptions => ({ 9 | token: options.token, 10 | new_password: options.newPassword, 11 | }); 12 | -------------------------------------------------------------------------------- /src/user-management/serializers/role.serializer.ts: -------------------------------------------------------------------------------- 1 | import { RoleEvent, RoleEventResponse } from '../../roles/interfaces'; 2 | 3 | export const deserializeRoleEvent = (role: RoleEventResponse): RoleEvent => ({ 4 | object: 'role', 5 | slug: role.slug, 6 | permissions: role.permissions, 7 | createdAt: role.created_at, 8 | updatedAt: role.updated_at, 9 | }); 10 | -------------------------------------------------------------------------------- /src/user-management/serializers/send-invitation-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import {} from '../interfaces'; 2 | import { 3 | SendInvitationOptions, 4 | SerializedSendInvitationOptions, 5 | } from '../interfaces/send-invitation-options.interface'; 6 | 7 | export const serializeSendInvitationOptions = ( 8 | options: SendInvitationOptions, 9 | ): SerializedSendInvitationOptions => ({ 10 | email: options.email, 11 | organization_id: options.organizationId, 12 | expires_in_days: options.expiresInDays, 13 | inviter_user_id: options.inviterUserId, 14 | role_slug: options.roleSlug, 15 | }); 16 | -------------------------------------------------------------------------------- /src/user-management/serializers/send-magic-auth-code-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SendMagicAuthCodeOptions, 3 | SerializedSendMagicAuthCodeOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeSendMagicAuthCodeOptions = ( 7 | options: SendMagicAuthCodeOptions, 8 | ): SerializedSendMagicAuthCodeOptions => ({ 9 | email: options.email, 10 | }); 11 | -------------------------------------------------------------------------------- /src/user-management/serializers/send-password-reset-email.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SendPasswordResetEmailOptions, 3 | SerializedSendPasswordResetEmailOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeSendPasswordResetEmailOptions = ( 7 | options: SendPasswordResetEmailOptions, 8 | ): SerializedSendPasswordResetEmailOptions => ({ 9 | email: options.email, 10 | password_reset_url: options.passwordResetUrl, 11 | }); 12 | -------------------------------------------------------------------------------- /src/user-management/serializers/session.serializer.ts: -------------------------------------------------------------------------------- 1 | import { Session, SessionResponse } from '../interfaces'; 2 | 3 | export const deserializeSession = (session: SessionResponse): Session => ({ 4 | object: 'session', 5 | id: session.id, 6 | userId: session.user_id, 7 | ipAddress: session.ip_address, 8 | userAgent: session.user_agent, 9 | organizationId: session.organization_id, 10 | impersonator: session.impersonator, 11 | }); 12 | -------------------------------------------------------------------------------- /src/user-management/serializers/update-organization-membership-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | UpdateOrganizationMembershipOptions, 3 | SerializedUpdateOrganizationMembershipOptions, 4 | } from '../interfaces/update-organization-membership-options.interface'; 5 | 6 | export const serializeUpdateOrganizationMembershipOptions = ( 7 | options: UpdateOrganizationMembershipOptions, 8 | ): SerializedUpdateOrganizationMembershipOptions => ({ 9 | role_slug: options.roleSlug, 10 | }); 11 | -------------------------------------------------------------------------------- /src/user-management/serializers/update-user-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { SerializedUpdateUserOptions, UpdateUserOptions } from '../interfaces'; 2 | 3 | export const serializeUpdateUserOptions = ( 4 | options: UpdateUserOptions, 5 | ): SerializedUpdateUserOptions => ({ 6 | email: options.email, 7 | email_verified: options.emailVerified, 8 | first_name: options.firstName, 9 | last_name: options.lastName, 10 | password: options.password, 11 | password_hash: options.passwordHash, 12 | password_hash_type: options.passwordHashType, 13 | external_id: options.externalId, 14 | metadata: options.metadata, 15 | }); 16 | -------------------------------------------------------------------------------- /src/user-management/serializers/update-user-password-options.serializer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SerializedUpdateUserPasswordOptions, 3 | UpdateUserPasswordOptions, 4 | } from '../interfaces'; 5 | 6 | export const serializeUpdateUserPasswordOptions = ( 7 | options: UpdateUserPasswordOptions, 8 | ): SerializedUpdateUserPasswordOptions => ({ 9 | password: options.password, 10 | }); 11 | -------------------------------------------------------------------------------- /src/user-management/serializers/user.serializer.spec.ts: -------------------------------------------------------------------------------- 1 | import { deserializeUser } from './user.serializer'; 2 | import userFixture from '../fixtures/user.json'; 3 | 4 | describe('deserializeUser', () => { 5 | it('includes metadata if present', () => { 6 | const metadata = { key: 'value' }; 7 | 8 | expect( 9 | deserializeUser({ 10 | ...userFixture, 11 | object: 'user', 12 | metadata, 13 | }), 14 | ).toMatchObject({ 15 | metadata, 16 | }); 17 | }); 18 | 19 | it('coerces missing metadata to empty object', () => { 20 | const { metadata, ...userResponseWithoutMetadata } = userFixture; 21 | 22 | expect( 23 | deserializeUser({ ...userResponseWithoutMetadata, object: 'user' }), 24 | ).toMatchObject({ 25 | metadata: {}, 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/user-management/serializers/user.serializer.ts: -------------------------------------------------------------------------------- 1 | import { User, UserResponse } from '../interfaces'; 2 | 3 | export const deserializeUser = (user: UserResponse): User => ({ 4 | object: user.object, 5 | id: user.id, 6 | email: user.email, 7 | emailVerified: user.email_verified, 8 | firstName: user.first_name, 9 | profilePictureUrl: user.profile_picture_url, 10 | lastName: user.last_name, 11 | lastSignInAt: user.last_sign_in_at, 12 | createdAt: user.created_at, 13 | updatedAt: user.updated_at, 14 | externalId: user.external_id ?? null, 15 | metadata: user.metadata ?? {}, 16 | }); 17 | -------------------------------------------------------------------------------- /src/vault/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './key/create-data-key.interface'; 2 | export * from './key/decrypt-data-key.interface'; 3 | export * from './key.interface'; 4 | export * from './object/create-object.interface'; 5 | export * from './object/delete-object.interface'; 6 | export * from './object/list-object-versions.interface'; 7 | export * from './object/list-objects.interface'; 8 | export * from './object/read-object.interface'; 9 | export * from './object/update-object.interface'; 10 | export * from './object.interface'; 11 | export * from './secret.interface'; 12 | -------------------------------------------------------------------------------- /src/vault/interfaces/key.interface.ts: -------------------------------------------------------------------------------- 1 | export interface KeyContext { 2 | [key: string]: any; 3 | } 4 | 5 | export interface DataKeyPair { 6 | context: KeyContext; 7 | dataKey: DataKey; 8 | encryptedKeys: string; 9 | } 10 | 11 | export interface DataKey { 12 | key: string; 13 | id: string; 14 | } 15 | -------------------------------------------------------------------------------- /src/vault/interfaces/key/create-data-key.interface.ts: -------------------------------------------------------------------------------- 1 | import { KeyContext } from '../key.interface'; 2 | 3 | export interface CreateDataKeyOptions { 4 | context: KeyContext; 5 | } 6 | 7 | export interface CreateDataKeyResponse { 8 | context: KeyContext; 9 | data_key: string; 10 | encrypted_keys: string; 11 | id: string; 12 | } 13 | -------------------------------------------------------------------------------- /src/vault/interfaces/key/decrypt-data-key.interface.ts: -------------------------------------------------------------------------------- 1 | export interface DecryptDataKeyOptions { 2 | keys: string; 3 | } 4 | 5 | export interface DecryptDataKeyResponse { 6 | data_key: string; 7 | id: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/vault/interfaces/object.interface.ts: -------------------------------------------------------------------------------- 1 | import { KeyContext } from './key.interface'; 2 | 3 | export interface ObjectDigest { 4 | id: string; 5 | name: string; 6 | updatedAt: Date; 7 | } 8 | 9 | export interface ObjectUpdateBy { 10 | id: string; 11 | name: string; 12 | } 13 | 14 | export interface ObjectMetadata { 15 | context: KeyContext; 16 | environmentId: string; 17 | id: string; 18 | keyId: string; 19 | updatedAt: Date; 20 | updatedBy: ObjectUpdateBy; 21 | versionId: string; 22 | } 23 | 24 | export interface VaultObject { 25 | id: string; 26 | metadata: ObjectMetadata; 27 | name: string; 28 | value?: string; 29 | } 30 | 31 | export interface ObjectVersion { 32 | createdAt: Date; 33 | currentVersion: boolean; 34 | id: string; 35 | } 36 | -------------------------------------------------------------------------------- /src/vault/interfaces/object/create-object.interface.ts: -------------------------------------------------------------------------------- 1 | import { KeyContext } from '../key.interface'; 2 | 3 | export interface CreateObjectEntity { 4 | name: string; 5 | value: string; 6 | key_context: KeyContext; 7 | } 8 | 9 | export interface CreateObjectOptions { 10 | name: string; 11 | value: string; 12 | context: KeyContext; 13 | } 14 | -------------------------------------------------------------------------------- /src/vault/interfaces/object/delete-object.interface.ts: -------------------------------------------------------------------------------- 1 | export interface DeleteObjectOptions { 2 | id: string; 3 | } 4 | -------------------------------------------------------------------------------- /src/vault/interfaces/object/list-object-versions.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ObjectVersionResponse { 2 | id: string; 3 | created_at: string; 4 | current_version: boolean; 5 | } 6 | 7 | export interface ListObjectVersionsResponse { 8 | data: ObjectVersionResponse[]; 9 | } 10 | -------------------------------------------------------------------------------- /src/vault/interfaces/object/list-objects.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ObjectDigestResponse { 2 | id: string; 3 | name: string; 4 | updated_at: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/vault/interfaces/object/read-object.interface.ts: -------------------------------------------------------------------------------- 1 | import { KeyContext } from '../key.interface'; 2 | import { ObjectUpdateBy } from '../object.interface'; 3 | 4 | export interface ReadObjectOptions { 5 | id: string; 6 | } 7 | 8 | export interface ReadObjectMetadataResponse { 9 | context: KeyContext; 10 | environment_id: string; 11 | id: string; 12 | key_id: string; 13 | updated_at: string; 14 | updated_by: ObjectUpdateBy; 15 | version_id: string; 16 | } 17 | 18 | export interface ReadObjectResponse { 19 | id: string; 20 | metadata: ReadObjectMetadataResponse; 21 | name: string; 22 | value: string; 23 | } 24 | -------------------------------------------------------------------------------- /src/vault/interfaces/object/update-object.interface.ts: -------------------------------------------------------------------------------- 1 | export interface UpdateObjectEntity { 2 | value: string; 3 | version_check?: string; 4 | } 5 | 6 | export interface UpdateObjectOptions { 7 | id: string; 8 | value: string; 9 | versionCheck?: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/vault/interfaces/secret.interface.ts: -------------------------------------------------------------------------------- 1 | import { KeyContext } from './key.interface'; 2 | import { 3 | ObjectDigest, 4 | ObjectUpdateBy, 5 | ObjectMetadata, 6 | VaultObject, 7 | ObjectVersion, 8 | } from './object.interface'; 9 | 10 | // tslint:disable:no-empty-interface 11 | 12 | /* 13 | * @deprecated Use `KeyContext` instead. 14 | */ 15 | export interface SecretContext extends KeyContext {} 16 | /* 17 | * @deprecated Use `ObjectDigest` instead. 18 | */ 19 | export interface SecretDigest extends ObjectDigest {} 20 | /* 21 | * @deprecated Use `ObjectUpdateBy` instead. 22 | */ 23 | export interface SecretUpdateBy extends ObjectUpdateBy {} 24 | /* 25 | * @deprecated Use `ObjectMetadata` instead. 26 | */ 27 | export interface SecretMetadata extends ObjectMetadata {} 28 | /* 29 | * @deprecated Use `VaultObject` instead. 30 | */ 31 | export interface VaultSecret extends VaultObject {} 32 | /* 33 | * @deprecated Use `ObjectVersion` instead. 34 | */ 35 | export interface SecretVersion extends ObjectVersion {} 36 | -------------------------------------------------------------------------------- /src/vault/leb.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'leb'; 2 | -------------------------------------------------------------------------------- /src/vault/serializers/vault-key.serializer.ts: -------------------------------------------------------------------------------- 1 | import { CreateDataKeyResponse } from '../interfaces/key/create-data-key.interface'; 2 | import { DecryptDataKeyResponse } from '../interfaces/key/decrypt-data-key.interface'; 3 | import { DataKey, DataKeyPair } from '../interfaces/key.interface'; 4 | 5 | export const deserializeCreateDataKeyResponse = ( 6 | key: CreateDataKeyResponse, 7 | ): DataKeyPair => ({ 8 | context: key.context, 9 | dataKey: { 10 | key: key.data_key, 11 | id: key.id, 12 | }, 13 | encryptedKeys: key.encrypted_keys, 14 | }); 15 | 16 | export const deserializeDecryptDataKeyResponse = ( 17 | key: DecryptDataKeyResponse, 18 | ): DataKey => ({ 19 | key: key.data_key, 20 | id: key.id, 21 | }); 22 | -------------------------------------------------------------------------------- /src/webhooks/fixtures/webhook.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "wh_123", 3 | "data": { 4 | "id": "directory_user_01FAEAJCR3ZBZ30D8BD1924TVG", 5 | "state": "active", 6 | "emails": [ 7 | { 8 | "type": "work", 9 | "value": "blair@foo-corp.com", 10 | "primary": true 11 | } 12 | ], 13 | "idp_id": "00u1e8mutl6wlH3lL4x7", 14 | "object": "directory_user", 15 | "username": "blair@foo-corp.com", 16 | "last_name": "Lunchford", 17 | "first_name": "Blair", 18 | "job_title": "Software Engineer", 19 | "directory_id": "directory_01F9M7F68PZP8QXP8G7X5QRHS7", 20 | "created_at": "2021-06-25T19:07:33.155Z", 21 | "updated_at": "2021-06-25T19:07:33.155Z", 22 | "raw_attributes": { 23 | "name": { 24 | "givenName": "Blair", 25 | "familyName": "Lunchford", 26 | "middleName": "Elizabeth", 27 | "honorificPrefix": "Ms." 28 | }, 29 | "title": "Software Engineer", 30 | "active": true, 31 | "emails": [ 32 | { 33 | "type": "work", 34 | "value": "blair@foo-corp.com", 35 | "primary": true 36 | } 37 | ], 38 | "groups": [], 39 | "locale": "en-US", 40 | "schemas": [ 41 | "urn:ietf:params:scim:schemas:core:2.0:User", 42 | "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" 43 | ], 44 | "userName": "blair@foo-corp.com", 45 | "addresses": [ 46 | { 47 | "region": "CA", 48 | "primary": true, 49 | "locality": "San Francisco", 50 | "postalCode": "94016" 51 | } 52 | ], 53 | "externalId": "00u1e8mutl6wlH3lL4x7", 54 | "displayName": "Blair Lunchford", 55 | "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": { 56 | "manager": { 57 | "value": "2", 58 | "displayName": "Kate Chapman" 59 | }, 60 | "division": "Engineering", 61 | "department": "Customer Success" 62 | } 63 | } 64 | }, 65 | "event": "dsync.user.created", 66 | "created_at": "2021-06-25T19:07:33.155Z" 67 | } -------------------------------------------------------------------------------- /src/webhooks/webhooks.ts: -------------------------------------------------------------------------------- 1 | import { deserializeEvent } from '../common/serializers'; 2 | import { Event, EventResponse } from '../common/interfaces'; 3 | import { SignatureProvider } from '../common/crypto/signature-provider'; 4 | import { CryptoProvider } from '../common/crypto/crypto-provider'; 5 | 6 | export class Webhooks { 7 | private signatureProvider: SignatureProvider; 8 | 9 | constructor(cryptoProvider: CryptoProvider) { 10 | this.signatureProvider = new SignatureProvider(cryptoProvider); 11 | } 12 | 13 | get verifyHeader() { 14 | return this.signatureProvider.verifyHeader.bind(this.signatureProvider); 15 | } 16 | 17 | get computeSignature() { 18 | return this.signatureProvider.computeSignature.bind(this.signatureProvider); 19 | } 20 | 21 | get getTimestampAndSignatureHash() { 22 | return this.signatureProvider.getTimestampAndSignatureHash.bind( 23 | this.signatureProvider, 24 | ); 25 | } 26 | 27 | async constructEvent({ 28 | payload, 29 | sigHeader, 30 | secret, 31 | tolerance = 180000, 32 | }: { 33 | payload: unknown; 34 | sigHeader: string; 35 | secret: string; 36 | tolerance?: number; 37 | }): Promise { 38 | const options = { payload, sigHeader, secret, tolerance }; 39 | await this.verifyHeader(options); 40 | 41 | const webhookPayload = payload as EventResponse; 42 | 43 | return deserializeEvent(webhookPayload); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/widgets/fixtures/get-token-error.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "User not found 'user_123'", 3 | "code": "entity_not_found", 4 | "entity_id": "user_123" 5 | } -------------------------------------------------------------------------------- /src/widgets/fixtures/token.json: -------------------------------------------------------------------------------- 1 | { 2 | "token": "this.is.a.token" 3 | } -------------------------------------------------------------------------------- /src/widgets/interfaces/get-token.ts: -------------------------------------------------------------------------------- 1 | export type WidgetScope = 'widgets:users-table:manage'; 2 | 3 | export interface GetTokenOptions { 4 | organizationId: string; 5 | userId: string; 6 | scopes?: [WidgetScope]; 7 | } 8 | 9 | export interface SerializedGetTokenOptions { 10 | organization_id: string; 11 | user_id: string; 12 | scopes?: [WidgetScope]; 13 | } 14 | 15 | export const serializeGetTokenOptions = ( 16 | options: GetTokenOptions, 17 | ): SerializedGetTokenOptions => ({ 18 | organization_id: options.organizationId, 19 | user_id: options.userId, 20 | scopes: options.scopes, 21 | }); 22 | 23 | export interface GetTokenResponse { 24 | token: string; 25 | } 26 | 27 | export interface GetTokenResponseResponse { 28 | token: string; 29 | } 30 | 31 | export const deserializeGetTokenResponse = ( 32 | data: GetTokenResponseResponse, 33 | ): GetTokenResponse => ({ 34 | token: data.token, 35 | }); 36 | -------------------------------------------------------------------------------- /src/widgets/widgets.spec.ts: -------------------------------------------------------------------------------- 1 | import fetch from 'jest-fetch-mock'; 2 | import { WorkOS } from '../workos'; 3 | import { fetchOnce, fetchURL } from '../common/utils/test-utils'; 4 | import tokenFixture from './fixtures/token.json'; 5 | import getTokenErrorFixture from './fixtures/get-token-error.json'; 6 | 7 | describe('Widgets', () => { 8 | let workos: WorkOS; 9 | 10 | beforeAll(() => { 11 | workos = new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU', { 12 | apiHostname: 'api.workos.test', 13 | clientId: 'proj_123', 14 | }); 15 | }); 16 | 17 | beforeEach(() => fetch.resetMocks()); 18 | describe('getToken', () => { 19 | it('sends a Get Token request', async () => { 20 | fetchOnce(tokenFixture); 21 | const token = await workos.widgets.getToken({ 22 | organizationId: 'org_123', 23 | userId: 'user_123', 24 | scopes: ['widgets:users-table:manage'], 25 | }); 26 | expect(fetchURL()).toContain('/widgets/token'); 27 | expect(token).toEqual('this.is.a.token'); 28 | }); 29 | 30 | it('returns an error if the API returns an error', async () => { 31 | fetchOnce(getTokenErrorFixture, { status: 404 }); 32 | await expect( 33 | workos.widgets.getToken({ 34 | organizationId: 'org_123', 35 | userId: 'user_123', 36 | scopes: ['widgets:users-table:manage'], 37 | }), 38 | ).rejects.toThrow("User not found 'user_123'"); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/widgets/widgets.ts: -------------------------------------------------------------------------------- 1 | import { WorkOS } from '../workos'; 2 | import { 3 | deserializeGetTokenResponse, 4 | GetTokenOptions, 5 | GetTokenResponseResponse, 6 | SerializedGetTokenOptions, 7 | serializeGetTokenOptions, 8 | } from './interfaces/get-token'; 9 | 10 | export class Widgets { 11 | constructor(private readonly workos: WorkOS) {} 12 | 13 | async getToken(payload: GetTokenOptions): Promise { 14 | const { data } = await this.workos.post< 15 | GetTokenResponseResponse, 16 | SerializedGetTokenOptions 17 | >('/widgets/token', serializeGetTokenOptions(payload)); 18 | 19 | return deserializeGetTokenResponse(data).token; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/worker.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment miniflare 3 | */ 4 | 5 | import { WorkOS } from './index.worker'; 6 | 7 | test('WorkOS is initialized without errors', () => { 8 | expect(() => new WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU')).not.toThrow(); 9 | }); 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "alwaysStrict": true, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "moduleResolution": "node", 8 | "noFallthroughCasesInSwitch": true, 9 | "noUnusedLocals": true, 10 | "noUnusedParameters": true, 11 | "resolveJsonModule": true, 12 | "target": "es6", 13 | "module": "commonjs", 14 | "declaration": true, 15 | "outDir": "lib", 16 | "strict": true, 17 | "lib": ["dom", "es2022"], 18 | "types": ["jest", "jest-environment-miniflare/globals"] 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": { 5 | "no-unused-expression": true 6 | }, 7 | "rules": { 8 | "object-literal-key-quotes": false, 9 | "arrow-parens": false, 10 | "interface-name": [false], 11 | "max-line-length": [true, 150], 12 | "member-access": [false], 13 | "member-ordering": [false], 14 | "object-literal-sort-keys": false, 15 | "ordered-imports": [false], 16 | "quotemark": [true, "single", "jsx-double", "avoid-escape"], 17 | "variable-name": [true, "allow-leading-underscore"], 18 | "semicolon": [true, "always", "ignore-bound-class-methods"], 19 | "no-default-export": true, 20 | "no-shadowed-variable": false 21 | }, 22 | "rulesDirectory": [] 23 | } 24 | --------------------------------------------------------------------------------