diff --git a/.env.sample b/.env.sample new file mode 100644 index 00000000000..e35554989cb --- /dev/null +++ b/.env.sample @@ -0,0 +1,9 @@ +# Default config +TRUST_PROXY_HEADER=1 +DB_URL=postgresql://postgres:p0stgr3s@localhost:5433/logto +ADMIN_PORT=3302 +PORT=3301 + +# OGCIO Config +MOCK_TOKEN_ENDPOINT=http://localhost:4005/logto/mock/token +MOCK_KEYS_ENDPOINT=http://localhost:4005/logto/mock/keys \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 33e6bb149b2..310af5c1bac 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,10 @@ -/packages/schemas/tables @simeng-li @wangsijie @gao-sun -/packages/core @simeng-li @wangsijie @gao-sun -/packages/console @wangsijie @charIeszhao @gao-sun -/packages/ui @simeng-li @charIeszhao +/.github/ @gao-sun /.changeset @gao-sun +/packages/schemas/tables/ @simeng-li @wangsijie @gao-sun +/packages/core/ @simeng-li @wangsijie @gao-sun +/packages/console/ @wangsijie @charIeszhao @xiaoyijun @gao-sun +/packages/ui/ @simeng-li @charIeszhao +connector*/ @darcyYe @gao-sun + +# The file below should be generated by the script, just in case someone accidentally edits it +/packages/console/src/assets/docs/guides/index.ts @gao-sun diff --git a/.github/workflows/alteration-compatibility-integration-test.yml b/.github/workflows/alteration-compatibility-integration-test.yml index 2b06b877c93..4e82e5485c7 100644 --- a/.github/workflows/alteration-compatibility-integration-test.yml +++ b/.github/workflows/alteration-compatibility-integration-test.yml @@ -28,7 +28,7 @@ jobs: id: changes-detection run: | if [[ "${{ github.event_name }}" == "pull_request" ]]; then - BASE=$(git merge-base origin/${{github.base_ref}} HEAD) + BASE=$(git merge-base origin/${{ github.base_ref }} HEAD) else BASE=${{ github.event.before }} fi @@ -37,24 +37,25 @@ jobs: if [ -n "$CHANGE_FILES" ]; then echo "$CHANGE_FILES" - echo "::set-output name=has-alteration-changes::true" + echo "has-alteration-changes=true" >> $GITHUB_OUTPUT echo "Alteration changes detected" else - echo "::set-output name=has-alteration-changes::false" + echo "has-alteration-changes=false" >> $GITHUB_OUTPUT echo "No alteration changes detected" fi package: needs: check-alteration-changes runs-on: ubuntu-latest - if: ${{needs.check-alteration-changes.outputs.has-alteration-changes == 'true'}} + if: ${{ needs.check-alteration-changes.outputs.has-alteration-changes == 'true' }} env: INTEGRATION_TEST: true + DEV_FEATURES_ENABLED: false steps: - - uses: logto-io/actions-package-logto-artifact@v2 + - uses: logto-io/actions-package-logto-artifact@v3 with: artifact-name: alteration-integration-test-${{ github.sha }} - branch: ${{github.base_ref}} + branch: ${{ github.base_ref }} pnpm-version: 9 run-logto: @@ -66,13 +67,27 @@ jobs: runs-on: ubuntu-latest env: INTEGRATION_TEST: true + DEV_FEATURES_ENABLED: false DB_URL: postgres://postgres:postgres@localhost:5432/postgres steps: - - uses: logto-io/actions-run-logto-integration-tests@v3 + - uses: logto-io/actions-run-logto-integration-tests@v4 with: - branch: ${{github.base_ref}} + branch: ${{ github.base_ref }} logto-artifact: alteration-integration-test-${{ github.sha }} test-target: ${{ matrix.target }} - db-alteration-target: ${{github.head_ref}} + db-alteration-target: ${{ github.head_ref }} pnpm-version: 9 + + # Automatically rerun the workflow since the integration tests are moody + # From this genius: https://github.com/orgs/community/discussions/67654#discussioncomment-8038649 + rerun-on-failure: + needs: run-logto + if: failure() && fromJSON(github.run_attempt) < 3 + runs-on: ubuntu-latest + steps: + - env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + GH_DEBUG: api + run: gh workflow run rerun.yml -F run_id=${{ github.run_id }} diff --git a/.github/workflows/changesets.yml b/.github/workflows/changesets.yml index 0815cc3d372..8c70dc7d811 100644 --- a/.github/workflows/changesets.yml +++ b/.github/workflows/changesets.yml @@ -18,7 +18,7 @@ jobs: token: ${{ secrets.BOT_PAT }} - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 4e031d6450f..2f568a9e433 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -21,7 +21,7 @@ jobs: fetch-depth: 0 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 0fd1557dbd8..076267b8795 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -4,7 +4,6 @@ on: push: branches: - master - - "push-action/**" pull_request: concurrency: @@ -13,14 +12,19 @@ concurrency: jobs: package: + strategy: + matrix: + # Run the integration tests with and without dev features enabled + dev-features-enabled: [true, false] runs-on: ubuntu-latest env: INTEGRATION_TEST: true + DEV_FEATURES_ENABLED: ${{ matrix.dev-features-enabled }} steps: - - uses: logto-io/actions-package-logto-artifact@v2 + - uses: logto-io/actions-package-logto-artifact@v3 with: - artifact-name: integration-test-${{ github.sha }} + artifact-name: integration-test-${{ github.sha }}-dev-features-${{ matrix.dev-features-enabled }} pnpm-version: 9 run-logto: @@ -28,15 +32,31 @@ jobs: fail-fast: false matrix: target: [api, experience, console] + # Run the integration tests with and without dev features enabled + dev-features-enabled: [true, false] needs: package runs-on: ubuntu-latest env: INTEGRATION_TEST: true + DEV_FEATURES_ENABLED: ${{ matrix.dev-features-enabled }} DB_URL: postgres://postgres:postgres@localhost:5432/postgres steps: - - uses: logto-io/actions-run-logto-integration-tests@v3 + - uses: logto-io/actions-run-logto-integration-tests@v4 with: - logto-artifact: integration-test-${{ github.sha }} + logto-artifact: integration-test-${{ github.sha }}-dev-features-${{ env.DEV_FEATURES_ENABLED }} test-target: ${{ matrix.target }} pnpm-version: 9 + + # Automatically rerun the workflow since the integration tests are moody + # From this genius: https://github.com/orgs/community/discussions/67654#discussioncomment-8038649 + rerun-on-failure: + needs: run-logto + if: failure() && fromJSON(github.run_attempt) < 3 + runs-on: ubuntu-latest + steps: + - env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + GH_DEBUG: api + run: gh workflow run rerun.yml -r ${{ github.head_ref || github.ref_name }} -F run_id=${{ github.run_id }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 82d8ea6905e..b525dd8d6f1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 @@ -53,7 +53,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 @@ -89,7 +89,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . build-args: | # Test cloud build @@ -120,7 +120,7 @@ jobs: run: cp ./fresh/pnpm-lock.yaml ./ - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 run-install: false @@ -136,7 +136,7 @@ jobs: # ** End ** - name: Setup Postgres - uses: ikalnytskyi/action-setup-postgres@v5 + uses: ikalnytskyi/action-setup-postgres@v6 # ** Setup up-to-date databases and compare (test `up`) ** - name: Setup fresh database diff --git a/.github/workflows/master-codecov-report.yml b/.github/workflows/master-codecov-report.yml index 9f1efb01c04..1fe2fa12f9b 100644 --- a/.github/workflows/master-codecov-report.yml +++ b/.github/workflows/master-codecov-report.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c942800e52f..bf1de731672 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -105,7 +105,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Build and push docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: platforms: linux/amd64 context: . @@ -129,7 +129,7 @@ jobs: fetch-depth: 0 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 @@ -161,7 +161,7 @@ jobs: fetch-depth: 0 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.github/workflows/rerun.yml b/.github/workflows/rerun.yml new file mode 100644 index 00000000000..a630dafafaa --- /dev/null +++ b/.github/workflows/rerun.yml @@ -0,0 +1,20 @@ +# From this genius: https://github.com/orgs/community/discussions/67654#discussioncomment-8038649 +name: Rerun workflow + +on: + workflow_dispatch: + inputs: + run_id: + required: true +jobs: + rerun: + runs-on: ubuntu-latest + steps: + - name: rerun ${{ inputs.run_id }} + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + GH_DEBUG: api + run: | + gh run watch ${{ inputs.run_id }} > /dev/null 2>&1 + gh run rerun ${{ inputs.run_id }} --failed diff --git a/.github/workflows/upload-annotations.yml b/.github/workflows/upload-annotations.yml index 835c8fd07ba..d9691c6f744 100644 --- a/.github/workflows/upload-annotations.yml +++ b/.github/workflows/upload-annotations.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node and pnpm - uses: silverhand-io/actions-node-pnpm-run-steps@v4 + uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: pnpm-version: 9 diff --git a/.scripts/compare-database.js b/.scripts/compare-database.js index 5a44b4f2f16..b959db38533 100644 --- a/.scripts/compare-database.js +++ b/.scripts/compare-database.js @@ -99,21 +99,46 @@ const queryDatabaseManifest = async (database) => { `); // This function removes the last segment of grantee since Logto will use 'logto_tenant_fresh/alteration' for the role name. - const normalizeGrantee = ({ grantee, ...rest }) => { - if (grantee.startsWith('logto_tenant_')) { - return { ...rest, grantee: 'logto_tenant' }; + const normalizeRoleName = (roleName) => { + if (roleName.startsWith('logto_tenant_')) { + return 'logto_tenant'; } - return { grantee, ...rest }; + // Removes the last segment of region grantee since Logto will use 'logto_region_xxx' for the role name for different regions. + if (roleName.startsWith('logto_region_')) { + return 'logto_region'; + } + + return roleName; }; + const normalizeGrantee = ({ grantee, ...rest }) => ({ + ...rest, + grantee: normalizeRoleName(grantee), + }); + // Ditto. const normalizeRoles = ({ roles: raw, ...rest }) => { - const roles = raw.slice(1, -1).split(',').map((name) => name.startsWith('logto_tenant_') ? 'logto_tenant' : name); + const roles = raw + .slice(1, -1) + .split(',') + .map((name) => normalizeRoleName(name)); return { roles, ...rest }; }; + const normalizePolicyname = ({ policyname, ...rest }) => { + const prefix = 'allow_'; + const suffix = '_access'; + if (policyname && policyname.startsWith(prefix) && policyname.endsWith(suffix)) { + // This is a naming convention in Logto cloud, it is formatted as `allow_{role_name}_access`, we need to normalize the role name part for the convenience of comparing DB updates. + // Ref: https://github.com/logto-io/cloud/pull/738 + return { policyname: `${prefix}${normalizeRoleName(policyname.slice(prefix.length, -suffix.length))}${suffix}`, ...rest }; + } + + return { policyname, ...rest }; + }; + // Omit generated ids and values return { tables: omitArray(tables, 'table_catalog'), @@ -144,7 +169,7 @@ const queryDatabaseManifest = async (database) => { indexes, funcs, triggers: omitArray(triggers, 'trigger_catalog', 'event_object_catalog'), - policies: policies.map(normalizeRoles), + policies: policies.map(normalizeRoles).map(normalizePolicyname), columnGrants: omitArray(columnGrants, 'table_catalog').map(normalizeGrantee), tableGrants: omitArray(tableGrants, 'table_catalog').map(normalizeGrantee), }; diff --git a/.vscode/settings.json b/.vscode/settings.json index b83dc8dfa43..805805faacf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -54,6 +54,7 @@ "timestamptz", "topbar", "upsell", - "withtyped" + "withtyped", + "backchannel" ] } diff --git a/README.OGCIO.md b/README.OGCIO.md index 26d53169c73..3b39ab59468 100644 --- a/README.OGCIO.md +++ b/README.OGCIO.md @@ -26,6 +26,46 @@ e.g. `git merge v1.17.0 --strategy-option theirs` - commit the changes with `git commit -a` to end the merge and let git write the correct message - push and open your PR! +## Run with Docker Compose +It is possible to run Logto, its database and our MyGovId mock service in a dockerized solution, with local or remote images. + +### With local images +If you have the repository on your machine and want to use images built locally for both services you can run: +``` +make build run +``` + +### With remote images +If you want to run Logto on your machine without cloning the repo, you need to have access to aws to pull our images as a prerequisite. If you haven't already, install [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). +If not yet configured, run +``` +aws sso configure +``` +And follow the prompts. If you don't know what your SSO start URL is, you can find it on your AWS access portal. Click on your AWS account and then on the `Access keys` option. + +A script is available to login with AWS and Docker, create the custom network and run the containers. This is useful when launching it for the first time, or more in general when the image needs to be pulled. +The script expects an environment variable for the aws profile that you need to be logged in: +``` +AWS_PROFILE=awsProfile-accountId +``` + +To execute the script, run: +``` +[ ! -f docker-compose-ogcio-logto.yml ] && curl -fsSL https://raw.githubusercontent.com/ogcio/logto/HEAD/docker-compose-ogcio-logto.yml > /tmp/docker-compose-ogcio-logto.yml && curl -fsSL https://raw.githubusercontent.com/ogcio/logto/HEAD/run-logto-remote.sh | bash -s /tmp/docker-compose-ogcio-logto.yml +``` +The command downloads the Docker Compose file from Github to a temporary location if it doesn't exist already, then fetches and executes the script from GitHub, passing the temporary Docker Compose file. + +If you are already authenticated and just want to run the docker compose: + +``` +curl -fsSL https://raw.githubusercontent.com/ogcio/logto/HEAD/docker-compose-ogcio-logto.yml | docker compose -f - up -d +``` + +If you already have the repo cloned locally there is a Make command available: +``` +make build run-remote +``` + ## Setup and run Logto natively You can also run Logto natively on your machine outside the docker container. @@ -58,19 +98,25 @@ ADMIN_PORT=3302 PORT=3301 # OGCIO Config -USER_DEFAULT_ORGANIZATION_NAMES=OGCIO Seeded Org -USER_DEFAULT_ORGANIZATION_ROLE_NAMES=OGCIO Employee, OGCIO Manager +MOCK_TOKEN_ENDPOINT=http://localhost:4005/logto/mock/token +MOCK_KEYS_ENDPOINT=http://localhost:4005/logto/mock/keys +``` +2. Run the makefile command ``` +make run-native +``` + +It runs, under the hood, all the following commands: -2. Install all the dependencies. Please also refer to the [original guide](.github/CONTRIBUTING.md) when building the project. +1. Install all the dependencies. Please also refer to the [original guide](.github/CONTRIBUTING.md) when building the project. `pnpm pnpm:devPreinstall && pnpm i && pnpm prepack` -3. After the installation, you can start seeding the database. You have to seed in two steps: +2. After the installation, you can start seeding the database. You have to seed in two steps: - seed Logto's database: `pnpm cli db seed` - seed custom OGCIO data: `npm run cli db ogcio -- --seeder-filepath="./packages/cli/src/commands/database/ogcio/ogcio-seeder-local.json"` - 3.5. Database alteration + 2.5. Database alteration If you are upgrading your dev environment from an older version, or facing the `Found undeployed database alterations...` error when starting Logto, you need to deploy the database alteration first. @@ -78,15 +124,13 @@ USER_DEFAULT_ORGANIZATION_ROLE_NAMES=OGCIO Employee, OGCIO Manager If you are developing something with database alterations, see [packages/schemas/alteration](https://github.com/logto-io/logto/tree/master/packages/schemas/alterations) to learn more. -4. After the seeding of the database was finished, the connectors must be built and linked to the system: +3. After the seeding of the database was finished, the connectors must be built and linked to the system: - `pnpm connectors build` - `pnpm cli connector link` -### Starting Logto - -The local Logto instance can be started by running the following command: +4. The local Logto instance can be started by running the following command: `pnpm dev` diff --git a/README.md b/README.md index 3ddb20811e3..35d06147178 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@

- - + + + + + Logto logo +

+
+ [![discord](https://img.shields.io/discord/965845662535147551?color=5865f2&label=discord)](https://discord.gg/vRvwuwgpVX) [![checks](https://img.shields.io/github/checks-status/logto-io/logto/master)](https://github.com/logto-io/logto/actions?query=branch%3Amaster) [![release](https://img.shields.io/github/v/release/logto-io/logto?color=3a3c3f)](https://github.com/logto-io/logto/releases) diff --git a/azure_pipelines.yml b/azure_pipelines.yml index 4085f714049..10e6680d1ad 100644 --- a/azure_pipelines.yml +++ b/azure_pipelines.yml @@ -84,3 +84,25 @@ stages: awsServiceConnection: ${{ variables.awsServiceConnection }} awsRegion: ${{ variables.awsRegion }} serviceName: logto-admin + - stage: Build_MyGovId_Mock + displayName: Build MyGovId Mock + dependsOn: + - securityScan + condition: or(eq(variables['Build.SourceBranchName'], 'dev'),eq(variables['Build.Reason'], 'PullRequest')) + jobs: + - template: pipeline-templates/build_service.yml + parameters: + serviceName: mygovid-mock-service + pushArtefacts: true + buildArguments: $(buildArguments) + dockerfile: ./mygovid-mock-service/Dockerfile + - stage: Push_MyGovId_Mock + displayName: Push MyGovId Mock to ECR + dependsOn: Build_MyGovId_Mock + condition: eq(variables['Build.SourceBranchName'], 'dev') + jobs: + - template: pipeline-templates/push_image.yml + parameters: + awsServiceConnection: ${{ variables.awsServiceConnection }} + awsRegion: ${{ variables.awsRegion }} + serviceName: mygovid-mock-service diff --git a/docker-compose-local.yml b/docker-compose-local.yml index a9d5657ad8f..9815c70ac7c 100644 --- a/docker-compose-local.yml +++ b/docker-compose-local.yml @@ -23,23 +23,25 @@ services: - ADMIN_ENDPOINT - PORT=3301 - ADMIN_PORT=3302 - postgres: - image: postgres:14-alpine - user: postgres - volumes: - - db:/var/lib/postgresql/data - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: p0stgr3s - PGPORT: 5433 - healthcheck: - test: [ "CMD-SHELL", "pg_isready" ] - interval: 10s - timeout: 5s - retries: 5 + - MOCK_TOKEN_ENDPOINT=http://mygovid-mock-service:4005/logto/mock/token + - MOCK_KEYS_ENDPOINT=http://mygovid-mock-service:4005/logto/mock/keys + + mygovid-mock-service: + image: local-mygovid-mock-service:latest + build: + dockerfile: ./mygovid-mock-service/Dockerfile ports: - - 5433:5433 + - 4005:4005 + + postgres: + extends: + file: docker-compose-db.yml + service: postgres volumes: db: driver: local + +networks: + logto_network: + external: true diff --git a/docker-compose-ogcio-logto.yml b/docker-compose-ogcio-logto.yml new file mode 100644 index 00000000000..9c427d4577d --- /dev/null +++ b/docker-compose-ogcio-logto.yml @@ -0,0 +1,59 @@ +# This file has been added on OGCIO fork +services: + app: + depends_on: + postgres: + condition: service_healthy + mygovid-mock-service: + condition: service_started + image: 730335224023.dkr.ecr.eu-west-1.amazonaws.com/life-events-logto:dev + entrypoint: + [ + "sh", + "-c", + "npm run cli db seed -- --swe && npm run cli db alteration deploy latest && npm run cli db ogcio -- --seeder-filepath=\"/etc/logto/packages/cli/src/commands/database/ogcio/ogcio-seeder-local.json\" && npm start" + ] + ports: + - 3301:3301 + - 3302:3302 + environment: + - TRUST_PROXY_HEADER=1 + - DB_URL=postgres://postgres:p0stgr3s@postgres:5433/logto + # Mandatory for GitPod to map host env to the container, thus GitPod can dynamically configure the public URL of Logto; + # Or, you can leverage it for local testing. + - ENDPOINT + - ADMIN_ENDPOINT + - PORT=3301 + - ADMIN_PORT=3302 + - MOCK_TOKEN_ENDPOINT=http://mygovid-mock-service:4005/logto/mock/token + - MOCK_KEYS_ENDPOINT=http://mygovid-mock-service:4005/logto/mock/keys + + postgres: + image: postgres:14-alpine + user: postgres + volumes: + - db:/var/lib/postgresql/data + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: p0stgr3s + PGPORT: 5433 + healthcheck: + test: [ "CMD-SHELL", "pg_isready" ] + interval: 10s + timeout: 5s + retries: 5 + ports: + - 5433:5433 + + mygovid-mock-service: + image: 730335224023.dkr.ecr.eu-west-1.amazonaws.com/life-events-logto@sha256:d27a147deb66d0869c8384c0f7415d7df7694212b95b3a54b183be4723924e36 + ports: + - 4005:4005 + +volumes: + db: + driver: local + +networks: + logto_network: + external: true diff --git a/makefile b/makefile index a8dcef72b2e..9bdec946ce1 100644 --- a/makefile +++ b/makefile @@ -1,10 +1,33 @@ # This file has been added on OGCIO fork TAG = local-logto:latest +MOCK_SERVICE_TAG = local-mygovid-mock-service:latest +GREEN=\033[0;32m +NC=\033[0m build: docker build -t ${TAG} . + docker build -f ./mygovid-mock-service/Dockerfile -t ${MOCK_SERVICE_TAG} . run: docker-compose -f docker-compose-local.yml up --detach - down: - docker-compose -f docker-compose-local.yml down \ No newline at end of file + docker-compose -f docker-compose-local.yml down +run-native: + @echo "${GREEN}Copying .env file...${NC}" + cp -- ".env.sample" ".env" + @echo "${GREEN}Copied!${NC}" + @echo "${GREEN}Starting db...${NC}" + docker compose -f docker-compose-db.yml up --detach + @echo "${GREEN}Db started!${NC}" + @echo "${GREEN}Installing stuffs...${NC}" + pnpm pnpm:devPreinstall && pnpm i && pnpm prepack + @echo "${GREEN}Stuffs installed!${NC}" + @echo "${GREEN}Seeding db...${NC}" + npm run cli db seed -- --swe && npm run cli db alteration deploy latest && npm run cli db ogcio -- --seeder-filepath="./packages/cli/src/commands/database/ogcio/ogcio-seeder-local.json" + @echo "${GREEN}Db ready!${NC}" + @echo "${GREEN}Preparing connectors...${NC}" + pnpm connectors build && pnpm cli connector link + @echo "${GREEN}Connectors ready!${NC}" + @echo "${GREEN}Starting Logto...${NC}" + pnpm dev +run-remote: + ./run-logto-remote.sh diff --git a/mygovid-mock-service/.dockerignore b/mygovid-mock-service/.dockerignore new file mode 100644 index 00000000000..f06235c460c --- /dev/null +++ b/mygovid-mock-service/.dockerignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/mygovid-mock-service/.gitignore b/mygovid-mock-service/.gitignore index 3e6091d0c02..bc9fa936a35 100644 --- a/mygovid-mock-service/.gitignore +++ b/mygovid-mock-service/.gitignore @@ -1,2 +1,3 @@ node_modules .tap +dist diff --git a/mygovid-mock-service/Dockerfile b/mygovid-mock-service/Dockerfile new file mode 100644 index 00000000000..9aafaa0bc1e --- /dev/null +++ b/mygovid-mock-service/Dockerfile @@ -0,0 +1,28 @@ +FROM node:20-alpine as builder + +WORKDIR /app + +COPY ./mygovid-mock-service/package*.json ./ + +RUN npm i + +COPY ./mygovid-mock-service ./ + +RUN npm run build + +FROM node:20-alpine AS runtime + +WORKDIR /app + +COPY --from=builder /app/node_modules /app/node_modules +COPY --from=builder /app/package*.json /app/ +COPY --from=builder /app /app/ + +ENV NODE_ENV=development +ENV LOG_LEVEL=trace + +RUN npm prune --omit=dev + +EXPOSE 4005 + +CMD [ "node", "dist/", "index.js" ] diff --git a/mygovid-mock-service/package.json b/mygovid-mock-service/package.json index 482060be441..a953da5c34c 100644 --- a/mygovid-mock-service/package.json +++ b/mygovid-mock-service/package.json @@ -8,7 +8,7 @@ "start": "node dist/index.js", "dev": "nodemon | pino-pretty", "lint": "eslint . --ext .ts", - "build": "echo Build script for the MyGovId mock service not needed so far" + "build": "rm -rf dist && tsc -p tsconfig.json && cp -r src/routes/static dist/routes/static" }, "nodemonConfig": { "ext": "ts,json", @@ -34,6 +34,7 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", + "nodemon": "^3.0.0", "pino-pretty": "^11.0.0", "prettier": "^3.2.5", "tap": "^18.8.0", diff --git a/mygovid-mock-service/src/index.ts b/mygovid-mock-service/src/index.ts index 0b26cb15f28..de49adf0722 100644 --- a/mygovid-mock-service/src/index.ts +++ b/mygovid-mock-service/src/index.ts @@ -1,6 +1,6 @@ import { build } from "./app.js"; -const app = await build(); +const app = await build({logger: true}); app.listen({ host: "0.0.0.0", port: 4005 }, (err, address) => { if (err) { diff --git a/mygovid-mock-service/src/routes/logto/index.ts b/mygovid-mock-service/src/routes/logto/index.ts index 9f62eb86377..b1d36f782cf 100644 --- a/mygovid-mock-service/src/routes/logto/index.ts +++ b/mygovid-mock-service/src/routes/logto/index.ts @@ -27,7 +27,6 @@ export default async function login(app: FastifyInstance) { "/auth", { schema: { - tags: ["Mock"], querystring: { response_type: Type.String(), client_id: Type.String(), @@ -43,14 +42,14 @@ export default async function login(app: FastifyInstance) { const { redirect_uri, state } = request.query; const stream = fs.createReadStream( - path.join(__dirname, "..", "static", "mock-login.html") + path.join(__dirname, "..", "static", "mock-login.html"), ); const result = (await streamToString(stream)) .replace("%REDIRECT_URL%", redirect_uri) .replace("%STATE%", state); return reply.type("text/html").send(result); - } + }, ); app.post<{ @@ -61,19 +60,29 @@ export default async function login(app: FastifyInstance) { email: string; redirect_url: string; state: string; + sub: string; + oid: string; }; }>("/login", async (request, reply) => { - const { password, firstName, lastName, email, redirect_url, state } = - request.body; + const { + password, + firstName, + lastName, + email, + redirect_url, + state, + sub, + oid, + } = request.body; if (password !== "123") reply.redirect( - `/logto/mock/auth?redirect_uri=${redirect_url}&state=${state}` + `/logto/mock/auth?redirect_uri=${redirect_url}&state=${state}`, ); const id_token = await createMockSignedJwt( - { firstName, lastName, email }, - request.headers.origin as unknown as string + { firstName, lastName, email, sub, oid }, + request.headers.origin as unknown as string, ); return reply.redirect(`${redirect_url}?code=${id_token}&state=${state}`); @@ -102,7 +111,6 @@ export default async function login(app: FastifyInstance) { "/token", { schema: { - tags: ["Mock"], body: Type.Object({ code: Type.String(), grant_type: Type.String(), @@ -140,7 +148,7 @@ export default async function login(app: FastifyInstance) { "eyJ2ZXIiOiIxLjAiLCJ0aWQiOiI4OTc5MmE2ZC0xZWE0LTQxMjYtOTRkZi1hNzFkMjkyZGViYzciLCJzdWIiOm51bGwsIm5hbWUiOm51bGwsInByZWZlcnJlZF91c2VybmFtZSI6bnVsbCwiaWRwIjpudWxsfQ", scope: "openid", }; - } + }, ); app.get<{ @@ -157,7 +165,6 @@ export default async function login(app: FastifyInstance) { "/keys", { schema: { - tags: ["Mock"], response: { 200: Type.Object({ keys: Type.Array( @@ -167,7 +174,7 @@ export default async function login(app: FastifyInstance) { kty: Type.Optional(Type.String()), n: Type.Optional(Type.String()), e: Type.Optional(Type.String()), - }) + }), ), }), 500: HttpError, @@ -181,6 +188,6 @@ export default async function login(app: FastifyInstance) { return { keys: [{ kid: "signingkey.mygovid.v1", use: "sig", kty, n, e }], }; - } + }, ); } diff --git a/mygovid-mock-service/src/routes/logto/utils/index.ts b/mygovid-mock-service/src/routes/logto/utils/index.ts index 689abff07cf..2c219595812 100644 --- a/mygovid-mock-service/src/routes/logto/utils/index.ts +++ b/mygovid-mock-service/src/routes/logto/utils/index.ts @@ -33,15 +33,17 @@ export const createMockSignedJwt = async ( firstName: string; lastName: string; email: string; + sub: string; + oid: string; }, origin: string, ) => { const body = { ver: "1.0", - sub: getRandomString(), + sub: user.sub, auth_time: Date.now(), email: user.email, - oid: getRandomString(), + oid: user.oid, AlternateIds: "", BirthDate: "13/06/1941", PublicServiceNumber: "0111019P", diff --git a/mygovid-mock-service/src/routes/static/mock-login.html b/mygovid-mock-service/src/routes/static/mock-login.html index f811e401f86..aeef9c9dfd8 100644 --- a/mygovid-mock-service/src/routes/static/mock-login.html +++ b/mygovid-mock-service/src/routes/static/mock-login.html @@ -84,6 +84,8 @@ /> + + import { faker } from "https://esm.sh/@faker-js/faker"; + let isCurrentUserSet = false; const returnUrl = new URLSearchParams(window.location.search).get( "return_url", @@ -209,17 +212,24 @@ users: [ { user_name: "Peter Parker", - govid_email: "peter.parker@mail.ie" + govid_email: "peter.parker@mail.ie", + oid: "4cfa878a4fd9892a64ac", + sub: "932d94fc69be147f6fcb", }, { user_name: "Tony Stark", - govid_email: "tony.stark@mail.ie" + govid_email: "tony.stark@mail.ie", + oid: "71848ec91433bc4222d0", + sub: "7ffe40ff7d558de01c54", + is_public_servant: true, }, ] } const usersSelectElement = document.querySelector("#user_select"); + const getRandomString = (size = 20) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(''); + for (const u of apiUsers.users) { var option = document.createElement("option"); option.text = `${u.user_name} ${ @@ -234,7 +244,7 @@ (user) => user.govid_email === e.target.value, ); - let firstName, lastName, email; + let firstName, lastName, email, oid, sub; if (user) { const nameSplit = user.user_name.split(" "); @@ -242,16 +252,22 @@ lastName = nameSplit.slice(1).join(" "); email = user.govid_email; isCurrentUserSet = true; + oid = user.oid; + sub = user.sub; } else { firstName = faker.person.firstName(); lastName = faker.person.lastName(); email = `${firstName.toLowerCase()}.${lastName.toLowerCase()}@mail.ie`; isCurrentUserSet = false; + oid = getRandomString(); + sub = getRandomString(); } document.querySelector("#firstName").value = firstName; document.querySelector("#lastName").value = lastName; document.querySelector("#email").value = email; + document.querySelector("#oid").value = oid; + document.querySelector("#sub").value = sub; document.querySelector("#submit_btn").innerHTML = `
Login ${firstName} ${lastName}
`; }); @@ -263,6 +279,8 @@ document.querySelector("#firstName").value = firstName; document.querySelector("#lastName").value = lastName; document.querySelector("#email").value = email; + document.querySelector("#oid").value = getRandomString(); + document.querySelector("#sub").value = getRandomString(); document.querySelector("#submit_btn").innerHTML = `
Login ${firstName} ${lastName}
`; diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index dd8c0dbbe53..c0cf24da4bb 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,27 @@ # Change Log +## 1.18.0 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] +- Updated dependencies [942780fcf] +- Updated dependencies [87615d58c] +- Updated dependencies [9f33d997b] +- Updated dependencies [061a30a87] +- Updated dependencies [ef21c7a99] +- Updated dependencies [136320584] +- Updated dependencies [b52609a1e] +- Updated dependencies [efa884c40] +- Updated dependencies [b50ba0b7e] +- Updated dependencies [d81e13d21] + - @logto/connector-kit@4.0.0 + - @logto/phrases@1.12.0 + - @logto/schemas@1.18.0 + - @logto/phrases-experience@1.7.0 + ## 1.17.0 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 691b1a197f4..21793fdb2e4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@logto/cli", - "version": "1.17.0", + "version": "1.18.0", "description": "Logto CLI.", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", @@ -42,12 +42,12 @@ "url": "https://github.com/logto-io/logto/issues" }, "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@logto/core-kit": "workspace:^2.5.0", "@logto/language-kit": "workspace:^1.1.0", - "@logto/phrases": "workspace:^1.11.0", - "@logto/phrases-experience": "workspace:^1.6.1", - "@logto/schemas": "workspace:1.17.0", + "@logto/phrases": "workspace:^1.12.0", + "@logto/phrases-experience": "workspace:^1.7.0", + "@logto/schemas": "workspace:1.18.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", "@silverhand/slonik": "31.0.0-beta.2", diff --git a/packages/cli/src/commands/database/ogcio/ogcio-seeder-dev.json b/packages/cli/src/commands/database/ogcio/ogcio-seeder-dev.json deleted file mode 100644 index a56a5d3c7f3..00000000000 --- a/packages/cli/src/commands/database/ogcio/ogcio-seeder-dev.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "default": { - "organizations": [ - { - "name": "OGCIO", - "description": "OGCIO Organization", - "id": "ogcio" - } - ], - "applications": [ - { - "name": "Payments Building Block", - "description": "Payments App of Life Events", - "type": "Traditional", - "redirect_uri": "", - "logout_redirect_uri": "", - "secret": "", - "id": "r5f56tpkytpqyyshiutd2" - }, - { - "name": "Messaging Building Block", - "description": "Messaging App of Life Events", - "type": "Traditional", - "redirect_uri": "", - "logout_redirect_uri": "", - "secret": "", - "id": "1lvmteh2ao3xrswyq7j3e" - } - ], - "resources": [ - { - "id": "payments-api", - "name": "Payments Building Block API", - "indicator": "" - }, - { - "id": "messaging-api", - "name": "Messaging Building Block API", - "indicator": "" - } - ], - "connectors": [ - { - "id": "mygovid", - "sync_profile": false, - "connector_id": "mygovid", - "config": { - "scope": "openid profile email", - "clientId": "", - "clientSecret": "", - "tokenEndpoint": "", - "authorizationEndpoint": "", - "tokenEndpointAuthMethod": "client_secret_post", - "idTokenVerificationConfig": { - "jwksUri": "" - }, - "clientSecretJwtSigningAlgorithm": "HS256" - }, - "metadata": { - "logo": "https://mygovidstatic.blob.core.windows.net/assets/images/favicon_196x196.png", - "name": { - "en": "MyGovId" - }, - "target": "MyGovId (MyGovId connector)" - } - } - ], - "sign_in_experiences": [ - { - "id": "default", - "color": { - "primaryColor": "#007DA6", - "darkPrimaryColor": "#007DA6", - "isDarkModeEnabled": false - }, - "branding": { - "logoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png", - "darkLogoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png" - }, - "language_info": { - "autoDetect": true, - "fallbackLanguage": "en" - }, - "sign_in": { - "methods": [] - }, - "sign_up": { - "verify": false, - "password": false, - "identifiers": [] - }, - "social_sign_in_connector_targets": [ - "MyGovId (MyGovId connector)" - ], - "sign_in_mode": "SignInAndRegister" - } - ], - "webhooks": [ - { - "id": "login_webhook", - "name": "User log in", - "events": [ - "PostRegister", - "PostSignIn" - ], - "config": { - "url": "" - }, - "signing_key": "", - "enabled": true - } - ] - } -} diff --git a/packages/cli/src/commands/database/ogcio/ogcio-seeder-local.json b/packages/cli/src/commands/database/ogcio/ogcio-seeder-local.json index 3b52e50b2af..b1f30bfee72 100644 --- a/packages/cli/src/commands/database/ogcio/ogcio-seeder-local.json +++ b/packages/cli/src/commands/database/ogcio/ogcio-seeder-local.json @@ -1,193 +1,216 @@ { - "default": { - "organizations": [ - { - "name": "OGCIO Seeded Org", - "description": "Organization created through seeder", - "id": "ogcio" - } - ], - "organization_permissions": { + "default": { + "organizations": [ + { + "name": "OGCIO Seeded Org", + "description": "Organization created through seeder", + "id": "ogcio" + } + ], + "organization_permissions": { + "specific_permissions": [ + "payments:provider:*", + "payments:payment_request:*", + "payments:payment_request.public:read", + "payments:transaction:*", + "messaging:message:*", + "messaging:provider:*", + "messaging:template:*", + "messaging:citizen:*", + "messaging:event:read", + "life-events:digital-wallet-flow:*" + ] + }, + "organization_roles": [ + { + "id": "pay-public-servant", + "name": "Payments Public Servant", + "description": "Payments Public servant", + "specific_permissions": [ + "payments:provider:*", + "payments:payment_request:*", + "payments:payment_request.public:read", + "payments:transaction:*" + ] + }, + { + "id": "msg-public-servant", + "name": "Messaging Public Servant", + "description": "Messaging Public servant", + "specific_permissions": [ + "messaging:message:*", + "messaging:provider:*", + "messaging:template:*", + "messaging:citizen:*", + "messaging:event:read" + ] + }, + { + "id": "le-public-servant", + "name": "Life Events Public Servant", + "description": "Life Events Public servant", + "specific_permissions": ["life-events:digital-wallet-flow:*"] + } + ], + "applications": [ + { + "name": "Payments Building Block", + "description": "Payments App of Life Events", + "type": "Traditional", + "redirect_uri": "http://localhost:3001/callback", + "logout_redirect_uri": "http://localhost:3001", + "secret": "payments_app_local_secret", + "id": "2xz6sbi8ch01uhjt1oq8r", + "is_third_party": false + }, + { + "name": "Messaging Building Block", + "description": "Messaging App of Life Events", + "type": "Traditional", + "redirect_uri": "http://localhost:3002/callback", + "logout_redirect_uri": "http://localhost:3002", + "secret": "messaging_app_local_secret", + "id": "4695d8onfb9f3bv18phtq", + "is_third_party": false + }, + { + "name": "Life Events", + "description": "Life Events App", + "type": "Traditional", + "redirect_uri": "http://localhost:3000/callback", + "logout_redirect_uri": "http://localhost:3000", + "secret": "life_events_app_local_secret", + "id": "nfg61tuyfsgizsx8c4p3t", + "is_third_party": false + } + ], + "resources": [ + { + "id": "payments-api", + "name": "Payments Building Block API", + "indicator": "http://localhost:8001/" + }, + { + "id": "messaging-api", + "name": "Messaging Building Block API", + "indicator": "http://localhost:8002/" + } + ], + "resource_permissions": [ + { + "resource_id": "payments-api", + "specific_permissions": [ + "payments:transaction.self:read", + "payments:payment_request.public:read", + "payments:transaction.self:write", + "payments:provider.public:read" + ] + }, + { + "resource_id": "messaging-api", + "specific_permissions": [ + "messaging:message.self:read", + "messaging:citizen.self:read", + "messaging:citizen.self:write" + ] + } + ], + "resource_roles": [ + { + "id": "bb-citizen", + "name": "Citizen", + "description": "A citizen using Life Events and the Building Blocks ecosystem", + "permissions": [ + { + "resource_id": "payments-api", "specific_permissions": [ - "payments:provider:*", - "payments:payment_request:*", - "payments:payment_request.public:read", - "payments:transaction:*", - "messaging:message:*", - "messaging:provider:*", - "messaging:template:*", - "messaging:citizen:*" + "payments:transaction.self:read", + "payments:payment_request.public:read", + "payments:transaction.self:write", + "payments:provider.public:read" ] + }, + { + "resource_id": "messaging-api", + "specific_permissions": [ + "messaging:message.self:read", + "messaging:citizen.self:read", + "messaging:citizen.self:write" + ] + } + ] + } + ], + "connectors": [ + { + "id": "mygovid", + "sync_profile": false, + "connector_id": "mygovid", + "config": { + "scope": "openid profile email", + "clientId": "mock_client_id", + "clientSecret": "mock_client_secret", + "tokenEndpoint": "", + "authorizationEndpoint": "http://localhost:4005/logto/mock/auth", + "tokenEndpointAuthMethod": "client_secret_post", + "idTokenVerificationConfig": { + "jwksUri": "" + }, + "clientSecretJwtSigningAlgorithm": "HS256" }, - "organization_roles": [ - { - "id": "bb-public-servant", - "name": "Public Servant", - "description": "Building Blocks Public servant", - "specific_permissions": [ - "payments:provider:*", - "payments:payment_request:*", - "payments:payment_request.public:read", - "payments:transaction:*" - ] - }, - { - "id": "msg-public-servant", - "name": "Messaging Public Servant", - "description": "Messaging Public servant", - "specific_permissions": [ - "messaging:message:*", - "messaging:provider:*", - "messaging:template:*", - "messaging:citizen:*" - ] - } - ], - "applications": [ - { - "name": "Payments Building Block", - "description": "Payments App of Life Events", - "type": "Traditional", - "redirect_uri": "http://localhost:3001/callback", - "logout_redirect_uri": "http://localhost:3001", - "secret": "payments_app_local_secret", - "id": "2xz6sbi8ch01uhjt1oq8r", - "is_third_party": false - }, - { - "name": "Messaging Building Block", - "description": "Messaging App of Life Events", - "type": "Traditional", - "redirect_uri": "http://localhost:3002/callback", - "logout_redirect_uri": "http://localhost:3002", - "secret": "messaging_app_local_secret", - "id": "4695d8onfb9f3bv18phtq", - "is_third_party": false - } - ], - "resources": [ - { - "id": "payments-api", - "name": "Payments Building Block API", - "indicator": "http://localhost:8001/" - }, - { - "id": "messaging-api", - "name": "Messaging Building Block API", - "indicator": "http://localhost:8002/" - } - ], - "resource_permissions": [ - { - "resource_id": "payments-api", - "specific_permissions": [ - "payments:transaction.self:read", - "payments:payment_request.public:read", - "payments:transaction.self:write", - "payments:provider.public:read" - ] - }, - { - "resource_id": "messaging-api", - "specific_permissions": [ - "messaging:message.self:read" - ] - } - ], - "resource_roles": [ - { - "id": "bb-citizen", - "name": "Citizen", - "description": "A citizen using Life Events and the Building Blocks ecosystem", - "permissions": [ - { - "resource_id": "payments-api", - "specific_permissions": [ - "payments:transaction.self:read", - "payments:payment_request.public:read", - "payments:transaction.self:write", - "payments:provider.public:read" - ] - }, - { - "resource_id": "messaging-api", - "specific_permissions": [ - "messaging:message.self:read" - ] - } - ] - } - ], - "connectors": [ - { - "id": "mygovid", - "sync_profile": false, - "connector_id": "mygovid", - "config": { - "scope": "openid profile email", - "clientId": "mock_client_id", - "clientSecret": "mock_client_secret", - "tokenEndpoint": "http://localhost:4005/logto/mock/token", - "authorizationEndpoint": "http://localhost:4005/logto/mock/auth", - "tokenEndpointAuthMethod": "client_secret_post", - "idTokenVerificationConfig": { - "jwksUri": "http://localhost:4005/logto/mock/keys" - }, - "clientSecretJwtSigningAlgorithm": "HS256" - }, - "metadata": { - "logo": "https://mygovidstatic.blob.core.windows.net/assets/images/favicon_196x196.png", - "name": { - "en": "MyGovId" - }, - "target": "MyGovId (MyGovId connector)" - } - } - ], - "sign_in_experiences": [ - { - "id": "default", - "color": { - "primaryColor": "#007DA6", - "darkPrimaryColor": "#007DA6", - "isDarkModeEnabled": false - }, - "branding": { - "logoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png", - "darkLogoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png" - }, - "language_info": { - "autoDetect": true, - "fallbackLanguage": "en" - }, - "sign_in": { - "methods": [] - }, - "sign_up": { - "verify": false, - "password": false, - "identifiers": [] - }, - "social_sign_in_connector_targets": [ - "MyGovId (MyGovId connector)" - ], - "sign_in_mode": "SignInAndRegister" - } + "metadata": { + "logo": "https://mygovidstatic.blob.core.windows.net/assets/images/favicon_196x196.png", + "name": { + "en": "MyGovId" + }, + "target": "MyGovId (MyGovId connector)" + } + } + ], + "sign_in_experiences": [ + { + "id": "default", + "color": { + "primaryColor": "#007DA6", + "darkPrimaryColor": "#007DA6", + "isDarkModeEnabled": false + }, + "branding": { + "logoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png", + "darkLogoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png" + }, + "language_info": { + "autoDetect": true, + "fallbackLanguage": "en" + }, + "sign_in": { + "methods": [] + }, + "sign_up": { + "verify": false, + "password": false, + "identifiers": [] + }, + "social_sign_in_connector_targets": ["MyGovId (MyGovId connector)"], + "sign_in_mode": "SignInAndRegister" + } + ], + "webhooks": [ + { + "id": "login-webhook", + "name": "User log in", + "events": [ + "User.Created", + "User.Deleted", + "User.Data.Updated", + "User.SuspensionStatus.Updated" ], - "webhooks": [ - { - "id": "login-webhook", - "name": "User log in", - "events": [ - "PostRegister", - "PostSignIn" - ], - "config": { - "url": "http://localhost:8003/user-login-wh" - }, - "signing_key": "webhooks_local_signing_key", - "enabled": true - } - ] - } + "config": { + "url": "http://localhost:8003/user-login-wh" + }, + "signing_key": "webhooks_local_signing_key", + "enabled": true + } + ] + } } diff --git a/packages/cli/src/commands/database/ogcio/ogcio-seeder.json b/packages/cli/src/commands/database/ogcio/ogcio-seeder.json index 924d446b53d..bc2316ccce6 100644 --- a/packages/cli/src/commands/database/ogcio/ogcio-seeder.json +++ b/packages/cli/src/commands/database/ogcio/ogcio-seeder.json @@ -1,193 +1,216 @@ { - "default": { - "organizations": [ - { - "name": "OGCIO", - "description": "OGCIO Organization", - "id": "ogcio" - } - ], - "organization_permissions": { + "default": { + "organizations": [ + { + "name": "OGCIO", + "description": "OGCIO Organization", + "id": "ogcio" + } + ], + "organization_permissions": { + "specific_permissions": [ + "payments:provider:*", + "payments:payment_request:*", + "payments:payment_request.public:read", + "payments:transaction:*", + "messaging:message:*", + "messaging:provider:*", + "messaging:template:*", + "messaging:citizen:*", + "messaging:event:read", + "life-events:digital-wallet-flow:*" + ] + }, + "organization_roles": [ + { + "id": "pay-public-servant", + "name": "Payments Public Servant", + "description": "Payments Public servant", + "specific_permissions": [ + "payments:provider:*", + "payments:payment_request:*", + "payments:payment_request.public:read", + "payments:transaction:*" + ] + }, + { + "id": "msg-public-servant", + "name": "Messaging Public Servant", + "description": "Messaging Public servant", + "specific_permissions": [ + "messaging:message:*", + "messaging:provider:*", + "messaging:template:*", + "messaging:citizen:*", + "messaging:event:read" + ] + }, + { + "id": "le-public-servant", + "name": "Life Events Public Servant", + "description": "Life Events Public servant", + "specific_permissions": ["life-events:digital-wallet-flow:*"] + } + ], + "applications": [ + { + "name": "Payments Building Block", + "description": "Payments App of Life Events", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "r5f56tpkytpqyyshiutd2", + "is_third_party": false + }, + { + "name": "Messaging Building Block", + "description": "Messaging App of Life Events", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "1lvmteh2ao3xrswyq7j3e", + "is_third_party": false + }, + { + "name": "Life Events", + "description": "Life Events App", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "i61nya0wctzpqeyeno54z", + "is_third_party": false + } + ], + "resources": [ + { + "id": "payments-api", + "name": "Payments Building Block API", + "indicator": "" + }, + { + "id": "messaging-api", + "name": "Messaging Building Block API", + "indicator": "" + } + ], + "resource_permissions": [ + { + "resource_id": "payments-api", + "specific_permissions": [ + "payments:transaction.self:read", + "payments:payment_request.public:read", + "payments:transaction.self:write", + "payments:provider.public:read" + ] + }, + { + "resource_id": "messaging-api", + "specific_permissions": [ + "messaging:message.self:read", + "messaging:citizen.self:read", + "messaging:citizen.self:write" + ] + } + ], + "resource_roles": [ + { + "id": "bb-citizen", + "name": "Citizen", + "description": "A citizen using Life Events and the Building Blocks ecosystem", + "permissions": [ + { + "resource_id": "payments-api", "specific_permissions": [ - "payments:provider:*", - "payments:payment_request:*", - "payments:payment_request.public:read", - "payments:transaction:*", - "messaging:message:*", - "messaging:provider:*", - "messaging:template:*", - "messaging:citizen:*" + "payments:transaction.self:read", + "payments:payment_request.public:read", + "payments:transaction.self:write", + "payments:provider.public:read" ] + }, + { + "resource_id": "messaging-api", + "specific_permissions": [ + "messaging:message.self:read", + "messaging:citizen.self:read", + "messaging:citizen.self:write" + ] + } + ] + } + ], + "connectors": [ + { + "id": "mygovid", + "sync_profile": false, + "connector_id": "mygovid", + "config": { + "scope": "openid profile email", + "clientId": "", + "clientSecret": "", + "tokenEndpoint": "", + "authorizationEndpoint": "", + "tokenEndpointAuthMethod": "client_secret_post", + "idTokenVerificationConfig": { + "jwksUri": "" + }, + "clientSecretJwtSigningAlgorithm": "HS256" }, - "organization_roles": [ - { - "id": "bb-public-servant", - "name": "Public Servant", - "description": "Building Blocks Public servant", - "specific_permissions": [ - "payments:provider:*", - "payments:payment_request:*", - "payments:payment_request.public:read", - "payments:transaction:*" - ] - }, - { - "id": "msg-public-servant", - "name": "Messaging Public Servant", - "description": "Messaging Public servant", - "specific_permissions": [ - "messaging:message:*", - "messaging:provider:*", - "messaging:template:*", - "messaging:citizen:*" - ] - } - ], - "applications": [ - { - "name": "Payments Building Block", - "description": "Payments App of Life Events", - "type": "Traditional", - "redirect_uri": "", - "logout_redirect_uri": "", - "secret": "", - "id": "r5f56tpkytpqyyshiutd2", - "is_third_party": false - }, - { - "name": "Messaging Building Block", - "description": "Messaging App of Life Events", - "type": "Traditional", - "redirect_uri": "", - "logout_redirect_uri": "", - "secret": "", - "id": "1lvmteh2ao3xrswyq7j3e", - "is_third_party": false - } - ], - "resources": [ - { - "id": "payments-api", - "name": "Payments Building Block API", - "indicator": "" - }, - { - "id": "messaging-api", - "name": "Messaging Building Block API", - "indicator": "" - } - ], - "resource_permissions": [ - { - "resource_id": "payments-api", - "specific_permissions": [ - "payments:transaction.self:read", - "payments:payment_request.public:read", - "payments:transaction.self:write", - "payments:provider.public:read" - ] - }, - { - "resource_id": "messaging-api", - "specific_permissions": [ - "messaging:message.self:read" - ] - } - ], - "resource_roles": [ - { - "id": "bb-citizen", - "name": "Citizen", - "description": "A citizen using Life Events and the Building Blocks ecosystem", - "permissions": [ - { - "resource_id": "payments-api", - "specific_permissions": [ - "payments:transaction.self:read", - "payments:payment_request.public:read", - "payments:transaction.self:write", - "payments:provider.public:read" - ] - }, - { - "resource_id": "messaging-api", - "specific_permissions": [ - "messaging:message.self:read" - ] - } - ] - } - ], - "connectors": [ - { - "id": "mygovid", - "sync_profile": false, - "connector_id": "mygovid", - "config": { - "scope": "openid profile email", - "clientId": "", - "clientSecret": "", - "tokenEndpoint": "", - "authorizationEndpoint": "", - "tokenEndpointAuthMethod": "client_secret_post", - "idTokenVerificationConfig": { - "jwksUri": "" - }, - "clientSecretJwtSigningAlgorithm": "HS256" - }, - "metadata": { - "logo": "https://mygovidstatic.blob.core.windows.net/assets/images/favicon_196x196.png", - "name": { - "en": "MyGovId" - }, - "target": "MyGovId (MyGovId connector)" - } - } - ], - "sign_in_experiences": [ - { - "id": "default", - "color": { - "primaryColor": "#007DA6", - "darkPrimaryColor": "#007DA6", - "isDarkModeEnabled": false - }, - "branding": { - "logoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png", - "darkLogoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png" - }, - "language_info": { - "autoDetect": true, - "fallbackLanguage": "en" - }, - "sign_in": { - "methods": [] - }, - "sign_up": { - "verify": false, - "password": false, - "identifiers": [] - }, - "social_sign_in_connector_targets": [ - "MyGovId (MyGovId connector)" - ], - "sign_in_mode": "SignInAndRegister" - } + "metadata": { + "logo": "https://mygovidstatic.blob.core.windows.net/assets/images/favicon_196x196.png", + "name": { + "en": "MyGovId" + }, + "target": "MyGovId (MyGovId connector)" + } + } + ], + "sign_in_experiences": [ + { + "id": "default", + "color": { + "primaryColor": "#007DA6", + "darkPrimaryColor": "#007DA6", + "isDarkModeEnabled": false + }, + "branding": { + "logoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png", + "darkLogoUrl": "https://mygovidstatic.blob.core.windows.net/assets/images/helpchat-logo.png" + }, + "language_info": { + "autoDetect": true, + "fallbackLanguage": "en" + }, + "sign_in": { + "methods": [] + }, + "sign_up": { + "verify": false, + "password": false, + "identifiers": [] + }, + "social_sign_in_connector_targets": ["MyGovId (MyGovId connector)"], + "sign_in_mode": "SignInAndRegister" + } + ], + "webhooks": [ + { + "id": "login-webhook", + "name": "User log in", + "events": [ + "User.Created", + "User.Deleted", + "User.Data.Updated", + "User.SuspensionStatus.Updated" ], - "webhooks": [ - { - "id": "login-webhook", - "name": "User log in", - "events": [ - "PostRegister", - "PostSignIn" - ], - "config": { - "url": "" - }, - "signing_key": "", - "enabled": true - } - ] - } + "config": { + "url": "" + }, + "signing_key": "", + "enabled": true + } + ] + } } diff --git a/packages/connectors/connector-alipay-native/CHANGELOG.md b/packages/connectors/connector-alipay-native/CHANGELOG.md index 6e84bd0e814..f1876d9f116 100644 --- a/packages/connectors/connector-alipay-native/CHANGELOG.md +++ b/packages/connectors/connector-alipay-native/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-alipay-native +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-alipay-native/README.md b/packages/connectors/connector-alipay-native/README.md index d1defbf05f7..ef6ac9a20b5 100644 --- a/packages/connectors/connector-alipay-native/README.md +++ b/packages/connectors/connector-alipay-native/README.md @@ -68,7 +68,7 @@ Alipay Native connector works closely with Logto SDK on mobile platforms. It tak 3. Fill out the Logto connector settings: - Fill out the `appId` field with APPID you've got from step 1. - Fill out the `privateKey` field with contents from the private key file mentioned in step 2. Please MAKE SURE to use '\n' to replace all newline characters. You don't need to remove header and footer in private key file. - - Fill out the `signType` filed with 'RSA2' due to the `Public key` signing mode we chose in step 7 of "Create And Configure Alipay Apps". + - Fill out the `signType` field with 'RSA2' due to the `Public key` signing mode we chose in step 7 of "Create And Configure Alipay Apps". ### Config types @@ -138,7 +138,7 @@ dependencies { ### Test Alipay native connector -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once Alipay native connector is enabled, you can build and run your app to see if it works. @@ -258,7 +258,7 @@ dependencies { ## 测试支付宝原生连接器 -大功告成。别忘了 [在登录体验中启用社交登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用社交登录](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在支付宝原生连接器启用后,你可以构建并运行你的应用看看是否生效。 diff --git a/packages/connectors/connector-alipay-native/package.json b/packages/connectors/connector-alipay-native/package.json index 6cbf3aa528c..7099bda0ce0 100644 --- a/packages/connectors/connector-alipay-native/package.json +++ b/packages/connectors/connector-alipay-native/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-alipay-native", - "version": "1.2.0", + "version": "1.2.1", "description": "Alipay Native implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "dayjs": "^1.10.5", "got": "^14.0.0", @@ -13,7 +13,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-alipay-web/CHANGELOG.md b/packages/connectors/connector-alipay-web/CHANGELOG.md index d2682545a91..5ffe877b82d 100644 --- a/packages/connectors/connector-alipay-web/CHANGELOG.md +++ b/packages/connectors/connector-alipay-web/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-alipay-web +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-alipay-web/README.md b/packages/connectors/connector-alipay-web/README.md index c4df55bb3b1..a5cbbc07a55 100644 --- a/packages/connectors/connector-alipay-web/README.md +++ b/packages/connectors/connector-alipay-web/README.md @@ -78,7 +78,7 @@ Alipay Web connector is designed for desktop Web applications. It takes advantag ## Test Alipay web connector -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once Alipay web connector is enabled, you can build and run your web app to see if it works. @@ -144,7 +144,7 @@ Once Alipay web connector is enabled, you can build and run your web app to see ## 测试支付宝网页连接器 -大功告成。别忘了 [在登录体验中启用社交登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用社交登录](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在支付宝网页连接器启用后,你可以构建并运行你的网页应用看看是否生效。 diff --git a/packages/connectors/connector-alipay-web/package.json b/packages/connectors/connector-alipay-web/package.json index 6a787a15fc3..10b5095b6b4 100644 --- a/packages/connectors/connector-alipay-web/package.json +++ b/packages/connectors/connector-alipay-web/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-alipay-web", - "version": "1.3.0", + "version": "1.3.1", "description": "Alipay implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "dayjs": "^1.10.5", "got": "^14.0.0", @@ -12,7 +12,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-aliyun-dm/CHANGELOG.md b/packages/connectors/connector-aliyun-dm/CHANGELOG.md index 425189c294e..194f3b8132c 100644 --- a/packages/connectors/connector-aliyun-dm/CHANGELOG.md +++ b/packages/connectors/connector-aliyun-dm/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-aliyun-dm +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-aliyun-dm/README.md b/packages/connectors/connector-aliyun-dm/README.md index 0589e1094e8..5a465d9cf11 100644 --- a/packages/connectors/connector-aliyun-dm/README.md +++ b/packages/connectors/connector-aliyun-dm/README.md @@ -65,7 +65,7 @@ After finishing setup, there are two different ways to test: You can type in an email address and click on "Send" to see whether the settings can work before "Save and Done". -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/). ### Config types @@ -129,7 +129,7 @@ That's it. Don't forget to [Enable connector in sign-in experience](https://docs 你可以在「保存并完成」之前输入一个邮件地址并点按「发送」来测试配置是否可以正常工作。 -大功告成!快去 [启用短信或邮件验证码登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in) 吧。 +大功告成!快去 [启用邮件验证码登录](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/) 吧。 ### 配置类型 diff --git a/packages/connectors/connector-aliyun-dm/package.json b/packages/connectors/connector-aliyun-dm/package.json index 0aac03039c5..59e41ddd8e8 100644 --- a/packages/connectors/connector-aliyun-dm/package.json +++ b/packages/connectors/connector-aliyun-dm/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-aliyun-dm", - "version": "1.1.1", + "version": "1.1.2", "description": "Aliyun DM connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -51,7 +51,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-aliyun-sms/CHANGELOG.md b/packages/connectors/connector-aliyun-sms/CHANGELOG.md index 952eb8f9155..dc502f55bae 100644 --- a/packages/connectors/connector-aliyun-sms/CHANGELOG.md +++ b/packages/connectors/connector-aliyun-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-aliyun-sms +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-aliyun-sms/README.md b/packages/connectors/connector-aliyun-sms/README.md index 3a624e2a15b..c1e1b84517f 100644 --- a/packages/connectors/connector-aliyun-sms/README.md +++ b/packages/connectors/connector-aliyun-sms/README.md @@ -67,7 +67,7 @@ Go to the [Aliyun website](https://cn.aliyun.com/) and register your Aliyun acco You can type in a phone number and click on "Send" to see whether the settings can work before "Save and Done". -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/). ### Config types @@ -133,7 +133,7 @@ That's it. Don't forget to [Enable connector in sign-in experience](https://docs 你可以在「保存并完成」之前输入一个手机号码并点按「发送」来测试配置是否可以正常工作。 -大功告成!快去 [启用短信或邮件验证码登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in) 吧。 +大功告成!快去 [启用短信验证码登录](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/) 吧。 ### 配置类型 diff --git a/packages/connectors/connector-aliyun-sms/package.json b/packages/connectors/connector-aliyun-sms/package.json index bf35fd641c7..9739f43eb75 100644 --- a/packages/connectors/connector-aliyun-sms/package.json +++ b/packages/connectors/connector-aliyun-sms/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-aliyun-sms", - "version": "1.1.1", + "version": "1.1.2", "description": "Aliyun SMS connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -51,7 +51,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-apple/CHANGELOG.md b/packages/connectors/connector-apple/CHANGELOG.md index 56904be0ab1..1e5544aa92e 100644 --- a/packages/connectors/connector-apple/CHANGELOG.md +++ b/packages/connectors/connector-apple/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-apple +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-apple/README.md b/packages/connectors/connector-apple/README.md index ba1e5324547..e942743d613 100644 --- a/packages/connectors/connector-apple/README.md +++ b/packages/connectors/connector-apple/README.md @@ -82,4 +82,4 @@ See developer discussion [here](https://forums.developer.apple.com/forums/thread ## Test Apple connector -That's it. The Apple connector should be available in both web and native apps. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in). +That's it. The Apple connector should be available in both web and native apps. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). diff --git a/packages/connectors/connector-apple/package.json b/packages/connectors/connector-apple/package.json index 8aa1e61eb37..2c11467106b 100644 --- a/packages/connectors/connector-apple/package.json +++ b/packages/connectors/connector-apple/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-apple", - "version": "1.3.0", + "version": "1.3.1", "description": "Apple web connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@logto/shared": "workspace:^3.1.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", @@ -53,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-aws-ses/CHANGELOG.md b/packages/connectors/connector-aws-ses/CHANGELOG.md index eb0337e4ede..c1be1d0c438 100644 --- a/packages/connectors/connector-aws-ses/CHANGELOG.md +++ b/packages/connectors/connector-aws-ses/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-aws-ses +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-aws-ses/README.md b/packages/connectors/connector-aws-ses/README.md index 2e4c5dcb0b9..a032fca6b85 100644 --- a/packages/connectors/connector-aws-ses/README.md +++ b/packages/connectors/connector-aws-ses/README.md @@ -52,7 +52,7 @@ the following parameters are optional; parameters description can be found in th You can type in an email address and click on "Send" to see whether the settings work before "Save and Done". -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/). ### Configure types diff --git a/packages/connectors/connector-aws-ses/package.json b/packages/connectors/connector-aws-ses/package.json index a9563d24b21..99f6582e302 100644 --- a/packages/connectors/connector-aws-ses/package.json +++ b/packages/connectors/connector-aws-ses/package.json @@ -1,12 +1,12 @@ { "name": "@logto/connector-aws-ses", - "version": "1.1.1", + "version": "1.1.2", "description": "Logto Connector for Amazon SES", "author": "Jeff ", "dependencies": { "@aws-sdk/client-sesv2": "^3.556.0", "@aws-sdk/types": "^3.535.0", - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -54,7 +54,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-azuread/CHANGELOG.md b/packages/connectors/connector-azuread/CHANGELOG.md index f24cfea3574..6fd68879451 100644 --- a/packages/connectors/connector-azuread/CHANGELOG.md +++ b/packages/connectors/connector-azuread/CHANGELOG.md @@ -1,5 +1,18 @@ # @logto/connector-azuread +## 1.3.0 + +### Minor Changes + +- 15953609b: support config of `prompt` + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-azuread/README.md b/packages/connectors/connector-azuread/README.md index ac09b62863a..6bb274fa36b 100644 --- a/packages/connectors/connector-azuread/README.md +++ b/packages/connectors/connector-azuread/README.md @@ -6,9 +6,12 @@ The Microsoft Azure AD connector provides a succinct way for your application to - [Microsoft Azure AD connector](#microsoft-azure-ad-connector) - [Set up Microsoft Azure AD in the Azure Portal](#set-up-microsoft-azure-ad-in-the-azure-portal) - - [Fill in the configuration](#fill-in-the-configuration) - - [Configure your client secret](#configure-your-client-secret) - - [Config types](#config-types) + - [Fill in the configuration in Logto](#fill-in-the-configuration-in-logto) + - [Client ID](#client-id) + - [Client Secret](#client-secret) + - [Cloud Instance](#cloud-instance) + - [Tenant ID](#tenant-id) + - [Prompts](#prompts) - [References](#references) ## Set up Microsoft Azure AD in the Azure Portal @@ -21,12 +24,47 @@ The Microsoft Azure AD connector provides a succinct way for your application to ## Fill in the configuration in Logto -| Name | Type | -| ------------- | ------ | -| clientId | string | -| clientSecret | string | -| tenantId | string | -| cloudInstance | string | +| Name | Type | +| ------------- | -------- | +| clientId | string | +| clientSecret | string | +| tenantId | string | +| cloudInstance | string | +| prompts | string[] | + +### Client ID + +You may find the **Application (client) ID** in the **Overview** section of your newly created application in the Azure Portal. + +### Client Secret + +- In your newly created application, click the **Certificates & Secrets** to get a client secret, and click the **New client secret** from the top. +- Enter a description and an expiration. +- This will only show your client secret once. Fill the **value** to the Logto connector configuration and save it to a secure location. + +### Cloud Instance + +Usually, it is `https://login.microsoftonline.com/`. See [Azure AD authentication endpoints](https://learn.microsoft.com/en-us/azure/active-directory/develop/authentication-national-cloud#azure-ad-authentication-endpoints) for more information. + +### Tenant ID + +Logto will use this field to construct the authorization endpoints. This value is dependent on the **access type** you selected when creating the application in the Azure Portal. + +- If you select **Accounts in this organizational directory only** for access type then you need to enter your **{TenantID}**. You can find the tenant ID in the **Overview** section of your Azure Active Directory. +- If you select **Accounts in any organizational directory** for access type then you need to enter **organizations**. +- If you select **Accounts in any organizational directory or personal Microsoft accounts** for access type then you need to enter **common**. +- If you select **Personal Microsoft accounts only** for access type then you need to enter **consumers**. + +### Prompts + +The `prompts` field is an array of strings that specifies the type of user interaction that is required. The string can be one of the following values: + +- `prompt=login` forces the user to enter their credentials on that request, negating single-sign on. +- `prompt=none` is the opposite. It ensures that the user isn't presented with any interactive prompt. If the request can't be completed silently by using single-sign on, the Microsoft identity platform returns an `interaction_required` error. +- `prompt=consent` triggers the OAuth consent dialog after the user signs in, asking the user to grant permissions to the app. +- `prompt=select_account` interrupts single sign-on providing account selection experience listing all the accounts either in session or any remembered account or an option to choose to use a different account altogether. + +Logto will concatenate the prompts with a space as the value of `prompt` in the authorization URL. ### Client ID diff --git a/packages/connectors/connector-azuread/package.json b/packages/connectors/connector-azuread/package.json index ceb0c74cf83..773b2da57be 100644 --- a/packages/connectors/connector-azuread/package.json +++ b/packages/connectors/connector-azuread/package.json @@ -1,11 +1,11 @@ { "name": "@logto/connector-azuread", - "version": "1.2.0", + "version": "1.3.0", "description": "Microsoft Azure AD connector implementation.", "author": "Mobilist Inc. ", "dependencies": { "@azure/msal-node": "^2.0.0", - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -53,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-azuread/src/constant.ts b/packages/connectors/connector-azuread/src/constant.ts index 9e38a31f04a..0eafd7cee51 100644 --- a/packages/connectors/connector-azuread/src/constant.ts +++ b/packages/connectors/connector-azuread/src/constant.ts @@ -1,5 +1,5 @@ import type { ConnectorMetadata } from '@logto/connector-kit'; -import { ConnectorPlatform, ConnectorConfigFormItemType } from '@logto/connector-kit'; +import { ConnectorPlatform, ConnectorConfigFormItemType, OidcPrompt } from '@logto/connector-kit'; export const graphAPIEndpoint = 'https://graph.microsoft.com/v1.0/me'; export const scopes = ['User.Read']; @@ -53,6 +53,15 @@ export const defaultMetadata: ConnectorMetadata = { label: 'Tenant ID', placeholder: '', }, + { + key: 'prompts', + type: ConnectorConfigFormItemType.MultiSelect, + required: false, + label: 'Prompts', + selectItems: Object.values(OidcPrompt).map((prompt) => ({ + value: prompt, + })), + }, ], }; diff --git a/packages/connectors/connector-azuread/src/index.ts b/packages/connectors/connector-azuread/src/index.ts index eec02121a34..879df9c77cc 100644 --- a/packages/connectors/connector-azuread/src/index.ts +++ b/packages/connectors/connector-azuread/src/index.ts @@ -37,12 +37,13 @@ const getAuthorizationUri = const config = await getConfig(defaultMetadata.id); validateConfig(config, azureADConfigGuard); - const { clientId, clientSecret, cloudInstance, tenantId } = config; + const { clientId, clientSecret, cloudInstance, tenantId, prompts } = config; const defaultAuthCodeUrlParameters: AuthorizationUrlRequest = { scopes, state, redirectUri, + ...conditional(prompts && prompts.length > 0 && { prompt: prompts.join(' ') }), }; const clientApplication = new ConfidentialClientApplication({ diff --git a/packages/connectors/connector-azuread/src/types.ts b/packages/connectors/connector-azuread/src/types.ts index d2a28809ff1..9af09f1698f 100644 --- a/packages/connectors/connector-azuread/src/types.ts +++ b/packages/connectors/connector-azuread/src/types.ts @@ -1,10 +1,13 @@ import { z } from 'zod'; +import { oidcPromptsGuard } from '@logto/connector-kit'; + export const azureADConfigGuard = z.object({ clientId: z.string(), clientSecret: z.string(), cloudInstance: z.string(), tenantId: z.string(), + prompts: oidcPromptsGuard, }); export type AzureADConfig = z.infer; diff --git a/packages/connectors/connector-dingtalk-web/CHANGELOG.md b/packages/connectors/connector-dingtalk-web/CHANGELOG.md index 07a02ba542d..61d3dd9d940 100644 --- a/packages/connectors/connector-dingtalk-web/CHANGELOG.md +++ b/packages/connectors/connector-dingtalk-web/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-dingtalk-web +## 0.1.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 0.1.0 ### Minor Changes diff --git a/packages/connectors/connector-dingtalk-web/README.md b/packages/connectors/connector-dingtalk-web/README.md index b2df1b1380c..6e0be8350a7 100644 --- a/packages/connectors/connector-dingtalk-web/README.md +++ b/packages/connectors/connector-dingtalk-web/README.md @@ -12,6 +12,7 @@ The official Logto connector for DingTalk social sign-in in web apps. - [Register a DingTalk Developer Account](#register-a-dingtalk-developer-account) - [Create an Application](#create-an-application) - [Configure Permissions](#configure-permissions) + - [Release Application](#release-application) - [Configure Your Connector](#configure-your-connector) - [Config Types](#config-types) - [Test DingTalk Connector](#test-dingtalk-connector) @@ -22,6 +23,7 @@ The official Logto connector for DingTalk social sign-in in web apps. - [注册钉钉开发者账号](#注册钉钉开发者账号) - [创建应用](#创建应用) - [配置权限](#配置权限) + - [应用发布](#应用发布) - [配置你的连接器](#配置你的连接器) - [配置类型](#配置类型) - [测试钉钉连接器](#测试钉钉连接器) @@ -34,7 +36,6 @@ The DingTalk web connector is designed for desktop web applications. It uses the ## Create a web app in the DingTalk Open Platform > 💡 **Tip** -> > You can skip some sections if you have already finished. ### Register a DingTalk developer account @@ -43,19 +44,21 @@ If you do not have a DingTalk developer account, please register at the [DingTal ### Create an application -1. In the [DingTalk Developer Console](https://open-dev.dingtalk.com/console/index), click "Create Application" -2. Choose "Self-built Application", fill in the application name and basic information, and click "Create" -3. In the left navigation bar, select "Development Configuration" -> "Security Settings", find and configure the "Redirect URL" `${your_logto_origin}/callback/${connector_id}`. You can find the `connector_id` on the connector details page after adding the respective connector in the management console -4. In the left navigation bar, select "Basic Information" -> "Credentials and Basic Information" to get the "Client ID" and "Client Secret" -5. In the left navigation bar, select "Application Release" -> "Version Management and Release", create and release the first version to activate the "Client ID" and "Client Secret" - -> ℹ️ **Note** -> If the application does not release a version, the obtained "Client ID" and "Client Secret" cannot be used, or requests will fail. +1. In the DingTalk Open Platform "[Application Development](https://open-dev.dingtalk.com/fe/app)" > "Internal Enterprise Application" > "DingTalk Application", click "Create Application" +2. Fill in the **application name** and **description**, and click "Save" +3. In the left navigation bar, select "Development Configuration" > "Security Settings", find and configure the "Redirect URL" `${your_logto_origin}/callback/${connector_id}`. You can find the `connector_id` on the connector details page after adding the respective connector in the management console +4. In the left navigation bar, select "Basic Information" > "Credentials and Basic Information" to get the `Client ID` and `Client Secret` ### Configure permissions -1. In "Development Configuration" -> "Permission Management", select `Contact.User.Read` and `Contact.User.mobile` permissions and authorize them -2. After confirming the permission configuration, click "Save" and publish the application +In "Development Configuration" > "Permission Management", select `Contact.User.Read` and `Contact.User.mobile` permissions and authorize them + +### Release Application + +In the left navigation bar, select "Application Release" > "Version Management and Release", create and release the first version to activate the `Client ID` and `Client Secret` + +> ℹ️ **Note** +> If the application does not release a version, the obtained "Client ID" and "Client Secret" cannot be used, or requests will fail. ## Configure your connector @@ -73,7 +76,7 @@ Fill out the `clientId` and `clientSecret` field with _Client ID(formerly AppKey ## Test DingTalk connector -That's it. The DingTalk connector should be available now. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in). +That's it. The DingTalk connector should be available now. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once DingTalk web connector is enabled, you can sign in to your app again to see if it works. @@ -93,7 +96,6 @@ If you have any questions or need further assistance, please visit the [DingTalk ## 在钉钉开放平台新建一个应用 > 💡 **Tip** -> > 你可以跳过已经完成的部分。 ### 注册钉钉开发者账号 @@ -102,25 +104,27 @@ If you have any questions or need further assistance, please visit the [DingTalk ### 创建应用 -1. 在 [钉钉开发者后台](https://open-dev.dingtalk.com/console/index) 中,点击「创建应用」 -2. 选择「自建应用」,填写应用名称和基本信息,点击「创建」 -3. 在左侧导航栏选择「开发配置」->「安全设置」,找到并配置「重定向 URL」 `${your_logto_origin}/callback/${connector_id}`。其中 `connector_id` 在管理控制台添加了相应的连接器之后,可以在连接器的详情页中找到 -4. 在左侧导航栏选择「基础信息」->「凭证与基础信息」中可以获取「Client ID」、「Client Secret」 -5. 在左侧导航栏选择「应用发布」->「版本管理与发布」,创建并发布第一个版本,以使「Client ID」、「Client Secret」生效 - -> ℹ️ **Note** -> 应用不发布版本,所获取的「Client ID」、「Client Secret」 均无法使用,或请求错误。 +1. 在 钉钉开放平台「[应用开发](https://open-dev.dingtalk.com/fe/app)」>「企业内部应用」>「钉钉应用」中,点击「创建应用」 +2. 填写**应用名称**和**应用描述**,点击「保存」 +3. 在左侧导航栏选择「开发配置」>「安全设置」,找到并配置「重定向 URL」 `${your_logto_origin}/callback/${connector_id}`。其中 `connector_id` 在管理控制台添加了相应的连接器之后,可以在连接器的详情页中找到 +4. 在左侧导航栏选择「基础信息」>「凭证与基础信息」中可以获取「Client ID」、「Client Secret」 ### 配置权限 -1. 在「开发配置」->「权限管理」中,选择`通讯录个人信息读权限`和`个人手机号信息`权限并进行授权 -2. 确认权限配置后,点击「保存」并发布应用 +在「开发配置」>「权限管理」中,选择`通讯录个人信息读权限`和`个人手机号信息`权限并进行授权 + +### 应用发布 + +在左侧导航栏选择「应用发布」>「版本管理与发布」,点击「创建新版本」发布第一个版本,以使「Client ID」、「Client Secret」生效 + +> ℹ️ **Note** +> 应用不发布版本,所获取的「Client ID」、「Client Secret」 均无法使用,或请求错误。 ## 配置你的连接器 -在 clientId 和 clientSecret 字段中填入你在上一个部分中提到的 OAuth 应用详情页面获取的 Client ID(原 AppKey 和 SuiteKey) 和 Client Secret(原 AppKey 和 SuiteKey) 。 +在 `clientId` 和 `clientSecret` 字段中填入你在上一个部分中提到的 OAuth 应用详情页面获取的 _Client ID_(原 AppKey 和 SuiteKey)和 _Client Secret_(原 AppKey 和 SuiteKey)。 -scope 目前支持两种值:openid 和 openid corpid。openid 授权后可以获取用户的 userid,而 openid corpid 授权后可以获取用户的 id 和登录过程中用户选择的组织 id。这些值应以空格分隔。注意:需要进行 URL 编码。 +`scope` 目前支持两种值:`openid` 和 `openid corpid`。`openid` 授权后可以获取用户的 `userid`,而 `openid corpid` 授权后可以获取用户的 `id` 和登录过程中用户选择的组织 `id`。这些值应以空格分隔。注意:需要进行 URL 编码。 ### 配置类型 @@ -132,7 +136,7 @@ scope 目前支持两种值:openid 和 openid corpid。openid 授权后可以 ## 测试钉钉连接器 -大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在钉钉web连接器启用后,你可以构建并运行你的应用看看是否生效。 diff --git a/packages/connectors/connector-dingtalk-web/package.json b/packages/connectors/connector-dingtalk-web/package.json index 08374ef511e..d2815d24c29 100644 --- a/packages/connectors/connector-dingtalk-web/package.json +++ b/packages/connectors/connector-dingtalk-web/package.json @@ -1,9 +1,9 @@ { "name": "@logto/connector-dingtalk-web", - "version": "0.1.0", + "version": "0.1.1", "description": "Dingtalk web connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "dayjs": "^1.10.5", "got": "^14.0.0", @@ -12,7 +12,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-discord/CHANGELOG.md b/packages/connectors/connector-discord/CHANGELOG.md index 7a1db6bf874..a321f0c4a49 100644 --- a/packages/connectors/connector-discord/CHANGELOG.md +++ b/packages/connectors/connector-discord/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-discord +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-discord/package.json b/packages/connectors/connector-discord/package.json index f5abedaa2ba..eef708ea75b 100644 --- a/packages/connectors/connector-discord/package.json +++ b/packages/connectors/connector-discord/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-discord", - "version": "1.3.0", + "version": "1.3.1", "description": "Discord connector implementation.", "author": "ZR3SYSTEMS. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-facebook/CHANGELOG.md b/packages/connectors/connector-facebook/CHANGELOG.md index 9e5e7bcf628..094ebede4a5 100644 --- a/packages/connectors/connector-facebook/CHANGELOG.md +++ b/packages/connectors/connector-facebook/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-facebook +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-facebook/package.json b/packages/connectors/connector-facebook/package.json index 3cc19cc84da..787adb612c6 100644 --- a/packages/connectors/connector-facebook/package.json +++ b/packages/connectors/connector-facebook/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-facebook", - "version": "1.3.0", + "version": "1.3.1", "description": "Facebook web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-feishu-web/CHANGELOG.md b/packages/connectors/connector-feishu-web/CHANGELOG.md index 69fca21a6ae..3d0f5dbfbf8 100644 --- a/packages/connectors/connector-feishu-web/CHANGELOG.md +++ b/packages/connectors/connector-feishu-web/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-feishu-web +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-feishu-web/package.json b/packages/connectors/connector-feishu-web/package.json index 4d592023046..b6db69458b6 100644 --- a/packages/connectors/connector-feishu-web/package.json +++ b/packages/connectors/connector-feishu-web/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-feishu-web", - "version": "1.2.0", + "version": "1.2.1", "description": "Feishu web connector.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-github/CHANGELOG.md b/packages/connectors/connector-github/CHANGELOG.md index 353a78a327b..3070f3df691 100644 --- a/packages/connectors/connector-github/CHANGELOG.md +++ b/packages/connectors/connector-github/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-github +## 1.4.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.4.1 ### Patch Changes diff --git a/packages/connectors/connector-github/README.md b/packages/connectors/connector-github/README.md index f3315992ae7..fabfea8a026 100644 --- a/packages/connectors/connector-github/README.md +++ b/packages/connectors/connector-github/README.md @@ -55,7 +55,7 @@ Fill out the `clientId` and `clientSecret` field with _Client ID_ and _Client Se ## Test GitHub connector -That's it. The GitHub connector should be available now. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in). +That's it. The GitHub connector should be available now. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). ## Reference diff --git a/packages/connectors/connector-github/package.json b/packages/connectors/connector-github/package.json index b21f6d97487..1e9eeb2a4e9 100644 --- a/packages/connectors/connector-github/package.json +++ b/packages/connectors/connector-github/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-github", - "version": "1.4.1", + "version": "1.4.2", "description": "Github web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "ky": "^1.2.3", "query-string": "^9.0.0", @@ -53,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-google/CHANGELOG.md b/packages/connectors/connector-google/CHANGELOG.md index 2f79c263150..1525e3dea4a 100644 --- a/packages/connectors/connector-google/CHANGELOG.md +++ b/packages/connectors/connector-google/CHANGELOG.md @@ -1,5 +1,23 @@ # @logto/connector-google +## 1.4.0 + +### Minor Changes + +- 6308ee185: support Google One Tap + + - support parsing and validating Google One Tap data in `connector-google` + - add Google connector constants in `connector-kit` for reuse + +- 15953609b: support config of `prompt` + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-google/README.md b/packages/connectors/connector-google/README.md index 767e6b71e19..2ed9db7e7b4 100644 --- a/packages/connectors/connector-google/README.md +++ b/packages/connectors/connector-google/README.md @@ -3,18 +3,17 @@ The Google connector provides a succinct way for your application to use Google’s OAuth 2.0 authentication system. **Table of contents** -- [Google connector](#google-connector) - - [Set up a project in the Google API Console](#set-up-a-project-in-the-google-api-console) - - [Configure your consent screen](#configure-your-consent-screen) - - [Configure and register your application](#configure-and-register-your-application) - - [Edit app registration](#edit-app-registration) - - [Config OAuth consent screen](#config-oauth-consent-screen) - - [Config scopes](#config-scopes) - - [Add test users (External user type only)](#add-test-users-external-user-type-only) - - [Obtain OAuth 2.0 credentials](#obtain-oauth-20-credentials) - - [Configure your connector](#configure-your-connector) - - [Config types](#config-types) - - [References](#references) +- [Set up a project in the Google API Console](#set-up-a-project-in-the-google-api-console) +- [Configure your consent screen](#configure-your-consent-screen) + - [Configure and register your application](#configure-and-register-your-application) + - [Edit app registration](#edit-app-registration) + - [Config OAuth consent screen](#config-oauth-consent-screen) + - [Config scopes](#config-scopes) + - [Add test users (External user type only)](#add-test-users-external-user-type-only) +- [Obtain OAuth 2.0 credentials](#obtain-oauth-20-credentials) +- [Configure your connector](#configure-your-connector) + - [Config types](#config-types) +- [References](#references) ## Set up a project in the Google API Console @@ -57,8 +56,8 @@ Now you should have the Google OAuth 2.0 consent screen configured. - On the **Credentials** page, click the **+ CREATE CREDENTIALS** button on the top menu bar, and select **OAuth client ID**. - On the **Create OAuth client ID** page, select **Web application** as the application type. - Fill out the basic information for your application. -- Click **+ Add URI** to add an authorized domain to the **Authorized JavaScript origins** section. This is the domain that your logto authorization page will be served from. In our case, this will be `${your_logto_origin}`. e.g.`https://logto.dev`. -- Click **+ Add URI** in the ****Authorized redirect URIs**** section to set up the ****Authorized redirect URIs****, which redirect the user to the application after logging in. In our case, this will be `${your_logto_endpoint}/callback/${connector_id}`. e.g. `https://logto.dev/callback/${connector_id}`. The `connector_id` can be found on the top bar of the Logto Admin Console connector details page. +- Click **+ Add URI** to add an authorized domain to the **Authorized JavaScript origins** section. This is the domain that your logto authorization page will be served from. In our case, this will be `${your_logto_endpoint_origin}`. e.g.`https://foo.logto.app`. +- Click **+ Add URI** in the ****Authorized redirect URIs**** section to set up the ****Authorized redirect URIs****, which redirect the user to the application after logging in. In our case, this will be `${your_logto_endpoint}/callback/${connector_id}`. e.g. `https://foo.logto.app/callback/${connector_id}`. The `connector_id` can be found on the top bar of the Logto Admin Console connector details page. - Click **Create** to finish and then you will get the **Client ID** and **Client Secret**. ## Configure your connector @@ -67,13 +66,20 @@ Fill out the `clientId` and `clientSecret` field with _Client ID_ and _Client Se `scope` is a space-delimited list of [scopes](https://developers.google.com/identity/protocols/oauth2/scopes). If not provided, scope defaults to be `openid profile email`. +`prompts` is an array of strings that specifies the type of user interaction that is required. The string can be one of the following values: + +- `none`: The authorization server does not display any authentication or user consent screens; it will return an error if the user is not already authenticated and has not pre-configured consent for the requested scopes. You can use none to check for existing authentication and/or consent. +- `consent`: The authorization server prompts the user for consent before returning information to the client. +- `select_account`: The authorization server prompts the user to select a user account. This allows a user who has multiple accounts at the authorization server to select amongst the multiple accounts that they may have current sessions for. + ### Config types -| Name | Type | -|--------------|--------| -| clientId | string | -| clientSecret | string | -| scope | string | +| Name | Type | +|--------------|----------| +| clientId | string | +| clientSecret | string | +| scope | string | +| prompts | string[] | ## References * [Google Identity: Setting up OAuth 2.0](https://developers.google.com/identity/protocols/oauth2/openid-connect#appsetup) diff --git a/packages/connectors/connector-google/package.json b/packages/connectors/connector-google/package.json index 06479432def..6f5c8da1591 100644 --- a/packages/connectors/connector-google/package.json +++ b/packages/connectors/connector-google/package.json @@ -1,12 +1,13 @@ { "name": "@logto/connector-google", - "version": "1.3.0", + "version": "1.4.0", "description": "Google web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", + "jose": "^5.0.0", "snakecase-keys": "^8.0.0", "zod": "^3.22.4" }, @@ -52,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-google/src/constant.ts b/packages/connectors/connector-google/src/constant.ts index 9ff63ada22a..129ec67e024 100644 --- a/packages/connectors/connector-google/src/constant.ts +++ b/packages/connectors/connector-google/src/constant.ts @@ -1,14 +1,22 @@ import type { ConnectorMetadata } from '@logto/connector-kit'; -import { ConnectorConfigFormItemType, ConnectorPlatform } from '@logto/connector-kit'; +import { + ConnectorConfigFormItemType, + ConnectorPlatform, + GoogleConnector, + OidcPrompt, +} from '@logto/connector-kit'; export const authorizationEndpoint = 'https://accounts.google.com/o/oauth2/v2/auth'; export const accessTokenEndpoint = 'https://oauth2.googleapis.com/token'; export const userInfoEndpoint = 'https://openidconnect.googleapis.com/v1/userinfo'; export const scope = 'openid profile email'; +// Instead of defining the metadata in the connector, we reuse the metadata from the connector-kit. +// This is not the normal practice, but Google One Tap is a special case. +// @see {@link GoogleConnector} for more information. export const defaultMetadata: ConnectorMetadata = { - id: 'google-universal', - target: 'google', + id: GoogleConnector.factoryId, + target: GoogleConnector.target, platform: ConnectorPlatform.Universal, name: { en: 'Google', @@ -49,7 +57,23 @@ export const defaultMetadata: ConnectorMetadata = { description: "The `scope` determines permissions granted by the user's authorization. If you are not sure what to enter, do not worry, just leave it blank.", }, + { + key: 'prompts', + type: ConnectorConfigFormItemType.MultiSelect, + required: false, + label: 'Prompts', + // Google does not support `login` prompt. + // Ref: https://developers.google.com/identity/openid-connect/openid-connect#authenticationuriparameters + selectItems: Object.values(OidcPrompt) + .filter((prompt) => prompt !== OidcPrompt.Login) + .map((prompt) => ({ + value: prompt, + })), + }, ], }; export const defaultTimeout = 5000; + +// https://developers.google.com/identity/gsi/web/guides/verify-google-id-token +export const jwksUri = 'https://www.googleapis.com/oauth2/v3/certs'; diff --git a/packages/connectors/connector-google/src/index.test.ts b/packages/connectors/connector-google/src/index.test.ts index 4b258f02144..8cdeb4c202a 100644 --- a/packages/connectors/connector-google/src/index.test.ts +++ b/packages/connectors/connector-google/src/index.test.ts @@ -8,6 +8,25 @@ import { mockedConfig } from './mock.js'; const getConfig = vi.fn().mockResolvedValue(mockedConfig); +vi.mock('jose', () => ({ + createRemoteJWKSet: vi.fn().mockReturnValue({ + getSigningKey: vi.fn().mockResolvedValue({ + publicKey: 'publicKey', + }), + }), + jwtVerify: vi.fn().mockResolvedValue({ + payload: { + sub: '1234567890', + name: 'John Wick', + given_name: 'John', + family_name: 'Wick', + email: 'john@silverhand.io', + email_verified: true, + picture: 'https://example.com/image.jpg', + }, + }), +})); + describe('google connector', () => { describe('getAuthorizationUri', () => { afterEach(() => { @@ -105,6 +124,31 @@ describe('google connector', () => { }); }); + it('should be able to decode ID token from Google One Tap', async () => { + const connector = await createConnector({ getConfig }); + const socialUserInfo = await connector.getUserInfo( + { + credential: 'credential', + }, + vi.fn() + ); + expect(socialUserInfo).toStrictEqual({ + id: '1234567890', + avatar: 'https://example.com/image.jpg', + name: 'John Wick', + email: 'john@silverhand.io', + rawData: { + sub: '1234567890', + name: 'John Wick', + given_name: 'John', + family_name: 'Wick', + email: 'john@silverhand.io', + email_verified: true, + picture: 'https://example.com/image.jpg', + }, + }); + }); + it('throws SocialAccessTokenInvalid error if remote response code is 401', async () => { nock(userInfoEndpoint).post('').reply(401); const connector = await createConnector({ getConfig }); diff --git a/packages/connectors/connector-google/src/index.ts b/packages/connectors/connector-google/src/index.ts index 19c8fd9a273..a01e506368f 100644 --- a/packages/connectors/connector-google/src/index.ts +++ b/packages/connectors/connector-google/src/index.ts @@ -11,6 +11,7 @@ import type { GetConnectorConfig, CreateConnector, SocialConnector, + GoogleConnectorConfig, } from '@logto/connector-kit'; import { ConnectorError, @@ -18,7 +19,9 @@ import { validateConfig, ConnectorType, parseJson, + GoogleConnector, } from '@logto/connector-kit'; +import { createRemoteJWKSet, jwtVerify } from 'jose'; import { accessTokenEndpoint, @@ -27,34 +30,37 @@ import { userInfoEndpoint, defaultMetadata, defaultTimeout, + jwksUri, } from './constant.js'; -import type { GoogleConfig } from './types.js'; import { - googleConfigGuard, accessTokenResponseGuard, userInfoResponseGuard, authResponseGuard, + googleOneTapDataGuard, } from './types.js'; const getAuthorizationUri = (getConfig: GetConnectorConfig): GetAuthorizationUri => async ({ state, redirectUri }) => { const config = await getConfig(defaultMetadata.id); - validateConfig(config, googleConfigGuard); + validateConfig(config, GoogleConnector.configGuard); + + const { clientId, scope, prompts } = config; const queryParameters = new URLSearchParams({ - client_id: config.clientId, + client_id: clientId, redirect_uri: redirectUri, response_type: 'code', state, - scope: config.scope ?? defaultScope, + scope: scope ?? defaultScope, + ...conditional(prompts && prompts.length > 0 && { prompt: prompts.join(' ') }), }); return `${authorizationEndpoint}?${queryParameters.toString()}`; }; export const getAccessToken = async ( - config: GoogleConfig, + config: GoogleConnectorConfig, codeObject: { code: string; redirectUri: string } ) => { const { code, redirectUri } = codeObject; @@ -86,22 +92,58 @@ export const getAccessToken = async ( return { accessToken }; }; +type Json = ReturnType; + +/** + * Get user information JSON from Google Identity Platform. It will use the following order to + * retrieve user information: + * + * 1. Google One Tap: https://developers.google.com/identity/gsi/web/guides/verify-google-id-token + * 2. Normal Google OAuth: https://developers.google.com/identity/protocols/oauth2/openid-connect + * + * @param data The data from the client. + * @param config The configuration of the connector. + * @returns A Promise that resolves to the user information JSON. + */ +const getUserInfoJson = async (data: unknown, config: GoogleConnectorConfig): Promise => { + // Google One Tap + const oneTapResult = googleOneTapDataGuard.safeParse(data); + + if (oneTapResult.success) { + const { payload } = await jwtVerify( + oneTapResult.data.credential, + createRemoteJWKSet(new URL(jwksUri)), + { + // https://developers.google.com/identity/gsi/web/guides/verify-google-id-token + issuer: ['https://accounts.google.com', 'accounts.google.com'], + audience: config.clientId, + clockTolerance: 10, + } + ); + return payload; + } + + // Normal Google OAuth + const { code, redirectUri } = await authorizationCallbackHandler(data); + const { accessToken } = await getAccessToken(config, { code, redirectUri }); + + const httpResponse = await got.post(userInfoEndpoint, { + headers: { + authorization: `Bearer ${accessToken}`, + }, + timeout: { request: defaultTimeout }, + }); + return parseJson(httpResponse.body); +}; + const getUserInfo = (getConfig: GetConnectorConfig): GetUserInfo => async (data) => { - const { code, redirectUri } = await authorizationCallbackHandler(data); const config = await getConfig(defaultMetadata.id); - validateConfig(config, googleConfigGuard); - const { accessToken } = await getAccessToken(config, { code, redirectUri }); + validateConfig(config, GoogleConnector.configGuard); try { - const httpResponse = await got.post(userInfoEndpoint, { - headers: { - authorization: `Bearer ${accessToken}`, - }, - timeout: { request: defaultTimeout }, - }); - const rawData = parseJson(httpResponse.body); + const rawData = await getUserInfoJson(data, config); const result = userInfoResponseGuard.safeParse(rawData); if (!result.success) { @@ -150,7 +192,7 @@ const createGoogleConnector: CreateConnector = async ({ getConf return { metadata: defaultMetadata, type: ConnectorType.Social, - configGuard: googleConfigGuard, + configGuard: GoogleConnector.configGuard, getAuthorizationUri: getAuthorizationUri(getConfig), getUserInfo: getUserInfo(getConfig), }; diff --git a/packages/connectors/connector-google/src/types.ts b/packages/connectors/connector-google/src/types.ts index 5837e568ed6..8141627bbbb 100644 --- a/packages/connectors/connector-google/src/types.ts +++ b/packages/connectors/connector-google/src/types.ts @@ -1,12 +1,6 @@ import { z } from 'zod'; -export const googleConfigGuard = z.object({ - clientId: z.string(), - clientSecret: z.string(), - scope: z.string().optional(), -}); - -export type GoogleConfig = z.infer; +import { GoogleConnector } from '@logto/connector-kit'; export const accessTokenResponseGuard = z.object({ access_token: z.string(), @@ -33,3 +27,11 @@ export const authResponseGuard = z.object({ code: z.string(), redirectUri: z.string(), }); + +/** + * Response payload from Google One Tap. Note the CSRF token is not included since it should be + * verified by the web server. + */ +export const googleOneTapDataGuard = z.object({ + [GoogleConnector.oneTapParams.credential]: z.string(), +}); diff --git a/packages/connectors/connector-huggingface/CHANGELOG.md b/packages/connectors/connector-huggingface/CHANGELOG.md index a64bffe3fd9..9a4e9d322cd 100644 --- a/packages/connectors/connector-huggingface/CHANGELOG.md +++ b/packages/connectors/connector-huggingface/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-huggingface +## 0.1.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + - @logto/connector-oauth@1.3.1 + ## 0.1.0 ### Minor Changes diff --git a/packages/connectors/connector-huggingface/package.json b/packages/connectors/connector-huggingface/package.json index b2bd8121bc0..b87196f02f6 100644 --- a/packages/connectors/connector-huggingface/package.json +++ b/packages/connectors/connector-huggingface/package.json @@ -1,11 +1,11 @@ { "name": "@logto/connector-huggingface", - "version": "0.1.0", + "version": "0.1.1", "description": "Hugging Face connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", - "@logto/connector-oauth": "workspace:^1.3.0", + "@logto/connector-kit": "workspace:^4.0.0", + "@logto/connector-oauth": "workspace:^1.3.1", "@silverhand/essentials": "^2.9.1", "ky": "^1.2.3", "zod": "^3.22.4" @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-huggingface/src/index.test.ts b/packages/connectors/connector-huggingface/src/index.test.ts index e0b79e4904c..7c2ce197838 100644 --- a/packages/connectors/connector-huggingface/src/index.test.ts +++ b/packages/connectors/connector-huggingface/src/index.test.ts @@ -91,7 +91,7 @@ describe('Hugging Face connector', () => { nock.cleanAll(); nock(tokenEndpoint).post('').reply(200, { - invalid_filed: true, + invalid_field: true, }); const connector = await createConnector({ getConfig }); diff --git a/packages/connectors/connector-kakao/CHANGELOG.md b/packages/connectors/connector-kakao/CHANGELOG.md index 341fe977229..813a9f758ef 100644 --- a/packages/connectors/connector-kakao/CHANGELOG.md +++ b/packages/connectors/connector-kakao/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-kakao +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-kakao/package.json b/packages/connectors/connector-kakao/package.json index 4d41b6b817c..900f095e6c5 100644 --- a/packages/connectors/connector-kakao/package.json +++ b/packages/connectors/connector-kakao/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-kakao", - "version": "1.2.0", + "version": "1.2.1", "description": "Kakao connector implementation.", "author": "Kyungyoon Kim. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-logto-email/CHANGELOG.md b/packages/connectors/connector-logto-email/CHANGELOG.md index 0742b6e2d83..2a7dbe906db 100644 --- a/packages/connectors/connector-logto-email/CHANGELOG.md +++ b/packages/connectors/connector-logto-email/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-logto-email +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-logto-email/package.json b/packages/connectors/connector-logto-email/package.json index 7466da480ef..d209481cfcc 100644 --- a/packages/connectors/connector-logto-email/package.json +++ b/packages/connectors/connector-logto-email/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-logto-email", - "version": "1.1.1", + "version": "1.1.2", "description": "Logto email connector.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,8 +52,8 @@ "access": "public" }, "devDependencies": { - "@logto/cloud": "0.2.5-38aae44", - "@rollup/plugin-commonjs": "^25.0.7", + "@logto/cloud": "0.2.5-a7eedce", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-logto-sms/CHANGELOG.md b/packages/connectors/connector-logto-sms/CHANGELOG.md index cdb1dcb483d..08b1d95aa47 100644 --- a/packages/connectors/connector-logto-sms/CHANGELOG.md +++ b/packages/connectors/connector-logto-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-logto-sms +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-logto-sms/package.json b/packages/connectors/connector-logto-sms/package.json index 4b3b2ecc55e..00fa94c4f50 100644 --- a/packages/connectors/connector-logto-sms/package.json +++ b/packages/connectors/connector-logto-sms/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-logto-sms", - "version": "1.1.1", + "version": "1.1.2", "description": "Logto SMS connector.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-logto-social-demo/CHANGELOG.md b/packages/connectors/connector-logto-social-demo/CHANGELOG.md index ba7962448b0..e641551e64a 100644 --- a/packages/connectors/connector-logto-social-demo/CHANGELOG.md +++ b/packages/connectors/connector-logto-social-demo/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-logto-social-demo +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-logto-social-demo/package.json b/packages/connectors/connector-logto-social-demo/package.json index 666c617ffc9..0e765182fbf 100644 --- a/packages/connectors/connector-logto-social-demo/package.json +++ b/packages/connectors/connector-logto-social-demo/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-logto-social-demo", - "version": "1.1.1", + "version": "1.1.2", "description": "OAuth standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-mailgun/CHANGELOG.md b/packages/connectors/connector-mailgun/CHANGELOG.md index 2820a1bb633..47a8407f9dc 100644 --- a/packages/connectors/connector-mailgun/CHANGELOG.md +++ b/packages/connectors/connector-mailgun/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mailgun +## 1.2.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-mailgun/README.md b/packages/connectors/connector-mailgun/README.md index 4a493b884a9..72546840f26 100644 --- a/packages/connectors/connector-mailgun/README.md +++ b/packages/connectors/connector-mailgun/README.md @@ -99,4 +99,4 @@ The following is an example of the deliveries config: You can type in an email address and click on "Send" to see whether the settings can work before "Save and Done". -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in) +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/) diff --git a/packages/connectors/connector-mailgun/package.json b/packages/connectors/connector-mailgun/package.json index 446d52b28ad..07cacc5bdb8 100644 --- a/packages/connectors/connector-mailgun/package.json +++ b/packages/connectors/connector-mailgun/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mailgun", - "version": "1.2.1", + "version": "1.2.2", "description": "Mailgun connector for Logto.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-mock-email-alternative/CHANGELOG.md b/packages/connectors/connector-mock-email-alternative/CHANGELOG.md index a1142990d04..c324ffcb4bc 100644 --- a/packages/connectors/connector-mock-email-alternative/CHANGELOG.md +++ b/packages/connectors/connector-mock-email-alternative/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mock-standard-email +## 2.0.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 2.0.1 ### Patch Changes diff --git a/packages/connectors/connector-mock-email-alternative/package.json b/packages/connectors/connector-mock-email-alternative/package.json index d4207960959..9132e8f8530 100644 --- a/packages/connectors/connector-mock-email-alternative/package.json +++ b/packages/connectors/connector-mock-email-alternative/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mock-standard-email", - "version": "2.0.1", + "version": "2.0.2", "description": "Mock Standard Email Service connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-mock-email/CHANGELOG.md b/packages/connectors/connector-mock-email/CHANGELOG.md index 4ad35a91007..9f0a121575b 100644 --- a/packages/connectors/connector-mock-email/CHANGELOG.md +++ b/packages/connectors/connector-mock-email/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mock-email +## 2.0.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 2.0.1 ### Patch Changes diff --git a/packages/connectors/connector-mock-email/package.json b/packages/connectors/connector-mock-email/package.json index 66344682f55..f1b4024346a 100644 --- a/packages/connectors/connector-mock-email/package.json +++ b/packages/connectors/connector-mock-email/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mock-email", - "version": "2.0.1", + "version": "2.0.2", "description": "Mock Email Service connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-mock-sms/CHANGELOG.md b/packages/connectors/connector-mock-sms/CHANGELOG.md index a72ac350823..db5aedaeb47 100644 --- a/packages/connectors/connector-mock-sms/CHANGELOG.md +++ b/packages/connectors/connector-mock-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mock-sms +## 2.0.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 2.0.1 ### Patch Changes diff --git a/packages/connectors/connector-mock-sms/package.json b/packages/connectors/connector-mock-sms/package.json index 09f82cbced3..1ae46035a40 100644 --- a/packages/connectors/connector-mock-sms/package.json +++ b/packages/connectors/connector-mock-sms/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mock-sms", - "version": "2.0.1", + "version": "2.0.2", "description": "Mock SMS connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-mock-social/CHANGELOG.md b/packages/connectors/connector-mock-social/CHANGELOG.md index 57dcf6d62ec..44cbf2e1725 100644 --- a/packages/connectors/connector-mock-social/CHANGELOG.md +++ b/packages/connectors/connector-mock-social/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-mock-social +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-mock-social/package.json b/packages/connectors/connector-mock-social/package.json index 4a687efb762..902cecb18f7 100644 --- a/packages/connectors/connector-mock-social/package.json +++ b/packages/connectors/connector-mock-social/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-mock-social", - "version": "1.2.0", + "version": "1.2.1", "description": "Social mock connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-mock-social/src/index.ts b/packages/connectors/connector-mock-social/src/index.ts index b9c76058c35..77c24b645a3 100644 --- a/packages/connectors/connector-mock-social/src/index.ts +++ b/packages/connectors/connector-mock-social/src/index.ts @@ -2,9 +2,9 @@ import { randomUUID } from 'node:crypto'; import { z } from 'zod'; import type { + CreateConnector, GetAuthorizationUri, GetUserInfo, - CreateConnector, SocialConnector, } from '@logto/connector-kit'; import { @@ -17,11 +17,23 @@ import { import { defaultMetadata } from './constant.js'; import { mockSocialConfigGuard } from './types.js'; -const getAuthorizationUri: GetAuthorizationUri = async ({ state, redirectUri }) => { +const getAuthorizationUri: GetAuthorizationUri = async ( + { state, redirectUri, connectorId }, + setSession +) => { + try { + await setSession({ state, redirectUri, connectorId }); + } catch (error: unknown) { + // Ignore the error if the method is not implemented + if (!(error instanceof ConnectorError && error.code === ConnectorErrorCodes.NotImplemented)) { + throw error; + } + } + return `http://mock.social.com/?state=${state}&redirect_uri=${redirectUri}`; }; -const getUserInfo: GetUserInfo = async (data) => { +const getUserInfo: GetUserInfo = async (data, getSession) => { const dataGuard = z.object({ code: z.string(), userId: z.optional(z.string()), @@ -34,6 +46,19 @@ const getUserInfo: GetUserInfo = async (data) => { throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(data)); } + try { + const connectorSession = await getSession(); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!connectorSession) { + throw new ConnectorError(ConnectorErrorCodes.AuthorizationFailed); + } + } catch (error: unknown) { + // Ignore the error if the method is not implemented + if (!(error instanceof ConnectorError && error.code === ConnectorErrorCodes.NotImplemented)) { + throw error; + } + } + const { code, userId, ...rest } = result.data; // For mock use only. Use to track the created user entity diff --git a/packages/connectors/connector-mygovid/package.json b/packages/connectors/connector-mygovid/package.json index 6caf2af0ae9..91ccf411e4d 100644 --- a/packages/connectors/connector-mygovid/package.json +++ b/packages/connectors/connector-mygovid/package.json @@ -3,10 +3,10 @@ "version": "1.3.0", "description": "MyGovId connector implementation. - A fork of the OIDC connector", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", - "@logto/connector-oauth": "workspace:^1.3.0", + "@logto/connector-kit": "workspace:^4.0.0", + "@logto/connector-oauth": "workspace:^1.3.1", "@logto/shared": "workspace:^3.1.1", - "@silverhand/essentials": "^2.9.0", + "@silverhand/essentials": "^2.9.1", "jose": "^5.0.0", "ky": "^1.2.3", "nanoid": "^5.0.1", @@ -55,7 +55,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", @@ -66,7 +66,7 @@ "@vitest/coverage-v8": "^1.4.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.6", + "nock": "14.0.0-beta.7", "prettier": "^3.0.0", "rollup": "^4.12.0", "rollup-plugin-output-size": "^1.3.0", diff --git a/packages/connectors/connector-naver/CHANGELOG.md b/packages/connectors/connector-naver/CHANGELOG.md index 978f2a4c883..565c435eb0d 100644 --- a/packages/connectors/connector-naver/CHANGELOG.md +++ b/packages/connectors/connector-naver/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-naver +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-naver/package.json b/packages/connectors/connector-naver/package.json index 9202e4b1b4d..22a3e422816 100644 --- a/packages/connectors/connector-naver/package.json +++ b/packages/connectors/connector-naver/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-naver", - "version": "1.2.0", + "version": "1.2.1", "description": "Naver connector implementation.", "author": "Kyungyoon Kim. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-oauth2/CHANGELOG.md b/packages/connectors/connector-oauth2/CHANGELOG.md index a1f37599889..6943b878a57 100644 --- a/packages/connectors/connector-oauth2/CHANGELOG.md +++ b/packages/connectors/connector-oauth2/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-oauth +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-oauth2/package.json b/packages/connectors/connector-oauth2/package.json index 4322537afe7..9fb22112f82 100644 --- a/packages/connectors/connector-oauth2/package.json +++ b/packages/connectors/connector-oauth2/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-oauth", - "version": "1.3.0", + "version": "1.3.1", "description": "OAuth standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", "jose": "^5.0.0", @@ -56,7 +56,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-oidc/CHANGELOG.md b/packages/connectors/connector-oidc/CHANGELOG.md index c135a5550d1..8ff39c430b4 100644 --- a/packages/connectors/connector-oidc/CHANGELOG.md +++ b/packages/connectors/connector-oidc/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-oidc +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + - @logto/connector-oauth@1.3.1 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-oidc/package.json b/packages/connectors/connector-oidc/package.json index 85443167a45..bffa1bbc509 100644 --- a/packages/connectors/connector-oidc/package.json +++ b/packages/connectors/connector-oidc/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-oidc", - "version": "1.3.0", + "version": "1.3.1", "description": "OIDC standard connector implementation.", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", - "@logto/connector-oauth": "workspace:^1.3.0", + "@logto/connector-kit": "workspace:^4.0.0", + "@logto/connector-oauth": "workspace:^1.3.1", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", "jose": "^5.0.0", @@ -55,7 +55,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-saml/CHANGELOG.md b/packages/connectors/connector-saml/CHANGELOG.md index 3abfe03a9d0..dabc6162ef2 100644 --- a/packages/connectors/connector-saml/CHANGELOG.md +++ b/packages/connectors/connector-saml/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-saml +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-saml/package.json b/packages/connectors/connector-saml/package.json index 440defa1d65..7e84b900cf4 100644 --- a/packages/connectors/connector-saml/package.json +++ b/packages/connectors/connector-saml/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-saml", - "version": "1.1.1", + "version": "1.1.2", "description": "SAML standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "fast-xml-parser": "^4.3.6", "got": "^14.0.0", @@ -54,7 +54,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-sendgrid-email/CHANGELOG.md b/packages/connectors/connector-sendgrid-email/CHANGELOG.md index a6c4d6730db..421045fef2c 100644 --- a/packages/connectors/connector-sendgrid-email/CHANGELOG.md +++ b/packages/connectors/connector-sendgrid-email/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-sendgrid-email +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-sendgrid-email/README.md b/packages/connectors/connector-sendgrid-email/README.md index aa8c6b2b295..b35e1f87091 100644 --- a/packages/connectors/connector-sendgrid-email/README.md +++ b/packages/connectors/connector-sendgrid-email/README.md @@ -93,7 +93,7 @@ Here is an example of SendGrid connector template JSON. You can type in an email address and click on "Send" to see whether the settings can work before "Save and Done". -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in) +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/) ### Config types diff --git a/packages/connectors/connector-sendgrid-email/package.json b/packages/connectors/connector-sendgrid-email/package.json index f57a58a1a91..58231c9eb00 100644 --- a/packages/connectors/connector-sendgrid-email/package.json +++ b/packages/connectors/connector-sendgrid-email/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-sendgrid-email", - "version": "1.1.1", + "version": "1.1.2", "description": "SendGrid Email Service connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-smsaero/CHANGELOG.md b/packages/connectors/connector-smsaero/CHANGELOG.md index 72813750cc4..fe9faafda9c 100644 --- a/packages/connectors/connector-smsaero/CHANGELOG.md +++ b/packages/connectors/connector-smsaero/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-smsaero +## 1.2.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-smsaero/README.md b/packages/connectors/connector-smsaero/README.md index 572d4697fd9..fe4720d8397 100644 --- a/packages/connectors/connector-smsaero/README.md +++ b/packages/connectors/connector-smsaero/README.md @@ -5,13 +5,12 @@ The official Logto connector for SMSAero short message service. **Table of contents** - [SMSAero short message service connector](#smsaero-short-message-service-connector) - - [Register account](#register-account) - - [Set up senders' phone numbers](#set-up-senders-phone-numbers) - - [Get account credentials](#get-account-credentials) - - [Compose the connector JSON](#compose-the-connector-json) - - [Test SMSAero connector](#test-smsaero-connector) - - [Config types](#config-types) - - [Reference](#reference) + - [Register account](#register-account) + - [Get account credentials](#get-account-credentials) + - [Compose the connector JSON](#compose-the-connector-json) + - [Test SMSAero connector](#test-smsaero-connector) + - [Config types](#config-types) + - [Reference](#reference) ## Register account @@ -43,7 +42,7 @@ You can add multiple SMS connector templates for different cases. Here is an exa You can enter a phone number and click on "Send" to see whether the settings can work before "Save and Done". That's it. Don't forget -to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in). +to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/). ### Config types diff --git a/packages/connectors/connector-smsaero/package.json b/packages/connectors/connector-smsaero/package.json index fd87e00c55b..306eb38dbb3 100644 --- a/packages/connectors/connector-smsaero/package.json +++ b/packages/connectors/connector-smsaero/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-smsaero", - "version": "1.2.1", + "version": "1.2.2", "description": "SMSAero connector implementation.", "author": "Danil Tankov ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-smtp/CHANGELOG.md b/packages/connectors/connector-smtp/CHANGELOG.md index 2543676bcd0..999f63372ba 100644 --- a/packages/connectors/connector-smtp/CHANGELOG.md +++ b/packages/connectors/connector-smtp/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-smtp +## 1.1.3 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-smtp/README.md b/packages/connectors/connector-smtp/README.md index 025175c7ac5..1d659e83b59 100644 --- a/packages/connectors/connector-smtp/README.md +++ b/packages/connectors/connector-smtp/README.md @@ -61,7 +61,7 @@ To check "Sender Addresses", you can find the entrance on the left-side navigati You can type in an email address and click on "Send" to see whether the settings can work before "Save and Done". -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/email-connector/enable-email-sign-in/). ### Config types diff --git a/packages/connectors/connector-smtp/package.json b/packages/connectors/connector-smtp/package.json index 24c028f8aff..8fbd5f46869 100644 --- a/packages/connectors/connector-smtp/package.json +++ b/packages/connectors/connector-smtp/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-smtp", - "version": "1.1.2", + "version": "1.1.3", "description": "SMTP connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "nodemailer": "^6.9.9", @@ -12,7 +12,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-tencent-sms/CHANGELOG.md b/packages/connectors/connector-tencent-sms/CHANGELOG.md index 9e296ec2754..a0f03437d48 100644 --- a/packages/connectors/connector-tencent-sms/CHANGELOG.md +++ b/packages/connectors/connector-tencent-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-tencent-sms +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-tencent-sms/README.md b/packages/connectors/connector-tencent-sms/README.md index a473705e131..a382d7615ea 100644 --- a/packages/connectors/connector-tencent-sms/README.md +++ b/packages/connectors/connector-tencent-sms/README.md @@ -72,7 +72,7 @@ The official Logto connector for Tencent short message service. 你可以在「保存并完成」之前输入一个手机号码并点按「发送」来测试配置是否可以正常工作。 -大功告成!快去 [启用短信或邮件验证码登录](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in) +大功告成!快去 [启用短信或邮件验证码登录](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/) 吧。 ### 配置类型 diff --git a/packages/connectors/connector-tencent-sms/package.json b/packages/connectors/connector-tencent-sms/package.json index e089a1c222b..a0da5a6bcae 100644 --- a/packages/connectors/connector-tencent-sms/package.json +++ b/packages/connectors/connector-tencent-sms/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-tencent-sms", - "version": "1.1.1", + "version": "1.1.2", "description": "Tencent SMS connector implementation.", "author": "StringKe", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-twilio-sms/CHANGELOG.md b/packages/connectors/connector-twilio-sms/CHANGELOG.md index bafc7348638..71e7e4630f3 100644 --- a/packages/connectors/connector-twilio-sms/CHANGELOG.md +++ b/packages/connectors/connector-twilio-sms/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-twilio-sms +## 1.1.2 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/connectors/connector-twilio-sms/README.md b/packages/connectors/connector-twilio-sms/README.md index bd9a9cd0d19..2d0db29ef37 100644 --- a/packages/connectors/connector-twilio-sms/README.md +++ b/packages/connectors/connector-twilio-sms/README.md @@ -62,7 +62,7 @@ You can add multiple SMS connector templates for different cases. Here is an exa You can enter a phone number and click on "Send" to see whether the settings can work before "Save and Done". -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/enable-SMS-sign-in/). ### Config types diff --git a/packages/connectors/connector-twilio-sms/package.json b/packages/connectors/connector-twilio-sms/package.json index c42f2ce3766..976f3070249 100644 --- a/packages/connectors/connector-twilio-sms/package.json +++ b/packages/connectors/connector-twilio-sms/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-twilio-sms", - "version": "1.1.1", + "version": "1.1.2", "description": "Twilio SMS connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-wechat-native/CHANGELOG.md b/packages/connectors/connector-wechat-native/CHANGELOG.md index a8b63c8afc6..8a11df3eed8 100644 --- a/packages/connectors/connector-wechat-native/CHANGELOG.md +++ b/packages/connectors/connector-wechat-native/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-wechat-native +## 1.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.2.0 ### Minor Changes diff --git a/packages/connectors/connector-wechat-native/README.md b/packages/connectors/connector-wechat-native/README.md index 3c756b19447..1022e15ddb7 100644 --- a/packages/connectors/connector-wechat-native/README.md +++ b/packages/connectors/connector-wechat-native/README.md @@ -258,7 +258,7 @@ Add the following line to your `AndroidManifest.xml`: ## Test WeChat native connector -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once WeChat native connector is enabled, you can build and run your app to see if it works. @@ -494,7 +494,7 @@ src/main/kotlin/com/sample/app/wxapi/WXEntryActivity.kt ## 测试微信原生连接器 -大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在微信原生连接器启用后,你可以构建并运行你的应用看看是否生效。 diff --git a/packages/connectors/connector-wechat-native/package.json b/packages/connectors/connector-wechat-native/package.json index b249d33307b..9a68f2dc462 100644 --- a/packages/connectors/connector-wechat-native/package.json +++ b/packages/connectors/connector-wechat-native/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-wechat-native", - "version": "1.2.0", + "version": "1.2.1", "description": "WeChat native connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-wechat-web/CHANGELOG.md b/packages/connectors/connector-wechat-web/CHANGELOG.md index c30b52bafe5..f9206ebff28 100644 --- a/packages/connectors/connector-wechat-web/CHANGELOG.md +++ b/packages/connectors/connector-wechat-web/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-wechat-web +## 1.3.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-wechat-web/README.md b/packages/connectors/connector-wechat-web/README.md index 8881e014345..eb21d174768 100644 --- a/packages/connectors/connector-wechat-web/README.md +++ b/packages/connectors/connector-wechat-web/README.md @@ -77,7 +77,7 @@ Fill out the `scope` field with either 'snsapi_userinfo' or 'snsapi_base'. You c ### Test WeChat web connector -That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in). +That's it. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). Once WeChat web connector is enabled, you can sign in to your app again to see if it works. @@ -133,6 +133,6 @@ Once WeChat web connector is enabled, you can sign in to your app again to see i ### 测试微信网页连接器 -大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-social-sign-in)。 +大功告成。别忘了 [在登录体验中启用本连接器](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/)。 在微信原生连接器启用后,你可以构建并运行你的应用看看是否生效。 diff --git a/packages/connectors/connector-wechat-web/package.json b/packages/connectors/connector-wechat-web/package.json index 4af7a603140..07d99ad9280 100644 --- a/packages/connectors/connector-wechat-web/package.json +++ b/packages/connectors/connector-wechat-web/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-wechat-web", - "version": "1.3.0", + "version": "1.3.1", "description": "Wechat Web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/connectors/connector-wecom/CHANGELOG.md b/packages/connectors/connector-wecom/CHANGELOG.md index b96f0187cdd..56e23dc3fb2 100644 --- a/packages/connectors/connector-wecom/CHANGELOG.md +++ b/packages/connectors/connector-wecom/CHANGELOG.md @@ -1,5 +1,14 @@ # @logto/connector-wecom +## 0.2.1 + +### Patch Changes + +- Updated dependencies [6308ee185] +- Updated dependencies [15953609b] +- Updated dependencies [6308ee185] + - @logto/connector-kit@4.0.0 + ## 0.2.0 ### Minor Changes diff --git a/packages/connectors/connector-wecom/package.json b/packages/connectors/connector-wecom/package.json index 61ec7c224c9..b06794e294c 100644 --- a/packages/connectors/connector-wecom/package.json +++ b/packages/connectors/connector-wecom/package.json @@ -1,10 +1,10 @@ { "name": "@logto/connector-wecom", - "version": "0.2.0", + "version": "0.2.1", "description": "Wecom connector implementation.", "author": "Dove fork from Wechat Web connector", "dependencies": { - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "snakecase-keys": "^8.0.0", @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.6", diff --git a/packages/console/.parcelrc b/packages/console/.parcelrc index bdafd2c2bbe..eeade8d2890 100644 --- a/packages/console/.parcelrc +++ b/packages/console/.parcelrc @@ -6,7 +6,7 @@ "@parcel/transformer-svg-react" ], "*.{md,mdx}": [ - "@parcel/transformer-mdx" + "./parcel-transformer-mdx2.js" ] }, "compressors": { diff --git a/packages/console/.parcelrc.arm64 b/packages/console/.parcelrc.arm64 index 0bc273f8909..c31f639e17b 100644 --- a/packages/console/.parcelrc.arm64 +++ b/packages/console/.parcelrc.arm64 @@ -10,7 +10,7 @@ "@parcel/transformer-svg-react" ], "*.{md,mdx}": [ - "@parcel/transformer-mdx" + "./parcel-transformer-mdx2.js" ] }, "compressors": { diff --git a/packages/console/CHANGELOG.md b/packages/console/CHANGELOG.md index 379fa169d38..a07f5892216 100644 --- a/packages/console/CHANGELOG.md +++ b/packages/console/CHANGELOG.md @@ -1,5 +1,123 @@ # Change Log +## 1.16.0 + +### Minor Changes + +- eacec10ac: improve machine-to-machine application integration user experience + + - Display a role assignment modal to facilitate setting permissions for the newly created machine-to-machine app. + - In the role assignment modal, add a Logto icon to roles that carry the Logto Management API access permission, making it easier for users to select roles with Logto Management API access permission. + - Add a notification for machine-to-machine roles to guide users in using the machine-to-machine role by creating a machine-to-machine application. + - Improve machine-to-machine application integration guide. + +- 87615d58c: support machine-to-machine apps for organizations + + This feature allows machine-to-machine apps to be associated with organizations, and be assigned with organization roles. + + ### Console + + - Add a new "machine-to-machine" type to organization roles. All existing roles are now "user" type. + - You can manage machine-to-machine apps in the organization details page -> Machine-to-machine apps section. + - You can view the associated organizations in the machine-to-machine app details page. + + ### OpenID Connect grant + + The `client_credentials` grant type is now supported for organizations. You can use this grant type to obtain an access token for an organization. + + ### Management API + + A set of new endpoints are added to the Management API: + + - `/api/organizations/{id}/applications` to manage machine-to-machine apps. + - `/api/organizations/{id}/applications/{applicationId}` to manage a specific machine-to-machine app in an organization. + - `/api/applications/{id}/organizations` to view the associated organizations of a machine-to-machine app. + +- 061a30a87: support agree to terms polices for Logto’s sign-in experiences + + - Automatic: Users automatically agree to terms by continuing to use the service + - ManualRegistrationOnly: Users must agree to terms by checking a box during registration, and don't need to agree when signing in + - Manual: Users must agree to terms by checking a box during registration or signing in + +- ead51e555: add Ruby app guide +- ef21c7a99: support per-organization multi-factor authentication requirement + + An organization can now require its member to have multi-factor authentication (MFA) configured. If an organization has this requirement and a member does not have MFA configured, the member will not be able to fetch the organization access token. + +- 0ef712e4e: support Google One Tap configuration +- 15953609b: support the dynamic config rendering for connector multi-select configuration +- b52609a1e: add `hasPassword` to custom JWT user context +- efa884c40: feature: just-in-time user provisioning for organizations + + This feature allows users to automatically join the organization and be assigned roles upon their first sign-in through some authentication methods. You can set requirements to meet for just-in-time provisioning. + + ### Email domains + + New users will automatically join organizations with just-in-time provisioning if they: + + - Sign up with verified email addresses, or; + - Use social sign-in with verified email addresses. + + This applies to organizations that have the same email domain configured. + + To enable this feature, you can add email domain via the Management API or the Logto Console: + + - We added the following new endpoints to the Management API: + - `GET /organizations/{organizationId}/jit/email-domains` + - `POST /organizations/{organizationId}/jit/email-domains` + - `PUT /organizations/{organizationId}/jit/email-domains` + - `DELETE /organizations/{organizationId}/jit/email-domains/{emailDomain}` + - In the Logto Console, you can manage email domains in the organization details page -> "Just-in-time provisioning" section. + + ### SSO connectors + + New or existing users signing in through enterprise SSO for the first time will automatically join organizations that have just-in-time provisioning configured for the SSO connector. + + To enable this feature, you can add SSO connectors via the Management API or the Logto Console: + + - We added the following new endpoints to the Management API: + - `GET /organizations/{organizationId}/jit/sso-connectors` + - `POST /organizations/{organizationId}/jit/sso-connectors` + - `PUT /organizations/{organizationId}/jit/sso-connectors` + - `DELETE /organizations/{organizationId}/jit/sso-connectors/{ssoConnectorId}` + - In the Logto Console, you can manage SSO connectors in the organization details page -> "Just-in-time provisioning" section. + + ### Default organization roles + + You can also configure the default roles for users provisioned via this feature. The default roles will be assigned to the user when they are provisioned. + + To enable this feature, you can set the default roles via the Management API or the Logto Console: + + - We added the following new endpoints to the Management API: + - `GET /organizations/{organizationId}/jit/roles` + - `POST /organizations/{organizationId}/jit/roles` + - `PUT /organizations/{organizationId}/jit/roles` + - `DELETE /organizations/{organizationId}/jit/roles/{organizationRoleId}` + - In the Logto Console, you can manage default roles in the organization details page -> "Just-in-time provisioning" section. + +- b50ba0b7e: enable backchannel logout support + + Enable the support of [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html). + + To register for backchannel logout, navigate to the application details page in the Logto Console and locate the "Backchannel logout" section. Enter the backchannel logout URL of your RP and click "Save". + + You can also enable session requirements for backchannel logout. When enabled, Logto will include the `sid` claim in the logout token. + + For programmatic registration, you can set the `backchannelLogoutUri` and `backchannelLogoutSessionRequired` properties in the application `oidcClientMetadata` object. + +### Patch Changes + +- 9f33d997b: view and update user's `profile` property in the user settings page +- 06ef19905: fix a regression bug that error toasts pop up in audit log when logs are associated with deleted applications +- af44e87eb: add Chrome extension guide +- 136320584: allow skipping manual account linking during sign-in + + You can find this configuration in Console -> Sign-in experience -> Sign-up and sign-in -> Social sign-in -> Automatic account linking. + + When switched on, if a user signs in with a social identity that is new to the system, and there is exactly one existing account with the same identifier (e.g., email), Logto will automatically link the account with the social identity instead of prompting the user for account linking. + +- d81e13d21: display OIDC issuer endpoint in the application details form + ## 1.15.0 ### Minor Changes diff --git a/packages/console/package.json b/packages/console/package.json index b99bd8554fe..cda9fc37676 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,6 +1,6 @@ { "name": "@logto/console", - "version": "1.15.0", + "version": "1.16.0", "description": "> TODO: description", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", @@ -27,21 +27,21 @@ "devDependencies": { "@fontsource/roboto-mono": "^5.0.0", "@jest/types": "^29.5.0", - "@logto/cloud": "0.2.5-38aae44", - "@logto/connector-kit": "workspace:^3.0.0", + "@logto/cloud": "0.2.5-a7eedce", + "@logto/connector-kit": "workspace:^4.0.0", "@logto/core-kit": "workspace:^2.5.0", "@logto/language-kit": "workspace:^1.1.0", - "@logto/phrases": "workspace:^1.11.0", - "@logto/phrases-experience": "workspace:^1.6.1", - "@logto/react": "^3.0.8", - "@logto/schemas": "workspace:^1.17.0", + "@logto/phrases": "workspace:^1.12.0", + "@logto/phrases-experience": "workspace:^1.7.0", + "@logto/react": "^3.0.12", + "@logto/schemas": "workspace:^1.18.0", "@logto/shared": "workspace:^3.1.1", - "@mdx-js/react": "^1.6.22", + "@mdx-js/mdx": "^3.0.1", + "@mdx-js/react": "^3.0.1", "@monaco-editor/react": "^4.6.0", "@parcel/compressor-brotli": "2.9.3", "@parcel/compressor-gzip": "2.9.3", "@parcel/core": "2.9.3", - "@parcel/transformer-mdx": "2.9.3", "@parcel/transformer-sass": "2.9.3", "@parcel/transformer-svg-react": "2.9.3", "@silverhand/eslint-config": "6.0.1", @@ -51,12 +51,11 @@ "@silverhand/ts-config-react": "6.0.0", "@swc/core": "^1.3.52", "@swc/jest": "^0.2.26", - "@testing-library/react": "^15.0.0", + "@testing-library/react": "^16.0.0", "@types/color": "^3.0.3", "@types/debug": "^4.1.7", "@types/jest": "^29.4.0", - "@types/mdx": "^2.0.1", - "@types/mdx-js__react": "^1.5.5", + "@types/mdx": "^2.0.13", "@types/react": "^18.0.31", "@types/react-color": "^3.0.6", "@types/react-dom": "^18.0.0", @@ -64,7 +63,7 @@ "@types/react-modal": "^3.13.1", "@types/react-syntax-highlighter": "^15.5.1", "@withtyped/client": "^0.8.7", - "buffer": "^5.7.1", + "buffer": "^6.0.0", "classnames": "^2.3.1", "clean-deep": "^3.4.0", "cross-env": "^7.0.3", @@ -78,7 +77,7 @@ "eslint": "^8.56.0", "history": "^5.3.0", "i18next": "^22.4.15", - "i18next-browser-languagedetector": "^7.0.1", + "i18next-browser-languagedetector": "^8.0.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.0.0", @@ -88,6 +87,7 @@ "ky": "^1.2.3", "libphonenumber-js": "^1.10.51", "lint-staged": "^15.0.0", + "mermaid": "^10.9.1", "nanoid": "^5.0.1", "overlayscrollbars": "^2.0.2", "overlayscrollbars-react": "^0.5.0", @@ -117,6 +117,7 @@ "react-syntax-highlighter": "^15.5.0", "react-timer-hook": "^3.0.5", "recharts": "^2.1.13", + "rehype-mdx-code-props": "^3.0.1", "remark-gfm": "^4.0.0", "stylelint": "^15.0.0", "swr": "^2.2.0", @@ -139,8 +140,7 @@ }, "alias": { "@/*": "./src/$1", - "@cloud/*": "./src/cloud/$1", - "@mdx/components/*": "./src/mdx-components/$1" + "@cloud/*": "./src/cloud/$1" }, "stylelint": { "extends": "@silverhand/eslint-config-react/.stylelintrc" diff --git a/packages/console/parcel-transformer-mdx2.js b/packages/console/parcel-transformer-mdx2.js new file mode 100644 index 00000000000..bd005897575 --- /dev/null +++ b/packages/console/parcel-transformer-mdx2.js @@ -0,0 +1,61 @@ +// https://github.com/parcel-bundler/parcel/pull/7922#issuecomment-1750704973 + +import { compile } from '@mdx-js/mdx'; +import { default as ThrowableDiagnostic } from '@parcel/diagnostic'; +import { Transformer } from '@parcel/plugin'; +import rehypeMdxCodeProps from 'rehype-mdx-code-props'; +import remarkGfm from 'remark-gfm'; + +export default new Transformer({ + async transform({ asset }) { + const source = await asset.getCode(); + + let codeVFile; + + try { + codeVFile = await compile(source, { + development: true, + jsx: true, + providerImportSource: '@mdx-js/react', + remarkPlugins: [remarkGfm], + rehypePlugins: [[rehypeMdxCodeProps, { tagName: 'code' }]], + }); + } catch (error) { + const { start, end } = error.position; + + const highlight = { + message: error.reason, + start, + end, + }; + + if (!(end.line && end.column)) { + highlight.end = { ...start }; + } + + // Adjust for parser and reporter differences + highlight.start.column -= 1; + highlight.end.column -= 1; + + throw new ThrowableDiagnostic({ + diagnostic: { + message: 'Unable to compile MDX', + codeFrames: [ + { + filePath: asset.filePath, + code: source, + codeHighlights: [highlight], + }, + ], + }, + }); + } + + const code = String(codeVFile); + + asset.type = 'jsx'; + asset.setCode(code); + + return [asset]; + }, +}); diff --git a/packages/console/src/App.tsx b/packages/console/src/App.tsx index c2d8fdd49f4..96ff5ed713f 100644 --- a/packages/console/src/App.tsx +++ b/packages/console/src/App.tsx @@ -31,6 +31,7 @@ import AppConfirmModalProvider from './contexts/AppConfirmModalProvider'; import AppDataProvider, { AppDataContext } from './contexts/AppDataProvider'; import { AppThemeProvider } from './contexts/AppThemeProvider'; import TenantsProvider, { TenantsContext } from './contexts/TenantsProvider'; +import Toast from './ds-components/Toast'; import useCurrentUser from './hooks/use-current-user'; import initI18n from './i18n/init'; @@ -86,6 +87,7 @@ function Providers() { UserScope.Identities, UserScope.CustomData, UserScope.Organizations, + UserScope.OrganizationRoles, PredefinedScope.All, ...conditionalArray( isCloud && [ @@ -111,6 +113,7 @@ function Providers() { > + {/** diff --git a/packages/console/src/assets/docs/fragments/_checkpoint.md b/packages/console/src/assets/docs/fragments/_checkpoint.md index 2abf8cde580..49d2692bc90 100644 --- a/packages/console/src/assets/docs/fragments/_checkpoint.md +++ b/packages/console/src/assets/docs/fragments/_checkpoint.md @@ -2,5 +2,5 @@ Now, you can test your application: 1. Run your application, you will see the sign-in button. 2. Click the sign-in button, the SDK will init the sign-in process and redirect you to the Logto sign-in page. -3. After you signed in, you will be redirected back to your application and see user data and the sign-out button. +3. After you signed in, you will be redirected back to your application and see the sign-out button. 4. Click the sign-out button to sign out. diff --git a/packages/console/src/assets/docs/fragments/_experience-overview.mdx b/packages/console/src/assets/docs/fragments/_experience-overview.mdx new file mode 100644 index 00000000000..829bdb8d8fc --- /dev/null +++ b/packages/console/src/assets/docs/fragments/_experience-overview.mdx @@ -0,0 +1,17 @@ +import RegardingRedirectBasedSignIn from './_regarding-redirect-based-sign-in.md'; + +Before we dive into the details, here's a quick overview of the end-user experience. The sign-in process can be simplified as follows: + +```mermaid +graph LR + A(Your app) -->|1. Invoke sign-in| B(Logto) + B -->|2. Finish sign-in| A +``` + +1. Your app invokes the sign-in method. +2. The user is redirected to the Logto sign-in page. For native apps, the system browser is opened. +3. The user signs in and is redirected back to your app (configured as the redirect URI). + + + +--- diff --git a/packages/console/src/assets/docs/fragments/_redirect-uris-native.mdx b/packages/console/src/assets/docs/fragments/_redirect-uris-native.mdx new file mode 100644 index 00000000000..a9f73712888 --- /dev/null +++ b/packages/console/src/assets/docs/fragments/_redirect-uris-native.mdx @@ -0,0 +1,12 @@ +import InlineNotification from '@/ds-components/InlineNotification'; +import UriInputField from '@/mdx-components/UriInputField'; + +import ExperienceOverview from './_experience-overview.mdx'; + +export const defaultRedirectUri = 'io.logto://callback'; + + + +Now, let's configure your redirect URI. E.g. {`${props.defaultUri ?? defaultRedirectUri}`}. + + diff --git a/packages/console/src/assets/docs/fragments/_redirect-uris-web.mdx b/packages/console/src/assets/docs/fragments/_redirect-uris-web.mdx new file mode 100644 index 00000000000..5c4b30cfe47 --- /dev/null +++ b/packages/console/src/assets/docs/fragments/_redirect-uris-web.mdx @@ -0,0 +1,22 @@ +import InlineNotification from '@/ds-components/InlineNotification'; +import UriInputField from '@/mdx-components/UriInputField'; + +import ExperienceOverview from './_experience-overview.mdx'; + +export const defaultBaseUrl = 'http://localhost:3000/'; +export const defaultRedirectUri = `${defaultBaseUrl}callback`; +export const defaultPostSignOutUri = defaultBaseUrl; + + + + + In the following steps, we assume your app is running on {props.defaultBaseUrl || defaultBaseUrl}. + + +Now, let's configure your redirect URI. E.g. {`${props.defaultRedirectUri || defaultRedirectUri}`}. + + + +Just like signing in, users should be redirected to Logto for signing out of the shared session. Once finished, it would be great to redirect the user back to your website. For example, add {`${props.defaultPostSignOutUri || defaultPostSignOutUri}`} as the post sign-out redirect URI below. + + diff --git a/packages/console/src/assets/docs/fragments/_regarding-redirect-based-sign-in.md b/packages/console/src/assets/docs/fragments/_regarding-redirect-based-sign-in.md new file mode 100644 index 00000000000..8a1194e2e79 --- /dev/null +++ b/packages/console/src/assets/docs/fragments/_regarding-redirect-based-sign-in.md @@ -0,0 +1,9 @@ +
+Regarding redirect-based sign-in + +1. This authentication process follows the [OpenID Connect (OIDC)](https://openid.net/specs/openid-connect-core-1_0.html) protocol, and Logto enforces strict security measures to protect user sign-in. +2. If you have multiple apps, you can use the same identity provider (Logto). Once the user signs in to one app, Logto will automatically complete the sign-in process when the user accesses another app. + +To learn more about the rationale and benefits of redirect-based sign-in, see [Logto sign-in experience explained](../../docs/tutorials/get-started/sign-in-experience.mdx). + +
diff --git a/packages/console/src/assets/docs/guides/api-express/README.mdx b/packages/console/src/assets/docs/guides/api-express/README.mdx index 7c0b04da852..1a635c50f2a 100644 --- a/packages/console/src/assets/docs/guides/api-express/README.mdx +++ b/packages/console/src/assets/docs/guides/api-express/README.mdx @@ -1,5 +1,5 @@ -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; @@ -28,8 +28,7 @@ To proceed, you'll need to integrate the Logto SDK into your client application. You'll also need to tweak the Logto SDK configuration to inform Logto that you want to request an access token for your API in this grant. Here's an example using React: -
-  
+
   {`import { LogtoProvider } from '@logto/react';
 
 const App = () => {
@@ -44,13 +43,11 @@ const App = () => {
     
   );
 };`}
-  
-
+ Once a user signs in with Logto, `isAuthenticated` within the Logto SDK will become `true`: -
-  
+
   {`import { useLogto } from '@logto/react';
 
 const Content = () => {
@@ -58,13 +55,11 @@ const Content = () => {
 
   console.log(isAuthenticated); // true
 };`}
-  
-
+ Now, you can use the `getAccessToken` method to retrieve an access token for your API: -
-  
+
   {`const Content = () => {
   const { getAccessToken, isAuthenticated } = useLogto();
 
@@ -75,13 +70,11 @@ Now, you can use the `getAccessToken` method to retrieve an access token for you
     }
   }, [isAuthenticated, getAccessToken]);
 };`}
-  
-
+ Lastly, include this access token in the `Authorization` header when making requests to your API: -
-  
+
   {`const Content = () => {
   const { getAccessToken, isAuthenticated } = useLogto();
 
@@ -97,8 +90,7 @@ Lastly, include this access token in the `Authorization` header when making requ
     }
   }, [isAuthenticated, getAccessToken]);
 };`}
-  
-
+ @@ -150,8 +142,7 @@ const extractBearerTokenFromHeaders = ({ authorization }: IncomingHttpHeaders) = Subsequently, create a middleware to verify the access token: -
-  
+
   {`import { createRemoteJWKSet, jwtVerify } from 'jose';
 
 // Generate a JWKS using jwks_uri obtained from the Logto server
@@ -181,8 +172,7 @@ export const authMiddleware = async (req, res, next) => {
 
   return next();
 };`}
-  
-
+ You can now employ this middleware to protect your API endpoints: @@ -210,8 +200,7 @@ To address this, we can employ role-based access control (RBAC). In Logto, you c After defining roles and permissions, you can add the `scopes` option to the `LogtoProvider` component: -
-  
+
   {``}
-  
-
+ Logto will then only issue an access token with the appropriate scope(s) to the user. For instance, if a user only has the `read:products` scope, the access token will solely contain that scope: diff --git a/packages/console/src/assets/docs/guides/api-python/README.mdx b/packages/console/src/assets/docs/guides/api-python/README.mdx index 621abc9275c..d166c36f691 100644 --- a/packages/console/src/assets/docs/guides/api-python/README.mdx +++ b/packages/console/src/assets/docs/guides/api-python/README.mdx @@ -7,9 +7,7 @@ import { appendPath } from '@silverhand/essentials'; -```python -"""requires-auth.py -""" +```python title="requires-auth.py" def get_auth_token(): auth = request.headers.get("Authorization", None) @@ -41,30 +39,22 @@ pip install python-jose[ecdsa] ### Retrieve Logto's OIDC configurations -

You will need a JWK public key set and the token issuer to verify the signature and source of the received JWS token. -All the latest public Logto Authorization Configurations can be found at {appendPath(props.endpoint, '/oidc/.well-known/openid-configuration')}. +All the latest public Logto Authorization Configurations can be found at {appendPath(props.endpoint, '/oidc/.well-known/openid-configuration').href}. e.g. You can locate the following two fields in the response body if you request the above endpoint. -

-
-  
+
 {`{
   "issuer": "${appendPath(props.endpoint, '/oidc')}",
   "jwks_uri": "${appendPath(props.endpoint, '/oidc/jwks')}"
 }`}
-  
-
+ ### Create the authorization validation decorator -
-  
-{`"""requires-auth.py
-"""
-
-import json
+
+{`import json
 from flask import request,  _request_ctx_stack
 from six.moves.urllib.request import urlopen
 from functools import wraps
@@ -105,11 +95,12 @@ def requires_auth(f):
 
     return f(*args, **kwargs)
   return decorated`}
-  
-
+ + +
- For 🔐 RBAC, scope validation is also required. + For 🔐 RBAC, scope validation is also required.
diff --git a/packages/console/src/assets/docs/guides/api-spring-boot/README.mdx b/packages/console/src/assets/docs/guides/api-spring-boot/README.mdx index 161da637afb..50b6ee27737 100644 --- a/packages/console/src/assets/docs/guides/api-spring-boot/README.mdx +++ b/packages/console/src/assets/docs/guides/api-spring-boot/README.mdx @@ -1,5 +1,5 @@ -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; @@ -35,9 +35,10 @@ dependencies { Since Spring Boot and Spring Security have built-in support for both OAuth2 resource server and JWT validation, you DO NOT need to add additional libraries from Logto to integrate. - See [Spring Security OAuth 2.0 Resource Server](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html) - and [Spring Security Architecture](https://spring.io/guides/topicals/spring-security-architecture) - for more details. +See [Spring Security OAuth 2.0 Resource Server](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html) +and [Spring Security Architecture](https://spring.io/guides/topicals/spring-security-architecture) +for more details. + @@ -50,22 +51,20 @@ and signed with [JWK](https://datatracker.ietf.org/doc/html/rfc7517) Before moving on, you will need to get an issuer and a JWKS URI to verify the issuer and the signature of the Bearer Token (`access_token`). -

-All the Logto Authorization server configurations can be found by requesting {appendPath(props.endpoint, '/oidc/.well-known/openid-configuration')}, including the issuer, jwks_uri and other authorization configs. -

+All the Logto Authorization server configurations can be found by requesting{' '} +{appendPath(props.endpoint, '/oidc/.well-known/openid-configuration').href}, including the{' '} +issuer, jwks_uri and other authorization configs. An example of the response: -
-  
-{`{
+
+    {`{
   // ...
   "issuer": "${appendPath(props.endpoint, '/oidc')}",
   "jwks_uri": "${appendPath(props.endpoint, '/oidc/jwks')}"
   // ...
 }`}
-  
-
+ @@ -73,10 +72,8 @@ An example of the response: Use an `application.yml` file (instead of the default `application.properties`) to configure the server port, audience, and OAuth2 resource server. -
-
-{`# path/to/project/src/main/resources/application.yaml
-server:
+
+    {`server:
   port: 3000
 
 logto:
@@ -89,8 +86,7 @@ spring:
         jwt:
           issuer-uri: ${appendPath(props.endpoint, '/oidc')}
           jwk-set-uri: ${appendPath(props.endpoint, '/oidc/jwks')}`}
-
-
+ - `audience`: The unique API identifier of your protected API resource. - `spring.security.oauth2.resourceserver.jwt.issuer-uri`: The iss claim value and the issuer URI in the JWT issued by Logto. @@ -102,8 +98,7 @@ spring: Provide your own `AudienceValidator` class that implements the `OAuth2TokenValidator` interface to validate whether the required audience is present in the JWT. -```java -// path/to/project/src/main/java/io/logto/springboot/sample/validator/AudienceValidator.java +```java title="validator/AudienceValidator.java" package io.logto.springboot.sample.validator; import org.springframework.security.oauth2.core.OAuth2Error; @@ -113,8 +108,6 @@ import org.springframework.security.oauth2.jwt.Jwt; public class AudienceValidator implements OAuth2TokenValidator { - private final OAuth2Error oAuth2Error = new OAuth2Error("invalid_token", "Required audience not found", null); - private final String audience; public AudienceValidator(String audience) { @@ -124,18 +117,21 @@ public class AudienceValidator implements OAuth2TokenValidator { @Override public OAuth2TokenValidatorResult validate(Jwt jwt) { if (!jwt.getAudience().contains(audience)) { - return OAuth2TokenValidatorResult.failure(oAuth2Error); + return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", "Required audience not found", null)); } + // Optional: For RBAC validate the scopes of the JWT. + String scopes = jwt.getClaimAsString("scope"); + if (scopes == null || !scopes.contains("read:profile")) { + return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", "Insufficient permission", null)); + } + + return OAuth2TokenValidatorResult.success(); } } ``` - - For 🔐 RBAC, scope validation is also required. - - @@ -144,8 +140,7 @@ Spring Security makes it easy to configure your application as a resource server You need to provide instances of `JwtDecoder` and `SecurityFilterChain` (as Spring beans), and add the `@EnableWebSecurity` annotation. -```java -// path/to/project/src/main/java/io/logto/springboot/sample/configuration/SecurityConfiguration.java +```java title="configuration/SecurityConfiguration.java" package io.logto.springboot.sample.configuration; import com.nimbusds.jose.JOSEObjectType; @@ -154,17 +149,19 @@ import com.nimbusds.jose.proc.SecurityContext; import io.logto.springboot.sample.validator.AudienceValidator; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidator; -import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtValidators; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; -import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; +import org.springframework.security.web.DefaultSecurityFilterChain; +@Configuration @EnableWebSecurity public class SecurityConfiguration { @@ -180,6 +177,8 @@ public class SecurityConfiguration { @Bean public JwtDecoder jwtDecoder() { NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwksUri) + // Logto uses the ES384 algorithm to sign the JWTs by default. + .jwsAlgorithm(ES384) // The decoder should support the token type: Access Token + JWT. .jwtProcessorCustomizer(customizer -> customizer.setJWSTypeVerifier( new DefaultJOSEObjectTypeVerifier(new JOSEObjectType("at+jwt")))) @@ -194,14 +193,17 @@ public class SecurityConfiguration { } @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt).cors().and() - .authorizeRequests(customizer -> customizer - // Only authenticated requests can access your protected APIs - .mvcMatchers("/", "/secret").authenticated() - // Anyone can access the public profile. - .mvcMatchers("/profile").permitAll() - ); + public DefaultSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .securityMatcher("/api/**") + .oauth2ResourceServer(oauth2 -> oauth2 + .jwt(Customizer.withDefaults())) + .authorizeHttpRequests(requests -> requests + // Allow all requests to the public APIs. + .requestMatchers("/api/.wellknown/**").permitAll() + // Require jwt token validation for the protected APIs. + .anyRequest().authenticated()); + return http.build(); } } @@ -213,8 +215,7 @@ public class SecurityConfiguration { Add a controller to provide the protected and public APIs: -```java -// path/to/project/src/main/java/io/logto/springboot/sample/controller/ProtectedController.java +```java title="controller/ProtectedController.java" package io.logto.springboot.sample.controller; import org.springframework.web.bind.annotation.CrossOrigin; @@ -226,20 +227,14 @@ import org.springframework.web.bind.annotation.RestController; @CrossOrigin(origins = "*") @RestController public class ProtectedController { - - @GetMapping("/") - public String protectedRoot() { - return "Protected root."; - } - - @GetMapping("/secret") - public String protectedSecret() { - return "Protected secret."; + @GetMapping("/api/profile") + public String protectedProfile() { + return "Protected profile."; } - @GetMapping("/profile") - public String publicProfile() { - return "Public profile."; + @GetMapping("/api/.wellknown/config.json") + public String publicConfig() { + return "Public config."; } } ``` @@ -272,12 +267,10 @@ gradlew.bat bootRun Request your protected API with the Access Token as the Bearer token in the Authorization header, e.g. execute the `curl` command. -
-  
-  {`curl --include '${appendPath(props.endpoint, '/secret')}' \\
+
+    {`curl --include '${appendPath(props.endpoint, '/api/profile')}' \\
 --header 'Authorization: Bearer '`}
-  
-
+ If successful, you will get a response with 200 status: @@ -298,7 +291,7 @@ WWW-Authenticate: Bearer error="invalid_token", error_description="An error occu
- + - [Protect your API](https://docs.logto.io/docs/recipes/protect-your-api/) - [Spring Security OAuth 2.0 Resource Server](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html) diff --git a/packages/console/src/assets/docs/guides/generate-metadata.js b/packages/console/src/assets/docs/guides/generate-metadata.js index a0be81e5738..d1ca726d995 100644 --- a/packages/console/src/assets/docs/guides/generate-metadata.js +++ b/packages/console/src/assets/docs/guides/generate-metadata.js @@ -20,8 +20,7 @@ const data = await Promise.all( return; } - // Add `.png` later - const logo = ['logo.svg'].find((logo) => existsSync(`${directory}/${logo}`)); + const logo = ['logo.webp', 'logo.svg', 'logo.png'].find((logo) => existsSync(`${directory}/${logo}`)); const config = existsSync(`${directory}/config.json`) ? await import(`./${directory}/config.json`, { assert: { type: 'json' } }).then( @@ -42,20 +41,31 @@ const metadata = data .sort((a, b) => a.order - b.order); const camelCase = (value) => value.replaceAll(/-./g, (x) => x[1].toUpperCase()); -const filename = 'index.ts'; +const filename = 'index.tsx'; await fs.writeFile( filename, "// This is a generated file, don't update manually.\n\nimport { lazy } from 'react';\n\nimport { type Guide } from './types';\n" ); -for (const { name } of metadata) { +for (const { name, logo } of metadata) { // eslint-disable-next-line no-await-in-loop await fs.appendFile(filename, `import ${camelCase(name)} from './${name}/index';\n`); + + if (logo && !logo.endsWith('.svg')) { + // eslint-disable-next-line no-await-in-loop + await fs.appendFile(filename, `import ${camelCase(name)}Logo from './${name}/${logo}';\n`); + } } await fs.appendFile(filename, '\n'); -await fs.appendFile(filename, 'const guides: Readonly = Object.freeze(['); +await fs.appendFile(filename, 'export const guides: Readonly = Object.freeze(['); + +const getLogo = ({ name, logo }) => { + if (!logo) return 'undefined'; + if (logo.endsWith('.svg')) return `lazy(async () => import('./${name}/${logo}'))`; + return `({ className }: { readonly className?: string }) => ${name}`; +}; for (const { name, logo, order } of metadata) { // eslint-disable-next-line no-await-in-loop @@ -65,11 +75,11 @@ for (const { name, logo, order } of metadata) { { order: ${order}, id: '${name}', - Logo: ${logo ? `lazy(async () => import('./${name}/${logo}'))` : 'undefined'}, + Logo: ${getLogo({ name, logo })}, Component: lazy(async () => import('./${name}/README.mdx')), metadata: ${camelCase(name)}, },` ); } -await fs.appendFile(filename, ']);\n\nexport default guides;\n'); +await fs.appendFile(filename, ']);\n'); diff --git a/packages/console/src/assets/docs/guides/index.ts b/packages/console/src/assets/docs/guides/index.tsx similarity index 92% rename from packages/console/src/assets/docs/guides/index.ts rename to packages/console/src/assets/docs/guides/index.tsx index a9a4f38a577..d83d09817d4 100644 --- a/packages/console/src/assets/docs/guides/index.ts +++ b/packages/console/src/assets/docs/guides/index.tsx @@ -12,6 +12,7 @@ import nativeExpo from './native-expo/index'; import nativeFlutter from './native-flutter/index'; import nativeIosSwift from './native-ios-swift/index'; import spaAngular from './spa-angular/index'; +import spaChromeExtension from './spa-chrome-extension/index'; import spaReact from './spa-react/index'; import spaVanilla from './spa-vanilla/index'; import spaVue from './spa-vue/index'; @@ -33,11 +34,19 @@ import webNuxt from './web-nuxt/index'; import webOutline from './web-outline/index'; import webPhp from './web-php/index'; import webPython from './web-python/index'; -import webRemix from './web-remix/index'; +import webRuby from './web-ruby/index'; +import webRubyLogo from './web-ruby/logo.webp'; import webSveltekit from './web-sveltekit/index'; import webWordpress from './web-wordpress/index'; -const guides: Readonly = Object.freeze([ +export const guides: Readonly = Object.freeze([ + { + order: 1, + id: 'web-next-app-router', + Logo: lazy(async () => import('./web-next-app-router/logo.svg')), + Component: lazy(async () => import('./web-next-app-router/README.mdx')), + metadata: webNextAppRouter, + }, { order: 1.1, id: 'native-expo', @@ -52,6 +61,13 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./spa-angular/README.mdx')), metadata: spaAngular, }, + { + order: 1.1, + id: 'spa-chrome-extension', + Logo: lazy(async () => import('./spa-chrome-extension/logo.svg')), + Component: lazy(async () => import('./spa-chrome-extension/README.mdx')), + metadata: spaChromeExtension, + }, { order: 1.1, id: 'spa-react', @@ -59,13 +75,6 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./spa-react/README.mdx')), metadata: spaReact, }, - { - order: 1.1, - id: 'web-next-app-router', - Logo: lazy(async () => import('./web-next-app-router/logo.svg')), - Component: lazy(async () => import('./web-next-app-router/README.mdx')), - metadata: webNextAppRouter, - }, { order: 1.2, id: 'm2m-general', @@ -115,13 +124,6 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./web-java-spring-boot/README.mdx')), metadata: webJavaSpringBoot, }, - { - order: 1.5, - id: 'web-gpt-plugin', - Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')), - Component: lazy(async () => import('./web-gpt-plugin/README.mdx')), - metadata: webGptPlugin, - }, { order: 1.6, id: 'spa-vue', @@ -164,6 +166,15 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./web-php/README.mdx')), metadata: webPhp, }, + { + order: 2, + id: 'web-ruby', + Logo: ({ className }: { readonly className?: string }) => ( + web-ruby + ), + Component: lazy(async () => import('./web-ruby/README.mdx')), + metadata: webRuby, + }, { order: 2.1, id: 'spa-webflow', @@ -192,13 +203,6 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./native-capacitor/README.mdx')), metadata: nativeCapacitor, }, - { - order: 4, - id: 'web-remix', - Logo: lazy(async () => import('./web-remix/logo.svg')), - Component: lazy(async () => import('./web-remix/README.mdx')), - metadata: webRemix, - }, { order: 5, id: 'native-flutter', @@ -241,6 +245,13 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./web-outline/README.mdx')), metadata: webOutline, }, + { + order: 999, + id: 'web-gpt-plugin', + Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')), + Component: lazy(async () => import('./web-gpt-plugin/README.mdx')), + metadata: webGptPlugin, + }, { order: Number.POSITIVE_INFINITY, id: 'api-express', @@ -270,5 +281,3 @@ const guides: Readonly = Object.freeze([ metadata: thirdPartyOidc, }, ]); - -export default guides; diff --git a/packages/console/src/assets/docs/guides/m2m-general/README.mdx b/packages/console/src/assets/docs/guides/m2m-general/README.mdx index 97be012e070..21392ee3b24 100644 --- a/packages/console/src/assets/docs/guides/m2m-general/README.mdx +++ b/packages/console/src/assets/docs/guides/m2m-general/README.mdx @@ -1,10 +1,12 @@ -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; import ApplicationCredentials from '@/mdx-components/ApplicationCredentials'; import AppIdentifierSrc from './assets/api-identifier.png'; +import AssignM2mRolesModalSrc from './assets/assign-m2m-roles-modal.png'; +import AssignM2mRolesPageSrc from './assets/assign-m2m-roles-page.png'; import LogtoManagementApiSrc from './assets/logto-management-api.png'; @@ -19,6 +21,14 @@ There are two common use cases of using machine-to-machine apps in Logto: 1. **Accessing Logto Management API**: In this case, you need to assign a M2M role that include the `all` permission from the built-in Logto Management API to your M2M app. 2. **Accessing your API resource**: In this case, you need to assign M2M roles that include permissions from your API resources to your M2M app. +During the M2M app creation process, you’ll be directed to a page where you can assign machine-to-machine (M2M) roles to your applications: + +Assign M2M roles modal + +Or you can also assign these roles on the M2M app detail page. + +M2M app details page + @@ -67,6 +77,10 @@ The resource API indicator is in the pattern of `https://[your-tenant-id].logto. Before accessing Logto Management API, make sure your M2M app has been assigned with M2M roles that include the `all` permission from this built-in “Logto Management API” resource. + +Logto also provides a pre-configured “Logto Management API access” M2M role for new created tenants, which the Logto Management API resource’s all permission has already assigned to. You can use it directly without manually setting permissions. This pre-configured role can also be edited and deleted as needed. + + Now, compose all we have and send the request: diff --git a/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-modal.png b/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-modal.png new file mode 100644 index 00000000000..2ce64235f35 Binary files /dev/null and b/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-modal.png differ diff --git a/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-page.png b/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-page.png new file mode 100644 index 00000000000..15ed9e768e5 Binary files /dev/null and b/packages/console/src/assets/docs/guides/m2m-general/assets/assign-m2m-roles-page.png differ diff --git a/packages/console/src/assets/docs/guides/m2m-general/index.ts b/packages/console/src/assets/docs/guides/m2m-general/index.ts index 00c1a10c629..4ad62a3e5b1 100644 --- a/packages/console/src/assets/docs/guides/m2m-general/index.ts +++ b/packages/console/src/assets/docs/guides/m2m-general/index.ts @@ -7,10 +7,7 @@ const metadata: Readonly = Object.freeze({ description: 'Enables direct communication between machines.', target: ApplicationType.MachineToMachine, isFeatured: true, - fullGuide: { - title: 'Full machine-to-machine integration tutorial', - url: 'https://docs.logto.io/quick-starts/m2m', - }, + fullGuide: 'm2m', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/native-android/README.mdx b/packages/console/src/assets/docs/guides/native-android/README.mdx index b6a966f5613..80020ff5ea7 100644 --- a/packages/console/src/assets/docs/guides/native-android/README.mdx +++ b/packages/console/src/assets/docs/guides/native-android/README.mdx @@ -2,6 +2,8 @@ import UriInputField from '@/mdx-components/UriInputField'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import RedirectUrisNative from '../../fragments/_redirect-uris-native.mdx'; +import Checkpoint from '../../fragments/_checkpoint.md'; @@ -14,17 +16,17 @@ import Step from '@/mdx-components/Step'; Before you install Logto Android SDK, ensure `mavenCentral()` is added to your repository configuration in the Gradle project build file: -```kotlin -dependencyResolutionManagement { + +{`dependencyResolutionManagement { repositories { mavenCentral() } -} -``` +}`} + Add Logto Android SDK to your dependencies: -```kotlin +```kotlin title="build.gradle.kts" dependencies { implementation("io.logto.sdk:android:1.1.3") } @@ -32,7 +34,7 @@ dependencies { Since the SDK needs internet access, you need to add the following permission to your `AndroidManifest.xml` file: -```xml +```xml title="AndroidManifest.xml" @@ -46,18 +48,14 @@ Since the SDK needs internet access, you need to add the following permission to - + We use Kotlin in this example, but the concepts are the same for Java. Create a `LogtoViewModel.kt` and init `LogtoClient` in this view model: -
-  
-    {`//...with other imports
+```kotlin title="LogtoViewModel.kt"
+//...with other imports
 import io.logto.sdk.android.LogtoClient
 import io.logto.sdk.android.type.LogtoConfig
 
@@ -85,13 +83,12 @@ class LogtoViewModel(application: Application) : AndroidViewModel(application) {
             }
         }
     }
-}`}
-  
-
+} +``` then, create a `LogtoViewModel` for your `MainActivity.kt`: -```kotlin +```kotlin title="MainActivity.kt" //...with other imports class MainActivity : AppCompatActivity() { private val logtoViewModel: LogtoViewModel by viewModels { LogtoViewModel.Factory } @@ -101,12 +98,9 @@ class MainActivity : AppCompatActivity() {
- + -Before starting, you need to add a redirect URI in the Admin Console for your application. + In Android, the redirect URI follows the pattern: `$(LOGTO_REDIRECT_SCHEME)://$(YOUR_APP_PACKAGE)/callback`: @@ -115,98 +109,15 @@ In Android, the redirect URI follows the pattern: `$(LOGTO_REDIRECT_SCHEME)://$( Assuming you treat `io.logto.android` as the custom `LOGTO_REDIRECT_SCHEME`, and `io.logto.sample` is your app package name, the Redirect URI should be `io.logto.android://io.logto.sample/callback`. -You can add the redirect URI in the following input field: - - - -After the redirect URI is configured, we add a `signIn` method to your `LogtoViewModel.kt`, which will call `logtoClient.signIn` API to invoke the Logto sign-in page: - -
-  
-    {`//...with other imports
-class LogtoViewModel(application: Application) : AndroidViewModel(application) {
-    // ...other codes
-    fun signIn(context: Activity) {
-        logtoClient.signIn(context, "${props.redirectUris[0] ?? ''}") { logtoException ->
-            logtoException?.let { println(it) }
-        }
-    }
-}`}
-  
-
- -Now setup on-click listener for the sign-in button in your `MainActivity.kt` to call the `signIn` method: - -```kotlin -//...with other imports -class MainActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - //...other codes - - // Assume you have a button with id `sign_in_button` in your layout - val signInButton = findViewById
{{ userData | json }}
+

ID token: {{ idToken }}

Access token: {{ accessToken }}

@@ -160,12 +135,4 @@ And use it in the template:
- - - - - - diff --git a/packages/console/src/assets/docs/guides/spa-angular/index.ts b/packages/console/src/assets/docs/guides/spa-angular/index.ts index ef02c1ced7d..3073c73b72a 100644 --- a/packages/console/src/assets/docs/guides/spa-angular/index.ts +++ b/packages/console/src/assets/docs/guides/spa-angular/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'js', path: 'packages/angular-sample', }, - fullGuide: { - title: 'Full Angular guide', - url: 'https://docs.logto.io/quick-starts/angular', - }, + fullGuide: 'angular', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/README.mdx b/packages/console/src/assets/docs/guides/spa-chrome-extension/README.mdx new file mode 100644 index 00000000000..98d22ecee13 --- /dev/null +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/README.mdx @@ -0,0 +1,238 @@ +import UriInputField from '@/mdx-components/UriInputField'; +import InlineNotification from '@/ds-components/InlineNotification'; +import Steps from '@/mdx-components/Steps'; +import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + +import RegardingRedirectBasedSignIn from '../../fragments/_regarding-redirect-based-sign-in.md'; + +import extensionPopup from './extension-popup.webp'; + + + + + + + + + + + +Assuming you put a "Sign in" button in your Chrome extension's popup, the authentication flow will look like this: + +```mermaid +sequenceDiagram + participant A as Extension popup + participant B as Extension service worker + participant C as Logto sign-in experience + + A->>B: Invokes sign-in + B->>C: Redirects to Logto + C->>C: User signs in + C->>B: Redirects back to extension + B->>A: Notifies the popup +``` + +For other interactive pages in your extension, you just need to replace the `Extension popup` participant with the page's name. In this tutorial, we will focus on the popup page. + + + + + + + +### Update the `manifest.json` + +Logto SDK requires the following permissions in the `manifest.json`: + +```json title="manifest.json" +{ + "permissions": ["identity", "storage"], + "host_permissions": ["https://*.logto.app/*"] +} +``` + +- `permissions.identity`: Required for the Chrome Identity API, which is used to sign in and sign out. +- `permissions.storage`: Required for storing the user's session. +- `host_permissions`: Required for the Logto SDK to communicate with the Logto APIs. + + +If you are using a custom domain on Logto Cloud, you need to update the `host_permissions` to match your domain. + + +### Set up a background script (service worker) + +In your Chrome extension's background script, initialize the Logto SDK: + +```js title="service-worker.js" +import LogtoClient from '@logto/chrome-extension'; + +export const logtoClient = new LogtoClient({ + endpoint: '' + appId: '', +}); +``` + +Replace `` and `` with the actual values. You can find these values in the application page you just created in the Logto Console. + +If you don't have a background script, you can follow the [official guide](https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/basics) to create one. + + +**Why do we need a background script?** + +Normal extension pages like the popup or options page can't run in the background, and they have the possibility to be closed during the authentication process. A background script ensures the authentication process can be properly handled. + + +Then, we need to listen to the message from other extension pages and handle the authentication process: + +```js title="service-worker.js" +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + // In the below code, since we return `true` for each action, we need to call `sendResponse` + // to notify the sender. You can also handle errors here, or use other ways to notify the sender. + + if (message.action === 'signIn') { + const redirectUri = chrome.identity.getRedirectURL('/callback'); + logtoClient.signIn(redirectUri).finally(sendResponse); + return true; + } + + if (message.action === 'signOut') { + const redirectUri = chrome.identity.getRedirectURL(); + logtoClient.signOut(redirectUri).finally(sendResponse); + return true; + } + + return false; +}); +``` + +You may notice there are two redirect URIs used in the code above. They are both created by `chrome.identity.getRedirectURL`, which is a [built-in Chrome API](https://developer.chrome.com/docs/extensions/reference/api/identity#method-getRedirectURL) to generate a redirect URL for auth flows. The two URIs will be: + +- `https://.chromiumapp.org/callback` for sign-in. +- `https://.chromiumapp.org/` for sign-out. + +Note that these URIs are not accessible, and they are only used for Chrome to trigger specific actions for the authentication process. + + + + + +As we mentioned in the previous step, we need to update the Logto application settings to allow the redirect URIs we just created (`https://.chromiumapp.org/callback`): + + + +And the post sign-out redirect URI (`https://.chromiumapp.org/`): + + + +Finally, the CORS allowed origins should include the extension's origin (`chrome-extension://`). The SDK in Chrome extension will use this origin to communicate with the Logto APIs. + + + +Don't forget to replace `` with your actual extension ID and click the "Save" button. + + + + + +We're almost there! Let's add the sign-in and sign-out buttons and other necessary logic to the popup page. + +In the `popup.html` file: + +```html title="popup.html" + +``` + +In the `popup.js` file (assuming `popup.js` is included in the `popup.html`): + +```js title="popup.js" +document.getElementById('sign-in').addEventListener('click', async () => { + await chrome.runtime.sendMessage({ action: 'signIn' }); + // Sign-in completed (or failed), you can update the UI here. +}); + +document.getElementById('sign-out').addEventListener('click', async () => { + await chrome.runtime.sendMessage({ action: 'signOut' }); + // Sign-out completed (or failed), you can update the UI here. +}); +``` + + + + + +Now you can test the authentication flow in your Chrome extension: + +1. Open the extension popup. +2. Click on the "Sign in" button. +3. You will be redirected to the Logto sign-in page. +4. Sign in with your Logto account. +5. You will be redirected back to the Chrome. + + + + + +Since Chrome provide unified storage APIs, rather than the sign-in and sign-out flow, all other Logto SDK methods can be used in the popup page directly. + +In your `popup.js`, you can reuse the `LogtoClient` instance created in the background script, or create a new one with the same configuration: + +```js title="popup.js" +import LogtoClient from '@logto/chrome-extension'; + +const logtoClient = new LogtoClient({ + endpoint: '' + appId: '', +}); + +// Or reuse the logtoClient instance created in the background script +import { logtoClient } from './service-worker.js'; +``` + +Then you can create a function to load the authentication state and user's profile: + +```js title="popup.js" +const loadAuthenticationState = async () => { + const isAuthenticated = await logtoClient.isAuthenticated(); + // Update the UI based on the authentication state + + if (isAuthenticated) { + const user = await logtoClient.getIdTokenClaims(); // { sub: '...', email: '...', ... } + // Update the UI with the user's profile + } +}; +``` + +You can also combine the `loadAuthenticationState` function with the sign-in and sign-out logic: + +```js title="popup.js" +document.getElementById('sign-in').addEventListener('click', async () => { + await chrome.runtime.sendMessage({ action: 'signIn' }); + await loadAuthenticationState(); +}); + +document.getElementById('sign-out').addEventListener('click', async () => { + await chrome.runtime.sendMessage({ action: 'signOut' }); + await loadAuthenticationState(); +}); +``` + +Here's an example of the popup page with the authentication state: + +Popup page + + + + + +- **Service worker bundling**: If you use a bundler like Webpack or Rollup, you need to explicitly set the target to `browser` or similar to avoid unnecessary bundling of Node.js modules. +- **Module resolution**: Logto Chrome extension SDK is an ESM-only module. + +See our [sample project](https://github.com/logto-io/js/tree/HEAD/packages/chrome-extension-sample) for a complete example with TypeScript, Rollup, and other configurations. + + + + diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/config.json b/packages/console/src/assets/docs/guides/spa-chrome-extension/config.json new file mode 100644 index 00000000000..4721ad2f793 --- /dev/null +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/config.json @@ -0,0 +1,3 @@ +{ + "order": 1.1 +} diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/extension-popup.webp b/packages/console/src/assets/docs/guides/spa-chrome-extension/extension-popup.webp new file mode 100644 index 00000000000..9e136c06e25 Binary files /dev/null and b/packages/console/src/assets/docs/guides/spa-chrome-extension/extension-popup.webp differ diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/index.ts b/packages/console/src/assets/docs/guides/spa-chrome-extension/index.ts new file mode 100644 index 00000000000..90ad8eb36eb --- /dev/null +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/index.ts @@ -0,0 +1,16 @@ +import { ApplicationType } from '@logto/schemas'; + +import { type GuideMetadata } from '../types'; + +const metadata: Readonly = Object.freeze({ + name: 'Chrome extension', + description: 'Build a Chrome extension with Logto.', + target: ApplicationType.SPA, + sample: { + repo: 'js', + path: 'packages/chrome-extension-sample', + }, + fullGuide: 'chrome-extension', +}); + +export default metadata; diff --git a/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg b/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg new file mode 100644 index 00000000000..4fc53255e9c --- /dev/null +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/spa-react/README.mdx b/packages/console/src/assets/docs/guides/spa-react/README.mdx index 8f684b45e82..ce910680ca0 100644 --- a/packages/console/src/assets/docs/guides/spa-react/README.mdx +++ b/packages/console/src/assets/docs/guides/spa-react/README.mdx @@ -1,9 +1,11 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb, { defaultRedirectUri, defaultPostSignOutUri } from '../../fragments/_redirect-uris-web.mdx'; @@ -11,37 +13,16 @@ import Step from '@/mdx-components/Step'; title="Installation" subtitle="Install Logto SDK for your project" > - - - -```bash -npm i @logto/react -``` - - - - -```bash -yarn add @logto/react -``` - - - -```bash -pnpm add @logto/react -``` + - -
- + -Import and use `LogtoProvider` to provide a Logto context: +Import and use `LogtoProvider` to provide a Logto context to your app: -
-  
+
     {`import { LogtoProvider, LogtoConfig } from '@logto/react';
 
 const config: LogtoConfig = {
@@ -54,59 +35,23 @@ const App = () => (
     
   
 );`}
-  
-
+
- - - - In the following steps, we assume your app is running on http://localhost:3000. - - -### Configure Redirect URI + -First, let’s enter your redirect URI. E.g. `http://localhost:3000/callback`. + - - -### Implement a sign-in button - -We provide two hooks `useHandleSignInCallback()` and `useLogto()` which can help you easily manage the authentication flow. - -Go back to your IDE/editor, use the following code to implement the sign-in button: - -
-  
-    {`import { useLogto } from '@logto/react';
-
-const SignIn = () => {
-  const { signIn, isAuthenticated } = useLogto();
-
-  if (isAuthenticated) {
-    return 
Signed in
; - } - - return ( - - ); -};`} -
-
+
-### Handle redirect + -We're almost there! In the last step, we use `http://localhost:3000/callback` as the Redirect URI, and now we need to handle it properly. +After the user signs in, Logto will redirect the user back to the redirect URI configured above. However, there are still things to do to make your application work properly. -First let's create a callback component: +First let's create a callback page: -```tsx +```tsx title="pages/Callback/index.tsx" import { useHandleSignInCallback } from '@logto/react'; const Callback = () => { @@ -118,92 +63,99 @@ const Callback = () => { if (isLoading) { return
Redirecting...
; } + + return null; }; ``` -Finally insert the code below to create a `/callback` route which does NOT require authentication: +Then, insert the code below to create a `/callback` route which does NOT require authentication: -```tsx +```tsx title="App.tsx" // Assuming react-router } /> ```
- - -Calling `.signOut()` will clear all the Logto data in memory and localStorage if they exist. + -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below, and use it as the parameter when calling `.signOut()`. +We provide a hook `useLogto()` which can help you easily manage the authentication flow. - + + Before calling `.signIn()`, make sure you have correctly configured Redirect URI in Admin Console. + -### Implement a sign-out button + + {`import { useLogto } from '@logto/react'; -
-  
-    {`const SignOut = () => {
-  const { signOut } = useLogto();
+const Home = () => {
+  const { signIn, signOut, isAuthenticated } = useLogto();
 
-  return (
-    
+  return isAuthenticated ? (
+    
+  ) : (
+    
   );
 };`}
-  
-
+
+ +Calling `.signOut()` will clear all the Logto data in memory and localStorage if they exist. + +
+ + + + - + -In Logto SDK, generally we can use `logtoClient.isAuthenticated` to check the authentication status, if the user is signed in, the value will be `true`, otherwise, the value will be `false`. +To display the user's information, you can use the `getIdTokenClaims()` method. For example, in your Home page: -In Logto React SDK, the `isAuthenticated` status can be checked by using the `useLogto` hook. In the example code below, we can use it to programmatically show and hide the sign-in and sign-out buttons. And also use `getIdTokenClaims` to get the id of the currently logged-in user. +```tsx title="pages/Home/index.tsx" +import { useLogto, type IdTokenClaims } from '@logto/react'; +import { useEffect, useState } from 'react'; -```tsx const Home = () => { - const { isAuthenticated, getIdTokenClaims, signIn, signOut } = useLogto(); - const [userId, setUserId] = useState(''); + const { isAuthenticated, getIdTokenClaims } = useLogto(); + const [user, setUser] = useState(); useEffect(() => { (async () => { if (isAuthenticated) { const claims = await getIdTokenClaims(); - setUserId(claims.sub); + setUser(claims); } })(); - }, [isAuthenticated]); + }, [getIdTokenClaims, isAuthenticated]); return ( -
- {userId &&

Logged in as {userId}

} - {isAuthenticated ? ( - - ) : ( - - )} -
+ // ... + {isAuthenticated && user && ( + + + + + + + + + {Object.entries(user).map(([key, value]) => ( + + + + + ))} + +
NameValue
{key}{typeof value === 'string' ? value : JSON.stringify(value)}
+ )} ); -}; +} ```
- - -Now, you can test your application: - -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, the SDK will init the sign-in process and redirect you to the Logto sign-in page. -3. After you signed in, you will be redirected back to your application and see user ID and the sign-out button. -4. Click the sign-out button to sign-out. - - - diff --git a/packages/console/src/assets/docs/guides/spa-react/index.ts b/packages/console/src/assets/docs/guides/spa-react/index.ts index 53fdeeff520..b560d8dd4ee 100644 --- a/packages/console/src/assets/docs/guides/spa-react/index.ts +++ b/packages/console/src/assets/docs/guides/spa-react/index.ts @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ path: 'packages/react-sample', }, isFeatured: true, - fullGuide: { - title: 'Full React SDK tutorial', - url: 'https://docs.logto.io/quick-starts/react', - }, + fullGuide: 'react', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/spa-vanilla/README.mdx b/packages/console/src/assets/docs/guides/spa-vanilla/README.mdx index d0848d2b299..cff41a900ec 100644 --- a/packages/console/src/assets/docs/guides/spa-vanilla/README.mdx +++ b/packages/console/src/assets/docs/guides/spa-vanilla/README.mdx @@ -1,10 +1,13 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb, { defaultRedirectUri, defaultPostSignOutUri } from '../../fragments/_redirect-uris-web.mdx'; + - + ```bash -yarn add @logto/browser +pnpm add @logto/browser ``` - + ```bash -pnpm add @logto/browser +yarn add @logto/browser ``` + + + +```html + +``` @@ -40,133 +51,108 @@ pnpm add @logto/browser Import and init `LogtoClient` with configs: -
-  
+
     {`import LogtoClient from '@logto/browser';
 
 const logtoClient = new LogtoClient({
   endpoint: '${props.endpoint}',
   appId: '${props.app.id}',
 });`}
-  
-
+
- - - - In the following steps, we assume your app is running on http://localhost:3000. - - -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/callback`. - - - -### Implement a sign-in button - -Go back to your IDE/editor, use the following code to implement the sign-in button: - -
-  
-    {``}
-  
-
- -### Handle redirect + -We're almost there! In the last step, we use `http://localhost:3000/callback` as the Redirect URI, and now we need to handle it properly. - -Insert the code below in your `/callback` route: - -```ts -await logtoClient.handleSignInCallback(window.location.href); - -if (!logtoClient.isAuthenticated) { - // Handle failed sign-in - alert('Failed to sign in'); - return; -} - -// Handle successful sign-in. E.g. redirect to home page. -window.location.assign('http://localhost:3000/'); -``` - -Now you can test the sign-in flow. + - - -Calling `.signOut()` will clear all the Logto data in memory and localStorage if they exist. + -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below, and use it as the parameter when calling `.signOut()`. +There are still things to do after the user is redirected back to your application from Logto. Let's handle it properly. - +```ts title="pages/Callback.js" +const callbackHandler = async (logtoClient) => { + await logtoClient.handleSignInCallback(window.location.href); -### Implement a sign-out button + if (!logtoClient.isAuthenticated) { + // Handle failed sign-in + alert('Failed to sign in'); + return; + } -
-  
-    {``}
-  
-
+ // Handle successful sign-in + window.location.assign('/'); +}; +```
- - -In Logto SDK, generally we can use `logtoClient.isAuthenticated` to check the authentication status, if the user is signed in, the value will be `true`, otherwise, the value will be `false`. + -In your vanilla JS app, you can use the `isAuthenticated` status to programmatically show and hide the sign-in and sign-out buttons. Let's see how to do it. +`logtoClient` provides `signIn` and `signOut` methods to help you easily manage the authentication flow. -```ts -const redirectUrl = 'http://localhost:3000/callback'; -const baseUrl = 'http://localhost:3000'; + + {`const isAuthenticated = await logtoClient.isAuthenticated(); -// Conditional rendering of sign-in and sign-out buttons -const isAuthenticated = await logtoClient.isAuthenticated(); - -// Assuming there's a div with id 'container' in your HTML -const container = document.querySelector('#container'); - -const onClickSignIn = () => logtoClient.signIn(redirectUrl); -const onClickSignOut = () => logtoClient.signOut(baseUrl); +const onClickSignIn = () => { + logtoClient.signIn('${props.redirectUris[0] ?? defaultRedirectUri}'); +}; +const onClickSignOut = () => { + logtoClient.signOut('${props.postLogoutRedirectUris[0] ?? defaultPostSignOutUri}'); +}; const button = document.createElement('button'); button.innerHTML = isAuthenticated ? 'Sign Out' : 'Sign In'; button.addEventListener('click', isAuthenticated ? onClickSignOut : onClickSignIn); -container.append(button); -``` +document.body.appendChild(button);`} + + +Calling `.signOut()` will clear all the Logto data in memory and `localStorage` if they exist. -Now, you can test your application: + -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, the SDK will init the sign-in process and redirect you to the Logto sign-in page. -3. After you signed in, you will be redirected back to your application and see user ID and the sign-out button. -4. Click the sign-out button to sign-out. + + + + +To display the user's information, you can use the `logtoClient.getIdTokenClaims()` method. For example, in your Home page: + +```js title="pages/Home.js" +const userInfo = await logtoClient.getIdTokenClaims(); + +// Generate display table for ID token claims +const table = document.createElement('table'); +const thead = document.createElement('thead'); +const tr = document.createElement('tr'); +const thName = document.createElement('th'); +const thValue = document.createElement('th'); +thName.innerHTML = 'Name'; +thValue.innerHTML = 'Value'; +tr.append(thName, thValue); +thead.append(tr); +table.append(thead); + +const tbody = document.createElement('tbody'); + +for (const [key, value] of Object.entries(userInfo)) { + const tr = document.createElement('tr'); + const tdName = document.createElement('td'); + const tdValue = document.createElement('td'); + tdName.innerHTML = key; + tdValue.innerHTML = typeof value === 'string' ? value : JSON.stringify(value); + tr.append(tdName, tdValue); + tbody.append(tr); +} + +table.append(tbody); +``` diff --git a/packages/console/src/assets/docs/guides/spa-vanilla/index.ts b/packages/console/src/assets/docs/guides/spa-vanilla/index.ts index 48198f3cc2d..a75ff94be90 100644 --- a/packages/console/src/assets/docs/guides/spa-vanilla/index.ts +++ b/packages/console/src/assets/docs/guides/spa-vanilla/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'js', path: 'packages/browser-sample', }, - fullGuide: { - title: 'Full vanilla JS SDK tutorial', - url: 'https://docs.logto.io/quick-starts/vanilla-js', - }, + fullGuide: 'vanilla-js', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/spa-vue/README.mdx b/packages/console/src/assets/docs/guides/spa-vue/README.mdx index b9a63997747..a522828ac43 100644 --- a/packages/console/src/assets/docs/guides/spa-vue/README.mdx +++ b/packages/console/src/assets/docs/guides/spa-vue/README.mdx @@ -1,9 +1,12 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb from '../../fragments/_redirect-uris-web.mdx'; +import { defaultRedirectUri, defaultPostSignOutUri } from '../../fragments/_redirect-uris-web.mdx'; @@ -11,29 +14,9 @@ import Step from '@/mdx-components/Step'; title="Installation" subtitle="Install Logto SDK for your project" > - - - -```bash -npm i @logto/vue -``` - - - - -```bash -yarn add @logto/vue -``` - - + -```bash -pnpm add @logto/vue -``` - - - - We only support Vue 3 Composition API at this point. Will add support to Vue Options API and - possibly Vue 2 in future releases. + Logto Vue SDK is built with Vue 3 composition API. Therefore, only Vue 3 is supported at the moment. Contact us if you want to add support for Vue 2. Import and use `createLogto` to install Logto plugin: -
-  
+
     {`import { createLogto, LogtoConfig } from '@logto/vue';
+import { createApp } from 'vue';
+import App from './App.vue';
 
 const config: LogtoConfig = {
   endpoint: '${props.endpoint}',
@@ -60,69 +43,29 @@ const app = createApp(App);
 
 app.use(createLogto, config);
 app.mount("#app");`}
-  
-
+
- - - - In the following steps, we assume your app is running on http://localhost:3000. - - -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/callback`. + - + -### Implement a sign-in button - -We provide two composables `useHandleSignInCallback()` and `useLogto()`, which can help you easily manage the authentication flow. - -Go back to your IDE/editor, use the following code to implement the sign-in button: + -
-
-{``}
+There are still things to do after the user is redirected back to your application from Logto. Let's handle it properly.
 
-
-
+First let's create a callback page: -```html - -``` +```ts title="views/CallbackView.vue" +import { useHandleSignInCallback } from '@logto/vue'; +import router from '@/router'; -### Handle redirect - -We're almost there! In the last step, we use `http://localhost:3000/callback` as the Redirect URI, and now we need to handle it properly. - -First let's create a callback component: - -```html - - +const { isLoading } = useHandleSignInCallback(() => { + // Do something when finished, e.g. redirect to home page +}); ``` ```html @@ -132,9 +75,9 @@ First let's create a callback component: ``` -Finally insert the code below to create a `/callback` route which does NOT require authentication: +Insert the code below in your `/callback` route which does NOT require authentication: -```ts +```ts title="router/index.ts" // Assuming vue-router const router = createRouter({ routes: [ @@ -149,80 +92,70 @@ const router = createRouter({
- + -Calling `.signOut()` will clear all the Logto data in memory and localStorage if they exist. +We provide a composable `useLogto()` which can help you easily manage the authentication flow. -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below, and use it as the parameter when calling `.signOut()`. + + {`import { useLogto } from '@logto/vue'; - +const { signIn, signOut, isAuthenticated } = useLogto(); -### Implement a sign-out button +const onClickSignIn = () => signIn('${props.redirectUris[0] || defaultRedirectUri}'); +const onClickSignOut = () => signOut('${props.postLogoutRedirectUris[0] || defaultPostSignOutUri}'); +`} + -
-
-{``}
+
 
-
-
+ -```html - -``` + - - -In Logto SDK, generally we can use `logtoClient.isAuthenticated` to check the authentication status, if the user is signed in, the value will be `true`, otherwise, the value will be `false`. + -In Logto Vue SDK, the `isAuthenticated` status can be checked by using the `useLogto` composable. In the example code below, we can use it to programmatically show and hide the sign-in and sign-out buttons. Also we'll use `getIdTokenClaims` to get the ID of the currently logged-in user. +To display the user's information, you can use the `getIdTokenClaims()` method. For example, in your Home page: -```tsx -import { useLogto } from "@logto/vue"; -import { ref } from "vue"; +```ts title="views/HomeView.vue" +import { useLogto, type IdTokenClaims } from '@logto/vue'; +import { ref } from 'vue'; -const { isAuthenticated, getIdTokenClaims, signIn, signOut } = useLogto(); -const userId = ref(); +const { isAuthenticated, getIdTokenClaims } = useLogto(); +const user = ref(); if (isAuthenticated.value) { (async () => { const claims = await getIdTokenClaims(); - userId.value = claims.sub; + user.value = claims; })(); } ``` ```html ``` - - -Now, you can test your application: - -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, the SDK will init the sign-in process and redirect you to the Logto sign-in page. -3. After you signed in, you will be redirected back to your application and see user ID and the sign-out button. -4. Click the sign-out button to sign-out. - - - diff --git a/packages/console/src/assets/docs/guides/spa-vue/index.ts b/packages/console/src/assets/docs/guides/spa-vue/index.ts index 71f6553bcd8..195ceebed9e 100644 --- a/packages/console/src/assets/docs/guides/spa-vue/index.ts +++ b/packages/console/src/assets/docs/guides/spa-vue/index.ts @@ -12,10 +12,7 @@ const metadata: Readonly = Object.freeze({ path: 'packages/vue-sample', }, isFeatured: true, - fullGuide: { - title: 'Full Vue SDK tutorial', - url: 'https://docs.logto.io/quick-starts/vue', - }, + fullGuide: 'vue', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/spa-webflow/README.mdx b/packages/console/src/assets/docs/guides/spa-webflow/README.mdx index 1ad4cf7cf04..ceca5e9683c 100644 --- a/packages/console/src/assets/docs/guides/spa-webflow/README.mdx +++ b/packages/console/src/assets/docs/guides/spa-webflow/README.mdx @@ -1,6 +1,6 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; +import TabItem from '@/mdx-components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; @@ -22,8 +22,7 @@ In this step, we'll add global-level custom code to your Webflow site. Since NPM Open the "Site settings" page, and navigate to the "Custom code" section. Add the following code to the "Head code" section. -
-  
+
     {``}
-  
-
+
@@ -59,15 +57,13 @@ First, let’s enter your redirect URI. E.g. `https://your-awesome-site.webflow. Return to your Webflow designer, drag and drop a "Sign in" button to the home page, and assign it an ID “sign-in” for later reference using `getElementById()`. -
-  
+
     {``}
-  
-
+ ### Handle redirect @@ -99,13 +95,11 @@ After signing out, it'll be great to redirect user back to your website. Let's a Return to the Webflow designer, and add a “Sign out” button on your home page. Similarly, assign an ID “sign-out” to the button, and add the following code to the page-level custom code. -
-  
+
     {`const signOutButton = document.getElementById('sign-out');
 const onClickSignOut = () => logtoClient.signOut('${props.postLogoutRedirectUris[0] ?? 'https://your-awesome-site.webflow.io'}');
 signOutButton.addEventListener('click', onClickSignOut);`}
-  
-
+
diff --git a/packages/console/src/assets/docs/guides/types.ts b/packages/console/src/assets/docs/guides/types.ts index 5f11825cd8b..0b18523c99e 100644 --- a/packages/console/src/assets/docs/guides/types.ts +++ b/packages/console/src/assets/docs/guides/types.ts @@ -31,18 +31,24 @@ export type GuideMetadata = { /** Indicate whether the application is for third-party use */ isThirdParty?: boolean; - /** The related complete guide for this guide which will be displayed in the 'Further readings' section. */ - fullGuide?: { + /** The related complete guide url relative to the quick starts page (https://docs.logto.io/quick-starts). */ + fullGuide?: string; + + /** The related URLs to add to the further readings section. */ + furtherReadings?: Array<{ title: string; - url: string; - }; + url: URL; + }>; }; /** The guide instance to build in the console. */ export type Guide = { + order: number; /** The unique identifier of the guide. */ id: string; - Logo: LazyExoticComponent; + Logo: + | LazyExoticComponent + | ((props: { readonly className?: string }) => JSX.Element); Component: LazyExoticComponent>; metadata: Readonly; }; diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/README.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/README.mdx index 1258ea8a865..20508bf92da 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/README.mdx +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/README.mdx @@ -1,52 +1,35 @@ -import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import SignInAndSignOutFlows from '../web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx'; +import ConfigureRedirectUris from '../web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx'; +import Installation from '../web-dotnet-core-mvc/fragments/_installation.md'; +import AddAuthentication from '../web-dotnet-core-mvc/fragments/_add-authentication.mdx'; +import DisplayUserInformation from '../web-dotnet-core-mvc/fragments/_display-user-information.md'; +import Checkpoint from '../../fragments/_checkpoint.md'; - + -This tutorial will show you how to use Logto ASP.NET Core authentication middleware to protect your web application. - -
    -
  • It assumes your website is hosted on {props.sampleUrls.origin}.
  • -
- -### Installation - -```bash -dotnet add package Logto.AspNetCore.Authentication -``` +
-Open `Startup.cs` (or `Program.cs`) and add the following code to register Logto authentication middleware: + -
-  
-{`using Logto.AspNetCore.Authentication;
+
 
-var builder = WebApplication.CreateBuilder(args);
+
 
-builder.Services.AddLogtoAuthentication(options =>
-{
-  options.Endpoint = "${props.endpoint}";
-  options.AppId = "${props.app.id}";
-  options.AppSecret = "${props.app.secret}";
-});
+
 
-app.UseAuthentication();`}
-  
-
+
-The `AddLogtoAuthentication` method will do the following things: + -- Set the default authentication scheme to `LogtoDefaults.CookieScheme`. -- Set the default challenge scheme to `LogtoDefaults.AuthenticationScheme`. -- Set the default sign-out scheme to `LogtoDefaults.AuthenticationScheme`. -- Add cookie and OpenID Connect authentication handlers to the authentication scheme. + @@ -56,7 +39,7 @@ Since Blazor Server uses SignalR to communicate between the server and the clien To make it right, we need to explicitly add two endpoints for sign-in and sign-out redirects: -```csharp +```csharp title="Program.cs" app.MapGet("/SignIn", async context => { if (!(context.User?.Identity?.IsAuthenticated ?? false)) @@ -82,35 +65,11 @@ Now we can redirect to these endpoints to trigger sign-in and sign-out.
- - -

-First, let's enter your redirect URI. E.g. {props.sampleUrls.origin + 'Callback'} (replace the endpoint with yours). This is where Logto will redirect users after they sign in. -

- - - -Remember to keep the path `/Callback` in the URI as it's the default value for the Logto authentication middleware. - ---- - -To clean up both ASP.NET session and Logto session, we can designate a post sign-out redierct URI. This is where Logto will redirect users after they sign out. - -

-For example, set the URI to {props.sampleUrls.origin + 'SignedOutCallback'} (replace the endpoint with yours): -

- - - -Remember to keep the path `/SignedOutCallback` in the URI as it's the default value for the Logto authentication middleware. - -
- In the Razor component, add the following code: -```cshtml +```cshtml title="Components/Pages/Index.razor" @using Microsoft.AspNetCore.Components.Authorization @using System.Security.Claims @inject AuthenticationStateProvider AuthenticationStateProvider @@ -160,41 +119,13 @@ The page will show the "Sign in" button if the user is not authenticated, and sh - - -Now you can run the web application and try to sign in and sign out with Logto: - -1. Open the web application in your browser, you should see "Is authenticated: False" and the "Sign in" button. -2. Click the "Sign in" button, and you should be redirected to the Logto sign-in page. -3. After you have signed in, you should be redirected back to the web application, and you should see "Is authenticated: True" and the "Sign out" button. -4. Click the "Sign out" button, and you should be redirected to the Logto sign-out page, and then redirected back to the web application. - - - - - -To know if the user is authenticated, you can check the `User.Identity?.IsAuthenticated` property. - -To get the user profile claims, you can use the `User.Claims` property: - -```csharp -var claims = User.Claims; - -// Get the user ID -var userId = claims.FirstOrDefault(c => c.Type == LogtoParameters.Claims.Subject)?.Value; -``` - -See the [full tutorial](https://docs.logto.io/quick-starts/dotnet-core/blazor-server/) for more details. - - - - + Alternatively, you can use the `AuthorizeView` component to conditionally render content based on the user's authentication state. This component is useful when you want to show different content to authenticated and unauthenticated users. In your Razor component, add the following code: -```cshtml +```cshtml title="Components/Pages/Index.razor" @using Microsoft.AspNetCore.Components.Authorization @* ... *@ @@ -214,7 +145,7 @@ In your Razor component, add the following code: The `AuthorizeView` component requires a cascading parameter of type `Task`. A direct way to get this parameter is to add the `` component. However, due to the nature of Blazor Server, we cannot simply add the component to the layout or the root component (it may not work as expected). Instead, we can add the following code to the builder (`Program.cs` or `Startup.cs`) to provide the cascading parameter: -```csharp +```csharp title="Program.cs" builder.Services.AddCascadingAuthenticationState(); ``` @@ -222,4 +153,16 @@ Then you can use the `AuthorizeView` component in every component that needs it. + + + + + + + + + + + +
diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/index.ts b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/index.ts index 9deb50ecb89..1e01259c368 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/index.ts +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'csharp', path: '/', }, - fullGuide: { - title: 'Full .NET Core (Blazor Server) integration tutorial', - url: 'https://docs.logto.io/quick-starts/dotnet-core/blazor-server', - }, + fullGuide: 'dotnet-core/blazor-server', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/README.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/README.mdx index 75718996401..1e1d62c3cb0 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/README.mdx +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/README.mdx @@ -2,15 +2,14 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb, { defaultRedirectUri, defaultPostSignOutUri } from '../../fragments/_redirect-uris-web.mdx'; + -This tutorial will show you how to use [Blorc.OpenIdConnect](https://github.com/WildGums/Blorc.OpenIdConnect) to add Logto authentication to a Blazor WebAssembly application. - -
    -
  • It assumes your website is hosted on {props.sampleUrls.origin}.
  • -
+This guide will show you how to use [Blorc.OpenIdConnect](https://github.com/WildGums/Blorc.OpenIdConnect) to add Logto authentication to a Blazor WebAssembly application. ### Installation @@ -26,7 +25,7 @@ dotnet add package Blorc.OpenIdConnect Include `Blorc.Core/injector.js` the `index.html` file: -```html +```html title="index.html" @@ -38,7 +37,7 @@ Include `Blorc.Core/injector.js` the `index.html` file: Add the following code to the `Program.cs` file: -```csharp +```csharp title="Program.cs" using Blorc.OpenIdConnect; using Blorc.Services; @@ -66,45 +65,29 @@ Note: There's no need to use the `Microsoft.AspNetCore.Components.WebAssembly.Au
- - -### Configure redirect URI - -

-First, let's enter your redirect URI. E.g. {props.sampleUrls.origin + 'Callback'} (replace the endpoint with yours). This is where Logto will redirect users after they sign in. -

- - - -### Configure post sign-out redirect URI - -To clean up both ASP.NET session and Logto session, we can designate a post sign-out redierct URI. This is where Logto will redirect users after they sign out. + -

-For example, set the URI to {props.sampleUrls.origin + 'SignedOutCallback'} (replace the endpoint with yours): -

+ - +
-### Configure application + Add the following code to the `appsettings.json` file: -
-  
+
 {`// ...
   IdentityServer: {
     Authority: '${props.endpoint}oidc',
     ClientId: '${props.app.id}',
-    RedirectUri: '${props.redirectUris[0] ?? props.sampleUrls.callback}',
-    PostLogoutRedirectUri: '${props.postLogoutRedirectUris[0] ?? props.sampleUrls.origin}',
+    RedirectUri: '${props.redirectUris[0] ?? defaultRedirectUri}',
+    PostLogoutRedirectUri: '${props.postLogoutRedirectUris[0] ?? defaultPostSignOutUri}',
     ResponseType: 'code',
     Scope: 'openid profile', // Add more scopes if needed
   },
 }
 `}
-    
-
+
@@ -114,7 +97,7 @@ Add the following code to the `appsettings.json` file: In the Razor pages that require authentication, add the `AuthorizeView` component. Let's assume it's the `Home.razor` page: -```cshtml +```cshtml title="Pages/Home.razor" @using Microsoft.AspNetCore.Components.Authorization @page "/" @@ -138,7 +121,7 @@ In the Razor pages that require authentication, add the `AuthorizeView` componen In the `Home.razor.cs` file (create it if it doesn't exist), add the following code: -```csharp +```csharp title="Pages/Home.razor.cs" using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; @@ -175,11 +158,19 @@ public partial class Home : ComponentBase Once the user is authenticated, the `User` property will be populated with the user information. -### Display user information +
+ + + + + + + + Here are some examples of how to display user information in the `Home.razor` page: -```cshtml +```cshtml title="Pages/Home.razor" @* Signed in view *@ @@ -194,23 +185,4 @@ For more properties and claims, check the `User` and `Profile` classes in the `B - - -Now you can run the web application and try to sign in and sign out with Logto: - -1. Open the web application in your browser, you should see "Is authenticated: False" and the "Sign in" button. -2. Click the "Sign in" button, and you should be redirected to the Logto sign-in page. -3. After you have signed in, you should be redirected back to the web application, and you should see "Is authenticated: True" and the "Sign out" button. -4. Click the "Sign out" button, and you should be redirected to the Logto sign-out page, and then redirected back to the web application. - - - - - -To get the user profile, you can use the `User?.Profile` property; to fetch the access token, you can use the `User?.AccessToken` property or add it to your HTTP client using `.AddAccessToken()`. - -See the [full tutorial](https://docs.logto.io/quick-starts/dotnet-core/blazor-wasm/) for more details. - - -
diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/index.ts b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/index.ts index 55770849b8c..1ad19eeeed5 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/index.ts +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'csharp', path: '/', }, - fullGuide: { - title: 'Full .NET Core (Blazor WASM) integration tutorial', - url: 'https://docs.logto.io/quick-starts/dotnet-core/blazor-wasm', - }, + fullGuide: 'dotnet-core/blazor-wasm', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/README.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/README.mdx index 8fa99eea24c..27f31597d8f 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/README.mdx +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/README.mdx @@ -2,143 +2,68 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; - - - +import Checkpoint from '../../fragments/_checkpoint.md'; -This tutorial will show you how to use Logto ASP.NET Core authentication middleware to protect your web application. +import SignInAndSignOutFlows from './fragments/_sign-in-and-sign-out-flows.mdx'; +import ConfigureRedirectUris from './fragments/_configure-redirect-uris.mdx'; +import Installation from './fragments/_installation.md'; +import AddAuthentication from './fragments/_add-authentication.mdx'; +import DisplayUserInformation from './fragments/_display-user-information.md'; -
    -
  • It assumes your website is hosted on {props.sampleUrls.origin}.
  • -
+ -### Installation + -```bash -dotnet add package Logto.AspNetCore.Authentication -``` + -Open `Startup.cs` (or `Program.cs`) and add the following code to register Logto authentication middleware: - -
-  
-{`using Logto.AspNetCore.Authentication;
-
-var builder = WebApplication.CreateBuilder(args);
+
 
-builder.Services.AddLogtoAuthentication(options =>
-{
-  options.Endpoint = "${props.endpoint}";
-  options.AppId = "${props.app.id}";
-  options.AppSecret = "${props.app.secret}";
-});
-
-app.UseAuthentication();`}
-  
-
+
-The `AddLogtoAuthentication` method will do the following things: + -- Set the default authentication scheme to `LogtoDefaults.CookieScheme`. -- Set the default challenge scheme to `LogtoDefaults.AuthenticationScheme`. -- Set the default sign-out scheme to `LogtoDefaults.AuthenticationScheme`. -- Add cookie and OpenID Connect authentication handlers to the authentication scheme. + - - -

-First, let's enter your redirect URI. E.g. {props.sampleUrls.origin + 'Callback'} (replace the endpoint with yours). This is where Logto will redirect users after they sign in. -

- - + -Remember to keep the path `/Callback` in the URI as it's the default value for the Logto authentication middleware. + -To sign-in with Logto, you can use the `Challenge` method of `ControllerBase`: + -```csharp -Challenge(new AuthenticationProperties -{ - // The URI below is different from the redirect URI you entered above. - // It's the URI where users will be redirected after successfully signed in. - // You can change it to any path you want. - RedirectUri = "/" -}); -``` + -For example, you can add the following code to the controller: +First, add actions methods to your `Controller`, for example: -```csharp +```csharp title="Controllers/HomeController.cs" public class HomeController : Controller { public IActionResult SignIn() { + // This will redirect the user to the Logto sign-in page. return Challenge(new AuthenticationProperties { RedirectUri = "/" }); } -} -``` - -And then add the following code to your View: - -```html -

Is authenticated: @User.Identity?.IsAuthenticated

-Sign in -``` - -
- - - -To clean up both ASP.NET session and Logto session, we can designate a post sign-out redierct URI. This is where Logto will redirect users after they sign out. - -

-For example, set the URI to {props.sampleUrls.origin + 'SignedOutCallback'} (replace the endpoint with yours): -

- - - -Remember to keep the path `/SignedOutCallback` in the URI as it's the default value for the Logto authentication middleware. - -To sign-out with Logto, you can use the `SignOut` method of `ControllerBase`: - -```csharp -SignOut(new AuthenticationProperties -{ - // The URI below is different from the post sign-out redirect URI you entered above. - // It's the URI where users will be redirected after successfully signed out. - // You can change it to any path you want. - RedirectUri = "/" -}); -``` - -The `SignOut` method will clear the authentication cookie and redirect the user to the Logto sign-out page. -For example, you can add the following code to your controller: - -```csharp -public class HomeController : Controller -{ - // ... // Use the `new` keyword to avoid conflict with the `ControllerBase.SignOut` method new public IActionResult SignOut() { + // This will clear the authentication cookie and redirect the user to the Logto sign-out page + // to clear the Logto session as well. return SignOut(new AuthenticationProperties { RedirectUri = "/" }); } } ``` -Then, update the form on your View: +Then, add the links to your View: -```html +```cshtml title="Views/Home/Index.cshtml"

Is authenticated: @User.Identity?.IsAuthenticated

-@if (User.Identity?.IsAuthenticated == true) -{ +@if (User.Identity?.IsAuthenticated == true) { Sign out } else { Sign in @@ -151,29 +76,13 @@ It will show the "Sign in" link if the user is not authenticated, and show the " -Now you can run the web application and try to sign in and sign out with Logto: - -1. Open the web application in your browser, you should see "Is authenticated: False" and the "Sign in" link. -2. Click the "Sign in" link, and you should be redirected to the Logto sign-in page. -3. After you have signed in, you should be redirected back to the web application, and you should see "Is authenticated: True" and the "Sign out" link. -4. Click the "Sign out" link, and you should be redirected to the Logto sign-out page, and then redirected back to the web application. + - - -To know if the user is authenticated, you can check the `User.Identity?.IsAuthenticated` property. - -To get the user profile claims, you can use the `User.Claims` property: - -```csharp -var claims = User.Claims; - -// Get the user ID -var userId = claims.FirstOrDefault(c => c.Type == LogtoParameters.Claims.Subject)?.Value; -``` + -See the [full tutorial](https://docs.logto.io/quick-starts/dotnet-core/mvc/) for more details. + diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_add-authentication.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_add-authentication.mdx new file mode 100644 index 00000000000..34b1fe74690 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_add-authentication.mdx @@ -0,0 +1,23 @@ +Open `Startup.cs` (or `Program.cs`) and add the following code to register Logto authentication middleware: + + +{`using Logto.AspNetCore.Authentication; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddLogtoAuthentication(options => +{ + options.Endpoint = "${props.endpoint}"; + options.AppId = "${props.app.id}"; + options.AppSecret = "${props.app.secret}"; +}); + +app.UseAuthentication();`} + + +The `AddLogtoAuthentication` method will do the following things: + +- Set the default authentication scheme to `LogtoDefaults.CookieScheme`. +- Set the default challenge scheme to `LogtoDefaults.AuthenticationScheme`. +- Set the default sign-out scheme to `LogtoDefaults.AuthenticationScheme`. +- Add cookie and OpenID Connect authentication handlers to the authentication scheme. diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx new file mode 100644 index 00000000000..6c1b0820946 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx @@ -0,0 +1,28 @@ +import UriInputField from '@/mdx-components/UriInputField'; + +export const defaultBaseUrl = 'http://localhost:5000/'; + +First, let's configure the **Logto redirect URI**. For example, if your website is hosted on {defaultBaseUrl}, add {defaultBaseUrl + 'Callback'} as the redirect URI below: + + + +Now let's configure the **Logto post sign-out redirect URI**. For example, set the URI to {defaultBaseUrl + 'SignedOutCallback'}: + + + +#### Change the default paths + +The **Logto redirect URI** has a default path of `/Callback`, and the **Logto post sign-out redirect URI** has a default path of `/SignedOutCallback`. + +You can leave them as are if there's no special requirement. If you want to change it, you can set the `CallbackPath` and `SignedOutCallbackPath` property for `LogtoOptions`: + +```csharp title="Program.cs" +builder.Services.AddLogtoAuthentication(options => +{ + // Other configurations... + options.CallbackPath = "/Foo"; + options.SignedOutCallbackPath = "/Bar"; +}); +``` + +Remember to update the values in the redirect URI fields accordingly. diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_display-user-information.md b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_display-user-information.md new file mode 100644 index 00000000000..229c20ebe8f --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_display-user-information.md @@ -0,0 +1,12 @@ +To know if the user is authenticated, you can check the `User.Identity?.IsAuthenticated` property. + +To get the user profile claims, you can use the `User.Claims` property: + +```csharp title="Controllers/HomeController.cs" +var claims = User.Claims; + +// Get the user ID +var userId = claims.FirstOrDefault(c => c.Type == LogtoParameters.Claims.Subject)?.Value; +``` + +See [`LogtoParameters.Claims`](https://github.com/logto-io/csharp/blob/master/src/Logto.AspNetCore.Authentication/LogtoParameters.cs) for the list of claim names and their meanings. diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_installation.md b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_installation.md new file mode 100644 index 00000000000..7037dce9bb0 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_installation.md @@ -0,0 +1,5 @@ +Install the Logto SDK to your project: + +```bash showLineNumbers={false} +dotnet add package Logto.AspNetCore.Authentication +``` diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx new file mode 100644 index 00000000000..090a75062e7 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx @@ -0,0 +1,37 @@ +import RegardingRedirectBasedSignIn from '../../../fragments/_regarding-redirect-based-sign-in.md'; + +Before we proceed, there are two confusing terms in the .NET Core authentication middleware that we need to clarify: + +1. **CallbackPath**: The URI that Logto will redirect the user back to after the user has signed in (the "redirect URI" in Logto) +2. **RedirectUri**: The URI that will be redirected to after necessary actions have been taken in the Logto authentication middleware. + +The sign-in process can be illustrated as follows: + +```mermaid +graph LR + subgraph Your app + A + C + D + end + subgraph Logto + B + end + A(Sign-in path) -->|Redirect to| B(Logto) + B -->|Redirect to| C(CallbackPath) + C -->|Redirect to| D(RedirectUri) +``` + +
+ +Similarly, .NET Core also has **SignedOutCallbackPath** and **RedirectUri** for the sign-out flow. + +For the sack of clarity, we'll refer them as follows: + +| Term we use | .NET Core term | +| -------------------------------- | --------------------- | +| Logto redirect URI | CallbackPath | +| Logto post sign-out redirect URI | SignedOutCallbackPath | +| Application redirect URI | RedirectUri | + + diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/index.ts b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/index.ts index 315a8943e86..ac60c2b4048 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/index.ts +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/index.ts @@ -10,10 +10,7 @@ const metadata: Readonly = Object.freeze({ repo: 'csharp', path: '/', }, - fullGuide: { - title: 'Full .NET Core (MVC) integration tutorial', - url: 'https://docs.logto.io/quick-starts/dotnet-core/mvc', - }, + fullGuide: 'dotnet-core/mvc', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core/README.mdx b/packages/console/src/assets/docs/guides/web-dotnet-core/README.mdx index 245a9fe3a51..dee9d255372 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core/README.mdx +++ b/packages/console/src/assets/docs/guides/web-dotnet-core/README.mdx @@ -2,82 +2,46 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; - - - - -This tutorial will show you how to use Logto ASP.NET Core authentication middleware to protect your web application. +import SignInAndSignOutFlows from '../web-dotnet-core-mvc/fragments/_sign-in-and-sign-out-flows.mdx'; +import ConfigureRedirectUris from '../web-dotnet-core-mvc/fragments/_configure-redirect-uris.mdx'; +import Installation from '../web-dotnet-core-mvc/fragments/_installation.md'; +import AddAuthentication from '../web-dotnet-core-mvc/fragments/_add-authentication.mdx'; +import DisplayUserInformation from '../web-dotnet-core-mvc/fragments/_display-user-information.md'; +import Checkpoint from '../../fragments/_checkpoint.md'; -
    -
  • It assumes your website is hosted on {props.sampleUrls.origin}.
  • -
+ -### Installation + -```bash -dotnet add package Logto.AspNetCore.Authentication -``` + -Open `Startup.cs` (or `Program.cs`) and add the following code to register Logto authentication middleware: - -
-  
-{`using Logto.AspNetCore.Authentication;
+
 
-var builder = WebApplication.CreateBuilder(args);
-
-builder.Services.AddLogtoAuthentication(options =>
-{
-  options.Endpoint = "${props.endpoint}";
-  options.AppId = "${props.app.id}";
-  options.AppSecret = "${props.app.secret}";
-});
-
-app.UseAuthentication();`}
-  
-
+
-The `AddLogtoAuthentication` method will do the following things: + -- Set the default authentication scheme to `LogtoDefaults.CookieScheme`. -- Set the default challenge scheme to `LogtoDefaults.AuthenticationScheme`. -- Set the default sign-out scheme to `LogtoDefaults.AuthenticationScheme`. -- Add cookie and OpenID Connect authentication handlers to the authentication scheme. + - - -

-First, let's enter your redirect URI. E.g. {props.sampleUrls.origin + 'Callback'} (replace the endpoint with yours). This is where Logto will redirect users after they sign in. -

- - + -Remember to keep the path `/Callback` in the URI as it's the default value for the Logto authentication middleware. + -To sign-in with Logto, you can use the `ChallengeAsync` method: + -```csharp -await HttpContext.ChallengeAsync(new AuthenticationProperties -{ - // The URI below is different from the redirect URI you entered above. - // It's the URI where users will be redirected after successfully signed in. - // You can change it to any path you want. - RedirectUri = "/" -}); -``` + -For example, if you are using Razor Pages, you can add the following code to the `Index` page model: +First, add the handler methods to your `PageModel`, for example: -```csharp +```csharp title="Pages/Index.cshtml.cs" public class IndexModel : PageModel { - // ... public async Task OnPostSignInAsync() { await HttpContext.ChallengeAsync(new AuthenticationProperties @@ -85,52 +49,7 @@ public class IndexModel : PageModel RedirectUri = "/" }); } -} -``` - -And then add the following code to the `Index` page: - -```html -

Is authenticated: @User.Identity?.IsAuthenticated

-
- -
-``` -
- - - -To clean up both ASP.NET session and Logto session, we can designate a post sign-out redierct URI. This is where Logto will redirect users after they sign out. - -

-For example, set the URI to {props.sampleUrls.origin + 'SignedOutCallback'} (replace the endpoint with yours): -

- - - -Remember to keep the path `/SignedOutCallback` in the URI as it's the default value for the Logto authentication middleware. - -To sign-out with Logto, you can use the `SignOutAsync` method: - -```csharp -await HttpContext.SignOutAsync(new AuthenticationProperties -{ - // The URI below is different from the post sign-out redirect URI you entered above. - // It's the URI where users will be redirected after successfully signed out. - // You can change it to any path you want. - RedirectUri = "/" -}); -``` - -The `SignOutAsync` method will clear the authentication cookie and redirect the user to the Logto sign-out page. - -For example, if you are using Razor Pages, you can add the following code to the `Index` page model: - -```csharp -public class IndexModel : PageModel -{ - // ... public async Task OnPostSignOutAsync() { await HttpContext.SignOutAsync(new AuthenticationProperties @@ -141,13 +60,12 @@ public class IndexModel : PageModel } ``` -Then, update the form on your Razor page: +Then, add the buttons to your Razor page: -```html +```cshtml title="Pages/Index.cshtml"

Is authenticated: @User.Identity?.IsAuthenticated

- @if (User.Identity?.IsAuthenticated == true) - { + @if (User.Identity?.IsAuthenticated == true) { } else { @@ -161,29 +79,13 @@ It will show the "Sign in" button if the user is not authenticated, and show the -Now you can run the web application and try to sign in and sign out with Logto: - -1. Open the web application in your browser, you should see "Is authenticated: False" and the "Sign in" button. -2. Click the "Sign in" button, and you should be redirected to the Logto sign-in page. -3. After you have signed in, you should be redirected back to the web application, and you should see "Is authenticated: True" and the "Sign out" button. -4. Click the "Sign out" button, and you should be redirected to the Logto sign-out page, and then redirected back to the web application. + - - -To know if the user is authenticated, you can check the `User.Identity?.IsAuthenticated` property. - -To get the user profile claims, you can use the `User.Claims` property: - -```csharp -var claims = User.Claims; - -// Get the user ID -var userId = claims.FirstOrDefault(c => c.Type == LogtoParameters.Claims.Subject)?.Value; -``` + -See the [full tutorial](https://docs.logto.io/quick-starts/dotnet-core/razor/) for more details. + diff --git a/packages/console/src/assets/docs/guides/web-express/README.mdx b/packages/console/src/assets/docs/guides/web-express/README.mdx index a498ee67a05..4325a964589 100644 --- a/packages/console/src/assets/docs/guides/web-express/README.mdx +++ b/packages/console/src/assets/docs/guides/web-express/README.mdx @@ -1,167 +1,96 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import { generateStandardSecret } from '@logto/shared/universal'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUris from '../../fragments/_redirect-uris-web.mdx'; - - - -```bash -npm i @logto/express cookie-parser express-session -``` - - - - -```bash -yarn add @logto/express cookie-parser express-session -``` - - - - -```bash -pnpm add @logto/express cookie-parser express-session -``` + - - - - In the following steps, we assume your app is running on http://localhost:3000. - +Prepare configuration for the Logto client: -Import and initialize LogtoClient: + + {`import type { LogtoExpressConfig } from '@logto/express'; -
-  
-    {`import LogtoClient from '@logto/express';
-
-export const logtoClient = new LogtoClient({
+const config: LogtoExpressConfig = {
   endpoint: '${props.endpoint}',
   appId: '${props.app.id}',
   appSecret: '${props.app.secret}',
   baseUrl: 'http://localhost:3000', // Change to your own base URL
-});`}
-  
-
- -
- - +}; +`} + The SDK requires [express-session](https://www.npmjs.com/package/express-session) to be configured in prior. -
-  
+
     {`import cookieParser from 'cookie-parser';
 import session from 'express-session';
 
 app.use(cookieParser());
 app.use(session({ secret: '${generateStandardSecret()}', cookie: { maxAge: 14 * 24 * 60 * 60 } }));`}
-  
-
+
- - -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/api/logto/sign-in-callback`. - - - -### Prepare Logto routes - -Prepare routes to connect with Logto. + -Go back to your IDE/editor, use the following code to implement the API routes first: - -```ts -import { handleAuthRoutes } from '@logto/express'; - -app.use(handleAuthRoutes(config)); -``` - -This will create 3 routes automatically: +The SDK provides a helper function `handleAuthRoutes` to register 3 routes: 1. `/logto/sign-in`: Sign in with Logto. 2. `/logto/sign-in-callback`: Handle sign-in callback. 3. `/logto/sign-out`: Sign out with Logto. -### Implement sign-in +Add the following code to your app: -We're almost there! Now, create a sign-in button to redirect to the sign-in route on user click. +```ts title="app.ts" +import { handleAuthRoutes } from '@logto/express'; -```ts -app.get('/', (req, res) => { - res.setHeader('content-type', 'text/html'); - res.end(``); -}); +app.use(handleAuthRoutes(config)); ``` - -Calling `/logto/sign-out` will clear all the Logto data in memory and cookies if they exist. - -After signing out, it'll be great to redirect your user back to your website. Let's add `http://localhost:3000` as one of the Post Sign-out URIs in Admin Console (shows under Redirect URIs). + - + -In Logto SDK, you can use the `withLogto` middleware to get `req.user.isAuthenticated` to check the authentication status, if the user is signed in, the value will be `true`, otherwise, the value will be `false`. +With the routes registered, now let's implement the sign-in and sign-out buttons in the home page. We need to redirect the user to the sign-in or sign-out route when needed. To help with this, use `withLogto` to inject authentication status to `req.user`. -``ts +```ts title="app.ts" import { withLogto } from '@logto/express'; -app.use(withLogto(config)); -``` - -No, let's use this value to protect routes by creating a simple middleware: +app.get('/', withLogto(config), (req, res) => { + res.setHeader('content-type', 'text/html'); -```ts -const requireAuth = async (req: Request, res: Response, next: NextFunction) => { - if (!req.user.isAuthenticated) { - res.redirect('/logto/sign-in'); + if (req.user.isAuthenticated) { + res.end(`
Hello ${req.user.claims?.sub}, Sign Out
`); + } else { + res.end(''); } - - next(); -}; -``` - -And then use it in the route handler: - -```ts -app.get('/protected', requireAuth, (req, res) => { - res.end('protected resource'); }); ```
@@ -170,12 +99,7 @@ app.get('/protected', requireAuth, (req, res) => { title="Checkpoint: Test your application" > -Now, you can test your application: - -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, and you will be redirected to the sign in route, and the SDK will then init the sign-in process and redirect to the Logto sign-in page. -3. After you signed in, you will be redirect back to your application and see the sign-out button. -4. Calling `/logto/sign-out` to sign-out. +
diff --git a/packages/console/src/assets/docs/guides/web-express/index.ts b/packages/console/src/assets/docs/guides/web-express/index.ts index 6c22e22b2e8..1f3ac22b3d0 100644 --- a/packages/console/src/assets/docs/guides/web-express/index.ts +++ b/packages/console/src/assets/docs/guides/web-express/index.ts @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ repo: 'js', path: 'packages/express-sample', }, - fullGuide: { - title: 'Full Express SDK tutorial', - url: 'https://docs.logto.io/quick-starts/express', - }, + fullGuide: 'express', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-go/README.mdx b/packages/console/src/assets/docs/guides/web-go/README.mdx index 3044cb21d9d..26e34567b4a 100644 --- a/packages/console/src/assets/docs/guides/web-go/README.mdx +++ b/packages/console/src/assets/docs/guides/web-go/README.mdx @@ -2,6 +2,8 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; import InlineNotification from '@/ds-components/InlineNotification'; +import RedirectUrisWeb, {defaultBaseUrl, defaultRedirectUri} from '../../fragments/_redirect-uris-web.mdx'; +import Checkpoint from '../../fragments/_checkpoint.md'; @@ -10,9 +12,8 @@ import InlineNotification from '@/ds-components/InlineNotification'; > - The following demonstration is built upon the Gin Web Framework. - You may also integrate Logto into other frameworks by taking the same steps. - We assume your app is running on http://localhost:8080 in this guide. + The following demonstration is built upon the Gin Web Framework. + You may also integrate Logto into other frameworks by taking the same steps. Execute in the project root directory: @@ -23,8 +24,7 @@ go get github.com/logto-io/go Add the `github.com/logto-io/go/client` package to your application code: -```go -// main.go +```go title="main.go" package main import ( @@ -38,15 +38,13 @@ func main() { router.GET("/", func(c *gin.Context) { c.String(200, "Hello Logto!") }) - router.Run(":8080") + router.Run(":3000") } ```
- + In traditional web applications, the user authentication information will be stored in the user session. @@ -60,8 +58,7 @@ Logto SDK provides a `Storage` interface, you can implement a `Storage` adapter The `Storage` type in the Logto SDK is as follows: -```go -// github.com/logto-io/client/storage.go +```go title="github.com/logto-io/client/storage.go" package client type Storage interface { @@ -74,7 +71,7 @@ We use [github.com/gin-contrib/sessions](https://github.com/gin-contrib/sessions Apply the middleware to the application, so that we can get the user session by the user request context in the route handler: -```go +```go title="main.go" package main import ( @@ -97,14 +94,13 @@ func main() { // ... ctx.String(200, "Hello Logto!") }) - router.Run(":8080") + router.Run(":3000") } ``` Create a `session_storage.go` file, define a `SessionStorage` and implement the Logto SDK's `Storage` interfaces: -```go -// session_storage.go +```go title="session_storage.go" package main import ( @@ -138,16 +134,12 @@ sessionStorage := &SessionStorage{session: session} - + First, create a Logto config: -
-  
-    {`// main.go
-func main() {
+
+{`func main() {
     // ...
 
     logtoConfig := &client.LogtoConfig{
@@ -158,13 +150,11 @@ func main() {
 
     // ...
 }`}
-  
-
+ Then, you can create a `LogtoClient` for each user request with the Logto config above: -```go -// main.go +```go title="main.go" func main() { // ... @@ -183,7 +173,7 @@ func main() { authState = "You are logged in to this website! :)" } - homePage := `

Hello Logto

` + + homePage := "

Hello Logto

" + "
" + authState + "
" ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(homePage)) @@ -195,24 +185,18 @@ func main() {
- - -Before you start implementing the sign-in flow, you need to add a redirect URI in the Admin Console for your application. + -This allows Logto to redirect the user to the redirect URI after signing in. + -For example, if you add `http://localhost:8080/sign-in-callback` to your Redirect URI, Logto will redirect the user to the `/sign-in-callback` route of your application after signing in. + - + After the redirect URI is configured, we add a `sign-in` route to handle the sign-in request and also add an sign-in link on the home page: -
-  
-    {`//main.go
-func main() {
+
+    {`func main() {
     // ...
 
     // Add a link to perform a sign-in request on the home page
@@ -248,28 +232,24 @@ func main() {
 
     // ...
 }`}
-  
-
+ -Now, when your user visit `http://localhost:8080/sign-in`, the user will be redirected to the Logto sign-in page. +Now, when your user visit {defaultBaseUrl}sign-in, the user will be redirected to the Logto sign-in page.
- + When the user signs in successfully on the Logto sign-in page, Logto will redirect the user to the Redirect URI. -Assuming your Redirect URI is `http://localhost:8080/sign-in-callback`, then we will add the `/sign-in-callback` route to handle the callback after signing in. +Assuming your Redirect URI is {defaultRedirectUri}, then we will add the `/callback` route to handle the callback after signing in. -```go -// main.go +```go title="main.go" func main() { // ... // Add a route for handling sign-in callback requests - router.GET("/sign-in-callback", func(ctx *gin.Context) { + router.GET("/callback", func(ctx *gin.Context) { session := sessions.Default(ctx) logtoClient := client.NewLogtoClient( logtoConfig, @@ -294,22 +274,14 @@ func main() { - + Similar to the sign-in flow, when the user signs out, Logto will redirect the user to the post sign-out redirect URI. -Assuming that you add `http://localhost:8080` to the Post Sign-out Redirect URI filed, Logto will redirect the user to the home page after signing out. - - - Now, let's add the `sign-out` route to handle the sign-out request and also add a sign-out link on the home page: -
-  
-    {`//main.go
-func main() {
+
+    {`func main() {
     // ...
 
     // Add a link to perform a sign-out request on the home page
@@ -346,23 +318,40 @@ func main() {
 
     // ...
 }`}
-  
-
+ After the user makes a signing-out request, Logto will clear all user authentication information in the session.
- + + + + + + + -Now, you can test your application: +To display the user's information, you can use the `client.GetIdTokenClaims` method. For example, add a route: -1. Visit `http://localhost:8080`, you will see "You are not logged in to this website." message on the home page. -2. Click the "Sign In" link, you will be redirected to the Logto sign-in page. -3. Sign in with your Logto account, you will be redirected to the home page and see "You are logged in to this website!" message. -4. Click the "Sign Out" link, and your user authentication information will be cleared, and the home page will display "You are not logged in to this website." message again. +```go title="main.go" +func main() { + //... + + router.GET("/user-id-token-claims", func(ctx *gin.Context) { + session := sessions.Default(ctx) + logtoClient := client.NewLogtoClient(logtoConfig, &SessionStorage{session: session}) + + idTokenClaims, err := logtoClient.GetIdTokenClaims() + + if err != nil { + ctx.String(http.StatusOK, err.Error()) + } + + ctx.JSON(http.StatusOK, idTokenClaims) + }) +} +``` diff --git a/packages/console/src/assets/docs/guides/web-go/index.ts b/packages/console/src/assets/docs/guides/web-go/index.ts index 6bd10eae34c..e7190d0305b 100644 --- a/packages/console/src/assets/docs/guides/web-go/index.ts +++ b/packages/console/src/assets/docs/guides/web-go/index.ts @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ repo: 'go', path: 'gin-sample', }, - fullGuide: { - title: 'Full Go SDK tutorial', - url: 'https://docs.logto.io/quick-starts/go', - }, + fullGuide: 'go', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/config.json b/packages/console/src/assets/docs/guides/web-gpt-plugin/config.json index cd39155a7cc..458b34b4578 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/config.json +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/config.json @@ -1,3 +1,3 @@ { - "order": 1.5 + "order": 999 } diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/index.ts b/packages/console/src/assets/docs/guides/web-gpt-plugin/index.ts index 154f6589a53..77a6a6d897a 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/index.ts +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/index.ts @@ -6,7 +6,6 @@ const metadata: Readonly = Object.freeze({ name: 'ChatGPT plugin', description: 'Use Logto as an OAuth identity provider for ChatGPT plugins.', target: ApplicationType.Traditional, - isFeatured: true, }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-java-spring-boot/README.mdx b/packages/console/src/assets/docs/guides/web-java-spring-boot/README.mdx index 7019654e233..92625af90a4 100644 --- a/packages/console/src/assets/docs/guides/web-java-spring-boot/README.mdx +++ b/packages/console/src/assets/docs/guides/web-java-spring-boot/README.mdx @@ -2,23 +2,16 @@ import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb from '../../fragments/_redirect-uris-web.mdx'; + - This tutorial will show you how to integrate Logto into your Java Spring Boot web application. - -
    -
  • - The sample was created using the Spring Boot [securing web - starter](https://spring.io/guides/gs/securing-web). Following the instructions to bootstrap a - new web application. -
  • -
  • - The sample uses the [Spring Security - OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2) library to handle OIDC - authentication and integrate with Logto. -
  • -
+ +This tutorial will show you how to integrate Logto into your Java Spring Boot application. + +No official SDK is required to integrate Logto with your Java Spring Boot application. We will use the [Spring Security](https://spring.io/projects/spring-security) and [Spring Security OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2) libraries to handle the OIDC authentication flow with Logto. Before we begin, make sure you have went through the spring boot guides linked above. @@ -27,21 +20,21 @@ Before we begin, make sure you have went through the spring boot guides linked a Include the following dependencies in your `build.gradle` file: -```gradle +```groovy title="build.gradle" dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' } ``` -The sample uses [gradle](https://spring.io/guides/gs/gradle) as the build tool. You can use +Our sample project uses [gradle](https://spring.io/guides/gs/gradle) as the build tool. You can use maven or any other build tool as well. The configurations might be slightly different. For maven, include the following dependencies in your `pom.xml` file: -```maven +```xml title="pom.xml" org.springframework.boot spring-boot-starter-thymeleaf @@ -67,8 +60,7 @@ For maven, include the following dependencies in your `pom.xml` file: Register your application with Logto to get the client credentials and IdP configurations. Add the following configuration to your `application.properties` file: -
-  
+
     {`spring.security.oauth2.client.registration.logto.client-name=logto
 spring.security.oauth2.client.registration.logto.client-id=${props.app.id}
 spring.security.oauth2.client.registration.logto.client-secret=${props.app.secret}
@@ -81,26 +73,29 @@ spring.security.oauth2.client.provider.logto.issuer-uri=${props.endpoint}oidc
 spring.security.oauth2.client.provider.logto.authorization-uri=${props.endpoint}oidc/auth
 spring.security.oauth2.client.provider.logto.jwk-set-uri=${props.endpoint}oidc/jwks
   `}
-  
-
+
-In order to redirect users back to your application after they sign in, you need to set the redirect URI using the `client.registration.logto.redirect-uri` property in the previous step. + - - -e.g. In our example, the redirect URI is `http://localhost:8080/login/oauth2/code/logto`. +Make sure the redirect URI in Logto matches the `redirect-uri` set in the `application.properties` file in the previous step. -#### Create a new class `WebSecurityConfig` in your project: +The `WebSecurityConfig` class will be used to configure the security settings for your application. It is the key class that will handle the authentication and authorization flow. Please check the [Spring Security documentation](https://spring.io/guides/topicals/spring-security-architecture) for more details. + +### Create a new class `WebSecurityConfig` in your project -```java +```java title="WebSecurityConfig.java" package com.example.securingweb; import org.springframework.context.annotation.Configuration; @@ -114,11 +109,11 @@ public class WebSecurityConfig { } ``` -#### Create a idTokenDecoderFactory bean to set the JWS algorithm to `ES384`: +### Create a idTokenDecoderFactory bean to set the JWS algorithm to `ES384` This is required because Logto uses ES384 as the default algorithm, we need to update the OidcIdTokenDecoderFactory to use the same algorithm. -```java +```java title="WebSecurityConfig.java" import org.springframework.context.annotation.Bean; import org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -137,11 +132,11 @@ public class WebSecurityConfig { } ``` -#### Create a LoginSuccessHandler class to handle the login success event: +### Create a LoginSuccessHandler class to handle the login success event Redirect the user to the user page after successful login: -```java +```java title="LoginSuccessHandler.java" package com.example.securingweb; import java.io.IOException; @@ -162,11 +157,11 @@ public class CustomSuccessHandler implements AuthenticationSuccessHandler { } ``` -#### Create a LogoutSuccessHandler class to handle the logout success event: +### Create a LogoutSuccessHandler class to handle the logout success event Clear the session and redirect the user to the home page. -```java +```java title="LogoutSuccessHandler.java" package com.example.securingweb; import java.io.IOException; @@ -194,11 +189,11 @@ public class CustomLogoutHandler implements LogoutSuccessHandler { } ``` -#### Create a `securityFilterChain` bean to configure the security configuration: +#### Create a `securityFilterChain` bean to configure the security configuration Add the following code to complete the `WebSecurityConfig` class: -```java +```java title="WebSecurityConfig.java" import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.DefaultSecurityFilterChain; @@ -229,13 +224,12 @@ public class WebSecurityConfig { - + (You may skip this step if you already have a home page in your project) -HomeController.java: -```java +```java title="HomeController.java" package com.example.securingweb; import java.security.Principal; @@ -254,9 +248,7 @@ public class HomeController { This controller will redirect the user to the user page if the user is authenticated, otherwise, it will show the home page. -home.html: - -```html +```html title="resources/templates/home.html"

Welcome!

@@ -266,11 +258,11 @@ home.html:
- + Create a new controller to handle the user page: -```java +```java title="UserController.java" package com.example.securingweb; import java.security.Principal; @@ -306,9 +298,7 @@ public class UserController { Read the user information from the `OAuth2User` object and pass it to the `user.html` template. -user.html: - -```html +```html title="resources/templates/user.html"

User Details

@@ -327,4 +317,10 @@ user.html: + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-next-app-router/README.mdx b/packages/console/src/assets/docs/guides/web-next-app-router/README.mdx index 51cec13fd09..5c6196036df 100644 --- a/packages/console/src/assets/docs/guides/web-next-app-router/README.mdx +++ b/packages/console/src/assets/docs/guides/web-next-app-router/README.mdx @@ -1,54 +1,28 @@ -import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; -import InlineNotification from '@/ds-components/InlineNotification'; import { generateStandardSecret } from '@logto/shared/universal'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb from '../../fragments/_redirect-uris-web.mdx'; - - -```bash -npm i @logto/next -``` - - - - -```bash -yarn add @logto/next -``` + - - - -```bash -pnpm add @logto/next -``` - - - - - In the following steps, we assume your app is running on http://localhost:3000. - - -Prepare configuration for the Logto client. Create a new file `app/logto.ts` and add the following code: +Prepare configuration for the Logto client: -
-  
+
     {`export const logtoConfig = {
   endpoint: '${props.endpoint}',
   appId: '${props.app.id}',
@@ -58,26 +32,25 @@ Prepare configuration for the Logto client. Create a new file `app/logto.ts` and
   cookieSecure: process.env.NODE_ENV === 'production',
 };
 `}
-  
-
+
- -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/callback`. + - + -### Implement callback page + -Add a callback page to your app: +Add a callback route to your app: -```tsx -// pages/callback/page.tsx +```tsx title="app/callback/route.ts" import { handleSignIn } from '@logto/next/server-actions'; import { redirect } from 'next/navigation'; import { NextRequest } from 'next/server'; @@ -91,12 +64,17 @@ export async function GET(request: NextRequest) { } ``` -### Implement sign-in button + + + + +### Implement sign-in and sign-out button -The sign-in button will call the method we just created, it is a client component: - -```tsx -// app/sign-in.tsx +In Next.js App Router, events are handled in client components, so we need to create two components first: `SignIn` and `SignOut`. + +```tsx title="app/sign-in.tsx" 'use client'; type Props = { @@ -118,53 +96,7 @@ const SignIn = ({ onSignIn }: Props) => { export default SignIn; ``` -### Add sign in button to home page - -We're almost there! Add this button to home page at `/app/page.tsx` and implement the `onSignIn` function: - -```tsx -import { signIn } from '@logto/next/server-actions'; -import SignIn from './sign-in'; -import { logtoConfig } from './logto'; - -export default async function Home() { - return ( -
-

Hello Logto.

-
- { - 'use server'; - - await signIn(logtoConfig); - }} - /> -
-
- ); -} -``` - -Now you will be navigated to Logto sign-in page when you click the button. - -
- - - -### Configure URI - -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below. - - - -### Implement a sign-out button - -The sign-out button is also a client component, so we will create it in `/app/sign-out.tsx`: - -```tsx -// app/sign-out.tsx +```tsx title="app/sign-out.tsx" 'use client'; type Props = { @@ -186,63 +118,26 @@ const SignOut = ({ onSignOut }: Props) => { export default SignOut; ``` -### Add sign out button to home page +Remember to add `'use client'` to the top of the file to indicate that these components are client components. -Then add the sign-out button to the home page in `/app/page.tsx`: +### Add buttons to home page -```tsx -import { signIn, signOut } from '@logto/next/server-actions'; -import SignIn from './sign-in'; -import SignOut from './sign-out'; -import { logtoConfig } from './logto'; - -export default async function Home() { - return ( -
-

Hello Logto.

-
- { - 'use server'; - - await signOut(logtoConfig); - }} - /> - { - 'use server'; - - await signIn(logtoConfig); - }} - /> -
-
- ); -} -``` +Now let's add the sign-in and sign-out buttons in your hoem page. We need to call the server actions in SDK when needed. To help with this, use `getLogtoContext` to fetch authentication status. -
- - - -We can call the function `getLogtoContext` to get context as the authentication state in pages, let's modify the home page: - -```tsx +```tsx title="app/page.tsx" import { getLogtoContext, signIn, signOut } from '@logto/next/server-actions'; import SignIn from './sign-in'; import SignOut from './sign-out'; import { logtoConfig } from './logto'; -export default async function Home() { - const { isAuthenticated } = await getLogtoContext(logtoConfig); +const Home = () => { + const { isAuthenticated, claims } = await getLogtoContext(logtoConfig); return ( -
-

Hello Logto.

-
- {isAuthenticated ? ( +
-
+

+ )} + ); -} +}; + +export default Home; ```
-
\ No newline at end of file + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-next-app-router/index.ts b/packages/console/src/assets/docs/guides/web-next-app-router/index.ts index 01aa121548f..492ee05158f 100644 --- a/packages/console/src/assets/docs/guides/web-next-app-router/index.ts +++ b/packages/console/src/assets/docs/guides/web-next-app-router/index.ts @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ path: 'packages/next-server-actions-sample', }, isFeatured: true, - fullGuide: { - title: 'Full Next.js SDK tutorial', - url: 'https://docs.logto.io/sdk/next-app-router/', - }, + fullGuide: 'next-app-router', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-next-auth/README.mdx b/packages/console/src/assets/docs/guides/web-next-auth/README.mdx index baeeff2600b..badcf966507 100644 --- a/packages/console/src/assets/docs/guides/web-next-auth/README.mdx +++ b/packages/console/src/assets/docs/guides/web-next-auth/README.mdx @@ -31,8 +31,7 @@ Modify your API route config of Next Auth, if you are using Pages Router, the fi The following is an example of App Router: -
-  
+
     {`import NextAuth from 'next-auth';
 
 const handler = NextAuth({
@@ -64,8 +63,7 @@ const handler = NextAuth({
 });
 
 export { handler as GET, handler as POST };`}
-  
-
+ diff --git a/packages/console/src/assets/docs/guides/web-next/README.mdx b/packages/console/src/assets/docs/guides/web-next/README.mdx index 0313e9dddfd..961465f691c 100644 --- a/packages/console/src/assets/docs/guides/web-next/README.mdx +++ b/packages/console/src/assets/docs/guides/web-next/README.mdx @@ -1,88 +1,58 @@ -import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; -import InlineNotification from '@/ds-components/InlineNotification'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; import { generateStandardSecret } from '@logto/shared/universal'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb, { defaultBaseUrl } from '../../fragments/_redirect-uris-web.mdx'; - - -```bash -npm i @logto/next -``` - - - - -```bash -yarn add @logto/next -``` - - - - -```bash -pnpm add @logto/next -``` + - - - - In the following steps, we assume your app is running on http://localhost:3000. - - Import and initialize LogtoClient: -
-  
-    {`// libraries/logto.js
-import LogtoClient from '@logto/next';
+
+    {`import LogtoClient from '@logto/next';
 
 export const logtoClient = new LogtoClient({
   endpoint: '${props.endpoint}',
   appId: '${props.app.id}',
   appSecret: '${props.app.secret}',
-  baseUrl: 'http://localhost:3000', // Change to your own base URL
+  baseUrl: '${defaultBaseUrl}', // Change to your own base URL
   cookieSecret: '${generateStandardSecret()}', // Auto-generated 32 digit secret
   cookieSecure: process.env.NODE_ENV === 'production',
 });`}
-  
-
+
- -### Configure Redirect URI - -First, let’s enter your redirect URI. E.g. `http://localhost:3000/api/logto/sign-in-callback`. + - + -### Prepare API routes + Prepare [API routes](https://nextjs.org/docs/api-routes/introduction) to connect with Logto. Go back to your IDE/editor, use the following code to implement the API routes first: -```ts -// pages/api/logto/[action].ts +```ts title="pages/api/logto/[action].ts" import { logtoClient } from '../../../libraries/logto'; export default logtoClient.handleAuthRoutes(); @@ -91,126 +61,65 @@ export default logtoClient.handleAuthRoutes(); This will create 4 routes automatically: 1. `/api/logto/sign-in`: Sign in with Logto. -2. `/api/logto/sign-in-callback`: Handle sign-in callback. +2. `/api/logto/sign-in-callback`: Handle sign-in callback (redirect URI). 3. `/api/logto/sign-out`: Sign out with Logto. 4. `/api/logto/user`: Check if user is authenticated with Logto, if yes, return user info. -### Implement sign-in button - -We're almost there! In the last step, we will create a sign-in button: - -```tsx -import { useRouter } from 'next/router'; - -const { push } = useRouter(); - -; -``` - -Now you will be navigated to Logto sign-in page when you click the button. - -Calling `/api/logto/sign-out` will clear all the Logto data in memory and cookies if they exist. - -After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below before calling `/api/logto/sign-out`. - - +We have prepared the API routes, now let's implement the sign-in and sign-out buttons in your home page. We need to redirect the user to the sign-in or sign-out route when needed. To help with this, use `useSWR` to fetch authentication status from `/api/logto/user`. -### Implement a sign-out button - -```tsx - -``` - - - - - -### Through API request in the frontend - -You can fetch user info by calling `/api/logto/user`. +Check [this guide](https://swr.vercel.app/docs/getting-started) to learn more about `useSWR`. -```tsx -import { LogtoUser } from '@logto/next'; +```tsx title="/pages/index.tsx" +import { type LogtoContext } from '@logto/next'; import useSWR from 'swr'; const Home = () => { - const { data } = useSWR('/api/logto/user'); - - return
User ID: {data?.claims?.sub}
; + const { data } = useSWR('/api/logto/user'); + + return ( + + ); }; -export default Profile; +export default Home; ``` -Check [this guide](https://swr.vercel.app/docs/getting-started) to learn more about `useSWR`. - -### Through `getServerSideProps` in the backend - -```tsx -import { LogtoUser } from '@logto/next'; -import { logtoClient } from '../libraries/logto'; - -type Props = { - user: LogtoUser; -}; - -const Profile = ({ user }: Props) => { - return
User ID: {user.claims?.sub}
; -}; - -export default Profile; - -export const getServerSideProps = logtoClient.withLogtoSsr(({ request }) => { - const { user } = request; - - return { - props: { user }, - }; -}); -``` - -Check [Next.js documentation](https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props) for more details on `getServerSideProps`. - -### Protect API routes - -Wrap your handler with `logtoClient.withLogtoApiRoute`. - -```ts -// pages/api/protected-resource.ts -import { logtoClient } from '../../libraries/logto'; - -export default logtoClient.withLogtoApiRoute((request, response) => { - if (!request.user.isAuthenticated) { - response.status(401).json({ message: 'Unauthorized' }); - - return; - } - - response.json({ - data: 'this_is_protected_resource', - }); -}); -```
-Now, you can test your application: - -1. Run your application, you will see the sign-in button. -2. Click the sign-in button, and you will be redirected to the sign in route, and the SDK will then init the sign-in process and redirect to the Logto sign-in page. -3. After you signed in, you will be redirect back to your application and see user id and the sign-out button. -4. Click the sign-out button to sign-out. + diff --git a/packages/console/src/assets/docs/guides/web-next/index.ts b/packages/console/src/assets/docs/guides/web-next/index.ts index 703b36bae68..7e60ff1e724 100644 --- a/packages/console/src/assets/docs/guides/web-next/index.ts +++ b/packages/console/src/assets/docs/guides/web-next/index.ts @@ -3,7 +3,7 @@ import { ApplicationType } from '@logto/schemas'; import { type GuideMetadata } from '../types'; const metadata: Readonly = Object.freeze({ - name: 'Next.js', + name: 'Next.js (Page Router)', description: 'Next.js is a React framework for production - it makes building fullstack React apps a breeze and ships with built-in SSR.', target: ApplicationType.Traditional, @@ -11,10 +11,7 @@ const metadata: Readonly = Object.freeze({ repo: 'js', path: 'packages/next-sample', }, - fullGuide: { - title: 'Full Next.js SDK tutorial', - url: 'https://docs.logto.io/quick-starts/next', - }, + fullGuide: 'next', }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-nuxt/README.mdx b/packages/console/src/assets/docs/guides/web-nuxt/README.mdx index 3ce9c63ca14..b274d4849e7 100644 --- a/packages/console/src/assets/docs/guides/web-nuxt/README.mdx +++ b/packages/console/src/assets/docs/guides/web-nuxt/README.mdx @@ -1,65 +1,35 @@ import UriInputField from '@/mdx-components/UriInputField'; -import Tabs from '@mdx/components/Tabs'; -import TabItem from '@mdx/components/TabItem'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; import Checkpoint from '../../fragments/_checkpoint.md'; +import RedirectUrisWeb from '../../fragments/_redirect-uris-web.mdx'; import { generateStandardSecret } from '@logto/shared/universal'; export const cookieEncryptionKey = generateStandardSecret(); - - Logto Nuxt SDK only works with Nuxt 3. - - - - - -```bash -npm i @logto/nuxt -``` - - - - -```bash -yarn add @logto/nuxt -``` - - + + Logto Nuxt SDK only works with Nuxt 3. + -```bash -pnpm add @logto/nuxt -``` + - - -In your Nuxt config file (`next.config.ts`), add the Logto module: +In your Nuxt config file, add the Logto module and configure it: -```ts -export default defineNuxtConfig({ - modules: ['@logto/nuxt'], - // ...other configurations -}); -``` - -The minimal configuration for the module is as follows: - -
-  
-    {`export default defineNuxtConfig({
+
+{`export default defineNuxtConfig({
   modules: ['@logto/nuxt'],
   runtimeConfig: {
     logto: {
@@ -71,38 +41,28 @@ The minimal configuration for the module is as follows:
   },
   // ...other configurations
 });`}
-  
-
+ Since these information are sensitive, it's recommended to use environment variables (`.env`): -
-  
+
     {`NUXT_LOGTO_ENDPOINT=${props.endpoint}
 NUXT_LOGTO_APP_ID=${props.app.id}
 NUXT_LOGTO_APP_SECRET=${props.app.secret}
 NUXT_LOGTO_COOKIE_ENCRYPTION_KEY=${cookieEncryptionKey} # Random-generated
 `}
-  
-
+ See [runtime config](https://nuxt.com/docs/guide/going-further/runtime-config) for more information.
- - - - In the following steps, we assume your app is running on http://localhost:3000. - - -First, let's enter your redirect URI. E.g. `http://localhost:3000/callback`. [Redirect URI](https://www.oauth.com/oauth2-servers/redirect-uris/) is an OAuth 2.0 concept which implies the location should redirect after authentication. - - - -After signing out, it'll be great to redirect user back to your website. For example, add `http://localhost:3000` as the post sign-out redirect URI below. + - + When registering `@logto/nuxt` module, it will do the following: @@ -111,7 +71,7 @@ When registering `@logto/nuxt` module, it will do the following: These routes are configurable via `logto.pathnames` in the module options, for example: -```ts +```ts title="nuxt.config.ts" export default defineNuxtConfig({ logto: { pathnames: { @@ -126,32 +86,24 @@ export default defineNuxtConfig({ Check out the [type definition file](https://github.com/logto-io/js/blob/HEAD/packages/nuxt/src/runtime/utils/types.ts) in the `@logto/nuxt` package for more information. -Note: If you configure the callback route to a different path, you need to update the redirect URI in Logto accordingly. + +If you configure the callback route to a different path, you need to update the redirect URI in Logto accordingly. + -Since Nuxt pages will be hydrated and become a single-page application (SPA) after the initial load, we need to redirect the user to the sign-in or sign-out route when needed. - -```html -Sign in -
-Sign out -``` - -
- - - -To display the user's information, you can use the `useLogtoUser()` composable, which is available on both server and client side: +Since Nuxt pages will be hydrated and become a single-page application (SPA) after the initial load, we need to redirect the user to the sign-in or sign-out route when needed. To help with this, our SDK provides the `useLogtoUser()` composable, which can be used in both server and client side. -```html +```html title="index.vue"