From 411a6e4c93ab3ade24be78e5a44aa1ae30fbbbd3 Mon Sep 17 00:00:00 2001 From: Sara Zanellato <47299026+peschina@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:51:14 +0200 Subject: [PATCH] [19328] Deploy to staging (#135) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile * 18630 seed real roles (#45) * feat(cli): seed roles and permissions into db * fix(cli): cleanup code * chore(cli): small update * chore(cli): refactoring roles * chore(cli): refactoring organization roles * chore(cli): update * chore(cli): rename seeder file (#52) * chore(cli): rename seeder file * chore(cli): fix eof new line * chore: use port 7001 instead of 5001 to avoid conflicts with forms (#56) * fix(cli): remove secrets from local seeder (#59) * feat(cli): seed is_third_party flag (#57) * feat(cli): seed is_third_party flag * fix(demo-app): trying to ignore the entire seeder local file from gitleaks * Reverted .gitleaksignore change --------- Co-authored-by: alfonsograziano Co-authored-by: Alfonso Graziano * feat(cli): seed multiple redurect uris (#58) * Feature(ogcio): messaging integration (#60) * feat(ogcio): added messaging permissions locally * feat(core): updated seeder for deployments * chore(core): added EOL --------- Co-authored-by: Alfonso Graziano * Add MyGovId Mock service to Logto (#55) * feat(demo-app): copied mock service from life events repo * feat(demo-app): add mock users + fix build * feat(demo-app): changed port to not clash with 3005 in life-events auth-service * fix(demo-app): trying to ignore the entire seeder local file from gitleaks * feat(cli): seeder updates already existing entries (#61) * feat(cli): seeder updates already existing entries * feat(cli): seeder file updated * feat(cli): update * feat(cli): documenting the seeders limitations * feat(cli): updated documentation * Deploy STA in DEV (#63) * Align staging to dev (#37) Add staging pipeline file (#36) * add file * fix * Update staging (#39) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) --------- Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * fix(cli): fix seeder to handle empty data (#42) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * chore(cli): cleanup * fix(cli): azure pipeline * [17584] Deploy to staging (#47) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * [18810] Deploy to staging (#50) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * [18630] Deploy renamed seeder to staging (#53) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile * 18630 seed real roles (#45) * feat(cli): seed roles and permissions into db * fix(cli): cleanup code * chore(cli): small update * chore(cli): refactoring roles * chore(cli): refactoring organization roles * chore(cli): update * chore(cli): rename seeder file (#52) * chore(cli): rename seeder file * chore(cli): fix eof new line --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * 18941 Deploy dev into stage (#62) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile * 18630 seed real roles (#45) * feat(cli): seed roles and permissions into db * fix(cli): cleanup code * chore(cli): small update * chore(cli): refactoring roles * chore(cli): refactoring organization roles * chore(cli): update * chore(cli): rename seeder file (#52) * chore(cli): rename seeder file * chore(cli): fix eof new line * chore: use port 7001 instead of 5001 to avoid conflicts with forms (#56) * fix(cli): remove secrets from local seeder (#59) * feat(cli): seed is_third_party flag (#57) * feat(cli): seed is_third_party flag * fix(demo-app): trying to ignore the entire seeder local file from gitleaks * Reverted .gitleaksignore change --------- Co-authored-by: alfonsograziano Co-authored-by: Alfonso Graziano * feat(cli): seed multiple redurect uris (#58) * Feature(ogcio): messaging integration (#60) * feat(ogcio): added messaging permissions locally * feat(core): updated seeder for deployments * chore(core): added EOL --------- Co-authored-by: Alfonso Graziano * Add MyGovId Mock service to Logto (#55) * feat(demo-app): copied mock service from life events repo * feat(demo-app): add mock users + fix build * feat(demo-app): changed port to not clash with 3005 in life-events auth-service * fix(demo-app): trying to ignore the entire seeder local file from gitleaks * feat(cli): seeder updates already existing entries (#61) * feat(cli): seeder updates already existing entries * feat(cli): seeder file updated * feat(cli): update * feat(cli): documenting the seeders limitations * feat(cli): updated documentation --------- Co-authored-by: Alfonso Graziano Co-authored-by: William Monteiro Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> Co-authored-by: Marius Sebastian Besel <145235082+msebastianb@users.noreply.github.com> Co-authored-by: alfonsograziano Co-authored-by: SamSalvatico <40636569+SamSalvatico@users.noreply.github.com> --------- Co-authored-by: Alfonso Graziano Co-authored-by: William Monteiro Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> Co-authored-by: Marius Sebastian Besel <145235082+msebastianb@users.noreply.github.com> Co-authored-by: alfonsograziano Co-authored-by: SamSalvatico <40636569+SamSalvatico@users.noreply.github.com> * 18941 deploy uat in dev (#68) * [18810] Deploy to UAT (#51) * Align staging to dev (#37) Add staging pipeline file (#36) * add file * fix * Update staging (#39) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) --------- Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * fix(cli): fix seeder to handle empty data (#42) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * chore(cli): cleanup * fix(cli): azure pipeline * [17584] Deploy to staging (#47) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile * chore: remove whitespace --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * [18630] Deploy renamed seeder to UAT (#54) * Align staging to dev (#37) Add staging pipeline file (#36) * add file * fix * Update staging (#39) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) --------- Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * fix(cli): fix seeder to handle empty data (#42) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * chore(cli): cleanup * fix(cli): azure pipeline * [17584] Deploy to staging (#47) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile * [18810] Deploy to staging (#50) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * 18630 seed real roles (#45) * feat(cli): seed roles and permissions into db * fix(cli): cleanup code * chore(cli): small update * chore(cli): refactoring roles * chore(cli): refactoring organization roles * chore(cli): update * chore(cli): rename seeder file (#52) * chore(cli): rename seeder file * chore(cli): fix eof new line --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * 18941 deploy sta in uat (#64) * Align staging to dev (#37) Add staging pipeline file (#36) * add file * fix * Update staging (#39) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) --------- Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * fix(cli): fix seeder to handle empty data (#42) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * chore(cli): cleanup * fix(cli): azure pipeline * [17584] Deploy to staging (#47) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * [18810] Deploy to staging (#50) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * [18630] Deploy renamed seeder to staging (#53) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile * 18630 seed real roles (#45) * feat(cli): seed roles and permissions into db * fix(cli): cleanup code * chore(cli): small update * chore(cli): refactoring roles * chore(cli): refactoring organization roles * chore(cli): update * chore(cli): rename seeder file (#52) * chore(cli): rename seeder file * chore(cli): fix eof new line --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro * 18941 Deploy dev into stage (#62) * Add staging pipeline file (#36) * add file * fix * feat(cli): update seeder (#35) * Add deploy stage to logto-admin (#38) * fix(cli): fix logto pipeline (#40) * fix(cli): fix seeder to handle empty data (#41) fix(cli): fix seeder to handle empty values * feat: logto uat changes (#43) * feat: logto uat changes * Update pipeline-variables/uat.yml Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> * Test migration command in Terraform (#48) fix: remove seed commands from dockerfile * 18630 seed real roles (#45) * feat(cli): seed roles and permissions into db * fix(cli): cleanup code * chore(cli): small update * chore(cli): refactoring roles * chore(cli): refactoring organization roles * chore(cli): update * chore(cli): rename seeder file (#52) * chore(cli): rename seeder file * chore(cli): fix eof new line * chore: use port 7001 instead of 5001 to avoid conflicts with forms (#56) * fix(cli): remove secrets from local seeder (#59) * feat(cli): seed is_third_party flag (#57) * feat(cli): seed is_third_party flag * fix(demo-app): trying to ignore the entire seeder local file from gitleaks * Reverted .gitleaksignore change --------- Co-authored-by: alfonsograziano Co-authored-by: Alfonso Graziano * feat(cli): seed multiple redurect uris (#58) * Feature(ogcio): messaging integration (#60) * feat(ogcio): added messaging permissions locally * feat(core): updated seeder for deployments * chore(core): added EOL --------- Co-authored-by: Alfonso Graziano * Add MyGovId Mock service to Logto (#55) * feat(demo-app): copied mock service from life events repo * feat(demo-app): add mock users + fix build * feat(demo-app): changed port to not clash with 3005 in life-events auth-service * fix(demo-app): trying to ignore the entire seeder local file from gitleaks * feat(cli): seeder updates already existing entries (#61) * feat(cli): seeder updates already existing entries * feat(cli): seeder file updated * feat(cli): update * feat(cli): documenting the seeders limitations * feat(cli): updated documentation --------- Co-authored-by: Alfonso Graziano Co-authored-by: William Monteiro Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> Co-authored-by: Marius Sebastian Besel <145235082+msebastianb@users.noreply.github.com> Co-authored-by: alfonsograziano Co-authored-by: SamSalvatico <40636569+SamSalvatico@users.noreply.github.com> --------- Co-authored-by: Alfonso Graziano Co-authored-by: William Monteiro Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> Co-authored-by: Marius Sebastian Besel <145235082+msebastianb@users.noreply.github.com> Co-authored-by: alfonsograziano Co-authored-by: SamSalvatico <40636569+SamSalvatico@users.noreply.github.com> --------- Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> Co-authored-by: Alfonso Graziano Co-authored-by: William Monteiro Co-authored-by: Marius Sebastian Besel <145235082+msebastianb@users.noreply.github.com> Co-authored-by: alfonsograziano Co-authored-by: SamSalvatico <40636569+SamSalvatico@users.noreply.github.com> * Add missing dev dependency in MyGovId mock service (#69) * fix: add missing dev dependency * fix: missing file * chore: add new line EOF * Chore(OGCIO): makefile run native (#70) * chore(core): added run native command * chore(core): re-added old docs * chore(core): added EOF * chore: add default public servant user to mygovid mock service and rename public servant (#72) * chore: add default public servant user to mygovid mock service) * chore(cli): changed public servant name for payments service * [18938] Improve DX (#71) * feat: create docker compose * fix: add missing dev dependency * fix: missing file * chore: add new line EOF * fix: update dockerfile * chore: add dockerignore * chore: add logging to mock * fix: fix network be to be * chore: build and push mygovid mock on dev branch only * fix: remove node-gyp installation * chore: reorder stages * fix: dockerfile parameter * chore: build service on every pr * fix: dockerfile path * feat: add env vars for mock endpoints * chore: remove redundant docker compose db * chore: spin up db from docker compose local * fix: remove comment * chore: remove unused env vars * Chore(mygovid): set fixed oid and sub (#73) * chore(core): node to 20.10.0 * chore(connector): fixed oid for users * chore(core): added .env.sample * chore(core): add ogcio env vars * chore(core): updated webhooks * chore(core): added docker compose db file (#75) * chore(core): added docker compose db file * fix(core): fixed mock random string * chore(core): standalone docker-compose-ogcio-logto file * [18938] Fix MyGovId mock service image push (#74) fix: use logto repository in ecr Co-authored-by: William Monteiro * [18938] Run with Docker Compose and remote images (#76) * fix: local docker * chore: use mock service image from ecr * chore: add script * chore: add make command * chore: update script to support docker compose file from cdn * docs: add documentation on docker compose * chore: add newline * [18938] Fix yml file reference (#79) fix: fix yml file reference * [19838] Add mock service to docker compose local file (#80) chore: add mygovid mock service build * Chore(OGCIO): messaging perms (#78) chore(core): messaging perms * [18938] Update command to point to HEAD (#81) * chore: fix config and command in documentation * chore: update image sha * feat: added life events permissions (#77) * [19299] Update to v1.18.0 (#82) * feat(connector): add DingTalk web connector changeset (#5940) * fix(console): avoid rendering outdated role options (#5953) * refactor(console): remove redundant notification from m2m guide (#5954) * feat(core,toolkit): add new sso_identities claim (#5955) * feat(core,toolkit): add new sso_identities claim add new sso_identities claim to the userinfo endpoint * chore: update changeset update changeset * chore: update comments update comments * refactor(core): use findUserSsoIdentites query method in user library use findUserSsoIdentites query method in user library * refactor: improve user experience (#5958) * feat(console): show version number for oss (#5950) * refactor: remove service log fkey (#5959) * refactor(console): improve onboarding data and subscription fetching (#5960) * release: version packages (#5868) * chore: launch us region (#5962) * chore: update links (#5963) * fix: use correct `disabled` logic for free plan (#5964) fix: use correct disable logic for free plan * fix(console): only show m2m role notification for m2m roles (#5957) * refactor(console): make long text breakable in roles page (#5956) * refactor(console): fix plausible hostname (#5968) * chore(deps): update dependency @logto/cloud to v0.2.5-a7eedce (#5847) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat(console): implement account deletion (#5969) * feat(console): implement account deletion * refactor: remove unused phrases * chore: add i18n phrases * refactor: add comments and error handling * fix(console): avoid skipping m2m role assignment after switching browser tabs (#5973) * fix(core): profile avatar upload should not return 400 error (#5974) * refactor(console,phrases): update rbac-related phrases (#5975) * chore(schemas): add reserved plan ID for admin tenant (#5976) * feat(core): report oidc exceptions to the appInsights (#5978) report oidc exceptions to the appInsights * refactor(console): click console logo should navigate to root page (#5981) * fix(console): language switch should work on profile page (#5980) * refactor(core): try to fix uncaught exception (#5982) * refactor(console): use permanently delete (#5979) * refactor(core): optimize redis error handling (#5965) * chore(deps): update dependency @testing-library/react to v16 (#5984) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(connector): improve DingTalk web connector readme (#5977) * feat: add dingtalk connector * refactor(connector): optimize codes * refactor(connector): optimize the logic of getting user phone * docs(connector): add English configuration guide for DingTalk * docs(connector): add table of contents * docs(connector): optimize format * chore(connector): update DingTalk web connector readme * chore(connector): apply suggestions from code review Co-authored-by: Darcy Ye Co-authored-by: Charles Zhao --------- Co-authored-by: aidenlu Co-authored-by: Darcy Ye Co-authored-by: Charles Zhao * chore(deps): update dependency i18next-browser-languagedetector to v8 (#5850) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore: remove dev flags and add changeset for m2m guide feature (#5983) * feat(console): add new feature content for m2m integration guide (#5947) * refactor(console): always display role creation hint in role assignment modal (#5988) * refactor(core,schemas): add user detail payload to User.Deleted webhook event (#5986) * refactor(core,schemas): add user detail payload to User.Deleted DataHook event add user detail data payload to the User.Deleted DataHook event * fix(core): fix unit test fix unit test * refactor(console,phrases): update role assignment modal phrases (#5989) * refactor(core): reorg organization routes * refactor(core): reorg organization queries * feat(schemas): init organization email domains table * feat(core): organization email domains apis * chore: add api docs * refactor: fix tests * feat(core): implement organization jit provisioning * refactor(core): trigger organization membership updated hook from jit * refactor: fix tests * feat(console): implement organization jit ui * feat(core,console): enable backchannel logout * chore: add tests and changeset * chore(console,core): launch organization jit * feat(core,console): organization mfa requirement * chore: add tests * feat: automatic social account linking (#5881) * feat: automatic social account linking * chore: add integration tests * chore: add changeset * fix(core): prevent uncaught promise rejection (#6009) * fix(core): prevent uncaught promise rejection prevent uncaught promise rejection crashing the app * refactor(core): remove inline await remove inline await statement * chore(core): update comment update comment * refactor(console): allow view and update `user.profile` in settings * refactor: apply suggestions from code review Co-authored-by: Charles Zhao * refactor(console): update jit styles * feat(core,console): organization jit roles * refactor: add organization jit role api tests * chore: update changeset * style(console): update tab item style in readme docs (#6013) * refactor(console): imporve custom phrase fetch request error handling (#6015) * refactor(console): improve webhook test request error handling (#6017) * refactor: rename method * feat(connector): google one tap * feat(core): google one tap * feat(experience): google one tap * chore: add tests * chore: add tests * refactor(console): update spring boot api protection guide (#6018) update spring boot api protection guide * refactor(console,experience,test): decouple isDevFeatureEnabled with isIntegrationTest (#6012) * refactor(console,experience,test): decouple isDevFeatureEnabled with isIntegrationTest decouple isDevFeatureEnabled with isIntegrationTest ENV variables * chore: update environment variable get method update environment variable get method * refactor(console): improve swr error handling that previously omitted (#6021) * chore: add comments * chore: add changeset (#6004) * feat: add dev feature disabled test (#6014) feat: implement dev feature disabled integration test implement dev feature desiabled integration test * chore: update README.md (#6038) * refactor(console): show sso status in jit domains (#6040) * feat(console): google one tap (#6034) * test(core): implement sso related integration tests (#6041) * test(core): implement sso related integration tests implement sso related integration tests * chore(core): remove unnecessary comments remove unnecessary comments * feat(console): add Ruby app guide * chore(deps): update docker/build-push-action action to v6 (#6042) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(connector): update outdated links in connector readme (#6039) * refactor(console): support non-svg logos * feat(schemas): add `agree_to_terms_policy` for sie table (#6036) * feat(console,phrases): support agree to terms policy configuration (#6037) * feat(experience): support agree to terms policies (#6044) * chore(console): update guide orders (#6047) * feat(core): jit organization roles (#6049) * refactor(core): update relation queries * refactor: fix google one tap issues (#6054) * chore(schemas): add legacy-pro tag to reserved plan ID (#6061) * feat(core): init organization app apis * refactor(core): reorg organization users api docs * chore: skip tests if needed * style(experience): add margin-bottom for terms checkbox on sign-in page (#6058) * chore(test): reorg the sso connector api cleanup logic (#6053) reorg the sso connector api cleanup logic * docs(console): add the troubleshooting section in expo guide (#6052) * docs(console): add the troubleshoot section in expo guide add the troubleshoot section in expo integration guide * chore: update the words update the words * feat(core): organization jit sso apis * feat(core): init organization app role apis * chore: rename legacy pro to grandfathered pro (#6076) * refactor(console): update subscription plan ID (#6074) * feat(core): organization jit sso * feat(console): update jit config * refactor: improve code, content, and styles * chore(deps): update dependency buffer to v6 (#6060) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(core): add dev feature tag for openapi.json (#6025) chore(core): add dev feature tag for openapi.json to indicate operation should not show up in swagger.json * chore(deps): update ikalnytskyi/action-setup-postgres action to v6 (#5815) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(experience): apply agree to terms policy for sso (#6080) * chore: update changeset (#6077) * refactor(core): update naming and fix typos * refactor(core): reorg organization routes * fix: fix dev feature disabled integration tests fix dev feature disabled integration tests * feat(core): issue subject tokens (#6045) * chore: update code owners (#6081) * refactor(core): return roles in organization app get api * feat: organization role types * chore: fix tests * feat(core): get application organizations api * feat(core): update application organization role apis * fix: change special character to fix root paramter naming issue feat: add customParameters function for fixing tenantId error on `/api/.well-known/endpoints/{tenantId}` * fix: update mocks and docstrings * feat(console): show organization list for m2m apps * refactor: filter whole supplement document if needed (#6085) * refactor: update status code * feat(console): m2m pages in organizations * refactor: add changeset and improve code * refactor(core): fork client credentials grant * feat: support prompt config for some built-in connectors (#6023) * feat: support prompt config for some built-in connectors * chore: adopt code review suggestions Co-authored-by: Gao Sun --------- Co-authored-by: Gao Sun * refactor: refactor integration test ci job (#6095) * fix: remove dev feature diff check remove dev feature diff check * refactor: refactor alteration integration test ci job refactor alteration integration test ci job * fix: fix the dev feature disbaled integration tests fix the dev feature disabled integration tests * fix: fix alteration-compatibility-test fix alteration-compatibility-test * feat(console,phrases): add issuer endpoint to application form (#6094) * feat(console,phrases): add issuer endpoint to application form add issuer endpoint to the application form * chore: add changeset add changeset * fix(schemas): explicitly set search path (#6101) * refactor(console): update role-related content and components (#6091) * refactor(console): hide backchannel for m2m apps (#6075) * refactor: add brief intro in swagger.json (#6102) * fix: include `tenantId` and its root param in responses (#6092) * fix: include tenantId and its root param in responses * refactor: use shared object --------- Co-authored-by: Gao Sun * refactor(experience,phrases): update resend passcode phrases (#6103) * feat(core): add hasPassword field to custom JWT user context (#6096) * refactor: handle potential errors during ky requests in koa-auth middleware (#6112) * feat(core): issue organization token via client credentials (#6098) * feat(core): issue organization token via client credentials * refactor: fix tests * refactor(console): upgrade mdx packages * refactor(console): remove unused config * chore: fix typo (#6110) * refactor: update nuxt guide (#6114) * refactor: update nuxt guide * refactor: polish content * refactor(console): update ruby guide (#6116) * refactor(console): update ruby guide * refactor(console): support further readings * refactor(console): reorg docs * refactor(console): update next guide (#6119) * refactor(core): update grant comments (#6120) * chore: update README.mdx (#6121) Added a missing backtick that breaks layout * refactor(console): update swift guide (#6123) * refactor(console): polish ui (#6122) * refactor(console): polish ui * refactor: fix code editor title color * refactor(console): use correct array for checking enterprise sso (#6135) * refactor(console): use correct array for checking enterprise sso * refactor(console): hide add connector button when no connector available * refactor(console): fix sso connector check conditions in the organization jit section * refactor(console): update styles * refactor(console): update express guide (#6124) * refactor(console): polish android guide (#6131) * ci: refactor integration tests workflow * ci: add spaces * refactor(test): use secure random method in integration test util (#6139) * refactor(console): update python and php guide (#6136) * refactor(console): update python/php console guide * refactor(console): improve php guide * refactor(console): improve python guide --------- Co-authored-by: Gao Sun * refactor(console): polish guides * chore(console): remove unmaintained remix guide (#6137) * refactor(console): update golang guide (#6134) * refactor(console): update golang guide * refactor: use imported uris for go docs * refactor(console): fix switch styles (#6132) * refactor(console): fix php guide (#6143) * feat: demo app dev panel (#6105) * docs(console): update the sveltekit guide (#6130) * docs(console): update the sveltekit guide update the sveltekit guide * chore(console): reorg the display user section in svltekit reorg the display user section in svltekit * refactor(console): improve content --------- Co-authored-by: Gao Sun * chore: launch jit (#6127) * chore: launch m2m app for organizations (#6129) * chore: launch m2m app for organizations * chore: add changeset * chore: normalize Logto DB region role names for DB alteration CI (#6144) * docs(console): update the expo SDK integration guide (#6126) * docs(console): update the expo SDK integration guide update the expo SDK integrtion guide * chore(console): update rn guide section title update rn guide section title * refactor(console): improve content --------- Co-authored-by: Gao Sun * feat(core,schemas): token exchange grant (#6057) * docs(console): update flutter intergration guide (#6125) improve content --------- Co-authored-by: Gao Sun * fix(experience): add missing `agreeToTermsPolicy` deps (#6148) * docs(console): update the capacitor integration guide (#6128) * docs(console): update the capacitor integration guide update the capacitor integration guide * fix(console): reorg capacitor guide reorg capacitor guide * chore(console): update the section title update the section title * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor: fix mermaid in production (#6149) Use dynamic CDN import to use Mermaid as Parcel has issues on handling the static import in production. * ci: rerun integration tests on failure (#6141) * docs(console): update the java spring guide (#6133) --------- Co-authored-by: Gao Sun * refactor(console): add retry button on error (#6158) * refactor(console): update vanilla js integration guide (#6156) * refactor(console): update vanilla js integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update react integration guide (#6151) * refactor(console): update react integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update vue integration guide (#6153) * refactor(console): update vue integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update angular integration guide (#6157) * refactor(console): update angular integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): load mermaid in dev (#6155) * feat(core): third-party applications are not allowed for token exchange (#6100) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * refactor: update compare DB alteration scripts (#6152) * refactor: update compare DB alteration scripts * chore: add comments * refactor: upgrade logto sdks (#6160) * fix(console): fix broken api resource guides (#6161) * feat(core): organization token for token exchange flow (#6106) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * feat(core,schemas): token exchange grant * feat(core): organization token for token exchange flow * refactor(console): optimize api resource guides (#6162) * fix(console): fix custom element swap in mdx (#6166) * refactor(console): add aggregated npm installation component (#6159) * refactor: update ci and package (#6167) * refactor: update ci and package * chore: fix tests * fix(console): hide error toast for non-existed application in audit logs (#6168) * fix(console): hide error toast for non-existed application in audit logs * chore: add changeset * feat: add `operationId` to HTTP methods on paths (#6108) * feat: add operationId to HTTP methods on paths * refactor(core): strictly handle routes for building operation id * chore: add changeset * refactor: reorg code * refactor: use get as verb for singular items --------- Co-authored-by: Gao Sun * feat(schemas): custom ui assets db update (#6010) * fix(core): issue `organization_id` claim for client credentials (#6170) * feat(core): handle oidc scopes for token exchange (#6147) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * feat(core,schemas): token exchange grant * feat(core): organization token for token exchange flow * feat(core): handle oidc scopes for token exchange * chore(deps): update dependency @rollup/plugin-commonjs to v26 (#5994) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(experience): rename `SingleSignOnContext` to `UserInteractionContext` (#6163) * chore(deps): update logto-io/actions-run-logto-integration-tests action to v4 (#6176) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update logto-io/actions-package-logto-artifact action to v3 (#6175) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update silverhand-io/actions-node-pnpm-run-steps action to v5 (#6174) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(console): improve dotnet guides * fix(console): fix page issues (#6181) * refactor(console): check mermaid by integration test env (#6183) * feat(core): implement new experience API routes (#5992) * feat(core): implement new interaction-session management flow implement a new interaction-session management flow for experience api use * feat(core): implement password sign-in flow implement password sign-in flow * test(core,schemas): add sign-in password tests add sign-in password tests * chore(core): update comments update comments * refactor(core): rename the password input value key rename the password input value key * refactor(core,schemas): refactor the experience API refactor the exerpience API structure * chore(test): add devFeature test add devFeature test * refactor(core): rename the path rename the path * refactor(core,schemas): refactor using the latest API design refactor using the latest API design * chore(test): replace using devFeature test statement replace using devFeature test statement * fix(core): fix lint error fix lint error * refactor(core): refactor experience API implementations refactor experience API implementations * refactor(core): replace with switch replace object map with switch * refactor: apply suggestions from code review * refactor(core): refactor the interaction class refactor the interaction class * refactor(core): update the user identification logic update the user identification logic --------- Co-authored-by: Gao Sun * feat(core): implement verification code verification API (#6001) * feat(core,schemas): implement the verification code flow implement the verification code flow * chore(core): fix rebase issue fix rebase issue * refactor(console): add chrome extension guide (#6178) * feat(core,schemas): implement social verification experience API endpoints (#6150) feat(core,schemas): implement the social verification flow implement the social verificaiton flow * release: version packages (#5987) * chore: update dependencies * chore: fix wrong conflict resolution * fix: organization and role assignment to public servant * chore: remove duplicate tab --------- Co-authored-by: Darcy Ye Co-authored-by: Xiao Yijun Co-authored-by: simeng-li Co-authored-by: Gao Sun Co-authored-by: silverhand-bot <107667382+silverhand-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Charles Zhao Co-authored-by: aiden Co-authored-by: aidenlu Co-authored-by: wangsijie Co-authored-by: Mostafa Moradian Co-authored-by: ScreenCom User * 19341 inactive public servant role (#83) * refactor(console): remove redundant notification from m2m guide (#5954) * feat(core,toolkit): add new sso_identities claim (#5955) * feat(core,toolkit): add new sso_identities claim add new sso_identities claim to the userinfo endpoint * chore: update changeset update changeset * chore: update comments update comments * refactor(core): use findUserSsoIdentites query method in user library use findUserSsoIdentites query method in user library * refactor: improve user experience (#5958) * feat(console): show version number for oss (#5950) * refactor: remove service log fkey (#5959) * refactor(console): improve onboarding data and subscription fetching (#5960) * release: version packages (#5868) * chore: launch us region (#5962) * chore: update links (#5963) * fix: use correct `disabled` logic for free plan (#5964) fix: use correct disable logic for free plan * fix(console): only show m2m role notification for m2m roles (#5957) * refactor(console): make long text breakable in roles page (#5956) * refactor(console): fix plausible hostname (#5968) * chore(deps): update dependency @logto/cloud to v0.2.5-a7eedce (#5847) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat(console): implement account deletion (#5969) * feat(console): implement account deletion * refactor: remove unused phrases * chore: add i18n phrases * refactor: add comments and error handling * fix(console): avoid skipping m2m role assignment after switching browser tabs (#5973) * fix(core): profile avatar upload should not return 400 error (#5974) * refactor(console,phrases): update rbac-related phrases (#5975) * chore(schemas): add reserved plan ID for admin tenant (#5976) * feat(core): report oidc exceptions to the appInsights (#5978) report oidc exceptions to the appInsights * refactor(console): click console logo should navigate to root page (#5981) * fix(console): language switch should work on profile page (#5980) * refactor(core): try to fix uncaught exception (#5982) * refactor(console): use permanently delete (#5979) * refactor(core): optimize redis error handling (#5965) * chore(deps): update dependency @testing-library/react to v16 (#5984) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(connector): improve DingTalk web connector readme (#5977) * feat: add dingtalk connector * refactor(connector): optimize codes * refactor(connector): optimize the logic of getting user phone * docs(connector): add English configuration guide for DingTalk * docs(connector): add table of contents * docs(connector): optimize format * chore(connector): update DingTalk web connector readme * chore(connector): apply suggestions from code review Co-authored-by: Darcy Ye Co-authored-by: Charles Zhao --------- Co-authored-by: aidenlu Co-authored-by: Darcy Ye Co-authored-by: Charles Zhao * chore(deps): update dependency i18next-browser-languagedetector to v8 (#5850) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore: remove dev flags and add changeset for m2m guide feature (#5983) * feat(console): add new feature content for m2m integration guide (#5947) * refactor(console): always display role creation hint in role assignment modal (#5988) * refactor(core,schemas): add user detail payload to User.Deleted webhook event (#5986) * refactor(core,schemas): add user detail payload to User.Deleted DataHook event add user detail data payload to the User.Deleted DataHook event * fix(core): fix unit test fix unit test * refactor(console,phrases): update role assignment modal phrases (#5989) * refactor(core): reorg organization routes * refactor(core): reorg organization queries * feat(schemas): init organization email domains table * feat(core): organization email domains apis * chore: add api docs * refactor: fix tests * feat(core): implement organization jit provisioning * refactor(core): trigger organization membership updated hook from jit * refactor: fix tests * feat(console): implement organization jit ui * feat(core,console): enable backchannel logout * chore: add tests and changeset * chore(console,core): launch organization jit * feat(core,console): organization mfa requirement * chore: add tests * feat: automatic social account linking (#5881) * feat: automatic social account linking * chore: add integration tests * chore: add changeset * fix(core): prevent uncaught promise rejection (#6009) * fix(core): prevent uncaught promise rejection prevent uncaught promise rejection crashing the app * refactor(core): remove inline await remove inline await statement * chore(core): update comment update comment * refactor(console): allow view and update `user.profile` in settings * refactor: apply suggestions from code review Co-authored-by: Charles Zhao * refactor(console): update jit styles * feat(core,console): organization jit roles * refactor: add organization jit role api tests * chore: update changeset * style(console): update tab item style in readme docs (#6013) * refactor(console): imporve custom phrase fetch request error handling (#6015) * refactor(console): improve webhook test request error handling (#6017) * refactor: rename method * feat(connector): google one tap * feat(core): google one tap * feat(experience): google one tap * chore: add tests * chore: add tests * refactor(console): update spring boot api protection guide (#6018) update spring boot api protection guide * refactor(console,experience,test): decouple isDevFeatureEnabled with isIntegrationTest (#6012) * refactor(console,experience,test): decouple isDevFeatureEnabled with isIntegrationTest decouple isDevFeatureEnabled with isIntegrationTest ENV variables * chore: update environment variable get method update environment variable get method * refactor(console): improve swr error handling that previously omitted (#6021) * chore: add comments * chore: add changeset (#6004) * feat: add dev feature disabled test (#6014) feat: implement dev feature disabled integration test implement dev feature desiabled integration test * chore: update README.md (#6038) * refactor(console): show sso status in jit domains (#6040) * feat(console): google one tap (#6034) * test(core): implement sso related integration tests (#6041) * test(core): implement sso related integration tests implement sso related integration tests * chore(core): remove unnecessary comments remove unnecessary comments * feat(console): add Ruby app guide * chore(deps): update docker/build-push-action action to v6 (#6042) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(connector): update outdated links in connector readme (#6039) * refactor(console): support non-svg logos * feat(schemas): add `agree_to_terms_policy` for sie table (#6036) * feat(console,phrases): support agree to terms policy configuration (#6037) * feat(experience): support agree to terms policies (#6044) * chore(console): update guide orders (#6047) * feat(core): jit organization roles (#6049) * refactor(core): update relation queries * refactor: fix google one tap issues (#6054) * chore(schemas): add legacy-pro tag to reserved plan ID (#6061) * feat(core): init organization app apis * refactor(core): reorg organization users api docs * chore: skip tests if needed * style(experience): add margin-bottom for terms checkbox on sign-in page (#6058) * chore(test): reorg the sso connector api cleanup logic (#6053) reorg the sso connector api cleanup logic * docs(console): add the troubleshooting section in expo guide (#6052) * docs(console): add the troubleshoot section in expo guide add the troubleshoot section in expo integration guide * chore: update the words update the words * feat(core): organization jit sso apis * feat(core): init organization app role apis * chore: rename legacy pro to grandfathered pro (#6076) * refactor(console): update subscription plan ID (#6074) * feat(core): organization jit sso * feat(console): update jit config * refactor: improve code, content, and styles * chore(deps): update dependency buffer to v6 (#6060) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(core): add dev feature tag for openapi.json (#6025) chore(core): add dev feature tag for openapi.json to indicate operation should not show up in swagger.json * chore(deps): update ikalnytskyi/action-setup-postgres action to v6 (#5815) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(experience): apply agree to terms policy for sso (#6080) * chore: update changeset (#6077) * refactor(core): update naming and fix typos * refactor(core): reorg organization routes * fix: fix dev feature disabled integration tests fix dev feature disabled integration tests * feat(core): issue subject tokens (#6045) * chore: update code owners (#6081) * refactor(core): return roles in organization app get api * feat: organization role types * chore: fix tests * feat(core): get application organizations api * feat(core): update application organization role apis * fix: change special character to fix root paramter naming issue feat: add customParameters function for fixing tenantId error on `/api/.well-known/endpoints/{tenantId}` * fix: update mocks and docstrings * feat(console): show organization list for m2m apps * refactor: filter whole supplement document if needed (#6085) * refactor: update status code * feat(console): m2m pages in organizations * refactor: add changeset and improve code * refactor(core): fork client credentials grant * feat: support prompt config for some built-in connectors (#6023) * feat: support prompt config for some built-in connectors * chore: adopt code review suggestions Co-authored-by: Gao Sun --------- Co-authored-by: Gao Sun * refactor: refactor integration test ci job (#6095) * fix: remove dev feature diff check remove dev feature diff check * refactor: refactor alteration integration test ci job refactor alteration integration test ci job * fix: fix the dev feature disbaled integration tests fix the dev feature disabled integration tests * fix: fix alteration-compatibility-test fix alteration-compatibility-test * feat(console,phrases): add issuer endpoint to application form (#6094) * feat(console,phrases): add issuer endpoint to application form add issuer endpoint to the application form * chore: add changeset add changeset * fix(schemas): explicitly set search path (#6101) * refactor(console): update role-related content and components (#6091) * refactor(console): hide backchannel for m2m apps (#6075) * refactor: add brief intro in swagger.json (#6102) * fix: include `tenantId` and its root param in responses (#6092) * fix: include tenantId and its root param in responses * refactor: use shared object --------- Co-authored-by: Gao Sun * refactor(experience,phrases): update resend passcode phrases (#6103) * feat(core): add hasPassword field to custom JWT user context (#6096) * refactor: handle potential errors during ky requests in koa-auth middleware (#6112) * feat(core): issue organization token via client credentials (#6098) * feat(core): issue organization token via client credentials * refactor: fix tests * refactor(console): upgrade mdx packages * refactor(console): remove unused config * chore: fix typo (#6110) * refactor: update nuxt guide (#6114) * refactor: update nuxt guide * refactor: polish content * refactor(console): update ruby guide (#6116) * refactor(console): update ruby guide * refactor(console): support further readings * refactor(console): reorg docs * refactor(console): update next guide (#6119) * refactor(core): update grant comments (#6120) * chore: update README.mdx (#6121) Added a missing backtick that breaks layout * refactor(console): update swift guide (#6123) * refactor(console): polish ui (#6122) * refactor(console): polish ui * refactor: fix code editor title color * refactor(console): use correct array for checking enterprise sso (#6135) * refactor(console): use correct array for checking enterprise sso * refactor(console): hide add connector button when no connector available * refactor(console): fix sso connector check conditions in the organization jit section * refactor(console): update styles * refactor(console): update express guide (#6124) * refactor(console): polish android guide (#6131) * ci: refactor integration tests workflow * ci: add spaces * refactor(test): use secure random method in integration test util (#6139) * refactor(console): update python and php guide (#6136) * refactor(console): update python/php console guide * refactor(console): improve php guide * refactor(console): improve python guide --------- Co-authored-by: Gao Sun * refactor(console): polish guides * chore(console): remove unmaintained remix guide (#6137) * refactor(console): update golang guide (#6134) * refactor(console): update golang guide * refactor: use imported uris for go docs * refactor(console): fix switch styles (#6132) * refactor(console): fix php guide (#6143) * feat: demo app dev panel (#6105) * docs(console): update the sveltekit guide (#6130) * docs(console): update the sveltekit guide update the sveltekit guide * chore(console): reorg the display user section in svltekit reorg the display user section in svltekit * refactor(console): improve content --------- Co-authored-by: Gao Sun * chore: launch jit (#6127) * chore: launch m2m app for organizations (#6129) * chore: launch m2m app for organizations * chore: add changeset * chore: normalize Logto DB region role names for DB alteration CI (#6144) * docs(console): update the expo SDK integration guide (#6126) * docs(console): update the expo SDK integration guide update the expo SDK integrtion guide * chore(console): update rn guide section title update rn guide section title * refactor(console): improve content --------- Co-authored-by: Gao Sun * feat(core,schemas): token exchange grant (#6057) * docs(console): update flutter intergration guide (#6125) improve content --------- Co-authored-by: Gao Sun * fix(experience): add missing `agreeToTermsPolicy` deps (#6148) * docs(console): update the capacitor integration guide (#6128) * docs(console): update the capacitor integration guide update the capacitor integration guide * fix(console): reorg capacitor guide reorg capacitor guide * chore(console): update the section title update the section title * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor: fix mermaid in production (#6149) Use dynamic CDN import to use Mermaid as Parcel has issues on handling the static import in production. * ci: rerun integration tests on failure (#6141) * docs(console): update the java spring guide (#6133) --------- Co-authored-by: Gao Sun * refactor(console): add retry button on error (#6158) * refactor(console): update vanilla js integration guide (#6156) * refactor(console): update vanilla js integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update react integration guide (#6151) * refactor(console): update react integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update vue integration guide (#6153) * refactor(console): update vue integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update angular integration guide (#6157) * refactor(console): update angular integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): load mermaid in dev (#6155) * feat(core): third-party applications are not allowed for token exchange (#6100) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * refactor: update compare DB alteration scripts (#6152) * refactor: update compare DB alteration scripts * chore: add comments * refactor: upgrade logto sdks (#6160) * fix(console): fix broken api resource guides (#6161) * feat(core): organization token for token exchange flow (#6106) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * feat(core,schemas): token exchange grant * feat(core): organization token for token exchange flow * refactor(console): optimize api resource guides (#6162) * fix(console): fix custom element swap in mdx (#6166) * refactor(console): add aggregated npm installation component (#6159) * refactor: update ci and package (#6167) * refactor: update ci and package * chore: fix tests * fix(console): hide error toast for non-existed application in audit logs (#6168) * fix(console): hide error toast for non-existed application in audit logs * chore: add changeset * feat: add `operationId` to HTTP methods on paths (#6108) * feat: add operationId to HTTP methods on paths * refactor(core): strictly handle routes for building operation id * chore: add changeset * refactor: reorg code * refactor: use get as verb for singular items --------- Co-authored-by: Gao Sun * feat(schemas): custom ui assets db update (#6010) * fix(core): issue `organization_id` claim for client credentials (#6170) * feat(core): handle oidc scopes for token exchange (#6147) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * feat(core,schemas): token exchange grant * feat(core): organization token for token exchange flow * feat(core): handle oidc scopes for token exchange * chore(deps): update dependency @rollup/plugin-commonjs to v26 (#5994) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(experience): rename `SingleSignOnContext` to `UserInteractionContext` (#6163) * chore(deps): update logto-io/actions-run-logto-integration-tests action to v4 (#6176) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update logto-io/actions-package-logto-artifact action to v3 (#6175) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update silverhand-io/actions-node-pnpm-run-steps action to v5 (#6174) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(console): improve dotnet guides * fix(console): fix page issues (#6181) * refactor(console): check mermaid by integration test env (#6183) * feat(core): implement new experience API routes (#5992) * feat(core): implement new interaction-session management flow implement a new interaction-session management flow for experience api use * feat(core): implement password sign-in flow implement password sign-in flow * test(core,schemas): add sign-in password tests add sign-in password tests * chore(core): update comments update comments * refactor(core): rename the password input value key rename the password input value key * refactor(core,schemas): refactor the experience API refactor the exerpience API structure * chore(test): add devFeature test add devFeature test * refactor(core): rename the path rename the path * refactor(core,schemas): refactor using the latest API design refactor using the latest API design * chore(test): replace using devFeature test statement replace using devFeature test statement * fix(core): fix lint error fix lint error * refactor(core): refactor experience API implementations refactor experience API implementations * refactor(core): replace with switch replace object map with switch * refactor: apply suggestions from code review * refactor(core): refactor the interaction class refactor the interaction class * refactor(core): update the user identification logic update the user identification logic --------- Co-authored-by: Gao Sun * feat(core): implement verification code verification API (#6001) * feat(core,schemas): implement the verification code flow implement the verification code flow * chore(core): fix rebase issue fix rebase issue * refactor(console): add chrome extension guide (#6178) * feat(core,schemas): implement social verification experience API endpoints (#6150) feat(core,schemas): implement the social verification flow implement the social verificaiton flow * release: version packages (#5987) * chore: update dependencies * chore: fix wrong conflict resolution * fix: organization and role assignment to public servant * chore: remove duplicate tab * feat: additiona inactive public servant role --------- Co-authored-by: Xiao Yijun Co-authored-by: simeng-li Co-authored-by: Gao Sun Co-authored-by: silverhand-bot <107667382+silverhand-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Charles Zhao Co-authored-by: Darcy Ye Co-authored-by: aiden Co-authored-by: aidenlu Co-authored-by: wangsijie Co-authored-by: Mostafa Moradian Co-authored-by: ScreenCom User Co-authored-by: Sara Zanellato Co-authored-by: Sara Zanellato <47299026+peschina@users.noreply.github.com> * chore(test): fix life-events rredirect-uri (#86) * feat: add analytics seed (#87) * feat: add analytics seed * [19299] Update MyGovId mock service image tag (#88) chore: update image tag * docs: update docker remote images instructions (#89) * docs: update docker remote images istructions * docs: rephrase * Fix(mygovid): connector fix parsing (#90) fix(connector): updated mapping for mygovid * fix(connector): fixed mygovid 00000 phone number (#91) * Feat(ogcio): scheduler permissions (#92) * chore(core): local seeder * chore(core): added missing resource in local * chore(core): added scheduler to ogcio seeder * [18097]: Extend docs on conflict resolution (#93) docs: add rerere docs * Feature(OGCIO) - 15380 integrate profile (#94) * feat(cli): added local profile permissions * feat(cli): updated permissions for profile * feat(cli): added eof * [16988] Home bb application (#95) * chore: seed home app * chore: change redirect uri * feat(ogcio): entraid connector (#98) * fix: dockerignore - exclude node_modules in subfolders (#97) fix(shared): dockerignore - exclude node_modules from subfolders * chore: seed upload app (#96) * chore: seed upload port (#100) * chore: fix pipelines (#102) * Adding environment approvals to pipeline (#103) * feat: seed entraid connector (#99) * feat: seed entraid connector * chore: remove <> from entraid seed config * feat: enable entraid login * Feature: add m2m profile reader (#101) * feat(cli): started adding profile app * feat(cli): added role type * feat(cli): added application organization relations * feat(cli): updated seeder * chore(cli): eof * chore(cli): fixed seeding order * fix: upload-api app port (#104) * Fix(OGCIO): 19876 always sync user profile (#105) chore(cli): set sync to true for my govid * feat(cli): add support for seeding always_issue_refresh_token option (#106) * Chore(OGCIO): shorter refresh token (#108) chore(cli): shorter refresh token * Fix(OGCIO): fix m2m permissions (#109) * fix(cli): fixed locally * fix(cli): fixed for deployments * Feature(OGCIO): add testing organisations (#110) * feat(cli): updated seeding for local testing * feat(cli): added file for testing environments * feat(cli): new permissions (#112) * chore: add new mock user (#111) * feat(cli): add analytics seeding (#107) * Feature/v1.19.0 (#113) * refactor(console): check mermaid by integration test env (#6183) * feat(core): implement new experience API routes (#5992) * feat(core): implement new interaction-session management flow implement a new interaction-session management flow for experience api use * feat(core): implement password sign-in flow implement password sign-in flow * test(core,schemas): add sign-in password tests add sign-in password tests * chore(core): update comments update comments * refactor(core): rename the password input value key rename the password input value key * refactor(core,schemas): refactor the experience API refactor the exerpience API structure * chore(test): add devFeature test add devFeature test * refactor(core): rename the path rename the path * refactor(core,schemas): refactor using the latest API design refactor using the latest API design * chore(test): replace using devFeature test statement replace using devFeature test statement * fix(core): fix lint error fix lint error * refactor(core): refactor experience API implementations refactor experience API implementations * refactor(core): replace with switch replace object map with switch * refactor: apply suggestions from code review * refactor(core): refactor the interaction class refactor the interaction class * refactor(core): update the user identification logic update the user identification logic --------- Co-authored-by: Gao Sun * feat(core): implement verification code verification API (#6001) * feat(core,schemas): implement the verification code flow implement the verification code flow * chore(core): fix rebase issue fix rebase issue * refactor(console): add chrome extension guide (#6178) * feat(core,schemas): implement social verification experience API endpoints (#6150) feat(core,schemas): implement the social verification flow implement the social verificaiton flow * release: version packages (#5987) * fix(deps): update dependency p-limit to v6 (#6182) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor: correct phrases and translate some untranslate phrases for zh-cn (#6190) * refactor: correct zh-cn translations * refactor: translate some untranslate phrases for zh-cn * feat: organization logo * refactor(experience): cache user input identifier for a better sign-in experience (#6164) * refactor(core, experience): remove `no_cache` param * refactor(experience): add hidden identifier input for browser password manager (#6165) * refactor(core): refactor identifyUser method (#6154) refactor(core): refactor the user identification flow refactor the user identification flow * refactor(experience,phrases): update phrases for link identities page (#6104) * refactor: remove unused patches (#6179) * chore(deps): update dependency superstruct to v2 (#6173) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat(core): actor token (#6171) * feat(console,schemas): add grant context to custom jwt (#6184) * feat(core): add subject token context to jwt customizer (#6185) * feat: support app-level branding * fix(deps): update dependency lru-cache to v11 (#6203) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(core): refactor the sso interaction handlers (#6186) refactor(core): revert the sso utils input refactor revert the sso utils input refactor * feat(core): implement enterprise sso verification flow (#6198) implement the enterprise sso verification flow * refactor(console): reorg logo uploads * refactor(experience): add label for input field (#6200) * feat(core): add quota guard for subject tokens (#6205) * style(experience): update floating label position (#6211) * refactor(core): update cache key * refactor(console): rename file * refactor(console): update all logo uploaders (#6209) * refactor(experience): show dark favicon (#6210) * feat(core): implement TOTP verification routes (#6201) * feat(core): implmenent totp verification routes implement totp verification routes * fix(core): update comments update comments * feat(core,schemas): implement backup codes verification (#6207) implement the backup code verification flow * refactor: fix experience branding fallback * fix(experience): use forgot password identifier in related flow (#6221) * refactor(console): improve branding experience * feat(core): handle dpop and client certificate for token exchange (#6199) * refactor: fix third-party app experience branding (#6223) * refactor(core): refactor organizations in grants (#6208) * test: add resource test cases for token exchange (#6216) * feat(core): handle dpop and client certificate for token exchange * refactor(core): refactor organizations in grants * test: add resource test cases for token exchange * feat(core,schemas): introduce new PUT experience API (#6212) * feat(core,schemas): introduce new PUT experience API introduce new PUT experience API * fix(core): fix some comments fix some comments * refactor: experience ssr (#6229) * refactor: experience ssr * refactor: fix parameter issue * chore(deps): upgrade packages * chore(deps): upgrade zod * feat(experience): support loading state for buttons (#6232) * refactor: patch type issues * chore: add changesets (#6239) * chore(deps): update vitest monorepo to v2 (major) (#6202) * chore(deps): update vitest monorepo to v2 * refactor: remove unused lint ignorings --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Gao Sun * feat(core,schemas): implement the sie settings guard (#6215) * feat(core,schemas): implement the sie settings guard implement the sie settings guard * fix(test): fix integration test fix integration test * test(core): add sie guard ut add sie guard ut * chore(core): add some comment add some comment * refactor(core): rename the sign-in-experience-settings class rename the sign-in-experience-settings class * feat: init elements * refactor(core): remove subject token api prefix (#6235) * feat(core): add get available sso connectors endpoint (#6224) feat(core): implement get sso connectors implement get sso connectors endpoint * feat(elements): init i18n * feat(core,schemas): implement the register flow (#6237) * feat(core,schemas): implement the register flow implement the register flow * refactor(core,schemas): relocate the profile type defs relocate the profile type defs * fix(core): fix the validation guard logic fix the validation guard logic * fix(core): fix social and sso identity not created bug fix social and sso identity not created bug * fix(core): fix social identities profile key fix social identities profile key * fix(core): fix sso query method fix sso query method * feat(core,schemas): add post custom ui assets api (#6118) * feat(core,schemas): add post custom ui assets api * test(core): add register integration tests (#6248) * test(core): add register integration tests add register integration tests * test: add enterprise sso integration tests add enterprise sso integration tests * feat(elements): add components * feat(core,schemas): implement the username password registration flow (#6249) * feat(core,schemas): implement the username password registration flow implement the username password registration flow * chore(core): update some comments update some comments * fix(test): fix integration tests fix integration tests * fix(test): fix lint fix lint * fix(experience): correct active state for input field (#6255) * refactor(console): use button loading in experience flow if possible (#6234) * refactor(experience): support and apply modal loading state (#6236) * refactor(experience): support and apply modal loading state * feat(experience): support cancel loading for modal * chore(elements): update readme * feat(core): add the new user provision (#6253) add the new user provision * feat(connector): enable custom headers for SMTP connector (#6256) * fix(console): fix Google connector `scope` field can not be unset bug (#6254) * style(experience): improve input filed style (#6260) * feat(core): set up proxy to host custom ui assets if available (#6214) * feat(core): set up proxy to host custom ui assets if available * refactor: use object param for koa spa proxy middleware * refactor: make queries param mandatory * style(experience): remove autofill style from input component (#6261) * fix(console): fix image upload in onboarding process (#6266) * fix(console): fix grant data card height (#6264) * fix(console): fix passwordless connector tester send failed bug (#6268) * feat(console): implement custom ui assets upload component (#6217) * ci: always set conclusion for alteration tests (#6276) * style(experience): add transition for notched border (#6265) * refactor(experience): avoid disabled button for continue button (#6271) * feat(core): add api quota guard for bring your ui feature (#6273) * fix(console): should not toast invitation sent message when creating tenant w/o invitee (#6270) fix(console): should not toast invitation sent message when creating tenant without invitee * feat(console): add impersonation price item (#6269) * fix(experience): shrink input field when autofilled by the browser (#6280) * feat(console): add impersonation tag to audit log (#6267) * feat(core,schemas): implement social/sso link and sync logic (#6257) * feat(core,schemas): implement social/sso link and sync logic implement social/sso link and sync logic * test(core): add intergration tests add integration tests * feat(core): add mfa verification guard (#6262) add mfa verification guard * chore: remove feature guard for token exchange (#6246) * chore: add changeset for impersonation (#6251) * chore(elements): move check to build * chore(deps): upgrade typescript * chore(elements): add locale changes * chore(deps): upgrade react * chore(elements): check git existence * feat(schemas): init app_secrets table * feat(core): multiple app secrets * refactor(core,schemas): refactor `CodeVerification` (#6277) * refactor(core,schemas): refactor the CodeVerification class split the CodeVerification class into EmailCodeVerification and PhoneCodeVerification * refactor(core,schemas): split CodeVerification type split CodeVerification type * fix(core): code review updates code review updates * feat: add content schema to HTTP 201 CREATED messages (#6244) feat: add content schema to 201 messages * feat(console,phrases): add bring your ui quota item to pricing table (#6274) * refactor(console,phrases,schemas): increase file upload size limit to 10mb (#6258) refactor(console,phrases,schemas): increase file upload size limit to 10 mb * feat(elements): init modal and input * refactor: fix phrases * feat(elements): init user provider * feat(elements): update name * feat(console,phrases): add bring your UI feature paywall (#6275) feat(console,phrases): add bring your ui feature paywall * chore: update README.md (#6297) * chore: update README.md * chore: add awesome list * style(experience): improve notched border animation (#6296) * fix(console): sidebar width should not be shrunk (#6299) * refactor(core): extract password-validator (#6282) * refactor(core): extract password-validator extract password validator * fix(core): update comments and rename method name update comment and rename method name * refactor(console): update file uploader component to 80px fixed height * fix(core): should not sync registered identifier from social (#6283) should not sync registered identifier from social * style(experience): use brand loading color for buttons (#6302) * chore(console): remove dev feature guard (#6303) * refactor(core): extract helpers and provision methods (#6285) extract helpers and provision methods * feat(console): support multiple app secrets * refactor(phrases): improve bring your ui field description * fix(console): add cloud guard to bring your ui form field * refactor(schemas): increase max upload file size limit to 20MB * fix(core): disable bring your ui feature for admin tenant (#6300) * fix(console): should be able to remove the zip on upload error (#6306) * refactor: generate application secret on creation * fix(console): fix loading and error handling for org details page (#6313) * refactor(console): keep button loading before redirecting to sign-in success page (#6305) * refactor: use vite for demo app * refactor(core): log app secret name * chore(phrases): sync keys and translate (#6315) * refactor(core): implement verification records map (#6289) * refactor(core): implement verificaiton records map implement verification records map * fix(core): fix invalid verification type error fix invalid verificaiton type error * fix(core): update the verification record map update the verification record map * fix(core): update some comments update some comments * refactor(core): polish promise dependency polish promise dependency * fix(core): fix the social/sso syncing profile logic fix the social/sso syncing profile logic * refactor(core): optimize the verification records map optimize the verification records map * fix(core): fix set method of VerificationRecord map fix set method of VerificationRecord map * refactor(experience): use button loading for social sign-in (#6316) * chore: add comment * chore: add comment * refactor(console): use vite * refactor(experience): use vite * refactor(console): use local mermaid import * fix(console): use correct public url (#6325) * refactor(console, experience): optimize bundling (#6326) * refactor(console, experience): optimize bundling * fix: use correct favicon paths * chore: use dynamic react dependency checking in bundling * refactor(core): rename some file names and methods (#6321) * refactor(core): rename some files name and methods rename some files name and methods, fix some comments * chore: update comments update comments * chore: update comments update comments * chore: polish the words polish the words * fix(console): check scope only when data is ready (#6329) * feat(core,schemas): implement profile fulfillment flow (#6293) * feat(core,schemas): implement profile fulfillment flow implement profile fulfillment flow * fix(test): fix integration tests fix integration tests * fix(core): fix rebase issue fix rebase issue * refactor(core): refactor the interaction set profile flow refactor the interaction set profile flow * test(core): add profile fulfillment integration tests (#6294) * test(core): add profile fufillment integration tests add profile fufillment integration tests * fix: fix integration tests fix integration tests * refactor(test): rebase and update the latest profile api rebase and update the latest profile api * fix(console): css loaded svg should be rendered properly (#6333) * fix(core): fix some webhook api body status 404 bug (#6311) * fix(core): fix some webhook api body status 404 bug fix some webhook api body status 404 bug * fix(core): improve the webhook trigger logic improve the webhook trigger logic * chore: add changeset add changeset * chore: update the changeset update the changeset * feat(core): implement the WebAuthn verification (#6308) feat(core): implement the webauthn verification implement the webauthn verification * feat(schemas): add custom data to application (#6309) * feat(core,schemas): add application custom data add application custom data * test(core): add update application with new custom data test add update application with new custom data test * refactor(console): increase custom ui assets upload timeout to 5 mins (#6319) refactor(console): increase custom ui assets upload timeout to 5mins * refactor: update logto/core cloud API usage * refactor: update code according to CR * refactor(console): update admin console using new pricing model (#6295) * refactor(console): update cloud API calls * refactor: update code according to CR * refactor: correct component usage * refactor(console): safely lazy load pages (#6332) * refactor(console): safely lazy load pages * chore(console): use react-safe-lazy * feat(core): implement the missing mfa bind and guard flow (#6320) * feat(core): implement the mfa binding flow implment the mfa binding flow * fix(test): fix integration tests fix integration tests * fix(core): fix the wrong status code fix the wrong status code * refactor(core): refactor bind backup codes refactor bind backup codes * refactor(core): extract isNewMfaVerification property (#6338) extract isNewMfaVerifrication property * refactor(core): refactor backup code generates flow (#6339) refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(console): dragging anchor in the color picker on application branding page (#6340) * test(core): add the mfa binding integration tests (#6330) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * test(core): implement the mfa binding integration tests implement the mfa binding integration tests * test(core): rebase backup code refactor rebase backup code refactor * style(console): fix custom jwt guide card style (#6343) * refactor(console): block page navigation when uploading custom ui assets (#6342) * chore(console): update bring your ui documentation link (#6317) chore(console): add bring your ui documentation link * fix(elements): fix user context tag name (#6346) * chore: launch multiple app secrets * chore: launch multiple app secrets * refactor(core): use tsup for building * refactor: use tsup for building * refactor(console): improve ux * chore: fix failed tests * refactor(connector): use tsup for building * ci: add check job * feat(console): remove beta tag for protected app (#6341) * feat(console): add passport.js guide (#6344) * chore: update plausible urls (#6349) * refactor(console, experience): solve sass deprecations (#6356) * fix(console): fix the plan title for subscription plan selector (#6348) * refactor(core): refactor openapi docs for protected app (#6331) * refactor: update per review * feat: allow app secret edit (#6352) * fix(console): add dev guard on new pricing model subscription hooks (#6363) * feat(core): migrate register flow affiliate report logic (#6334) Migrate the new user affiliate flow from interaction API. - `postAffiliateLogs` is forked from `routes/interaction/actions/helpers.ts` * refactor(core): extract verified interaction guard middleware (#6336) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * refactor(core): extract verified interaction guard middleware extract verified interaction guard middleware * refactor(console): fix text overflow issue (#6366) * refactor(core): make the interaction event mandatory (#6337) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * refactor(core): make the interaction event mandatory make the interaction event mandatory * test: update integration tests update integration tests * fix(core): fix the middleware apply bug fix the koaExperienceInteraction middleware apply bug * feat(core): add webhooks middleware to experience api (#6357) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * feat(core): add hooks middleware to experience APIs add interaction hooks to experience APIs * refactor(core): refactor experience API context type refactor experience API context type * feat(connector): added postmark connector * chore: remove unused deps (#6372) * chore: remove unused deps * chore: fix version * refactor(core): improve swagger auth description (#6367) * feat(core,schemas): add auditLogs to experience API (#6361) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * feat(core,schemas): add auditLogs to experience API add auditLogs to experience API * refactor(core): allow cloudflare insights origin in csp (#6375) refactor(core): allow cloudflare csp * feat(core,schemas): add mandatory password guard on register (#6368) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * feat(core,schemas): add mandatory password guard on register add mandatory password guard on register * feat: add advanced search params to all supported endpoints (#6358) * feat: add search params to list users endpoint * feat: implement advanced search for all supported endpoints * chore(deps): update dependency nock to v14.0.0-beta.9 (#6243) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat(cli): add cli command to setup custom ui local debugging proxy (#6365) * feat(cli): add proxy * refactor(cli): polish code per comments * refactor(cli): polish code * refactor(cli): support serving static files * chore: add changeset * refactor: polish code * refactor(cli): polish code * refactor(cli): make json parse safer * feat(core,console,phrases): add custom data editor to application details page (#6370) * feat(core,console,phrases): add custom data editor to application details page add custom data editor to application details page * chore: add changeset add changeset * fix(core): fix input params bug fix input params bug * fix(test): fix the integration tests fix the integration tests * fix(console): use the form controller element use the form controller element * fix(core,console): remove deepPartial statement remove deepPartial statement from the patch application API payload guard * fix(test): fix backchannel integration test fix backchannel integration test * fix(core): allow non-json body type when parsing (#6379) * refactor(core): make password optional in NewPasswordIdentity (#6377) refactor(core): make password optional in NewPasswordIdentity verification make password optioanl in NewPasswordIdentity verification * refactor(console): get and check `skuId` from checkout session (#6369) * refactor(console): get and check skuId from checkout session * chore: update @logto/cloud dependency * refactor: add tests for content-type in oidc apis (#6380) * refactor(console): delay module loading suspense component display by 500ms (#6345) * chore(console): remove redunant login hint usage for invitation (#6385) * fix(core): error data bug fixing (#6382) fix(core): error code bug fixing error code bug fixing * refactor(console): update billing info showed in subscription details page (#6384) * fix(console): add in-line error message (#6386) * fix(console): add in-line error message add in-line error message * refactor(console): remove old validation logic remove old validation logic * fix(console): create tenant button should stretch to full width (#6381) * fix(console): manual update subscription data when add/delete resources (#6360) * fix(console): add post response hook to update subscription info for useApi hook * refactor: wrap sync subscription data method * chore(phrases): update content (#6392) chore: update content * fix(console): fix the subscription plan display in tenant dropdown (#6393) * refactor(core): should not guard sso authentication flow (#6394) should not guard mfa and profile fulfillment for the sso authentication flow * fix(core): should not throw when not adding any new roles to a user (#6387) * fix(console): should not call cloud API when tenant ID is not valid (#6399) * refactor(console): improve guide logo and contact us logo display (#6391) * feat(core,schemas): add support for argon2d and argon2id (#6404) * feat(console): support next auth v5 (#6397) * feat: add add-on feature notice/tag * chore: define add on unit price temporarily * refactor: produce br outputs (#6376) * refactor: produce br outputs * refactor: fix favicon url * refactor: add `report:subscription:updates` Cloud scope (#6403) * Revert "refactor: add `report:subscription:updates` Cloud scope" (#6412) Revert "refactor: add `report:subscription:updates` Cloud scope (#6403)" This reverts commit e1922e9afbb8f9a0ce4b9fea4b154e9266bcedcf. * fix(console): fix unexpected 401 error toast (#6416) * feat(core): add Sentinel guard (#6374) feat(core): add sentinel protection add sentinel protection * feat(core): support google one tap (#6395) * feat(core): support google one tap support google one tap verification * fix(core): fix google one tap verification error fix google one tap verification error * fix(test): optimize social verification test optimize social verificaiton tests * fix(test): update social verification ut update social verification util unit test * refactor(core,schemas): refactor the register flow (#6401) * refactor(core,schemas): refactor the registration flow refactor the registraction flow * fix(core): remove unused method remove unused method * fix(test): remove legacy test remove legacy test * fix(core): fix webauthn verificaiton api fix webauthn verification api * feat(console): add new usage display for pro subscription plan (#6413) * release: version packages (#6197) * Fixing missing variables. --------- Co-authored-by: Gao Sun Co-authored-by: simeng-li Co-authored-by: silverhand-bot <107667382+silverhand-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Misaka_L Co-authored-by: Xiao Yijun Co-authored-by: wangsijie Co-authored-by: Charles Zhao Co-authored-by: Darcy Ye Co-authored-by: Mostafa Moradian Co-authored-by: Sten Roger Sandvik * Revert "Feature/v1.19.0" (#114) Revert "Feature/v1.19.0 (#113)" This reverts commit 744b5172a1271baa02bbe789b9bdbcb752905878. * fix(core): added user profile permissions for upload public servants (#116) * Fix: missing relation between profile reader (#117) * fix(cli): fixed relation locally * fix(cli): fixed relation in testing * fix(cli): fixed delete query (#118) * Seeded M2M management APIs application (#119) * chore: add inactive ps user (#121) * chore: add inactive ps user * chore: update * Spike/try to rebase to v1.18.0 (#122) * chore(schemas): add reserved plan ID for admin tenant (#5976) * feat(core): report oidc exceptions to the appInsights (#5978) report oidc exceptions to the appInsights * refactor(console): click console logo should navigate to root page (#5981) * fix(console): language switch should work on profile page (#5980) * refactor(core): try to fix uncaught exception (#5982) * refactor(console): use permanently delete (#5979) * refactor(core): optimize redis error handling (#5965) * chore(deps): update dependency @testing-library/react to v16 (#5984) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(connector): improve DingTalk web connector readme (#5977) * feat: add dingtalk connector * refactor(connector): optimize codes * refactor(connector): optimize the logic of getting user phone * docs(connector): add English configuration guide for DingTalk * docs(connector): add table of contents * docs(connector): optimize format * chore(connector): update DingTalk web connector readme * chore(connector): apply suggestions from code review Co-authored-by: Darcy Ye Co-authored-by: Charles Zhao --------- Co-authored-by: aidenlu Co-authored-by: Darcy Ye Co-authored-by: Charles Zhao * chore(deps): update dependency i18next-browser-languagedetector to v8 (#5850) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore: remove dev flags and add changeset for m2m guide feature (#5983) * feat(console): add new feature content for m2m integration guide (#5947) * refactor(console): always display role creation hint in role assignment modal (#5988) * refactor(core,schemas): add user detail payload to User.Deleted webhook event (#5986) * refactor(core,schemas): add user detail payload to User.Deleted DataHook event add user detail data payload to the User.Deleted DataHook event * fix(core): fix unit test fix unit test * refactor(console,phrases): update role assignment modal phrases (#5989) * refactor(core): reorg organization routes * refactor(core): reorg organization queries * feat(schemas): init organization email domains table * feat(core): organization email domains apis * chore: add api docs * refactor: fix tests * feat(core): implement organization jit provisioning * refactor(core): trigger organization membership updated hook from jit * refactor: fix tests * feat(console): implement organization jit ui * feat(core,console): enable backchannel logout * chore: add tests and changeset * chore(console,core): launch organization jit * feat(core,console): organization mfa requirement * chore: add tests * feat: automatic social account linking (#5881) * feat: automatic social account linking * chore: add integration tests * chore: add changeset * fix(core): prevent uncaught promise rejection (#6009) * fix(core): prevent uncaught promise rejection prevent uncaught promise rejection crashing the app * refactor(core): remove inline await remove inline await statement * chore(core): update comment update comment * refactor(console): allow view and update `user.profile` in settings * refactor: apply suggestions from code review Co-authored-by: Charles Zhao * refactor(console): update jit styles * feat(core,console): organization jit roles * refactor: add organization jit role api tests * chore: update changeset * style(console): update tab item style in readme docs (#6013) * refactor(console): imporve custom phrase fetch request error handling (#6015) * refactor(console): improve webhook test request error handling (#6017) * refactor: rename method * feat(connector): google one tap * feat(core): google one tap * feat(experience): google one tap * chore: add tests * chore: add tests * refactor(console): update spring boot api protection guide (#6018) update spring boot api protection guide * refactor(console,experience,test): decouple isDevFeatureEnabled with isIntegrationTest (#6012) * refactor(console,experience,test): decouple isDevFeatureEnabled with isIntegrationTest decouple isDevFeatureEnabled with isIntegrationTest ENV variables * chore: update environment variable get method update environment variable get method * refactor(console): improve swr error handling that previously omitted (#6021) * chore: add comments * chore: add changeset (#6004) * feat: add dev feature disabled test (#6014) feat: implement dev feature disabled integration test implement dev feature desiabled integration test * chore: update README.md (#6038) * refactor(console): show sso status in jit domains (#6040) * feat(console): google one tap (#6034) * test(core): implement sso related integration tests (#6041) * test(core): implement sso related integration tests implement sso related integration tests * chore(core): remove unnecessary comments remove unnecessary comments * feat(console): add Ruby app guide * chore(deps): update docker/build-push-action action to v6 (#6042) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(connector): update outdated links in connector readme (#6039) * refactor(console): support non-svg logos * feat(schemas): add `agree_to_terms_policy` for sie table (#6036) * feat(console,phrases): support agree to terms policy configuration (#6037) * feat(experience): support agree to terms policies (#6044) * chore(console): update guide orders (#6047) * feat(core): jit organization roles (#6049) * refactor(core): update relation queries * refactor: fix google one tap issues (#6054) * chore(schemas): add legacy-pro tag to reserved plan ID (#6061) * feat(core): init organization app apis * refactor(core): reorg organization users api docs * chore: skip tests if needed * style(experience): add margin-bottom for terms checkbox on sign-in page (#6058) * chore(test): reorg the sso connector api cleanup logic (#6053) reorg the sso connector api cleanup logic * docs(console): add the troubleshooting section in expo guide (#6052) * docs(console): add the troubleshoot section in expo guide add the troubleshoot section in expo integration guide * chore: update the words update the words * feat(core): organization jit sso apis * feat(core): init organization app role apis * chore: rename legacy pro to grandfathered pro (#6076) * refactor(console): update subscription plan ID (#6074) * feat(core): organization jit sso * feat(console): update jit config * refactor: improve code, content, and styles * chore(deps): update dependency buffer to v6 (#6060) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(core): add dev feature tag for openapi.json (#6025) chore(core): add dev feature tag for openapi.json to indicate operation should not show up in swagger.json * chore(deps): update ikalnytskyi/action-setup-postgres action to v6 (#5815) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(experience): apply agree to terms policy for sso (#6080) * chore: update changeset (#6077) * refactor(core): update naming and fix typos * refactor(core): reorg organization routes * fix: fix dev feature disabled integration tests fix dev feature disabled integration tests * feat(core): issue subject tokens (#6045) * chore: update code owners (#6081) * refactor(core): return roles in organization app get api * feat: organization role types * chore: fix tests * feat(core): get application organizations api * feat(core): update application organization role apis * fix: change special character to fix root paramter naming issue feat: add customParameters function for fixing tenantId error on `/api/.well-known/endpoints/{tenantId}` * fix: update mocks and docstrings * feat(console): show organization list for m2m apps * refactor: filter whole supplement document if needed (#6085) * refactor: update status code * feat(console): m2m pages in organizations * refactor: add changeset and improve code * refactor(core): fork client credentials grant * feat: support prompt config for some built-in connectors (#6023) * feat: support prompt config for some built-in connectors * chore: adopt code review suggestions Co-authored-by: Gao Sun --------- Co-authored-by: Gao Sun * refactor: refactor integration test ci job (#6095) * fix: remove dev feature diff check remove dev feature diff check * refactor: refactor alteration integration test ci job refactor alteration integration test ci job * fix: fix the dev feature disbaled integration tests fix the dev feature disabled integration tests * fix: fix alteration-compatibility-test fix alteration-compatibility-test * feat(console,phrases): add issuer endpoint to application form (#6094) * feat(console,phrases): add issuer endpoint to application form add issuer endpoint to the application form * chore: add changeset add changeset * fix(schemas): explicitly set search path (#6101) * refactor(console): update role-related content and components (#6091) * refactor(console): hide backchannel for m2m apps (#6075) * refactor: add brief intro in swagger.json (#6102) * fix: include `tenantId` and its root param in responses (#6092) * fix: include tenantId and its root param in responses * refactor: use shared object --------- Co-authored-by: Gao Sun * refactor(experience,phrases): update resend passcode phrases (#6103) * feat(core): add hasPassword field to custom JWT user context (#6096) * refactor: handle potential errors during ky requests in koa-auth middleware (#6112) * feat(core): issue organization token via client credentials (#6098) * feat(core): issue organization token via client credentials * refactor: fix tests * refactor(console): upgrade mdx packages * refactor(console): remove unused config * chore: fix typo (#6110) * refactor: update nuxt guide (#6114) * refactor: update nuxt guide * refactor: polish content * refactor(console): update ruby guide (#6116) * refactor(console): update ruby guide * refactor(console): support further readings * refactor(console): reorg docs * refactor(console): update next guide (#6119) * refactor(core): update grant comments (#6120) * chore: update README.mdx (#6121) Added a missing backtick that breaks layout * refactor(console): update swift guide (#6123) * refactor(console): polish ui (#6122) * refactor(console): polish ui * refactor: fix code editor title color * refactor(console): use correct array for checking enterprise sso (#6135) * refactor(console): use correct array for checking enterprise sso * refactor(console): hide add connector button when no connector available * refactor(console): fix sso connector check conditions in the organization jit section * refactor(console): update styles * refactor(console): update express guide (#6124) * refactor(console): polish android guide (#6131) * ci: refactor integration tests workflow * ci: add spaces * refactor(test): use secure random method in integration test util (#6139) * refactor(console): update python and php guide (#6136) * refactor(console): update python/php console guide * refactor(console): improve php guide * refactor(console): improve python guide --------- Co-authored-by: Gao Sun * refactor(console): polish guides * chore(console): remove unmaintained remix guide (#6137) * refactor(console): update golang guide (#6134) * refactor(console): update golang guide * refactor: use imported uris for go docs * refactor(console): fix switch styles (#6132) * refactor(console): fix php guide (#6143) * feat: demo app dev panel (#6105) * docs(console): update the sveltekit guide (#6130) * docs(console): update the sveltekit guide update the sveltekit guide * chore(console): reorg the display user section in svltekit reorg the display user section in svltekit * refactor(console): improve content --------- Co-authored-by: Gao Sun * chore: launch jit (#6127) * chore: launch m2m app for organizations (#6129) * chore: launch m2m app for organizations * chore: add changeset * chore: normalize Logto DB region role names for DB alteration CI (#6144) * docs(console): update the expo SDK integration guide (#6126) * docs(console): update the expo SDK integration guide update the expo SDK integrtion guide * chore(console): update rn guide section title update rn guide section title * refactor(console): improve content --------- Co-authored-by: Gao Sun * feat(core,schemas): token exchange grant (#6057) * docs(console): update flutter intergration guide (#6125) improve content --------- Co-authored-by: Gao Sun * fix(experience): add missing `agreeToTermsPolicy` deps (#6148) * docs(console): update the capacitor integration guide (#6128) * docs(console): update the capacitor integration guide update the capacitor integration guide * fix(console): reorg capacitor guide reorg capacitor guide * chore(console): update the section title update the section title * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor: fix mermaid in production (#6149) Use dynamic CDN import to use Mermaid as Parcel has issues on handling the static import in production. * ci: rerun integration tests on failure (#6141) * docs(console): update the java spring guide (#6133) --------- Co-authored-by: Gao Sun * refactor(console): add retry button on error (#6158) * refactor(console): update vanilla js integration guide (#6156) * refactor(console): update vanilla js integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update react integration guide (#6151) * refactor(console): update react integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update vue integration guide (#6153) * refactor(console): update vue integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): update angular integration guide (#6157) * refactor(console): update angular integration guide * refactor(console): improve content --------- Co-authored-by: Gao Sun * refactor(console): load mermaid in dev (#6155) * feat(core): third-party applications are not allowed for token exchange (#6100) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * refactor: update compare DB alteration scripts (#6152) * refactor: update compare DB alteration scripts * chore: add comments * refactor: upgrade logto sdks (#6160) * fix(console): fix broken api resource guides (#6161) * feat(core): organization token for token exchange flow (#6106) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * feat(core,schemas): token exchange grant * feat(core): organization token for token exchange flow * refactor(console): optimize api resource guides (#6162) * fix(console): fix custom element swap in mdx (#6166) * refactor(console): add aggregated npm installation component (#6159) * refactor: update ci and package (#6167) * refactor: update ci and package * chore: fix tests * fix(console): hide error toast for non-existed application in audit logs (#6168) * fix(console): hide error toast for non-existed application in audit logs * chore: add changeset * feat: add `operationId` to HTTP methods on paths (#6108) * feat: add operationId to HTTP methods on paths * refactor(core): strictly handle routes for building operation id * chore: add changeset * refactor: reorg code * refactor: use get as verb for singular items --------- Co-authored-by: Gao Sun * feat(schemas): custom ui assets db update (#6010) * fix(core): issue `organization_id` claim for client credentials (#6170) * feat(core): handle oidc scopes for token exchange (#6147) * feat(core,schemas): token exchange grant * feat(core): third-party applications are not allowed for token exchange * feat(core,schemas): token exchange grant * feat(core): organization token for token exchange flow * feat(core): handle oidc scopes for token exchange * chore(deps): update dependency @rollup/plugin-commonjs to v26 (#5994) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(experience): rename `SingleSignOnContext` to `UserInteractionContext` (#6163) * chore(deps): update logto-io/actions-run-logto-integration-tests action to v4 (#6176) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update logto-io/actions-package-logto-artifact action to v3 (#6175) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update silverhand-io/actions-node-pnpm-run-steps action to v5 (#6174) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(console): improve dotnet guides * fix(console): fix page issues (#6181) * refactor(console): check mermaid by integration test env (#6183) * feat(core): implement new experience API routes (#5992) * feat(core): implement new interaction-session management flow implement a new interaction-session management flow for experience api use * feat(core): implement password sign-in flow implement password sign-in flow * test(core,schemas): add sign-in password tests add sign-in password tests * chore(core): update comments update comments * refactor(core): rename the password input value key rename the password input value key * refactor(core,schemas): refactor the experience API refactor the exerpience API structure * chore(test): add devFeature test add devFeature test * refactor(core): rename the path rename the path * refactor(core,schemas): refactor using the latest API design refactor using the latest API design * chore(test): replace using devFeature test statement replace using devFeature test statement * fix(core): fix lint error fix lint error * refactor(core): refactor experience API implementations refactor experience API implementations * refactor(core): replace with switch replace object map with switch * refactor: apply suggestions from code review * refactor(core): refactor the interaction class refactor the interaction class * refactor(core): update the user identification logic update the user identification logic --------- Co-authored-by: Gao Sun * feat(core): implement verification code verification API (#6001) * feat(core,schemas): implement the verification code flow implement the verification code flow * chore(core): fix rebase issue fix rebase issue * refactor(console): add chrome extension guide (#6178) * feat(core,schemas): implement social verification experience API endpoints (#6150) feat(core,schemas): implement the social verification flow implement the social verificaiton flow * release: version packages (#5987) * feat(cli): added ogcio folder * chore(cli): added ref to ogcio command * feat(core): added env sample * chore(cli): added port collision fix * chore(core): updated docker compose * chore(cli): fixed dockerfile * chore(cli): added makefile * chore(core): updated package json * chore(core): updated run logto remote * chore(core): updated pr request template * chore(cli): ogcio connectors * chore(phrases): updated errors * chore(core): added lot of stuffs * fix(cli): fixed port * workflow main * dockerignore * readme * basics * basics * basics * basics * eof * eof * eof * chore(core): synced --------- Co-authored-by: Darcy Ye Co-authored-by: simeng-li Co-authored-by: Charles Zhao Co-authored-by: Gao Sun Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: aiden Co-authored-by: aidenlu Co-authored-by: Xiao Yijun Co-authored-by: wangsijie Co-authored-by: Mostafa Moradian Co-authored-by: ScreenCom User Co-authored-by: silverhand-bot <107667382+silverhand-bot@users.noreply.github.com> * Feature(OGCIO): merge to v1.19.0 (#124) * refactor(console): reorg logo uploads * refactor(experience): add label for input field (#6200) * feat(core): add quota guard for subject tokens (#6205) * style(experience): update floating label position (#6211) * refactor(core): update cache key * refactor(console): rename file * refactor(console): update all logo uploaders (#6209) * refactor(experience): show dark favicon (#6210) * feat(core): implement TOTP verification routes (#6201) * feat(core): implmenent totp verification routes implement totp verification routes * fix(core): update comments update comments * feat(core,schemas): implement backup codes verification (#6207) implement the backup code verification flow * refactor: fix experience branding fallback * fix(experience): use forgot password identifier in related flow (#6221) * refactor(console): improve branding experience * feat(core): handle dpop and client certificate for token exchange (#6199) * refactor: fix third-party app experience branding (#6223) * refactor(core): refactor organizations in grants (#6208) * test: add resource test cases for token exchange (#6216) * feat(core): handle dpop and client certificate for token exchange * refactor(core): refactor organizations in grants * test: add resource test cases for token exchange * feat(core,schemas): introduce new PUT experience API (#6212) * feat(core,schemas): introduce new PUT experience API introduce new PUT experience API * fix(core): fix some comments fix some comments * refactor: experience ssr (#6229) * refactor: experience ssr * refactor: fix parameter issue * chore(deps): upgrade packages * chore(deps): upgrade zod * feat(experience): support loading state for buttons (#6232) * refactor: patch type issues * chore: add changesets (#6239) * chore(deps): update vitest monorepo to v2 (major) (#6202) * chore(deps): update vitest monorepo to v2 * refactor: remove unused lint ignorings --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Gao Sun * feat(core,schemas): implement the sie settings guard (#6215) * feat(core,schemas): implement the sie settings guard implement the sie settings guard * fix(test): fix integration test fix integration test * test(core): add sie guard ut add sie guard ut * chore(core): add some comment add some comment * refactor(core): rename the sign-in-experience-settings class rename the sign-in-experience-settings class * feat: init elements * refactor(core): remove subject token api prefix (#6235) * feat(core): add get available sso connectors endpoint (#6224) feat(core): implement get sso connectors implement get sso connectors endpoint * feat(elements): init i18n * feat(core,schemas): implement the register flow (#6237) * feat(core,schemas): implement the register flow implement the register flow * refactor(core,schemas): relocate the profile type defs relocate the profile type defs * fix(core): fix the validation guard logic fix the validation guard logic * fix(core): fix social and sso identity not created bug fix social and sso identity not created bug * fix(core): fix social identities profile key fix social identities profile key * fix(core): fix sso query method fix sso query method * feat(core,schemas): add post custom ui assets api (#6118) * feat(core,schemas): add post custom ui assets api * test(core): add register integration tests (#6248) * test(core): add register integration tests add register integration tests * test: add enterprise sso integration tests add enterprise sso integration tests * feat(elements): add components * feat(core,schemas): implement the username password registration flow (#6249) * feat(core,schemas): implement the username password registration flow implement the username password registration flow * chore(core): update some comments update some comments * fix(test): fix integration tests fix integration tests * fix(test): fix lint fix lint * fix(experience): correct active state for input field (#6255) * refactor(console): use button loading in experience flow if possible (#6234) * refactor(experience): support and apply modal loading state (#6236) * refactor(experience): support and apply modal loading state * feat(experience): support cancel loading for modal * chore(elements): update readme * feat(core): add the new user provision (#6253) add the new user provision * feat(connector): enable custom headers for SMTP connector (#6256) * fix(console): fix Google connector `scope` field can not be unset bug (#6254) * style(experience): improve input filed style (#6260) * feat(core): set up proxy to host custom ui assets if available (#6214) * feat(core): set up proxy to host custom ui assets if available * refactor: use object param for koa spa proxy middleware * refactor: make queries param mandatory * style(experience): remove autofill style from input component (#6261) * fix(console): fix image upload in onboarding process (#6266) * fix(console): fix grant data card height (#6264) * fix(console): fix passwordless connector tester send failed bug (#6268) * feat(console): implement custom ui assets upload component (#6217) * ci: always set conclusion for alteration tests (#6276) * style(experience): add transition for notched border (#6265) * refactor(experience): avoid disabled button for continue button (#6271) * feat(core): add api quota guard for bring your ui feature (#6273) * fix(console): should not toast invitation sent message when creating tenant w/o invitee (#6270) fix(console): should not toast invitation sent message when creating tenant without invitee * feat(console): add impersonation price item (#6269) * fix(experience): shrink input field when autofilled by the browser (#6280) * feat(console): add impersonation tag to audit log (#6267) * feat(core,schemas): implement social/sso link and sync logic (#6257) * feat(core,schemas): implement social/sso link and sync logic implement social/sso link and sync logic * test(core): add intergration tests add integration tests * feat(core): add mfa verification guard (#6262) add mfa verification guard * chore: remove feature guard for token exchange (#6246) * chore: add changeset for impersonation (#6251) * chore(elements): move check to build * chore(deps): upgrade typescript * chore(elements): add locale changes * chore(deps): upgrade react * chore(elements): check git existence * feat(schemas): init app_secrets table * feat(core): multiple app secrets * refactor(core,schemas): refactor `CodeVerification` (#6277) * refactor(core,schemas): refactor the CodeVerification class split the CodeVerification class into EmailCodeVerification and PhoneCodeVerification * refactor(core,schemas): split CodeVerification type split CodeVerification type * fix(core): code review updates code review updates * feat: add content schema to HTTP 201 CREATED messages (#6244) feat: add content schema to 201 messages * feat(console,phrases): add bring your ui quota item to pricing table (#6274) * refactor(console,phrases,schemas): increase file upload size limit to 10mb (#6258) refactor(console,phrases,schemas): increase file upload size limit to 10 mb * feat(elements): init modal and input * refactor: fix phrases * feat(elements): init user provider * feat(elements): update name * feat(console,phrases): add bring your UI feature paywall (#6275) feat(console,phrases): add bring your ui feature paywall * chore: update README.md (#6297) * chore: update README.md * chore: add awesome list * style(experience): improve notched border animation (#6296) * fix(console): sidebar width should not be shrunk (#6299) * refactor(core): extract password-validator (#6282) * refactor(core): extract password-validator extract password validator * fix(core): update comments and rename method name update comment and rename method name * refactor(console): update file uploader component to 80px fixed height * fix(core): should not sync registered identifier from social (#6283) should not sync registered identifier from social * style(experience): use brand loading color for buttons (#6302) * chore(console): remove dev feature guard (#6303) * refactor(core): extract helpers and provision methods (#6285) extract helpers and provision methods * feat(console): support multiple app secrets * refactor(phrases): improve bring your ui field description * fix(console): add cloud guard to bring your ui form field * refactor(schemas): increase max upload file size limit to 20MB * fix(core): disable bring your ui feature for admin tenant (#6300) * fix(console): should be able to remove the zip on upload error (#6306) * refactor: generate application secret on creation * fix(console): fix loading and error handling for org details page (#6313) * refactor(console): keep button loading before redirecting to sign-in success page (#6305) * refactor: use vite for demo app * refactor(core): log app secret name * chore(phrases): sync keys and translate (#6315) * refactor(core): implement verification records map (#6289) * refactor(core): implement verificaiton records map implement verification records map * fix(core): fix invalid verification type error fix invalid verificaiton type error * fix(core): update the verification record map update the verification record map * fix(core): update some comments update some comments * refactor(core): polish promise dependency polish promise dependency * fix(core): fix the social/sso syncing profile logic fix the social/sso syncing profile logic * refactor(core): optimize the verification records map optimize the verification records map * fix(core): fix set method of VerificationRecord map fix set method of VerificationRecord map * refactor(experience): use button loading for social sign-in (#6316) * chore: add comment * chore: add comment * refactor(console): use vite * refactor(experience): use vite * refactor(console): use local mermaid import * fix(console): use correct public url (#6325) * refactor(console, experience): optimize bundling (#6326) * refactor(console, experience): optimize bundling * fix: use correct favicon paths * chore: use dynamic react dependency checking in bundling * refactor(core): rename some file names and methods (#6321) * refactor(core): rename some files name and methods rename some files name and methods, fix some comments * chore: update comments update comments * chore: update comments update comments * chore: polish the words polish the words * fix(console): check scope only when data is ready (#6329) * feat(core,schemas): implement profile fulfillment flow (#6293) * feat(core,schemas): implement profile fulfillment flow implement profile fulfillment flow * fix(test): fix integration tests fix integration tests * fix(core): fix rebase issue fix rebase issue * refactor(core): refactor the interaction set profile flow refactor the interaction set profile flow * test(core): add profile fulfillment integration tests (#6294) * test(core): add profile fufillment integration tests add profile fufillment integration tests * fix: fix integration tests fix integration tests * refactor(test): rebase and update the latest profile api rebase and update the latest profile api * fix(console): css loaded svg should be rendered properly (#6333) * fix(core): fix some webhook api body status 404 bug (#6311) * fix(core): fix some webhook api body status 404 bug fix some webhook api body status 404 bug * fix(core): improve the webhook trigger logic improve the webhook trigger logic * chore: add changeset add changeset * chore: update the changeset update the changeset * feat(core): implement the WebAuthn verification (#6308) feat(core): implement the webauthn verification implement the webauthn verification * feat(schemas): add custom data to application (#6309) * feat(core,schemas): add application custom data add application custom data * test(core): add update application with new custom data test add update application with new custom data test * refactor(console): increase custom ui assets upload timeout to 5 mins (#6319) refactor(console): increase custom ui assets upload timeout to 5mins * refactor: update logto/core cloud API usage * refactor: update code according to CR * refactor(console): update admin console using new pricing model (#6295) * refactor(console): update cloud API calls * refactor: update code according to CR * refactor: correct component usage * refactor(console): safely lazy load pages (#6332) * refactor(console): safely lazy load pages * chore(console): use react-safe-lazy * feat(core): implement the missing mfa bind and guard flow (#6320) * feat(core): implement the mfa binding flow implment the mfa binding flow * fix(test): fix integration tests fix integration tests * fix(core): fix the wrong status code fix the wrong status code * refactor(core): refactor bind backup codes refactor bind backup codes * refactor(core): extract isNewMfaVerification property (#6338) extract isNewMfaVerifrication property * refactor(core): refactor backup code generates flow (#6339) refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(console): dragging anchor in the color picker on application branding page (#6340) * test(core): add the mfa binding integration tests (#6330) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * test(core): implement the mfa binding integration tests implement the mfa binding integration tests * test(core): rebase backup code refactor rebase backup code refactor * style(console): fix custom jwt guide card style (#6343) * refactor(console): block page navigation when uploading custom ui assets (#6342) * chore(console): update bring your ui documentation link (#6317) chore(console): add bring your ui documentation link * fix(elements): fix user context tag name (#6346) * chore: launch multiple app secrets * chore: launch multiple app secrets * refactor(core): use tsup for building * refactor: use tsup for building * refactor(console): improve ux * chore: fix failed tests * refactor(connector): use tsup for building * ci: add check job * feat(console): remove beta tag for protected app (#6341) * feat(console): add passport.js guide (#6344) * chore: update plausible urls (#6349) * refactor(console, experience): solve sass deprecations (#6356) * fix(console): fix the plan title for subscription plan selector (#6348) * refactor(core): refactor openapi docs for protected app (#6331) * refactor: update per review * feat: allow app secret edit (#6352) * fix(console): add dev guard on new pricing model subscription hooks (#6363) * feat(core): migrate register flow affiliate report logic (#6334) Migrate the new user affiliate flow from interaction API. - `postAffiliateLogs` is forked from `routes/interaction/actions/helpers.ts` * refactor(core): extract verified interaction guard middleware (#6336) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * refactor(core): extract verified interaction guard middleware extract verified interaction guard middleware * refactor(console): fix text overflow issue (#6366) * refactor(core): make the interaction event mandatory (#6337) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * refactor(core): make the interaction event mandatory make the interaction event mandatory * test: update integration tests update integration tests * fix(core): fix the middleware apply bug fix the koaExperienceInteraction middleware apply bug * feat(core): add webhooks middleware to experience api (#6357) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * feat(core): add hooks middleware to experience APIs add interaction hooks to experience APIs * refactor(core): refactor experience API context type refactor experience API context type * feat(connector): added postmark connector * chore: remove unused deps (#6372) * chore: remove unused deps * chore: fix version * refactor(core): improve swagger auth description (#6367) * feat(core,schemas): add auditLogs to experience API (#6361) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * feat(core,schemas): add auditLogs to experience API add auditLogs to experience API * refactor(core): allow cloudflare insights origin in csp (#6375) refactor(core): allow cloudflare csp * feat(core,schemas): add mandatory password guard on register (#6368) * refactor(core): refactor backup code generate flow refactor backup code generate flow * fix(core): fix api payload fix api payload * fix(core): fix rebase issue fix rebase issue * feat(core,schemas): add mandatory password guard on register add mandatory password guard on register * feat: add advanced search params to all supported endpoints (#6358) * feat: add search params to list users endpoint * feat: implement advanced search for all supported endpoints * chore(deps): update dependency nock to v14.0.0-beta.9 (#6243) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat(cli): add cli command to setup custom ui local debugging proxy (#6365) * feat(cli): add proxy * refactor(cli): polish code per comments * refactor(cli): polish code * refactor(cli): support serving static files * chore: add changeset * refactor: polish code * refactor(cli): polish code * refactor(cli): make json parse safer * feat(core,console,phrases): add custom data editor to application details page (#6370) * feat(core,console,phrases): add custom data editor to application details page add custom data editor to application details page * chore: add changeset add changeset * fix(core): fix input params bug fix input params bug * fix(test): fix the integration tests fix the integration tests * fix(console): use the form controller element use the form controller element * fix(core,console): remove deepPartial statement remove deepPartial statement from the patch application API payload guard * fix(test): fix backchannel integration test fix backchannel integration test * fix(core): allow non-json body type when parsing (#6379) * refactor(core): make password optional in NewPasswordIdentity (#6377) refactor(core): make password optional in NewPasswordIdentity verification make password optioanl in NewPasswordIdentity verification * refactor(console): get and check `skuId` from checkout session (#6369) * refactor(console): get and check skuId from checkout session * chore: update @logto/cloud dependency * refactor: add tests for content-type in oidc apis (#6380) * refactor(console): delay module loading suspense component display by 500ms (#6345) * chore(console): remove redunant login hint usage for invitation (#6385) * fix(core): error data bug fixing (#6382) fix(core): error code bug fixing error code bug fixing * refactor(console): update billing info showed in subscription details page (#6384) * fix(console): add in-line error message (#6386) * fix(console): add in-line error message add in-line error message * refactor(console): remove old validation logic remove old validation logic * fix(console): create tenant button should stretch to full width (#6381) * fix(console): manual update subscription data when add/delete resources (#6360) * fix(console): add post response hook to update subscription info for useApi hook * refactor: wrap sync subscription data method * chore(phrases): update content (#6392) chore: update content * fix(console): fix the subscription plan display in tenant dropdown (#6393) * refactor(core): should not guard sso authentication flow (#6394) should not guard mfa and profile fulfillment for the sso authentication flow * fix(core): should not throw when not adding any new roles to a user (#6387) * fix(console): should not call cloud API when tenant ID is not valid (#6399) * refactor(console): improve guide logo and contact us logo display (#6391) * feat(core,schemas): add support for argon2d and argon2id (#6404) * feat(console): support next auth v5 (#6397) * feat: add add-on feature notice/tag * chore: define add on unit price temporarily * refactor: produce br outputs (#6376) * refactor: produce br outputs * refactor: fix favicon url * refactor: add `report:subscription:updates` Cloud scope (#6403) * Revert "refactor: add `report:subscription:updates` Cloud scope" (#6412) Revert "refactor: add `report:subscription:updates` Cloud scope (#6403)" This reverts commit e1922e9afbb8f9a0ce4b9fea4b154e9266bcedcf. * fix(console): fix unexpected 401 error toast (#6416) * feat(core): add Sentinel guard (#6374) feat(core): add sentinel protection add sentinel protection * feat(core): support google one tap (#6395) * feat(core): support google one tap support google one tap verification * fix(core): fix google one tap verification error fix google one tap verification error * fix(test): optimize social verification test optimize social verificaiton tests * fix(test): update social verification ut update social verification util unit test * refactor(core,schemas): refactor the register flow (#6401) * refactor(core,schemas): refactor the registration flow refactor the registraction flow * fix(core): remove unused method remove unused method * fix(test): remove legacy test remove legacy test * fix(core): fix webauthn verificaiton api fix webauthn verification api * feat(console): add new usage display for pro subscription plan (#6413) * release: version packages (#6197) * feat(cli): added ogcio folder * chore(cli): added ref to ogcio command * feat(core): added env sample * chore(cli): added port collision fix * chore(core): updated docker compose * chore(cli): fixed dockerfile * chore(cli): added makefile * chore(core): updated package json * chore(core): updated run logto remote * chore(core): updated pr request template * chore(cli): ogcio connectors * chore(phrases): updated errors * chore(core): added lot of stuffs * fix(cli): fixed port * workflow main * dockerignore * readme * basics * basics * basics * basics * eof * eof * eof * chore(core): synced --------- Co-authored-by: Gao Sun Co-authored-by: Xiao Yijun Co-authored-by: wangsijie Co-authored-by: simeng-li Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Charles Zhao Co-authored-by: Darcy Ye Co-authored-by: Mostafa Moradian Co-authored-by: Sten Roger Sandvik Co-authored-by: silverhand-bot <107667382+silverhand-bot@users.noreply.github.com> * Feat/messaging api seed role to m2m test (#125) * feat(cli): seeded M2M role locally * feat(cli): seeded M2M role for testing env * chore(core): add e2e tester role for uploads (#123) * Feat(OGCIO): seed users (#126) * feat(cli): added user seeding in local seeder * feat(cli): seed users code * feat(cli): seed testing env * feat(cli): seed testing permissions (#127) * Feat(OGCIO): invoke webhook after user creation (#128) * feat(cli): invoke webhook after user creation * chore(cli): removed console log * chore(cli): managed error * fix(cli): fixed seeding file (#129) * feat(core): seed m2m app for scheduler (#130) Co-authored-by: SamSalvatico <40636569+SamSalvatico@users.noreply.github.com> * fix(core): seeder id too long (#131) * feat(cli): change analytics permissions name (#132) * feat(cli): seed analytics sdk (#134) * chore: align with dev * chore: fix pnpm lock --------- Co-authored-by: Alfonso Graziano Co-authored-by: Norbert Nagy Co-authored-by: William Monteiro Co-authored-by: Marius Sebastian Besel <145235082+msebastianb@users.noreply.github.com> Co-authored-by: alfonsograziano Co-authored-by: SamSalvatico <40636569+SamSalvatico@users.noreply.github.com> Co-authored-by: Francesco Maida Co-authored-by: Darcy Ye Co-authored-by: Xiao Yijun Co-authored-by: simeng-li Co-authored-by: Gao Sun Co-authored-by: silverhand-bot <107667382+silverhand-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Charles Zhao Co-authored-by: aiden Co-authored-by: aidenlu Co-authored-by: wangsijie Co-authored-by: Mostafa Moradian Co-authored-by: ScreenCom User Co-authored-by: Andrea Cappadona <19609031+andreacappadona17@users.noreply.github.com> Co-authored-by: Francesco Maida Co-authored-by: Misaka_L Co-authored-by: Sten Roger Sandvik Co-authored-by: Ludwig Thufjell Co-authored-by: Alessio Koci --- .dockerignore | 3 +- ...eration-compatibility-integration-test.yml | 8 + .github/workflows/main.yml | 24 +- .gitpod.yml | 2 +- .npmrc | 6 - .scripts/compare-database.js | 4 +- .scripts/package.sh | 2 +- .vscode/settings.json | 8 +- .vscode/tsx.code-snippets | 2 +- AWESOME.md | 11 + Dockerfile | 8 +- README.OGCIO.md | 10 +- README.md | 7 +- azure_pipelines.yml | 30 +- commitlint.config.ts | 2 +- docker-compose-ogcio-logto.yml | 2 +- mygovid-mock-service/.dockerignore | 2 +- .../src/routes/static/mock-login.html | 22 +- package.json | 8 +- packages/app-insights/package.json | 6 +- packages/cli/CHANGELOG.md | 48 + packages/cli/package.json | 22 +- .../commands/database/ogcio/applications.ts | 28 +- .../database/ogcio/ogcio-seeder-local.json | 348 +- .../database/ogcio/ogcio-seeder-testing.json | 478 + .../commands/database/ogcio/ogcio-seeder.json | 273 +- .../commands/database/ogcio/ogcio-seeder.ts | 22 +- .../cli/src/commands/database/ogcio/ogcio.ts | 35 +- .../database/ogcio/organizations-rbac.ts | 82 +- .../src/commands/database/ogcio/queries.ts | 10 + .../commands/database/ogcio/resources-rbac.ts | 80 +- .../cli/src/commands/database/ogcio/users.ts | 160 + .../src/commands/database/ogcio/webhooks.ts | 4 +- packages/cli/src/commands/proxy/index.ts | 106 + packages/cli/src/commands/proxy/types.ts | 18 + packages/cli/src/commands/proxy/utils.ts | 157 + packages/cli/src/index.ts | 2 + packages/connectors/.gitignore | 3 +- .../connector-alipay-native/CHANGELOG.md | 10 + .../connector-alipay-native/package.json | 25 +- .../connector-alipay-web/CHANGELOG.md | 10 + .../connector-alipay-web/package.json | 25 +- .../connector-aliyun-dm/CHANGELOG.md | 10 + .../connector-aliyun-dm/package.json | 25 +- .../src/single-send-mail.test.ts | 1 - .../connector-aliyun-dm/src/utils.test.ts | 1 - .../connector-aliyun-sms/CHANGELOG.md | 10 + .../connector-aliyun-sms/package.json | 25 +- .../src/single-send-text.test.ts | 1 - .../connector-aliyun-sms/src/utils.test.ts | 1 - .../connectors/connector-apple/CHANGELOG.md | 10 + .../connectors/connector-apple/package.json | 27 +- .../connectors/connector-aws-ses/CHANGELOG.md | 10 + .../connectors/connector-aws-ses/package.json | 25 +- .../connectors/connector-azuread/CHANGELOG.md | 10 + .../connectors/connector-azuread/README.md | 25 +- .../connectors/connector-azuread/package.json | 25 +- .../connector-dingtalk-web/CHANGELOG.md | 10 + .../connector-dingtalk-web/package.json | 25 +- .../connectors/connector-discord/CHANGELOG.md | 10 + .../connectors/connector-discord/package.json | 25 +- .../connector-facebook/CHANGELOG.md | 10 + .../connector-facebook/package.json | 25 +- .../connector-feishu-web/CHANGELOG.md | 10 + .../connector-feishu-web/package.json | 25 +- .../connectors/connector-github/CHANGELOG.md | 10 + .../connectors/connector-github/package.json | 27 +- .../connectors/connector-google/CHANGELOG.md | 10 + .../connectors/connector-google/package.json | 27 +- .../connector-huggingface/CHANGELOG.md | 15 + .../connector-huggingface/package.json | 27 +- .../connectors/connector-kakao/CHANGELOG.md | 10 + .../connectors/connector-kakao/package.json | 25 +- .../connector-logto-email/CHANGELOG.md | 10 + .../connector-logto-email/package.json | 27 +- .../connector-logto-email/src/constant.ts | 2 +- .../connector-logto-sms/CHANGELOG.md | 10 + .../connector-logto-sms/package.json | 25 +- .../connector-logto-social-demo/CHANGELOG.md | 10 + .../connector-logto-social-demo/package.json | 25 +- .../connectors/connector-mailgun/CHANGELOG.md | 10 + .../connectors/connector-mailgun/package.json | 25 +- .../CHANGELOG.md | 10 + .../package.json | 25 +- .../connector-mock-email/CHANGELOG.md | 10 + .../connector-mock-email/package.json | 25 +- .../connector-mock-sms/CHANGELOG.md | 10 + .../connector-mock-sms/package.json | 25 +- .../connector-mock-social/CHANGELOG.md | 10 + .../connector-mock-social/package.json | 25 +- .../connector-mock-social/src/index.ts | 4 +- .../connectors/connector-mygovid/package.json | 6 +- .../connectors/connector-mygovid/src/index.ts | 25 +- .../connectors/connector-mygovid/src/types.ts | 2 + .../connectors/connector-naver/CHANGELOG.md | 10 + .../connectors/connector-naver/package.json | 25 +- .../connectors/connector-oauth2/CHANGELOG.md | 10 + .../connectors/connector-oauth2/package.json | 32 +- .../connector-ogcio-entraid/CHANGELOG.md | 3 + .../connector-ogcio-entraid/README.md | 103 + .../connector-ogcio-entraid/logo.svg | 1 + .../connector-ogcio-entraid/package.json | 74 + .../connector-ogcio-entraid/src/constant.ts | 68 + .../connector-ogcio-entraid/src/index.test.ts | 76 + .../connector-ogcio-entraid/src/index.ts | 166 + .../connector-ogcio-entraid/src/types.ts | 42 + .../connectors/connector-oidc/CHANGELOG.md | 15 + .../connectors/connector-oidc/package.json | 31 +- .../connector-postmark/CHANGELOG.md | 7 + .../connectors/connector-postmark/README.md | 61 + .../connectors/connector-postmark/logo.svg | 17 + .../connector-postmark/package.json | 68 + .../connector-postmark/src/constant.ts | 57 + .../connector-postmark/src/index.test.ts | 42 + .../connector-postmark/src/index.ts | 65 + .../connectors/connector-postmark/src/mock.ts | 26 + .../connector-postmark/src/types.ts | 32 + .../connectors/connector-saml/CHANGELOG.md | 10 + .../connectors/connector-saml/package.json | 25 +- .../connector-sendgrid-email/CHANGELOG.md | 10 + .../connector-sendgrid-email/package.json | 25 +- .../connectors/connector-smsaero/CHANGELOG.md | 10 + .../connectors/connector-smsaero/package.json | 25 +- .../connectors/connector-smtp/CHANGELOG.md | 11 + .../connectors/connector-smtp/package.json | 25 +- .../connectors/connector-smtp/src/constant.ts | 9 + .../connector-smtp/src/index.test.ts | 22 + .../connectors/connector-smtp/src/index.ts | 11 +- .../connectors/connector-smtp/src/mock.ts | 1 + .../connectors/connector-smtp/src/types.ts | 1 + .../connector-tencent-sms/CHANGELOG.md | 10 + .../connector-tencent-sms/package.json | 25 +- .../connector-twilio-sms/CHANGELOG.md | 10 + .../connector-twilio-sms/package.json | 25 +- .../connector-wechat-native/CHANGELOG.md | 10 + .../connector-wechat-native/package.json | 25 +- .../connector-wechat-web/CHANGELOG.md | 10 + .../connector-wechat-web/package.json | 25 +- .../connectors/connector-wecom/CHANGELOG.md | 10 + .../connectors/connector-wecom/package.json | 25 +- packages/connectors/templates/package.json | 6 +- .../templates/preset/rollup.config.js | 25 - .../templates/preset/tsconfig.base.json | 9 - .../templates/preset/tsconfig.build.json | 5 - .../connectors/templates/preset/tsconfig.json | 15 +- .../templates/preset/tsconfig.test.json | 7 - .../templates/preset/tsup.config.ts | 5 + packages/connectors/templates/sync-preset.js | 2 +- packages/console/.eslintrc.cjs | 9 + packages/console/.parcelrc | 19 - packages/console/.parcelrc.arm64 | 23 - packages/console/CHANGELOG.md | 62 + packages/console/{src => }/index.html | 6 +- packages/console/package.json | 70 +- packages/console/parcel-transformer-mdx2.js | 61 - ...generate-jwt-customizer-type-definition.ts | 9 + packages/console/src/App.tsx | 2 + .../console/src/assets/docs/guides/README.md | 3 + .../docs/guides/api-express/logo-dark.svg | 10 + .../assets/docs/guides/api-express/logo.svg | 11 +- .../assets/docs/guides/api-python/logo.svg | 15 +- .../docs/guides/api-spring-boot/logo.svg | 10 +- .../assets/docs/guides/generate-metadata.js | 28 +- .../console/src/assets/docs/guides/index.tsx | 116 +- .../docs/guides/m2m-general/logo-dark.svg | 35 + .../assets/docs/guides/m2m-general/logo.svg | 52 +- .../docs/guides/native-android/logo.svg | 13 +- .../docs/guides/native-capacitor/logo.svg | 21 +- .../docs/guides/native-expo/logo-dark.svg | 3 + .../assets/docs/guides/native-expo/logo.svg | 4 +- .../docs/guides/native-flutter/logo.svg | 28 +- .../docs/guides/native-ios-swift/logo.svg | 13 +- .../assets/docs/guides/spa-angular/logo.svg | 28 +- .../docs/guides/spa-chrome-extension/logo.svg | 23 +- .../src/assets/docs/guides/spa-react/logo.svg | 11 +- .../assets/docs/guides/spa-vanilla/logo.svg | 14 +- .../src/assets/docs/guides/spa-vue/logo.svg | 6 +- .../assets/docs/guides/spa-webflow/logo.svg | 11 +- .../docs/guides/third-party-oidc/logo.svg | 21 +- .../console/src/assets/docs/guides/types.ts | 3 + .../web-dotnet-core-blazor-server/logo.svg | 8 +- .../web-dotnet-core-blazor-wasm/logo.svg | 8 +- .../fragments/_add-authentication.mdx | 2 +- .../docs/guides/web-dotnet-core-mvc/logo.svg | 14 +- .../docs/guides/web-dotnet-core/logo.svg | 14 +- .../assets/docs/guides/web-express/README.mdx | 2 +- .../docs/guides/web-express/logo-dark.svg | 10 + .../assets/docs/guides/web-express/logo.svg | 11 +- .../src/assets/docs/guides/web-go/README.mdx | 2 +- .../src/assets/docs/guides/web-go/logo.svg | 10 +- .../components/AlwaysIssueRefreshToken.tsx | 1 + .../components/ClientBasics/index.tsx | 2 +- .../docs/guides/web-gpt-plugin/logo.svg | 12 +- .../guides/web-java-spring-boot/README.mdx | 2 +- .../docs/guides/web-java-spring-boot/index.ts | 3 +- .../docs/guides/web-java-spring-boot/logo.svg | 11 +- .../guides/web-next-app-router/README.mdx | 2 +- .../guides/web-next-app-router/logo-dark.svg | 10 + .../docs/guides/web-next-app-router/logo.svg | 11 +- .../docs/guides/web-next-auth/README.mdx | 43 +- .../assets/docs/guides/web-next-auth/logo.svg | 40 +- .../assets/docs/guides/web-next/README.mdx | 2 +- .../assets/docs/guides/web-next/logo-dark.svg | 10 + .../src/assets/docs/guides/web-next/logo.svg | 11 +- .../assets/docs/guides/web-nuxt/README.mdx | 4 +- .../src/assets/docs/guides/web-nuxt/logo.svg | 4 +- .../docs/guides/web-outline/logo-dark.svg | 10 + .../assets/docs/guides/web-outline/logo.svg | 13 +- .../docs/guides/web-passport/README.mdx | 163 + .../docs/guides/web-passport/config.json | 3 + .../assets/docs/guides/web-passport/index.ts | 11 + .../docs/guides/web-passport/logo-dark.svg | 6 + .../assets/docs/guides/web-passport/logo.svg | 6 + .../src/assets/docs/guides/web-php/README.mdx | 2 +- .../src/assets/docs/guides/web-php/logo.svg | 15 +- .../assets/docs/guides/web-python/README.mdx | 2 +- .../assets/docs/guides/web-python/logo.svg | 15 +- .../assets/docs/guides/web-ruby/README.mdx | 4 +- .../src/assets/docs/guides/web-ruby/logo.svg | 199 + .../src/assets/docs/guides/web-ruby/logo.webp | Bin 33844 -> 0 bytes .../docs/guides/web-sveltekit/README.mdx | 2 +- .../assets/docs/guides/web-sveltekit/logo.svg | 5 +- .../assets/docs/guides/web-wordpress/logo.svg | 8 +- .../src/assets/icons/calendar-dark.svg | 9 + .../console/src/assets/icons/calendar.svg | 2 +- packages/console/src/assets/icons/email.svg | 2 +- packages/console/src/assets/icons/github.svg | 2 +- .../src/assets/images/blur-preview.svg | 102 + packages/console/src/cloud/AppRoutes.tsx | 2 +- .../cloud/pages/Main/InvitationList/index.tsx | 4 +- .../TenantLandingPageContent/index.tsx | 8 +- .../pages/Main/TenantLandingPage/index.tsx | 2 +- .../cloud/pages/SocialDemoCallback/index.tsx | 6 +- packages/console/src/cloud/types/router.ts | 20 + .../src/components/ActionBar/index.tsx | 2 +- .../src/components/ActionsButton/index.tsx | 8 +- .../AddOnNoticeFooter/index.module.scss | 17 + .../components/AddOnNoticeFooter/index.tsx | 30 + .../console/src/components/AppError/index.tsx | 10 +- .../src/components/AppLoading/index.tsx | 4 +- .../CreateForm/Footer/index.module.scss | 3 + .../CreateForm/Footer/index.tsx | 45 +- .../ApplicationCreation/CreateForm/index.tsx | 17 +- .../src/components/ApplicationName/index.tsx | 2 +- .../components/EventName/index.module.scss | 1 + .../components/EventName/index.tsx | 8 +- .../src/components/AuditLogTable/index.tsx | 2 +- .../src/components/BasicWebhookForm/index.tsx | 2 +- .../console/src/components/BillInfo/index.tsx | 4 +- .../src/components/Breakable/index.tsx | 2 +- .../components/ChargeNotification/index.tsx | 5 +- .../ConnectorForm/BasicForm/index.tsx | 78 +- .../ConfigForm/ConfigFormFields/index.tsx | 2 +- .../ConnectorForm/ConfigForm/index.tsx | 2 +- .../ConnectorForm/GoogleOneTapCard/index.tsx | 2 +- .../src/components/ConnectorLogo/index.tsx | 2 +- .../src/components/ConnectorTester/index.tsx | 2 +- .../src/components/Conversion/index.tsx | 3 +- .../ConnectorRadio/index.tsx | 2 +- .../ConnectorRadioGroup/index.tsx | 2 +- .../CreateConnectorForm/Footer/index.tsx | 68 +- .../PlatformSelector/index.tsx | 2 +- .../Skeleton/index.module.scss | 6 +- .../CreateConnectorForm/Skeleton/index.tsx | 6 +- .../components/CreateConnectorForm/index.tsx | 4 +- .../EnvTagOptionContent/index.tsx | 2 +- .../FeaturedPlanContent/index.tsx | 6 +- .../PlanCardItem/index.tsx | 4 +- .../SkuCardItem/FeaturedSkuContent/index.tsx | 35 + .../use-featured-sku-content.ts | 77 + .../SkuCardItem/index.tsx | 100 + .../SelectTenantPlanModal/index.tsx | 86 +- .../components/CreateTenantModal/index.tsx | 8 +- .../console/src/components/DateTime/index.tsx | 34 +- .../src/components/DetailsForm/index.tsx | 2 +- .../DetailsPage/DetailsPageHeader/index.tsx | 4 +- .../DetailsPage/Skeleton/index.module.scss | 8 +- .../components/DetailsPage/Skeleton/index.tsx | 2 +- .../src/components/DetailsPage/index.tsx | 4 +- .../console/src/components/Drawer/index.tsx | 4 +- .../src/components/EditScopeModal/index.tsx | 2 +- .../components/EmptyDataPlaceholder/index.tsx | 6 +- .../components/EntityItem/index.tsx | 2 +- .../components/SourceEntitiesBox/index.tsx | 6 +- .../components/SourceEntityItem/index.tsx | 2 +- .../components/TargetEntitiesBox/index.tsx | 4 +- .../components/TargetEntityItem/index.tsx | 4 +- .../src/components/EntitiesTransfer/index.tsx | 4 +- .../src/components/FeatureTag/AddOnTag.tsx | 19 + .../src/components/FeatureTag/BetaTag.tsx | 2 +- .../src/components/FeatureTag/index.tsx | 17 +- .../console/src/components/FileIcon/index.tsx | 20 + .../FormCard/FormCardLayout/index.module.scss | 2 +- .../FormCard/FormCardLayout/index.tsx | 2 +- .../FormCard/Skeleton/index.module.scss | 14 +- .../components/FormCard/Skeleton/index.tsx | 9 +- .../console/src/components/FormCard/index.tsx | 2 +- .../Guide/GuideCard/index.module.scss | 9 +- .../src/components/Guide/GuideCard/index.tsx | 11 +- .../components/Guide/GuideCardGroup/index.tsx | 2 +- .../components/Guide/ModalFooter/index.tsx | 2 +- .../Guide/ModalHeader/RequestForm.tsx | 2 +- .../components/Guide/ModalHeader/index.tsx | 4 +- .../Guide/StepsSkeleton/index.module.scss | 6 +- .../components/Guide/StepsSkeleton/index.tsx | 2 +- .../console/src/components/Guide/index.tsx | 5 +- .../components/ImageInputs/LogoAndFavicon.tsx | 62 + .../ImageInputs}/index.module.scss | 17 +- .../src/components/ImageInputs/index.tsx | 169 + .../console/src/components/Index/index.tsx | 4 +- .../src/components/InlineUpsell/index.tsx | 2 +- .../ItemPreview/ApplicationPreview.tsx | 2 +- .../src/components/ItemPreview/index.tsx | 2 +- .../console/src/components/ListPage/index.tsx | 4 +- .../components/LivePreviewButton/index.tsx | 4 +- .../index.tsx | 2 +- .../console/src/components/Markdown/index.tsx | 4 +- .../src/components/MauExceededModal/index.tsx | 20 +- .../src/components/MfaFactorTitle/index.tsx | 10 +- .../src/components/MultiOptionInput/index.tsx | 4 +- .../components/MultiTextInputField/index.tsx | 2 +- .../src/components/OpenExternalLink/index.tsx | 2 +- .../src/components/OrganizationList/index.tsx | 6 +- .../OrganizationRolesSelect/index.tsx | 4 +- .../components/PaymentOverdueModal/index.tsx | 4 +- .../src/components/PermissionsTable/index.tsx | 8 +- .../src/components/PlanDescription/index.tsx | 14 +- .../console/src/components/PlanName/index.tsx | 22 +- .../ProPlanUsageCard/index.module.scss | 35 + .../PlanUsage/ProPlanUsageCard/index.tsx | 78 + .../components/PlanUsage/index.module.scss | 17 + .../src/components/PlanUsage/index.tsx | 64 +- .../console/src/components/PlanUsage/utils.ts | 77 + .../src/components/ProgressBar/index.tsx | 2 +- .../src/components/QuotaGuardFooter/index.tsx | 2 +- .../console/src/components/Region/index.tsx | 2 +- .../src/components/RequestDataError/index.tsx | 6 +- .../components/RoleAssignmentModal/index.tsx | 4 +- .../console/src/components/RoleIcon/index.tsx | 4 +- .../components/ResourceItem/index.tsx | 6 +- .../components/SourceScopeItem/index.tsx | 2 +- .../components/SourceScopesBox/index.tsx | 6 +- .../components/TargetScopeItem/index.tsx | 4 +- .../components/TargetScopesBox/index.tsx | 4 +- .../components/RoleScopesTransfer/index.tsx | 4 +- .../SourceRolesBox/SourceRoleItem/index.tsx | 2 +- .../RolesTransfer/SourceRolesBox/index.tsx | 6 +- .../TargetRolesBox/TargetRoleItem/index.tsx | 4 +- .../RolesTransfer/TargetRolesBox/index.tsx | 4 +- .../components/RoleInformation/index.tsx | 4 +- .../src/components/RolesTransfer/index.tsx | 6 +- .../components/ToggleUiThemeButton/index.tsx | 6 +- .../SignInExperiencePreview/index.module.scss | 24 + .../SignInExperiencePreview/index.tsx | 78 +- .../SubmitFormChangesActionBar/index.tsx | 5 +- .../src/components/TemplateTable/index.tsx | 6 +- .../src/components/TenantEnvTag/index.tsx | 2 +- .../src/components/ThemedIcon/index.tsx | 2 +- .../components/Topbar/ContactModal/hook.tsx | 17 +- .../components/Topbar/ContactModal/index.tsx | 4 +- .../TenantDropdownItem/TenantStatusTag.tsx | 29 +- .../TenantDropdownItem/index.tsx | 22 +- .../TenantInvitationDropdownItem/index.tsx | 2 +- .../Topbar/TenantSelector/index.module.scss | 7 +- .../Topbar/TenantSelector/index.tsx | 6 +- .../Topbar/UserInfo/SubMenu/index.tsx | 6 +- .../UserInfoSkeleton/index.module.scss | 4 +- .../UserInfo/UserInfoSkeleton/index.tsx | 2 +- .../src/components/Topbar/UserInfo/index.tsx | 12 +- .../src/components/Topbar/index.module.scss | 6 +- .../console/src/components/Topbar/index.tsx | 12 +- .../UnsavedChangesAlertModal/index.tsx | 2 +- .../UserAccountInformation/index.tsx | 6 +- .../src/components/UserAvatar/index.tsx | 4 +- .../src/components/UserInfoCard/index.tsx | 2 +- .../console/src/components/UserName/index.tsx | 2 +- .../VerificationCodeInput/index.tsx | 2 +- packages/console/src/consts/applications.ts | 24 +- packages/console/src/consts/connectors.ts | 4 +- packages/console/src/consts/env.ts | 11 +- packages/console/src/consts/external-links.ts | 4 + packages/console/src/consts/logs.ts | 1 + packages/console/src/consts/plan-quotas.ts | 29 + .../console/src/consts/quota-item-phrases.ts | 123 + packages/console/src/consts/subscriptions.ts | 10 + packages/console/src/consts/tenants.ts | 92 +- packages/console/src/consts/user-assets.ts | 17 - .../CreateProductionTenantBanner/index.tsx | 2 +- .../TenantEnvMigrationModal/index.tsx | 10 +- .../AppContent/TenantSuspendedPage/index.tsx | 6 +- .../src/containers/AppContent/index.tsx | 27 +- .../Sidebar/components/Item/index.tsx | 2 +- .../Sidebar/components/Section/index.tsx | 2 +- .../ConsoleContent/Sidebar/hook.tsx | 34 +- .../ConsoleContent/Sidebar/index.module.scss | 7 + .../ConsoleContent/Sidebar/index.tsx | 6 +- .../src/containers/ConsoleContent/hooks.ts | 10 +- .../ConsoleContent/index.module.scss | 11 +- .../src/containers/ConsoleContent/index.tsx | 36 +- .../src/containers/ConsoleRoutes/index.tsx | 65 +- .../src/containers/ConsoleRoutes/internal.ts | 14 + .../src/contexts/AppThemeProvider/index.tsx | 8 +- .../SubscriptionDataProvider/index.tsx | 22 +- .../SubscriptionDataProvider/types.ts | 24 +- .../use-new-subscription-data.ts | 64 + .../src/ds-components/ActionMenu/index.tsx | 2 +- .../src/ds-components/Button/index.tsx | 2 +- .../console/src/ds-components/Card/index.tsx | 2 +- .../src/ds-components/CardTitle/index.tsx | 2 +- .../CategorizedCheckboxGroup/index.tsx | 2 +- .../Checkbox/Checkbox/index.module.scss | 1 + .../ds-components/Checkbox/Checkbox/index.tsx | 2 +- .../Checkbox/CheckboxGroup/index.tsx | 2 +- .../src/ds-components/CodeEditor/index.tsx | 2 +- .../ColorPicker/index.module.scss | 4 + .../src/ds-components/ColorPicker/index.tsx | 23 +- .../src/ds-components/ConfirmModal/index.tsx | 4 +- .../CopyToClipboard/index.module.scss | 22 +- .../ds-components/CopyToClipboard/index.tsx | 18 +- .../DataTransferBox/SourceDataItem/index.tsx | 2 +- .../DataTransferBox/SourceGroupItem/index.tsx | 6 +- .../DataTransferBox/SourcePanel/index.tsx | 6 +- .../DataTransferBox/TargetDataItem/index.tsx | 4 +- .../DataTransferBox/TargetPanel/index.tsx | 4 +- .../ds-components/DataTransferBox/index.tsx | 4 +- .../src/ds-components/Divider/index.tsx | 2 +- .../DragDrop/DragDropProvider.tsx | 2 +- .../ds-components/Dropdown/DropdownItem.tsx | 2 +- .../src/ds-components/Dropdown/index.tsx | 2 +- .../FormField/Skeleton.module.scss | 11 + .../src/ds-components/FormField/Skeleton.tsx | 18 + .../ds-components/FormField/index.module.scss | 4 + .../src/ds-components/FormField/index.tsx | 8 +- .../src/ds-components/IconButton/index.tsx | 2 +- .../ImageWithErrorFallback/index.tsx | 4 +- .../InlineNotification/index.tsx | 10 +- .../KeyValueInputField/index.tsx | 6 +- .../src/ds-components/ModalHeader/index.tsx | 4 +- .../src/ds-components/ModalLayout/index.tsx | 6 +- .../ds-components/MultiTextInput/index.tsx | 6 +- .../src/ds-components/Pagination/index.tsx | 2 +- .../src/ds-components/RadioGroup/Radio.tsx | 2 +- .../src/ds-components/RadioGroup/index.tsx | 2 +- .../src/ds-components/Search/index.tsx | 4 +- .../src/ds-components/Select/MultiSelect.tsx | 4 +- .../src/ds-components/Select/index.tsx | 10 +- .../src/ds-components/Spacer/index.tsx | 2 +- .../src/ds-components/Spinner/index.tsx | 2 +- .../src/ds-components/Switch/index.tsx | 2 +- .../src/ds-components/TabNav/TabNavItem.tsx | 2 +- .../src/ds-components/TabNav/index.tsx | 2 +- .../src/ds-components/TabWrapper/index.tsx | 2 +- .../Table/Skeleton/index.module.scss | 8 +- .../ds-components/Table/Skeleton/index.tsx | 2 +- .../ds-components/Table/TableEmptyWrapper.tsx | 2 +- .../src/ds-components/Table/TableError.tsx | 6 +- .../ds-components/Table/TablePlaceholder.tsx | 2 +- .../console/src/ds-components/Table/index.tsx | 2 +- .../console/src/ds-components/Tag/index.tsx | 6 +- .../ds-components/TextInput/NumericInput.tsx | 6 +- .../ds-components/TextInput/index.module.scss | 2 +- .../src/ds-components/TextInput/index.tsx | 6 +- .../src/ds-components/TextLink/index.tsx | 2 +- .../src/ds-components/Textarea/index.tsx | 2 +- .../src/ds-components/Tip/TipBubble/index.tsx | 2 +- .../src/ds-components/Tip/TipBubble/utils.ts | 4 +- .../src/ds-components/Tip/ToggleTip/index.tsx | 4 +- .../src/ds-components/Tip/Tooltip/index.tsx | 2 +- .../console/src/ds-components/Toast/index.tsx | 6 +- .../Uploader/FileUploader/index.module.scss | 24 +- .../Uploader/FileUploader/index.tsx | 54 +- .../Uploader/ImageUploader/index.tsx | 8 +- .../Uploader/ImageUploaderField/index.tsx | 6 +- .../src/hooks/use-api-resources-usage.ts | 55 +- packages/console/src/hooks/use-api.ts | 32 +- .../src/hooks/use-applications-usage.ts | 118 +- .../use-connector-form-config-parser.tsx | 8 +- .../src/hooks/use-console-routes/index.tsx | 8 +- .../routes/api-resources.tsx | 14 +- .../routes/applications.tsx | 8 +- .../use-console-routes/routes/audit-logs.tsx | 5 +- .../use-console-routes/routes/connectors.tsx | 6 +- .../routes/customize-jwt.tsx | 5 +- .../routes/enterprise-sso.tsx | 6 +- .../hooks/use-console-routes/routes/mfa.tsx | 3 +- .../routes/organization-template.tsx | 18 +- .../routes/organizations.tsx | 14 +- .../use-console-routes/routes/profile.tsx | 15 +- .../hooks/use-console-routes/routes/roles.tsx | 14 +- .../routes/sign-in-experience.tsx | 4 +- .../routes/tenant-settings.tsx | 24 +- .../hooks/use-console-routes/routes/users.tsx | 16 +- .../use-console-routes/routes/webhooks.tsx | 12 +- packages/console/src/hooks/use-logto-skus.ts | 52 + .../src/hooks/use-new-subscription-quota.ts | 19 + .../use-new-subscription-scopes-usage.ts | 39 + .../src/hooks/use-new-subscription-usage.ts | 19 + packages/console/src/hooks/use-subscribe.ts | 58 +- .../src/hooks/use-subscription-plans.ts | 1 + .../src/hooks/use-subscription-usage.ts | 1 + .../src/hooks/use-user-assets-service.ts | 13 + .../console/src/hooks/use-user-preferences.ts | 1 + .../src/icons/ConnectorPlatformIcon.tsx | 6 +- packages/console/src/include.d/react-app.d.ts | 65 - .../src/include.d/react-router-dom.d.ts | 1 - packages/console/src/include.d/vite-env.d.ts | 6 + .../ApplicationCredentials/index.tsx | 10 +- .../mdx-components/DetailsSummary/index.tsx | 4 +- .../src/mdx-components/Mermaid/index.tsx | 29 +- .../mdx-components/OidcCallbackUri/index.tsx | 2 +- .../src/mdx-components/Sample/index.tsx | 2 +- .../SsoSamlSpMetadata/index.tsx | 2 +- .../console/src/mdx-components/Step/index.tsx | 2 +- .../src/mdx-components/Steps/index.tsx | 2 +- .../console/src/mdx-components/Tabs/index.tsx | 2 +- .../mdx-components/UriInputField/index.tsx | 5 +- .../MultiCardSelector/CardItem.tsx | 2 +- .../CardSelector/MultiCardSelector/index.tsx | 2 +- .../components/DemoConnectorNotice/index.tsx | 2 +- .../onboarding/components/Topbar/index.tsx | 4 +- packages/console/src/onboarding/index.tsx | 2 +- .../onboarding/pages/CreateTenant/index.tsx | 34 +- .../InspireMe/index.module.scss | 2 +- .../SignInExperience/InspireMe/index.tsx | 6 +- .../Preview/PlatformTabs/PlatformTab.tsx | 2 +- .../Preview/PlatformTabs/index.tsx | 6 +- .../pages/SignInExperience/Preview/index.tsx | 2 +- .../Skeleton/index.module.scss | 4 +- .../pages/SignInExperience/Skeleton/index.tsx | 6 +- .../pages/SignInExperience/index.tsx | 13 +- .../pages/SignInExperience/options.tsx | 18 +- .../src/onboarding/pages/Welcome/index.tsx | 6 +- .../src/onboarding/pages/Welcome/options.tsx | 4 +- .../AcceptInvitation/SwitchAccount/index.tsx | 4 +- .../src/pages/AcceptInvitation/index.tsx | 5 +- .../CreatePermissionModal/index.tsx | 36 +- .../components/GuideDrawer/index.tsx | 6 +- .../components/GuideModal/index.tsx | 4 +- .../src/pages/ApiResourceDetails/index.tsx | 14 +- .../components/CreateForm/Footer.tsx | 40 +- .../components/CreateForm/index.module.scss | 3 + .../components/CreateForm/index.tsx | 14 +- .../components/GuideLibrary/index.tsx | 2 +- .../components/GuideLibraryModal/index.tsx | 4 +- .../console/src/pages/ApiResources/index.tsx | 10 +- .../Branding/LogoUploader.module.scss | 34 - .../Branding/LogoUploader.tsx | 79 - .../Branding/NonThirdPartyBrandingForm.tsx | 94 + .../Branding/index.module.scss | 13 + .../Branding/index.tsx | 155 +- .../Branding/utils.ts | 42 +- .../CreateSecretModal.tsx | 150 + .../EditSecretModal.tsx | 86 + .../EndpointsAndCredentials/index.module.scss | 35 + .../index.tsx} | 146 +- .../use-secret-table-columns.tsx | 108 + .../GuideDrawer/index.tsx | 11 +- .../MachineLogs/index.tsx | 2 +- .../index.tsx | 6 +- .../index.tsx | 2 +- .../Permissions/PermissionsCard/index.tsx | 2 +- .../PermissionsCard/use-scopes-table.tsx | 4 +- .../Permissions/index.tsx | 2 +- .../components/SessionForm.tsx | 2 +- .../ProtectedAppSettings/index.module.scss | 2 +- .../ProtectedAppSettings/index.tsx | 6 +- .../ApplicationDetailsContent/Settings.tsx | 29 +- .../index.module.scss | 15 - .../ApplicationDetailsContent/index.tsx | 78 +- .../ApplicationDetailsContent/utils.ts | 13 +- .../ApplicationDetails/GuideModal/index.tsx | 18 +- .../components/AppGuide/index.tsx | 31 +- .../src/pages/ApplicationDetails/index.tsx | 30 +- .../components/GuideLibrary/index.tsx | 4 +- .../components/GuideLibraryModal/index.tsx | 4 +- .../components/ProtectedAppCard/index.tsx | 8 +- .../components/ProtectedAppForm/index.tsx | 22 +- .../components/ProtectedAppModal/index.tsx | 2 +- .../components/TypeDescription/index.tsx | 2 +- .../console/src/pages/Applications/index.tsx | 6 +- .../components/EventIcon/index.tsx | 6 +- .../pages/AuditLogDetails/index.module.scss | 3 + .../src/pages/AuditLogDetails/index.tsx | 10 +- .../console/src/pages/AuditLogs/index.tsx | 2 +- .../pages/CheckoutSuccessCallback/index.tsx | 48 +- .../EmailServiceConnectorForm/index.tsx | 6 +- .../ConnectorContent/index.tsx | 12 +- .../ConnectorDetails/ConnectorTabs/index.tsx | 2 +- .../ConnectorDetails/EmailUsage/index.tsx | 8 +- .../src/pages/ConnectorDetails/index.tsx | 8 +- .../ConnectorDeleteButton/index.tsx | 2 +- .../pages/Connectors/ConnectorName/index.tsx | 2 +- .../Connectors/ConnectorStatusField/index.tsx | 4 +- .../src/pages/Connectors/Guide/index.tsx | 6 +- .../SignInExperienceSetupNotice/index.tsx | 2 +- .../console/src/pages/Connectors/index.tsx | 8 +- .../pages/CustomizeJwt/CreateButton/index.tsx | 18 +- .../CustomizeJwt/CustomizerItem/index.tsx | 6 +- .../UpsellNotice/index.module.scss | 52 + .../pages/CustomizeJwt/UpsellNotice/index.tsx | 48 + .../src/pages/CustomizeJwt/index.module.scss | 4 + .../console/src/pages/CustomizeJwt/index.tsx | 29 +- .../ActionButton/CodeRestoreButton.tsx | 4 +- .../MonacoCodeEditor/Dashboard/index.tsx | 4 +- .../MainContent/MonacoCodeEditor/index.tsx | 2 +- .../ScriptSection/ErrorContent/index.tsx | 2 +- .../MainContent/ScriptSection/index.tsx | 4 +- .../GuideCard/index.module.scss | 2 +- .../InstructionTab/GuideCard/index.tsx | 5 +- .../SettingsSection/InstructionTab/index.tsx | 23 +- .../SettingsSection/TestTab/index.tsx | 4 +- .../MainContent/SettingsSection/index.tsx | 6 +- .../CustomizeJwtDetails/MainContent/index.tsx | 2 +- .../PageLoadingSkeleton/index.module.scss | 5 +- .../PageLoadingSkeleton/index.tsx | 2 +- .../src/pages/CustomizeJwtDetails/index.tsx | 2 +- .../CustomizeJwtDetails/utils/config.tsx | 25 +- .../utils/type-definitions.ts | 4 + .../src/pages/Dashboard/components/Block.tsx | 8 +- .../components/ChartTooltip/index.tsx | 2 +- .../components/Skeleton/index.module.scss | 6 +- .../Dashboard/components/Skeleton/index.tsx | 2 +- .../console/src/pages/Dashboard/index.tsx | 2 +- .../EnterpriseSso/SsoConnectorLogo/index.tsx | 2 +- .../SsoConnectorRadio/index.tsx | 2 +- .../SsoConnectorRadioGroup/index.tsx | 2 +- .../SsoCreationModal/index.module.scss | 4 + .../EnterpriseSso/SsoCreationModal/index.tsx | 51 +- .../console/src/pages/EnterpriseSso/index.tsx | 26 +- .../Connection/FileReader/index.tsx | 10 +- .../ParsedConfigPreview/index.tsx | 2 +- .../Connection/OidcMetadataForm/index.tsx | 2 +- .../Connection/SamlAttributeMapping/index.tsx | 2 +- .../Connection/SamlConnectorForm.tsx | 2 +- .../ParsedConfigPreview/index.tsx | 2 +- .../SwitchFormatButton/index.tsx | 6 +- .../Connection/SamlMetadataForm/index.tsx | 2 +- .../Experience/DomainsInput/index.tsx | 4 +- .../Experience/LogosUploader/index.tsx | 10 +- .../EnterpriseSsoDetails/Experience/index.tsx | 2 +- .../EnterpriseSsoDetails/SsoGuide/index.tsx | 2 +- .../src/pages/EnterpriseSsoDetails/index.tsx | 6 +- .../index.module.scss | 2 +- .../ProtectedAppCreationForm/index.tsx | 2 +- .../console/src/pages/GetStarted/index.tsx | 14 +- .../FactorLabel/WebAuthnTipContent/index.tsx | 2 +- .../pages/Mfa/MfaForm/FactorLabel/index.tsx | 2 +- .../pages/Mfa/MfaForm/UpsellNotice/index.tsx | 51 + .../src/pages/Mfa/MfaForm/index.module.scss | 4 + .../console/src/pages/Mfa/MfaForm/index.tsx | 12 +- .../src/pages/Mfa/PageWrapper/index.tsx | 19 +- .../console/src/pages/Mfa/Skeleton/index.tsx | 2 +- packages/console/src/pages/NotFound/index.tsx | 6 +- .../EditOrganizationRolesModal/index.tsx | 2 +- .../AddAppsToOrganization.tsx | 2 +- .../MachineToMachine/index.tsx | 4 +- .../Members/AddMembersToOrganization.tsx | 2 +- .../OrganizationDetails/Members/index.tsx | 8 +- .../Settings/JitSettings.tsx | 15 +- .../Settings/index.module.scss | 6 +- .../OrganizationDetails/Settings/index.tsx | 35 +- .../OrganizationDetails/Settings/utils.ts | 14 +- .../src/pages/OrganizationDetails/index.tsx | 22 +- .../Permissions/ResourceName/index.tsx | 4 +- .../Permissions/index.tsx | 4 +- .../pages/OrganizationRoleDetails/index.tsx | 6 +- .../OrganizationPermissions/index.tsx | 8 +- .../CreateOrganizationRoleModal.tsx | 4 +- .../OrganizationRoles/index.tsx | 10 +- .../src/pages/OrganizationTemplate/index.tsx | 28 +- .../CreateOrganizationModal/index.module.scss | 3 + .../CreateOrganizationModal/index.tsx | 52 +- .../Introduction/components/FlexBox/index.tsx | 2 +- .../components/InteractiveDiagram/index.tsx | 2 +- .../Introduction/components/Panel/index.tsx | 2 +- .../components/Permission/index.tsx | 2 +- .../Introduction/components/Role/index.tsx | 2 +- .../Introduction/components/Section/index.tsx | 2 +- .../Introduction/components/User/index.tsx | 4 +- .../Introduction/components/Vector/John.tsx | 2 +- .../Introduction/components/Vector/Sarah.tsx | 2 +- .../Organizations/Introduction/index.tsx | 6 +- .../EmptyDataPlaceholder/index.tsx | 8 +- .../OrganizationsTable/index.tsx | 4 +- .../console/src/pages/Organizations/index.tsx | 26 +- .../Profile/components/CardContent/index.tsx | 2 +- .../components/ExperienceLikeModal/index.tsx | 6 +- .../components/LinkAccountSection/index.tsx | 4 +- .../pages/Profile/components/NotSet/index.tsx | 2 +- .../components/Skeleton/index.module.scss | 6 +- .../Profile/components/Skeleton/index.tsx | 2 +- .../BasicUserInfoUpdateModal/index.tsx | 2 +- .../containers/ChangePasswordModal/index.tsx | 2 +- .../DeletionConfirmationModal/index.tsx | 2 +- .../FinalConfirmationModal/index.tsx | 4 +- .../components/TenantsIssuesModal/index.tsx | 2 +- .../components/TenantsList/index.tsx | 2 +- .../containers/DeleteAccountModal/index.tsx | 2 +- .../VerificationCodeModal/index.tsx | 4 +- .../containers/VerifyPasswordModal/index.tsx | 8 +- packages/console/src/pages/Profile/index.tsx | 118 +- .../RoleDetails/RoleApplications/index.tsx | 6 +- .../AssignPermissionsModal/index.tsx | 32 +- .../src/pages/RoleDetails/RoleUsers/index.tsx | 10 +- .../console/src/pages/RoleDetails/index.tsx | 4 +- .../components/AssignRoleModal/index.tsx | 2 +- .../components/AssignedEntities/index.tsx | 2 +- .../components/CreateRoleForm/Footer.tsx | 49 +- .../Roles/components/CreateRoleForm/index.tsx | 2 +- .../components/CreateRoleModal/index.tsx | 2 +- packages/console/src/pages/Roles/index.tsx | 6 +- .../LogoAndFaviconUploader/index.tsx | 75 - .../Branding/BrandingForm/index.tsx | 103 +- .../Branding/CustomCssForm/index.tsx | 66 - .../index.module.scss | 6 + .../Branding/CustomUiForm/index.tsx | 117 + .../PageContent/Branding/index.tsx | 4 +- .../LanguageEditor/AddLanguageSelector.tsx | 6 +- .../LanguageEditor/LanguageDetails.tsx | 6 +- .../LanguageEditor/LanguageItem.tsx | 2 +- .../LanguageEditor/LanguageNav.tsx | 2 +- .../ManageLanguage/LanguageEditor/index.tsx | 4 +- .../Content/LanguagesForm/index.tsx | 2 +- .../PageContent/PasswordPolicy/index.tsx | 2 +- .../SignUpAndSignIn/AdvancedOptions/index.tsx | 2 +- .../SignInMethodEditBox/AddButton.tsx | 6 +- .../SignInMethodEditBox/SignInMethodItem.tsx | 8 +- .../SignInForm/SignInMethodEditBox/index.tsx | 2 +- .../SignUpAndSignIn/SignUpForm/index.tsx | 2 +- .../AddButton/index.tsx | 6 +- .../SelectedConnectorItem/index.tsx | 6 +- .../SocialConnectorEditBox/index.tsx | 2 +- .../ConnectorSetupWarning/index.tsx | 2 +- .../DiffSegment.tsx | 2 +- .../SignInDiffSection.tsx | 2 +- .../SignUpDiffSection.tsx | 2 +- .../SocialTargetsDiffSection.tsx | 2 +- .../SignUpAndSignInChangePreview/index.tsx | 2 +- .../components/FormFieldDescription/index.tsx | 2 +- .../components/FormSectionTitle/index.tsx | 2 +- .../SignInExperienceTabWrapper/index.tsx | 2 +- .../SignInExperience/PageContent/index.tsx | 22 +- .../PageContent/utils/parser.ts | 18 +- .../Skeleton/index.module.scss | 4 +- .../pages/SignInExperience/Skeleton/index.tsx | 6 +- .../SignInExperience/Welcome/GuideModal.tsx | 6 +- .../pages/SignInExperience/Welcome/index.tsx | 6 +- .../CustomUiAssetsUploader/index.module.scss | 62 + .../CustomUiAssetsUploader/index.tsx | 114 + .../components/Preview/index.tsx | 5 +- .../SignInExperienceContextProvider/index.tsx | 48 + .../src/pages/SignInExperience/index.tsx | 21 +- .../SigningKeyFormCard/index.module.scss | 2 +- .../SigningKeys/SigningKeyFormCard/index.tsx | 4 +- .../console/src/pages/SigningKeys/index.tsx | 2 +- .../MauLimitExceededNotification/index.tsx | 50 +- .../Subscription/CurrentPlan/index.tsx | 54 +- .../DiffQuotaItem/SkuQuotaItemPhrase.tsx | 43 + .../PlanQuotaList/DiffQuotaItem/index.tsx | 45 +- .../PlanQuotaDiffCard/PlanQuotaList/index.tsx | 34 +- .../PlanQuotaDiffCard/index.tsx | 29 +- .../DowngradeConfirmModalContent/index.tsx | 39 +- .../TableDataContent/index.tsx | 4 +- .../TableDataWrapper/index.tsx | 4 +- .../PlanComparisonTable/index.tsx | 8 +- .../SwitchPlanActionBar/index.tsx | 133 +- .../TenantSettings/Subscription/index.tsx | 17 +- .../TenantBasicSettings/DeleteCard/index.tsx | 2 +- .../TenantBasicSettings/DeleteModal/index.tsx | 2 +- .../TenantBasicSettings/LeaveCard/index.tsx | 2 +- .../ProfileForm/TenantEnvironment/index.tsx | 2 +- .../ProfileForm/TenantRegion/index.tsx | 2 +- .../TenantBasicSettings/index.tsx | 2 +- .../AddDomainForm/index.tsx | 2 +- .../DnsRecordsTable/index.tsx | 2 +- .../ActivationProcess/Step/index.tsx | 6 +- .../CustomDomain/ActivationProcess/index.tsx | 2 +- .../CustomDomain/CustomDomainHeader/index.tsx | 6 +- .../CustomDomain/index.tsx | 2 +- .../DefaultDomain/index.tsx | 2 +- .../TenantDomainSettings/index.tsx | 2 +- .../TenantMembers/EditMemberModal/index.tsx | 4 +- .../TenantMembers/Invitations/index.tsx | 14 +- .../TenantMembers/InviteEmailsInput/index.tsx | 4 +- .../InviteMemberModal/Footer/index.tsx | 12 +- .../TenantMembers/InviteMemberModal/index.tsx | 63 +- .../TenantSettings/TenantMembers/hooks.ts | 57 +- .../TenantMembers/index.module.scss | 4 + .../TenantSettings/TenantMembers/index.tsx | 8 +- .../index.tsx | 105 +- .../components/Skeleton/index.tsx | 2 +- .../src/pages/TenantSettings/index.tsx | 2 +- .../src/pages/UserDetails/UserLogs/index.tsx | 2 +- .../src/pages/UserDetails/UserRoles/index.tsx | 6 +- .../UserMfaVerifications/index.tsx | 2 +- .../components/UserSocialIdentities/index.tsx | 2 +- .../components/UserSsoIdentities/index.tsx | 2 +- .../console/src/pages/UserDetails/index.tsx | 12 +- .../Users/components/CreateForm/index.tsx | 4 +- packages/console/src/pages/Users/index.tsx | 10 +- .../WebhookDetails/WebhookLogs/index.tsx | 2 +- .../WebhookSettings/SigningKeyField/index.tsx | 4 +- .../WebhookSettings/TestWebhook/index.tsx | 2 +- .../src/pages/WebhookDetails/index.tsx | 12 +- .../Webhooks/CreateFormModal/CreateForm.tsx | 31 +- .../pages/Webhooks/CreateFormModal/index.tsx | 2 +- packages/console/src/pages/Webhooks/index.tsx | 12 +- .../src/pages/Welcome/index.module.scss | 4 +- packages/console/src/pages/Welcome/index.tsx | 4 +- .../console/src/types/sign-in-experience.ts | 11 + packages/console/src/types/skus.ts | 14 + packages/console/src/types/subscriptions.ts | 8 + packages/console/src/utils/connector-form.ts | 9 +- packages/console/src/utils/json.ts | 8 +- packages/console/src/utils/object.ts | 2 + packages/console/src/utils/quota.ts | 42 + packages/console/src/utils/subscription.ts | 54 +- packages/console/src/utils/uploader.ts | 19 +- packages/console/svgo.config.json | 13 - packages/console/tsconfig.json | 6 +- packages/console/vite.config.ts | 84 + packages/core/CHANGELOG.md | 139 + packages/core/nodemon.json | 17 - packages/core/package.json | 47 +- packages/core/src/__mocks__/index.ts | 19 +- .../core/src/__mocks__/sign-in-experience.ts | 2 +- packages/core/src/app/init.ts | 7 +- packages/core/src/caches/well-known.ts | 5 + packages/core/src/event-listeners/grant.ts | 3 +- packages/core/src/event-listeners/index.ts | 9 +- packages/core/src/event-listeners/utils.ts | 4 +- packages/core/src/libraries/application.ts | 2 +- .../core/src/libraries/ogcio-constants.ts | 4 +- packages/core/src/libraries/ogcio-user.ts | 21 +- packages/core/src/libraries/quota.ts | 134 +- .../sign-in-experience/index.test.ts | 20 +- .../src/libraries/sign-in-experience/index.ts | 90 +- packages/core/src/libraries/user.test.ts | 39 +- packages/core/src/libraries/user.ts | 5 +- packages/core/src/libraries/user.utils.ts | 2 +- .../koa-app-secret-transpilation.test.ts | 154 + .../koa-app-secret-transpilation.ts | 141 + packages/core/src/middleware/koa-cors.ts | 4 + .../src/middleware/koa-experience-ssr.test.ts | 81 + .../core/src/middleware/koa-experience-ssr.ts | 67 + .../middleware/koa-interaction-details.ts | 0 .../src/middleware/koa-oidc-error-handler.ts | 14 +- .../core/src/middleware/koa-quota-guard.ts | 22 +- .../src/middleware/koa-security-headers.ts | 17 +- .../koa-serve-custom-ui-assets.test.ts | 91 + .../middleware/koa-serve-custom-ui-assets.ts | 40 + .../core/src/middleware/koa-serve-static.ts | 30 +- .../middleware/koa-slonik-error-handler.ts | 15 + .../core/src/middleware/koa-spa-proxy.test.ts | 34 +- packages/core/src/middleware/koa-spa-proxy.ts | 45 +- packages/core/src/oidc/extra-token-claims.ts | 52 +- .../src/oidc/grants/client-credentials.ts | 51 +- packages/core/src/oidc/grants/index.ts | 2 +- .../src/oidc/grants/refresh-token.test.ts | 40 +- .../core/src/oidc/grants/refresh-token.ts | 156 +- .../grants/token-exchange/actor-token.test.ts | 69 + .../oidc/grants/token-exchange/actor-token.ts | 38 + .../index.test.ts} | 12 +- .../index.ts} | 63 +- .../src/oidc/grants/token-exchange/types.ts | 13 + packages/core/src/oidc/grants/utils.ts | 191 + packages/core/src/oidc/init.ts | 106 +- packages/core/src/oidc/utils.test.ts | 18 +- packages/core/src/oidc/utils.ts | 24 +- .../core/src/queries/application-secrets.ts | 59 + .../queries/application-sign-in-experience.ts | 26 +- packages/core/src/queries/application.ts | 9 +- .../src/queries/sign-in-experience.test.ts | 3 +- packages/core/src/queries/users-roles.ts | 9 +- packages/core/src/routes-me/user-assets.ts | 2 +- packages/core/src/routes/admin-user/basics.ts | 3 +- .../application-custom-data.openapi.json | 24 + .../applications/application-custom-data.ts | 31 + ...cation-protected-app-metadata.openapi.json | 16 +- .../application-secret.openapi.json | 104 + .../routes/applications/application-secret.ts | 130 + .../application-sign-in-experience.ts | 6 +- .../applications/application.openapi.json | 64 +- .../routes/applications/application.test.ts | 2 +- .../src/routes/applications/application.ts | 124 +- .../core/src/routes/applications/types.ts | 1 - packages/core/src/routes/connector/index.ts | 5 +- packages/core/src/routes/domain.ts | 8 +- .../classes/experience-interaction.test.ts | 128 + .../classes/experience-interaction.ts | 513 +- .../src/routes/experience/classes/helpers.ts | 162 + .../classes/libraries/mfa-validator.ts | 131 + .../classes/libraries/password-validator.ts | 73 + .../classes/libraries/profile-validator.ts | 187 + .../classes/libraries/provision-library.ts | 288 + .../classes/libraries/sentinel-guard.ts | 72 + .../sign-in-experience-validator.test.ts | 399 + .../libraries/sign-in-experience-validator.ts | 253 + .../core/src/routes/experience/classes/mfa.ts | 330 + .../src/routes/experience/classes/profile.ts | 205 + .../routes/experience/classes/utils.test.ts | 38 + .../src/routes/experience/classes/utils.ts | 66 + .../verifications/backup-code-verification.ts | 152 + .../verifications/code-verification.ts | 287 +- .../enterprise-sso-verification.ts | 278 + .../experience/classes/verifications/index.ts | 101 +- .../new-password-identity-verification.ts | 150 + .../verifications/password-verification.ts | 63 +- .../verifications/social-verification.ts | 166 +- .../verifications/totp-verification.ts | 207 + .../verifications/verification-record.ts | 44 +- .../verifications/verification-records-map.ts | 36 + .../verifications/web-authn-verification.ts | 291 + packages/core/src/routes/experience/const.ts | 2 + packages/core/src/routes/experience/index.ts | 158 +- .../koa-experience-interaction-hooks.ts | 83 + .../middleware/koa-experience-interaction.ts | 41 +- .../koa-experience-verifications-audit-log.ts | 36 + .../src/routes/experience/profile-routes.ts | 222 + packages/core/src/routes/experience/types.ts | 86 + packages/core/src/routes/experience/utils.ts | 20 - .../backup-code-verification.ts | 104 + .../enterprise-sso-verification.ts | 163 + .../new-password-identity-verification.ts | 68 + .../password-verification.ts | 49 +- .../social-verification.ts | 79 +- .../verification-routes/totp-verification.ts | 135 + .../verification-routes/verification-code.ts | 88 +- .../web-authn-verification.ts | 224 + packages/core/src/routes/hook.ts | 7 +- packages/core/src/routes/init.ts | 8 +- .../actions/submit-interaction.test.ts | 1 - .../interaction/actions/submit-interaction.ts | 3 +- .../core/src/routes/interaction/additional.ts | 4 +- .../src/routes/interaction/consent/index.ts | 8 +- packages/core/src/routes/interaction/index.ts | 20 +- packages/core/src/routes/interaction/mfa.ts | 2 +- .../middleware/koa-interaction-hooks.ts | 11 +- .../src/routes/interaction/single-sign-on.ts | 2 +- .../src/routes/interaction/types/index.ts | 1 - .../utils/single-sign-on-session.ts | 29 +- .../interaction/utils/single-sign-on.test.ts | 6 +- .../interaction/utils/single-sign-on.ts | 77 +- .../utils/social-verification.test.ts | 19 +- .../interaction/utils/social-verification.ts | 6 +- .../mandatory-user-profile-validation.ts | 6 +- .../verifications/mfa-payload-verification.ts | 20 +- .../verifications/mfa-verification.ts | 6 +- .../src/routes/logto-config/jwt-customizer.ts | 17 +- .../src/routes/organization-role/index.ts | 9 +- .../src/routes/organization-scope/index.ts | 9 +- .../application/index.openapi.json | 16 +- .../core/src/routes/organization/index.ts | 9 +- packages/core/src/routes/resource.scope.ts | 5 +- packages/core/src/routes/resource.ts | 7 +- packages/core/src/routes/role.scope.ts | 5 +- packages/core/src/routes/role.ts | 23 +- .../custom-ui-assets/index.openapi.json | 42 + .../custom-ui-assets/index.test.ts | 133 + .../custom-ui-assets/index.ts | 127 + .../src/routes/sign-in-experience/index.ts | 15 +- .../core/src/routes/sso-connector/index.ts | 7 +- ...penapi.json => subject-token.openapi.json} | 9 +- .../{security/index.ts => subject-token.ts} | 24 +- packages/core/src/routes/swagger/consts.ts | 20 +- packages/core/src/routes/swagger/index.ts | 42 +- .../core/src/routes/swagger/utils/general.ts | 31 +- .../src/routes/swagger/utils/operation-id.ts | 6 +- .../src/routes/swagger/utils/parameters.ts | 14 + packages/core/src/routes/user-assets.ts | 2 +- packages/core/src/routes/well-known.ts | 30 +- packages/core/src/sso/types/session.ts | 53 +- packages/core/src/tenants/Libraries.ts | 3 +- packages/core/src/tenants/Queries.ts | 2 + packages/core/src/tenants/SystemContext.ts | 16 + packages/core/src/tenants/Tenant.ts | 22 +- packages/core/src/test-utils/quota.ts | 2 + packages/core/src/utils/SchemaRouter.ts | 13 +- packages/core/src/utils/file.test.ts | 16 + packages/core/src/utils/file.ts | 17 + packages/core/src/utils/i18n.ts | 31 + .../core/src/utils/storage/azure-storage.ts | 23 +- packages/core/src/utils/storage/consts.ts | 8 - packages/core/src/utils/subscription/index.ts | 46 +- packages/core/src/utils/subscription/types.ts | 22 + packages/core/tsconfig.base.json | 13 - packages/core/tsconfig.build.json | 10 - packages/core/tsconfig.json | 22 +- packages/core/tsconfig.test.json | 5 +- packages/core/tsup.config.ts | 11 + packages/core/tsup.dev.config.ts | 9 + packages/create/CHANGELOG.md | 7 + packages/create/package.json | 4 +- packages/demo-app/.parcelrc.arm64 | 7 - packages/demo-app/CHANGELOG.md | 12 + packages/demo-app/{src => }/index.html | 6 +- packages/demo-app/package.json | 47 +- packages/demo-app/src/App.tsx | 25 +- packages/demo-app/src/DevPanel.tsx | 30 +- packages/demo-app/src/include.d/vite-end.d.ts | 2 + packages/demo-app/src/index.tsx | 2 - packages/demo-app/src/utils.ts | 28 +- packages/demo-app/tsconfig.json | 1 + packages/demo-app/vite.config.ts | 22 + packages/elements/.gitignore | 1 + packages/elements/README.md | 41 + packages/elements/index.html | 19 + packages/elements/lit-localize.json | 15 + packages/elements/package.json | 89 + .../elements/src/components/logto-avatar.ts | 53 + .../src/components/logto-button.styles.ts | 106 + .../elements/src/components/logto-button.ts | 58 + .../src/components/logto-card-section.ts | 33 + .../elements/src/components/logto-card.ts | 33 + .../src/components/logto-form-card.ts | 78 + .../src/components/logto-icon-button.ts | 51 + .../elements/src/components/logto-list-row.ts | 55 + .../elements/src/components/logto-list.ts | 23 + .../src/components/logto-modal-layout.ts | 75 + .../src/components/logto-modal.context.ts | 16 + .../elements/src/components/logto-modal.ts | 88 + .../src/components/logto-text-input.ts | 85 + .../src/elements/logto-profile-card.ts | 129 + packages/elements/src/icons/close.svg | 5 + packages/elements/src/icons/index.d.ts | 4 + packages/elements/src/index.ts | 18 + packages/elements/src/phrases/index.ts | 6 + .../src/providers/logto-theme-provider.ts | 22 + .../src/providers/logto-user-provider.ts | 61 + packages/elements/src/react.ts | 48 + packages/elements/src/utils/api.ts | 22 + packages/elements/src/utils/css.ts | 37 + packages/elements/src/utils/locale.ts | 11 + packages/elements/src/utils/string.ts | 19 + packages/elements/src/utils/theme.ts | 174 + packages/elements/tsconfig.json | 12 + packages/elements/tsup.config.ts | 23 + packages/elements/web-dev-server.config.js | 33 + packages/elements/xliff/de.xlf | 50 + packages/elements/xliff/es.xlf | 50 + packages/elements/xliff/fr.xlf | 50 + packages/elements/xliff/it.xlf | 50 + packages/elements/xliff/ja.xlf | 50 + packages/elements/xliff/ko.xlf | 50 + packages/elements/xliff/pl-PL.xlf | 50 + packages/elements/xliff/pt-BR.xlf | 50 + packages/elements/xliff/pt-PT.xlf | 50 + packages/elements/xliff/ru.xlf | 50 + packages/elements/xliff/tr-TR.xlf | 50 + packages/elements/xliff/zh-CN.xlf | 50 + packages/elements/xliff/zh-HK.xlf | 50 + packages/elements/xliff/zh-TW.xlf | 50 + packages/experience/.eslintrc.cjs | 22 + packages/experience/.parcelrc | 16 - packages/experience/.parcelrc.arm64 | 20 - packages/experience/CHANGELOG.md | 53 + packages/experience/index.html | 20 + packages/experience/jest.config.ts | 2 +- packages/experience/package.json | 74 +- .../experience/src/Layout/AppLayout/index.tsx | 2 +- .../src/Layout/LandingPageLayout/index.tsx | 2 +- .../src/Layout/SecondaryPageLayout/index.tsx | 2 +- .../src/Layout/SectionLayout/index.tsx | 2 +- .../src/Layout/StaticPageLayout/index.tsx | 2 +- .../src/Providers/AppBoundary/AppMeta.tsx | 19 +- .../Providers/AppBoundary/use-color-theme.ts | 2 + .../Providers/ConfirmModalProvider/index.tsx | 119 +- .../ConfirmModalProvider/indext.test.tsx | 210 +- .../IframeModalProvider/IframeModal/index.tsx | 2 +- .../Providers/LoadingLayerProvider/index.tsx | 6 +- .../UserInteractionContext.tsx | 38 + .../UserInteractionContextProvider/index.tsx | 47 +- packages/experience/src/__mocks__/logto.tsx | 4 +- packages/experience/src/apis/settings.ts | 16 +- .../src/assets/icons/loading-ring.svg | 3 + .../src/components/BrandingHeader/index.tsx | 4 +- .../src/components/Button/IconButton.tsx | 2 +- .../src/components/Button/MfaFactorButton.tsx | 12 +- .../Button/RotatingRingIcon.module.scss | 14 + .../components/Button/RotatingRingIcon.tsx | 7 + .../Button/SocialLinkButton.module.scss | 9 + .../components/Button/SocialLinkButton.tsx | 28 +- .../src/components/Button/index.module.scss | 34 +- .../src/components/Button/index.tsx | 24 +- .../src/components/Checkbox/index.tsx | 4 +- .../src/components/ConfirmModal/AcModal.tsx | 10 +- .../components/ConfirmModal/MobileModal.tsx | 14 +- .../src/components/ConfirmModal/type.ts | 2 + .../src/components/Divider/index.tsx | 2 +- .../src/components/ErrorMessage/index.tsx | 2 +- .../NotchedBorder/index.module.scss | 131 + .../InputField/NotchedBorder/index.tsx | 54 + .../InputFields/InputField/index.module.scss | 176 +- .../InputFields/InputField/index.tsx | 117 +- .../InputFields/PasswordInputField/index.tsx | 4 +- .../SmartInputField/AnimatedPrefix/index.tsx | 2 +- .../CountryCodeDropdown/index.module.scss | 4 +- .../CountryCodeDropdown/index.test.tsx | 8 +- .../CountryCodeDropdown/index.tsx | 7 +- .../CountryCodeSelector/index.module.scss | 8 + .../CountryCodeSelector/index.tsx | 6 +- .../InputFields/SmartInputField/index.tsx | 5 +- .../SmartInputField/use-smart-input-field.ts | 9 +- .../InputFields/SmartInputField/utils.test.ts | 10 +- .../InputFields/SmartInputField/utils.ts | 10 +- .../components/LoadingLayer/LoadingIcon.tsx | 4 +- .../components/LoadingLayer/index.module.scss | 7 - .../src/components/LoadingLayer/index.tsx | 8 +- .../components/LoadingMask/index.module.scss | 8 + .../src/components/LoadingMask/index.tsx | 13 + .../LogtoSignature/index.module.scss | 4 + .../src/components/LogtoSignature/index.tsx | 8 +- .../src/components/NavBar/index.tsx | 6 +- .../Notification/AppNotification/index.tsx | 4 +- .../Notification/InlineNotification/index.tsx | 2 +- .../components/SwitchMfaFactorsLink/index.tsx | 2 +- .../src/components/TermsLinks/index.tsx | 2 +- .../src/components/TextLink/index.tsx | 2 +- .../experience/src/components/Toast/index.tsx | 2 +- .../src/components/VerificationCode/index.tsx | 2 +- packages/experience/src/constants/env.ts | 4 - .../DevelopmentTenantNotification/index.tsx | 2 +- .../containers/ForgotPasswordLink/index.tsx | 32 +- .../src/containers/MfaFactorList/index.tsx | 2 +- .../SetPassword/HiddenIdentifierInput.tsx | 25 + .../src/containers/SetPassword/Lite.tsx | 21 +- .../containers/SetPassword/SetPassword.tsx | 25 +- .../containers/SetPassword/TogglePassword.tsx | 2 +- .../src/containers/SetPassword/index.tsx | 2 +- .../src/containers/SocialLanding/index.tsx | 2 +- .../SocialLinkAccount/index.module.scss | 6 +- .../SocialLinkAccount/index.test.tsx | 6 +- .../containers/SocialLinkAccount/index.tsx | 52 +- .../use-social-link-related-user.ts | 2 +- .../src/containers/SocialSignInList/index.tsx | 14 +- .../containers/SocialSignInList/use-social.ts | 16 +- .../TermsAndPrivacyCheckbox/index.tsx | 2 +- .../TotpCodeVerification/index.module.scss | 4 + .../containers/TotpCodeVerification/index.tsx | 77 +- .../use-totp-code-verification.ts | 4 +- .../VerificationCode/PasswordSignInLink.tsx | 9 +- .../VerificationCode/index.module.scss | 3 +- .../src/containers/VerificationCode/index.tsx | 78 +- .../use-continue-flow-code-verification.ts | 2 +- .../use-identifier-error-alert.ts | 4 +- .../use-link-social-confirm-modal.ts | 16 +- .../use-register-flow-code-verification.ts | 38 +- .../use-sign-in-flow-code-verification.ts | 42 +- .../src/hooks/use-check-single-sign-on.ts | 10 +- .../experience/src/hooks/use-confirm-modal.ts | 47 +- .../experience/src/hooks/use-error-handler.ts | 1 + .../src/hooks/use-global-redirect-to.ts | 74 +- .../src/hooks/use-password-sign-in.ts | 2 +- .../src/hooks/use-send-mfa-payload.ts | 2 +- .../src/hooks/use-send-verification-code.ts | 5 +- .../src/hooks/use-session-storages.ts | 6 +- .../src/hooks/use-single-sign-on.ts | 16 +- packages/experience/src/hooks/use-skip-mfa.ts | 2 +- .../src/hooks/use-social-link-account.ts | 2 +- .../src/hooks/use-social-register.ts | 2 +- packages/experience/src/hooks/use-terms.ts | 4 +- packages/experience/src/i18n/utils.ts | 41 +- packages/experience/src/include.d/global.d.ts | 14 +- .../experience/src/include.d/react-app.d.ts | 65 - .../src/include.d/react-router-dom.d.ts | 1 - .../experience/src/include.d/vite-env.d.ts | 2 + packages/experience/src/index.html | 34 - packages/experience/src/jest.setup.ts | 22 +- .../experience/src/pages/Callback/index.tsx | 2 +- .../OrganizationItem/index.module.scss | 2 +- .../OrganizationItem/index.tsx | 6 +- .../OrganizationSelectorModal/index.tsx | 2 +- .../Consent/OrganizationSelector/index.tsx | 4 +- .../src/pages/Consent/ScopeGroup/index.tsx | 6 +- .../pages/Consent/ScopesListCard/index.tsx | 2 +- .../src/pages/Consent/UserProfile/index.tsx | 4 +- .../experience/src/pages/Consent/index.tsx | 10 +- .../Continue/IdentifierProfileForm/index.tsx | 8 +- .../SocialIdentityNotification.tsx | 2 +- .../pages/Continue/SetEmailOrPhone/index.tsx | 5 + .../src/pages/Continue/SetPassword/index.tsx | 8 +- .../src/pages/Continue/SetUsername/index.tsx | 6 + .../Continue/SetUsername/use-set-username.ts | 2 +- .../src/pages/DirectSignIn/index.test.tsx | 31 +- .../experience/src/pages/ErrorPage/index.tsx | 6 +- .../ForgotPasswordForm/index.test.tsx | 23 +- .../ForgotPasswordForm/index.tsx | 17 +- .../src/pages/ForgotPassword/index.test.tsx | 111 +- .../src/pages/ForgotPassword/index.tsx | 17 +- .../MfaBinding/BackupCodeBinding/index.tsx | 11 +- .../TotpBinding/SecretSection/index.tsx | 2 +- .../pages/MfaBinding/TotpBinding/index.tsx | 2 +- .../MfaBinding/WebAuthnBinding/index.tsx | 11 +- .../BackupCodeVerification/index.tsx | 14 +- .../TotpVerification/index.tsx | 2 +- .../WebAuthnVerification/index.tsx | 11 +- .../IdentifierRegisterForm/index.test.tsx | 19 +- .../Register/IdentifierRegisterForm/index.tsx | 17 +- .../experience/src/pages/Register/index.tsx | 2 +- .../src/pages/RegisterPassword/index.tsx | 8 +- .../src/pages/ResetPassword/index.tsx | 14 +- .../IdentifierSignInForm/index.test.tsx | 35 +- .../SignIn/IdentifierSignInForm/index.tsx | 16 +- .../IdentifierSignInForm/use-on-submit.ts | 33 +- packages/experience/src/pages/SignIn/Main.tsx | 2 +- .../pages/SignIn/PasswordSignInForm/index.tsx | 16 +- .../experience/src/pages/SignIn/index.tsx | 2 +- .../PasswordForm/VerificationCodeLink.tsx | 2 +- .../PasswordForm/index.test.tsx | 1 - .../SignInPassword/PasswordForm/index.tsx | 16 +- .../src/pages/SignInPassword/index.test.tsx | 26 +- .../src/pages/SignInPassword/index.tsx | 18 +- .../pages/SingleSignOnConnectors/index.tsx | 17 +- .../src/pages/SingleSignOnEmail/index.tsx | 13 +- .../src/pages/SocialLanding/index.test.tsx | 19 +- .../src/pages/SocialLanding/index.tsx | 2 +- .../pages/SocialLinkAccount/index.test.tsx | 16 +- .../use-single-sign-on-listener.ts | 4 +- .../src/pages/VerificationCode/index.test.tsx | 31 +- .../src/pages/VerificationCode/index.tsx | 42 +- packages/experience/src/scss/_colors.scss | 3 + packages/experience/src/types/guard.ts | 35 +- .../experience/src/utils/search-parameters.ts | 33 +- .../src/utils/sign-in-experience.ts | 17 +- packages/experience/tsconfig.json | 2 +- packages/experience/vite.config.ts | 55 + packages/integration-tests/CHANGELOG.md | 61 + packages/integration-tests/jest.setup.api.js | 6 + packages/integration-tests/jest.setup.js | 3 - packages/integration-tests/package.json | 17 +- .../src/__mocks__/jwt-customizer.ts | 2 +- .../integration-tests/src/api/admin-user.ts | 3 + .../integration-tests/src/api/application.ts | 53 +- .../src/api/sso-connector.ts | 1 + .../src/api/subject-token.ts | 2 +- .../src/client/experience/const.ts | 2 + .../src/client/experience/index.ts | 134 +- packages/integration-tests/src/constants.ts | 2 +- .../integration-tests/src/helpers/client.ts | 3 + .../src/helpers/connector.ts | 1 + .../experience/enterprise-sso-verification.ts | 41 + .../src/helpers/experience/index.ts | 233 +- .../helpers/experience/totp-verification.ts | 39 + .../src/include.d/global.d.ts | 4 + .../src/tests/api/admin-user.test.ts | 4 +- .../application-custom-data.test.ts | 52 + .../application-sign-in-experience.test.ts | 13 - .../application/application.secrets.test.ts | 180 + .../tests/api/application/application.test.ts | 9 +- .../bind-mfa/happpy-path.test.ts | 272 + .../experience-api/bind-mfa/sad-path.test.ts | 179 + .../api/experience-api/interaction.test.ts | 65 + .../profile/fulfill-user-profiles.test.ts | 180 + .../profile/reset-password.test.ts | 150 + .../organization-jti.test.ts | 201 + .../username-password.test.ts | 85 + .../verification-code.test.ts | 191 + .../enterprise-sso.test.ts | 191 + .../mfa-verification.test.ts | 97 + .../sign-in-interaction/password.test.ts | 77 +- .../sign-in-interaction/social.test.ts | 351 + .../verification-code.test.ts | 75 +- .../backup-code-verification.test.ts | 90 + .../enterprise-sso-verification.test.ts | 247 + ...new-password-identity-verification.test.ts | 129 + .../password-verification.test.ts | 4 +- .../verifications/social-verification.test.ts | 7 +- .../verifications/totp-verification.test.ts | 163 + .../verifications/verification-code.test.ts | 6 +- .../src/tests/api/hook/WebhookMockServer.ts | 1 + .../tests/api/hook/hook.trigger.data.test.ts | 2 + .../api/hook/hook.trigger.experience.test.ts | 336 + .../src/tests/api/hook/utils.ts | 1 + .../tests/api/interaction/mfa/totp.test.ts | 4 +- .../api/interaction/organization-jit.test.ts | 2 +- .../happy-path.test.ts | 2 +- .../api/oidc/client-authentication.test.ts | 242 + .../tests/api/oidc/content-type-json.test.ts | 35 + .../src/tests/api/oidc/id-token.test.ts | 4 +- .../api/oidc/refresh-token-grant.test.ts | 18 +- .../src/tests/api/oidc/token-exchange.test.ts | 257 +- .../src/tests/api/security.test.ts | 3 - .../src/tests/api/sign-in-experience.test.ts | 8 +- .../tests/console/backchannel-logout.test.ts | 9 +- .../src/tests/console/error-handling.test.ts | 46 + .../console/sign-in-experience/helpers.ts | 2 +- .../automatic-account-linking.test.ts | 2 +- .../experience/mfa/totp/social-flow.test.ts | 2 +- .../src/tests/experience/overrides.test.ts | 304 + .../experience/server-side-rendering.test.ts | 111 + .../src/ui-helpers/expect-experience.ts | 12 + .../integration-tests/src/ui-helpers/trace.ts | 54 + packages/integration-tests/tsup.config.ts | 10 + packages/phrases-experience/package.json | 5 +- .../src/locales/de/action.ts | 2 +- .../src/locales/de/description.ts | 3 +- .../src/locales/de/error/index.ts | 2 +- .../src/locales/en/action.ts | 2 +- .../src/locales/en/description.ts | 4 +- .../src/locales/en/error/index.ts | 2 +- .../src/locales/es/action.ts | 2 +- .../src/locales/es/description.ts | 3 +- .../src/locales/es/error/index.ts | 2 +- .../src/locales/fr/action.ts | 2 +- .../src/locales/fr/description.ts | 3 +- .../src/locales/fr/error/index.ts | 2 +- .../src/locales/it/action.ts | 2 +- .../src/locales/it/description.ts | 4 +- .../src/locales/it/error/index.ts | 2 +- .../src/locales/ja/action.ts | 2 +- .../src/locales/ja/description.ts | 4 +- .../src/locales/ja/error/index.ts | 2 +- .../src/locales/ko/action.ts | 2 +- .../src/locales/ko/description.ts | 3 +- .../src/locales/pl-pl/action.ts | 2 +- .../src/locales/pl-pl/description.ts | 4 +- .../src/locales/pl-pl/error/index.ts | 2 +- .../src/locales/pt-br/action.ts | 2 +- .../src/locales/pt-br/description.ts | 3 +- .../src/locales/pt-br/error/index.ts | 2 +- .../src/locales/pt-pt/action.ts | 2 +- .../src/locales/pt-pt/description.ts | 3 +- .../src/locales/ru/action.ts | 2 +- .../src/locales/ru/description.ts | 3 +- .../src/locales/ru/error/index.ts | 2 +- .../src/locales/tr-tr/action.ts | 2 +- .../src/locales/tr-tr/description.ts | 3 +- .../src/locales/tr-tr/error/index.ts | 2 +- .../src/locales/zh-cn/action.ts | 8 +- .../src/locales/zh-cn/description.ts | 16 +- .../src/locales/zh-cn/error/index.ts | 2 +- .../src/locales/zh-cn/user-scopes.ts | 27 +- .../src/locales/zh-hk/action.ts | 2 +- .../src/locales/zh-hk/description.ts | 3 +- .../src/locales/zh-hk/error/index.ts | 2 +- .../src/locales/zh-tw/action.ts | 2 +- .../src/locales/zh-tw/description.ts | 3 +- .../src/locales/zh-tw/error/index.ts | 2 +- packages/phrases-experience/src/types.ts | 10 +- packages/phrases/CHANGELOG.md | 44 + packages/phrases/package.json | 6 +- .../phrases/src/locales/de/errors/session.ts | 3 + .../admin-console/application-details.ts | 28 +- .../de/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 50 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../de/translation/admin-console/profile.ts | 38 + .../translation/admin-console/role-details.ts | 3 +- .../de/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 62 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 6 +- .../de/translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/de/translation/demo-app.ts | 1 + .../src/locales/en/errors/application.ts | 2 + .../phrases/src/locales/en/errors/guard.ts | 1 + .../phrases/src/locales/en/errors/session.ts | 5 + .../admin-console/application-details.ts | 47 +- .../translation/admin-console/applications.ts | 6 - .../en/translation/admin-console/cloud.ts | 2 +- .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 4 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 5 + .../admin-console/sign-in-exp/index.ts | 58 +- .../admin-console/subscription/index.ts | 2 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../admin-console/subscription/usage.ts | 59 + .../admin-console/upsell/add-on.ts | 18 + .../translation/admin-console/upsell/index.ts | 2 + .../admin-console/upsell/paywall.ts | 10 +- .../phrases/src/locales/es/errors/session.ts | 3 + .../admin-console/application-details.ts | 25 +- .../translation/admin-console/applications.ts | 7 - .../es/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 50 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../es/translation/admin-console/profile.ts | 38 + .../es/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../es/translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/es/translation/demo-app.ts | 1 + .../phrases/src/locales/fr/errors/session.ts | 3 + .../admin-console/application-details.ts | 27 +- .../fr/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 16 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 50 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../fr/translation/admin-console/profile.ts | 38 + .../fr/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 36 +- .../admin-console/subscription/quota-table.ts | 4 +- .../fr/translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 14 +- .../translation/admin-console/user-details.ts | 7 + .../src/locales/fr/translation/demo-app.ts | 1 + .../phrases/src/locales/it/errors/session.ts | 3 + .../admin-console/application-details.ts | 25 +- .../translation/admin-console/applications.ts | 7 - .../it/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 17 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 7 +- .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../it/translation/admin-console/profile.ts | 38 + .../it/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 62 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../it/translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/it/translation/demo-app.ts | 1 + .../phrases/src/locales/ja/errors/session.ts | 36 +- .../admin-console/application-details.ts | 79 +- .../translation/admin-console/applications.ts | 5 - .../ja/translation/admin-console/cloud.ts | 17 +- .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 30 +- .../translation/admin-console/connectors.ts | 43 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 48 +- .../admin-console/organization-template.ts | 7 +- .../admin-console/organizations.ts | 25 +- .../ja/translation/admin-console/profile.ts | 33 + .../ja/translation/admin-console/roles.ts | 10 +- .../admin-console/sign-in-exp/index.ts | 72 +- .../sign-in-exp/sign-up-and-sign-in.ts | 9 +- .../admin-console/subscription/quota-item.ts | 28 +- .../admin-console/subscription/quota-table.ts | 14 +- .../ja/translation/admin-console/tenants.ts | 15 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 18 +- .../translation/admin-console/user-details.ts | 13 +- .../src/locales/ja/translation/demo-app.ts | 1 + .../phrases/src/locales/ko/errors/session.ts | 28 +- .../admin-console/application-details.ts | 24 +- .../ko/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 11 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 48 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../ko/translation/admin-console/profile.ts | 33 + .../ko/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 62 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 20 +- .../ko/translation/admin-console/tenants.ts | 10 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 17 +- .../translation/admin-console/user-details.ts | 7 + .../src/locales/ko/translation/demo-app.ts | 1 + .../src/locales/pl-pl/errors/session.ts | 3 + .../admin-console/application-details.ts | 24 +- .../translation/admin-console/applications.ts | 6 - .../pl-pl/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../translation/admin-console/profile.ts | 34 + .../pl-pl/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 58 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 17 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/pl-pl/translation/demo-app.ts | 1 + .../src/locales/pt-br/errors/session.ts | 3 + .../admin-console/application-details.ts | 25 +- .../translation/admin-console/applications.ts | 7 - .../pt-br/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 11 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 3 +- .../translation/admin-console/profile.ts | 36 + .../pt-br/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 58 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/pt-br/translation/demo-app.ts | 1 + .../src/locales/pt-pt/errors/session.ts | 3 + .../admin-console/application-details.ts | 27 +- .../translation/admin-console/applications.ts | 7 - .../pt-pt/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../translation/admin-console/profile.ts | 36 + .../pt-pt/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 14 +- .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 13 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 2 + .../translation/admin-console/user-details.ts | 7 + .../src/locales/pt-pt/translation/demo-app.ts | 1 + .../phrases/src/locales/ru/errors/session.ts | 3 + .../admin-console/application-details.ts | 25 +- .../translation/admin-console/applications.ts | 6 - .../ru/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 12 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../ru/translation/admin-console/profile.ts | 37 + .../ru/translation/admin-console/roles.ts | 6 + .../admin-console/sign-in-exp/index.ts | 63 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../ru/translation/admin-console/tenants.ts | 13 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 4 +- .../translation/admin-console/user-details.ts | 7 + .../src/locales/ru/translation/demo-app.ts | 1 + .../src/locales/tr-tr/errors/session.ts | 29 +- .../admin-console/application-details.ts | 26 +- .../translation/admin-console/applications.ts | 6 - .../tr-tr/translation/admin-console/cloud.ts | 7 + .../translation/admin-console/components.ts | 3 +- .../admin-console/connector-details.ts | 18 +- .../translation/admin-console/connectors.ts | 11 +- .../translation/admin-console/jwt-claims.ts | 5 + .../admin-console/organization-details.ts | 49 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 1 + .../translation/admin-console/profile.ts | 35 + .../tr-tr/translation/admin-console/roles.ts | 5 + .../admin-console/sign-in-exp/index.ts | 64 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 11 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 6 +- .../translation/admin-console/user-details.ts | 7 + .../src/locales/tr-tr/translation/demo-app.ts | 1 + .../src/locales/zh-cn/errors/session.ts | 29 +- .../admin-console/application-details.ts | 25 +- .../translation/admin-console/applications.ts | 5 - .../zh-cn/translation/admin-console/cloud.ts | 6 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 15 +- .../translation/admin-console/connectors.ts | 10 +- .../translation/admin-console/jwt-claims.ts | 6 +- .../admin-console/organization-details.ts | 47 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 23 +- .../translation/admin-console/profile.ts | 27 + .../zh-cn/translation/admin-console/roles.ts | 10 +- .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 5 +- .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 24 +- .../translation/admin-console/tenants.ts | 9 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 1 + .../translation/admin-console/user-details.ts | 19 +- .../src/locales/zh-cn/translation/demo-app.ts | 1 + .../src/locales/zh-hk/errors/session.ts | 2 + .../admin-console/application-details.ts | 24 +- .../zh-hk/translation/admin-console/cloud.ts | 6 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 15 +- .../translation/admin-console/connectors.ts | 10 +- .../translation/admin-console/jwt-claims.ts | 12 +- .../admin-console/organization-details.ts | 47 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 5 +- .../translation/admin-console/profile.ts | 27 + .../zh-hk/translation/admin-console/roles.ts | 10 +- .../admin-console/sign-in-exp/index.ts | 68 +- .../sign-in-exp/sign-up-and-sign-in.ts | 5 +- .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 4 +- .../translation/admin-console/tenants.ts | 39 +- .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 37 +- .../translation/admin-console/user-details.ts | 15 +- .../src/locales/zh-hk/translation/demo-app.ts | 1 + .../src/locales/zh-tw/errors/session.ts | 2 + .../admin-console/application-details.ts | 26 +- .../zh-tw/translation/admin-console/cloud.ts | 6 + .../translation/admin-console/components.ts | 2 +- .../admin-console/connector-details.ts | 15 +- .../translation/admin-console/connectors.ts | 10 +- .../translation/admin-console/jwt-claims.ts | 8 +- .../admin-console/organization-details.ts | 51 +- .../admin-console/organization-template.ts | 5 +- .../admin-console/organizations.ts | 63 +- .../translation/admin-console/profile.ts | 27 + .../zh-tw/translation/admin-console/roles.ts | 10 +- .../admin-console/sign-in-exp/index.ts | 60 +- .../sign-in-exp/sign-up-and-sign-in.ts | 3 + .../admin-console/subscription/quota-item.ts | 12 + .../admin-console/subscription/quota-table.ts | 20 +- .../translation/admin-console/tenants.ts | 9 + .../admin-console/upsell/add-on.ts | 24 + .../admin-console/upsell/paywall.ts | 37 +- .../translation/admin-console/user-details.ts | 13 +- .../src/locales/zh-tw/translation/demo-app.ts | 1 + packages/schemas/CHANGELOG.md | 78 + ....0-1720253939-add-organization-branding.ts | 18 + .../1.19.0-1720345784-add-color-to-app-sie.ts | 18 + ...19.0-1720505152-update-custom-ui-assets.ts | 20 + .../1.19.0-1721483240-multiple-app-secrets.ts | 77 + ...5392-add-application-custom-data-column.ts | 18 + .../1.19.0-1722926389-argon2d-argon2id.ts | 35 + packages/schemas/package.json | 14 +- packages/schemas/src/consts/oidc.ts | 16 +- packages/schemas/src/consts/subscriptions.ts | 7 - packages/schemas/src/consts/system.ts | 3 + .../jsonb-types/sign-in-experience.ts | 31 +- packages/schemas/src/seeds/application.ts | 1 + .../schemas/src/seeds/sign-in-experience.ts | 4 +- packages/schemas/src/types/cookie.ts | 11 +- packages/schemas/src/types/index.ts | 1 + packages/schemas/src/types/interactions.ts | 114 +- packages/schemas/src/types/log/interaction.ts | 9 +- .../src/types/logto-config/jwt-customizer.ts | 15 +- .../schemas/src/types/sign-in-experience.ts | 2 +- packages/schemas/src/types/ssr.ts | 28 + packages/schemas/src/types/system.ts | 6 + packages/schemas/src/types/user-assets.ts | 27 +- packages/schemas/src/utils/application.ts | 9 + packages/schemas/src/utils/index.ts | 1 + .../schemas/tables/application_secrets.sql | 17 + .../application_sign_in_experiences.sql | 2 +- packages/schemas/tables/applications.sql | 9 +- packages/schemas/tables/organizations.sql | 2 + .../schemas/tables/sign_in_experiences.sql | 2 +- packages/schemas/tables/users.sql | 2 +- .../schemas/tsconfig.build.alterations.json | 5 +- packages/schemas/tsconfig.build.gen.json | 7 +- packages/shared/package.json | 8 +- packages/toolkit/connector-kit/package.json | 8 +- .../toolkit/core-kit/declaration/index.ts | 1 - .../core-kit/declaration/react-app.d.ts | 65 - packages/toolkit/core-kit/package.json | 10 +- packages/toolkit/language-kit/package.json | 8 +- pipeline-templates/build_service.yml | 2 + pipeline-templates/deploy_ecs.yml | 2 + pipeline-templates/push_image.yml | 5 +- pipeline-variables/dev.yml | 6 +- pipeline-variables/sta.yml | 3 + pipeline-variables/uat.yml | 3 + pnpm-lock.yaml | 8311 ++++++++--------- pnpm-workspace.yaml | 1 + run-logto-remote.sh | 0 tsup.shared.config.ts | 10 + vite.shared.config.ts | 52 + 1714 files changed, 38415 insertions(+), 11106 deletions(-) delete mode 100644 .npmrc create mode 100644 AWESOME.md create mode 100644 packages/cli/src/commands/database/ogcio/ogcio-seeder-testing.json create mode 100644 packages/cli/src/commands/database/ogcio/users.ts create mode 100644 packages/cli/src/commands/proxy/index.ts create mode 100644 packages/cli/src/commands/proxy/types.ts create mode 100644 packages/cli/src/commands/proxy/utils.ts create mode 100644 packages/connectors/connector-ogcio-entraid/CHANGELOG.md create mode 100644 packages/connectors/connector-ogcio-entraid/README.md create mode 100644 packages/connectors/connector-ogcio-entraid/logo.svg create mode 100644 packages/connectors/connector-ogcio-entraid/package.json create mode 100644 packages/connectors/connector-ogcio-entraid/src/constant.ts create mode 100644 packages/connectors/connector-ogcio-entraid/src/index.test.ts create mode 100644 packages/connectors/connector-ogcio-entraid/src/index.ts create mode 100644 packages/connectors/connector-ogcio-entraid/src/types.ts create mode 100644 packages/connectors/connector-postmark/CHANGELOG.md create mode 100644 packages/connectors/connector-postmark/README.md create mode 100644 packages/connectors/connector-postmark/logo.svg create mode 100644 packages/connectors/connector-postmark/package.json create mode 100644 packages/connectors/connector-postmark/src/constant.ts create mode 100644 packages/connectors/connector-postmark/src/index.test.ts create mode 100644 packages/connectors/connector-postmark/src/index.ts create mode 100644 packages/connectors/connector-postmark/src/mock.ts create mode 100644 packages/connectors/connector-postmark/src/types.ts delete mode 100644 packages/connectors/templates/preset/rollup.config.js delete mode 100644 packages/connectors/templates/preset/tsconfig.base.json delete mode 100644 packages/connectors/templates/preset/tsconfig.build.json delete mode 100644 packages/connectors/templates/preset/tsconfig.test.json create mode 100644 packages/connectors/templates/preset/tsup.config.ts delete mode 100644 packages/console/.parcelrc delete mode 100644 packages/console/.parcelrc.arm64 rename packages/console/{src => }/index.html (51%) delete mode 100644 packages/console/parcel-transformer-mdx2.js create mode 100644 packages/console/src/assets/docs/guides/api-express/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/m2m-general/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/native-expo/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-express/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-next-app-router/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-next/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-outline/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-passport/README.mdx create mode 100644 packages/console/src/assets/docs/guides/web-passport/config.json create mode 100644 packages/console/src/assets/docs/guides/web-passport/index.ts create mode 100644 packages/console/src/assets/docs/guides/web-passport/logo-dark.svg create mode 100644 packages/console/src/assets/docs/guides/web-passport/logo.svg create mode 100644 packages/console/src/assets/docs/guides/web-ruby/logo.svg delete mode 100644 packages/console/src/assets/docs/guides/web-ruby/logo.webp create mode 100644 packages/console/src/assets/icons/calendar-dark.svg create mode 100644 packages/console/src/assets/images/blur-preview.svg create mode 100644 packages/console/src/components/AddOnNoticeFooter/index.module.scss create mode 100644 packages/console/src/components/AddOnNoticeFooter/index.tsx create mode 100644 packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.module.scss create mode 100644 packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/index.tsx create mode 100644 packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/use-featured-sku-content.ts create mode 100644 packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/index.tsx create mode 100644 packages/console/src/components/FeatureTag/AddOnTag.tsx create mode 100644 packages/console/src/components/FileIcon/index.tsx create mode 100644 packages/console/src/components/ImageInputs/LogoAndFavicon.tsx rename packages/console/src/{pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader => components/ImageInputs}/index.module.scss (69%) create mode 100644 packages/console/src/components/ImageInputs/index.tsx create mode 100644 packages/console/src/components/PlanUsage/ProPlanUsageCard/index.module.scss create mode 100644 packages/console/src/components/PlanUsage/ProPlanUsageCard/index.tsx create mode 100644 packages/console/src/components/PlanUsage/utils.ts delete mode 100644 packages/console/src/consts/user-assets.ts create mode 100644 packages/console/src/containers/ConsoleRoutes/internal.ts create mode 100644 packages/console/src/contexts/SubscriptionDataProvider/use-new-subscription-data.ts create mode 100644 packages/console/src/ds-components/FormField/Skeleton.module.scss create mode 100644 packages/console/src/ds-components/FormField/Skeleton.tsx create mode 100644 packages/console/src/hooks/use-logto-skus.ts create mode 100644 packages/console/src/hooks/use-new-subscription-quota.ts create mode 100644 packages/console/src/hooks/use-new-subscription-scopes-usage.ts create mode 100644 packages/console/src/hooks/use-new-subscription-usage.ts delete mode 100644 packages/console/src/include.d/react-app.d.ts create mode 100644 packages/console/src/include.d/vite-env.d.ts create mode 100644 packages/console/src/pages/ApiResources/components/CreateForm/index.module.scss delete mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/LogoUploader.module.scss delete mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/LogoUploader.tsx create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/NonThirdPartyBrandingForm.tsx create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.module.scss create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/CreateSecretModal.tsx create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/EditSecretModal.tsx create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/EndpointsAndCredentials/index.module.scss rename packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/{EndpointsAndCredentials.tsx => EndpointsAndCredentials/index.tsx} (55%) create mode 100644 packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/EndpointsAndCredentials/use-secret-table-columns.tsx create mode 100644 packages/console/src/pages/CustomizeJwt/UpsellNotice/index.module.scss create mode 100644 packages/console/src/pages/CustomizeJwt/UpsellNotice/index.tsx create mode 100644 packages/console/src/pages/Mfa/MfaForm/UpsellNotice/index.tsx create mode 100644 packages/console/src/pages/Organizations/CreateOrganizationModal/index.module.scss delete mode 100644 packages/console/src/pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader/index.tsx delete mode 100644 packages/console/src/pages/SignInExperience/PageContent/Branding/CustomCssForm/index.tsx rename packages/console/src/pages/SignInExperience/PageContent/Branding/{CustomCssForm => CustomUiForm}/index.module.scss (63%) create mode 100644 packages/console/src/pages/SignInExperience/PageContent/Branding/CustomUiForm/index.tsx create mode 100644 packages/console/src/pages/SignInExperience/components/CustomUiAssetsUploader/index.module.scss create mode 100644 packages/console/src/pages/SignInExperience/components/CustomUiAssetsUploader/index.tsx create mode 100644 packages/console/src/pages/SignInExperience/contexts/SignInExperienceContextProvider/index.tsx create mode 100644 packages/console/src/pages/TenantSettings/Subscription/DowngradeConfirmModalContent/PlanQuotaDiffCard/PlanQuotaList/DiffQuotaItem/SkuQuotaItemPhrase.tsx create mode 100644 packages/console/src/types/sign-in-experience.ts create mode 100644 packages/console/src/types/skus.ts create mode 100644 packages/console/src/utils/object.ts delete mode 100644 packages/console/svgo.config.json create mode 100644 packages/console/vite.config.ts delete mode 100644 packages/core/nodemon.json create mode 100644 packages/core/src/middleware/koa-app-secret-transpilation.test.ts create mode 100644 packages/core/src/middleware/koa-app-secret-transpilation.ts create mode 100644 packages/core/src/middleware/koa-experience-ssr.test.ts create mode 100644 packages/core/src/middleware/koa-experience-ssr.ts rename packages/core/src/{routes/interaction => }/middleware/koa-interaction-details.ts (100%) create mode 100644 packages/core/src/middleware/koa-serve-custom-ui-assets.test.ts create mode 100644 packages/core/src/middleware/koa-serve-custom-ui-assets.ts create mode 100644 packages/core/src/oidc/grants/token-exchange/actor-token.test.ts create mode 100644 packages/core/src/oidc/grants/token-exchange/actor-token.ts rename packages/core/src/oidc/grants/{token-exchange.test.ts => token-exchange/index.test.ts} (95%) rename packages/core/src/oidc/grants/{token-exchange.ts => token-exchange/index.ts} (82%) create mode 100644 packages/core/src/oidc/grants/token-exchange/types.ts create mode 100644 packages/core/src/oidc/grants/utils.ts create mode 100644 packages/core/src/queries/application-secrets.ts create mode 100644 packages/core/src/routes/applications/application-custom-data.openapi.json create mode 100644 packages/core/src/routes/applications/application-custom-data.ts create mode 100644 packages/core/src/routes/applications/application-secret.openapi.json create mode 100644 packages/core/src/routes/applications/application-secret.ts create mode 100644 packages/core/src/routes/experience/classes/experience-interaction.test.ts create mode 100644 packages/core/src/routes/experience/classes/helpers.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/mfa-validator.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/password-validator.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/profile-validator.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/provision-library.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/sentinel-guard.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/sign-in-experience-validator.test.ts create mode 100644 packages/core/src/routes/experience/classes/libraries/sign-in-experience-validator.ts create mode 100644 packages/core/src/routes/experience/classes/mfa.ts create mode 100644 packages/core/src/routes/experience/classes/profile.ts create mode 100644 packages/core/src/routes/experience/classes/utils.test.ts create mode 100644 packages/core/src/routes/experience/classes/utils.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/backup-code-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/enterprise-sso-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/new-password-identity-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/totp-verification.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/verification-records-map.ts create mode 100644 packages/core/src/routes/experience/classes/verifications/web-authn-verification.ts create mode 100644 packages/core/src/routes/experience/middleware/koa-experience-interaction-hooks.ts create mode 100644 packages/core/src/routes/experience/middleware/koa-experience-verifications-audit-log.ts create mode 100644 packages/core/src/routes/experience/profile-routes.ts delete mode 100644 packages/core/src/routes/experience/utils.ts create mode 100644 packages/core/src/routes/experience/verification-routes/backup-code-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/enterprise-sso-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/new-password-identity-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/totp-verification.ts create mode 100644 packages/core/src/routes/experience/verification-routes/web-authn-verification.ts create mode 100644 packages/core/src/routes/sign-in-experience/custom-ui-assets/index.openapi.json create mode 100644 packages/core/src/routes/sign-in-experience/custom-ui-assets/index.test.ts create mode 100644 packages/core/src/routes/sign-in-experience/custom-ui-assets/index.ts rename packages/core/src/routes/{security/index.openapi.json => subject-token.openapi.json} (82%) rename packages/core/src/routes/{security/index.ts => subject-token.ts} (73%) create mode 100644 packages/core/src/utils/file.test.ts create mode 100644 packages/core/src/utils/file.ts delete mode 100644 packages/core/src/utils/storage/consts.ts delete mode 100644 packages/core/tsconfig.base.json delete mode 100644 packages/core/tsconfig.build.json create mode 100644 packages/core/tsup.config.ts create mode 100644 packages/core/tsup.dev.config.ts delete mode 100644 packages/demo-app/.parcelrc.arm64 rename packages/demo-app/{src => }/index.html (55%) create mode 100644 packages/demo-app/src/include.d/vite-end.d.ts create mode 100644 packages/demo-app/vite.config.ts create mode 100644 packages/elements/.gitignore create mode 100644 packages/elements/README.md create mode 100644 packages/elements/index.html create mode 100644 packages/elements/lit-localize.json create mode 100644 packages/elements/package.json create mode 100644 packages/elements/src/components/logto-avatar.ts create mode 100644 packages/elements/src/components/logto-button.styles.ts create mode 100644 packages/elements/src/components/logto-button.ts create mode 100644 packages/elements/src/components/logto-card-section.ts create mode 100644 packages/elements/src/components/logto-card.ts create mode 100644 packages/elements/src/components/logto-form-card.ts create mode 100644 packages/elements/src/components/logto-icon-button.ts create mode 100644 packages/elements/src/components/logto-list-row.ts create mode 100644 packages/elements/src/components/logto-list.ts create mode 100644 packages/elements/src/components/logto-modal-layout.ts create mode 100644 packages/elements/src/components/logto-modal.context.ts create mode 100644 packages/elements/src/components/logto-modal.ts create mode 100644 packages/elements/src/components/logto-text-input.ts create mode 100644 packages/elements/src/elements/logto-profile-card.ts create mode 100644 packages/elements/src/icons/close.svg create mode 100644 packages/elements/src/icons/index.d.ts create mode 100644 packages/elements/src/index.ts create mode 100644 packages/elements/src/phrases/index.ts create mode 100644 packages/elements/src/providers/logto-theme-provider.ts create mode 100644 packages/elements/src/providers/logto-user-provider.ts create mode 100644 packages/elements/src/react.ts create mode 100644 packages/elements/src/utils/api.ts create mode 100644 packages/elements/src/utils/css.ts create mode 100644 packages/elements/src/utils/locale.ts create mode 100644 packages/elements/src/utils/string.ts create mode 100644 packages/elements/src/utils/theme.ts create mode 100644 packages/elements/tsconfig.json create mode 100644 packages/elements/tsup.config.ts create mode 100644 packages/elements/web-dev-server.config.js create mode 100644 packages/elements/xliff/de.xlf create mode 100644 packages/elements/xliff/es.xlf create mode 100644 packages/elements/xliff/fr.xlf create mode 100644 packages/elements/xliff/it.xlf create mode 100644 packages/elements/xliff/ja.xlf create mode 100644 packages/elements/xliff/ko.xlf create mode 100644 packages/elements/xliff/pl-PL.xlf create mode 100644 packages/elements/xliff/pt-BR.xlf create mode 100644 packages/elements/xliff/pt-PT.xlf create mode 100644 packages/elements/xliff/ru.xlf create mode 100644 packages/elements/xliff/tr-TR.xlf create mode 100644 packages/elements/xliff/zh-CN.xlf create mode 100644 packages/elements/xliff/zh-HK.xlf create mode 100644 packages/elements/xliff/zh-TW.xlf create mode 100644 packages/experience/.eslintrc.cjs delete mode 100644 packages/experience/.parcelrc delete mode 100644 packages/experience/.parcelrc.arm64 create mode 100644 packages/experience/index.html create mode 100644 packages/experience/src/assets/icons/loading-ring.svg create mode 100644 packages/experience/src/components/Button/RotatingRingIcon.module.scss create mode 100644 packages/experience/src/components/Button/RotatingRingIcon.tsx create mode 100644 packages/experience/src/components/InputFields/InputField/NotchedBorder/index.module.scss create mode 100644 packages/experience/src/components/InputFields/InputField/NotchedBorder/index.tsx create mode 100644 packages/experience/src/components/LoadingMask/index.module.scss create mode 100644 packages/experience/src/components/LoadingMask/index.tsx delete mode 100644 packages/experience/src/constants/env.ts create mode 100644 packages/experience/src/containers/SetPassword/HiddenIdentifierInput.tsx delete mode 100644 packages/experience/src/include.d/react-app.d.ts create mode 100644 packages/experience/src/include.d/vite-env.d.ts delete mode 100644 packages/experience/src/index.html create mode 100644 packages/experience/vite.config.ts create mode 100644 packages/integration-tests/src/helpers/experience/enterprise-sso-verification.ts create mode 100644 packages/integration-tests/src/helpers/experience/totp-verification.ts create mode 100644 packages/integration-tests/src/include.d/global.d.ts create mode 100644 packages/integration-tests/src/tests/api/application/application-custom-data.test.ts create mode 100644 packages/integration-tests/src/tests/api/application/application.secrets.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/bind-mfa/happpy-path.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/bind-mfa/sad-path.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/interaction.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/profile/fulfill-user-profiles.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/profile/reset-password.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/register-interaction/organization-jti.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/register-interaction/username-password.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/register-interaction/verification-code.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/sign-in-interaction/enterprise-sso.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/sign-in-interaction/mfa-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/sign-in-interaction/social.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/backup-code-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/enterprise-sso-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/new-password-identity-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/experience-api/verifications/totp-verification.test.ts create mode 100644 packages/integration-tests/src/tests/api/hook/hook.trigger.experience.test.ts create mode 100644 packages/integration-tests/src/tests/api/oidc/client-authentication.test.ts create mode 100644 packages/integration-tests/src/tests/console/error-handling.test.ts create mode 100644 packages/integration-tests/src/tests/experience/overrides.test.ts create mode 100644 packages/integration-tests/src/tests/experience/server-side-rendering.test.ts create mode 100644 packages/integration-tests/src/ui-helpers/trace.ts create mode 100644 packages/integration-tests/tsup.config.ts create mode 100644 packages/phrases/src/locales/de/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/en/translation/admin-console/subscription/usage.ts create mode 100644 packages/phrases/src/locales/en/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/es/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/fr/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/it/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/ja/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/ko/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/pl-pl/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/pt-br/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/pt-pt/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/ru/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/tr-tr/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/zh-cn/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/zh-hk/translation/admin-console/upsell/add-on.ts create mode 100644 packages/phrases/src/locales/zh-tw/translation/admin-console/upsell/add-on.ts create mode 100644 packages/schemas/alterations/1.19.0-1720253939-add-organization-branding.ts create mode 100644 packages/schemas/alterations/1.19.0-1720345784-add-color-to-app-sie.ts create mode 100644 packages/schemas/alterations/1.19.0-1720505152-update-custom-ui-assets.ts create mode 100644 packages/schemas/alterations/1.19.0-1721483240-multiple-app-secrets.ts create mode 100644 packages/schemas/alterations/1.19.0-1721645392-add-application-custom-data-column.ts create mode 100644 packages/schemas/alterations/1.19.0-1722926389-argon2d-argon2id.ts create mode 100644 packages/schemas/src/types/ssr.ts create mode 100644 packages/schemas/src/utils/application.ts create mode 100644 packages/schemas/tables/application_secrets.sql delete mode 100644 packages/toolkit/core-kit/declaration/react-app.d.ts mode change 100755 => 100644 run-logto-remote.sh create mode 100644 tsup.shared.config.ts create mode 100644 vite.shared.config.ts diff --git a/.dockerignore b/.dockerignore index f0ffc0d34b7..26fc4427468 100644 --- a/.dockerignore +++ b/.dockerignore @@ -33,5 +33,4 @@ dump.rdb .devcontainer .github .husky -.parcel-cache -.vscode +.vscode \ No newline at end of file diff --git a/.github/workflows/alteration-compatibility-integration-test.yml b/.github/workflows/alteration-compatibility-integration-test.yml index 4e82e5485c7..27d1454786c 100644 --- a/.github/workflows/alteration-compatibility-integration-test.yml +++ b/.github/workflows/alteration-compatibility-integration-test.yml @@ -91,3 +91,11 @@ jobs: GH_TOKEN: ${{ github.token }} GH_DEBUG: api run: gh workflow run rerun.yml -F run_id=${{ github.run_id }} + + alteration-compatibility-conclusion: + needs: run-logto + runs-on: ubuntu-latest + if: always() && (needs.run-logto.result == 'success' || needs.run-logto.result == 'skipped') + steps: + - name: Conclusion + run: echo "Alteration compatibility integration test completed successfully" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b525dd8d6f1..1afaf44a0dc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,12 +20,25 @@ jobs: - name: Setup Node and pnpm uses: silverhand-io/actions-node-pnpm-run-steps@v5 - with: - pnpm-version: 9 - name: Build run: pnpm ci:build + main-check: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node and pnpm + uses: silverhand-io/actions-node-pnpm-run-steps@v5 + + - name: Prepack + run: pnpm prepack + + - name: Check + run: pnpm -r check + main-lint: runs-on: ubuntu-latest @@ -34,8 +47,6 @@ jobs: - name: Setup Node and pnpm uses: silverhand-io/actions-node-pnpm-run-steps@v5 - with: - pnpm-version: 9 - name: Prepack run: pnpm prepack @@ -54,8 +65,6 @@ jobs: - name: Setup Node and pnpm uses: silverhand-io/actions-node-pnpm-run-steps@v5 - with: - pnpm-version: 9 - name: Build for test run: pnpm -r build:test @@ -122,7 +131,6 @@ jobs: - name: Setup Node and pnpm uses: silverhand-io/actions-node-pnpm-run-steps@v5 with: - pnpm-version: 9 run-install: false # ** Prepack packages ** @@ -188,4 +196,4 @@ jobs: - name: Check alteration sequence working-directory: ./fresh - run: node .scripts/check-alterations-sequence.js \ No newline at end of file + run: node .scripts/check-alterations-sequence.js diff --git a/.gitpod.yml b/.gitpod.yml index b6a984ffb9f..e8440cdea7c 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -38,7 +38,7 @@ ports: port: 5432 visibility: public onOpen: ignore - # OGCIO - formsie port collision fixed + # OGCIO - formsie port collision fixed - port: 7001 onOpen: ignore - port: 5002 diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 573e44e096e..00000000000 --- a/.npmrc +++ /dev/null @@ -1,6 +0,0 @@ -# Hoist for Parcel -public-hoist-pattern[]=@parcel/* -public-hoist-pattern[]=postcss -public-hoist-pattern[]=process -public-hoist-pattern[]=*eslint* -public-hoist-pattern[]=buffer diff --git a/.scripts/compare-database.js b/.scripts/compare-database.js index b959db38533..f7afeaab692 100644 --- a/.scripts/compare-database.js +++ b/.scripts/compare-database.js @@ -42,14 +42,14 @@ const queryDatabaseManifest = async (database) => { `); const { rows: constraints } = await pool.query(/* sql */` - select conrelid::regclass AS table, con.*, pg_get_constraintdef(con.oid) + select conrelid::regclass as r_table, con.*, pg_get_constraintdef(con.oid) as def from pg_catalog.pg_constraint con inner join pg_catalog.pg_class rel on rel.oid = con.conrelid inner join pg_catalog.pg_namespace nsp on nsp.oid = connamespace where nsp.nspname = 'public' - order by conname asc; + order by conname asc, def asc; `); const { rows: indexes } = await pool.query(/* sql */` diff --git a/.scripts/package.sh b/.scripts/package.sh index c7654930ab8..7e2720b1840 100755 --- a/.scripts/package.sh +++ b/.scripts/package.sh @@ -18,7 +18,7 @@ fi # Some node packages use `src` as their dist folder, so ignore them from the rm list in the end find \ -.git .changeset .devcontainer .github .husky .parcel-cache .scripts .vscode pnpm-*.yaml *.js \ +.git .changeset .devcontainer .github .husky .scripts .vscode pnpm-*.yaml *.js \ packages/**/src \ packages/**/*.config.js packages/**/*.config.ts packages/**/tsconfig*.json \ ! -path '**/node_modules/**' \ diff --git a/.vscode/settings.json b/.vscode/settings.json index 805805faacf..9df9bb7b150 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,9 +23,6 @@ "typescript", "typescriptreact", ], - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" - }, "json.schemas": [ { "fileMatch": [ @@ -55,6 +52,7 @@ "topbar", "upsell", "withtyped", - "backchannel" + "backchannel", + "deepmerge" ] -} +} \ No newline at end of file diff --git a/.vscode/tsx.code-snippets b/.vscode/tsx.code-snippets index 2f1caf11c1b..be2b52bab50 100644 --- a/.vscode/tsx.code-snippets +++ b/.vscode/tsx.code-snippets @@ -10,7 +10,7 @@ "scope": "javascriptreact,typescriptreact", "prefix": "isc", "body": [ - "import * as styles from './index.module.scss';", + "import styles from './index.module.scss';", "$0" ], "description": "Import SCSS styles from the same directory." diff --git a/AWESOME.md b/AWESOME.md new file mode 100644 index 00000000000..5b4a0dfb104 --- /dev/null +++ b/AWESOME.md @@ -0,0 +1,11 @@ +# Logto awesome + +Here's the list of awesome community-contributed resources for Logto. Feel free to add yours by submitting a pull request. + +## Account + +- [Logto Account Dashboard](https://github.com/t2vee/Logto-Account-Dashboard) by @t2vee + +## API + +- [Go API client for logto](https://github.com/mostafa/go-api-client) by @mostafa diff --git a/Dockerfile b/Dockerfile index 1be470e7711..18ac74d02ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,7 @@ ENV PUPPETEER_SKIP_DOWNLOAD=true ENV PORT=3301 # OGCIO ENV ADMIN_PORT=3302 + ### Install toolchain ### RUN npm add --location=global pnpm@^9.0.0 # https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md#node-gyp-alpine @@ -18,7 +19,6 @@ RUN apk add --no-cache python3 make g++ rsync COPY . . ### Install dependencies and build ### -RUN node .scripts/update-parcelrc.js RUN pnpm i ### Set if dev features enabled ### @@ -39,7 +39,7 @@ RUN rm -rf node_modules packages/**/node_modules RUN NODE_ENV=production pnpm i ### Clean up ### -RUN rm -rf .scripts .parcel-cache pnpm-*.yaml packages/cloud +RUN rm -rf .scripts pnpm-*.yaml packages/cloud ###### [STAGE] Seal ###### FROM node:20-alpine as app @@ -49,9 +49,11 @@ ENV PORT=3301 ENV ADMIN_PORT=3302 WORKDIR /etc/logto COPY --from=builder /etc/logto . -RUN apk add --no-cache jq +# OGCIO EXPOSE 3301 # OGCIO EXPOSE 3302 +#OGCIO +RUN apk add --no-cache jq # OGCIO CMD [ "sh", "-c", "export ENCODED_PASSWORD=$(jq --slurp --raw-input --raw-output @uri <(printf \"%s\" $POSTGRES_PASSWORD)) && export DB_URL=\"postgres://$POSTGRES_USER:$ENCODED_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB_NAME\" && export REDIS_URL=\"redis://$REDIS_HOST:$REDIS_PORT\" && npm run ogcio:start"] diff --git a/README.OGCIO.md b/README.OGCIO.md index 3b39ab59468..b4306a43571 100644 --- a/README.OGCIO.md +++ b/README.OGCIO.md @@ -26,6 +26,9 @@ 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! +To help with conflict resolution you can leverage git rerere functionality (reuse recorded resolution). To enable it run `git config rerere.enabled true`. Enabling the rerere setting makes Git run for you `git rerere`, with no subcommand, at the appropriate times. When performing any merge, git will record the conflicting diff hunks and record, at commit time, how they were manually resolved. If there is already any previous recorded resolutions for those conflicts, git will use them to resolve the conflicts automatically. Additional [subcommands](https://git-scm.com/docs/git-rerere) are available and can help interacting with its working state. + + ## 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. @@ -39,9 +42,10 @@ make build run 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 +aws configure sso ``` -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. +After that, follow the prompts. You should be asked a name for the session (whatever helps you identify the session), the SSO start URL and the region. 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. You can also find the region value in the same section. +For all other options, such as registration scopes, you can go with the default. 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: @@ -63,7 +67,7 @@ curl -fsSL https://raw.githubusercontent.com/ogcio/logto/HEAD/docker-compose-ogc If you already have the repo cloned locally there is a Make command available: ``` -make build run-remote +make run-remote ``` ## Setup and run Logto natively diff --git a/README.md b/README.md index 35d06147178..889aa2d71a3 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Logto[^info] is an Auth0 alternative designed for modern apps and SaaS products. - Enables OIDC-based authentication with Logto SDKs. - Supports passwordless sign-in, along with various options like email, phone number, username, Google, Facebook, and other social sign-in methods. - Offers beautiful UI components with customizable CSS to suit your business needs. +- Has an open community with many warm-hearted contributors and users. Check out our [awesome list](./AWESOME.md) of community-contributed resources. 📦 **Out-of-the-box infrastructure** @@ -37,12 +38,12 @@ Logto[^info] is an Auth0 alternative designed for modern apps and SaaS products. - Implements role-based access control (RBAC) for scalable role authorization, catering to a wide range of use cases. - Facilitates user management and provides audit logs for understanding identity-related user information and maintaining security. - Enables single sign-on (SSO) and multi-factor authentication (MFA) without extra coding. -- Leverages Logto Organizations to build multi-tenancy apps with ease. +- Leverages Logto organizations to build multi-tenancy apps with ease. In a more approachable way, we refer to this solution as "[Customer Identity Access Management (CIAM)](https://en.wikipedia.org/wiki/Customer_identity_access_management)" or simply, the "Customer Identity Solution." -[Subscribe to us](https://logto.io/subscribe/?utm_source=github&utm_medium=repo_logto) now to stay updated with the latest information about the Logto Cloud (SaaS) and receive feature updates in real-time. - +> [!IMPORTANT] +> [Subscribe to us](https://logto.io/subscribe/?utm_source=github&utm_medium=repo_logto) now to stay updated with the latest information about the Logto Cloud (SaaS) and receive feature updates in real-time. ## Get started diff --git a/azure_pipelines.yml b/azure_pipelines.yml index 10e6680d1ad..7f072eb5d3b 100644 --- a/azure_pipelines.yml +++ b/azure_pipelines.yml @@ -1,3 +1,5 @@ +# OGCIO + trigger: - dev - uat @@ -56,16 +58,33 @@ stages: serviceName: logto pushArtefacts: ${{ variables.pushArtefacts }} buildArguments: $(buildArguments) + - stage: EnvApproval + displayName: Approvals for deployments - ${{ upper(variables.environment) }} + dependsOn: + - Build_Logto + condition: ${{ variables.pushArtefacts }} + jobs: + - deployment: VerifyDeployment + displayName: Verify conditions for deployment + environment: ${{ variables.environment }} + strategy: + runOnce: + deploy: + steps: + - script: | + date + displayName: Show current date - stage: Push_Logto displayName: Push logto to ECR - dependsOn: Build_Logto - condition: ${{ variables.pushArtefacts }} + dependsOn: EnvApproval + condition: and(${{ variables.pushArtefacts }}, succeeded()) jobs: - template: pipeline-templates/push_image.yml parameters: awsServiceConnection: ${{ variables.awsServiceConnection }} awsRegion: ${{ variables.awsRegion }} serviceName: logto + containerTag: $(Build.SourceBranchName) - stage: Deploy_Logto displayName: Deploy to ECS - logto dependsOn: Push_Logto @@ -98,11 +117,14 @@ stages: 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') + dependsOn: + - Build_MyGovId_Mock + - EnvApproval + condition: and(eq(variables['Build.SourceBranchName'], 'dev'), succeeded()) jobs: - template: pipeline-templates/push_image.yml parameters: awsServiceConnection: ${{ variables.awsServiceConnection }} awsRegion: ${{ variables.awsRegion }} serviceName: mygovid-mock-service + containerTag: mygovid-mock-service diff --git a/commitlint.config.ts b/commitlint.config.ts index 5f44c75e5e8..c89743462dc 100644 --- a/commitlint.config.ts +++ b/commitlint.config.ts @@ -7,7 +7,7 @@ const config: UserConfig = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [2, 'always', [...conventional.rules['type-enum'][2], 'api', 'release']], - 'scope-enum': [2, 'always', ['connector', 'console', 'core', 'demo-app', 'test', 'phrases', 'schemas', 'shared', 'experience', 'deps', 'deps-dev', 'cli', 'toolkit', 'cloud', 'app-insights']], + 'scope-enum': [2, 'always', ['connector', 'console', 'core', 'demo-app', 'test', 'phrases', 'schemas', 'shared', 'experience', 'deps', 'deps-dev', 'cli', 'toolkit', 'cloud', 'app-insights', 'elements']], // Slightly increase the tolerance to allow the appending PR number ...(isCi && { 'header-max-length': [2, 'always', 110] }), 'body-max-line-length': [2, 'always', 110], diff --git a/docker-compose-ogcio-logto.yml b/docker-compose-ogcio-logto.yml index 9c427d4577d..9367f37efd0 100644 --- a/docker-compose-ogcio-logto.yml +++ b/docker-compose-ogcio-logto.yml @@ -46,7 +46,7 @@ services: - 5433:5433 mygovid-mock-service: - image: 730335224023.dkr.ecr.eu-west-1.amazonaws.com/life-events-logto@sha256:d27a147deb66d0869c8384c0f7415d7df7694212b95b3a54b183be4723924e36 + image: 730335224023.dkr.ecr.eu-west-1.amazonaws.com/life-events-logto@sha256:e970daf7bba41fd23f8b2fda0ab12f1f13449883fc0b0ce625fa1b1c2233d698 ports: - 4005:4005 diff --git a/mygovid-mock-service/.dockerignore b/mygovid-mock-service/.dockerignore index f06235c460c..b35f0b6726c 100644 --- a/mygovid-mock-service/.dockerignore +++ b/mygovid-mock-service/.dockerignore @@ -1,2 +1,2 @@ -node_modules +**/node_modules dist diff --git a/mygovid-mock-service/src/routes/static/mock-login.html b/mygovid-mock-service/src/routes/static/mock-login.html index aeef9c9dfd8..a7f4b0f59e9 100644 --- a/mygovid-mock-service/src/routes/static/mock-login.html +++ b/mygovid-mock-service/src/routes/static/mock-login.html @@ -216,13 +216,33 @@ oid: "4cfa878a4fd9892a64ac", sub: "932d94fc69be147f6fcb", }, + { + user_name: "Bruce Wayne", + govid_email: "bruce.wayne@mail.ie", + oid: "4cfa878a4fd9892a92ij", + sub: "932d94fc69be147fpq3v", + }, { user_name: "Tony Stark", - govid_email: "tony.stark@mail.ie", + govid_email: "tony.stark@gov.ie", oid: "71848ec91433bc4222d0", sub: "7ffe40ff7d558de01c54", is_public_servant: true, }, + { + user_name: "John Doe", + govid_email: "john.doe@gov.ie", + oid: "71848ec91433bc422244", + sub: "7ffe40ff7d558de01c67", + is_public_servant: true, + }, + { + user_name: "Flash Gordon", + govid_email: "flash.gordon@gov.ie", + oid: "93f48ec91433bc422231", + sub: "zq3e40ff7d558de01c7t", + is_public_servant: true, + }, ] } diff --git a/package.json b/package.json index 28ff7ea7f52..527d8d94110 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,9 @@ "@types/pg": "^8.6.6", "husky": "^9.0.0", "pg": "^8.8.0", - "typescript": "^5.0.0" + "tsup": "^8.1.0", + "typescript": "^5.0.0", + "vite": "^5.3.4" }, "engines": { "node": "^20.9.0", @@ -52,9 +54,5 @@ }, "dependencies": { "@logto/cli": "workspace:^1.1.0" - }, - "//": "@see https://parceljs.org/features/dependency-resolution/#package-exports", - "@parcel/resolver-default": { - "packageExports": true } } diff --git a/packages/app-insights/package.json b/packages/app-insights/package.json index 8016dc02794..9591063d8af 100644 --- a/packages/app-insights/package.json +++ b/packages/app-insights/package.json @@ -32,12 +32,12 @@ "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.9.5", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.0", "prettier": "^3.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "engines": { "node": "^20.9.0" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index c0cf24da4bb..3dfb5f2e8fc 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,53 @@ # Change Log +## 1.19.0 + +### Minor Changes + +- 2d0502a42: add new cli command to setup proxy for developing and debugging custom ui locally + + This command will establish a proxy tunnel between the following 3 entities together: your Logto cloud auth services, your application, and your custom sign-in UI. + + Assuming you have a custom sign-in page running on `http://localhost:4000`. + Then you can execute the command this way: + + ```bash + npm cli proxy --endpoint https://.logto.app --port 9000 --experience-uri http://localhost:4000 + ``` + + Or if you don't have your custom UI pages hosted on a dev server, you can use the `--experience-path` option to specify the path to your static files: + + ```bash + npm cli proxy --endpoint https://.logto.app --port 9000 --experience-path /path/to/your/custom/ui + ``` + + This command also works if you have enabled custom domain in your Logto tenant. E.g.: + + ```bash + npm cli proxy --endpoint https://your-custom-domain.com --port 9000 --experience-path /path/to/your/custom/ui + ``` + + This should set up the proxy and it will be running on your local machine at `http://localhost:9000/`. + + Finally, run your application and set its Logto endpoint to the proxy address `http://localhost:9000/` instead. + + If all set up correctly, when you click the "sign-in" button in your application, you should be navigated to your custom sign-in page instead of Logto's built-in UI, along with valid session (cookies) that allows you to further interact with Logto experience API. + + Happy coding! + +### Patch Changes + +- Updated dependencies [6477c6dee] +- Updated dependencies [3a839f6d6] +- Updated dependencies [b91ec0cd6] +- Updated dependencies [d203c8d2f] +- Updated dependencies [b188bb161] +- Updated dependencies [62f5e5e0c] +- Updated dependencies [d56bc2f73] +- Updated dependencies [510f681fa] + - @logto/schemas@1.19.0 + - @logto/phrases@1.13.0 + ## 1.18.0 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 21793fdb2e4..10c7ea85d7a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@logto/cli", - "version": "1.18.0", + "version": "1.19.0", "description": "Logto CLI.", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", @@ -45,30 +45,32 @@ "@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.12.0", + "@logto/phrases": "workspace:^1.13.0", "@logto/phrases-experience": "workspace:^1.7.0", - "@logto/schemas": "workspace:1.18.0", + "@logto/schemas": "workspace:1.19.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", "@silverhand/slonik": "31.0.0-beta.2", - "chalk": "^5.0.0", + "chalk": "^5.3.0", "decamelize": "^6.0.0", - "dotenv": "^16.0.0", + "dotenv": "^16.4.5", "got": "^14.0.0", "hpagent": "^1.2.0", + "http-proxy-middleware": "^3.0.0", "inquirer": "^9.0.0", + "mime": "^4.0.4", "nanoid": "^5.0.1", "ora": "^8.0.1", - "p-limit": "^5.0.0", + "p-limit": "^6.0.0", "p-queue": "^8.0.0", "p-retry": "^6.0.0", "pg-protocol": "^1.6.0", "roarr": "^7.11.0", "semver": "^7.3.8", "tar": "^7.0.0", - "typescript": "^5.3.3", + "typescript": "^5.5.3", "yargs": "^17.6.0", - "zod": "^3.22.4" + "zod": "^3.23.8" }, "devDependencies": { "@silverhand/eslint-config": "6.0.1", @@ -79,13 +81,13 @@ "@types/sinon": "^17.0.0", "@types/tar": "^6.1.12", "@types/yargs": "^17.0.13", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "@withtyped/server": "^0.13.6", "eslint": "^8.56.0", "lint-staged": "^15.0.0", "prettier": "^3.0.0", "sinon": "^18.0.0", - "vitest": "^1.4.0" + "vitest": "^2.0.0" }, "eslintConfig": { "extends": "@silverhand", diff --git a/packages/cli/src/commands/database/ogcio/applications.ts b/packages/cli/src/commands/database/ogcio/applications.ts index 42c4a47b3bf..56d50d850a9 100644 --- a/packages/cli/src/commands/database/ogcio/applications.ts +++ b/packages/cli/src/commands/database/ogcio/applications.ts @@ -6,6 +6,7 @@ import { sql, type DatabaseTransactionConnection } from '@silverhand/slonik'; import { type ApplicationSeeder } from './ogcio-seeder.js'; import { createOrUpdateItem } from './queries.js'; +import { applyManagementApiRole } from './resources-rbac.js'; type SeedingApplication = { id: string; @@ -43,9 +44,14 @@ const setApplicationId = async ( const createArrayString = (values: string | string[]): string => { const valuesString = (Array.isArray(values) ? values : [values]) + .filter((value) => value.length > 0) .map((uri) => `"${uri}"`) .join(','); + if (valuesString.length === 0) { + return '[]'; + } + return `[${valuesString}]`; }; @@ -60,9 +66,12 @@ const fillApplications = ( secret: inputApp.secret, description: inputApp.description, type: inputApp.type, - oidc_client_metadata: `{"redirectUris": ${createArrayString(inputApp.redirect_uri)}, "postLogoutRedirectUris": ${createArrayString(inputApp.logout_redirect_uri)}}`, - custom_client_metadata: - '{"idTokenTtl": 3600, "corsAllowedOrigins": [], "rotateRefreshToken": true, "refreshTokenTtlInDays": 14, "alwaysIssueRefreshToken": false}', + oidc_client_metadata: `{"redirectUris": ${createArrayString( + inputApp.redirect_uri + )}, "postLogoutRedirectUris": ${createArrayString(inputApp.logout_redirect_uri)}}`, + custom_client_metadata: `{"idTokenTtl": 3600, "corsAllowedOrigins": [], "rotateRefreshToken": true, "refreshTokenTtlInDays": 1, "alwaysIssueRefreshToken": ${ + inputApp.always_issue_refresh_token ?? false + }}`, is_third_party: inputApp.is_third_party ?? false, }; } @@ -83,5 +92,18 @@ export const seedApplications = async (params: { await Promise.all(queries); + // Seed the M2M application with the correct role + const m2mManagementAPIsApplicationId = params.applications.find( + (app) => app.apply_management_api_role + )?.id; + + if (m2mManagementAPIsApplicationId) { + await applyManagementApiRole( + params.transaction, + params.tenantId, + m2mManagementAPIsApplicationId + ); + } + return appsToCreate; }; 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 b1f30bfee72..e33afad2aa9 100644 --- a/packages/cli/src/commands/database/ogcio/ogcio-seeder-local.json +++ b/packages/cli/src/commands/database/ogcio/ogcio-seeder-local.json @@ -5,10 +5,32 @@ "name": "OGCIO Seeded Org", "description": "Organization created through seeder", "id": "ogcio" + }, + { + "name": "Inactive Public Servants Org", + "description": "Temporary organization for inactive Public Servants", + "id": "inactive-ps-org" + }, + { + "name": "First Testing Organisation", + "description": "Organisation used to do E2E testing", + "id": "first-testing" + }, + { + "name": "Second Testing Organisation", + "description": "Organisation used to do E2E testing", + "id": "second-testing" } ], "organization_permissions": { "specific_permissions": [ + "analytics:global:*", + "analytics:measurable:admin", + "analytics:measurable:write", + "analytics:measurable:view", + "analytics:website:read", + "analytics:website:write", + "analytics:website:tracking", "payments:provider:*", "payments:payment_request:*", "payments:payment_request.public:read", @@ -18,7 +40,14 @@ "messaging:template:*", "messaging:citizen:*", "messaging:event:read", - "life-events:digital-wallet-flow:*" + "profile:user:read", + "profile:user:*", + "profile:address:*", + "profile:entitlement:*", + "life-events:digital-wallet-flow:*", + "bb:public-servant.inactive:*", + "scheduler:jobs:write", + "upload:file:*" ] }, "organization_roles": [ @@ -30,7 +59,8 @@ "payments:provider:*", "payments:payment_request:*", "payments:payment_request.public:read", - "payments:transaction:*" + "payments:transaction:*", + "scheduler:jobs:write" ] }, { @@ -42,7 +72,8 @@ "messaging:provider:*", "messaging:template:*", "messaging:citizen:*", - "messaging:event:read" + "messaging:event:read", + "scheduler:jobs:write" ] }, { @@ -50,6 +81,100 @@ "name": "Life Events Public Servant", "description": "Life Events Public servant", "specific_permissions": ["life-events:digital-wallet-flow:*"] + }, + { + "id": "pro-public-servant", + "name": "Profile Public Servant", + "description": "Profile Public servant", + "specific_permissions": ["profile:user:*", "profile:address:*", "profile:entitlement:*"] + }, + { + "id": "anl-admin", + "name": "Analytics Admin", + "description": "Analytics Admin", + "specific_permissions": ["analytics:measurable:admin"] + }, + { + "id": "anl-public-servant", + "name": "Analytics Public Servant", + "description": "Analytics Public servant", + "specific_permissions": ["analytics:measurable:view"] + }, + { + "id": "anl-super-user", + "name": "Analytics Super User", + "description": "Analytics Super User", + "specific_permissions": ["analytics:global:*"] + }, + { + "id": "upload-public-servant", + "name": "File Upload Public Servant", + "description": "File Upload Public servant", + "specific_permissions": ["upload:file:*", "profile:user:read"] + }, + { + "id": "bb-inactive-ps", + "name": "Inactive Public Servant", + "description": "Inactive Public Servant", + "specific_permissions": ["bb:public-servant.inactive:*"] + }, + { + "id": "m2m-ps-profile-reader", + "name": "M2M Public Servant Profile Reader", + "description": "Role for M2M Applications that need to read from Profile resources", + "specific_permissions": ["profile:user:read"], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "ddl4llp30risjwcjymqw3", "organization_id": "ogcio" }, + { "application_id": "ddl4llp30risjwcjymqw3", "organization_id": "first-testing" }, + { "application_id": "ddl4llp30risjwcjymqw3", "organization_id": "second-testing" } + ] + }, + { + "id": "m2m-ps-sched-writer", + "name": "M2M Public Servant Scheduler Writer", + "description": "Role for M2M Applications that need to write to Scheduler resources", + "specific_permissions": ["scheduler:jobs:write"], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "bjd8knt92hxslvrfyezp7", "organization_id": "ogcio" }, + { "application_id": "bjd8knt92hxslvrfyezp7", "organization_id": "first-testing" }, + { "application_id": "bjd8knt92hxslvrfyezp7", "organization_id": "second-testing" } + ] + }, + { + "id": "m2m-analytics-sdk", + "name": "M2M Analytics SDK Role", + "description": "Role for M2M Applications that need to read from Analytics resources", + "specific_permissions": [ + "analytics:website:read", + "analytics:website:write", + "analytics:website:tracking" + ], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "03emabp0dwznxw414mxme", "organization_id": "ogcio" } + ] + }, + { + "id": "m2m-e2e-tester", + "name": "M2M E2E Tester Role", + "description": "Role for M2M Applications that need to perform E2E testing", + "specific_permissions": [ + "messaging:message:*", + "messaging:provider:*", + "messaging:template:*", + "messaging:citizen:*", + "messaging:event:read", + "profile:user:read", + "upload:file:*", + "scheduler:jobs:write" + ], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "qrtllp45fgbvsdjyasd5", "organization_id": "first-testing" }, + { "application_id": "qrtllp45fgbvsdjyasd5", "organization_id": "second-testing" } + ] } ], "applications": [ @@ -78,10 +203,102 @@ "description": "Life Events App", "type": "Traditional", "redirect_uri": "http://localhost:3000/callback", - "logout_redirect_uri": "http://localhost:3000", + "logout_redirect_uri": "http://localhost:3000/admin", "secret": "life_events_app_local_secret", "id": "nfg61tuyfsgizsx8c4p3t", "is_third_party": false + }, + { + "name": "Profile Building Block", + "description": "Profile App of Life Events", + "type": "Traditional", + "redirect_uri": "http://localhost:3003/callback", + "logout_redirect_uri": "http://localhost:3003", + "secret": "profile_app_local_secret", + "id": "RZmVtZUnrv01YzUYEIwYW", + "is_third_party": false + }, + { + "name": "Analytics Building Block", + "description": "Shared platform for Analytics", + "type": "Traditional", + "redirect_uri": "http://localhost:8055/index.php?module=LoginOGCIO&action=callback&provider=oidc", + "logout_redirect_uri": "http://localhost:8055", + "secret": "analytics_app_local_secret", + "id": "kgz1zomlw27cxx4pxh5gi", + "is_third_party": false, + "always_issue_refresh_token": true + }, + { + "name": "Home Building Block", + "description": "Building Blocks home application", + "type": "Traditional", + "redirect_uri": "http://localhost:3004/callback", + "logout_redirect_uri": "http://localhost:3004", + "secret": "home_app_local_secret", + "id": "4icd76x8we31j1e8bqyfn", + "is_third_party": false + }, + { + "name": "File Upload Service", + "description": "File Upload Service", + "type": "Traditional", + "redirect_uri": "http://localhost:3008/callback", + "logout_redirect_uri": "http://localhost:3008", + "secret": "upload_app_local_sercret", + "id": "5vnf94y2lr17s2u9htkmq", + "is_third_party": false + }, + { + "name": "M2M Profile Reader", + "description": "Machine 2 Machine application used to communicate between services and Profile resource", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "profile_reader_local_secret", + "id": "ddl4llp30risjwcjymqw3", + "is_third_party": false + }, + { + "name": "M2M Scheduler Writer", + "description": "Machine 2 Machine application used to communicate between services and Scheduler resource", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "scheduler_writer_local_secret", + "id": "bjd8knt92hxslvrfyezp7", + "is_third_party": false + }, + { + "name": "M2M E2E Tester", + "description": "Machine 2 Machine application used to do E2E testing", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "e2e_tester_local_secret", + "id": "qrtllp45fgbvsdjyasd5", + "is_third_party": false + }, + { + "name": "M2M Management APIs", + "description": "Machine 2 Machine application used to communicate with the Logto Management APIs", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "m2m_management_api_local_secret", + "id": "46ewhh940rn1e29cmecxs", + "is_third_party": false, + "apply_management_api_role": true + }, + { + "name": "M2M Analytics API", + "description": "Machine 2 Machine application used to communicate with the Analytics APIs", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "m2m_analytics_api_local_secret", + "id": "03emabp0dwznxw414mxme", + "is_third_party": false } ], "resources": [ @@ -94,6 +311,26 @@ "id": "messaging-api", "name": "Messaging Building Block API", "indicator": "http://localhost:8002/" + }, + { + "id": "scheduler-api", + "name": "Scheduler Building Block API", + "indicator": "http://localhost:8005/" + }, + { + "id": "profile-api", + "name": "Profile Building Block API", + "indicator": "http://localhost:8003/" + }, + { + "id": "upload-api", + "name": "File Upload Service API", + "indicator": "http://localhost:8008/" + }, + { + "id": "analytics-api", + "name": "Analytics Building Block API", + "indicator": "http://localhost:8075/" } ], "resource_permissions": [ @@ -113,6 +350,34 @@ "messaging:citizen.self:read", "messaging:citizen.self:write" ] + }, + { + "resource_id": "scheduler-api", + "specific_permissions": ["scheduler:jobs:write"] + }, + { + "resource_id": "profile-api", + "specific_permissions": [ + "profile:user.self:read", + "profile:user.self:write", + "profile:address.self:read", + "profile:address.self:write", + "profile:entitlement.self:read", + "profile:entitlement.self:write", + "profile:user:read" + ] + }, + { + "resource_id": "upload-api", + "specific_permissions": ["upload:file.self:write", "upload:file.self:read"] + }, + { + "resource_id": "analytics-api", + "specific_permissions": [ + "analytics:website:read", + "analytics:website:write", + "analytics:website:tracking" + ] } ], "resource_roles": [ @@ -137,14 +402,59 @@ "messaging:citizen.self:read", "messaging:citizen.self:write" ] + }, + { + "resource_id": "profile-api", + "specific_permissions": [ + "profile:user.self:read", + "profile:user.self:write", + "profile:address.self:read", + "profile:address.self:write", + "profile:entitlement.self:read", + "profile:entitlement.self:write" + ] + }, + { + "resource_id": "upload-api", + "specific_permissions": ["upload:file.self:read"] } ] + }, + { + "id": "m2m-citizen-profile", + "name": "M2M Citizen Profile Reader role", + "description": "Role used to make M2M applications able to read from profile resource", + "permissions": [ + { + "resource_id": "profile-api", + "specific_permissions": ["profile:user:read"] + } + ], + "type": "MachineToMachine", + "related_application_ids": ["ddl4llp30risjwcjymqw3"] + }, + { + "id": "m2m-e2e-messaging", + "name": "M2M E2E Messaging Citizen", + "description": "Role used to make M2M applications able to login as messaging citizen to perform E2E testing", + "permissions": [ + { + "resource_id": "messaging-api", + "specific_permissions": [ + "messaging:message.self:read", + "messaging:citizen.self:read", + "messaging:citizen.self:write" + ] + } + ], + "type": "MachineToMachine", + "related_application_ids": ["qrtllp45fgbvsdjyasd5"] } ], "connectors": [ { "id": "mygovid", - "sync_profile": false, + "sync_profile": true, "connector_id": "mygovid", "config": { "scope": "openid profile email", @@ -165,6 +475,20 @@ }, "target": "MyGovId (MyGovId connector)" } + }, + { + "id": "ogcio-entraid", + "sync_profile": true, + "connector_id": "ogcio-entraid", + "config": { + "clientId": "mock-client-id", + "tenantId": "organizations", + "clientSecret": "mock-client-secret", + "cloudInstance": "https://login.microsoftonline.com" + }, + "metadata": { + "target": "OGCIO EntraID" + } } ], "sign_in_experiences": [ @@ -191,7 +515,7 @@ "password": false, "identifiers": [] }, - "social_sign_in_connector_targets": ["MyGovId (MyGovId connector)"], + "social_sign_in_connector_targets": ["MyGovId (MyGovId connector)", "OGCIO EntraID"], "sign_in_mode": "SignInAndRegister" } ], @@ -211,6 +535,18 @@ "signing_key": "webhooks_local_signing_key", "enabled": true } + ], + "users": [ + { + "id": "e2e-user-1", + "username": "e2e_test_user_1", + "primary_email": "e2e_test_1@user.com", + "primary_phone": "+21312666999", + "name": "E2E First User", + "application_id": "4695d8onfb9f3bv18phtq", + "resource_role_ids": ["bb-citizen"], + "ppsn": "E2E_TEST_USER_1" + } ] } } diff --git a/packages/cli/src/commands/database/ogcio/ogcio-seeder-testing.json b/packages/cli/src/commands/database/ogcio/ogcio-seeder-testing.json new file mode 100644 index 00000000000..5626792d352 --- /dev/null +++ b/packages/cli/src/commands/database/ogcio/ogcio-seeder-testing.json @@ -0,0 +1,478 @@ +{ + "default": { + "organizations": [ + { + "name": "OGCIO", + "description": "OGCIO Organization", + "id": "ogcio" + }, + { + "name": "Inactive Public Servants Org", + "description": "Temporary organization for inactive Public Servants", + "id": "inactive-ps-org" + }, + { + "name": "First Testing Organisation", + "description": "Organisation used to do E2E testing", + "id": "first-testing" + }, + { + "name": "Second Testing Organisation", + "description": "Organisation used to do E2E testing", + "id": "second-testing" + } + ], + "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", + "profile:user:read", + "profile:user:*", + "profile:address:*", + "profile:entitlement:*", + "life-events:digital-wallet-flow:*", + "bb:public-servant.inactive:*", + "scheduler:jobs:write", + "upload:file:*" + ] + }, + "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:*", + "scheduler:jobs:write" + ] + }, + { + "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", + "scheduler:jobs:write" + ] + }, + { + "id": "le-public-servant", + "name": "Life Events Public Servant", + "description": "Life Events Public servant", + "specific_permissions": ["life-events:digital-wallet-flow:*"] + }, + { + "id": "pro-public-servant", + "name": "Profile Public Servant", + "description": "Profile Public servant", + "specific_permissions": ["profile:user:*", "profile:address:*", "profile:entitlement:*"] + }, + { + "id": "upload-public-servant", + "name": "File Upload Public Servant", + "description": "File Upload Public servant", + "specific_permissions": ["upload:file:*", "profile:user:read"] + }, + { + "id": "bb-inactive-ps", + "name": "Inactive Public Servant", + "description": "Inactive Public Servant", + "specific_permissions": ["bb:public-servant.inactive:*"] + }, + { + "id": "m2m-ps-profile-reader", + "name": "M2M Public Servant Profile Reader", + "description": "Role for M2M Applications that need to read from Profile resources", + "specific_permissions": ["profile:user:read"], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "tty6llp30risjwcjhbvc9", "organization_id": "ogcio" }, + { "application_id": "tty6llp30risjwcjhbvc9", "organization_id": "first-testing" }, + { "application_id": "tty6llp30risjwcjhbvc9", "organization_id": "second-testing" } + ] + }, + { + "id": "m2m-ps-sched-writer", + "name": "M2M Public Servant Scheduler Writer", + "description": "Role for M2M Applications that need to schedule tasks using Scheduler resources", + "specific_permissions": ["scheduler:jobs:write"], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "f7p2k8w5jv4c1rm6xn3q9", "organization_id": "ogcio" }, + { "application_id": "f7p2k8w5jv4c1rm6xn3q9", "organization_id": "first-testing" }, + { "application_id": "f7p2k8w5jv4c1rm6xn3q9", "organization_id": "second-testing" } + ] + }, + { + "id": "m2m-e2e-tester", + "name": "M2M E2E Tester Role", + "description": "Role for M2M Applications that need to perform E2E testing", + "specific_permissions": [ + "messaging:message:*", + "messaging:provider:*", + "messaging:template:*", + "messaging:citizen:*", + "messaging:event:read", + "profile:user:read", + "upload:file:*" + ], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "treftr21fgbvsdjwlol9", "organization_id": "first-testing" }, + { "application_id": "treftr21fgbvsdjwlol9", "organization_id": "second-testing" } + ] + } + ], + "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 + }, + { + "name": "Home Building Block", + "description": "Building Blocks home application", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "4icd76x8we31j1e8bqyfn", + "is_third_party": false + }, + { + "name": "File Upload Service", + "description": "File Upload Service", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "8mdj23ty5vk94w7rblxhf", + "is_third_party": false + }, + { + "name": "Profile Building Block", + "description": "Profile App of Life Events", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "0921d8onfb9f3bv75trgf", + "is_third_party": false + }, + { + "name": "M2M Profile Reader", + "description": "Machine 2 Machine application used to communicate between services and Profile resource", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "tty6llp30risjwcjhbvc9", + "is_third_party": false + }, + { + "name": "M2M Scheduler Writer", + "description": "Machine 2 Machine application used to communicate between services and Scheduler resource", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "f7p2k8w5jv4c1rm6xn3q9", + "is_third_party": false + }, + { + "name": "M2M E2E Tester", + "description": "Machine 2 Machine application used to do E2E testing", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "treftr21fgbvsdjwlol9", + "is_third_party": false + }, + { + "name": "M2M Management APIs", + "description": "Machine 2 Machine application used to communicate with the Logto Management APIs", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "46ewhh940rn1e29cmecxs", + "is_third_party": false, + "apply_management_api_role": true + } + ], + "resources": [ + { + "id": "payments-api", + "name": "Payments Building Block API", + "indicator": "" + }, + { + "id": "messaging-api", + "name": "Messaging Building Block API", + "indicator": "" + }, + { + "id": "scheduler-api", + "name": "Scheduler Building Block API", + "indicator": "" + }, + { + "id": "profile-api", + "name": "Profile Building Block API", + "indicator": "" + }, + { + "id": "upload-api", + "name": "Upload 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_id": "scheduler-api", + "specific_permissions": ["scheduler:jobs:write"] + }, + { + "resource_id": "profile-api", + "specific_permissions": [ + "profile:user.self:read", + "profile:user.self:write", + "profile:address.self:read", + "profile:address.self:write", + "profile:entitlement.self:read", + "profile:entitlement.self:write", + "profile:user:read" + ] + }, + { + "resource_id": "upload-api", + "specific_permissions": ["upload:file.self:write", "upload:file.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", + "messaging:citizen.self:read", + "messaging:citizen.self:write" + ] + }, + { + "resource_id": "profile-api", + "specific_permissions": [ + "profile:user.self:read", + "profile:user.self:write", + "profile:address.self:read", + "profile:address.self:write", + "profile:entitlement.self:read", + "profile:entitlement.self:write" + ] + }, + { + "resource_id": "upload-api", + "specific_permissions": ["upload:file.self:read"] + } + ] + }, + { + "id": "m2m-citizen-profile", + "name": "M2M Citizen Profile Reader role", + "description": "Role used to make M2M applications able to read from profile resource", + "permissions": [ + { + "resource_id": "profile-api", + "specific_permissions": ["profile:user:read"] + } + ], + "type": "MachineToMachine", + "related_application_ids": ["tty6llp30risjwcjhbvc9"] + }, + { + "id": "m2m-e2e-messaging", + "name": "M2M E2E Messaging Citizen", + "description": "Role used to make M2M applications able to login as messaging citizen to perform E2E testing", + "permissions": [ + { + "resource_id": "messaging-api", + "specific_permissions": [ + "messaging:message.self:read", + "messaging:citizen.self:read", + "messaging:citizen.self:write" + ] + } + ], + "type": "MachineToMachine", + "related_application_ids": ["treftr21fgbvsdjwlol9"] + } + ], + "connectors": [ + { + "id": "mygovid", + "sync_profile": true, + "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)" + } + }, + { + "id": "ogcio-entraid", + "sync_profile": true, + "connector_id": "ogcio-entraid", + "config": { + "clientId": "", + "tenantId": "organizations", + "clientSecret": "", + "cloudInstance": "https://login.microsoftonline.com" + }, + "metadata": { + "target": "OGCIO EntraID" + } + } + ], + "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)", "OGCIO EntraID"], + "sign_in_mode": "SignInAndRegister" + } + ], + "webhooks": [ + { + "id": "login-webhook", + "name": "User log in", + "events": [ + "User.Created", + "User.Deleted", + "User.Data.Updated", + "User.SuspensionStatus.Updated" + ], + "config": { + "url": "" + }, + "signing_key": "", + "enabled": true + } + ], + "users": [ + { + "id": "e2e-user-1", + "username": "e2e_test_user_1", + "primary_email": "e2e_test_1@user.com", + "primary_phone": "+21312666999", + "name": "E2E First User", + "application_id": "1lvmteh2ao3xrswyq7j3e", + "resource_role_ids": ["bb-citizen"], + "ppsn": "E2E_TEST_USER_1" + } + ] + } +} diff --git a/packages/cli/src/commands/database/ogcio/ogcio-seeder.json b/packages/cli/src/commands/database/ogcio/ogcio-seeder.json index bc2316ccce6..56a9f5d3022 100644 --- a/packages/cli/src/commands/database/ogcio/ogcio-seeder.json +++ b/packages/cli/src/commands/database/ogcio/ogcio-seeder.json @@ -5,10 +5,22 @@ "name": "OGCIO", "description": "OGCIO Organization", "id": "ogcio" + }, + { + "name": "Inactive Public Servants Org", + "description": "Temporary organization for inactive Public Servants", + "id": "inactive-ps-org" } ], "organization_permissions": { "specific_permissions": [ + "analytics:global:*", + "analytics:measurable:admin", + "analytics:measurable:write", + "analytics:measurable:view", + "analytics:website:read", + "analytics:website:write", + "analytics:website:tracking", "payments:provider:*", "payments:payment_request:*", "payments:payment_request.public:read", @@ -18,7 +30,14 @@ "messaging:template:*", "messaging:citizen:*", "messaging:event:read", - "life-events:digital-wallet-flow:*" + "profile:user:read", + "profile:user:*", + "profile:address:*", + "profile:entitlement:*", + "life-events:digital-wallet-flow:*", + "bb:public-servant.inactive:*", + "scheduler:jobs:write", + "upload:file:*" ] }, "organization_roles": [ @@ -30,7 +49,8 @@ "payments:provider:*", "payments:payment_request:*", "payments:payment_request.public:read", - "payments:transaction:*" + "payments:transaction:*", + "scheduler:jobs:write" ] }, { @@ -42,7 +62,8 @@ "messaging:provider:*", "messaging:template:*", "messaging:citizen:*", - "messaging:event:read" + "messaging:event:read", + "scheduler:jobs:write" ] }, { @@ -50,6 +71,76 @@ "name": "Life Events Public Servant", "description": "Life Events Public servant", "specific_permissions": ["life-events:digital-wallet-flow:*"] + }, + { + "id": "pro-public-servant", + "name": "Profile Public Servant", + "description": "Profile Public servant", + "specific_permissions": ["profile:user:*", "profile:address:*", "profile:entitlement:*"] + }, + { + "id": "anl-admin", + "name": "Analytics Admin", + "description": "Analytics Admin", + "specific_permissions": ["analytics:measurable:admin"] + }, + { + "id": "anl-public-servant", + "name": "Analytics Public Servant", + "description": "Analytics Public servant", + "specific_permissions": ["analytics:measurable:view"] + }, + { + "id": "anl-super-user", + "name": "Analytics Super User", + "description": "Analytics Super User", + "specific_permissions": ["analytics:global:*"] + }, + { + "id": "upload-public-servant", + "name": "File Upload Public Servant", + "description": "File Upload Public servant", + "specific_permissions": ["upload:file:*", "profile:user:read"] + }, + { + "id": "bb-inactive-ps", + "name": "Inactive Public Servant", + "description": "Inactive Public Servant", + "specific_permissions": ["bb:public-servant.inactive:*"] + }, + { + "id": "m2m-ps-profile-reader", + "name": "M2M Public Servant Profile Reader", + "description": "Role for M2M Applications that need to read from Profile resources", + "specific_permissions": ["profile:user:read"], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "tty6llp30risjwcjhbvc9", "organization_id": "ogcio" } + ] + }, + { + "id": "m2m-ps-sched-writer", + "name": "M2M Public Servant Scheduler Writer", + "description": "Role for M2M Applications that need to schedule tasks using Scheduler resources", + "specific_permissions": ["scheduler:jobs:write"], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "d5q7l2m9r3w1x6bp8cv0j", "organization_id": "ogcio" } + ] + }, + { + "id": "m2m-analytics-sdk", + "name": "M2M Analytics SDK Role", + "description": "Role for M2M Applications that need to read from Analytics resources", + "specific_permissions": [ + "analytics:website:read", + "analytics:website:write", + "analytics:website:tracking" + ], + "type": "MachineToMachine", + "related_applications": [ + { "application_id": "03emabp0dwznxw414mxme", "organization_id": "ogcio" } + ] } ], "applications": [ @@ -82,6 +173,88 @@ "secret": "", "id": "i61nya0wctzpqeyeno54z", "is_third_party": false + }, + { + "name": "Analytics Building Block", + "description": "Shared platform for Analytics", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "kgz1zomlw27cxx4pxh5gi", + "is_third_party": false, + "always_issue_refresh_token": true + }, + { + "name": "Home Building Block", + "description": "Building Blocks home application", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "4icd76x8we31j1e8bqyfn", + "is_third_party": false + }, + { + "name": "File Upload Service", + "description": "File Upload Service", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "8mdj23ty5vk94w7rblxhf", + "is_third_party": false + }, + { + "name": "Profile Building Block", + "description": "Profile App of Life Events", + "type": "Traditional", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "0921d8onfb9f3bv75trgf", + "is_third_party": false + }, + { + "name": "M2M Profile Reader", + "description": "Machine 2 Machine application used to communicate between services and Profile resource", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "tty6llp30risjwcjhbvc9", + "is_third_party": false + }, + { + "name": "M2M Scheduler Writer", + "description": "Machine 2 Machine application used to communicate between services and Scheduler resource", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "d5q7l2m9r3w1x6bp8cv0j", + "is_third_party": false + }, + { + "name": "M2M Management APIs", + "description": "Machine 2 Machine application used to communicate with the Logto Management APIs", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "46ewhh940rn1e29cmecxs", + "is_third_party": false, + "apply_management_api_role": true + }, + { + "name": "M2M Analytics API", + "description": "Machine 2 Machine application used to communicate with the Analytics APIs", + "type": "MachineToMachine", + "redirect_uri": "", + "logout_redirect_uri": "", + "secret": "", + "id": "03emabp0dwznxw414mxme", + "is_third_party": false } ], "resources": [ @@ -94,6 +267,26 @@ "id": "messaging-api", "name": "Messaging Building Block API", "indicator": "" + }, + { + "id": "scheduler-api", + "name": "Scheduler Building Block API", + "indicator": "" + }, + { + "id": "profile-api", + "name": "Profile Building Block API", + "indicator": "" + }, + { + "id": "upload-api", + "name": "Upload Building Block API", + "indicator": "" + }, + { + "id": "analytics-api", + "name": "Analytics Building Block API", + "indicator": "" } ], "resource_permissions": [ @@ -113,6 +306,34 @@ "messaging:citizen.self:read", "messaging:citizen.self:write" ] + }, + { + "resource_id": "scheduler-api", + "specific_permissions": ["scheduler:jobs:write"] + }, + { + "resource_id": "profile-api", + "specific_permissions": [ + "profile:user.self:read", + "profile:user.self:write", + "profile:address.self:read", + "profile:address.self:write", + "profile:entitlement.self:read", + "profile:entitlement.self:write", + "profile:user:read" + ] + }, + { + "resource_id": "upload-api", + "specific_permissions": ["upload:file.self:write", "upload:file.self:read"] + }, + { + "resource_id": "analytics-api", + "specific_permissions": [ + "analytics:website:read", + "analytics:website:write", + "analytics:website:tracking" + ] } ], "resource_roles": [ @@ -137,14 +358,42 @@ "messaging:citizen.self:read", "messaging:citizen.self:write" ] + }, + { + "resource_id": "profile-api", + "specific_permissions": [ + "profile:user.self:read", + "profile:user.self:write", + "profile:address.self:read", + "profile:address.self:write", + "profile:entitlement.self:read", + "profile:entitlement.self:write" + ] + }, + { + "resource_id": "upload-api", + "specific_permissions": ["upload:file.self:read"] } ] + }, + { + "id": "m2m-citizen-profile", + "name": "M2M Citizen Profile Reader role", + "description": "Role used to make M2M applications able to read from profile resource", + "permissions": [ + { + "resource_id": "profile-api", + "specific_permissions": ["profile:user:read"] + } + ], + "type": "MachineToMachine", + "related_application_ids": ["tty6llp30risjwcjhbvc9"] } ], "connectors": [ { "id": "mygovid", - "sync_profile": false, + "sync_profile": true, "connector_id": "mygovid", "config": { "scope": "openid profile email", @@ -165,6 +414,20 @@ }, "target": "MyGovId (MyGovId connector)" } + }, + { + "id": "ogcio-entraid", + "sync_profile": true, + "connector_id": "ogcio-entraid", + "config": { + "clientId": "", + "tenantId": "organizations", + "clientSecret": "", + "cloudInstance": "https://login.microsoftonline.com" + }, + "metadata": { + "target": "OGCIO EntraID" + } } ], "sign_in_experiences": [ @@ -191,7 +454,7 @@ "password": false, "identifiers": [] }, - "social_sign_in_connector_targets": ["MyGovId (MyGovId connector)"], + "social_sign_in_connector_targets": ["MyGovId (MyGovId connector)", "OGCIO EntraID"], "sign_in_mode": "SignInAndRegister" } ], diff --git a/packages/cli/src/commands/database/ogcio/ogcio-seeder.ts b/packages/cli/src/commands/database/ogcio/ogcio-seeder.ts index 15d42d58c26..13f40b62c0f 100644 --- a/packages/cli/src/commands/database/ogcio/ogcio-seeder.ts +++ b/packages/cli/src/commands/database/ogcio/ogcio-seeder.ts @@ -1,6 +1,8 @@ /* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable @silverhand/fp/no-mutation */ /* eslint-disable @silverhand/fp/no-let */ + +export type OgcioApplicationTypes = 'User' | 'MachineToMachine'; export type OgcioTenantSeeder = Record; export type OgcioSeeder = { @@ -14,6 +16,7 @@ export type OgcioSeeder = { sign_in_experiences?: SignInExperienceSeeder[]; resource_permissions?: ResourcePermissionSeeder[]; resource_roles?: ResourceRoleSeeder[]; + users?: UserSeeder[]; }; export type OrganizationSeeder = { @@ -31,6 +34,8 @@ export type OrganizationRoleSeeder = { name: string; specific_permissions: string[]; description: string; + type?: OgcioApplicationTypes; + related_applications?: Array<{ application_id: string; organization_id: string }>; }; export type ApplicationSeeder = { @@ -42,6 +47,8 @@ export type ApplicationSeeder = { logout_redirect_uri: string | string[]; secret: string; is_third_party?: boolean; + always_issue_refresh_token?: boolean; + apply_management_api_role?: boolean; }; export type ResourceSeeder = { @@ -110,6 +117,8 @@ export type ResourceRoleSeeder = { name: string; description: string; permissions: ScopePerResourceRoleSeeder[]; + type?: OgcioApplicationTypes; + related_application_ids?: string[]; }; export type ScopePerResourceRoleSeeder = { @@ -126,7 +135,18 @@ export type WebhookSeeder = { url: string; }; signing_key: string; - enabled: true; + enabled: boolean; +}; + +export type UserSeeder = { + id: string; + username: string; + primary_email: string; + primary_phone?: string; + name: string; + application_id: string; + resource_role_ids: string[]; + ppsn: string; }; let inputSeeder: OgcioTenantSeeder | undefined; diff --git a/packages/cli/src/commands/database/ogcio/ogcio.ts b/packages/cli/src/commands/database/ogcio/ogcio.ts index 4871c85c359..ee40c63cc5e 100644 --- a/packages/cli/src/commands/database/ogcio/ogcio.ts +++ b/packages/cli/src/commands/database/ogcio/ogcio.ts @@ -1,4 +1,6 @@ /* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable @silverhand/fp/no-let */ +/* eslint-disable @silverhand/fp/no-mutation */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @silverhand/fp/no-mutating-methods */ @@ -12,7 +14,10 @@ import { createOrganizations } from './organizations.js'; import { seedResourceRbacData } from './resources-rbac.js'; import { seedResources } from './resources.js'; import { seedSignInExperiences } from './sign-in-experiences.js'; -import { seedWebhooks } from './webhooks.js'; +import { seedUsers } from './users.js'; +import { type SeedingWebhook, seedWebhooks } from './webhooks.js'; + +const WEBHOOK_ID_FOR_USERS = 'login-webhook'; const createDataForTenant = async ( transaction: DatabaseTransactionConnection, @@ -27,12 +32,6 @@ const createDataForTenant = async ( }); } - await seedOrganizationRbacData({ - transaction, - tenantId, - toSeed: tenantData, - }); - if (tenantData.applications?.length) { const applications = await seedApplications({ transaction, @@ -41,6 +40,12 @@ const createDataForTenant = async ( }); } + await seedOrganizationRbacData({ + transaction, + tenantId, + toSeed: tenantData, + }); + if (tenantData.resources?.length) { const resources = await seedResources({ transaction, @@ -71,14 +76,24 @@ const createDataForTenant = async ( experiences: tenantData.sign_in_experiences, }); } - + let webhooks: SeedingWebhook[] = []; if (tenantData.webhooks?.length) { - const webhooks = await seedWebhooks({ + webhooks = await seedWebhooks({ transaction, tenantId, hooks: tenantData.webhooks, }); } + + if (tenantData.users?.length) { + const webhookToUse = webhooks.find((web) => web.id === WEBHOOK_ID_FOR_USERS && web.enabled); + const users = await seedUsers({ + transaction, + tenantId, + usersToSeed: tenantData.users, + webhook: webhookToUse, + }); + } }; const transactionMethod = async (transaction: DatabaseTransactionConnection) => { @@ -89,8 +104,6 @@ const transactionMethod = async (transaction: DatabaseTransactionConnection) => } await Promise.all(items); - - // Const resourcesRbac = await seedResourceRbacData(transaction, defaultTenantId, resources); }; export const seedOgcio = async (connection: CommonQueryMethods) => { diff --git a/packages/cli/src/commands/database/ogcio/organizations-rbac.ts b/packages/cli/src/commands/database/ogcio/organizations-rbac.ts index 270caf3a18e..ffd1f67df0e 100644 --- a/packages/cli/src/commands/database/ogcio/organizations-rbac.ts +++ b/packages/cli/src/commands/database/ogcio/organizations-rbac.ts @@ -1,10 +1,12 @@ /* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ - +/* eslint-disable @silverhand/fp/no-mutating-methods */ /* eslint-disable @silverhand/fp/no-mutation */ /* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ import { + OrganizationApplicationRelations, + OrganizationRoleApplicationRelations, OrganizationRoles, OrganizationRoleScopeRelations, OrganizationScopes, @@ -81,6 +83,7 @@ const createRole = async (params: { name: string; description: string; id: string; + type: string; }; }) => { await createOrUpdateItem({ @@ -92,6 +95,7 @@ const createRole = async (params: { id: params.roleToSeed.id, name: params.roleToSeed.name, description: params.roleToSeed.description, + type: params.roleToSeed.type, }, tableName: OrganizationRoles.table, }); @@ -110,6 +114,8 @@ const createRoles = async (params: { name: role.name, description: role.description, scopes: role.specific_permissions, + type: role.type ?? 'User', + related_applications: role.related_applications ?? [], })); const queries = rolesToCreate.map(async (role) => @@ -204,5 +210,79 @@ export const seedOrganizationRbacData = async (params: { }); await createRelations(params.transaction, params.tenantId, createdScopes, createdRoles); + + await assignOrganizationsToM2MApplications(params.transaction, params.tenantId, createdRoles); + } +}; + +const assignOrganizationsToM2MApplications = async ( + transaction: DatabaseTransactionConnection, + tenantId: string, + roles: Array<{ + id: string; + related_applications: Array<{ application_id: string; organization_id: string }>; + type: string; + }> +) => { + const addedRoles: Array< + Promise<{ organization_role_id: string; application_id: string; organization_id: string }> + > = []; + for (const role of roles) { + if (role.type === 'MachineToMachine' && role.related_applications.length > 0) { + addedRoles.push( + ...role.related_applications.map( + async (relation: { application_id: string; organization_id: string }) => + assignOrganizationToM2MApplication(transaction, tenantId, { + organization_role_id: role.id, + application_id: relation.application_id, + organization_id: relation.organization_id, + }) + ) + ); + } } + + await Promise.all(addedRoles); +}; + +const assignOrganizationToM2MApplication = async ( + transaction: DatabaseTransactionConnection, + tenantId: string, + relation: { + organization_role_id: string; + application_id: string; + organization_id: string; + } +) => { + await createOrUpdateItemWithoutId({ + transaction, + tableName: OrganizationApplicationRelations.table, + tenantId, + toLogFieldName: 'organization_id', + whereClauses: [ + sql`tenant_id = ${tenantId}`, + sql`application_id = ${relation.application_id}`, + sql`organization_id = ${relation.organization_id}`, + ], + toInsert: { + organization_id: relation.organization_id, + application_id: relation.application_id, + }, + columnToGet: 'organization_id', + }); + + return createOrUpdateItemWithoutId({ + transaction, + tableName: OrganizationRoleApplicationRelations.table, + tenantId, + toLogFieldName: 'organization_role_id', + whereClauses: [ + sql`tenant_id = ${tenantId}`, + sql`organization_role_id = ${relation.organization_role_id}`, + sql`application_id = ${relation.application_id}`, + sql`organization_id = ${relation.organization_id}`, + ], + toInsert: relation, + columnToGet: 'organization_role_id', + }); }; diff --git a/packages/cli/src/commands/database/ogcio/queries.ts b/packages/cli/src/commands/database/ogcio/queries.ts index d2498d004fc..27f0651f5f9 100644 --- a/packages/cli/src/commands/database/ogcio/queries.ts +++ b/packages/cli/src/commands/database/ogcio/queries.ts @@ -3,6 +3,8 @@ /* eslint-disable @silverhand/fp/no-mutating-methods */ /* eslint-disable @silverhand/fp/no-mutation */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ + +import { Roles } from '@logto/schemas'; import { generateStandardId } from '@logto/shared'; import { type DatabaseTransactionConnection, @@ -201,3 +203,11 @@ export const deleteQuery = (whereClauses: ValueExpression[], table: string) => { where ${sql.join(whereClauses, sql` AND `)} `; }; + +export const findManagementApiRole = async (transaction: DatabaseTransactionConnection) => { + const role = await transaction.query>(sql` + select id from ${sql.identifier([Roles.table])} + where name='Logto Management API access' limit 1`); + + return getColumnValueByQueryResult(role, 'id'); +}; diff --git a/packages/cli/src/commands/database/ogcio/resources-rbac.ts b/packages/cli/src/commands/database/ogcio/resources-rbac.ts index 5e3f704567d..7ae0321757f 100644 --- a/packages/cli/src/commands/database/ogcio/resources-rbac.ts +++ b/packages/cli/src/commands/database/ogcio/resources-rbac.ts @@ -6,7 +6,7 @@ /* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ -import { Roles, RolesScopes, Scopes } from '@logto/schemas'; +import { ApplicationsRoles, Roles, RolesScopes, Scopes } from '@logto/schemas'; import { sql, type DatabaseTransactionConnection } from '@silverhand/slonik'; import { @@ -14,7 +14,7 @@ import { type ResourcePermissionSeeder, type ScopePerResourceRoleSeeder, } from './ogcio-seeder.js'; -import { createOrUpdateItem, deleteQuery } from './queries.js'; +import { createOrUpdateItem, deleteQuery, findManagementApiRole } from './queries.js'; import { type SeedingResource } from './resources.js'; type SeedingScope = { @@ -105,6 +105,8 @@ const createRole = async (params: { name: string; description: string; id: string; + type: string; + related_application_ids: string[]; }; }) => { await createOrUpdateItem({ @@ -116,6 +118,7 @@ const createRole = async (params: { id: params.roleToSeed.id, name: params.roleToSeed.name, description: params.roleToSeed.description, + type: params.roleToSeed.type, }, tableName: Roles.table, }); @@ -134,6 +137,8 @@ const createRoles = async (params: { name: role.name, description: role.description, scopes: role.permissions, + type: role.type ?? 'User', + related_application_ids: role.related_application_ids ?? [], })); const queries = rolesToCreate.map(async (role) => @@ -202,7 +207,10 @@ const createRelations = async (params: { export const cleanScopes = async (transaction: DatabaseTransactionConnection, tenantId: string) => { await cleanScopeRelations(transaction, tenantId); - const deleteQueryString = deleteQuery([sql`tenant_id = ${tenantId}`], Scopes.table); + const deleteQueryString = deleteQuery( + [sql`tenant_id = ${tenantId}`, sql`resource_id <> 'management-api'`], + Scopes.table + ); return transaction.query(deleteQueryString); }; @@ -210,7 +218,10 @@ export const cleanScopeRelations = async ( transaction: DatabaseTransactionConnection, tenantId: string ) => { - const deleteQueryString = deleteQuery([sql`tenant_id = ${tenantId}`], RolesScopes.table); + const deleteQueryString = deleteQuery( + [sql`tenant_id = ${tenantId}`, sql`scope_id <> 'management-api-all'`], + RolesScopes.table + ); return transaction.query(deleteQueryString); }; @@ -245,5 +256,66 @@ export const seedResourceRbacData = async (params: { roles: createdRoles, scopes: createdScopes, }); + + await assignRolesToM2MApplications(params.transaction, params.tenantId, createdRoles); + } +}; + +const assignRolesToM2MApplications = async ( + transaction: DatabaseTransactionConnection, + tenantId: string, + roles: Array<{ id: string; related_application_ids: string[]; type: string }> +) => { + const addedRoles: Array> = []; + for (const role of roles) { + if (role.type === 'MachineToMachine' && role.related_application_ids.length > 0) { + addedRoles.push( + ...role.related_application_ids.map(async (appId: string) => + assignRoleToM2MApplication(transaction, tenantId, { + role_id: role.id, + application_id: appId, + }) + ) + ); + } } + + await Promise.all(addedRoles); +}; + +const assignRoleToM2MApplication = async ( + transaction: DatabaseTransactionConnection, + tenantId: string, + relation: { + role_id: string; + application_id: string; + } +) => + createOrUpdateItem({ + transaction, + tableName: ApplicationsRoles.table, + tenantId, + toLogFieldName: 'role_id', + whereClauses: [ + sql`tenant_id = ${tenantId}`, + sql`role_id = ${relation.role_id}`, + sql`application_id = ${relation.application_id}`, + ], + toInsert: relation, + }); + +export const applyManagementApiRole = async ( + transaction: DatabaseTransactionConnection, + tenantId: string, + appId: string +) => { + const roleId = await findManagementApiRole(transaction); + if (!roleId) { + throw new Error("Cannot find 'Logto Management API access' role"); + } + + return assignRoleToM2MApplication(transaction, tenantId, { + role_id: roleId, + application_id: appId, + }); }; diff --git a/packages/cli/src/commands/database/ogcio/users.ts b/packages/cli/src/commands/database/ogcio/users.ts new file mode 100644 index 00000000000..6b6bde0b16a --- /dev/null +++ b/packages/cli/src/commands/database/ogcio/users.ts @@ -0,0 +1,160 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ +/* eslint-disable @silverhand/fp/no-mutating-methods */ + +import { createHmac } from 'node:crypto'; + +import { Users, UsersRoles } from '@logto/schemas'; +import { conditional } from '@silverhand/essentials'; +import { sql, type DatabaseTransactionConnection } from '@silverhand/slonik'; +import { got, type RequestError } from 'got'; + +import { consoleLog } from '../../../utils.js'; + +import { type UserSeeder } from './ogcio-seeder.js'; +import { createOrUpdateItem } from './queries.js'; +import { type SeedingWebhook } from './webhooks.js'; + +const MYGOVID_IDENTITY = 'MyGovId (MyGovId connector)'; + +export const seedUsers = async (params: { + transaction: DatabaseTransactionConnection; + tenantId: string; + usersToSeed: UserSeeder[]; + webhook?: SeedingWebhook; +}) => { + if (params.usersToSeed.length === 0) { + return {}; + } + + const queries: Array> = []; + + for (const user of params.usersToSeed) { + queries.push(createUser({ ...params, userToSeed: user })); + } + + await Promise.all(queries); + + return params.usersToSeed; +}; + +const createUser = async (params: { + transaction: DatabaseTransactionConnection; + tenantId: string; + userToSeed: UserSeeder; + webhook?: SeedingWebhook; +}): Promise => { + await createOrUpdateItem({ + transaction: params.transaction, + tenantId: params.tenantId, + toLogFieldName: 'id', + whereClauses: [sql`tenant_id = ${params.tenantId}`, sql`id = ${params.userToSeed.id}`], + toInsert: { + id: params.userToSeed.id, + username: params.userToSeed.username, + primary_email: params.userToSeed.primary_email, + primary_phone: params.userToSeed.primary_phone ?? undefined, + name: params.userToSeed.name, + application_id: params.userToSeed.application_id, + }, + tableName: Users.table, + }); + + const assignRoleQueries = []; + for (const roleId of params.userToSeed.resource_role_ids) { + assignRoleQueries.push( + createOrUpdateItem({ + transaction: params.transaction, + tenantId: params.tenantId, + toLogFieldName: 'role_id', + whereClauses: [ + sql`tenant_id = ${params.tenantId}`, + sql`user_id = ${params.userToSeed.id}`, + sql`role_id = ${roleId}`, + ], + toInsert: { + user_id: params.userToSeed.id, + role_id: roleId, + }, + tableName: UsersRoles.table, + }) + ); + } + + await Promise.all(assignRoleQueries); + + if (params.webhook) { + consoleLog.info(`Invoking webhook for user. User id: ${params.userToSeed.id}`); + try { + await sendWebhookRequest({ seededUser: params.userToSeed, webhook: params.webhook }); + consoleLog.info(`Webhook invoked for user. User id: ${params.userToSeed.id}`); + } catch (error) { + consoleLog.error( + 'Error invoking webhook. Please ensure profile-api service is available and reachable' + ); + if (isRequestError(error)) { + consoleLog.error(error.code); + } else { + consoleLog.error(error); + } + } + } +}; + +const isRequestError = (input: unknown): input is RequestError => { + return input instanceof Error && 'input' in input && 'code' in input && 'stack' in input; +}; + +export const sendWebhookRequest = async (params: { + seededUser: UserSeeder; + webhook: SeedingWebhook; +}) => { + // eslint-disable-next-line no-restricted-syntax + const config = JSON.parse(params.webhook.config) as { url: string }; + const payload = buildWebhookPayload(params.seededUser); + return got.post(config.url, { + headers: { + 'user-agent': 'Logto (https://logto.io/)', + ...conditional( + params.webhook.signing_key && { + 'logto-signature-sha-256': sign(params.webhook.signing_key, payload), + } + ), + }, + json: payload, + retry: { limit: 3 }, + }); +}; + +const buildWebhookPayload = (user: UserSeeder) => { + const names = user.name.split(' '); + const lastName = names.pop(); + const firstName = names.join(' '); + + return { + event: 'User.Created', + data: { + id: user.id, + identities: { + [MYGOVID_IDENTITY]: { + details: { + rawData: { + firstName, + lastName, + PublicServiceNumber: user.ppsn, + BirthDate: undefined, + }, + email: user.primary_email, + phone: user.primary_phone, + }, + }, + }, + }, + }; +}; + +const sign = (signingKey: string, payload: Record) => { + const hmac = createHmac('sha256', signingKey); + const payloadString = JSON.stringify(payload); + hmac.update(payloadString); + return hmac.digest('hex'); +}; diff --git a/packages/cli/src/commands/database/ogcio/webhooks.ts b/packages/cli/src/commands/database/ogcio/webhooks.ts index 096fef61f39..b01968de0d8 100644 --- a/packages/cli/src/commands/database/ogcio/webhooks.ts +++ b/packages/cli/src/commands/database/ogcio/webhooks.ts @@ -7,14 +7,14 @@ import { sql, type DatabaseTransactionConnection } from '@silverhand/slonik'; import { type WebhookSeeder } from './ogcio-seeder.js'; import { createOrUpdateItemWithoutId } from './queries.js'; -type SeedingWebhook = { +export type SeedingWebhook = { tenant_id: string; id: string; name: string; events: string; config: string; signing_key: string; - enabled: true; + enabled: boolean; }; const createWebhook = async ( diff --git a/packages/cli/src/commands/proxy/index.ts b/packages/cli/src/commands/proxy/index.ts new file mode 100644 index 00000000000..a12428b3ef3 --- /dev/null +++ b/packages/cli/src/commands/proxy/index.ts @@ -0,0 +1,106 @@ +import http from 'node:http'; + +import { isValidUrl } from '@logto/core-kit'; +import { conditional } from '@silverhand/essentials'; +import chalk from 'chalk'; +import type { CommandModule } from 'yargs'; + +import { consoleLog } from '../../utils.js'; + +import { type ProxyCommandArgs } from './types.js'; +import { + checkExperienceInput, + createLogtoResponseHandler, + createProxy, + createStaticFileProxy, + isLogtoRequestPath, +} from './utils.js'; + +const proxy: CommandModule = { + command: ['proxy'], + describe: 'Command for Logto proxy', + builder: (yargs) => + yargs + .options({ + 'experience-uri': { + alias: ['x'], + describe: 'The URI of your custom sign-in experience page.', + type: 'string', + }, + 'experience-path': { + alias: ['xp'], + describe: 'The local folder path of your custom sign-in experience assets.', + type: 'string', + }, + endpoint: { + alias: 'ep', + describe: + 'Logto endpoint URI, which can be found in Logto Console. E.g.: https://.logto.app/', + type: 'string', + }, + port: { + alias: 'p', + describe: 'The port number where the proxy server will be running on. Defaults to 9000.', + type: 'number', + default: 9000, + }, + verbose: { + alias: 'v', + describe: 'Show verbose output.', + type: 'boolean', + default: false, + }, + }) + .global('e'), + handler: async ({ 'experience-uri': url, 'experience-path': path, endpoint, port, verbose }) => { + checkExperienceInput(url, path); + + if (!endpoint || !isValidUrl(endpoint)) { + consoleLog.fatal('A valid Logto endpoint URI must be provided.'); + } + const logtoEndpointUrl = new URL(endpoint); + const proxyUrl = new URL(`http://localhost:${port}`); + + const proxyLogtoRequest = createProxy( + logtoEndpointUrl.href, + async (proxyResponse, request, response) => + createLogtoResponseHandler({ + proxyResponse, + request, + response, + logtoEndpointUrl, + proxyUrl, + verbose, + }) + ); + const proxyExperienceServerRequest = conditional(url && createProxy(url)); + const proxyExperienceStaticFileRequest = conditional(path && createStaticFileProxy(path)); + + const server = http.createServer((request, response) => { + if (verbose) { + consoleLog.info(`Incoming request: ${chalk.blue(request.method, request.url)}`); + } + + // Proxy the requests to Logto endpoint + if (isLogtoRequestPath(request.url)) { + void proxyLogtoRequest(request, response); + return; + } + + if (proxyExperienceServerRequest) { + void proxyExperienceServerRequest(request, response); + return; + } + + if (proxyExperienceStaticFileRequest) { + void proxyExperienceStaticFileRequest(request, response); + } + }); + + server.listen(port, () => { + consoleLog.info(`Proxy server is running on ${chalk.blue(proxyUrl.href)}`); + }); + }, +}; + +export default proxy; diff --git a/packages/cli/src/commands/proxy/types.ts b/packages/cli/src/commands/proxy/types.ts new file mode 100644 index 00000000000..af80b479254 --- /dev/null +++ b/packages/cli/src/commands/proxy/types.ts @@ -0,0 +1,18 @@ +import type * as http from 'node:http'; + +export type ProxyCommandArgs = { + 'experience-uri'?: string; + 'experience-path'?: string; + endpoint?: string; + port: number; + verbose: boolean; +}; + +export type ProxyResponseHandler = { + proxyResponse: http.IncomingMessage; + request: http.IncomingMessage; + response: http.ServerResponse; + logtoEndpointUrl: URL; + proxyUrl: URL; + verbose: boolean; +}; diff --git a/packages/cli/src/commands/proxy/utils.ts b/packages/cli/src/commands/proxy/utils.ts new file mode 100644 index 00000000000..28283be84c3 --- /dev/null +++ b/packages/cli/src/commands/proxy/utils.ts @@ -0,0 +1,157 @@ +import { existsSync } from 'node:fs'; +import fs from 'node:fs/promises'; +import type http from 'node:http'; +import path from 'node:path'; + +import { isValidUrl } from '@logto/core-kit'; +import { conditional, trySafe } from '@silverhand/essentials'; +import chalk from 'chalk'; +import { createProxyMiddleware, responseInterceptor } from 'http-proxy-middleware'; +import { type OnProxyEvent } from 'http-proxy-middleware/dist/types.js'; +import mime from 'mime'; + +import { consoleLog } from '../../utils.js'; + +import { type ProxyResponseHandler } from './types.js'; + +export const createProxy = (targetUrl: string, onProxyResponse?: OnProxyEvent['proxyRes']) => { + const hasResponseHandler = Boolean(onProxyResponse); + return createProxyMiddleware({ + target: targetUrl, + changeOrigin: true, + selfHandleResponse: hasResponseHandler, + ...conditional( + hasResponseHandler && { + on: { + proxyRes: onProxyResponse, + error: (error) => { + consoleLog.error(chalk.red(error)); + }, + }, + } + ), + }); +}; + +const index = 'index.html'; +const indexContentType = 'text/html; charset=utf-8'; +const noCache = 'no-cache, no-store, must-revalidate'; +const maxAgeSevenDays = 'max-age=604_800_000'; + +export const createStaticFileProxy = + (staticPath: string) => async (request: http.IncomingMessage, response: http.ServerResponse) => { + if (!request.url) { + response.writeHead(400).end(); + return; + } + + if (request.method === 'HEAD' || request.method === 'GET') { + const fallBackToIndex = !isFileAssetPath(request.url); + const requestPath = path.join(staticPath, fallBackToIndex ? index : request.url); + try { + const content = await fs.readFile(requestPath, 'utf8'); + response.setHeader('cache-control', fallBackToIndex ? noCache : maxAgeSevenDays); + response.setHeader('content-type', getMimeType(request.url)); + response.writeHead(200); + response.end(content); + } catch (error: unknown) { + consoleLog.error(chalk.red(error)); + response.setHeader('content-type', getMimeType(request.url)); + response.writeHead(existsSync(request.url) ? 500 : 404); + response.end(); + } + } + }; + +/** + * Intercept the response from Logto endpoint and replace Logto endpoint URLs in the response with the + * proxy URL. The string replace happens in the following cases: + * - The response is a redirect response, and the `location` property in response header may contain Logto + * endpoint URI. + * - The response body is JSON, which consists of properties such as `**_endpoint` and `redirectTo`. These + * properties may contain Logto endpoint URI. + * - The response is HTML content that contains a form. The form action URL may contain Logto endpoint URI. + * + * Note: the `issuer` and `jwks_uri` properties in the `/oidc/.well-known` response should not be replaced, + * even they also contain the Logto endpoint URI. + */ +export const createLogtoResponseHandler = async ({ + proxyResponse, + request, + response, + logtoEndpointUrl, + proxyUrl, + verbose, +}: ProxyResponseHandler) => { + const { location } = proxyResponse.headers; + if (location) { + // eslint-disable-next-line @silverhand/fp/no-mutation + proxyResponse.headers.location = location.replace(logtoEndpointUrl.href, proxyUrl.href); + } + + void responseInterceptor(async (responseBuffer, proxyResponse) => { + const responseBody = responseBuffer.toString(); + if (verbose) { + consoleLog.info(`Response received: ${chalk.green(responseBody)}`); + } + + if (proxyResponse.headers['content-type']?.includes('text/html')) { + return responseBody.replace(`action="${logtoEndpointUrl.href}`, `action="${proxyUrl.href}`); + } + + if (proxyResponse.headers['content-type']?.includes('application/json')) { + const jsonData = trySafe(() => JSON.parse(responseBody)); + + if (jsonData && typeof jsonData === 'object') { + const updatedEntries: Array<[string, unknown]> = Object.entries(jsonData).map( + ([key, value]) => { + if ((key === 'redirectTo' || key.endsWith('_endpoint')) && typeof value === 'string') { + return [key, value.replace(logtoEndpointUrl.href, proxyUrl.href)]; + } + return [key, value]; + } + ); + + return JSON.stringify(Object.fromEntries(updatedEntries)); + } + } + return responseBody; + })(proxyResponse, request, response); +}; + +export const checkExperienceInput = (url?: string, staticPath?: string) => { + if (staticPath && url) { + consoleLog.fatal('Only one of the experience URI or path can be provided.'); + } + if (!staticPath && !url) { + consoleLog.fatal('Either a sign-in experience URI or local path must be provided.'); + } + if (url && !isValidUrl(url)) { + consoleLog.fatal( + 'A valid sign-in experience URI must be provided. E.g.: http://localhost:4000' + ); + } + if (staticPath && !existsSync(path.join(staticPath, index))) { + consoleLog.fatal('The provided path does not contain a valid index.html file.'); + } +}; + +/** + * Check if the request path is a Logto request path. + * @example isLogtoRequestPath('/oidc/.well-known/openid-configuration') // true + * @example isLogtoRequestPath('/oidc/auth') // true + * @example isLogtoRequestPath('/api/interaction/submit') // true + * @example isLogtoRequestPath('/consent') // true + */ +export const isLogtoRequestPath = (requestPath?: string) => + ['/oidc/', '/api/'].some((path) => requestPath?.startsWith(path)) || requestPath === '/consent'; + +const isFileAssetPath = (url: string) => url.split('/').at(-1)?.includes('.'); + +const getMimeType = (requestPath: string) => { + const fallBackToIndex = !isFileAssetPath(requestPath); + if (fallBackToIndex) { + return indexContentType; + } + return mime.getType(requestPath) ?? 'application/octet-stream'; +}; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 13de2f79785..7afd7c9ec79 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -6,6 +6,7 @@ import { hideBin } from 'yargs/helpers'; import connector from './commands/connector/index.js'; import database from './commands/database/index.js'; import install from './commands/install/index.js'; +import proxy from './commands/proxy/index.js'; import translate from './commands/translate/index.js'; import { packageJson } from './package-json.js'; import { cliConfig, ConfigKey, consoleLog } from './utils.js'; @@ -48,6 +49,7 @@ void yargs(hideBin(process.argv)) .command(database) .command(connector) .command(translate) + .command(proxy) .demandCommand(1) .showHelpOnFail(false, `Specify ${chalk.green('--help')} for available options`) .strict() diff --git a/packages/connectors/.gitignore b/packages/connectors/.gitignore index 65245801929..533db69aa78 100644 --- a/packages/connectors/.gitignore +++ b/packages/connectors/.gitignore @@ -1,7 +1,6 @@ # generated files /*/tsconfig.* -/*/rollup.config.* -/*/vitest.config.* +/*/*.config.* # keep templates !/templates/** diff --git a/packages/connectors/connector-alipay-native/CHANGELOG.md b/packages/connectors/connector-alipay-native/CHANGELOG.md index f1876d9f116..827cf921a07 100644 --- a/packages/connectors/connector-alipay-native/CHANGELOG.md +++ b/packages/connectors/connector-alipay-native/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-alipay-native +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-alipay-native/package.json b/packages/connectors/connector-alipay-native/package.json index 7099bda0ce0..f9693c27791 100644 --- a/packages/connectors/connector-alipay-native/package.json +++ b/packages/connectors/connector-alipay-native/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-alipay-native", - "version": "1.2.1", + "version": "1.3.0", "description": "Alipay Native implementation.", "author": "Silverhand Inc. ", "dependencies": { @@ -9,29 +9,24 @@ "dayjs": "^1.10.5", "got": "^14.0.0", "iconv-lite": "^0.6.3", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "devDependencies": { - "@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", "@shopify/jest-koa-mocks": "^5.0.0", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -46,9 +41,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-alipay-web/CHANGELOG.md b/packages/connectors/connector-alipay-web/CHANGELOG.md index 5ffe877b82d..afa9b87c27b 100644 --- a/packages/connectors/connector-alipay-web/CHANGELOG.md +++ b/packages/connectors/connector-alipay-web/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-alipay-web +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-alipay-web/package.json b/packages/connectors/connector-alipay-web/package.json index 10b5095b6b4..c567bb78973 100644 --- a/packages/connectors/connector-alipay-web/package.json +++ b/packages/connectors/connector-alipay-web/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-alipay-web", - "version": "1.3.1", + "version": "1.4.0", "description": "Alipay implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", @@ -8,29 +8,24 @@ "dayjs": "^1.10.5", "got": "^14.0.0", "iconv-lite": "^0.6.3", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "devDependencies": { - "@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", "@shopify/jest-koa-mocks": "^5.0.0", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -45,9 +40,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-aliyun-dm/CHANGELOG.md b/packages/connectors/connector-aliyun-dm/CHANGELOG.md index 194f3b8132c..cf64e5b303d 100644 --- a/packages/connectors/connector-aliyun-dm/CHANGELOG.md +++ b/packages/connectors/connector-aliyun-dm/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-aliyun-dm +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-aliyun-dm/package.json b/packages/connectors/connector-aliyun-dm/package.json index 59e41ddd8e8..6e2d9901923 100644 --- a/packages/connectors/connector-aliyun-dm/package.json +++ b/packages/connectors/connector-aliyun-dm/package.json @@ -1,13 +1,13 @@ { "name": "@logto/connector-aliyun-dm", - "version": "1.1.2", + "version": "1.2.0", "description": "Aliyun DM connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -22,9 +22,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -51,23 +51,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-aliyun-dm/src/single-send-mail.test.ts b/packages/connectors/connector-aliyun-dm/src/single-send-mail.test.ts index 1162b5d3210..cf4f13e33db 100644 --- a/packages/connectors/connector-aliyun-dm/src/single-send-mail.test.ts +++ b/packages/connectors/connector-aliyun-dm/src/single-send-mail.test.ts @@ -21,7 +21,6 @@ describe('singleSendMail', () => { }, '' ); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const calledData = request.mock.calls[0]; expect(calledData).not.toBeUndefined(); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment diff --git a/packages/connectors/connector-aliyun-dm/src/utils.test.ts b/packages/connectors/connector-aliyun-dm/src/utils.test.ts index 24da02e7a88..ac40456fe08 100644 --- a/packages/connectors/connector-aliyun-dm/src/utils.test.ts +++ b/packages/connectors/connector-aliyun-dm/src/utils.test.ts @@ -24,7 +24,6 @@ describe('request', () => { it('should call got.post with extended params', async () => { const parameters = mockedParameters; await request('http://test.endpoint.com', parameters, 'testsecret'); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const calledData = post.mock.calls[0]; expect(calledData).not.toBeUndefined(); const payload = calledData?.[0].form as Record; diff --git a/packages/connectors/connector-aliyun-sms/CHANGELOG.md b/packages/connectors/connector-aliyun-sms/CHANGELOG.md index dc502f55bae..d8cf480ef0d 100644 --- a/packages/connectors/connector-aliyun-sms/CHANGELOG.md +++ b/packages/connectors/connector-aliyun-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-aliyun-sms +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-aliyun-sms/package.json b/packages/connectors/connector-aliyun-sms/package.json index 9739f43eb75..bf66618112f 100644 --- a/packages/connectors/connector-aliyun-sms/package.json +++ b/packages/connectors/connector-aliyun-sms/package.json @@ -1,13 +1,13 @@ { "name": "@logto/connector-aliyun-sms", - "version": "1.1.2", + "version": "1.2.0", "description": "Aliyun SMS connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -22,9 +22,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -51,23 +51,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-aliyun-sms/src/single-send-text.test.ts b/packages/connectors/connector-aliyun-sms/src/single-send-text.test.ts index ec7da921964..142628c87c6 100644 --- a/packages/connectors/connector-aliyun-sms/src/single-send-text.test.ts +++ b/packages/connectors/connector-aliyun-sms/src/single-send-text.test.ts @@ -20,7 +20,6 @@ describe('sendSms', () => { }, '' ); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const calledData = request.mock.calls[0]; expect(calledData).not.toBeUndefined(); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment diff --git a/packages/connectors/connector-aliyun-sms/src/utils.test.ts b/packages/connectors/connector-aliyun-sms/src/utils.test.ts index 24da02e7a88..ac40456fe08 100644 --- a/packages/connectors/connector-aliyun-sms/src/utils.test.ts +++ b/packages/connectors/connector-aliyun-sms/src/utils.test.ts @@ -24,7 +24,6 @@ describe('request', () => { it('should call got.post with extended params', async () => { const parameters = mockedParameters; await request('http://test.endpoint.com', parameters, 'testsecret'); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const calledData = post.mock.calls[0]; expect(calledData).not.toBeUndefined(); const payload = calledData?.[0].form as Record; diff --git a/packages/connectors/connector-apple/CHANGELOG.md b/packages/connectors/connector-apple/CHANGELOG.md index 1e5544aa92e..3b7a9534c53 100644 --- a/packages/connectors/connector-apple/CHANGELOG.md +++ b/packages/connectors/connector-apple/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-apple +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-apple/package.json b/packages/connectors/connector-apple/package.json index 2c11467106b..3ddbbe02385 100644 --- a/packages/connectors/connector-apple/package.json +++ b/packages/connectors/connector-apple/package.json @@ -1,15 +1,15 @@ { "name": "@logto/connector-apple", - "version": "1.3.1", + "version": "1.4.0", "description": "Apple web connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@logto/shared": "workspace:^3.1.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "jose": "^5.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "jose": "^5.6.3", + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -24,9 +24,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -53,23 +53,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-aws-ses/CHANGELOG.md b/packages/connectors/connector-aws-ses/CHANGELOG.md index c1be1d0c438..eaa603c95b1 100644 --- a/packages/connectors/connector-aws-ses/CHANGELOG.md +++ b/packages/connectors/connector-aws-ses/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-aws-ses +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-aws-ses/package.json b/packages/connectors/connector-aws-ses/package.json index 99f6582e302..9c9f32c0cb6 100644 --- a/packages/connectors/connector-aws-ses/package.json +++ b/packages/connectors/connector-aws-ses/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-aws-ses", - "version": "1.1.2", + "version": "1.2.0", "description": "Logto Connector for Amazon SES", "author": "Jeff ", "dependencies": { @@ -9,8 +9,8 @@ "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -25,9 +25,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -54,23 +54,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-azuread/CHANGELOG.md b/packages/connectors/connector-azuread/CHANGELOG.md index 6fd68879451..67bbedd0686 100644 --- a/packages/connectors/connector-azuread/CHANGELOG.md +++ b/packages/connectors/connector-azuread/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-azuread +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.0 ### Minor Changes diff --git a/packages/connectors/connector-azuread/README.md b/packages/connectors/connector-azuread/README.md index 6bb274fa36b..b38cac16525 100644 --- a/packages/connectors/connector-azuread/README.md +++ b/packages/connectors/connector-azuread/README.md @@ -66,29 +66,6 @@ The `prompts` field is an array of strings that specifies the type of user inter Logto will concatenate the prompts with a space as the value of `prompt` in the authorization URL. -### 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**. - ## References -- [Web app that signs in users](https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-sign-user-overview) +- [Web app that signs in users](https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-sign-user-overview) \ No newline at end of file diff --git a/packages/connectors/connector-azuread/package.json b/packages/connectors/connector-azuread/package.json index 773b2da57be..d95be30e90e 100644 --- a/packages/connectors/connector-azuread/package.json +++ b/packages/connectors/connector-azuread/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-azuread", - "version": "1.3.0", + "version": "1.4.0", "description": "Microsoft Azure AD connector implementation.", "author": "Mobilist Inc. ", "dependencies": { @@ -8,8 +8,8 @@ "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -24,9 +24,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -53,23 +53,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-dingtalk-web/CHANGELOG.md b/packages/connectors/connector-dingtalk-web/CHANGELOG.md index 61d3dd9d940..e00eb0f6673 100644 --- a/packages/connectors/connector-dingtalk-web/CHANGELOG.md +++ b/packages/connectors/connector-dingtalk-web/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-dingtalk-web +## 0.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 0.1.1 ### Patch Changes diff --git a/packages/connectors/connector-dingtalk-web/package.json b/packages/connectors/connector-dingtalk-web/package.json index d2815d24c29..b099ca2d0c5 100644 --- a/packages/connectors/connector-dingtalk-web/package.json +++ b/packages/connectors/connector-dingtalk-web/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-dingtalk-web", - "version": "0.1.1", + "version": "0.2.0", "description": "Dingtalk web connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", @@ -8,29 +8,24 @@ "dayjs": "^1.10.5", "got": "^14.0.0", "iconv-lite": "^0.6.3", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "devDependencies": { - "@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", "@shopify/jest-koa-mocks": "^5.0.0", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -45,9 +40,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-discord/CHANGELOG.md b/packages/connectors/connector-discord/CHANGELOG.md index a321f0c4a49..f102353eefa 100644 --- a/packages/connectors/connector-discord/CHANGELOG.md +++ b/packages/connectors/connector-discord/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-discord +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-discord/package.json b/packages/connectors/connector-discord/package.json index eef708ea75b..0b7c856ddaf 100644 --- a/packages/connectors/connector-discord/package.json +++ b/packages/connectors/connector-discord/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-discord", - "version": "1.3.1", + "version": "1.4.0", "description": "Discord connector implementation.", "author": "ZR3SYSTEMS. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-facebook/CHANGELOG.md b/packages/connectors/connector-facebook/CHANGELOG.md index 094ebede4a5..90a43257788 100644 --- a/packages/connectors/connector-facebook/CHANGELOG.md +++ b/packages/connectors/connector-facebook/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-facebook +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-facebook/package.json b/packages/connectors/connector-facebook/package.json index 787adb612c6..823c7644e73 100644 --- a/packages/connectors/connector-facebook/package.json +++ b/packages/connectors/connector-facebook/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-facebook", - "version": "1.3.1", + "version": "1.4.0", "description": "Facebook web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-feishu-web/CHANGELOG.md b/packages/connectors/connector-feishu-web/CHANGELOG.md index 3d0f5dbfbf8..caeb8d362d9 100644 --- a/packages/connectors/connector-feishu-web/CHANGELOG.md +++ b/packages/connectors/connector-feishu-web/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-feishu-web +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-feishu-web/package.json b/packages/connectors/connector-feishu-web/package.json index b6db69458b6..77f01bd255b 100644 --- a/packages/connectors/connector-feishu-web/package.json +++ b/packages/connectors/connector-feishu-web/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-feishu-web", - "version": "1.2.1", + "version": "1.3.0", "description": "Feishu web connector.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-github/CHANGELOG.md b/packages/connectors/connector-github/CHANGELOG.md index 3070f3df691..05ad1af36e9 100644 --- a/packages/connectors/connector-github/CHANGELOG.md +++ b/packages/connectors/connector-github/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-github +## 1.5.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.4.2 ### Patch Changes diff --git a/packages/connectors/connector-github/package.json b/packages/connectors/connector-github/package.json index 1e9eeb2a4e9..4df0c375e89 100644 --- a/packages/connectors/connector-github/package.json +++ b/packages/connectors/connector-github/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-github", - "version": "1.4.2", + "version": "1.5.0", "description": "Github web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { @@ -8,8 +8,8 @@ "@silverhand/essentials": "^2.9.1", "ky": "^1.2.3", "query-string": "^9.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -24,9 +24,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -53,23 +53,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.7", + "nock": "14.0.0-beta.9", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-google/CHANGELOG.md b/packages/connectors/connector-google/CHANGELOG.md index 1525e3dea4a..81ff42db4f6 100644 --- a/packages/connectors/connector-google/CHANGELOG.md +++ b/packages/connectors/connector-google/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-google +## 1.5.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.4.0 ### Minor Changes diff --git a/packages/connectors/connector-google/package.json b/packages/connectors/connector-google/package.json index 6f5c8da1591..fa995562f88 100644 --- a/packages/connectors/connector-google/package.json +++ b/packages/connectors/connector-google/package.json @@ -1,15 +1,15 @@ { "name": "@logto/connector-google", - "version": "1.4.0", + "version": "1.5.0", "description": "Google web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@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" + "jose": "^5.6.3", + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -24,9 +24,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -53,23 +53,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-huggingface/CHANGELOG.md b/packages/connectors/connector-huggingface/CHANGELOG.md index 9a4e9d322cd..20c3e36594d 100644 --- a/packages/connectors/connector-huggingface/CHANGELOG.md +++ b/packages/connectors/connector-huggingface/CHANGELOG.md @@ -1,5 +1,20 @@ # @logto/connector-huggingface +## 0.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + +### Patch Changes + +- Updated dependencies [510f681fa] + - @logto/connector-oauth@1.4.0 + ## 0.1.1 ### Patch Changes diff --git a/packages/connectors/connector-huggingface/package.json b/packages/connectors/connector-huggingface/package.json index b87196f02f6..5cd070f240c 100644 --- a/packages/connectors/connector-huggingface/package.json +++ b/packages/connectors/connector-huggingface/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-huggingface", - "version": "0.1.1", + "version": "0.2.0", "description": "Hugging Face connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", - "@logto/connector-oauth": "workspace:^1.3.1", + "@logto/connector-oauth": "workspace:^1.4.0", "@silverhand/essentials": "^2.9.1", "ky": "^1.2.3", - "zod": "^3.22.4" + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.7", + "nock": "14.0.0-beta.9", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-kakao/CHANGELOG.md b/packages/connectors/connector-kakao/CHANGELOG.md index 813a9f758ef..15c2b083ef6 100644 --- a/packages/connectors/connector-kakao/CHANGELOG.md +++ b/packages/connectors/connector-kakao/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-kakao +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-kakao/package.json b/packages/connectors/connector-kakao/package.json index 900f095e6c5..3c0091e4ea2 100644 --- a/packages/connectors/connector-kakao/package.json +++ b/packages/connectors/connector-kakao/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-kakao", - "version": "1.2.1", + "version": "1.3.0", "description": "Kakao connector implementation.", "author": "Kyungyoon Kim. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-logto-email/CHANGELOG.md b/packages/connectors/connector-logto-email/CHANGELOG.md index 2a7dbe906db..727a8e00f84 100644 --- a/packages/connectors/connector-logto-email/CHANGELOG.md +++ b/packages/connectors/connector-logto-email/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-logto-email +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-logto-email/package.json b/packages/connectors/connector-logto-email/package.json index d209481cfcc..ff9df9cd506 100644 --- a/packages/connectors/connector-logto-email/package.json +++ b/packages/connectors/connector-logto-email/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-logto-email", - "version": "1.1.2", + "version": "1.2.0", "description": "Logto email connector.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,24 +52,19 @@ "access": "public" }, "devDependencies": { - "@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", + "@logto/cloud": "0.2.5-3b703da", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-logto-email/src/constant.ts b/packages/connectors/connector-logto-email/src/constant.ts index 82a6157580d..a9f94c13e8f 100644 --- a/packages/connectors/connector-logto-email/src/constant.ts +++ b/packages/connectors/connector-logto-email/src/constant.ts @@ -89,7 +89,7 @@ export const defaultMetadata: ConnectorMetadata = { }, { key: 'appLogo', - label: 'App Logo', + label: 'Email logo', type: ConnectorConfigFormItemType.Text, }, ], diff --git a/packages/connectors/connector-logto-sms/CHANGELOG.md b/packages/connectors/connector-logto-sms/CHANGELOG.md index 08b1d95aa47..79ef1f9cb8d 100644 --- a/packages/connectors/connector-logto-sms/CHANGELOG.md +++ b/packages/connectors/connector-logto-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-logto-sms +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-logto-sms/package.json b/packages/connectors/connector-logto-sms/package.json index 00fa94c4f50..7ad0abc5343 100644 --- a/packages/connectors/connector-logto-sms/package.json +++ b/packages/connectors/connector-logto-sms/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-logto-sms", - "version": "1.1.2", + "version": "1.2.0", "description": "Logto SMS connector.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-logto-social-demo/CHANGELOG.md b/packages/connectors/connector-logto-social-demo/CHANGELOG.md index e641551e64a..4b77f04322b 100644 --- a/packages/connectors/connector-logto-social-demo/CHANGELOG.md +++ b/packages/connectors/connector-logto-social-demo/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-logto-social-demo +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-logto-social-demo/package.json b/packages/connectors/connector-logto-social-demo/package.json index 0e765182fbf..7ec2d21aaad 100644 --- a/packages/connectors/connector-logto-social-demo/package.json +++ b/packages/connectors/connector-logto-social-demo/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-logto-social-demo", - "version": "1.1.2", + "version": "1.2.0", "description": "OAuth standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mailgun/CHANGELOG.md b/packages/connectors/connector-mailgun/CHANGELOG.md index 47a8407f9dc..f97a2c5e96a 100644 --- a/packages/connectors/connector-mailgun/CHANGELOG.md +++ b/packages/connectors/connector-mailgun/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mailgun +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.2 ### Patch Changes diff --git a/packages/connectors/connector-mailgun/package.json b/packages/connectors/connector-mailgun/package.json index 07cacc5bdb8..77fd114c99b 100644 --- a/packages/connectors/connector-mailgun/package.json +++ b/packages/connectors/connector-mailgun/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-mailgun", - "version": "1.2.2", + "version": "1.3.0", "description": "Mailgun connector for Logto.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-email-alternative/CHANGELOG.md b/packages/connectors/connector-mock-email-alternative/CHANGELOG.md index c324ffcb4bc..e731249f313 100644 --- a/packages/connectors/connector-mock-email-alternative/CHANGELOG.md +++ b/packages/connectors/connector-mock-email-alternative/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mock-standard-email +## 2.1.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 2.0.2 ### Patch Changes diff --git a/packages/connectors/connector-mock-email-alternative/package.json b/packages/connectors/connector-mock-email-alternative/package.json index 9132e8f8530..123de9a5780 100644 --- a/packages/connectors/connector-mock-email-alternative/package.json +++ b/packages/connectors/connector-mock-email-alternative/package.json @@ -1,20 +1,20 @@ { "name": "@logto/connector-mock-standard-email", - "version": "2.0.2", + "version": "2.1.0", "description": "Mock Standard Email Service connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-email/CHANGELOG.md b/packages/connectors/connector-mock-email/CHANGELOG.md index 9f0a121575b..ef4375a6f7c 100644 --- a/packages/connectors/connector-mock-email/CHANGELOG.md +++ b/packages/connectors/connector-mock-email/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mock-email +## 2.1.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 2.0.2 ### Patch Changes diff --git a/packages/connectors/connector-mock-email/package.json b/packages/connectors/connector-mock-email/package.json index f1b4024346a..9f746fe443e 100644 --- a/packages/connectors/connector-mock-email/package.json +++ b/packages/connectors/connector-mock-email/package.json @@ -1,20 +1,20 @@ { "name": "@logto/connector-mock-email", - "version": "2.0.2", + "version": "2.1.0", "description": "Mock Email Service connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-sms/CHANGELOG.md b/packages/connectors/connector-mock-sms/CHANGELOG.md index db5aedaeb47..6167208e2ab 100644 --- a/packages/connectors/connector-mock-sms/CHANGELOG.md +++ b/packages/connectors/connector-mock-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mock-sms +## 2.1.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 2.0.2 ### Patch Changes diff --git a/packages/connectors/connector-mock-sms/package.json b/packages/connectors/connector-mock-sms/package.json index 1ae46035a40..fcc57351ddf 100644 --- a/packages/connectors/connector-mock-sms/package.json +++ b/packages/connectors/connector-mock-sms/package.json @@ -1,20 +1,20 @@ { "name": "@logto/connector-mock-sms", - "version": "2.0.2", + "version": "2.1.0", "description": "Mock SMS connector implementation for integration tests only.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-social/CHANGELOG.md b/packages/connectors/connector-mock-social/CHANGELOG.md index 44cbf2e1725..1e80ef4dabe 100644 --- a/packages/connectors/connector-mock-social/CHANGELOG.md +++ b/packages/connectors/connector-mock-social/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-mock-social +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-mock-social/package.json b/packages/connectors/connector-mock-social/package.json index 902cecb18f7..700247542c4 100644 --- a/packages/connectors/connector-mock-social/package.json +++ b/packages/connectors/connector-mock-social/package.json @@ -1,20 +1,20 @@ { "name": "@logto/connector-mock-social", - "version": "1.2.1", + "version": "1.3.0", "description": "Social mock connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-mock-social/src/index.ts b/packages/connectors/connector-mock-social/src/index.ts index 77c24b645a3..f9751ba22e5 100644 --- a/packages/connectors/connector-mock-social/src/index.ts +++ b/packages/connectors/connector-mock-social/src/index.ts @@ -30,7 +30,7 @@ const getAuthorizationUri: GetAuthorizationUri = async ( } } - return `http://mock.social.com/?state=${state}&redirect_uri=${redirectUri}`; + return `http://mock-social/?state=${state}&redirect_uri=${redirectUri}`; }; const getUserInfo: GetUserInfo = async (data, getSession) => { @@ -39,6 +39,8 @@ const getUserInfo: GetUserInfo = async (data, getSession) => { userId: z.optional(z.string()), email: z.string().optional(), phone: z.string().optional(), + name: z.string().optional(), + avatar: z.string().optional(), }); const result = dataGuard.safeParse(data); diff --git a/packages/connectors/connector-mygovid/package.json b/packages/connectors/connector-mygovid/package.json index 91ccf411e4d..7a086836dbf 100644 --- a/packages/connectors/connector-mygovid/package.json +++ b/packages/connectors/connector-mygovid/package.json @@ -26,9 +26,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-mygovid/src/index.ts b/packages/connectors/connector-mygovid/src/index.ts index 7da016a4707..2db9dd3ffd6 100644 --- a/packages/connectors/connector-mygovid/src/index.ts +++ b/packages/connectors/connector-mygovid/src/index.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line eslint-comments/disable-enable-pair +/* eslint-disable complexity */ import { assert, conditional } from '@silverhand/essentials'; import type { @@ -109,7 +111,16 @@ const getUserInfo = throw new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid, result.error); } - const { sub: id, firstName, lastName, email, mobile, nonce } = result.data; + const { + sub: id, + firstName, + lastName, + email, + mobile, + nonce, + surname, + givenName, + } = result.data; if (nonce) { // TODO @darcy: need to specify error code @@ -130,13 +141,21 @@ const getUserInfo = // TODO: Understand how to fill customData here // await setCustomData({ ...customDataNeededFromResult }) + const toConcatName = firstName && firstName.length > 0 ? firstName : givenName; + const toConcatSurname = lastName && lastName.length > 0 ? lastName : surname; + const concatenated = [toConcatName, toConcatSurname].join(' ').trim(); + const name = concatenated.length > 0 ? concatenated : 'Name not found'; + // MyGovId, if the user has no set phone number + // return +00000000 number, then, if we find it, + // we set it as undefined + const validatedPhone = mobile && /^\+0+$/.exec(mobile) === null ? mobile : undefined; return { id, - name: firstName + ' ' + lastName, + name, avatar: undefined, email: conditional(email), - phone: mobile ?? undefined, // Convert null to undefined + phone: validatedPhone, rawData: jsonGuard.parse(payload), }; } catch (error: unknown) { diff --git a/packages/connectors/connector-mygovid/src/types.ts b/packages/connectors/connector-mygovid/src/types.ts index 0eafdec24a9..2e523e998a8 100644 --- a/packages/connectors/connector-mygovid/src/types.ts +++ b/packages/connectors/connector-mygovid/src/types.ts @@ -25,6 +25,8 @@ export const myGovIdTokenProfileStandardClaimsGuard = z.object({ email: z.string().nullish(), mobile: z.string().nullish(), nonce: z.string().nullish(), + surname: z.string().nullish(), + givenName: z.string().nullish(), }); export const userProfileGuard = z.object({ diff --git a/packages/connectors/connector-naver/CHANGELOG.md b/packages/connectors/connector-naver/CHANGELOG.md index 565c435eb0d..bda3cffdf43 100644 --- a/packages/connectors/connector-naver/CHANGELOG.md +++ b/packages/connectors/connector-naver/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-naver +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-naver/package.json b/packages/connectors/connector-naver/package.json index 22a3e422816..8789957438f 100644 --- a/packages/connectors/connector-naver/package.json +++ b/packages/connectors/connector-naver/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-naver", - "version": "1.2.1", + "version": "1.3.0", "description": "Naver connector implementation.", "author": "Kyungyoon Kim. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-oauth2/CHANGELOG.md b/packages/connectors/connector-oauth2/CHANGELOG.md index 6943b878a57..fa90526996a 100644 --- a/packages/connectors/connector-oauth2/CHANGELOG.md +++ b/packages/connectors/connector-oauth2/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-oauth +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-oauth2/package.json b/packages/connectors/connector-oauth2/package.json index 9fb22112f82..097b6073f97 100644 --- a/packages/connectors/connector-oauth2/package.json +++ b/packages/connectors/connector-oauth2/package.json @@ -1,17 +1,17 @@ { "name": "@logto/connector-oauth", - "version": "1.3.1", + "version": "1.4.0", "description": "OAuth standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", - "jose": "^5.0.0", + "jose": "^5.6.3", "ky": "^1.2.3", "query-string": "^9.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -26,15 +26,16 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup && tsc --declaration --emitDeclarationOnly", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", "test:ci": "pnpm run test --silent --coverage", "prepublishOnly": "pnpm build", - "prepack": "pnpm build" + "prepack": "pnpm build", + "build:test": "pnpm build" }, "engines": { "node": "^20.9.0" @@ -56,23 +57,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.7", + "nock": "14.0.0-beta.9", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-ogcio-entraid/CHANGELOG.md b/packages/connectors/connector-ogcio-entraid/CHANGELOG.md new file mode 100644 index 00000000000..00956e7ea14 --- /dev/null +++ b/packages/connectors/connector-ogcio-entraid/CHANGELOG.md @@ -0,0 +1,3 @@ +# @logto/connector-ogcio-entraid + +## 1.0.0 diff --git a/packages/connectors/connector-ogcio-entraid/README.md b/packages/connectors/connector-ogcio-entraid/README.md new file mode 100644 index 00000000000..34cf6a02840 --- /dev/null +++ b/packages/connectors/connector-ogcio-entraid/README.md @@ -0,0 +1,103 @@ +# OGCIO EntraID connector + +The OGCIO EntraID connector is a fork of the original Microsoft Azure AD connector. + +# Microsoft Azure AD connector + +The Microsoft Azure AD connector provides a succinct way for your application to use Azure’s OAuth 2.0 authentication system. + +**Table of contents** + +- [OGCIO EntraID connector](#ogcio-entraid-connector) +- [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 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) + - [Client ID](#client-id-1) + - [Client Secret](#client-secret-1) + - [Cloud Instance](#cloud-instance-1) + - [Tenant ID](#tenant-id-1) + - [References](#references) + +## Set up Microsoft Azure AD in the Azure Portal + +- Visit the [Azure Portal](https://portal.azure.com/#home) and sign in with your Azure account. You need to have an active subscription to access Microsoft Azure AD. +- Click the **Azure Active Directory** from the services they offer, and click the **App Registrations** from the left menu. +- Click **New Registration** at the top, enter a description, select your **access type** and add your **Redirect URI**, which will 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 also found on the top bar of the Logto Admin Console connector details page) + > You can copy the `Callback URI` in the configuration section. +- Select Web as Platform. + +## Fill in the configuration in Logto + +| 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 + +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**. + +## References + +- [Web app that signs in users](https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-sign-user-overview) diff --git a/packages/connectors/connector-ogcio-entraid/logo.svg b/packages/connectors/connector-ogcio-entraid/logo.svg new file mode 100644 index 00000000000..a047924789f --- /dev/null +++ b/packages/connectors/connector-ogcio-entraid/logo.svg @@ -0,0 +1 @@ + diff --git a/packages/connectors/connector-ogcio-entraid/package.json b/packages/connectors/connector-ogcio-entraid/package.json new file mode 100644 index 00000000000..214f3af96b8 --- /dev/null +++ b/packages/connectors/connector-ogcio-entraid/package.json @@ -0,0 +1,74 @@ +{ + "name": "@logto/connector-ogcio-entraid", + "version": "1.3.0", + "description": "Microsoft EntraID connector implementation.", + "dependencies": { + "@azure/msal-node": "^2.0.0", + "@logto/connector-kit": "workspace:^4.0.0", + "@silverhand/essentials": "^2.9.1", + "got": "^14.0.0", + "snakecase-keys": "^8.0.0", + "zod": "^3.22.4" + }, + "main": "./lib/index.js", + "module": "./lib/index.js", + "exports": "./lib/index.js", + "license": "MPL-2.0", + "type": "module", + "files": [ + "lib", + "docs", + "logo.svg", + "logo-dark.svg" + ], + "scripts": { + "precommit": "lint-staged", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", + "lint": "eslint --ext .ts src", + "lint:report": "pnpm lint --format json --output-file report.json", + "test": "vitest src", + "test:ci": "pnpm run test --silent --coverage", + "prepublishOnly": "pnpm build" + }, + "engines": { + "node": "^20.9.0" + }, + "eslintConfig": { + "extends": "@silverhand", + "settings": { + "import/core-modules": [ + "@silverhand/essentials", + "got", + "nock", + "snakecase-keys", + "zod" + ] + } + }, + "prettier": "@silverhand/eslint-config/.prettierrc", + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@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", + "@silverhand/eslint-config": "6.0.1", + "@silverhand/ts-config": "6.0.0", + "@types/node": "^20.11.20", + "@types/supertest": "^6.0.2", + "@vitest/coverage-v8": "^1.4.0", + "eslint": "^8.56.0", + "lint-staged": "^15.0.2", + "nock": "^13.3.1", + "prettier": "^3.0.0", + "rollup": "^4.12.0", + "rollup-plugin-output-size": "^1.3.0", + "supertest": "^7.0.0", + "typescript": "^5.3.3", + "vitest": "^1.4.0" + } +} diff --git a/packages/connectors/connector-ogcio-entraid/src/constant.ts b/packages/connectors/connector-ogcio-entraid/src/constant.ts new file mode 100644 index 00000000000..2fe0145086b --- /dev/null +++ b/packages/connectors/connector-ogcio-entraid/src/constant.ts @@ -0,0 +1,68 @@ +import type { ConnectorMetadata } 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']; + +export const defaultMetadata: ConnectorMetadata = { + id: 'ogcio-entraid', + target: 'entraid', + platform: ConnectorPlatform.Universal, + name: { + en: 'OGCIO EntraID', + 'zh-CN': 'OGCIO EntraID', + 'tr-TR': 'OGCIO EntraID', + ko: 'OGCIO EntraID', + }, + logo: './logo.svg', + logoDark: null, + description: { + en: 'OGCIO EntraID', + 'zh-CN': 'OGCIO EntraID', + 'tr-TR': 'OGCIO EntraID', + ko: 'OGCIO EntraID', + }, + readme: './README.md', + formItems: [ + { + key: 'clientId', + type: ConnectorConfigFormItemType.Text, + required: true, + label: 'Client ID', + placeholder: '', + }, + { + key: 'clientSecret', + type: ConnectorConfigFormItemType.Text, + required: true, + label: 'Client Secret', + placeholder: '', + }, + { + key: 'cloudInstance', + type: ConnectorConfigFormItemType.Text, + required: true, + label: 'Cloud Instance', + placeholder: 'https://login.microsoftonline.com', + defaultValue: 'https://login.microsoftonline.com', + }, + { + key: 'tenantId', + type: ConnectorConfigFormItemType.Text, + required: true, + label: 'Tenant ID', + placeholder: '', + }, + { + key: 'prompts', + type: ConnectorConfigFormItemType.MultiSelect, + required: false, + label: 'Prompts', + selectItems: Object.values(OidcPrompt).map((prompt) => ({ + value: prompt, + })), + }, + ], +}; + +export const defaultTimeout = 5000; diff --git a/packages/connectors/connector-ogcio-entraid/src/index.test.ts b/packages/connectors/connector-ogcio-entraid/src/index.test.ts new file mode 100644 index 00000000000..1368f85b64b --- /dev/null +++ b/packages/connectors/connector-ogcio-entraid/src/index.test.ts @@ -0,0 +1,76 @@ +import nock from 'nock'; + +import { vi, describe, beforeAll, it, expect } from 'vitest'; + +import { graphAPIEndpoint } from './constant.js'; +import createConnector from './index.js'; + +vi.mock('@azure/msal-node', async () => ({ + ConfidentialClientApplication: class { + async acquireTokenByCode() { + return { + accessToken: 'accessToken', + scopes: ['scopes'], + tokenType: 'tokenType', + }; + } + }, +})); + +const getConnectorConfig = vi.fn().mockResolvedValue({ + clientId: 'clientId', + clientSecret: 'clientSecret', + cloudInstance: 'https://login.microsoftonline.com', + tenantId: 'tenantId', +}); + +describe('Azure AD connector', () => { + it('init without exploding', () => { + expect(async () => createConnector({ getConfig: getConnectorConfig })).not.toThrow(); + }); +}); + +describe('getUserInfo', () => { + beforeAll(async () => { + const graphMeUrl = new URL(graphAPIEndpoint); + nock(graphMeUrl.origin).get(graphMeUrl.pathname).reply(200, { + id: 'id', + displayName: 'displayName', + mail: 'mail', + userPrincipalName: 'userPrincipalName', + }); + }); + + it('should get user info from graph api', async () => { + const connector = await createConnector({ getConfig: getConnectorConfig }); + const userInfo = await connector.getUserInfo( + { code: 'code', redirectUri: 'redirectUri' }, + vi.fn() + ); + expect(userInfo).toEqual({ + id: 'id', + name: 'displayName', + email: 'mail', + rawData: { + id: 'id', + displayName: 'displayName', + mail: 'mail', + userPrincipalName: 'userPrincipalName', + }, + }); + }); + + it('should throw if graph api response has no id', async () => { + const graphMeUrl = new URL(graphAPIEndpoint); + nock(graphMeUrl.origin).get(graphMeUrl.pathname).reply(200, { + displayName: 'displayName', + mail: 'mail', + userPrincipalName: 'userPrincipalName', + }); + + const connector = await createConnector({ getConfig: getConnectorConfig }); + const userInfo = connector.getUserInfo({ code: 'code', redirectUri: 'redirectUri' }, vi.fn()); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + await expect(userInfo).rejects.toThrow(expect.objectContaining({ code: 'invalid_response' })); + }); +}); diff --git a/packages/connectors/connector-ogcio-entraid/src/index.ts b/packages/connectors/connector-ogcio-entraid/src/index.ts new file mode 100644 index 00000000000..d9f1e4ffa5f --- /dev/null +++ b/packages/connectors/connector-ogcio-entraid/src/index.ts @@ -0,0 +1,166 @@ +import { assert, conditional } from '@silverhand/essentials'; +import { got, HTTPError } from 'got'; +import path from 'node:path'; + +import type { AuthorizationCodeRequest, AuthorizationUrlRequest } from '@azure/msal-node'; +import { ConfidentialClientApplication } from '@azure/msal-node'; +import type { + GetAuthorizationUri, + GetUserInfo, + GetConnectorConfig, + CreateConnector, + SocialConnector, +} from '@logto/connector-kit'; +import { + ConnectorError, + ConnectorErrorCodes, + validateConfig, + ConnectorType, + parseJson, +} from '@logto/connector-kit'; + +import { scopes, defaultMetadata, defaultTimeout, graphAPIEndpoint } from './constant.js'; +import type { AzureADConfig } from './types.js'; +import { + azureADConfigGuard, + accessTokenResponseGuard, + userInfoResponseGuard, + authResponseGuard, +} from './types.js'; + +// eslint-disable-next-line @silverhand/fp/no-let +let authCodeRequest: AuthorizationCodeRequest; + +const getAuthorizationUri = + (getConfig: GetConnectorConfig): GetAuthorizationUri => + async ({ state, redirectUri }) => { + const config = await getConfig(defaultMetadata.id); + + validateConfig(config, azureADConfigGuard); + 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({ + auth: { + clientId, + clientSecret, + authority: new URL(path.join(cloudInstance, tenantId)).toString(), + }, + }); + + const authCodeUrlParameters = { + ...defaultAuthCodeUrlParameters, + }; + + const authCodeUrl = await clientApplication.getAuthCodeUrl(authCodeUrlParameters); + + return authCodeUrl; + }; + +const getAccessToken = async (config: AzureADConfig, code: string, redirectUri: string) => { + const codeRequest: AuthorizationCodeRequest = { + ...authCodeRequest, + redirectUri, + scopes: ['User.Read'], + code, + }; + + const { clientId, clientSecret, cloudInstance, tenantId } = config; + + const clientApplication = new ConfidentialClientApplication({ + auth: { + clientId, + clientSecret, + authority: new URL(path.join(cloudInstance, tenantId)).toString(), + }, + }); + + const authResult = await clientApplication.acquireTokenByCode(codeRequest); + const result = accessTokenResponseGuard.safeParse(authResult); + + if (!result.success) { + throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error); + } + + const { accessToken } = result.data; + + assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid)); + + return { accessToken }; +}; + +const getUserInfo = + (getConfig: GetConnectorConfig): GetUserInfo => + async (data) => { + const { code, redirectUri } = await authorizationCallbackHandler(data); + + // Temporarily keep this as this is a refactor, which should not change the logics. + const config = await getConfig(defaultMetadata.id); + validateConfig(config, azureADConfigGuard); + + const { accessToken } = await getAccessToken(config, code, redirectUri); + + try { + const httpResponse = await got.get(graphAPIEndpoint, { + headers: { + authorization: `Bearer ${accessToken}`, + }, + timeout: { request: defaultTimeout }, + }); + const rawData = parseJson(httpResponse.body); + const result = userInfoResponseGuard.safeParse(rawData); + + if (!result.success) { + throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error); + } + + const { id, mail, displayName, userPrincipalName } = result.data; + + return { + id, + email: conditional(mail ?? userPrincipalName), + name: conditional(displayName), + rawData, + }; + } catch (error: unknown) { + if (error instanceof HTTPError) { + const { statusCode, body: rawBody } = error.response; + + if (statusCode === 401) { + throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid); + } + + throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(rawBody)); + } + + throw error; + } + }; + +const authorizationCallbackHandler = async (parameterObject: unknown) => { + const result = authResponseGuard.safeParse(parameterObject); + + if (!result.success) { + throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject)); + } + + return result.data; +}; + +const createAzureAdConnector: CreateConnector = async ({ getConfig }) => { + return { + metadata: defaultMetadata, + type: ConnectorType.Social, + configGuard: azureADConfigGuard, + getAuthorizationUri: getAuthorizationUri(getConfig), + getUserInfo: getUserInfo(getConfig), + }; +}; + +export default createAzureAdConnector; diff --git a/packages/connectors/connector-ogcio-entraid/src/types.ts b/packages/connectors/connector-ogcio-entraid/src/types.ts new file mode 100644 index 00000000000..9af09f1698f --- /dev/null +++ b/packages/connectors/connector-ogcio-entraid/src/types.ts @@ -0,0 +1,42 @@ +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; + +export const accessTokenResponseGuard = z.object({ + accessToken: z.string(), + scopes: z.array(z.string()), + tokenType: z.string(), +}); + +export type AccessTokenResponse = z.infer; + +export const userInfoResponseGuard = z.object({ + id: z.string(), + displayName: z.string().nullish(), + givenName: z.string().nullish(), + surname: z.string().nullish(), + userPrincipalName: z.string().nullish(), + jobTitle: z.string().nullish(), + mail: z.string().nullish(), + mobilePhone: z.string().nullish(), + officeLocation: z.string().nullish(), + preferredLanguage: z.string().nullish(), + businessPhones: z.array(z.string()).nullish(), +}); + +export type UserInfoResponse = z.infer; + +export const authResponseGuard = z.object({ + code: z.string(), + redirectUri: z.string(), +}); diff --git a/packages/connectors/connector-oidc/CHANGELOG.md b/packages/connectors/connector-oidc/CHANGELOG.md index 8ff39c430b4..e90805f28d1 100644 --- a/packages/connectors/connector-oidc/CHANGELOG.md +++ b/packages/connectors/connector-oidc/CHANGELOG.md @@ -1,5 +1,20 @@ # @logto/connector-oidc +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + +### Patch Changes + +- Updated dependencies [510f681fa] + - @logto/connector-oauth@1.4.0 + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-oidc/package.json b/packages/connectors/connector-oidc/package.json index bffa1bbc509..432e3ccd0ca 100644 --- a/packages/connectors/connector-oidc/package.json +++ b/packages/connectors/connector-oidc/package.json @@ -1,17 +1,17 @@ { "name": "@logto/connector-oidc", - "version": "1.3.1", + "version": "1.4.0", "description": "OIDC standard connector implementation.", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", - "@logto/connector-oauth": "workspace:^1.3.1", + "@logto/connector-oauth": "workspace:^1.4.0", "@logto/shared": "workspace:^3.1.1", "@silverhand/essentials": "^2.9.1", - "jose": "^5.0.0", + "jose": "^5.6.3", "ky": "^1.2.3", "nanoid": "^5.0.1", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -26,9 +26,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -55,23 +55,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", - "nock": "14.0.0-beta.7", + "nock": "14.0.0-beta.9", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-postmark/CHANGELOG.md b/packages/connectors/connector-postmark/CHANGELOG.md new file mode 100644 index 00000000000..6e153566253 --- /dev/null +++ b/packages/connectors/connector-postmark/CHANGELOG.md @@ -0,0 +1,7 @@ +# @logto/connector-postmark + +## 1.0.0 + +### Major Changes + +- e9581d8b4: add postmark connector diff --git a/packages/connectors/connector-postmark/README.md b/packages/connectors/connector-postmark/README.md new file mode 100644 index 00000000000..f456741d78b --- /dev/null +++ b/packages/connectors/connector-postmark/README.md @@ -0,0 +1,61 @@ +# Postmark connector + +Logto connector for Postmark email service. + +## Get started + +Postmark is a mail platform for transactional and marketing email. We can use its email sending function to send a _verification code_. + +## Register Postmark account + +Create a new account at [Postmark website](https://postmark.com/). You may skip this step if you've already got an account. + +## Configure your connector + +Fill out the `serverToken` field with the Server Token you find under settings for your +server in Postmark. + +Fill out the `fromEmail` field with the senders' _From Address_. + +In order to enable full user flows, templates with usageType `Register`, `SignIn`, `ForgotPassword` and `Generic` are required + +Here is an example of Postmark connector template JSON. + +```jsonc +[ + { + "usageType": "Register", + "templateAlias": "logto-register" + }, + { + "usageType": "SignIn", + "templateAlias": "logto-sign-in" + }, + { + "usageType": "ForgotPassword", + "templateAlias": "logto-forgot-password" + }, + { + "usageType": "Generic", + "templateAlias": "logto-generic" + }, +] +``` + +## Test Postmark email connector + +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) + +## Config types + +| Name | Type | +|-------------|-------------------| +| serverToken | string | +| fromEmail | string | + +| Template Properties | Type | Enum values | +|---------------------|-------------|------------------------------------------------------| +| usageType | enum string | 'Register' \| 'SignIn' \| 'ForgotPassword' \| 'Generic' | +| templateAlias | string | N/A | diff --git a/packages/connectors/connector-postmark/logo.svg b/packages/connectors/connector-postmark/logo.svg new file mode 100644 index 00000000000..7826748742a --- /dev/null +++ b/packages/connectors/connector-postmark/logo.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/connectors/connector-postmark/package.json b/packages/connectors/connector-postmark/package.json new file mode 100644 index 00000000000..ab8fd454f22 --- /dev/null +++ b/packages/connectors/connector-postmark/package.json @@ -0,0 +1,68 @@ +{ + "name": "@logto/connector-postmark", + "version": "1.0.0", + "description": "Postmark connector implementation.", + "author": "Sten Sandvik ", + "dependencies": { + "@logto/connector-kit": "workspace:^4.0.0", + "@silverhand/essentials": "^2.9.0", + "postmark": "^4.0.2", + "zod": "^3.22.4" + }, + "devDependencies": { + "@silverhand/eslint-config": "6.0.1", + "@silverhand/ts-config": "6.0.0", + "@types/node": "^20.11.20", + "@types/supertest": "^6.0.2", + "@vitest/coverage-v8": "^2.0.0", + "eslint": "^8.56.0", + "lint-staged": "^15.0.2", + "nock": "14.0.0-beta.9", + "prettier": "^3.0.0", + "supertest": "^7.0.0", + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" + }, + "main": "./lib/index.js", + "module": "./lib/index.js", + "exports": "./lib/index.js", + "license": "MPL-2.0", + "type": "module", + "files": [ + "lib", + "docs", + "logo.svg", + "logo-dark.svg" + ], + "scripts": { + "precommit": "lint-staged", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", + "lint": "eslint --ext .ts src", + "lint:report": "pnpm lint --format json --output-file report.json", + "test": "vitest src", + "test:ci": "pnpm run test --silent --coverage", + "prepublishOnly": "pnpm build" + }, + "engines": { + "node": "^20.9.0" + }, + "eslintConfig": { + "extends": "@silverhand", + "settings": { + "import/core-modules": [ + "@silverhand/essentials", + "got", + "nock", + "snakecase-keys", + "zod" + ] + } + }, + "prettier": "@silverhand/eslint-config/.prettierrc", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/connectors/connector-postmark/src/constant.ts b/packages/connectors/connector-postmark/src/constant.ts new file mode 100644 index 00000000000..694012e0934 --- /dev/null +++ b/packages/connectors/connector-postmark/src/constant.ts @@ -0,0 +1,57 @@ +import type { ConnectorMetadata } from '@logto/connector-kit'; +import { ConnectorConfigFormItemType } from '@logto/connector-kit'; + +export const defaultMetadata: ConnectorMetadata = { + id: 'postmark-mail', + target: 'postmark-mail', + platform: null, + name: { + en: 'Postmark Mail', + }, + logo: './logo.svg', + logoDark: null, + description: { + en: 'Postmark is a mail sending platform.', + }, + readme: './README.md', + formItems: [ + { + key: 'serverToken', + label: 'Server Token', + type: ConnectorConfigFormItemType.Text, + required: true, + placeholder: '', + }, + { + key: 'fromEmail', + label: 'From Email', + type: ConnectorConfigFormItemType.Text, + required: true, + placeholder: '', + }, + { + key: 'templates', + label: 'Templates', + type: ConnectorConfigFormItemType.Json, + required: true, + defaultValue: [ + { + usageType: 'SignIn', + templateAlias: 'logto-sign-in', + }, + { + usageType: 'Register', + templateAlias: 'logto-register', + }, + { + usageType: 'ForgotPassword', + templateAlias: 'logto-forgot-password', + }, + { + usageType: 'Generic', + templateAlias: 'logto-generic', + }, + ], + }, + ], +}; diff --git a/packages/connectors/connector-postmark/src/index.test.ts b/packages/connectors/connector-postmark/src/index.test.ts new file mode 100644 index 00000000000..634a0bddaa5 --- /dev/null +++ b/packages/connectors/connector-postmark/src/index.test.ts @@ -0,0 +1,42 @@ +import { TemplateType } from '@logto/connector-kit'; + +import { mockedConfig } from './mock.js'; + +const getConfig = vi.fn().mockResolvedValue(mockedConfig); +const sendEmailWithTemplate = vi.fn(); +vi.mock('postmark', () => ({ + ServerClient: vi.fn(() => ({ + sendEmailWithTemplate, + })), +})); + +const { default: createConnector } = await import('./index.js'); + +describe('Postmark connector', () => { + it('init without throwing errors', async () => { + await expect(createConnector({ getConfig })).resolves.not.toThrow(); + }); + + describe('sendMessage()', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should call sendEmailWithTemplate() with correct template and content', async () => { + const connector = await createConnector({ getConfig }); + await connector.sendMessage({ + to: 'to@email.com', + type: TemplateType.SignIn, + payload: { code: '1234' }, + }); + expect(sendEmailWithTemplate).toHaveBeenCalledWith( + expect.objectContaining({ + From: mockedConfig.fromEmail, + TemplateAlias: 'logto-sign-in', + To: 'to@email.com', + TemplateModel: { code: '1234' }, + }) + ); + }); + }); +}); diff --git a/packages/connectors/connector-postmark/src/index.ts b/packages/connectors/connector-postmark/src/index.ts new file mode 100644 index 00000000000..6039796cdab --- /dev/null +++ b/packages/connectors/connector-postmark/src/index.ts @@ -0,0 +1,65 @@ +import { assert } from '@silverhand/essentials'; + +import type { + GetConnectorConfig, + CreateConnector, + EmailConnector, + SendMessageFunction, +} from '@logto/connector-kit'; +import { + ConnectorError, + ConnectorErrorCodes, + validateConfig, + ConnectorType, +} from '@logto/connector-kit'; +import { ServerClient } from 'postmark'; + +import { defaultMetadata } from './constant.js'; +import { postmarkConfigGuard } from './types.js'; + +const sendMessage = + (getConfig: GetConnectorConfig): SendMessageFunction => + async (data, inputConfig) => { + const { to, type, payload } = data; + + const config = inputConfig ?? (await getConfig(defaultMetadata.id)); + validateConfig(config, postmarkConfigGuard); + + const { serverToken, fromEmail, templates } = config; + const template = templates.find((template) => template.usageType === type); + + assert( + template, + new ConnectorError( + ConnectorErrorCodes.TemplateNotFound, + `Template not found for type: ${type}` + ) + ); + + const client = new ServerClient(serverToken); + + try { + await client.sendEmailWithTemplate({ + From: fromEmail, + TemplateAlias: template.templateAlias, + To: to, + TemplateModel: payload, + }); + } catch (error: unknown) { + throw new ConnectorError( + ConnectorErrorCodes.General, + error instanceof Error ? error.message : '' + ); + } + }; + +const createPostmarkConnector: CreateConnector = async ({ getConfig }) => { + return { + metadata: defaultMetadata, + type: ConnectorType.Email, + configGuard: postmarkConfigGuard, + sendMessage: sendMessage(getConfig), + }; +}; + +export default createPostmarkConnector; diff --git a/packages/connectors/connector-postmark/src/mock.ts b/packages/connectors/connector-postmark/src/mock.ts new file mode 100644 index 00000000000..457b541ccd1 --- /dev/null +++ b/packages/connectors/connector-postmark/src/mock.ts @@ -0,0 +1,26 @@ +import type { PostmarkConfig } from './types.js'; + +export const mockedServerToken = 'serverToken'; + +export const mockedConfig: PostmarkConfig = { + serverToken: mockedServerToken, + fromEmail: 'noreply@logto.test.io', + templates: [ + { + usageType: 'SignIn', + templateAlias: 'logto-sign-in', + }, + { + usageType: 'Register', + templateAlias: 'logto-register', + }, + { + usageType: 'ForgotPassword', + templateAlias: 'logto-forgot-password', + }, + { + usageType: 'Generic', + templateAlias: 'logto-generic', + }, + ], +}; diff --git a/packages/connectors/connector-postmark/src/types.ts b/packages/connectors/connector-postmark/src/types.ts new file mode 100644 index 00000000000..ff5a06cd636 --- /dev/null +++ b/packages/connectors/connector-postmark/src/types.ts @@ -0,0 +1,32 @@ +import { z } from 'zod'; + +/** + * UsageType here is used to specify the use case of the template, can be either + * 'Register', 'SignIn', 'ForgotPassword', 'Generic'. + */ +const requiredTemplateUsageTypes = ['Register', 'SignIn', 'ForgotPassword', 'Generic']; + +const templateGuard = z.object({ + usageType: z.string(), + templateAlias: z.string(), +}); + +export const postmarkConfigGuard = z.object({ + serverToken: z.string(), + fromEmail: z.string(), + templates: z.array(templateGuard).refine( + (templates) => + requiredTemplateUsageTypes.every((requiredType) => + templates.map((template) => template.usageType).includes(requiredType) + ), + (templates) => ({ + message: `Template with UsageType (${requiredTemplateUsageTypes + .filter( + (requiredType) => !templates.map((template) => template.usageType).includes(requiredType) + ) + .join(', ')}) should be provided!`, + }) + ), +}); + +export type PostmarkConfig = z.infer; diff --git a/packages/connectors/connector-saml/CHANGELOG.md b/packages/connectors/connector-saml/CHANGELOG.md index dabc6162ef2..ac9bf904c10 100644 --- a/packages/connectors/connector-saml/CHANGELOG.md +++ b/packages/connectors/connector-saml/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-saml +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-saml/package.json b/packages/connectors/connector-saml/package.json index 7e84b900cf4..af99429f3ca 100644 --- a/packages/connectors/connector-saml/package.json +++ b/packages/connectors/connector-saml/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-saml", - "version": "1.1.2", + "version": "1.2.0", "description": "SAML standard connector implementation.", "author": "Silverhand Inc. ", "dependencies": { @@ -9,8 +9,8 @@ "fast-xml-parser": "^4.3.6", "got": "^14.0.0", "samlify": "2.8.11", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -25,9 +25,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -54,23 +54,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-sendgrid-email/CHANGELOG.md b/packages/connectors/connector-sendgrid-email/CHANGELOG.md index 421045fef2c..2b86deb802d 100644 --- a/packages/connectors/connector-sendgrid-email/CHANGELOG.md +++ b/packages/connectors/connector-sendgrid-email/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-sendgrid-email +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-sendgrid-email/package.json b/packages/connectors/connector-sendgrid-email/package.json index 58231c9eb00..68700420171 100644 --- a/packages/connectors/connector-sendgrid-email/package.json +++ b/packages/connectors/connector-sendgrid-email/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-sendgrid-email", - "version": "1.1.2", + "version": "1.2.0", "description": "SendGrid Email Service connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-smsaero/CHANGELOG.md b/packages/connectors/connector-smsaero/CHANGELOG.md index fe9faafda9c..77f31884710 100644 --- a/packages/connectors/connector-smsaero/CHANGELOG.md +++ b/packages/connectors/connector-smsaero/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-smsaero +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.2 ### Patch Changes diff --git a/packages/connectors/connector-smsaero/package.json b/packages/connectors/connector-smsaero/package.json index 306eb38dbb3..9fcc20f83d6 100644 --- a/packages/connectors/connector-smsaero/package.json +++ b/packages/connectors/connector-smsaero/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-smsaero", - "version": "1.2.2", + "version": "1.3.0", "description": "SMSAero connector implementation.", "author": "Danil Tankov ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-smtp/CHANGELOG.md b/packages/connectors/connector-smtp/CHANGELOG.md index 999f63372ba..0d068c29e28 100644 --- a/packages/connectors/connector-smtp/CHANGELOG.md +++ b/packages/connectors/connector-smtp/CHANGELOG.md @@ -1,5 +1,16 @@ # @logto/connector-smtp +## 1.2.0 + +### Minor Changes + +- 6fca3fe3c: enable static custom headers for SMTP connector +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.3 ### Patch Changes diff --git a/packages/connectors/connector-smtp/package.json b/packages/connectors/connector-smtp/package.json index 8fbd5f46869..1edc7fad725 100644 --- a/packages/connectors/connector-smtp/package.json +++ b/packages/connectors/connector-smtp/package.json @@ -1,6 +1,6 @@ { "name": "@logto/connector-smtp", - "version": "1.1.3", + "version": "1.2.0", "description": "SMTP connector implementation.", "author": "Silverhand Inc. ", "dependencies": { @@ -8,29 +8,24 @@ "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", "nodemailer": "^6.9.9", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/nodemailer": "^6.4.7", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -45,9 +40,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/connector-smtp/src/constant.ts b/packages/connectors/connector-smtp/src/constant.ts index d2078b048c0..28d9a42761f 100644 --- a/packages/connectors/connector-smtp/src/constant.ts +++ b/packages/connectors/connector-smtp/src/constant.ts @@ -198,5 +198,14 @@ export const defaultMetadata: ConnectorMetadata = { type: ConnectorConfigFormItemType.Switch, required: false, }, + { + key: 'customHeaders', + label: 'Custom Headers', + type: ConnectorConfigFormItemType.Json, + required: false, + defaultValue: {}, + description: + 'Custom headers to be added to original email headers when sending messages. Both keys and values should be string-typed.', + }, ], }; diff --git a/packages/connectors/connector-smtp/src/index.test.ts b/packages/connectors/connector-smtp/src/index.test.ts index a28137dd32d..b57ad02e133 100644 --- a/packages/connectors/connector-smtp/src/index.test.ts +++ b/packages/connectors/connector-smtp/src/index.test.ts @@ -79,6 +79,28 @@ describe('SMTP connector', () => { to: 'baz', }); }); + + it('should send mail with customer headers', async () => { + const connector = await createConnector({ + getConfig: vi.fn().mockResolvedValue({ + ...mockedConfig, + customHeaders: { 'X-Test': 'test', 'X-Test-Another': ['test1', 'test2', 'test3'] }, + }), + }); + await connector.sendMessage({ + to: 'baz', + type: TemplateType.OrganizationInvitation, + payload: { code: '345678', link: 'https://example.com' }, + }); + + expect(sendMail).toHaveBeenCalledWith({ + from: '', + subject: 'Organization invitation', + text: 'This is for organization invitation. Your link is https://example.com.', + to: 'baz', + headers: { 'X-Test': 'test', 'X-Test-Another': ['test1', 'test2', 'test3'] }, + }); + }); }); describe('Test config guard', () => { diff --git a/packages/connectors/connector-smtp/src/index.ts b/packages/connectors/connector-smtp/src/index.ts index 8aed0e05dd6..f447f420e65 100644 --- a/packages/connectors/connector-smtp/src/index.ts +++ b/packages/connectors/connector-smtp/src/index.ts @@ -1,4 +1,4 @@ -import { assert } from '@silverhand/essentials'; +import { assert, conditional } from '@silverhand/essentials'; import type { GetConnectorConfig, @@ -14,6 +14,7 @@ import { replaceSendMessageHandlebars, } from '@logto/connector-kit'; import nodemailer from 'nodemailer'; +import type Mail from 'nodemailer/lib/mailer'; import type SMTPTransport from 'nodemailer/lib/smtp-transport'; import { defaultMetadata } from './constant.js'; @@ -44,11 +45,17 @@ const sendMessage = template.contentType ); - const mailOptions = { + const mailOptions: Mail.Options = { to, from: config.fromEmail, replyTo: config.replyTo, subject: replaceSendMessageHandlebars(template.subject, payload), + ...conditional( + config.customHeaders && + Object.entries(config.customHeaders).length > 0 && { + headers: config.customHeaders, + } + ), ...contentsObject, }; diff --git a/packages/connectors/connector-smtp/src/mock.ts b/packages/connectors/connector-smtp/src/mock.ts index 87f2017d3b5..bef4a38b1b1 100644 --- a/packages/connectors/connector-smtp/src/mock.ts +++ b/packages/connectors/connector-smtp/src/mock.ts @@ -35,6 +35,7 @@ export const mockedConfig = { usageType: 'OrganizationInvitation', }, ], + customHeaders: {}, }; export const mockedOauth2AuthWithToken = { diff --git a/packages/connectors/connector-smtp/src/types.ts b/packages/connectors/connector-smtp/src/types.ts index 263fe802807..cf18622105f 100644 --- a/packages/connectors/connector-smtp/src/types.ts +++ b/packages/connectors/connector-smtp/src/types.ts @@ -125,6 +125,7 @@ export const smtpConfigGuard = z.object({ servername: z.string().optional(), ignoreTLS: z.boolean().optional(), requireTLS: z.boolean().optional(), + customHeaders: z.record(z.string().or(z.string().array())).optional(), }); export type SmtpConfig = z.infer; diff --git a/packages/connectors/connector-tencent-sms/CHANGELOG.md b/packages/connectors/connector-tencent-sms/CHANGELOG.md index a0f03437d48..aee270d8a4f 100644 --- a/packages/connectors/connector-tencent-sms/CHANGELOG.md +++ b/packages/connectors/connector-tencent-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-tencent-sms +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-tencent-sms/package.json b/packages/connectors/connector-tencent-sms/package.json index a0da5a6bcae..ae37f2c376d 100644 --- a/packages/connectors/connector-tencent-sms/package.json +++ b/packages/connectors/connector-tencent-sms/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-tencent-sms", - "version": "1.1.2", + "version": "1.2.0", "description": "Tencent SMS connector implementation.", "author": "StringKe", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-twilio-sms/CHANGELOG.md b/packages/connectors/connector-twilio-sms/CHANGELOG.md index 71e7e4630f3..b9642803401 100644 --- a/packages/connectors/connector-twilio-sms/CHANGELOG.md +++ b/packages/connectors/connector-twilio-sms/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-twilio-sms +## 1.2.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.1.2 ### Patch Changes diff --git a/packages/connectors/connector-twilio-sms/package.json b/packages/connectors/connector-twilio-sms/package.json index 976f3070249..99f9d94d014 100644 --- a/packages/connectors/connector-twilio-sms/package.json +++ b/packages/connectors/connector-twilio-sms/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-twilio-sms", - "version": "1.1.2", + "version": "1.2.0", "description": "Twilio SMS connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-wechat-native/CHANGELOG.md b/packages/connectors/connector-wechat-native/CHANGELOG.md index 8a11df3eed8..ebdfaff229e 100644 --- a/packages/connectors/connector-wechat-native/CHANGELOG.md +++ b/packages/connectors/connector-wechat-native/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-wechat-native +## 1.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.2.1 ### Patch Changes diff --git a/packages/connectors/connector-wechat-native/package.json b/packages/connectors/connector-wechat-native/package.json index 9a68f2dc462..c4890f9f638 100644 --- a/packages/connectors/connector-wechat-native/package.json +++ b/packages/connectors/connector-wechat-native/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-wechat-native", - "version": "1.2.1", + "version": "1.3.0", "description": "WeChat native connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-wechat-web/CHANGELOG.md b/packages/connectors/connector-wechat-web/CHANGELOG.md index f9206ebff28..246624d6d66 100644 --- a/packages/connectors/connector-wechat-web/CHANGELOG.md +++ b/packages/connectors/connector-wechat-web/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-wechat-web +## 1.4.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 1.3.1 ### Patch Changes diff --git a/packages/connectors/connector-wechat-web/package.json b/packages/connectors/connector-wechat-web/package.json index 07d99ad9280..c2d7b409923 100644 --- a/packages/connectors/connector-wechat-web/package.json +++ b/packages/connectors/connector-wechat-web/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-wechat-web", - "version": "1.3.1", + "version": "1.4.0", "description": "Wechat Web connector implementation.", "author": "Silverhand Inc. ", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/connector-wecom/CHANGELOG.md b/packages/connectors/connector-wecom/CHANGELOG.md index 56e23dc3fb2..571d5b687d0 100644 --- a/packages/connectors/connector-wecom/CHANGELOG.md +++ b/packages/connectors/connector-wecom/CHANGELOG.md @@ -1,5 +1,15 @@ # @logto/connector-wecom +## 0.3.0 + +### Minor Changes + +- 510f681fa: use tsup for building + + We've updated some of the packages to use `tsup` for building. This will make the build process faster, and should not affect the functionality of the packages. + + Use minor version bump to catch your attention. + ## 0.2.1 ### Patch Changes diff --git a/packages/connectors/connector-wecom/package.json b/packages/connectors/connector-wecom/package.json index b06794e294c..15c3aa3555a 100644 --- a/packages/connectors/connector-wecom/package.json +++ b/packages/connectors/connector-wecom/package.json @@ -1,14 +1,14 @@ { "name": "@logto/connector-wecom", - "version": "0.2.1", + "version": "0.3.0", "description": "Wecom connector implementation.", "author": "Dove fork from Wechat Web connector", "dependencies": { "@logto/connector-kit": "workspace:^4.0.0", "@silverhand/essentials": "^2.9.1", "got": "^14.0.0", - "snakecase-keys": "^8.0.0", - "zod": "^3.22.4" + "snakecase-keys": "^8.0.1", + "zod": "^3.23.8" }, "main": "./lib/index.js", "module": "./lib/index.js", @@ -23,9 +23,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", @@ -52,23 +52,18 @@ "access": "public" }, "devDependencies": { - "@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", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.10.4", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.4.0", + "@vitest/coverage-v8": "^2.0.0", "eslint": "^8.56.0", "lint-staged": "^15.0.2", "nock": "^13.3.1", "prettier": "^3.0.0", - "rollup": "^4.12.0", - "rollup-plugin-output-size": "^1.3.0", "supertest": "^7.0.0", - "typescript": "^5.3.3", - "vitest": "^1.4.0" + "tsup": "^8.1.0", + "typescript": "^5.5.3", + "vitest": "^2.0.0" } } diff --git a/packages/connectors/templates/package.json b/packages/connectors/templates/package.json index ede58013e61..44870bc7b36 100644 --- a/packages/connectors/templates/package.json +++ b/packages/connectors/templates/package.json @@ -12,9 +12,9 @@ ], "scripts": { "precommit": "lint-staged", - "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", - "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", - "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "check": "tsc --noEmit", + "build": "tsup", + "dev": "tsup --watch", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", "test": "vitest src", diff --git a/packages/connectors/templates/preset/rollup.config.js b/packages/connectors/templates/preset/rollup.config.js deleted file mode 100644 index df98e6a1c1b..00000000000 --- a/packages/connectors/templates/preset/rollup.config.js +++ /dev/null @@ -1,25 +0,0 @@ -import commonjs from '@rollup/plugin-commonjs'; -import json from '@rollup/plugin-json'; -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import typescript from '@rollup/plugin-typescript'; -import outputSize from 'rollup-plugin-output-size'; - -/** - * @type {import('rollup').RollupOptions} - */ -const configs = [ - { - input: ['src/index.ts'], - output: [{ dir: 'lib' }], - external: ['zod', 'got', '@logto/connector-kit'], - plugins: [ - typescript({ tsconfig: 'tsconfig.build.json' }), - nodeResolve({ exportConditions: ['node'], preferBuiltins: true }), - commonjs(), - json(), - outputSize(), - ], - }, -]; - -export default configs; diff --git a/packages/connectors/templates/preset/tsconfig.base.json b/packages/connectors/templates/preset/tsconfig.base.json deleted file mode 100644 index ebab46568f2..00000000000 --- a/packages/connectors/templates/preset/tsconfig.base.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@silverhand/ts-config/tsconfig.base", - "compilerOptions": { - "moduleResolution": "nodenext", - "module": "nodenext", - "outDir": "lib", - "baseUrl": "." - } -} diff --git a/packages/connectors/templates/preset/tsconfig.build.json b/packages/connectors/templates/preset/tsconfig.build.json deleted file mode 100644 index d42923dd38c..00000000000 --- a/packages/connectors/templates/preset/tsconfig.build.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.base", - "include": ["src"], - "exclude": ["src/**/*.test.ts"] -} diff --git a/packages/connectors/templates/preset/tsconfig.json b/packages/connectors/templates/preset/tsconfig.json index 4fa2dd684aa..126a50cb0d9 100644 --- a/packages/connectors/templates/preset/tsconfig.json +++ b/packages/connectors/templates/preset/tsconfig.json @@ -1,7 +1,16 @@ { - "extends": "./tsconfig.base", + "extends": "@silverhand/ts-config/tsconfig.base", "compilerOptions": { - "types": ["node", "vitest/globals"] + "moduleResolution": "nodenext", + "module": "nodenext", + "outDir": "lib", + "baseUrl": ".", + "types": [ + "node", + "vitest/globals" + ] }, - "include": ["src", "types"] + "include": [ + "src" + ] } diff --git a/packages/connectors/templates/preset/tsconfig.test.json b/packages/connectors/templates/preset/tsconfig.test.json deleted file mode 100644 index 1424a155592..00000000000 --- a/packages/connectors/templates/preset/tsconfig.test.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig", - "compilerOptions": { - "isolatedModules": false, - "allowJs": true, - } -} diff --git a/packages/connectors/templates/preset/tsup.config.ts b/packages/connectors/templates/preset/tsup.config.ts new file mode 100644 index 00000000000..8e6ef8edb83 --- /dev/null +++ b/packages/connectors/templates/preset/tsup.config.ts @@ -0,0 +1,5 @@ +import { defineConfig } from 'tsup'; + +import { defaultConfig } from '../../../tsup.shared.config.js'; + +export default defineConfig(defaultConfig); diff --git a/packages/connectors/templates/sync-preset.js b/packages/connectors/templates/sync-preset.js index 32460417652..aa12dafb118 100644 --- a/packages/connectors/templates/sync-preset.js +++ b/packages/connectors/templates/sync-preset.js @@ -20,7 +20,7 @@ const templateKeys = Object.keys(templateJson); * Value format: `{ "": [""] }` * Example: `{ "connector-oauth2": ["prepack"] }` */ -const scriptExceptions = { 'connector-oauth2': ['prepack'] }; +const scriptExceptions = { 'connector-oauth2': ['prepack', 'build', 'build:test'] }; const sync = async () => { const packagesDirectory = './'; diff --git a/packages/console/.eslintrc.cjs b/packages/console/.eslintrc.cjs index f2cffeab3db..0997dc7409c 100644 --- a/packages/console/.eslintrc.cjs +++ b/packages/console/.eslintrc.cjs @@ -15,6 +15,7 @@ module.exports = { unnamedComponents: 'arrow-function', }, ], + 'react/jsx-pascal-case': ['error', { ignore: ['__Internal__*'] }], 'import/no-unused-modules': [ 'error', { @@ -30,6 +31,8 @@ module.exports = { '**/assets/docs/guides/*/index.ts', '**/assets/docs/guides/*/components/**/*.tsx', '**/mdx-components*/*/index.tsx', + '*.config.js', + '*.config.ts', ], rules: { 'import/no-unused-modules': 'off', @@ -49,5 +52,11 @@ module.exports = { ], }, }, + { + files: ['*.d.ts'], + rules: { + 'import/no-unassigned-import': 'off', + }, + }, ], }; diff --git a/packages/console/.parcelrc b/packages/console/.parcelrc deleted file mode 100644 index eeade8d2890..00000000000 --- a/packages/console/.parcelrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "@parcel/config-default", - "transformers": { - "raw:*": ["@parcel/transformer-raw"], - "**/assets/**/*.svg": [ - "@parcel/transformer-svg-react" - ], - "*.{md,mdx}": [ - "./parcel-transformer-mdx2.js" - ] - }, - "compressors": { - "*.{html,css,js,svg,map}": [ - "...", - "@parcel/compressor-gzip", - "@parcel/compressor-brotli" - ] - } -} diff --git a/packages/console/.parcelrc.arm64 b/packages/console/.parcelrc.arm64 deleted file mode 100644 index c31f639e17b..00000000000 --- a/packages/console/.parcelrc.arm64 +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "@parcel/config-default", - "optimizers": { - // Disable optimizers in arm64 arch https://github.com/parcel-bundler/parcel/issues/7402 - "*.{jpg,jpeg,png}": [] - }, - "transformers": { - "raw:*": ["@parcel/transformer-raw"], - "**/assets/**/*.svg": [ - "@parcel/transformer-svg-react" - ], - "*.{md,mdx}": [ - "./parcel-transformer-mdx2.js" - ] - }, - "compressors": { - "*.{html,css,js,svg,map}": [ - "...", - "@parcel/compressor-gzip", - "@parcel/compressor-brotli" - ] - } -} diff --git a/packages/console/CHANGELOG.md b/packages/console/CHANGELOG.md index a07f5892216..7898a683d93 100644 --- a/packages/console/CHANGELOG.md +++ b/packages/console/CHANGELOG.md @@ -1,5 +1,67 @@ # Change Log +## 1.17.0 + +### Minor Changes + +- 3a839f6d6: support organization logo and sign-in experience override + + Now it's able to set light and dark logos for organizations. You can upload the logos in the organization settings page. + + Also, it's possible to override the sign-in experience logo from an organization. Simply add the `organization_id` parameter to the authentication request. In most Logto SDKs, it can be done by using the `extraParams` field in the `signIn` method. + + For example, in the JavaScript SDK: + + ```ts + import LogtoClient from "@logto/client"; + + const logtoClient = new LogtoClient(/* your configuration */); + + logtoClient.signIn({ + redirectUri: "https://your-app.com/callback", + extraParams: { + organization_id: "", + }, + }); + ``` + + The value `` can be found in the organization settings page. + + If you could not find the `extraParams` field in the SDK you are using, please let us know. + +- b91ec0cd6: add the application `custom_data` field editor to the application details page in console +- 62f5e5e0c: support app-level branding + + You can now set logos, favicons, and colors for your app. These settings will be used in the sign-in experience when the app initiates the authentication flow. For apps that have no branding settings, the omni sign-in experience branding will be used. + + If `organization_id` is provided in the authentication request, the app-level branding settings will be overridden by the organization's branding settings, if available. + +- 3bf756f2b: use Vite for transpilation and bundling + + Removed ParcelJS and replaced with Vite. No breaking changes should be expected, but use a minor version bump to catch your attention. + + > [!Important] + > The browserlist configuration for `@logto/experience` and been synced with what is stated in README.md. + +- b188bb161: support multiple app secrets with expiration + + Now secure apps (machine-to-machine, traditional web, Protected) can have multiple app secrets with expiration. This allows for secret rotation and provides an even safer experience. + + To manage your application secrets, go to Logto Console -> Applications -> Application Details -> Endpoints & Credentials. + + We've also added a set of Management APIs (`/api/applications/{id}/secrets`) for this purpose. + + > [!Important] + > You can still use existing app secrets for client authentication, but it is recommended to delete the old ones and create new secrets with expiration for enhanced security. + +- 62f5e5e0c: support dark favicon + + The favicon for the dark theme now can be set in the sign-in experience branding settings. + +### Patch Changes + +- 3aa7e57b3: fix Google connector `scope` field can not be reset bug + ## 1.16.0 ### Minor Changes diff --git a/packages/console/src/index.html b/packages/console/index.html similarity index 51% rename from packages/console/src/index.html rename to packages/console/index.html index 91e6965ac8c..a5603fdf399 100644 --- a/packages/console/src/index.html +++ b/packages/console/index.html @@ -3,14 +3,14 @@ - - + +
- + diff --git a/packages/console/package.json b/packages/console/package.json index cda9fc37676..b93ee45baf9 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,6 +1,6 @@ { "name": "@logto/console", - "version": "1.16.0", + "version": "1.17.0", "description": "> TODO: description", "author": "Silverhand Inc. ", "homepage": "https://github.com/logto-io/logto#readme", @@ -14,10 +14,10 @@ "prepack": "pnpm generate", "generate": "./generate.sh", "precommit": "lint-staged", - "start": "parcel src/index.html", - "dev": "cross-env PORT=5002 parcel src/index.html --public-url ${CONSOLE_PUBLIC_URL:-/console} --no-cache --hmr-port 6002", + "start": "vite", + "dev": "vite", "check": "tsc --noEmit", - "build": "pnpm generate && pnpm check && rm -rf dist && parcel build src/index.html --no-autoinstall --no-cache --public-url ${CONSOLE_PUBLIC_URL:-/console}", + "build": "pnpm generate && vite build", "lint": "eslint --ext .ts --ext .tsx src", "lint:report": "pnpm lint --format json --output-file report.json", "stylelint": "stylelint \"src/**/*.scss\"", @@ -27,23 +27,19 @@ "devDependencies": { "@fontsource/roboto-mono": "^5.0.0", "@jest/types": "^29.5.0", - "@logto/cloud": "0.2.5-a7eedce", + "@logto/cloud": "0.2.5-923c26f", "@logto/connector-kit": "workspace:^4.0.0", "@logto/core-kit": "workspace:^2.5.0", + "@logto/elements": "workspace:^0.0.0", "@logto/language-kit": "workspace:^1.1.0", - "@logto/phrases": "workspace:^1.12.0", + "@logto/phrases": "workspace:^1.13.0", "@logto/phrases-experience": "workspace:^1.7.0", "@logto/react": "^3.0.12", - "@logto/schemas": "workspace:^1.18.0", + "@logto/schemas": "workspace:^1.19.0", "@logto/shared": "workspace:^3.1.1", - "@mdx-js/mdx": "^3.0.1", "@mdx-js/react": "^3.0.1", + "@mdx-js/rollup": "^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-sass": "2.9.3", - "@parcel/transformer-svg-react": "2.9.3", "@silverhand/eslint-config": "6.0.1", "@silverhand/eslint-config-react": "6.0.2", "@silverhand/essentials": "^2.9.1", @@ -56,31 +52,30 @@ "@types/debug": "^4.1.7", "@types/jest": "^29.4.0", "@types/mdx": "^2.0.13", - "@types/react": "^18.0.31", + "@types/react": "^18.3.3", "@types/react-color": "^3.0.6", - "@types/react-dom": "^18.0.0", + "@types/react-dom": "^18.3.0", "@types/react-helmet": "^6.1.6", "@types/react-modal": "^3.13.1", "@types/react-syntax-highlighter": "^15.5.1", + "@vitejs/plugin-react": "^4.3.1", "@withtyped/client": "^0.8.7", - "buffer": "^6.0.0", "classnames": "^2.3.1", "clean-deep": "^3.4.0", - "cross-env": "^7.0.3", - "csstype": "^3.0.11", "date-fns": "^2.29.3", "dayjs": "^1.10.5", "debug": "^4.3.4", "deep-object-diff": "^1.1.9", "deepmerge": "^4.2.2", "dnd-core": "^16.0.0", + "dotenv": "^16.4.5", "eslint": "^8.56.0", - "history": "^5.3.0", + "find-up": "^7.0.0", "i18next": "^22.4.15", "i18next-browser-languagedetector": "^8.0.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", - "jest-environment-jsdom": "^29.0.0", + "jest-environment-jsdom": "^29.7.0", "jest-transform-stub": "^2.0.0", "jest-transformer-svg": "^2.0.0", "just-kebab-case": "^4.2.0", @@ -91,20 +86,18 @@ "nanoid": "^5.0.1", "overlayscrollbars": "^2.0.2", "overlayscrollbars-react": "^0.5.0", - "parcel": "2.9.3", - "postcss": "^8.4.31", + "postcss": "^8.4.39", "postcss-modules": "^4.3.0", "prettier": "^3.0.0", - "process": "^0.11.10", "prop-types": "^15.8.1", "property-information": "^6.2.0", - "react": "^18.0.0", + "react": "^18.3.1", "react-animate-height": "^3.0.4", - "react-color": "^2.19.3", + "react-color-palette": "^7.2.1", "react-confetti": "^6.1.0", "react-dnd": "^16.0.0", "react-dnd-html5-backend": "^16.0.0", - "react-dom": "^18.0.0", + "react-dom": "^18.3.1", "react-dropzone": "^14.2.3", "react-helmet": "^6.1.0", "react-hook-form": "^7.43.9", @@ -113,7 +106,8 @@ "react-markdown": "^9.0.0", "react-modal": "^3.15.1", "react-paginate": "^8.1.3", - "react-router-dom": "^6.10.0", + "react-router-dom": "^6.25.1", + "react-safe-lazy": "^0.1.0", "react-syntax-highlighter": "^15.5.0", "react-timer-hook": "^3.0.5", "recharts": "^2.1.13", @@ -121,27 +115,17 @@ "remark-gfm": "^4.0.0", "stylelint": "^15.0.0", "swr": "^2.2.0", - "ts-node": "^10.9.2", - "tslib": "^2.4.1", - "typescript": "^5.3.3", - "zod": "^3.22.4", + "typescript": "^5.5.3", + "vite": "^5.3.4", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-prebundle": "^0.0.4", + "vite-plugin-svgr": "^4.2.0", + "zod": "^3.23.8", "zod-to-ts": "^1.2.0" }, "engines": { "node": "^20.9.0" }, - "//": "https://github.com/parcel-bundler/parcel/issues/7636", - "targets": { - "default": { - "engines": { - "browsers": "defaults" - } - } - }, - "alias": { - "@/*": "./src/$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 deleted file mode 100644 index bd005897575..00000000000 --- a/packages/console/parcel-transformer-mdx2.js +++ /dev/null @@ -1,61 +0,0 @@ -// 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/scripts/generate-jwt-customizer-type-definition.ts b/packages/console/scripts/generate-jwt-customizer-type-definition.ts index 9b25077a20b..e7f9af524d2 100644 --- a/packages/console/scripts/generate-jwt-customizer-type-definition.ts +++ b/packages/console/scripts/generate-jwt-customizer-type-definition.ts @@ -3,6 +3,7 @@ import fs from 'node:fs'; import { accessTokenPayloadGuard, clientCredentialsPayloadGuard, + jwtCustomizerGrantContextGuard, jwtCustomizerUserContextGuard, } from '@logto/schemas'; import prettier from 'prettier'; @@ -13,6 +14,7 @@ const filePath = 'src/consts/jwt-customizer-type-definition.ts'; const typeIdentifiers = `export enum JwtCustomizerTypeDefinitionKey { JwtCustomizerUserContext = 'JwtCustomizerUserContext', + JwtCustomizerGrantContext = 'JwtCustomizerGrantContext', AccessTokenPayload = 'AccessTokenPayload', ClientCredentialsPayload = 'ClientCredentialsPayload', EnvironmentVariables = 'EnvironmentVariables', @@ -43,6 +45,11 @@ const createJwtCustomizerTypeDefinitions = async () => { 'JwtCustomizerUserContext' ); + const jwtCustomizerGrantContextTypeDefinition = inferTsDefinitionFromZod( + jwtCustomizerGrantContextGuard, + 'JwtCustomizerGrantContext' + ); + const accessTokenPayloadTypeDefinition = inferTsDefinitionFromZod( accessTokenPayloadGuard, 'AccessTokenPayload' @@ -58,6 +65,8 @@ ${typeIdentifiers} export const jwtCustomizerUserContextTypeDefinition = \`${jwtCustomizerUserContextTypeDefinition}\`; +export const jwtCustomizerGrantContextTypeDefinition = \`${jwtCustomizerGrantContextTypeDefinition}\`; + export const accessTokenPayloadTypeDefinition = \`${accessTokenPayloadTypeDefinition}\`; export const clientCredentialsPayloadTypeDefinition = \`${clientCredentialsPayloadTypeDefinition}\`; diff --git a/packages/console/src/App.tsx b/packages/console/src/App.tsx index 96ff5ed713f..30830308c53 100644 --- a/packages/console/src/App.tsx +++ b/packages/console/src/App.tsx @@ -16,6 +16,8 @@ import './scss/normalized.scss'; import './scss/overlayscrollbars.scss'; // eslint-disable-next-line import/no-unassigned-import import '@fontsource/roboto-mono'; +// eslint-disable-next-line import/no-unassigned-import +import 'react-color-palette/css'; import CloudAppRoutes from '@/cloud/AppRoutes'; import AppLoading from '@/components/AppLoading'; diff --git a/packages/console/src/assets/docs/guides/README.md b/packages/console/src/assets/docs/guides/README.md index 29f0f0dea24..c2517133e50 100644 --- a/packages/console/src/assets/docs/guides/README.md +++ b/packages/console/src/assets/docs/guides/README.md @@ -52,6 +52,9 @@ Images and other assets (if any) should be placed in the `assets` directory of t ### Update metadata +> [!Note] +> This section is outdated and we should test if it's still necessary. + Since Parcel doesn't support dynamic import (see [#112](https://github.com/parcel-bundler/parcel/issues/112) [#125](https://github.com/parcel-bundler/parcel/issues/125)), we need to run `node generate-metadata.js` to update the metadata in `index.ts`, thus we can use it in the guide components with React lazy loading. This may be fixed by replacing Parcel with something else. diff --git a/packages/console/src/assets/docs/guides/api-express/logo-dark.svg b/packages/console/src/assets/docs/guides/api-express/logo-dark.svg new file mode 100644 index 00000000000..8ed50c5f2ab --- /dev/null +++ b/packages/console/src/assets/docs/guides/api-express/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/api-express/logo.svg b/packages/console/src/assets/docs/guides/api-express/logo.svg index bda5f80dabc..db674128014 100644 --- a/packages/console/src/assets/docs/guides/api-express/logo.svg +++ b/packages/console/src/assets/docs/guides/api-express/logo.svg @@ -1,3 +1,10 @@ - - + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/api-python/logo.svg b/packages/console/src/assets/docs/guides/api-python/logo.svg index c33c339ecb7..1d2ba15af8d 100644 --- a/packages/console/src/assets/docs/guides/api-python/logo.svg +++ b/packages/console/src/assets/docs/guides/api-python/logo.svg @@ -1,14 +1,19 @@ - - - + + + + + - + - + + + + diff --git a/packages/console/src/assets/docs/guides/api-spring-boot/logo.svg b/packages/console/src/assets/docs/guides/api-spring-boot/logo.svg index 55b425680de..2d4fe64ea1a 100644 --- a/packages/console/src/assets/docs/guides/api-spring-boot/logo.svg +++ b/packages/console/src/assets/docs/guides/api-spring-boot/logo.svg @@ -1,10 +1,10 @@ - - - + + + - - + + diff --git a/packages/console/src/assets/docs/guides/generate-metadata.js b/packages/console/src/assets/docs/guides/generate-metadata.js index d1ca726d995..adc18258106 100644 --- a/packages/console/src/assets/docs/guides/generate-metadata.js +++ b/packages/console/src/assets/docs/guides/generate-metadata.js @@ -1,4 +1,4 @@ -// This script generates the `index.ts` file for all the guides. +// This script generates the `index.tsx` file for all the guides. // It should be run from the `packages/console/src/assets/docs/guides` (current) directory. // For conventions and specifications, see the `README.md` file in this directory. @@ -21,6 +21,9 @@ const data = await Promise.all( } const logo = ['logo.webp', 'logo.svg', 'logo.png'].find((logo) => existsSync(`${directory}/${logo}`)); + const darkLogo = ['logo-dark.webp', 'logo-dark.svg', 'logo-dark.png'].find((logo) => + existsSync(`${directory}/${logo}`) + ); const config = existsSync(`${directory}/config.json`) ? await import(`./${directory}/config.json`, { assert: { type: 'json' } }).then( @@ -31,6 +34,7 @@ const data = await Promise.all( return { name: directory, logo, + darkLogo, order: config?.order ?? Number.POSITIVE_INFINITY, }; }) @@ -45,10 +49,11 @@ 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" + `/* eslint-disable max-lines */ +// This is a generated file, don't update manually.\n\nimport { lazy } from 'react';\n\nimport { type Guide } from './types';\n` ); -for (const { name, logo } of metadata) { +for (const { name, logo, darkLogo } of metadata) { // eslint-disable-next-line no-await-in-loop await fs.appendFile(filename, `import ${camelCase(name)} from './${name}/index';\n`); @@ -56,18 +61,23 @@ for (const { name, logo } of metadata) { // eslint-disable-next-line no-await-in-loop await fs.appendFile(filename, `import ${camelCase(name)}Logo from './${name}/${logo}';\n`); } + + if (darkLogo && !darkLogo.endsWith('.svg')) { + // eslint-disable-next-line no-await-in-loop + await fs.appendFile(filename, `import ${camelCase(name)}LogoDark from './${name}/${darkLogo}';\n`); + } } await fs.appendFile(filename, '\n'); await fs.appendFile(filename, 'export const guides: Readonly = Object.freeze(['); -const getLogo = ({ name, logo }) => { +const getLogo = ({ name, logo, isDark }) => { if (!logo) return 'undefined'; - if (logo.endsWith('.svg')) return `lazy(async () => import('./${name}/${logo}'))`; - return `({ className }: { readonly className?: string }) => ${name}`; + if (logo.endsWith('.svg')) return `lazy(async () => import('./${name}/${logo}?react'))`; + return `({ className }: { readonly className?: string }) => ${name}`; }; -for (const { name, logo, order } of metadata) { +for (const { name, logo, darkLogo, order } of metadata) { // eslint-disable-next-line no-await-in-loop await fs.appendFile( filename, @@ -76,10 +86,12 @@ for (const { name, logo, order } of metadata) { order: ${order}, id: '${name}', Logo: ${getLogo({ name, logo })}, + DarkLogo: ${getLogo({ name, logo: darkLogo, isDark: true })}, Component: lazy(async () => import('./${name}/README.mdx')), metadata: ${camelCase(name)}, },` ); } -await fs.appendFile(filename, ']);\n'); +await fs.appendFile(filename, `]); +/* eslint-enable max-lines */\n`); diff --git a/packages/console/src/assets/docs/guides/index.tsx b/packages/console/src/assets/docs/guides/index.tsx index d83d09817d4..d66e9699d27 100644 --- a/packages/console/src/assets/docs/guides/index.tsx +++ b/packages/console/src/assets/docs/guides/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ // This is a generated file, don't update manually. import { lazy } from 'react'; @@ -32,10 +33,10 @@ import webNextAppRouter from './web-next-app-router/index'; import webNextAuth from './web-next-auth/index'; import webNuxt from './web-nuxt/index'; import webOutline from './web-outline/index'; +import webPassport from './web-passport/index'; import webPhp from './web-php/index'; import webPython from './web-python/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'; @@ -43,241 +44,282 @@ export const guides: Readonly = Object.freeze([ { order: 1, id: 'web-next-app-router', - Logo: lazy(async () => import('./web-next-app-router/logo.svg')), + Logo: lazy(async () => import('./web-next-app-router/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-next-app-router/logo-dark.svg?react')), Component: lazy(async () => import('./web-next-app-router/README.mdx')), metadata: webNextAppRouter, }, { order: 1.1, id: 'native-expo', - Logo: lazy(async () => import('./native-expo/logo.svg')), + Logo: lazy(async () => import('./native-expo/logo.svg?react')), + DarkLogo: lazy(async () => import('./native-expo/logo-dark.svg?react')), Component: lazy(async () => import('./native-expo/README.mdx')), metadata: nativeExpo, }, { order: 1.1, id: 'spa-angular', - Logo: lazy(async () => import('./spa-angular/logo.svg')), + Logo: lazy(async () => import('./spa-angular/logo.svg?react')), + DarkLogo: undefined, 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')), + Logo: lazy(async () => import('./spa-chrome-extension/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-chrome-extension/README.mdx')), metadata: spaChromeExtension, }, { order: 1.1, id: 'spa-react', - Logo: lazy(async () => import('./spa-react/logo.svg')), + Logo: lazy(async () => import('./spa-react/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-react/README.mdx')), metadata: spaReact, }, { order: 1.2, id: 'm2m-general', - Logo: lazy(async () => import('./m2m-general/logo.svg')), + Logo: lazy(async () => import('./m2m-general/logo.svg?react')), + DarkLogo: lazy(async () => import('./m2m-general/logo-dark.svg?react')), Component: lazy(async () => import('./m2m-general/README.mdx')), metadata: m2mGeneral, }, { order: 1.2, id: 'web-express', - Logo: lazy(async () => import('./web-express/logo.svg')), + Logo: lazy(async () => import('./web-express/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-express/logo-dark.svg?react')), Component: lazy(async () => import('./web-express/README.mdx')), metadata: webExpress, }, { order: 1.2, id: 'web-next', - Logo: lazy(async () => import('./web-next/logo.svg')), + Logo: lazy(async () => import('./web-next/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-next/logo-dark.svg?react')), Component: lazy(async () => import('./web-next/README.mdx')), metadata: webNext, }, { order: 1.2, id: 'web-sveltekit', - Logo: lazy(async () => import('./web-sveltekit/logo.svg')), + Logo: lazy(async () => import('./web-sveltekit/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-sveltekit/README.mdx')), metadata: webSveltekit, }, { order: 1.3, id: 'web-go', - Logo: lazy(async () => import('./web-go/logo.svg')), + Logo: lazy(async () => import('./web-go/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-go/README.mdx')), metadata: webGo, }, { order: 1.3, id: 'web-next-auth', - Logo: lazy(async () => import('./web-next-auth/logo.svg')), + Logo: lazy(async () => import('./web-next-auth/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-next-auth/README.mdx')), metadata: webNextAuth, }, { order: 1.4, id: 'web-java-spring-boot', - Logo: lazy(async () => import('./web-java-spring-boot/logo.svg')), + Logo: lazy(async () => import('./web-java-spring-boot/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-java-spring-boot/README.mdx')), metadata: webJavaSpringBoot, }, { order: 1.6, id: 'spa-vue', - Logo: lazy(async () => import('./spa-vue/logo.svg')), + Logo: lazy(async () => import('./spa-vue/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-vue/README.mdx')), metadata: spaVue, }, { order: 1.7, id: 'native-ios-swift', - Logo: lazy(async () => import('./native-ios-swift/logo.svg')), + Logo: lazy(async () => import('./native-ios-swift/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./native-ios-swift/README.mdx')), metadata: nativeIosSwift, }, { order: 2, id: 'native-android', - Logo: lazy(async () => import('./native-android/logo.svg')), + Logo: lazy(async () => import('./native-android/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./native-android/README.mdx')), metadata: nativeAndroid, }, { order: 2, id: 'spa-vanilla', - Logo: lazy(async () => import('./spa-vanilla/logo.svg')), + Logo: lazy(async () => import('./spa-vanilla/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-vanilla/README.mdx')), metadata: spaVanilla, }, { order: 2, id: 'web-nuxt', - Logo: lazy(async () => import('./web-nuxt/logo.svg')), + Logo: lazy(async () => import('./web-nuxt/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-nuxt/README.mdx')), metadata: webNuxt, }, { order: 2, id: 'web-php', - Logo: lazy(async () => import('./web-php/logo.svg')), + Logo: lazy(async () => import('./web-php/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-php/README.mdx')), metadata: webPhp, }, { order: 2, id: 'web-ruby', - Logo: ({ className }: { readonly className?: string }) => ( - web-ruby - ), + Logo: lazy(async () => import('./web-ruby/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-ruby/README.mdx')), metadata: webRuby, }, { order: 2.1, id: 'spa-webflow', - Logo: lazy(async () => import('./spa-webflow/logo.svg')), + Logo: lazy(async () => import('./spa-webflow/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./spa-webflow/README.mdx')), metadata: spaWebflow, }, { order: 2.2, id: 'web-wordpress', - Logo: lazy(async () => import('./web-wordpress/logo.svg')), + Logo: lazy(async () => import('./web-wordpress/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-wordpress/README.mdx')), metadata: webWordpress, }, { order: 3, id: 'web-python', - Logo: lazy(async () => import('./web-python/logo.svg')), + Logo: lazy(async () => import('./web-python/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-python/README.mdx')), metadata: webPython, }, { order: 4, id: 'native-capacitor', - Logo: lazy(async () => import('./native-capacitor/logo.svg')), + Logo: lazy(async () => import('./native-capacitor/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./native-capacitor/README.mdx')), metadata: nativeCapacitor, }, { order: 5, id: 'native-flutter', - Logo: lazy(async () => import('./native-flutter/logo.svg')), + Logo: lazy(async () => import('./native-flutter/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./native-flutter/README.mdx')), metadata: nativeFlutter, }, { order: 5, id: 'web-dotnet-core', - Logo: lazy(async () => import('./web-dotnet-core/logo.svg')), + Logo: lazy(async () => import('./web-dotnet-core/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-dotnet-core/README.mdx')), metadata: webDotnetCore, }, { order: 5.1, id: 'web-dotnet-core-mvc', - Logo: lazy(async () => import('./web-dotnet-core-mvc/logo.svg')), + Logo: lazy(async () => import('./web-dotnet-core-mvc/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-dotnet-core-mvc/README.mdx')), metadata: webDotnetCoreMvc, }, { order: 5.2, id: 'web-dotnet-core-blazor-server', - Logo: lazy(async () => import('./web-dotnet-core-blazor-server/logo.svg')), + Logo: lazy(async () => import('./web-dotnet-core-blazor-server/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-dotnet-core-blazor-server/README.mdx')), metadata: webDotnetCoreBlazorServer, }, { order: 5.3, id: 'web-dotnet-core-blazor-wasm', - Logo: lazy(async () => import('./web-dotnet-core-blazor-wasm/logo.svg')), + Logo: lazy(async () => import('./web-dotnet-core-blazor-wasm/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-dotnet-core-blazor-wasm/README.mdx')), metadata: webDotnetCoreBlazorWasm, }, { order: 6, id: 'web-outline', - Logo: lazy(async () => import('./web-outline/logo.svg')), + Logo: lazy(async () => import('./web-outline/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-outline/logo-dark.svg?react')), Component: lazy(async () => import('./web-outline/README.mdx')), metadata: webOutline, }, + { + order: 6.1, + id: 'web-passport', + Logo: lazy(async () => import('./web-passport/logo.svg?react')), + DarkLogo: lazy(async () => import('./web-passport/logo-dark.svg?react')), + Component: lazy(async () => import('./web-passport/README.mdx')), + metadata: webPassport, + }, { order: 999, id: 'web-gpt-plugin', - Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')), + Logo: lazy(async () => import('./web-gpt-plugin/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./web-gpt-plugin/README.mdx')), metadata: webGptPlugin, }, { order: Number.POSITIVE_INFINITY, id: 'api-express', - Logo: lazy(async () => import('./api-express/logo.svg')), + Logo: lazy(async () => import('./api-express/logo.svg?react')), + DarkLogo: lazy(async () => import('./api-express/logo-dark.svg?react')), Component: lazy(async () => import('./api-express/README.mdx')), metadata: apiExpress, }, { order: Number.POSITIVE_INFINITY, id: 'api-python', - Logo: lazy(async () => import('./api-python/logo.svg')), + Logo: lazy(async () => import('./api-python/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./api-python/README.mdx')), metadata: apiPython, }, { order: Number.POSITIVE_INFINITY, id: 'api-spring-boot', - Logo: lazy(async () => import('./api-spring-boot/logo.svg')), + Logo: lazy(async () => import('./api-spring-boot/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./api-spring-boot/README.mdx')), metadata: apiSpringBoot, }, { order: Number.POSITIVE_INFINITY, id: 'third-party-oidc', - Logo: lazy(async () => import('./third-party-oidc/logo.svg')), + Logo: lazy(async () => import('./third-party-oidc/logo.svg?react')), + DarkLogo: undefined, Component: lazy(async () => import('./third-party-oidc/README.mdx')), metadata: thirdPartyOidc, }, ]); +/* eslint-enable max-lines */ diff --git a/packages/console/src/assets/docs/guides/m2m-general/logo-dark.svg b/packages/console/src/assets/docs/guides/m2m-general/logo-dark.svg new file mode 100644 index 00000000000..7aaf3ec6f52 --- /dev/null +++ b/packages/console/src/assets/docs/guides/m2m-general/logo-dark.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/m2m-general/logo.svg b/packages/console/src/assets/docs/guides/m2m-general/logo.svg index a2a63b7f663..5ef0d8edd3b 100644 --- a/packages/console/src/assets/docs/guides/m2m-general/logo.svg +++ b/packages/console/src/assets/docs/guides/m2m-general/logo.svg @@ -1,35 +1,35 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - + - + diff --git a/packages/console/src/assets/docs/guides/native-android/logo.svg b/packages/console/src/assets/docs/guides/native-android/logo.svg index 63418a92e23..04c799220d0 100644 --- a/packages/console/src/assets/docs/guides/native-android/logo.svg +++ b/packages/console/src/assets/docs/guides/native-android/logo.svg @@ -1,4 +1,11 @@ - - - + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/native-capacitor/logo.svg b/packages/console/src/assets/docs/guides/native-capacitor/logo.svg index e8c04da0996..84f1647a659 100644 --- a/packages/console/src/assets/docs/guides/native-capacitor/logo.svg +++ b/packages/console/src/assets/docs/guides/native-capacitor/logo.svg @@ -1,8 +1,15 @@ - - - - - - - + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/native-expo/logo-dark.svg b/packages/console/src/assets/docs/guides/native-expo/logo-dark.svg new file mode 100644 index 00000000000..78413920df1 --- /dev/null +++ b/packages/console/src/assets/docs/guides/native-expo/logo-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/console/src/assets/docs/guides/native-expo/logo.svg b/packages/console/src/assets/docs/guides/native-expo/logo.svg index 785dbbd164f..df2edaf0de0 100644 --- a/packages/console/src/assets/docs/guides/native-expo/logo.svg +++ b/packages/console/src/assets/docs/guides/native-expo/logo.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/packages/console/src/assets/docs/guides/native-flutter/logo.svg b/packages/console/src/assets/docs/guides/native-flutter/logo.svg index ad50e5eb043..90e5276fc46 100644 --- a/packages/console/src/assets/docs/guides/native-flutter/logo.svg +++ b/packages/console/src/assets/docs/guides/native-flutter/logo.svg @@ -1,18 +1,28 @@ - - - - - - - + + + + + + + + + + + - + - + + + + + + + diff --git a/packages/console/src/assets/docs/guides/native-ios-swift/logo.svg b/packages/console/src/assets/docs/guides/native-ios-swift/logo.svg index 1f273709d07..5bc1e96e120 100644 --- a/packages/console/src/assets/docs/guides/native-ios-swift/logo.svg +++ b/packages/console/src/assets/docs/guides/native-ios-swift/logo.svg @@ -1,4 +1,11 @@ - - - + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/spa-angular/logo.svg b/packages/console/src/assets/docs/guides/spa-angular/logo.svg index 96301efe1b4..e53c94dfce2 100644 --- a/packages/console/src/assets/docs/guides/spa-angular/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-angular/logo.svg @@ -1,16 +1,12 @@ - - - - - - - - - - + + + + + + + + + + + + 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 index 4fc53255e9c..e658a11e462 100644 --- a/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-chrome-extension/logo.svg @@ -1,26 +1,21 @@ - - - - - - - - + + + + + + - + - + - + - - - diff --git a/packages/console/src/assets/docs/guides/spa-react/logo.svg b/packages/console/src/assets/docs/guides/spa-react/logo.svg index eccfaa961b4..7d1653573a7 100644 --- a/packages/console/src/assets/docs/guides/spa-react/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-react/logo.svg @@ -1,3 +1,10 @@ - - + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/spa-vanilla/logo.svg b/packages/console/src/assets/docs/guides/spa-vanilla/logo.svg index 2b0ce5c72c2..ce73eb180b0 100644 --- a/packages/console/src/assets/docs/guides/spa-vanilla/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-vanilla/logo.svg @@ -1,12 +1,12 @@ - - - - - + + + + + - - + + diff --git a/packages/console/src/assets/docs/guides/spa-vue/logo.svg b/packages/console/src/assets/docs/guides/spa-vue/logo.svg index d6a24d4938e..a71f46129a5 100644 --- a/packages/console/src/assets/docs/guides/spa-vue/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-vue/logo.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/packages/console/src/assets/docs/guides/spa-webflow/logo.svg b/packages/console/src/assets/docs/guides/spa-webflow/logo.svg index e5df00dfe65..c4ffbd645e9 100644 --- a/packages/console/src/assets/docs/guides/spa-webflow/logo.svg +++ b/packages/console/src/assets/docs/guides/spa-webflow/logo.svg @@ -1,10 +1,3 @@ - - - - - - - - - + + diff --git a/packages/console/src/assets/docs/guides/third-party-oidc/logo.svg b/packages/console/src/assets/docs/guides/third-party-oidc/logo.svg index 0958f5836e9..3ef36eaf00c 100644 --- a/packages/console/src/assets/docs/guides/third-party-oidc/logo.svg +++ b/packages/console/src/assets/docs/guides/third-party-oidc/logo.svg @@ -1,12 +1,11 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/types.ts b/packages/console/src/assets/docs/guides/types.ts index 0b18523c99e..4e6f0b29433 100644 --- a/packages/console/src/assets/docs/guides/types.ts +++ b/packages/console/src/assets/docs/guides/types.ts @@ -49,6 +49,9 @@ export type Guide = { Logo: | LazyExoticComponent | ((props: { readonly className?: string }) => JSX.Element); + DarkLogo?: + | 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/logo.svg b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/logo.svg index 29dc61d07a8..16f6b42fd71 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/logo.svg +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-server/logo.svg @@ -1,6 +1,4 @@ - - - - - + + + diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/logo.svg b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/logo.svg index 29dc61d07a8..16f6b42fd71 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/logo.svg +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-blazor-wasm/logo.svg @@ -1,6 +1,4 @@ - - - - - + + + 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 index 34b1fe74690..62a183eab90 100644 --- 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 @@ -9,7 +9,7 @@ builder.Services.AddLogtoAuthentication(options => { options.Endpoint = "${props.endpoint}"; options.AppId = "${props.app.id}"; - options.AppSecret = "${props.app.secret}"; + options.AppSecret = "${props.secrets[0]?.value ?? props.app.secret}"; }); app.UseAuthentication();`} diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/logo.svg b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/logo.svg index d04bf0477bf..3aa3a41c122 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/logo.svg +++ b/packages/console/src/assets/docs/guides/web-dotnet-core-mvc/logo.svg @@ -1,2 +1,12 @@ - -logo_NETcore \ No newline at end of file + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-dotnet-core/logo.svg b/packages/console/src/assets/docs/guides/web-dotnet-core/logo.svg index d04bf0477bf..3aa3a41c122 100644 --- a/packages/console/src/assets/docs/guides/web-dotnet-core/logo.svg +++ b/packages/console/src/assets/docs/guides/web-dotnet-core/logo.svg @@ -1,2 +1,12 @@ - -logo_NETcore \ No newline at end of file + + + + + + + + + + + + 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 4325a964589..9fcb5f7a336 100644 --- a/packages/console/src/assets/docs/guides/web-express/README.mdx +++ b/packages/console/src/assets/docs/guides/web-express/README.mdx @@ -31,7 +31,7 @@ Prepare configuration for the Logto client: const config: LogtoExpressConfig = { endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', baseUrl: 'http://localhost:3000', // Change to your own base URL }; `} diff --git a/packages/console/src/assets/docs/guides/web-express/logo-dark.svg b/packages/console/src/assets/docs/guides/web-express/logo-dark.svg new file mode 100644 index 00000000000..8ed50c5f2ab --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-express/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-express/logo.svg b/packages/console/src/assets/docs/guides/web-express/logo.svg index bda5f80dabc..db674128014 100644 --- a/packages/console/src/assets/docs/guides/web-express/logo.svg +++ b/packages/console/src/assets/docs/guides/web-express/logo.svg @@ -1,3 +1,10 @@ - - + + + + + + + + + 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 26e34567b4a..478f88527a2 100644 --- a/packages/console/src/assets/docs/guides/web-go/README.mdx +++ b/packages/console/src/assets/docs/guides/web-go/README.mdx @@ -145,7 +145,7 @@ First, create a Logto config: logtoConfig := &client.LogtoConfig{ Endpoint: "${props.endpoint}", AppId: "${props.app.id}", - AppSecret: "${props.app.secret}", + AppSecret: "${props.secrets[0]?.value ?? props.app.secret}", } // ... diff --git a/packages/console/src/assets/docs/guides/web-go/logo.svg b/packages/console/src/assets/docs/guides/web-go/logo.svg index 35afcc5967c..ba1ff3e67aa 100644 --- a/packages/console/src/assets/docs/guides/web-go/logo.svg +++ b/packages/console/src/assets/docs/guides/web-go/logo.svg @@ -1,6 +1,6 @@ - - - - - + + + + + diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/components/AlwaysIssueRefreshToken.tsx b/packages/console/src/assets/docs/guides/web-gpt-plugin/components/AlwaysIssueRefreshToken.tsx index 858c8fe0f8d..9a446392cda 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/components/AlwaysIssueRefreshToken.tsx +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/components/AlwaysIssueRefreshToken.tsx @@ -23,6 +23,7 @@ export default function AlwaysIssueRefreshToken() { await api.patch(`api/applications/${app.id}`, { json: { customClientMetadata: { + ...app.customClientMetadata, alwaysIssueRefreshToken: value, }, }, diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/components/ClientBasics/index.tsx b/packages/console/src/assets/docs/guides/web-gpt-plugin/components/ClientBasics/index.tsx index c7fae219f79..b5ab4e70ba7 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/components/ClientBasics/index.tsx +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/components/ClientBasics/index.tsx @@ -4,7 +4,7 @@ import { GuideContext } from '@/components/Guide'; import CopyToClipboard from '@/ds-components/CopyToClipboard'; import FormField from '@/ds-components/FormField'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export default function ClientBasics() { const { app } = useContext(GuideContext); diff --git a/packages/console/src/assets/docs/guides/web-gpt-plugin/logo.svg b/packages/console/src/assets/docs/guides/web-gpt-plugin/logo.svg index b153d5dee60..0c89371dfdf 100644 --- a/packages/console/src/assets/docs/guides/web-gpt-plugin/logo.svg +++ b/packages/console/src/assets/docs/guides/web-gpt-plugin/logo.svg @@ -1,11 +1,11 @@ - - - - + + + + - - + + 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 92625af90a4..80f83361dc6 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 @@ -63,7 +63,7 @@ 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} +spring.security.oauth2.client.registration.logto.client-secret=${props.secrets[0]?.value ?? props.app.secret} spring.security.oauth2.client.registration.logto.redirect-uri={baseUrl}/login/oauth2/code/{registrationId} spring.security.oauth2.client.registration.logto.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.logto.scope=openid,profile,email,offline_access diff --git a/packages/console/src/assets/docs/guides/web-java-spring-boot/index.ts b/packages/console/src/assets/docs/guides/web-java-spring-boot/index.ts index cf1e468a2cb..f40d9bd7c7e 100644 --- a/packages/console/src/assets/docs/guides/web-java-spring-boot/index.ts +++ b/packages/console/src/assets/docs/guides/web-java-spring-boot/index.ts @@ -3,7 +3,7 @@ import { ApplicationType } from '@logto/schemas'; import { type GuideMetadata } from '../types'; const metadata: Readonly = Object.freeze({ - name: 'Java Spring Boot Web', + name: 'Java Spring Boot', description: 'Spring Boot is a web framework for Java that enables developers to build secure, fast, and scalable server applications with the Java programming language.', target: ApplicationType.Traditional, @@ -11,6 +11,7 @@ const metadata: Readonly = Object.freeze({ repo: 'spring-boot-sample', path: '', }, + isFeatured: true, }); export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-java-spring-boot/logo.svg b/packages/console/src/assets/docs/guides/web-java-spring-boot/logo.svg index d7256ddcf41..2d4fe64ea1a 100644 --- a/packages/console/src/assets/docs/guides/web-java-spring-boot/logo.svg +++ b/packages/console/src/assets/docs/guides/web-java-spring-boot/logo.svg @@ -1 +1,10 @@ - + + + + + + + + + + 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 5c6196036df..1ca7e409158 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 @@ -26,7 +26,7 @@ Prepare configuration for the Logto client: {`export const logtoConfig = { endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', baseUrl: 'http://localhost:3000', // Change to your own base URL cookieSecret: '${generateStandardSecret()}', // Auto-generated 32 digit secret cookieSecure: process.env.NODE_ENV === 'production', diff --git a/packages/console/src/assets/docs/guides/web-next-app-router/logo-dark.svg b/packages/console/src/assets/docs/guides/web-next-app-router/logo-dark.svg new file mode 100644 index 00000000000..69d2a9cd56d --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-next-app-router/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-next-app-router/logo.svg b/packages/console/src/assets/docs/guides/web-next-app-router/logo.svg index c1db01c06b7..252c045953d 100644 --- a/packages/console/src/assets/docs/guides/web-next-app-router/logo.svg +++ b/packages/console/src/assets/docs/guides/web-next-app-router/logo.svg @@ -1,10 +1,3 @@ - - - - - - - - - + + 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 badcf966507..2911141745d 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 @@ -2,6 +2,8 @@ import InlineNotification from '@/ds-components/InlineNotification'; import UriInputField from '@/mdx-components/UriInputField'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import TabItem from '@/mdx-components/TabItem'; +import Tabs from '@/mdx-components/Tabs'; @@ -31,9 +33,43 @@ 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'; +export const { handlers, signIn, signOut, auth } = NextAuth({ + providers: [ + { + id: 'logto', + name: 'Logto', + type: 'oidc', + issuer: '${props.endpoint}oidc', + clientId: '${props.app.id}', + clientSecret: '${props.secrets[0]?.value ?? props.app.secret}', + authorization: { + params: { scope: 'openid offline_access profile email' }, + }, + profile(profile) { + // You can customize the user profile mapping here + return { + id: profile.sub, + name: profile.name ?? profile.username, + email: profile.email, + image: profile.picture, + }; + }, + }, + ], +});`} + + + + + +{`import NextAuth from 'next-auth'; + const handler = NextAuth({ providers: [ { @@ -45,7 +81,7 @@ const handler = NextAuth({ wellKnown: '${props.endpoint}oidc/.well-known/openid-configuration', authorization: { params: { scope: 'openid offline_access profile email' } }, clientId: '${props.app.id}'', - clientSecret: '${props.app.secret}', + clientSecret: '${props.secrets[0]?.value ?? props.app.secret}', client: { id_token_signed_response_alg: 'ES384', }, @@ -64,6 +100,9 @@ const handler = NextAuth({ export { handler as GET, handler as POST };`} + + + diff --git a/packages/console/src/assets/docs/guides/web-next-auth/logo.svg b/packages/console/src/assets/docs/guides/web-next-auth/logo.svg index 47b9c27d79a..5bd92bb887f 100644 --- a/packages/console/src/assets/docs/guides/web-next-auth/logo.svg +++ b/packages/console/src/assets/docs/guides/web-next-auth/logo.svg @@ -1,25 +1,27 @@ - - - - - - - - - - + + + + + + - - - + + + - - - + + + + - - - + + + + + + + + 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 961465f691c..6cb14c8c16f 100644 --- a/packages/console/src/assets/docs/guides/web-next/README.mdx +++ b/packages/console/src/assets/docs/guides/web-next/README.mdx @@ -28,7 +28,7 @@ Import and initialize LogtoClient: export const logtoClient = new LogtoClient({ endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', baseUrl: '${defaultBaseUrl}', // Change to your own base URL cookieSecret: '${generateStandardSecret()}', // Auto-generated 32 digit secret cookieSecure: process.env.NODE_ENV === 'production', diff --git a/packages/console/src/assets/docs/guides/web-next/logo-dark.svg b/packages/console/src/assets/docs/guides/web-next/logo-dark.svg new file mode 100644 index 00000000000..69d2a9cd56d --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-next/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-next/logo.svg b/packages/console/src/assets/docs/guides/web-next/logo.svg index c1db01c06b7..252c045953d 100644 --- a/packages/console/src/assets/docs/guides/web-next/logo.svg +++ b/packages/console/src/assets/docs/guides/web-next/logo.svg @@ -1,10 +1,3 @@ - - - - - - - - - + + 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 b274d4849e7..29c58fa6663 100644 --- a/packages/console/src/assets/docs/guides/web-nuxt/README.mdx +++ b/packages/console/src/assets/docs/guides/web-nuxt/README.mdx @@ -35,7 +35,7 @@ In your Nuxt config file, add the Logto module and configure it: logto: { endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', cookieEncryptionKey: '${cookieEncryptionKey}', // Random-generated }, }, @@ -48,7 +48,7 @@ Since these information are sensitive, it's recommended to use environment varia {`NUXT_LOGTO_ENDPOINT=${props.endpoint} NUXT_LOGTO_APP_ID=${props.app.id} -NUXT_LOGTO_APP_SECRET=${props.app.secret} +NUXT_LOGTO_APP_SECRET=${props.secrets[0]?.value ?? props.app.secret} NUXT_LOGTO_COOKIE_ENCRYPTION_KEY=${cookieEncryptionKey} # Random-generated `} diff --git a/packages/console/src/assets/docs/guides/web-nuxt/logo.svg b/packages/console/src/assets/docs/guides/web-nuxt/logo.svg index ead151fc7ad..a2f1522d636 100644 --- a/packages/console/src/assets/docs/guides/web-nuxt/logo.svg +++ b/packages/console/src/assets/docs/guides/web-nuxt/logo.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/console/src/assets/docs/guides/web-outline/logo-dark.svg b/packages/console/src/assets/docs/guides/web-outline/logo-dark.svg new file mode 100644 index 00000000000..34cb2b54aee --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-outline/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-outline/logo.svg b/packages/console/src/assets/docs/guides/web-outline/logo.svg index eadcc194278..15f815789be 100644 --- a/packages/console/src/assets/docs/guides/web-outline/logo.svg +++ b/packages/console/src/assets/docs/guides/web-outline/logo.svg @@ -1,5 +1,10 @@ - - + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-passport/README.mdx b/packages/console/src/assets/docs/guides/web-passport/README.mdx new file mode 100644 index 00000000000..70d294ae5df --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/README.mdx @@ -0,0 +1,163 @@ +import InlineNotification from '@/ds-components/InlineNotification'; +import UriInputField from '@/mdx-components/UriInputField'; +import Steps from '@/mdx-components/Steps'; +import Step from '@/mdx-components/Step'; +import NpmLikeInstallation from '@/mdx-components/NpmLikeInstallation'; + + + + + +In this guide, we assume you have set up Express with session in you project. If you haven't, check out the [Express.js website](https://expressjs.com/) to get started. + + + + + +Install `passport` and the OIDC strategy plugin, `passport-openidconnect`: + + + + + + + + + In the following steps, we assume your app is running on http://localhost:3000. + + +First, let’s enter your redirect URI. + + + +Don't forget to click the **Save** button. + + + + + + + {`import passport from 'passport'; +import OpenIDConnectStrategy, { type Profile, type VerifyCallback } from 'passport-openidconnect'; + +const endpoint = '${props.endpoint}'; +const appId = '${props.app.id}'; +const appSecret = '${props.app.secret}'; + +export default function initPassport() { + passport.use( + new OpenIDConnectStrategy( + { + issuer: \`\${endpoint}/oidc\`, + authorizationURL: \`\${endpoint}/oidc/auth\`, + tokenURL: \`\${endpoint}/oidc/token\`, + userInfoURL: \`\${endpoint}/oidc/me\`, + clientID: appId, + clientSecret: appSecret, + callbackURL: '/callback', + scope: ['profile', 'offline_access'], + }, + (issuer: string, profile: Profile, callback: VerifyCallback) => { + callback(null, profile); + } + ) + ); + + passport.serializeUser((user, callback) => { + callback(null, user); + }); + + passport.deserializeUser(function (user, callback) { + callback(null, user as Express.User); + }); +}`} + + +This code initializes Passport with the **`OpenIDConnectStrategy`**. The serialize and deserialize methods are set for demonstration purposes. + +Ensure to initialize and attach Passport middleware in your application: + +```tsx +import initPassport from 'src/passport'; + +// ... other code +initPassport(); +// ... other code +app.use(passport.authenticate('session')); +// ... other code +``` + + + + + +We'll now create specific routes for authentication processes: + +### Sign in: `/sign-in` + +```tsx +app.get('/sign-in', passport.authenticate('openidconnect')); +``` + +This route builds and redirects to an OIDC auth route. + +### Handle sign in callback: `/callback` + +```tsx +app.get( + '/callback', + passport.authenticate('openidconnect', { + successReturnToOrRedirect: '/', + }) +); +``` + +This handles the OIDC sign-in callback, stores tokens, and redirects to the homepage. + +### Sign out: `/sign-out` + +```tsx +app.get('/sign-out', (request, response, next) => { + request.logout((error) => { + if (error) { + next(error); + return; + } + response.redirect(`${endpoint}/oidc/session/end?client_id=${appId}`); + }); +}); +``` + +This redirects to Logto's session end URL, then back to the homepage. + +### Add to the homepage + +```tsx +app.get('/', (request: Request, response) => { + const { user } = request; + response.setHeader('content-type', 'text/html'); + + if (user) { + response.end( + `

Hello Logto

Signed in as ${JSON.stringify( + user + )}, Sign Out

` + ); + } else { + response.end(`

Hello Logto

Sign In

`); + } +}); +``` + +
+ + + +Now, you can test your application to see if the authentication works as expected. + + + + diff --git a/packages/console/src/assets/docs/guides/web-passport/config.json b/packages/console/src/assets/docs/guides/web-passport/config.json new file mode 100644 index 00000000000..c81c3a59b30 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/config.json @@ -0,0 +1,3 @@ +{ + "order": 6.1 +} diff --git a/packages/console/src/assets/docs/guides/web-passport/index.ts b/packages/console/src/assets/docs/guides/web-passport/index.ts new file mode 100644 index 00000000000..bc3dfd037bc --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/index.ts @@ -0,0 +1,11 @@ +import { ApplicationType } from '@logto/schemas'; + +import { type GuideMetadata } from '../types'; + +const metadata: Readonly = Object.freeze({ + name: 'Passport', + description: 'Passport is authentication middleware for Node.js.', + target: ApplicationType.Traditional, +}); + +export default metadata; diff --git a/packages/console/src/assets/docs/guides/web-passport/logo-dark.svg b/packages/console/src/assets/docs/guides/web-passport/logo-dark.svg new file mode 100644 index 00000000000..a9337f4c359 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/logo-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-passport/logo.svg b/packages/console/src/assets/docs/guides/web-passport/logo.svg new file mode 100644 index 00000000000..f11c4f4d8a2 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-passport/logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-php/README.mdx b/packages/console/src/assets/docs/guides/web-php/README.mdx index 6dfe1d3d7af..32ea660d4fa 100644 --- a/packages/console/src/assets/docs/guides/web-php/README.mdx +++ b/packages/console/src/assets/docs/guides/web-php/README.mdx @@ -35,7 +35,7 @@ $client = new LogtoClient( new LogtoConfig( endpoint: "${props.endpoint}", appId: "${props.app.id}", - appSecret: "${props.app.secret}", + appSecret: "${props.secrets[0]?.value ?? props.app.secret}", ), );`}
diff --git a/packages/console/src/assets/docs/guides/web-php/logo.svg b/packages/console/src/assets/docs/guides/web-php/logo.svg index e9f2a895255..8ca1c32c63f 100644 --- a/packages/console/src/assets/docs/guides/web-php/logo.svg +++ b/packages/console/src/assets/docs/guides/web-php/logo.svg @@ -1,5 +1,12 @@ - - - - + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-python/README.mdx b/packages/console/src/assets/docs/guides/web-python/README.mdx index 52fd2300a3b..1616da8d390 100644 --- a/packages/console/src/assets/docs/guides/web-python/README.mdx +++ b/packages/console/src/assets/docs/guides/web-python/README.mdx @@ -32,7 +32,7 @@ client = LogtoClient( LogtoConfig( endpoint="${props.endpoint}", appId="${props.app.id}", - appSecret="${props.app.secret}", + appSecret="${props.secrets[0]?.value ?? props.app.secret}", ) )`}
diff --git a/packages/console/src/assets/docs/guides/web-python/logo.svg b/packages/console/src/assets/docs/guides/web-python/logo.svg index c33c339ecb7..1d2ba15af8d 100644 --- a/packages/console/src/assets/docs/guides/web-python/logo.svg +++ b/packages/console/src/assets/docs/guides/web-python/logo.svg @@ -1,14 +1,19 @@ - - - + + + + + - + - + + + + diff --git a/packages/console/src/assets/docs/guides/web-ruby/README.mdx b/packages/console/src/assets/docs/guides/web-ruby/README.mdx index 63d9e9e5470..89f05318152 100644 --- a/packages/console/src/assets/docs/guides/web-ruby/README.mdx +++ b/packages/console/src/assets/docs/guides/web-ruby/README.mdx @@ -41,7 +41,7 @@ In the file where you want to initialize the Logto client (e.g. a base controlle config: LogtoClient::Config.new( endpoint: "${props.endpoint}", app_id: "${props.app.id}", - app_secret: "${props.app.secret}" + app_secret: "${props.secrets[0]?.value ?? props.app.secret}" ), navigate: ->(uri) { a_redirect_method(uri) }, storage: LogtoClient::SessionStorage.new(the_session_object) @@ -64,7 +64,7 @@ class SampleController < ApplicationController config: LogtoClient::Config.new( endpoint: "${props.endpoint}", app_id: "${props.app.id}", - app_secret: "${props.app.secret}" + app_secret: "${props.secrets[0]?.value ?? props.app.secret}" ), # Allow the client to redirect to other hosts (i.e. your Logto tenant) navigate: ->(uri) { redirect_to(uri, allow_other_host: true) }, diff --git a/packages/console/src/assets/docs/guides/web-ruby/logo.svg b/packages/console/src/assets/docs/guides/web-ruby/logo.svg new file mode 100644 index 00000000000..99dbe45db06 --- /dev/null +++ b/packages/console/src/assets/docs/guides/web-ruby/logo.svg @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/docs/guides/web-ruby/logo.webp b/packages/console/src/assets/docs/guides/web-ruby/logo.webp deleted file mode 100644 index 991a3aa740358db07ea776a35e844dec3c20e870..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33844 zcmeFZb8w_Kd8 zbX8Z~y6&rgysE2}rNqT)g8={yF=0h@MNUmv004me+gkwg_eV%pQIduZ006-ov|Hz# zdv~T-KBXZh?+rs3p=){u+zw0FtjxP0ct=*RAEUg)zU_Q`j7h!vQNSQ8CL5bo{kTvb znp#_`KW`XhUNVa^J6pl`K8tm&$K{bmAOuwxM#`R%DFYEEBqA+(kS)mqF7F|2DJM>F zP2YG-9zBZ=fHPLu=>!vMm*oW}bpMjPD)qDGio*YdT6ZPzar1w}^D%bG&T6ssycvIj zJq!PXUjRpbawTNDGuGEBlec4AC}-xBFzF8ed|c^xM#L4W+hJMAUe7zejSDJ=?|`sT z9MAOd3Jl23SJ3v%x4ip zjw05oE5FJyhyXvlOuS=DgV?}i@dD13XEBgVrcqyIJ`!!h66$D?&dUy?3sb%~Ffrr> zLPVQQg}B_b4BOH$1_>d>Aj_Z_78Xc~X({}~MNlaO>ZZj8hj&+5<}8o^;&5x$P-q0#g@hHwQZzr5}23Cl%}m}L_3vLsEO|1!Zd_D3Yj$fQw6=0W4FF9iFm1@ zr8`$gp0Pz$#*6A>uH3O2C5E(E5{y;@PvtIkXNFGmBLU}v zuhjD}ZwRXqX_SL@f}{1gk(0vzeOBUk*i>Bv1*xSk%HklRTsT2yxhrJYBFp)!fUdFwNH5DC8#B15uFiY<8;Z5b*%EW5I}M3V&b`@Hhl-k}!V4B9{Dl_U0TE#&~= z2^U!ku#NEp&*}KYUU=9{F!#Bu41+ixW}aj34=6`mX(7Ek^P*bPes|G6b)}U<*%}Pb)u<*g6Y+xnK+MgS%o4Dp!lW#G3?LNMPMM9P{Lt1?q77@ngP+I$Y+D-(4M0 zLboy-P5VhL?kI+mk+aJn7V~_Vg{z4J&GGhQYOAXKBOD-lO0ulCkt4R|7;_JG`xd% zfklS>D5^2$U#R)wR%ytJUrtu+0|;YEZIht?(i59}PnR|PSz~-q9HD$1jWdC!2NN!< z6d#`ncgl2?r$p7K0QYVpLW}> zj=jFx@zcT1?U#NftotW|aWtIeJ4h$NL|?1klR!#cmAh$w$Zt{{m~htK9F6tUPEy#x zXRTx#P)b3&E_u!Q1+%Q*epN*N(l+J1uglqda>^HYRXj9r1A(`Zn>Ho{>~`KI=WNF% zj{=cjbd}$R32S}6Nk`+&`w34QvWNR#Cl7si%H>xTipoo60GZL8MT5Hoe>8;xl9j)s ziy5e*`&gXlk9nJ^Q}`t|Qve#a*ZTZ0pEJvp&8_xre94aa2kS@s!u?{2H5=9Cch4=( z)r-FB`66F9iuUL4Ws}~Tg+H0Gr<$ImQo3IBbGeK$@J^+4?DX2$=UFfj?|%W^5Z_pV zd&}Qv>9O-|H~j9>`7?LEaQLA$IpkV0`NHjZRL%~z*o7dnl)=kCW)-{9W!3cPDuP+~ z+?ZyD!ZoMSWd2BA_Cag5Ksu#g4*8LlX`r#%@F`=wT8Z-{sb#G`#cMpsk@Jkke64PY zHJc1<>Sb#>`Me+COLm~gd7VEU*m5ecCM`{#<;hkuVBb|-SrijulH0go{#I6A^Z^d# zH>~R2tfs)l3>EwJd{~JG{k*F1RvR%);NCSQM%WMGU?AqpyWgsHq5Xn_JPiP>Cc`e5 zxhV<6yDy+Pm#X8o+1}|Zx+deT=M4GeqAa});FCOUgG7ke=wfwV|C5Ye!m1QS8xuV9 z23H?qeyzAu7!0@V>sphL#!r$sYGmWHQ|Xtzd2wdJ$oZDF989)^WF(;|&&IPUOfShb zN@SDOBS-I>D2zEX>N2KRv8ZaT${;)@+_T3$0v?r`fZGWO0eVeqlLD$IPJSeKEza5Q zAIDy|tDv#A&&1X`@^KQ|dWKSL?Q=Eo_F*0HqE9wCa!)qg^kpDw_E}~M?<%o0nS!Zm zB)e|=J6}9R^2V>y=-BsD>^E>AOyucWK7Y2@%je{Deo|gfP>X?|Ijn(kGx1_k-hyys z_rq?+q)ALjggH?~rM4S9GQRvrcg86JKlQ5{AJ5gu2bvX=YxZ}v-m zNF(g*B}xoD&YvIIJ0vMO$}plkyny`oY35(-SCKGeTz7^TH91(hjmzU9a$$C}mGcAm zY6QFBel^v?*IiDndTESN{Ma6vd9+^=qfzic|)^{3;$_wrs*|1Bl_uH6WE1Lrsek6d{At|BVVo5 zVs|iQen^3^VezU6X*t0)EXCVe^i)z?G{!fH_}kq(^&)+!;~cPztrR;UX;%L&8o>oQ zEywZtgpX%iP_@bk1a%XFw>vs;o#`z*rfw4fckD;6d1V`>tk>Uc$3(-=g6iU^yE3D9 zHNo){oFmws-e*v|4`q9Ndb93zIpl{i-f>3%M}nB3c6WtF*x_ILkSiUYJ{u@ytV=Ra z(67}c?3w}MLAUebLH!n(n*PLdG9RBvyU94NtABKo90d;9bjx_mQ`Rm!A-4h1rN0zn zUk=3l_+>zYy8aUE{w!bjRM8oH&%2F44UwBRC29IFy9u#qc$*4yJ>ZJEuD1# zZ@c&r`w)>ljcB$8-)IBiO}>re_C7RkQ^aP%Os&HX#mQOAp=n%YPJ{oPrcyw*_qw5( zd0)GW*b#M-@x+|yaS#_i?zgid>3iFWsE@7f%e%k*EcDT{;O@~;R(*tG=^q8h-L5v_ zW=zo3+aH3z0dIWTftq1B&w#IkQJZZ5V#ml{-c0fmzVto^o&Ck$6Y3tN@~L13j^oHx z7b!8rK!}4jch=}FX>P7NFcj;Zeo07g(2~zwSRqSmn<-n&^6e&{hJJIFsO|A=&HOm3 z1H(=9r;D3D7PrZK9KJ$Q-{EG9kb|l~lN)Qt&og|>IOlseMw^uh{Ke08W+KDPQ4`9} z`65db=ZfUU?O1MUCEe3hrSehoW@6K)TV_a9u9SqN>+}Of~a)PGX+#S6$MvRBqEb%R$+VFkH zj17cuRC`)lGM6Q7^UA#eGWRWM5|XwUtTT3==lcS9Zfs36Ypdrb&>_WkOCpBvmMvj7 zlxIekg)FCQeN^ynsv-P=%{*&cpWyEaT;}Em?Wz|Eueyz@!Si$1G+~0|0?jKLMC`2j z0`&4+Pz|k|b;gM5o@Nx)bTS4EI0SFrvbyJ5PWVBdy`bAKP0kj(bYI$1y0(JvUpRg_ zH6IHEjoP5yZqI)^txAVbx%9+;Hd$1Hr0-i!x z_8Fio*CTk%;11kj-x>-y+(lUmw_Y#B%>;i4>0r|69mh4L!0CNsut5l6_d&CArJo5U zrYbrJWAB{)ah9|AJVQR`h5391I>k*^^hxfZs(vQCr=Y6Qn0re#I-6;nl zCpvrXgLwp&HuQzpa)WHoEtusEq-FVk%q}p?V`=OFc)u&R`lF7Qa01+uRJHl3eyBn9 z$|xfRq8m5^-UpNv)u$L5#q;dp|8Tt}5EtCELbyIxnaV?Ez8 zvb-u%`>s=3^QPC|^ZXn)D{I_c@Lw*z#0*cw{FETjax}3EzCXrPimIeg5rR8 z0j2^;X{%7ejhl>qb>F0~HYYKw`Y*Tmc@4r*cTxu~j=QM3f#F1sn9Ob{8TwT!50kQ= zS)6jB;&59p3#^#DPXc}3Ilvl#(pPGahfgYFuqB%HA{X|=CHaQZ&P^M7Lo!xRssTGr z9N}pM0FAHt0~a3JCoO5TO9wL9C$cgO{Xr2Lj*lq`;_HcT%{Sc#@qI@@Z>=1mOcStH4`0{PbpeBRDf-q{Mrj8ZEZ6olQF8+9j z55(@&6h&IAk4z>gaVaJ^G*E^l3cPyo$B!l-<~~g!8uEOo-V&|Ame`>7^WvlwIw$t9 zpd6^|47mL>9@rDDaSAL7i(KUyZ^DHLr+XZ*w!r9y8mS2av%Cbug-0Kz7bK874k`#O z<9iLzXP7K*t%<3zNY|A{(|94m14PS3*;=+Z%(c zlNK-Rag*FBo(Kpo=G5=}NC8?DBP$R4^L7l)ue2t)Nz-t7OM0%v_uLP+RR{V%`(E@V!XN`J5p6;RtyiHe3SJVCHmz}axVL7lSr$0uCq`n(t)5D z?BVY0&@Bzh-O>?9PKsxEPivCA<6PzqK9fh;nSJ%n2R6*-MRsJs4A|@EM7Il&@u^R> z9Ga!$_8CBG{jW=&u&g`=o%R&j7b(2+isX79gxSkNO^wB;pi^8m5msN5Yr3IyQUSS*b$aUbbkAw%=^Z5$cQXDb- z?Jc5~mV=!U9`7+{y`mhHmr$pqXT1^~>>+l=ZxAdw^eNS& z+76M(FAZZg-$vL}1UnlCPH-!*tI{b9stQLoO0Bg$;^6B_s67(8)x zN@9Fa(v=*j%ukJ0=oMs&`y>fbY)LU%H1G(GTVzQ#iU%UBPbD@lb?6~Zom${XIvd-VNrK(QKTco|=qgs@lkxd24OP68hUDGzE zr?5lM4AVBs8+ZfF-0GBr0DL3NW>vvnPuOG3a!pN2Uu%eg$((d{iMfqZhmtc+jj!J% z55e_?s~=s+fGpuwg}1D@q~7#Qb_qGesn^IVie5`~69VBdZ2c^mX78i2nZXvI;~SH* z>%kOBW&|cBbyuBZ%nXdE6=p|9sh*h`?4wgXTI5L?u%<^0v@DSs1r5&hs*zKp zv@A)P>{0R@sapMVA&K+Gb!T4oky29x8B{;WRbi*NGgc+!Ga)SKCs~tJ5`Op$GgCP_ zMZ!#3(9YwZ=@M~(q;e(Xw2Vy_(%$fRQ72@6G)#Ej7VRI_(mG@FslHix_b`+C8J-DwEiiHflbs@p=)x><=KN%ol)$GPb8(gh ze&ADeA-{KW5Q(=jPJP z`S;3|t++DGhptrX>qJ#7&8Zc{g3WS;8XG-fOR8)Sfw~3MDJdbmjR9rSrUy_tRHW1@ z>fMJx>4HEiYKLpt`11S6u_V@{ih)vAQp@6k-9lg?Q5Oq6C~{gudYP^W3;irp>*0iU z>6PD*7`&2HTe$gil-zu%7_PQsyd#-`H`xJv1~x5IeZiuQ&6n^Llp5c}xg_(N_|(+E z&O}bc+DI97UBwBsj5c-UvDP!hLc5A|O(J3nbjZnf2}$7&IGmXw6Ja%K3LVcwevtBQ zH3EqjR5)6v945@Mz5(qE@paP4%#VfZQr#AUs2Cg{B}qQ1k5 zC(i_%lBSNasNK^b(pU|VmB@-$z~|UCt6jg*mRd#11V!)P$l};G28P58nu9E_nAy zG(Rm<;=Mf%xg|gaIq7j;NcX*Ro@!M<2(8D&6ouahSRQz=0J18SqD=zRygzLLjNger zp~ahc9HI|{3DS}x9sK&Fum^O{Mh0BxF$V7!4*ghVz(WC4sKEsK+f!sXPQd^cQ80rr zt~JG31-PhR!i<>}i3oa7CUu7)1Z`9S{ku$&mZ>~U0AivJirmXjljLELf}Ez8fpZB$ ztR307fLRP&4J!Nx6uIU?Fv2C{W#TzUlG)%a0>HJo+3>*pcb4oQ@Rf(@Z`PiikiTRV z!l3=FV^^xEcQ|7$6wR!^Y=8kJUYR`EnwzvIyX>f6&}tWu+q!VjrR z;ry+mS5k0BJpwHCU3&p#q49JPGDB~oH#saVe$I_=$dj27hcZX-f^~!TtT00dqDcp3 zAeMG@=&pe~V0W?MOB zrdUhvAjnJ`{NlLq5=wFue#mYPK(4K+=Z|5Y4Y4F340}5r zr|eRauqs&j1|v7V>Z8o}3X@#Au*- zW*RcYQDY(+1wTQF5iTkeG=HtwBHKBTgCJAY$SX?YM5Z6IGJ_z~t)_)%6dBRYe|rKK ztTHW?W-VczFwcmhgz&GRjif+x%o<_!GeQSc5DR1o?)JxzvT~q|zu)l$MifY4>6>M& zk3tB(-KPd4qlQYP9}0a1nQ5wS*-IMoW0_<@1Isg8=U2k*X9Yt^JpjOTJ2;DqUEwfklaiyJ};0PUL=8l%y|j4&)| z!y~21M(wo+)f=x&thVPsYkphQuCLkF;+9r%DwFQoYj&zEaxyPGi?lOy?~-hl%}w*= z=9#HW+1B^4;k%V}Ld|!e!YgLFCMTA-gRV0kiZOHMNi=F9qGtNXxTt9l*;9Q$(+stEP@7b4a(-7|FydrI+ z&4QYJb?5U6^~Tu_iIj|yEvlo+N^o6nx0NQZ!$hqN_{vMRi4sHxwasA{vwSI&@n+mY zgqr=h(VidFYo0n{TheXp6X3;k1RN)`6Z_V;zf6*FuYG(Y7h&A(yn|ov^kkGq34ful zd3rRw{lc*9O2VpMHtO=;+1MM^+-nszu}9t zoOPqa{dA}6{L_KbDBO9uSobDwJ6?QH@2%}HS6FCk*lAJ^N1Hdsh9D&Z%Y6|`_oe@< zG~S3e&uV9Lu6cK~Ek8)!Kp$zX!KgXu?aizy3$|kWs<$Z%7$~+$EqsF(?Wf4D3zAG% z6rpX7#7X@C#ambGRZ$3HaRabXa&qKXU*1*LV~YyZ)#Oo;?4H_OC|a zjOm$n7if^M0s(gRGgGP-Ep5UW#PCaob?00Sf;%BD&KYhkk@oFTfl@{+$3xK4!qNRh z*K!G@UmhiV8l{nU@$ON!P2q%tzZ3k(Ob8U*{n*cRcWI<~I+WsqPFT_MajwLx^Z4A| zrpzI8l*uwcC~F)8xm`j-6`r(W-_Jx{>_4c%GNnz#sAY?>_eu-?2?KYUGZz*l#;>1q zdepz5UIXiBn|Vgs1c_-1g1L9^!#FF)Yc&^pxGc$^FfLn;tCEax%7j8`iWG+MS55Lw zDnQhuYBg`8!?nS9>KYo5PdZMz+3G7qoGHnDWA{19PntV-jc?GjHeo8r ztrj>+K$Jaxo@3nLmo&xX7B?0aA6q}Y1yO#cy96}4Bvy5UJ?*r*{MLzkx?y`tth!`f zck#1@Ueb)LDM{Jp=$jAUIo6G>ZuI!!uc;UInuu}o>nTOt(r;%hTaK%o%t%+UO2+FM zBWFyzK-={K(|dzm&;FMBr|s|~;f|#-^CMJ+BlS!S)5iJmVNKil<5$T;&&?dMRa#TV z%ar@7gvonl1+Pjv72B2ouV@H2*?`F-$LUJ9s+=iu=V>#yiK0e^Z>2Jg2zxgi--aI- zzp`BebA1S!421YQVtnN54T9g@e=)VJR3qoVCHQW&>L3KWalgE-+A}>Xoy`|-=>vl? zn2y~$yDEs9nBUu0^#`HtP(ScyjZ4v>eL+631Xjwm#WRHkTUW~o3w^}|`H0yg=e9`+ z_cUG>M6DDUXL`<5_=-3C1od!jQA-Ub1&1^{&~}>OsE%vh$|L8Wl@zVZm(c%y;wtno@sPc2dxeuVHldlsvAs0kMS%CaHIf>!bQkxV^HaY}&E++Cuij%tw>Y&^tR1tuD|sS+p!c>-*FpJ%rhfrGfxJThl12W z>FmmIZnvzjZlTl*@)6LlbWoLwzd8^hJMhrGF3GBjr~SkzD41;W-!7CjbJ4NHH?}i} z>f6C8VUE?e!&cl$Iv0y6*0X;)YS47{>^oT{G3c5&mBY95ox(>yZau~L%Huh1DX*E%wzTrkMX3Bfb4PdI^sFFjOJkbADXqYSzDE9Q zQR(lC76*SkB_q)aJ?&INkz~dcKaH2f_{<9|?76wJh5MyjfhhW|v^d4DYG(AbKgI0Max48|B=%&rZub*8?GUVfINZnAHqgEPn#ub1TNhxr^_LsJxw7|4Mp_XQW}W1WR?V>5 zWO;J*ud{HxA0{-7nQ@Vn>XT5bX|!`$9KZbm{b#7W-`20-;Da#o0=yJUz1ioi2;iTq zl;L9p1~pQ`adyK@X|g8&Jd6eGH*SA}h&>7(_r{#zQFj=>&x7z6kd;vh3s*=gs{K@) zXw<}hv+V_UTzf2F@A(9+m6Zv)*ZqSLkR3>U^w2&{HI}|pj}w2AST61~Oq7W_F(y(c zG*I9wcIr%S3i?6MI~SE zqO1Pc@%Gf3bX^u-JD$^e7phU-{*Pr1%>RWSXOv&1^Ar_g32%ZxC}Ih{dKxQkvb}eYEw2od*rOQV`!}8+~tFj>BI#{asvD|;e#sb}bsyQuYpLsf%(LIwVK zLu2U69|mMks&#QR9pT2R`3si}gIu}UM9b}8mp~&~y{T`F-Po)L&wP)=(Hxarj5S-; z`Y%(NizZ#D$tSsY!PETZS0!!x`?ih$eI`?)36hUT{}f^YXSc!Ef(X^~k;$ns_{Xr{ zT(L%c?t(xBbd~KK=@bw=fykiTOv1tQFLo*tM0s6jvOiB&+WVc(UW-?_(T@MV@^`q4 zqmphH&%@5B9Aaxx)Sydl;D27`UpHk+Z0HDu$Ia=YSvsr(zaF~(R5LOC$GN`x3OzOA zzaDFXYQFx+hH3LQu`6w(`fSIl3(U>T{uhmll5X2i3o`Z|l#Iq}6}yMuLyFfCwZ1V6 z{2zD}B%Xflr&Tl#-5+fn{CG@eL%Yo#)Y^#T!C?GJ&cT5Ev^fxQ>{x1f)-4T`(kyURBy)1DFsf4^qG27+IoFZ|9wQXa2p>dR?FO_+#$%%=gSXEcqkVb_eG=9DZEC=i#9*fWDjj zgnM^LRH)+%;jQcW`1p8Oe<}R<*u?w19JLBL&4Af}kfYCFip$%nKf>)M-e z^m=Mb`_%M)seM{Fp^X9AdH5?AZpC272 zpL*CM9lL$6?9+fX`U^|&bD95>D+$}UH7mvDLJjocC>T_-!)i*D- z@WwEA!Lj)QJ@W5$epP>soI>V3KL3AnR0;|j98FvAw@`MGkK2?8gs2f;0)^%$#pZnK z^3};7Zg`DD=Hx-6B%SucA!Xo9fO7Ph!Fp?i`&psYBUDmjDNhpG=KiE!wHIG z%JT6Li^szHj$jj~3U1BXU1jgH9RK;U!lWt)N~3Iaw)!!>X09<}kwn_|e~28dPEKWz z$A%CZ9|I8vAE~XXg4_o<$9LN66Jk+Fo)isv@;TW2>WgVrT#C|$;JXd%&jm4?xupIA#a|T@;{FZCwkj_Z5JGXZF zIi#rU1q;Mb@ju7D36!}O*lkz=-}Dzc)Gf@A1)hH#Uz78F2$-@$3Ux2A{P{j5S#ro&Vv2wpzvlVUjPqu12S7yp> z1d$4;-1RWb3>tcdkN<&6^z9B|U1{;9;TaB-pHOXC1qp)C6=AG(^^>EXuj6yGA~ZL- zJ{VKasyT;N(nf*&8;l;()j@oOnrQ6*$bqWWZeW)fl7h+g)$;ZgIX~``n>(O!V!9^9 z#B%tlcFrp8pW(kjU6lK`=7S6+B7v2S?ZkP|y1;>k{v*3~!_FQ=!hI#cdpc_;@x_tj zKxOT)RYYdzmmMfnhprTVKHD}*df5h;lzPUcSGD`0kGU&5`I4s^kxK_Umb+@pY4A0G=S*TF;}v(2i?o~ z2d?q&?Mulwkp3JMR2Iv67fw6*CnCld0;oWL04Lr!)ZpKv<|!piqg`>*CDFb_mc*SBo*nagUCEr_+`-?>UTtsCZ4pwm!A(|PlB zgYJD>Re#miGi65047p1L7Gi%*rTDV*L|LiED~gjtjItoD$N%*Mb{oT_VV1?qM9y%M z>fb9b_o1=~KoiC-eJUIRhcm=JYNks^m%+G?8W`o_4G|7Z_of7qBzy=>XlVdK59J^v z!Pt^gSicI8#nLeU-}m{gf(sFVR4Hb=>6LYQ9?<{sKoXT;T5UL4kn=<~T@Om~rR8lH z-SX^NJ45Hr9QA6v3+le(nmUv_E~^|7y)Tze zZVrw3cTzoKGtmzP-6iHwv%go7Ap&q2;^pcYLw!6sb_4L3At4>q+mgmyv)11_{P~RW z@-MYimyko6)6$EKA{N4DCJO!rp)M9QuAYH*=cjt#rbhfA6Fs3myaOyP>;)WFy)w7b zH!4GZ>`KZk?KiE?JiR(dB( z_}jlBEgmX{g9d_$HkoX4ko%lew^(ffz2D~$g>3G-=4%xw-uRb&|6UP)>H&XewU%&g zV!+xs_*@u<;vILOHw56oJEQHaL(k_gGm_`g+s{lA*Js>b-Oldqg)J~v&{k9-9i)ez z;#&A?G{LyJnYCK4wdX$H?K^8ovY4UonIxOEhSvcD&jIT2#| zYq=BW+of;h| zPX8eIy@qQ-YXT*#K0^LmVoed7#8Y=F5})TeX0N@s0eNR{1X6*}h#W(f3=Qwb8{j`J zBsgG1&Fn*OQM_g}wb2lm?HoF>l#$k4MSg1P9B{ApZE-jVy((EfGde4H+qb_weAd3K{A(ejQ-6rr!k~qg^Zm6q z&z79$?BXZPJ2gx*vfZJ9ebxz_zoW>d;OfJWwa&V8801yiOl_QgemVn=b&dXnU6HIe z;M{%jujuG@WjwnoK3%PWL8C=tWpNxceycHyCp-t?(A<5w zJ(@7*UhfTn#1rT)N^CFnc%v*aGY73YrCG1A%^!K;;GT; z5AvjB0sHh(MC_i-DU*4d>&ePO6cCNw7p$EPw`v{%2WpB35}GtLjVl z37>#0n;%sI^vzl!&+>&%0V&)!Bc4(AZVRM>@FEj0fyHB3Tu*Uf-11D-=;C#6y&(z{ z)S?ka=W)8H6Q*J^o|3tO&Ssmm=6}y;C$rQjWQFf;O6+S*9Z{em7s=e<-kQzdVYWj} za|UZ(Sx7fBI6wls;R{eY@I{W>D?|CIWiiL-Ur`j)N`CdKgY9XvV#{+@rvI z{zWsu8N5Vw+UgW{6v@ zoOpn%DuD+7G^o!#YmjGMaz@>mD%~m&tfTx(Q=z9##$a`%f;Fxw$ZyUCZ!d=%>Sd7r z_%3y(eMx$Udm9(D>5lH2jRZc)df-hvOBcd|;b&A(k~`?(0dL6ld;T3ya1)^8E~zt5 z{y$D+L>U%_{(wP+zn#wSxo?nh+l3oy)7>llDuCOIGSd%S13AI}(YBc!OsPh1JYpKq z=LtN*VADYcZxYO}zep;wD&pBZ{2RSjN14ehMLT^sGLCp>k05Z~6%Nedh(bV!nd?zsb zcV!3SOC3qOYJ;30VNT!{xex^h0^jNg$q2Ifd8fu*>naY*rzsv|KYk1 zJQ;Y-+MyeU^oZ%kH3rEL^6v0HhnLMt+a1cZgdldXTer|LBk>xGM+;G$Mq*uBnme&_2PDR4&?Ntm6(-$@>F!ROWnp)%A)QAQ($rwvTSHaR!X!XB7BdX!n)oHbzt@lI zZ%P_{HT-xugvrB7oeGzEbiHbPdaSZ&2u4V7p>*fUSG}w`A-bK8ELNG0$FN87tFGfW z5#fGw3X_>2Jzb{5zsN8e2xtEDO8r8CPx5Qp5=8Cx6Ho+3zRkBfTLD5V_^x6qOy`*C zsx@Eg*E-3-&6&0O$fLK$xfqlwS^m+|!Mr==_;wsWj^*pvxR+vJ2BGt>xG~U3DG@z4jmpO^Rv*aS;zmI~I?($9!ObHIBa-fu+HZ-a^!QA8D}f63HjX zzZfTZe;Mas%2R5NUue*}#6HW0$>ysnm{(d|eR{Hn(f@|M_P2JbaV(~+${n+=1kovx z2-p$e5i+x(;92?jyAX^t7Wf=k)lnK_*mn^u2XJYx<^$o33J65?(0J+-;ayFtdXCWd z`^ePP5otmi!n7E_Xth3xXgj&{DpCE1;-#H9fv2LkJF__CYgz8}75# z7-DVxU(1Mpb!6psvPfvuBO*U&1Jvy08&7T+#=G?g!hcuj{9?UYdx-YLHWig+B1ih` zwcu|q$>3!EldAFGlEmP_Q%$R=Bqqb4hwee}^7l*EtF6wzg}i_E;Qs>izk2cCT>5_r z`Tr3iGyuT+$^LKuRrLSi3xwi`*x*9~x@)!O#J12^nzN99s)Z}Uc4p%jOc~-!;8DbU z%T#M+&GPV}0&3un=i*2`CCDYVO=7RqdQ6mvSlKkAU~anrXx$Nz`FL=T&gzLmZ78QM ze|u@GkrMU@z?Z-P+v@#5j6Y39CocEADMLR?$ZBQLYs)Y=^ED+16O z(2gk08t?(=t{iyh_X-CM#n)ANdCb?}UlT9qhMxhp_!#IqM~~9}P^ECNgdlgd6{E{} z(ZN_ppz(MN*jN6YL?{I(?>okgVUcgxLV*d=o%H<^A<%FXF?EDj{xS`TbIp`B?482yqKil_dc4Y-pwIh2qwTZ+}3PxZa zR-iT2(wS}`r$0-k2*HgMd$;h};ocTtV91ftXByN~dy!0Fb!2Eb=lU@!t9ka%8*E5r z>jn`4n1RKSvjFaKJ84`F*>cD9uU0ZY!3t<{`l>w5-a2u(OlzA3(fI*a>BowttOpuH zM5aoSz0Ceb}A9P43)lh*x zOaC=brskn=(`zG2uzx90a-8Hi2L&ey)r~f!`R)$=k!F2Xo#z7ALz|GKZ+T@yIaivW z=*Hyw#3>~%Nsvgw-6#A^8*Zi{fAQH}-H(#-QwUPEbr0}@ioXMT15&7$0g^*_6$=A} zq|kO>T&GJVN0(49MiuIAC6nk^u-(Chmfm3@;?bh~3&bs^kNRA%PzU?8Mb9zIqlqQk z$Rq@%LsS1?q{?FNT_dchV@BUNm)*S+epME)DjQP0OX~>g z;LL0@+h?i`OIJ^kVss1oVwLwesPX9wP9WX#Ij2^kPgrJM`rOBJQ~H?6!0${-KaHe) z%=R<5#jr^7T-hxX7|zM$oaBi7q@m?wQPt9gGDqk+C6n4(NQSj@FRBSW2DL2=ulIUX zn|3G4WrADG1Kq#-O%ra~Q}3SsTuLdvGaVvYeaadyQB)Y@>#~5cE&o_66d`S3QIh>ls4) zN>0a)kSJ72yY6dq`6s)QTOB|TtElZ1_JMreG+j-jOI>4*=b=3Lz&o|oYWzGBImA%$ zbyn!0Ry=+I$rVVb%IVkqSPJR*O4}7`947KC%*3xWV;fE~+=eytKma!jY z__j2C6ejE8LB3J5B!|CEu2KVF!_15VEP)?VmUCf}6-o>9V#L)Rk5xo^rp%X&#qKRI zleza6Nl4{!e}P=np=c(r&Go^`rX%-=@iMO61p~z#63)?-Q>zPYViOyFoM-0OA?(U< zgY&}j_|4W(AlBXYK0<_o%pivp_r_Dw>e66xG1l&KiwrRYCb^@`TF)Nnv8%L%@Cj6N z*A}xQsT)~lZ#FW`DAdWK7+@h!2O69`_{=vTsyXYzhGJO z_kQPpD`e+E$9w(2;iBzd68Rtefn;TaT&hEo#pA$* zcbL)gD*VYwE3MZbqp|^FQ1BJi1^XoJM!7IQMv|vPDfJ4*yo(HoHF%Z?HQu~S)wu!# zV$_21oiCtcygT4Mq{be${C0HEUCSY?3j4cpcQQ^<=5oO--&ej?1YEU`N;Oy7{|co4 zKY{4#y2P*;p6O6iI)gnrj>8W@VE%B+z#)y{(2-xRHB(q@ub*HX`#()T5;C_#7UNze zYtKJUlK@y~AaKyvBu?WDHyZ&bTDOWF6nWw z*Mi9fle9gq4;KyP$e{?7$yF9W=t1bWu*xcOIvC(2uC?LOh-ZBz=tO~1+96m!l~$x;KEaoSQpkX znS=5*b^!p8vP~KSmfi|{+_s9WOOe>#vMmvNty*M?d_7J12Z7w4 zZf%Ocd-#z&!sHj#X?Xe^^GL~yzbw(m_3q3&<2sPfj3z+A`taNQ^peSb-ch8R7dkt+{C zp6}|#V)q|i1q>I9kq9f+Cna+4b(K(7A;*Q1KmZ{ku^aw^B7+!k2nB83@H{>n&0PH; zD$7nGC84XTVLZ}K-jBLfe=A6oDbQn(y$Utp&WN4)1KkUBndSwm3eSJifZ3aLae|-I zG*Q;-qH~|?jVd_#ofSW4u@U!9N%s8$cy{&>R%EpLGQp{4$#?2t`XGduf2&THm01eJ zLocYavEl1;mLH@sDA0Pr`Kq3Q{GNB^S3Kp%hHNlhiPy7^!7k-O0 z1a_8uJiT};aQ0Xq6GLa(inz$9mS*`1OAY+?B8Y7Q51Y=;@v{Dm~3Ly zw;5ldfMbdv9rtP(b^QGMVQ%`_HMaw+mNWRhA;ES>?arbim$v9v6^Gg;LTd(kSUc2? zpPeCmO}_Tr@Y$aBGVeOST~=;k;4pi9pjXuqEs4k6{`N`ujEc>%cbmW4(V?F1UNF|K zCgn=0ulRVjd={4tEy2qgskl$Y5^(J?3k{HmKvt@<9=_U^#7PGNj}bvS?md_1YkPc- z$RcNgT-K}VCD*S}jMV6fr#s0X4NgzirgOpi{X!iOw|8VZ23Z`N4aVMl;`|tS^pw<$ zTjJ5-B&xOyk!o0Q-4vwx^s1KC=gpg=I|3N)bUveabOdndrL}2bUpf^|_+Q92RTa?# zZUaqhxl1ZV=4Qx@d7?+X=b_1JOtfgOX@OAnz=xXRQn-iue>j^wCx73RmK+3y#kE{k z+Yfm;ol63)0Q)TUb<3rw7^T!PrwiO?lZ86NJ6Z>^XK?wiQt37z@0<6qk|&gc`#v)E zF((%O`|O99zz?LQW#$s)aE70be*=e81&mMW(AvK z{7=nqvJedW5o38*VQ!i%*xvTNR;h5jcHfrHko=Jl#yIcrhQlQW_n=|v1d{R!t=5IK z!TpISZy$*@@v&4$_1rbbXc`;oI<+sKeGPY&gf3;)E+4CA%G@&;yA&!XaGsMjCLrV#T5xrOYGsNJ^Q+e}1ssae z)4hWz{aQKytv;Z^{fq6zI3EK;dia}Q3IBi<6ycM9aFqMzQChT$3IH=J9dO3`(0|uG zg1_+1hQOU;@!CHBe(b_yQ`a$TIp6O7pnS`bgmv6 zcap0*Cw>Yv1Om-gcG{te2X2fxl?*BtEkXRJ$dWC;9(<0p923}{?C~$*r;HVa^+nUL&q0Rs@2h4DYuWooBYx3Rx{oh&94YzCtR)m;g3N39b`9dk&T`h zZG5|N;xu{ze{r~0h2Xx92Y6vU#V#0fyeH|R>|(#K>BLMid0kR>l@aARBeg=oSws%Q z@HMsm)xy}5sNj^xL`y2EFYix02|QyE7Va0XbayD#@ibLO0RX@#QZr6*5WUe`X-k== z{O=B(3%3aE^fKjhJt0Igo{y(J36J3Zv>Kki*NfU!7lBD>2=#P1QL@6!^0MPRK&r`u zxtPMA!lAJ6b`y!z&k?lBQ}d1-_7{J4t4)A!N(r|3{M#YohBu@{)|O!Ka<}fhXj9UB zFm>;hm6&O?gW%xQtqDE&F-J? z)Vy$HGtq2J`4qDyi%c7t_bV}n3g|gXo9Ws=C;?9CvHX5o@JS7m5CEX(P5vjiDoyqI zxy>eBSYm4T{&`86?vaP$M{Q z9CK_ftbF4A9rJnPyrNb;VEY`N#sNbdwupi^yM>-IRHR8mS)HgpI#l{o8_JNz(z~WEL<>AGCFL=Nh=Jvj*&iC8<+?Bm+^RGr)BbLJGU1|*N5B;M=b7l zDP;9El!d7@2)r>^%+NXT2A3N|E@&wPvJyYwmf75krS$tg6IJ_Q1xbUuq zE<+gm4frp)AXP)+pJYt9CQfZE_`P~!J(_(KM&z_sl!Zi8e1i;@wbbi|gxI-LdfU=8 zQ+|0%9Kt5&FyTi~Se&*GS(+~Mg$Aa3^B3*~l4$A;&oQL|lMHR!0%5eayt~GF(>H$o z4yS$6j2Zbacl!gUg~UwDq(4@}&7R8m%=?wC{^{@QygX<*OTT|K5t+4awf@?>orrZx zEy{^BVtk7TC{FQ9W}}B4<^nwe%bnBwsJVXXnza$xo_jog+wR}RfHf1fm#I+g0=EYS za6AZ>r^Mwx&V|IR3~xaIXx(D{sjtoFDec|Oo6T8XyE=}QkjLdo-ax6Vj9bdJzFcrw z$>ln5n}{a$)v;{_kYegArD1=RV>HeSvEqrQqi+am*I6^BEvyPs#Hr8WRg31B6?hwVgtD^e8YSd2&u!_B2gs*9&EJ+g zQ?+zXeN=dOpLD^W=0^UV3yym2u6m`7O@qdM>1>x~4ki5eBqiMC$wxW5GoC+#`#vmT1$URg+_I0Y$9YxFKE|uZewvr&BbZxkTrOJknkG#~u!TCcj|dsi_wiY!a`IF%RtUj%UhG6-~I4 zlFfVX=h{{gdrwN~Q`=%l2;&G6J4srdG5w4dwAG=i6iGfYDSHe)(WF9}U({$Bh_oV& z>it*>3{;}Nd>$DA2OTS!gxvrDpy~R)S%Cc;h&FR`qg%JNLdhE~s-F#e0FH2~9DV>k z8GkVi38Al8;U42je}F{)>vRWaQMf0uG4UHGlw|yC947}KPUBD6+2J?V*HKmS)GbvQ zqX0f97)}v}pvsnuQ+?mM^&c?cZQQ+%ZG&~BV?yj<>1DXYl9F1yQ17QHnTGK{h%PJMZc}{ehuqQ_!J+!(r4}Jxw;PbA(JK~BGWL}z z5)kD4EU(|1YZ$E|w;Hrs{d18D{n8Q!T=O{o0H_^)4X5+JbSwhOZ;C$E(VaB{Zj4AC zN&B--bk(g#JbJWv1TF&lP~%WAU_FVD@mza*qG-$=T!G#TVK+ z6j66Irx7@Hao(>H6l+MogGP4iH+NTL)Mv}lMQVBEl#UxH8e8{?ZyC4Z~-59P&A_;+yu zRg&SgpJl=mx}zK9;%x=ZP)KI)goF7H3z$K7n7HOrY)?w#*9TZL^1D5cC4JQk$Vc&26|I{673?>G?G_q)+|3=ye3gZ~hq z!h9aj$ksv&#{~dbo5UW&Lpe-oMtBYptW6|{=D=09h6)d|+#Av^hbsPCvtB}7_=;NZle3(6kU!S!YrVsud32Dt6 z)5};|M-@XbW&{9?<45pWw`1){{&m-s%VU57W1seNZ)G4z z$_PS(4&4$1S7o_}SSo|wgK;Db+))Go?goM* zQwieBk=3s`j8(hFclz1t{GEE41_yAI~=tR5M_1HW4roDmwe#Qc>(Of`iuC>*< zfXyDL-Zet`KNJ>;_-i`wdi*a$>PP7{jT9f{NXs4llQxfCSj~laNfOo-cI5(q_57KP z<(5f7B!qr_5a~WALZ0y1lLA0UTCCuE%&8m+d{Wfa_Db*ELdarNX-etnWn*uhP(=Mvtu88Nw1~ zPz<#PCqr-EhX8@WI|crpz2z(LF(~LrH-l<%chvVhH=fhYVN?T>?;E%$PYd!KYUX^U z@+N8{f$F$uf=i)7C>j@R;ctmT$ZK>9-S5ppk9(a$vWs^&`&Md(QE}AeSk4MB@wW-q zzALW!gMkja`HLYkXwV1~TUaY`LQNZZ+-L3LuJVq8j|%`~l5f5<=It_@^Th3R82G?5 zCbki+s+e1ANz$^}yo&V*8u~HRT`8jTLMYgJ5KmKs@-keB{Fb|2GC?J2J#L!(GVw@m zNzk!#68>UdpK5H|jop>%hlgq;bN|vymeKHzX9&9N?j8PI+*vxS$Xr<=e-y;hb-GCR z5j>;w1FLju4$iZ3v~BPCSH)UICQHfL@L@PG?3(2mcfH{d;#9;h7U20No#3`$$pI-x zPB~5kA{Y8k!=d0oOr6yqHFgYyf5~c>)54*$d^Du-BpU$8y2V7~&!LxOh-{T&{H=Kwbq>s9gEQ*)%Rptt2ubUr5hZV& zqhR(TwioR5`X7d5h>eOi%C|Rk?dtit`dX?d7SWU2M8~k}4=(aAC1R1URWS8&eJN8$ zHB~Miw%2d_?*pWvyvz#-NMq81uTg3Or!Fm}6P7OjV>2pPS0u!ubH;YwoQi$9TBVWT34R#lJ2J8)Ba0&n}oAar{&~^!Ru29z)OkY&6O%z+y(= z!Q)={5UI3Z)u_Y&6(14FoD2Zq-Up-%-3CT^hD!qNS(;28m{t*(UE zprMJpGeO7kWZXMf4U}zso|#fn&gM_UU;-$$iauN0>(WsQX!jCOGV-i+yu^(+@ql`s zupHR?)0$6#F(r}f#B$G6&fs}Uw#rV!yLOR3^#z>2j6=NaC`>8prlS`?MG`$CmfjP+ zQ>*|0SpDBn{xDe0xq|4eyE^nTD229p=3?#%9eIbW9qnWOl#_nMF(!!xS>OiA?CH9}@^*mzF9O8t zItuTepkSZ1lCttUgx{&NfDx90)7`~{t%*+)#D%|A*7tnxWRDy6vU-m+)Yl8N#TfB5 z1=(QQRZ4v}$6>4~p?ga3Uh%^)w92LW_={}K%fNl~XlU=t%k!+lb+cD{C!WT}x3T4| zRrS<>a9SAK73PZZuaOT~@M!}_?nSI;8R*GKp?jWdIsw`Fi<8$5B_Wj{&FWD+c7T$R zktelw746_pfBYRqJ}vWlvqf>0p10M!jxQVQ@(BW6@6c?jF8~o-~}+t4{RQCY3F?&=inW@qBd&!y3OfX$7afDh+mkt8aB#FUFo} zI;Tw&uo!edyEz!Xv4r|y4`uTDgiA8IRmzP2OOAc3ZzqHl;)!;S-mie&v{JZf##9+2 zxKn?$Uuku=Q#-oI$gksv(}5Gpg9AjB8Swn1H7T9BwD~QFyHUb!0#! z@^yC#8tEkH>|u{S>HYrlCgZWTHbSZl=rf28(aX6r6ulSDkx6*z~3-Y4T@c`3mPc z7Ev#b4o^#1o?uk_u54Y#sDlG7Fx6?{d{M<9oXsv?D?*~db(K-{=IPx@6Oi|X*xk%i z(_!P-3u|Q25P6Nu`iGKB@Z2QJWTdaR`F^3j{S^u{L!Y`4(KjR|6WLQzyyVG7#Q=Nf zhV}4L(sk}`IENi{KPvQF`a)_f8ahp;8%TGNBGj^yfy?h_^So9-BQ z#r|T98+}&#*^p!A(|a24E4aOmby|BdWl`4v!<9B??56&CQ-cI-;M5<3v{~}%>_qFH z2~xpgH$;*)_${plt7#2FawsAFo0-yJ%F&glT~X}rD)0JS)%Zr@%SK8%Uz&-s32yIL zBL^9Oac|W`4W6t_hFQW%UxjsdhVxi7jl7RlNjcovtxHq@z!aXi({JaF+Wg`MPYEn& zn~Kf`SuCzEUe5JaG9hXggcMn6eO58^FoCshtwLl$p`C>2;O$m87Ud0eOOm;H+bUCA z9Pc557p0yN5&81`cKM>h1S=?RT#j6kT9?^hTFrxbX0-guEp%COGui0a$_yP8kHW)y z_>j&nn(K0Dp@V#Nsu4OqzzKqrb7T?n>&u<++5Q>VkrWd5W^8zcM_ddcj$h0bQ{$6wI}?<1S~j@hYOERKuz z!zDnH*2HZpmtrL@ZAw0tPzpf;=B!|u8J&u&R`r#4*!dH4X#dGC;|aTya)Mc4+UTIP z(}%&`L6M6aqAtI22g6xT{46Ckl#;}jMTg?kHF}Bwy7`>_Z)<7~|AF1(3=$aHd1Lx?Zz+^2;n$N^a9&~(b`(zOX z1*a-6TjR1)DehYQ-2UC2)ZMb6lhQDQd-dht|1-wDF3=Uuq5A0L?{-9_|nC9Nu~2@pG*NXY3~do82ln*W{A6jk9W8yzg1j%#ORhM|1n8R>Dz zHBfXJWkM9OLw!ch&X(P1OQi}oAQF9Xd>T>7Cg2&^tv7di%*;fZ8hvCI1!mC1=uLII zyxU_>Q}oUFZ1~;C)byC9{8gf^(7)5Dn)zW_L{7EYmt(bCbsmXN@d&}b zUr+5v84sXmaoujGM;Crq=xl}{2Ynp{%G|cc98xJ8qadhrsuP*rGxadW3xJpx_b(NU za;O4|rEt=u{d(~$^-XvoGv_3h0BfUFV}OHebt3%mEWMGd&nd~jM0NQDAXD*Or^4C$f z+IB!ygbEWHZzVzTPJ0-ds%dK6Uq48s)^@0Wng`J;qDFIlfuW zB%?nd(H5DQsr}YBI@dbh$<54yA-|Z0S5vb3|d9v%>Xn21r zYitqh6h3rBHFO>#6QUxEr9iY%agA}msmN&_>up6dJ1QHzSHh_L9;bJ2hrx+i4pwbs z@h;LsS2{oHZ#I*NahdF{bUrxUzX(5azL0bC%J<1@pajBJrJs%DI5>_;%Y%9P1o1he z>`Rmi z$L(jt$J?KPW_!VN_)w~0NhT(@sGVxo>YDiSMbuG{%vK-_5ty?wPYNu-b7P!8e$C0f zAmWtaPGiI9btLQFaC4{z=!i&B5>3o8Jr(5ny=FpCQ0s38U%g<@GZv#VU-ts@{3%6- z?6G4i0z>O_)eG64_yD#c4PiKr-TVmQJzj?=VPt-Q>;q00{?kFa7jN|Z`3?&@>iYrh z|FUZsbJgJm*67V2YCd%6UA*75Wno*$7IlBn96nS9VuO3Hh4x^9-;niW0N%YpXpTGN zy=%j5;@OPO;>Vdep^~&otrvJl$kzDbozHF^S5s_xmWY%NbruEQe%2*12|mgOg9{$M zA`V7ss5qp7w$-zb{Z(I~B*hAJ#g8Ew&_`{M%?yAL$CxXkd(3~eZr%KqQ2D*I(HkJ> zqIIJ4v_HKHEDG8wTD!yx8Ycnef6GvpKlT;Tv{*B!>y8bHY2;s?q~UPIxZdi=tv1@~ z!QXVUeZ6Mq(yc2ibP$Jz)3om0XedXMqWLX;_(bK2Tom^-8%>X?kA31w(NB+E2D*yY zJFk?#Tyb*>-Q|L&c#ektJeEMU;>DDCz|!I%f=VeVKC1RHi_gSm%G^jD+7xB z9bz5CrPQZIs6LeRB{&1hwJ~~RC`U~0Eq|WNK4BIBJstrbq@bR_qGl$sEy92wxNg0!B-J3TAYM-mCM+)MK>aK*74UaZU^8}>lUZJZ2##9`#9m-LCDhhKA_)M%Z7kDj z%HcUOwR(y@-Ixor0gGsbJG~jD6_iZq3BTEjo5PR=Y#8NI{_;5&I_dK3T%tM&=3K&R zHFX|=8*^=fGwJZ@A~(2s&C5?z)8-;Bvup@JnpK8G$u1S0;6KLbw^4zWcp7}AUk_tBqf|3Y@8N^T9 zJG%7UCiAG95LKevQ#*%T@Yrjgm*bv3R3)Y*1Q4heTJ%TQ^>A}+w@eXzqKQRE`O|z^ z>Zwon#|iTO4MV0;Kc^=QnI3K3&@sD1+sg)IY{PngI4B{Rb3Oe=BF%o9mCzFTCKBsH z7>cIi)5{VmR72d_EEpaC8`fRwuc^x#ZCB$T1^gNa>emeCfI*n@0zXIJb(mj0Nd(JxKw01|*OJ1hJ zKj)fxksXBH@)E<+xSPqFNn`)$m>VR({Wi6OcMRtz+^;MLff69!?*4QGa|VY!5OYwD ziU+FXg#4W;gxJk87-*BxF$qax+>5mvIjy(2NjjXY+GXVP7WI9Chn%$w0!ZXo;p)<| zZ_R#v4l^4$>N^c<$Grgn&KNeS@X8w4aouoxO;aujjjrUdBW2%1mL*{~)F`8RYXx(~ zjn}Pw^n2>s>dTv`ME{0@L9e~%CdWvqyF4=$I+Rfixza;{vnW3K?4;$O7hnCHk8=>G zz|+iJtjmrDs3W7#&zkyX&C9CmdNRs?@AxPM*Z9dzP+M==T&5FwUBPO;rUi=4^c{nI zCv(&eVjGi?`Iw&qg`7!}MPewib5z~8nJQi+j`PE$3imFx{A?x^-dFF4B=!M7_k97C z4rv2`DC=LadWqeix%ePsN55Q0~$U1&@i;v_|7F0>;T{Ano z(Vlb}C?C-n9K8nh#IC)iEJY|=48>eJ4$sSl$5W`e;}f?s1k-j4txieU)F4>)bBdWa zddd(vsR+FOg1O14f{O#;S0TzM)9|5*^@-?2mt?_i@^S4V##OtWPPS&hT>ctsnR%zm z^D}waO|X_SSEH?uLenCP3u@w?6`gkUHqa9VsTiL31Ar6(ZA(FIV_^ngvbN{2+qa4Z zGmK*?ioVQca!i#5aprl6R4pwALZbJt2Nl>MU)wWgO8C+rbxSljvk@Ny>q77gJBug7 zR}XK}hF*mS$-yN@3A5KV7UIt*k!!kW-Md%5>-tIT${A5s*Ciy~rQc5S;i!Qf8IyTO zA7vc!$jRK1$^RD9vNo$f@8%i(^@(QDRJ-Jte+}Y(STxMvv zw6oQuf2K>^fQyLkohy&7_EAn_a3fB4bh1Af803^)m|UkqRK%u+pE%nc^X{Wa?%Y~W z;ObiI8h#Px0_&TE*eWv{*dp^N9nKP{k-yb3cT1J-0{}Qph@b^SNv`uo2=%%wWm#Cl zceFv;l?Qj7ZsM=Z0V9_uLx?UUziqTX2(RwFp2(3Em0K&G`kzB}APc}p`qgb<-LEc^BiPVsW3t|62cZq98$uH8-GNjc!ROEjY_fpX zdz%m@DI+R~RT%lXku=Ji`n|4kz*Q`M>2#4M-`s(T%l`OcIfXT=Wly2`0U61i7URX2lhBig zOMN6Etr=Fj9Ou4BeBOZA(CF-r&8Mh_NYoOJ67{w5OHw=tH+u+iEn=Eluehw=HlKp^{LqoZt zH|aERu~8P4i7rX<^4~{oN?T$x88gfLQ^*5S_yZh2t zyj~nYpz~kTJ-=9x6NXlRVVC}>bOZ(zp`{lObuZe#>gv};(QZN8ToK3;FDl!cf1~fr z?fSVdyj#o?$?WcRpn7UD?myBRcVB)u>bE0xdTJsKvof0*il%i%r$qW8lCNdROLSFG zy)VU2#{aUn@&9qZW7a~P8#$5VwAwUlFHfe)OW1sK#PAwy$i?Vsx|jX}P9@f4C<9Sj z+ofTkkn%+Blah>#Gm^ZkV?7F91ybhdAe?$@Fx?){49hdSC4^*v5slk=QzR1@(~iHL zVXgio(bBpMQX%9K&kALv<$!7<(nUma9OY9QY%8GGz*>AbhRT+(%9;ZJ-{VM0INPJ| zFA?mRT~RpBTAN-C)jj!mm!9T)c?+Td;(Vo zA;$d=Na$eif7juNrg$%vo2mFUJ5 zw0{I=BO+{ckhbU_Kmy_i>8$Ms_^0V*O0BuIKZ9?uHB~X%Bwxu6V?|vTe%=&MxoNv| zXRMvL6pVagOgtLT-}Ur9K=kJZ0-|RG@DW)Gf=NkB%^U~3RxIy#RYdz2T|;7-y~NNI zr5m|<{_QzxpAo!2^tc?B{fR;={2ApIcwqJL@6ouYyLzh{7Dc=p6)a4C64rJPDLoQa zWwOu*5}eL|N~*L=ZZj)VuiF}CJyG^tv_MLb)fJ^o`%D*m8)3slm+;kAEWqwwOlixo z(|%?l!aYN-Sng5XD{+MLN$YOgGW~@1F?X5*heehAAu7iLL001Ihv3~u48P&4=UYTQIvgF#=`YccmXn-?< zNUaxD5@Q=ge2B18I`2*=l zw{3~I&moJ91m=*E4j{(3wKgT-CdWX*tn|(C%hieys~b>!xm)EabP#fk-V*&a_Glz< z-W7_fgYk=YyQF!IIZp9e?SQ}WAwhx)UJ!(kScM$eH3J;(9uTDd^sbNF^Bh!`Y%I>y zaDQ%Cb<&L=P@JEw!VuNXQ6Q~j)}P|XwUzF-pDK_^Fp`@F++}%rZfnlOdqGfO`Efs9 z*q6`*u?F;b!jzO*g?Me!#CsmCK=hu@0S+vFNHT+=Gen$HD?O3GJ79Q{VP zJYH0VA#{5j{*@`M2B65(9ewxez9CM~$G?#39E+qlqCtXU$gMg92^_`kKuw<+)*-Dr?Sxn!N6lkQ#49+mk`(@S=Ml(!+O1)r-7X5 zDl!@u>2s;IOjd6XM`qIlPlwh#Iv5?zHM>0A@#>dEN8jDY%k`{>Jj(~hVkNcyac8u6 z+`{{CvZOm_?^VTLJ^}!MS0IR$+w=X1A~umccEY6L_#j_7en6Cz1mivrfnFcMbzK)N zK6zT&GGii5Ire{*(eQdX59rTl>sMo2Dpfpa_JN(>$&si;$>)T~1n0Z(u`KCU>99cx_5(C118xOgd zmO}78oWd!S%-+JzKiBkvAq`<=3(s zXGm9X>*!YLzmJo5waS-fZoJbB^j*GgJWKhUDS#LgF;+FFB#bag-#D@D}H|+*qv)iy)OtwS^p7m+L?$k|2u@=Ll5=klx zWPJnsV;qy7Q-4j}%Swgxn(==RyLeWx&Ds2HysmhGEBGTLz8K0IKW0@ZZ`nKCOYH=Y z+&pQt?_YQJ_*@|-TzB`l-gu;LHB8!5K+w}T^O?DSUvEUkwN? z$pN35_|$AZrs4md$KI^m&}L?z>d?qk z5EVQ*UoJsO)MBXS=r?zIqtN^TKo&n-5%@W$d59GQ>bKR$j##*PBa8S6K)zaPvRU^hvZSw_0UoDQ6cmHB^S>vU8aKN z=UU^(V9M{|14wtd<={SRUKKVqOq@`BcZQ9uI5XSljT`TvIz7JdtcyD;Xcv|P8WJl zIi$8(oK#t_^H93@CnYe|w8R*-hNglk8Rs+8tU(#(9!==M$vFhh_VPYwarKb3z~M-U zIn?-HQ5wcC#n1gZXr7s(e-+-q`R@zE0tk8MZ&0!n;I*DxT`|dK+ZgP>`OJLn_xhKi zq-@t5Q>QKp(DF*QStRz0;~6`^i+UT8Z>k-IiTZgGEjPjjAUboud_f8?GvEgUt^_s0 l(&enb^C^dWkBXuFy|!F30M!>RCGcUt`c82FtJeR+`VW6YajgIV diff --git a/packages/console/src/assets/docs/guides/web-sveltekit/README.mdx b/packages/console/src/assets/docs/guides/web-sveltekit/README.mdx index bd361427691..d3b1cbff7b8 100644 --- a/packages/console/src/assets/docs/guides/web-sveltekit/README.mdx +++ b/packages/console/src/assets/docs/guides/web-sveltekit/README.mdx @@ -40,7 +40,7 @@ export const handle = handleLogto( { endpoint: '${props.endpoint}', appId: '${props.app.id}', - appSecret: '${props.app.secret}', + appSecret: '${props.secrets[0]?.value ?? props.app.secret}', }, { encryptionKey: '${cookieEncryptionKey}' } // Random-generated key );`} diff --git a/packages/console/src/assets/docs/guides/web-sveltekit/logo.svg b/packages/console/src/assets/docs/guides/web-sveltekit/logo.svg index 0ab1ab0c3c0..09759b47f8a 100644 --- a/packages/console/src/assets/docs/guides/web-sveltekit/logo.svg +++ b/packages/console/src/assets/docs/guides/web-sveltekit/logo.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/packages/console/src/assets/docs/guides/web-wordpress/logo.svg b/packages/console/src/assets/docs/guides/web-wordpress/logo.svg index 72183e90424..30eb694c1ee 100644 --- a/packages/console/src/assets/docs/guides/web-wordpress/logo.svg +++ b/packages/console/src/assets/docs/guides/web-wordpress/logo.svg @@ -1 +1,7 @@ - \ No newline at end of file + + + + + + + diff --git a/packages/console/src/assets/icons/calendar-dark.svg b/packages/console/src/assets/icons/calendar-dark.svg new file mode 100644 index 00000000000..d63565f701f --- /dev/null +++ b/packages/console/src/assets/icons/calendar-dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/console/src/assets/icons/calendar.svg b/packages/console/src/assets/icons/calendar.svg index b8ffd462547..59e439e9150 100644 --- a/packages/console/src/assets/icons/calendar.svg +++ b/packages/console/src/assets/icons/calendar.svg @@ -1,5 +1,5 @@ - + diff --git a/packages/console/src/assets/icons/email.svg b/packages/console/src/assets/icons/email.svg index 6d156693a7f..36cdd5c31d8 100644 --- a/packages/console/src/assets/icons/email.svg +++ b/packages/console/src/assets/icons/email.svg @@ -1,4 +1,4 @@ - + diff --git a/packages/console/src/assets/icons/github.svg b/packages/console/src/assets/icons/github.svg index 52779b17747..3e30da2e97b 100644 --- a/packages/console/src/assets/icons/github.svg +++ b/packages/console/src/assets/icons/github.svg @@ -1,4 +1,4 @@ - + diff --git a/packages/console/src/assets/images/blur-preview.svg b/packages/console/src/assets/images/blur-preview.svg new file mode 100644 index 00000000000..9d07235496a --- /dev/null +++ b/packages/console/src/assets/images/blur-preview.svg @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/cloud/AppRoutes.tsx b/packages/console/src/cloud/AppRoutes.tsx index 9870c709ac6..2e49878cfa8 100644 --- a/packages/console/src/cloud/AppRoutes.tsx +++ b/packages/console/src/cloud/AppRoutes.tsx @@ -9,7 +9,7 @@ import CheckoutSuccessCallback from '@/pages/CheckoutSuccessCallback'; import Profile from '@/pages/Profile'; import HandleSocialCallback from '@/pages/Profile/containers/HandleSocialCallback'; -import * as styles from './AppRoutes.module.scss'; +import styles from './AppRoutes.module.scss'; import Main from './pages/Main'; import SocialDemoCallback from './pages/SocialDemoCallback'; diff --git a/packages/console/src/cloud/pages/Main/InvitationList/index.tsx b/packages/console/src/cloud/pages/Main/InvitationList/index.tsx index e86b71147fe..13e17b42a47 100644 --- a/packages/console/src/cloud/pages/Main/InvitationList/index.tsx +++ b/packages/console/src/cloud/pages/Main/InvitationList/index.tsx @@ -2,7 +2,7 @@ import { OrganizationInvitationStatus, getTenantIdFromOrganizationId } from '@lo import { useContext, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import OrganizationIcon from '@/assets/icons/organization-preview.svg'; +import OrganizationIcon from '@/assets/icons/organization-preview.svg?react'; import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; import { type InvitationListResponse } from '@/cloud/types/router'; import TenantEnvTag from '@/components/TenantEnvTag'; @@ -13,7 +13,7 @@ import Spacer from '@/ds-components/Spacer'; import useTenantPathname from '@/hooks/use-tenant-pathname'; import useUserOnboardingData from '@/onboarding/hooks/use-user-onboarding-data'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly invitations: InvitationListResponse; diff --git a/packages/console/src/cloud/pages/Main/TenantLandingPage/TenantLandingPageContent/index.tsx b/packages/console/src/cloud/pages/Main/TenantLandingPage/TenantLandingPageContent/index.tsx index 6978b899366..e51c9c24c4b 100644 --- a/packages/console/src/cloud/pages/Main/TenantLandingPage/TenantLandingPageContent/index.tsx +++ b/packages/console/src/cloud/pages/Main/TenantLandingPage/TenantLandingPageContent/index.tsx @@ -2,9 +2,9 @@ import { Theme } from '@logto/schemas'; import classNames from 'classnames'; import { useContext, useState } from 'react'; -import Plus from '@/assets/icons/plus.svg'; -import TenantLandingPageImageDark from '@/assets/images/tenant-landing-page-dark.svg'; -import TenantLandingPageImage from '@/assets/images/tenant-landing-page.svg'; +import Plus from '@/assets/icons/plus.svg?react'; +import TenantLandingPageImageDark from '@/assets/images/tenant-landing-page-dark.svg?react'; +import TenantLandingPageImage from '@/assets/images/tenant-landing-page.svg?react'; import { type TenantResponse } from '@/cloud/types/router'; import CreateTenantModal from '@/components/CreateTenantModal'; import { TenantsContext } from '@/contexts/TenantsProvider'; @@ -12,7 +12,7 @@ import Button from '@/ds-components/Button'; import DynamicT from '@/ds-components/DynamicT'; import useTheme from '@/hooks/use-theme'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/cloud/pages/Main/TenantLandingPage/index.tsx b/packages/console/src/cloud/pages/Main/TenantLandingPage/index.tsx index fb99b70dcc4..d5fd24d6c70 100644 --- a/packages/console/src/cloud/pages/Main/TenantLandingPage/index.tsx +++ b/packages/console/src/cloud/pages/Main/TenantLandingPage/index.tsx @@ -1,7 +1,7 @@ import Topbar from '@/components/Topbar'; import TenantLandingPageContent from './TenantLandingPageContent'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function TenantLandingPage() { return ( diff --git a/packages/console/src/cloud/pages/SocialDemoCallback/index.tsx b/packages/console/src/cloud/pages/SocialDemoCallback/index.tsx index 5c18de94f1c..b83cd0c46a5 100644 --- a/packages/console/src/cloud/pages/SocialDemoCallback/index.tsx +++ b/packages/console/src/cloud/pages/SocialDemoCallback/index.tsx @@ -3,12 +3,12 @@ import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useSearchParams } from 'react-router-dom'; -import CongratsDark from '@/assets/images/congrats-dark.svg'; -import Congrats from '@/assets/images/congrats.svg'; +import CongratsDark from '@/assets/images/congrats-dark.svg?react'; +import Congrats from '@/assets/images/congrats.svg?react'; import Card from '@/ds-components/Card'; import useTheme from '@/hooks/use-theme'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function SocialDemoCallback() { const theme = useTheme(); diff --git a/packages/console/src/cloud/types/router.ts b/packages/console/src/cloud/types/router.ts index ed175b7afda..7d6cf2f10bc 100644 --- a/packages/console/src/cloud/types/router.ts +++ b/packages/console/src/cloud/types/router.ts @@ -11,10 +11,30 @@ export type SubscriptionPlanResponse = GuardedResponse< GetRoutes['/api/subscription-plans'] >[number]; +export type LogtoSkuResponse = GetArrayElementType>; + export type Subscription = GuardedResponse; +/** @deprecated */ export type SubscriptionUsage = GuardedResponse; +/* ===== Use `New` in the naming to avoid confusion with legacy types ===== */ +/** The response of `GET /api/tenants/my/subscription/quota` has the same response type. */ +export type NewSubscriptionQuota = GuardedResponse< + GetRoutes['/api/tenants/:tenantId/subscription/quota'] +>; + +/** The response of `GET /api/tenants/my/subscription/usage` has the same response type. */ +export type NewSubscriptionUsage = GuardedResponse< + GetRoutes['/api/tenants/:tenantId/subscription/usage'] +>; + +/** The response of `GET /api/tenants/my/subscription/usage/:entityName/scopes` has the same response type. */ +export type NewSubscriptionScopeUsage = GuardedResponse< + GetRoutes['/api/tenants/:tenantId/subscription/usage/:entityName/scopes'] +>; +/* ===== Use `New` in the naming to avoid confusion with legacy types ===== */ + export type InvoicesResponse = GuardedResponse; export type InvitationResponse = GuardedResponse; diff --git a/packages/console/src/components/ActionBar/index.tsx b/packages/console/src/components/ActionBar/index.tsx index 11d09537973..403f3a981cc 100644 --- a/packages/console/src/components/ActionBar/index.tsx +++ b/packages/console/src/components/ActionBar/index.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react'; import ProgressBar from '../ProgressBar'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly step: number; diff --git a/packages/console/src/components/ActionsButton/index.tsx b/packages/console/src/components/ActionsButton/index.tsx index 575b05f1bd6..00a27113274 100644 --- a/packages/console/src/components/ActionsButton/index.tsx +++ b/packages/console/src/components/ActionsButton/index.tsx @@ -2,15 +2,15 @@ import { type AdminConsoleKey } from '@logto/phrases'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import Delete from '@/assets/icons/delete.svg'; -import Edit from '@/assets/icons/edit.svg'; -import More from '@/assets/icons/more.svg'; +import Delete from '@/assets/icons/delete.svg?react'; +import Edit from '@/assets/icons/edit.svg?react'; +import More from '@/assets/icons/more.svg?react'; import ActionMenu, { ActionMenuItem } from '@/ds-components/ActionMenu'; import ConfirmModal from '@/ds-components/ConfirmModal'; import DynamicT from '@/ds-components/DynamicT'; import useActionTranslation from '@/hooks/use-action-translation'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { /** A function that will be called when the user confirms the deletion. If not provided, diff --git a/packages/console/src/components/AddOnNoticeFooter/index.module.scss b/packages/console/src/components/AddOnNoticeFooter/index.module.scss new file mode 100644 index 00000000000..4a3dc3faa6e --- /dev/null +++ b/packages/console/src/components/AddOnNoticeFooter/index.module.scss @@ -0,0 +1,17 @@ +@use '@/scss/underscore' as _; + +.container { + display: flex; + align-items: center; + gap: _.unit(6); + padding: _.unit(6); + background-color: var(--color-info-container); + margin: 0 _.unit(-6) _.unit(-6); + flex: 1; // Should display in full width + + .description { + flex: 1; + flex-shrink: 0; + font: var(--font-body-2); + } +} diff --git a/packages/console/src/components/AddOnNoticeFooter/index.tsx b/packages/console/src/components/AddOnNoticeFooter/index.tsx new file mode 100644 index 00000000000..6f40c55f4e5 --- /dev/null +++ b/packages/console/src/components/AddOnNoticeFooter/index.tsx @@ -0,0 +1,30 @@ +import { type AdminConsoleKey } from '@logto/phrases'; +import { type ReactNode } from 'react'; + +import Button from '@/ds-components/Button'; + +import styles from './index.module.scss'; + +type Props = { + readonly children: ReactNode; + readonly isLoading?: boolean; + readonly buttonTitle?: AdminConsoleKey; + readonly onClick: () => void; +}; + +function AddOnNoticeFooter({ children, isLoading, onClick, buttonTitle }: Props) { + return ( +
+
{children}
+
+ ); +} + +export default AddOnNoticeFooter; diff --git a/packages/console/src/components/AppError/index.tsx b/packages/console/src/components/AppError/index.tsx index 49a2bfdbb89..468aa8d80fe 100644 --- a/packages/console/src/components/AppError/index.tsx +++ b/packages/console/src/components/AppError/index.tsx @@ -3,15 +3,15 @@ import { Theme } from '@logto/schemas'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import KeyboardArrowDown from '@/assets/icons/keyboard-arrow-down.svg'; -import KeyboardArrowUp from '@/assets/icons/keyboard-arrow-up.svg'; -import ErrorDark from '@/assets/images/error-dark.svg'; -import Error from '@/assets/images/error.svg'; +import KeyboardArrowDown from '@/assets/icons/keyboard-arrow-down.svg?react'; +import KeyboardArrowUp from '@/assets/icons/keyboard-arrow-up.svg?react'; +import ErrorDark from '@/assets/images/error-dark.svg?react'; +import Error from '@/assets/images/error.svg?react'; import Button from '@/ds-components/Button'; import useTheme from '@/hooks/use-theme'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title?: string; diff --git a/packages/console/src/components/AppLoading/index.tsx b/packages/console/src/components/AppLoading/index.tsx index 036e8efa270..c0db14cb006 100644 --- a/packages/console/src/components/AppLoading/index.tsx +++ b/packages/console/src/components/AppLoading/index.tsx @@ -1,7 +1,7 @@ -import Logo from '@/assets/images/logo.svg'; +import Logo from '@/assets/images/logo.svg?react'; import { Daisy as Spinner } from '@/ds-components/Spinner'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function AppLoading() { return ( diff --git a/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.module.scss b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.module.scss new file mode 100644 index 00000000000..c9daaa78419 --- /dev/null +++ b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.module.scss @@ -0,0 +1,3 @@ +.strong { + font-weight: 500; +} diff --git a/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx index 0928e6cf980..1f5845f4a6b 100644 --- a/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx +++ b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx @@ -2,13 +2,19 @@ import { ApplicationType, ReservedPlanId } from '@logto/schemas'; import { useContext } from 'react'; import { Trans, useTranslation } from 'react-i18next'; +import AddOnNoticeFooter from '@/components/AddOnNoticeFooter'; import ContactUsPhraseLink from '@/components/ContactUsPhraseLink'; import PlanName from '@/components/PlanName'; import QuotaGuardFooter from '@/components/QuotaGuardFooter'; +import { isDevFeaturesEnabled } from '@/consts/env'; +import { machineToMachineAddOnUnitPrice } from '@/consts/subscriptions'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import Button from '@/ds-components/Button'; +import TextLink from '@/ds-components/TextLink'; import useApplicationsUsage from '@/hooks/use-applications-usage'; +import styles from './index.module.scss'; + type Props = { readonly selectedType?: ApplicationType; readonly isLoading: boolean; @@ -17,8 +23,8 @@ type Props = { }; function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) { - const { currentPlan } = useContext(SubscriptionDataContext); - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell.paywall' }); + const { currentPlan, currentSku } = useContext(SubscriptionDataContext); + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell' }); const { hasAppsReachedLimit, hasMachineToMachineAppsReachedLimit, @@ -28,11 +34,36 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) if (selectedType) { const { id: planId, name: planName, quota } = currentPlan; + if ( + selectedType === ApplicationType.MachineToMachine && + isDevFeaturesEnabled && + planId === ReservedPlanId.Pro + ) { + return ( + + , + a: , + }} + > + {t('add_on.footer.machine_to_machine_app', { + price: machineToMachineAddOnUnitPrice, + })} + + + ); + } + if ( selectedType === ApplicationType.MachineToMachine && hasMachineToMachineAppsReachedLimit && // For paid plan (pro plan), we don't guard the m2m app creation since it's an add-on feature. - planId === ReservedPlanId.Free + (isDevFeaturesEnabled ? currentSku.id : planId) === ReservedPlanId.Free ) { return ( @@ -41,7 +72,7 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) a: , }} > - {t('machine_to_machine_feature')} + {t('paywall.machine_to_machine_feature')} ); @@ -56,7 +87,7 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) a: , }} > - {t('third_party_apps')} + {t('paywall.third_party_apps')} ); @@ -68,10 +99,10 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) , - planName: , + planName: , }} > - {t('applications', { count: quota.applicationsLimit ?? 0 })} + {t('paywall.applications', { count: quota.applicationsLimit ?? 0 })} ); diff --git a/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx b/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx index 21c88536a07..097c7cf7010 100644 --- a/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx +++ b/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx @@ -1,7 +1,8 @@ import { type AdminConsoleKey } from '@logto/phrases'; import type { Application } from '@logto/schemas'; -import { ApplicationType } from '@logto/schemas'; -import { type ReactElement, useMemo } from 'react'; +import { ApplicationType, ReservedPlanId } from '@logto/schemas'; +import { conditional } from '@silverhand/essentials'; +import { type ReactElement, useContext, useMemo } from 'react'; import { useController, useForm } from 'react-hook-form'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; @@ -9,6 +10,8 @@ import Modal from 'react-modal'; import { useSWRConfig } from 'swr'; import { GtagConversionId, reportConversion } from '@/components/Conversion/utils'; +import { isDevFeaturesEnabled } from '@/consts/env'; +import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import DynamicT from '@/ds-components/DynamicT'; import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; @@ -17,12 +20,12 @@ import TextInput from '@/ds-components/TextInput'; import useApi from '@/hooks/use-api'; import useCurrentUser from '@/hooks/use-current-user'; import TypeDescription from '@/pages/Applications/components/TypeDescription'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { applicationTypeI18nKey } from '@/types/applications'; import { trySubmitSafe } from '@/utils/form'; import Footer from './Footer'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type FormData = { type: ApplicationType; @@ -52,6 +55,9 @@ function CreateForm({ } = useForm({ defaultValues: { type: defaultCreateType, isThirdParty: isDefaultCreateThirdParty }, }); + const { + currentSubscription: { planId }, + } = useContext(SubscriptionDataContext); const { user } = useCurrentUser(); const { mutate: mutateGlobal } = useSWRConfig(); @@ -115,6 +121,9 @@ function CreateForm({ )} {!to &&
{title}
} + {eventKey === 'ExchangeTokenBy.TokenExchange' && Impersonation} ); } diff --git a/packages/console/src/components/AuditLogTable/index.tsx b/packages/console/src/components/AuditLogTable/index.tsx index 6901f5fa3a6..8cd31e744b3 100644 --- a/packages/console/src/components/AuditLogTable/index.tsx +++ b/packages/console/src/components/AuditLogTable/index.tsx @@ -19,7 +19,7 @@ import EmptyDataPlaceholder from '../EmptyDataPlaceholder'; import ApplicationSelector from './components/ApplicationSelector'; import EventName from './components/EventName'; import EventSelector from './components/EventSelector'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; const auditLogEventOptions = Object.entries(auditLogEventTitle).map(([value, title]) => ({ value, diff --git a/packages/console/src/components/BasicWebhookForm/index.tsx b/packages/console/src/components/BasicWebhookForm/index.tsx index f110c7b852c..98ba5b7097e 100644 --- a/packages/console/src/components/BasicWebhookForm/index.tsx +++ b/packages/console/src/components/BasicWebhookForm/index.tsx @@ -14,7 +14,7 @@ import FormField from '@/ds-components/FormField'; import TextInput from '@/ds-components/TextInput'; import { uriValidator } from '@/utils/validator'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; const hookEventGroups: Array> = [ ...schemaGroupedDataHookEvents.map(([schema, events]) => ({ diff --git a/packages/console/src/components/BillInfo/index.tsx b/packages/console/src/components/BillInfo/index.tsx index c2a3a25f2df..85ec6946fed 100644 --- a/packages/console/src/components/BillInfo/index.tsx +++ b/packages/console/src/components/BillInfo/index.tsx @@ -1,7 +1,7 @@ import { useContext, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import Tip from '@/assets/icons/tip.svg'; +import Tip from '@/assets/icons/tip.svg?react'; import { newPlansBlogLink } from '@/consts'; import { TenantsContext } from '@/contexts/TenantsProvider'; import Button from '@/ds-components/Button'; @@ -11,7 +11,7 @@ import TextLink from '@/ds-components/TextLink'; import { ToggleTip } from '@/ds-components/Tip'; import useSubscribe from '@/hooks/use-subscribe'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly cost: number; diff --git a/packages/console/src/components/Breakable/index.tsx b/packages/console/src/components/Breakable/index.tsx index ca5bf216797..45de5954c8c 100644 --- a/packages/console/src/components/Breakable/index.tsx +++ b/packages/console/src/components/Breakable/index.tsx @@ -1,4 +1,4 @@ -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly children: React.ReactNode; diff --git a/packages/console/src/components/ChargeNotification/index.tsx b/packages/console/src/components/ChargeNotification/index.tsx index 2fd62a5f4df..e2ce6ffe3ce 100644 --- a/packages/console/src/components/ChargeNotification/index.tsx +++ b/packages/console/src/components/ChargeNotification/index.tsx @@ -5,6 +5,7 @@ import { useContext } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { newPlansBlogLink } from '@/consts'; +import { isDevFeaturesEnabled } from '@/consts/env'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import InlineNotification from '@/ds-components/InlineNotification'; import TextLink from '@/ds-components/TextLink'; @@ -38,7 +39,7 @@ function ChargeNotification({ checkedFlagKey, }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell' }); - const { currentPlan } = useContext(SubscriptionDataContext); + const { currentPlan, currentSku } = useContext(SubscriptionDataContext); const { configs, updateConfigs } = useConfigs(); // Display null when loading @@ -52,7 +53,7 @@ function ChargeNotification({ Boolean(checkedChargeNotification?.[checkedFlagKey]) || !hasSurpassedLimit || // No charge notification for free plan - currentPlan.id === ReservedPlanId.Free + (isDevFeaturesEnabled ? currentSku.id : currentPlan.id) === ReservedPlanId.Free ) { return null; } diff --git a/packages/console/src/components/ConnectorForm/BasicForm/index.tsx b/packages/console/src/components/ConnectorForm/BasicForm/index.tsx index 2a72ed192cb..0711588a6a2 100644 --- a/packages/console/src/components/ConnectorForm/BasicForm/index.tsx +++ b/packages/console/src/components/ConnectorForm/BasicForm/index.tsx @@ -1,12 +1,10 @@ -import { useState } from 'react'; +import { Theme } from '@logto/schemas'; import { Controller, useFormContext } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; -import CaretDown from '@/assets/icons/caret-down.svg'; -import CaretUp from '@/assets/icons/caret-up.svg'; -import Error from '@/assets/icons/toast-error.svg'; +import Error from '@/assets/icons/toast-error.svg?react'; +import ImageInputs from '@/components/ImageInputs'; import UnnamedTrans from '@/components/UnnamedTrans'; -import Button from '@/ds-components/Button'; import FormField from '@/ds-components/FormField'; import Select from '@/ds-components/Select'; import TextInput from '@/ds-components/TextInput'; @@ -14,23 +12,21 @@ import TextLink from '@/ds-components/TextLink'; import useDocumentationUrl from '@/hooks/use-documentation-url'; import type { ConnectorFormType } from '@/types/connector'; import { SyncProfileMode } from '@/types/connector'; -import { uriValidator } from '@/utils/validator'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; + +const themeToField = Object.freeze({ + [Theme.Light]: 'logo', + [Theme.Dark]: 'logoDark', +} as const satisfies Record); type Props = { readonly isAllowEditTarget?: boolean; - readonly isDarkDefaultVisible?: boolean; readonly isStandard?: boolean; readonly conflictConnectorName?: Record; }; -function BasicForm({ - isAllowEditTarget, - isDarkDefaultVisible, - isStandard, - conflictConnectorName, -}: Props) { +function BasicForm({ isAllowEditTarget, isStandard, conflictConnectorName }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { getDocumentationUrl } = useDocumentationUrl(); const { @@ -38,17 +34,6 @@ function BasicForm({ register, formState: { errors }, } = useFormContext(); - const [darkVisible, setDarkVisible] = useState(Boolean(isDarkDefaultVisible)); - - const toggleDarkVisible = () => { - setDarkVisible((previous) => !previous); - }; - - const toggleVisibleButtonTitle = darkVisible - ? 'connectors.guide.logo_dark_collapse' - : 'connectors.guide.logo_dark_show'; - - const ToggleVisibleCaretIcon = darkVisible ? CaretUp : CaretDown; const syncProfileOptions = [ { @@ -72,37 +57,18 @@ function BasicForm({ {...register('name', { required: true })} /> - - - !value || uriValidator(value) || t('errors.invalid_uri_format'), - })} - /> - - {darkVisible && ( - - - !value || uriValidator(value) || t('errors.invalid_uri_format'), - })} - /> - - )} -
-
+ ({ + name: themeToField[theme], + error: errors[themeToField[theme]], + type: 'connector_logo', + theme, + }))} + /> )} diff --git a/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/ConnectorRadio/index.tsx b/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/ConnectorRadio/index.tsx index b722f3f99bb..fc2293ab59d 100644 --- a/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/ConnectorRadio/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/ConnectorRadio/index.tsx @@ -5,7 +5,7 @@ import ConnectorLogo from '@/components/ConnectorLogo'; import UnnamedTrans from '@/components/UnnamedTrans'; import { type ConnectorGroup } from '@/types/connector'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly data: ConnectorGroup; diff --git a/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/index.tsx b/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/index.tsx index f682f929e8a..38a81ebe01b 100644 --- a/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/ConnectorRadioGroup/index.tsx @@ -5,7 +5,7 @@ import RadioGroup, { Radio } from '@/ds-components/RadioGroup'; import { type ConnectorGroup } from '@/types/connector'; import ConnectorRadio from './ConnectorRadio'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export type ConnectorRadioGroupSize = 'medium' | 'large' | 'xlarge'; diff --git a/packages/console/src/components/CreateConnectorForm/Footer/index.tsx b/packages/console/src/components/CreateConnectorForm/Footer/index.tsx index 53c7b4032cb..8c86cce555c 100644 --- a/packages/console/src/components/CreateConnectorForm/Footer/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/Footer/index.tsx @@ -10,10 +10,11 @@ import { Trans, useTranslation } from 'react-i18next'; import ContactUsPhraseLink from '@/components/ContactUsPhraseLink'; import PlanName from '@/components/PlanName'; import QuotaGuardFooter from '@/components/QuotaGuardFooter'; +import { isDevFeaturesEnabled } from '@/consts/env'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import Button from '@/ds-components/Button'; import { type ConnectorGroup } from '@/types/connector'; -import { hasReachedQuotaLimit } from '@/utils/quota'; +import { hasReachedQuotaLimit, hasReachedSubscriptionQuotaLimit } from '@/utils/quota'; type Props = { readonly isCreatingSocialConnector: boolean; @@ -31,35 +32,51 @@ function Footer({ onClickCreateButton, }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell.paywall' }); - const { currentPlan } = useContext(SubscriptionDataContext); + const { currentPlan, currentSku, currentSubscriptionUsage, currentSubscriptionQuota } = + useContext(SubscriptionDataContext); const standardConnectorCount = useMemo( () => - existingConnectors.filter( - ({ isStandard, isDemo, type }) => isStandard && !isDemo && type === ConnectorType.Social - ).length, + isDevFeaturesEnabled + ? // No more standard connector limit in new pricing model. + 0 + : existingConnectors.filter( + ({ isStandard, isDemo, type }) => isStandard && !isDemo && type === ConnectorType.Social + ).length, [existingConnectors] ); const socialConnectorCount = useMemo( () => - existingConnectors.filter( - ({ isStandard, isDemo, type }) => !isStandard && !isDemo && type === ConnectorType.Social - ).length, - [existingConnectors] + isDevFeaturesEnabled + ? currentSubscriptionUsage.socialConnectorsLimit + : existingConnectors.filter( + ({ isStandard, isDemo, type }) => + !isStandard && !isDemo && type === ConnectorType.Social + ).length, + [existingConnectors, currentSubscriptionUsage.socialConnectorsLimit] ); - const isStandardConnectorsReachLimit = hasReachedQuotaLimit({ - quotaKey: 'standardConnectorsLimit', - plan: currentPlan, - usage: standardConnectorCount, - }); + const isStandardConnectorsReachLimit = isDevFeaturesEnabled + ? // No more standard connector limit in new pricing model. + false + : hasReachedQuotaLimit({ + quotaKey: 'standardConnectorsLimit', + plan: currentPlan, + usage: standardConnectorCount, + }); - const isSocialConnectorsReachLimit = hasReachedQuotaLimit({ - quotaKey: 'socialConnectorsLimit', - plan: currentPlan, - usage: socialConnectorCount, - }); + const isSocialConnectorsReachLimit = isDevFeaturesEnabled + ? hasReachedSubscriptionQuotaLimit({ + quotaKey: 'socialConnectorsLimit', + usage: currentSubscriptionUsage.socialConnectorsLimit, + quota: currentSubscriptionQuota, + }) + : hasReachedQuotaLimit({ + quotaKey: 'socialConnectorsLimit', + plan: currentPlan, + usage: socialConnectorCount, + }); if (isCreatingSocialConnector && selectedConnectorGroup) { const { id: planId, name: planName, quota } = currentPlan; @@ -70,13 +87,15 @@ function Footer({ , - planName: , + planName: , }} > {quota.standardConnectorsLimit === 0 ? t('standard_connectors_feature') : t( - planId === ReservedPlanId.Pro ? 'standard_connectors_pro' : 'standard_connectors', + (isDevFeaturesEnabled ? currentSku.id : planId) === ReservedPlanId.Pro + ? 'standard_connectors_pro' + : 'standard_connectors', { count: quota.standardConnectorsLimit ?? 0, } @@ -92,11 +111,14 @@ function Footer({ , - planName: , + planName: , }} > {t('social_connectors', { - count: quota.socialConnectorsLimit ?? 0, + count: + (isDevFeaturesEnabled + ? currentSubscriptionQuota.socialConnectorsLimit + : quota.socialConnectorsLimit) ?? 0, })} diff --git a/packages/console/src/components/CreateConnectorForm/PlatformSelector/index.tsx b/packages/console/src/components/CreateConnectorForm/PlatformSelector/index.tsx index 658db4e286d..01a9eac6eab 100644 --- a/packages/console/src/components/CreateConnectorForm/PlatformSelector/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/PlatformSelector/index.tsx @@ -6,7 +6,7 @@ import { connectorPlatformLabel } from '@/consts/connectors'; import RadioGroup, { Radio } from '@/ds-components/RadioGroup'; import type { ConnectorGroup } from '@/types/connector'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly connectorGroup: ConnectorGroup; diff --git a/packages/console/src/components/CreateConnectorForm/Skeleton/index.module.scss b/packages/console/src/components/CreateConnectorForm/Skeleton/index.module.scss index dbe6a3872a4..e53ce9df54d 100644 --- a/packages/console/src/components/CreateConnectorForm/Skeleton/index.module.scss +++ b/packages/console/src/components/CreateConnectorForm/Skeleton/index.module.scss @@ -7,23 +7,23 @@ } .logo { - @include _.shimmering-animation; width: 40px; height: 40px; border-radius: 8px; + @include _.shimmering-animation; } .name { - @include _.shimmering-animation; width: 50px; height: 16px; margin-bottom: _.unit(1); + @include _.shimmering-animation; } .description { - @include _.shimmering-animation; height: 14px; margin-bottom: _.unit(0.5); + @include _.shimmering-animation; &.shortDescription { width: 50%; diff --git a/packages/console/src/components/CreateConnectorForm/Skeleton/index.tsx b/packages/console/src/components/CreateConnectorForm/Skeleton/index.tsx index 9ecdb68975f..c305f630c42 100644 --- a/packages/console/src/components/CreateConnectorForm/Skeleton/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/Skeleton/index.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; -import * as radioStyles from '../ConnectorRadioGroup/ConnectorRadio/index.module.scss'; -import * as radioGroupStyles from '../ConnectorRadioGroup/index.module.scss'; +import radioStyles from '../ConnectorRadioGroup/ConnectorRadio/index.module.scss'; +import radioGroupStyles from '../ConnectorRadioGroup/index.module.scss'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly numberOfLoadingConnectors?: number; diff --git a/packages/console/src/components/CreateConnectorForm/index.tsx b/packages/console/src/components/CreateConnectorForm/index.tsx index 5ed33692ef9..db935cd198a 100644 --- a/packages/console/src/components/CreateConnectorForm/index.tsx +++ b/packages/console/src/components/CreateConnectorForm/index.tsx @@ -10,7 +10,7 @@ import useSWR from 'swr'; import DynamicT from '@/ds-components/DynamicT'; import ModalLayout from '@/ds-components/ModalLayout'; import type { RequestError } from '@/hooks/use-api'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { getConnectorGroups } from '../../pages/Connectors/utils'; @@ -18,7 +18,7 @@ import ConnectorRadioGroup from './ConnectorRadioGroup'; import Footer from './Footer'; import PlatformSelector from './PlatformSelector'; import Skeleton from './Skeleton'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; import { compareConnectors, getConnectorRadioGroupSize, getModalTitle } from './utils'; type Props = { diff --git a/packages/console/src/components/CreateTenantModal/EnvTagOptionContent/index.tsx b/packages/console/src/components/CreateTenantModal/EnvTagOptionContent/index.tsx index f9d6a10549d..48b2f75e2d7 100644 --- a/packages/console/src/components/CreateTenantModal/EnvTagOptionContent/index.tsx +++ b/packages/console/src/components/CreateTenantModal/EnvTagOptionContent/index.tsx @@ -7,7 +7,7 @@ import DynamicT from '@/ds-components/DynamicT'; import Tag from '@/ds-components/Tag'; import { ReservedPlanName } from '@/types/subscriptions'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly tag: TenantTag; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/FeaturedPlanContent/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/FeaturedPlanContent/index.tsx index 8e95a6a2a3a..9007d706273 100644 --- a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/FeaturedPlanContent/index.tsx +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/FeaturedPlanContent/index.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; -import Failed from '@/assets/icons/failed.svg'; -import Success from '@/assets/icons/success.svg'; +import Failed from '@/assets/icons/failed.svg?react'; +import Success from '@/assets/icons/success.svg?react'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; import useFeaturedPlanContent from './use-featured-plan-content'; type Props = { diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/index.tsx index f6836d4041c..417d5d77a66 100644 --- a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/index.tsx +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/PlanCardItem/index.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import { useContext, useMemo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import ArrowRight from '@/assets/icons/arrow-right.svg'; +import ArrowRight from '@/assets/icons/arrow-right.svg?react'; import PlanDescription from '@/components/PlanDescription'; import PlanName from '@/components/PlanName'; import { pricingLink } from '@/consts'; @@ -15,7 +15,7 @@ import TextLink from '@/ds-components/TextLink'; import { type SubscriptionPlan } from '@/types/subscriptions'; import FeaturedPlanContent from './FeaturedPlanContent'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly plan: SubscriptionPlan; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/index.tsx new file mode 100644 index 00000000000..6ecbbeead51 --- /dev/null +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/index.tsx @@ -0,0 +1,35 @@ +import classNames from 'classnames'; + +import Failed from '@/assets/icons/failed.svg?react'; +import Success from '@/assets/icons/success.svg?react'; + +import styles from '../../PlanCardItem/FeaturedPlanContent/index.module.scss'; + +import useFeaturedSkuContent from './use-featured-sku-content'; + +type Props = { + readonly skuId: string; +}; + +function FeaturedSkuContent({ skuId }: Props) { + const contentData = useFeaturedSkuContent(skuId); + + return ( +
    + {contentData.map(({ title, isAvailable }) => { + return ( +
  • + {isAvailable ? ( + + ) : ( + + )} + {title} +
  • + ); + })} +
+ ); +} + +export default FeaturedSkuContent; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/use-featured-sku-content.ts b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/use-featured-sku-content.ts new file mode 100644 index 00000000000..70662e19b03 --- /dev/null +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/FeaturedSkuContent/use-featured-sku-content.ts @@ -0,0 +1,77 @@ +import { ReservedPlanId } from '@logto/schemas'; +import { cond } from '@silverhand/essentials'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { + freePlanAuditLogsRetentionDays, + freePlanM2mLimit, + freePlanMauLimit, + freePlanPermissionsLimit, + freePlanRoleLimit, + proPlanAuditLogsRetentionDays, +} from '@/consts/subscriptions'; + +type ContentData = { + readonly title: string; + readonly isAvailable: boolean; +}; + +const useFeaturedSkuContent = (skuId: string) => { + const { t } = useTranslation(undefined, { + keyPrefix: 'admin_console.upsell.featured_plan_content', + }); + + const contentData: ContentData[] = useMemo(() => { + const isFreePlan = skuId === ReservedPlanId.Free; + const planPhraseKey = isFreePlan ? 'free_plan' : 'pro_plan'; + + return [ + { + title: t(`mau.${planPhraseKey}`, { ...cond(isFreePlan && { count: freePlanMauLimit }) }), + isAvailable: true, + }, + { + title: t(`m2m.${planPhraseKey}`, { ...cond(isFreePlan && { count: freePlanM2mLimit }) }), + isAvailable: true, + }, + { + title: t('third_party_apps'), + isAvailable: !isFreePlan, + }, + { + title: t('mfa'), + isAvailable: !isFreePlan, + }, + { + title: t('sso'), + isAvailable: !isFreePlan, + }, + { + title: t(`role_and_permissions.${planPhraseKey}`, { + ...cond( + isFreePlan && { + roleCount: freePlanRoleLimit, + permissionCount: freePlanPermissionsLimit, + } + ), + }), + isAvailable: true, + }, + { + title: t('organizations'), + isAvailable: !isFreePlan, + }, + { + title: t('audit_logs', { + count: isFreePlan ? freePlanAuditLogsRetentionDays : proPlanAuditLogsRetentionDays, + }), + isAvailable: true, + }, + ]; + }, [t, skuId]); + + return contentData; +}; + +export default useFeaturedSkuContent; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/index.tsx new file mode 100644 index 00000000000..81fbcb7c64e --- /dev/null +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/SkuCardItem/index.tsx @@ -0,0 +1,100 @@ +import { maxFreeTenantLimit, adminTenantId, ReservedPlanId } from '@logto/schemas'; +import classNames from 'classnames'; +import { useContext, useMemo } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import ArrowRight from '@/assets/icons/arrow-right.svg?react'; +import { type LogtoSkuResponse } from '@/cloud/types/router'; +import PlanDescription from '@/components/PlanDescription'; +import PlanName from '@/components/PlanName'; +import { pricingLink } from '@/consts'; +import { TenantsContext } from '@/contexts/TenantsProvider'; +import Button, { type Props as ButtonProps } from '@/ds-components/Button'; +import DangerousRaw from '@/ds-components/DangerousRaw'; +import DynamicT from '@/ds-components/DynamicT'; +import TextLink from '@/ds-components/TextLink'; + +import styles from '../PlanCardItem/index.module.scss'; + +import FeaturedSkuContent from './FeaturedSkuContent'; + +type Props = { + readonly sku: LogtoSkuResponse; + readonly onSelect: () => void; + readonly buttonProps?: Partial; +}; + +function SkuCardItem({ sku, onSelect, buttonProps }: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell.create_tenant' }); + const { tenants } = useContext(TenantsContext); + const { unitPrice: basePrice, id: skuId } = sku; + + const isFreeSku = skuId === ReservedPlanId.Free; + + const isFreeTenantExceeded = useMemo( + () => + /** Should not block admin tenant owners from creating more than three tenants */ + !tenants.some(({ id }) => id === adminTenantId) && + tenants.filter(({ planId }) => planId === ReservedPlanId.Free).length >= maxFreeTenantLimit, + [tenants] + ); + + return ( +
+
+
+ +
+
+
{t('base_price')}
+
+ ${t('monthly_price', { value: (basePrice ?? 0) / 100 })} +
+
+
+ +
+
+
+ + {isFreeSku && isFreeTenantExceeded && ( +
+ {t('free_tenants_limit', { count: maxFreeTenantLimit })} +
+ )} + {!isFreeSku && ( +
+ } + className={styles.link} + > + + +
+ )} +
+ {skuId === ReservedPlanId.Pro && ( +
{t('most_popular')}
+ )} +
+ ); +} + +export default SkuCardItem; diff --git a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/index.tsx b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/index.tsx index 38d4260599f..f7bfbfdbbd0 100644 --- a/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/index.tsx +++ b/packages/console/src/components/CreateTenantModal/SelectTenantPlanModal/index.tsx @@ -5,22 +5,25 @@ import { Trans, useTranslation } from 'react-i18next'; import Modal from 'react-modal'; import { useCloudApi, toastResponseError } from '@/cloud/hooks/use-cloud-api'; -import { type TenantResponse } from '@/cloud/types/router'; +import { type TenantResponse, type LogtoSkuResponse } from '@/cloud/types/router'; import { GtagConversionId, reportToGoogle } from '@/components/Conversion/utils'; import { pricingLink } from '@/consts'; +import { isDevFeaturesEnabled } from '@/consts/env'; import DangerousRaw from '@/ds-components/DangerousRaw'; import ModalLayout from '@/ds-components/ModalLayout'; import TextLink from '@/ds-components/TextLink'; +import useLogtoSkus from '@/hooks/use-logto-skus'; import useSubscribe from '@/hooks/use-subscribe'; import useSubscriptionPlans from '@/hooks/use-subscription-plans'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { type SubscriptionPlan } from '@/types/subscriptions'; -import { pickupFeaturedPlans } from '@/utils/subscription'; +import { pickupFeaturedPlans, pickupFeaturedLogtoSkus } from '@/utils/subscription'; import { type CreateTenantData } from '../types'; import PlanCardItem from './PlanCardItem'; -import * as styles from './index.module.scss'; +import SkuCardItem from './SkuCardItem'; +import styles from './index.module.scss'; type Props = { readonly tenantData?: CreateTenantData; @@ -28,21 +31,27 @@ type Props = { }; function SelectTenantPlanModal({ tenantData, onClose }: Props) { - const [isSubmitting, setIsSubmitting] = useState(); + const [processingPlanId, setProcessingPlanId] = useState(); + const [processingSkuId, setProcessingSkuId] = useState(); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const { data: subscriptionPlans } = useSubscriptionPlans(); + const { data: logtoSkus } = useLogtoSkus(); + const { subscribe } = useSubscribe(); const cloudApi = useCloudApi({ hideErrorToast: true }); + const reservedPlans = conditional(subscriptionPlans && pickupFeaturedPlans(subscriptionPlans)); + const reservedBasicLogtoSkus = conditional(logtoSkus && pickupFeaturedLogtoSkus(logtoSkus)); - if (!reservedPlans || !tenantData) { + if (!reservedPlans || !reservedBasicLogtoSkus || !tenantData) { return null; } const handleSelectPlan = async (plan: SubscriptionPlan) => { const { id: planId } = plan; try { - setIsSubmitting(planId); + setProcessingPlanId(planId); if (planId === ReservedPlanId.Free) { const { name, tag, regionName } = tenantData; const newTenant = await cloudApi.post('/api/tenants', { body: { name, tag, regionName } }); @@ -56,7 +65,28 @@ function SelectTenantPlanModal({ tenantData, onClose }: Props) { } catch (error: unknown) { void toastResponseError(error); } finally { - setIsSubmitting(undefined); + setProcessingPlanId(undefined); + } + }; + + const handleSelectSku = async (logtoSku: LogtoSkuResponse) => { + const { id: skuId } = logtoSku; + try { + setProcessingSkuId(skuId); + if (skuId === ReservedPlanId.Free) { + const { name, tag, regionName } = tenantData; + const newTenant = await cloudApi.post('/api/tenants', { body: { name, tag, regionName } }); + + reportToGoogle(GtagConversionId.CreateProductionTenant, { transactionId: newTenant.id }); + onClose(newTenant); + return; + } + + await subscribe({ skuId, planId: skuId, tenantData }); + } catch (error: unknown) { + void toastResponseError(error); + } finally { + setProcessingSkuId(undefined); } }; @@ -83,19 +113,33 @@ function SelectTenantPlanModal({ tenantData, onClose }: Props) { onClose={onClose} >
- {reservedPlans.map((plan) => ( - { - void handleSelectPlan(plan); - }} - /> - ))} + {isDevFeaturesEnabled + ? reservedBasicLogtoSkus.map((logtoSku) => ( + { + void handleSelectSku(logtoSku); + }} + /> + )) + : reservedPlans.map((plan) => ( + { + void handleSelectPlan(plan); + }} + /> + ))}
diff --git a/packages/console/src/components/CreateTenantModal/index.tsx b/packages/console/src/components/CreateTenantModal/index.tsx index d6525cc5162..3dc2fd67878 100644 --- a/packages/console/src/components/CreateTenantModal/index.tsx +++ b/packages/console/src/components/CreateTenantModal/index.tsx @@ -5,8 +5,8 @@ import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import Modal from 'react-modal'; -import CreateTenantHeaderIconDark from '@/assets/icons/create-tenant-header-dark.svg'; -import CreateTenantHeaderIcon from '@/assets/icons/create-tenant-header.svg'; +import CreateTenantHeaderIconDark from '@/assets/icons/create-tenant-header-dark.svg?react'; +import CreateTenantHeaderIcon from '@/assets/icons/create-tenant-header.svg?react'; import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; import { type TenantResponse } from '@/cloud/types/router'; import Region, { RegionName } from '@/components/Region'; @@ -17,11 +17,11 @@ import ModalLayout from '@/ds-components/ModalLayout'; import RadioGroup, { Radio } from '@/ds-components/RadioGroup'; import TextInput from '@/ds-components/TextInput'; import useTheme from '@/hooks/use-theme'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import EnvTagOptionContent from './EnvTagOptionContent'; import SelectTenantPlanModal from './SelectTenantPlanModal'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; import { type CreateTenantData } from './types'; type Props = { diff --git a/packages/console/src/components/DateTime/index.tsx b/packages/console/src/components/DateTime/index.tsx index 766d94d28d7..c40b39bfd4a 100644 --- a/packages/console/src/components/DateTime/index.tsx +++ b/packages/console/src/components/DateTime/index.tsx @@ -1,18 +1,30 @@ -import type { Nullable } from '@silverhand/essentials'; +import { type Nullable } from '@silverhand/essentials'; import { isValid } from 'date-fns'; -type Props = { - readonly children: Nullable; -}; +const parseDate = (date: Nullable) => { + if (!date) { + return; + } -function DateTime({ children }: Props) { - const date = children && new Date(children); + const parsed = new Date(date); + return isValid(parsed) ? parsed : undefined; +}; - if (!date || !isValid(date)) { - return -; - } +type Props = { + readonly children: Nullable; +}; - return {date.toLocaleDateString()}; +/** + * Safely display a date in the user's locale. If the date is invalid, it will display a dash. + */ +export function LocaleDate({ children }: Props) { + return {parseDate(children)?.toLocaleDateString() ?? '-'}; } -export default DateTime; +/** + * Safely display a date and time in the user's locale. If the date is invalid, it will display a + * dash. + */ +export function LocaleDateTime({ children }: Props) { + return {parseDate(children)?.toLocaleString() ?? '-'}; +} diff --git a/packages/console/src/components/DetailsForm/index.tsx b/packages/console/src/components/DetailsForm/index.tsx index c19161d60c4..fb2b8ef231a 100644 --- a/packages/console/src/components/DetailsForm/index.tsx +++ b/packages/console/src/components/DetailsForm/index.tsx @@ -3,7 +3,7 @@ import type { ReactNode } from 'react'; import SubmitFormChangesActionBar from '../SubmitFormChangesActionBar'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly autoComplete?: string; diff --git a/packages/console/src/components/DetailsPage/DetailsPageHeader/index.tsx b/packages/console/src/components/DetailsPage/DetailsPageHeader/index.tsx index b4b431a33a9..d72e741f6bf 100644 --- a/packages/console/src/components/DetailsPage/DetailsPageHeader/index.tsx +++ b/packages/console/src/components/DetailsPage/DetailsPageHeader/index.tsx @@ -10,7 +10,7 @@ import { } from 'react'; import { useTranslation } from 'react-i18next'; -import More from '@/assets/icons/more.svg'; +import More from '@/assets/icons/more.svg?react'; import ActionMenu, { ActionMenuItem } from '@/ds-components/ActionMenu'; import Button from '@/ds-components/Button'; import Card from '@/ds-components/Card'; @@ -20,7 +20,7 @@ import DynamicT from '@/ds-components/DynamicT'; import Tag, { type Props as TagProps } from '@/ds-components/Tag'; import useWindowResize from '@/hooks/use-window-resize'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type StatusTag = { status: TagProps['status']; diff --git a/packages/console/src/components/DetailsPage/Skeleton/index.module.scss b/packages/console/src/components/DetailsPage/Skeleton/index.module.scss index f01e5691a71..d7a86f5d7c4 100644 --- a/packages/console/src/components/DetailsPage/Skeleton/index.module.scss +++ b/packages/console/src/components/DetailsPage/Skeleton/index.module.scss @@ -18,11 +18,11 @@ background-color: var(--color-layer-1); .icon { - @include _.shimmering-animation; width: 60px; height: 60px; border-radius: 12px; margin-right: _.unit(6); + @include _.shimmering-animation; } .wrapper { @@ -30,23 +30,23 @@ flex-direction: column; .title { - @include _.shimmering-animation; width: 113px; height: 28px; + @include _.shimmering-animation; } .tags { - @include _.shimmering-animation; width: 453px; height: 20px; margin-top: _.unit(3); + @include _.shimmering-animation; } } .button { - @include _.shimmering-animation; width: 158px; height: 44px; + @include _.shimmering-animation; } } diff --git a/packages/console/src/components/DetailsPage/Skeleton/index.tsx b/packages/console/src/components/DetailsPage/Skeleton/index.tsx index b796c621e91..58bfcb30b24 100644 --- a/packages/console/src/components/DetailsPage/Skeleton/index.tsx +++ b/packages/console/src/components/DetailsPage/Skeleton/index.tsx @@ -1,7 +1,7 @@ import { FormCardSkeleton } from '@/components/FormCard'; import Spacer from '@/ds-components/Spacer'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function Skeleton() { return ( diff --git a/packages/console/src/components/DetailsPage/index.tsx b/packages/console/src/components/DetailsPage/index.tsx index c6083122d0f..2f4f8677413 100644 --- a/packages/console/src/components/DetailsPage/index.tsx +++ b/packages/console/src/components/DetailsPage/index.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import type { ReactElement, ReactNode } from 'react'; import { type To } from 'react-router-dom'; -import Back from '@/assets/icons/back.svg'; +import Back from '@/assets/icons/back.svg?react'; import type DangerousRaw from '@/ds-components/DangerousRaw'; import DynamicT from '@/ds-components/DynamicT'; import TextLink from '@/ds-components/TextLink'; @@ -12,7 +12,7 @@ import type { RequestError } from '@/hooks/use-api'; import RequestDataError from '../RequestDataError'; import Skeleton from './Skeleton'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly backLink: To; diff --git a/packages/console/src/components/Drawer/index.tsx b/packages/console/src/components/Drawer/index.tsx index 99e004b664d..bf920ab5d79 100644 --- a/packages/console/src/components/Drawer/index.tsx +++ b/packages/console/src/components/Drawer/index.tsx @@ -1,12 +1,12 @@ import type { AdminConsoleKey } from '@logto/phrases'; import ReactModal from 'react-modal'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import CardTitle from '@/ds-components/CardTitle'; import IconButton from '@/ds-components/IconButton'; import Spacer from '@/ds-components/Spacer'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title?: AdminConsoleKey; diff --git a/packages/console/src/components/EditScopeModal/index.tsx b/packages/console/src/components/EditScopeModal/index.tsx index 1ba3bc37bee..5e8a3cc1fda 100644 --- a/packages/console/src/components/EditScopeModal/index.tsx +++ b/packages/console/src/components/EditScopeModal/index.tsx @@ -8,7 +8,7 @@ import Button from '@/ds-components/Button'; import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; import TextInput from '@/ds-components/TextInput'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { trySubmitSafe } from '@/utils/form'; export type EditScopeData = { diff --git a/packages/console/src/components/EmptyDataPlaceholder/index.tsx b/packages/console/src/components/EmptyDataPlaceholder/index.tsx index bdf1706507b..4b7d12ad7b0 100644 --- a/packages/console/src/components/EmptyDataPlaceholder/index.tsx +++ b/packages/console/src/components/EmptyDataPlaceholder/index.tsx @@ -3,11 +3,11 @@ import classNames from 'classnames'; import { type ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; -import EmptyDark from '@/assets/images/table-empty-dark.svg'; -import Empty from '@/assets/images/table-empty.svg'; +import EmptyDark from '@/assets/images/table-empty-dark.svg?react'; +import Empty from '@/assets/images/table-empty.svg?react'; import useTheme from '@/hooks/use-theme'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title?: ReactNode; diff --git a/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx b/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx index 0b8ccaa9db2..7646f9f66ee 100644 --- a/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/EntityItem/index.tsx @@ -5,7 +5,7 @@ import UserAvatar from '@/components/UserAvatar'; import SuspendedTag from '@/pages/Users/components/SuspendedTag'; import { getUserTitle } from '@/utils/user'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type UserItemProps = { readonly entity: User; diff --git a/packages/console/src/components/EntitiesTransfer/components/SourceEntitiesBox/index.tsx b/packages/console/src/components/EntitiesTransfer/components/SourceEntitiesBox/index.tsx index e8402edf490..01715df9161 100644 --- a/packages/console/src/components/EntitiesTransfer/components/SourceEntitiesBox/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/SourceEntitiesBox/index.tsx @@ -6,7 +6,7 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import Search from '@/assets/icons/search.svg'; +import Search from '@/assets/icons/search.svg?react'; import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import { defaultPageSize } from '@/consts'; import DynamicT from '@/ds-components/DynamicT'; @@ -14,13 +14,13 @@ import Pagination from '@/ds-components/Pagination'; import TextInput from '@/ds-components/TextInput'; import type { RequestError } from '@/hooks/use-api'; import useDebounce from '@/hooks/use-debounce'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import { type Identifiable } from '@/types/general'; import { buildUrl, formatSearchKeyword } from '@/utils/url'; import SourceEntityItem from '../SourceEntityItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type SearchProps = { pathname: string; diff --git a/packages/console/src/components/EntitiesTransfer/components/SourceEntityItem/index.tsx b/packages/console/src/components/EntitiesTransfer/components/SourceEntityItem/index.tsx index b26f110b3ae..1c3dc0d1820 100644 --- a/packages/console/src/components/EntitiesTransfer/components/SourceEntityItem/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/SourceEntityItem/index.tsx @@ -4,7 +4,7 @@ import Checkbox from '@/ds-components/Checkbox'; import { type Identifiable } from '@/types/general'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly entity: T; diff --git a/packages/console/src/components/EntitiesTransfer/components/TargetEntitiesBox/index.tsx b/packages/console/src/components/EntitiesTransfer/components/TargetEntitiesBox/index.tsx index 145cbbfb31b..7c19d6e69cf 100644 --- a/packages/console/src/components/EntitiesTransfer/components/TargetEntitiesBox/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/TargetEntitiesBox/index.tsx @@ -1,11 +1,11 @@ import { useTranslation } from 'react-i18next'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import { type Identifiable } from '@/types/general'; import TargetEntityItem from '../TargetEntityItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly renderEntity: (entity: T) => React.ReactNode; diff --git a/packages/console/src/components/EntitiesTransfer/components/TargetEntityItem/index.tsx b/packages/console/src/components/EntitiesTransfer/components/TargetEntityItem/index.tsx index 8c71be5d205..47c4f05b19b 100644 --- a/packages/console/src/components/EntitiesTransfer/components/TargetEntityItem/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/components/TargetEntityItem/index.tsx @@ -1,10 +1,10 @@ import { type ReactNode } from 'react'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import IconButton from '@/ds-components/IconButton'; import { type Identifiable } from '@/types/general'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly entity: T; diff --git a/packages/console/src/components/EntitiesTransfer/index.tsx b/packages/console/src/components/EntitiesTransfer/index.tsx index ffec093f2ef..87d4ca74344 100644 --- a/packages/console/src/components/EntitiesTransfer/index.tsx +++ b/packages/console/src/components/EntitiesTransfer/index.tsx @@ -1,11 +1,11 @@ import classNames from 'classnames'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import { type Identifiable } from '@/types/general'; import SourceEntitiesBox, { type Props as SourceProps } from './components/SourceEntitiesBox'; import TargetEntitiesBox from './components/TargetEntitiesBox'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = SourceProps & { readonly errorMessage?: string; diff --git a/packages/console/src/components/FeatureTag/AddOnTag.tsx b/packages/console/src/components/FeatureTag/AddOnTag.tsx new file mode 100644 index 00000000000..68112af7ab2 --- /dev/null +++ b/packages/console/src/components/FeatureTag/AddOnTag.tsx @@ -0,0 +1,19 @@ +import classNames from 'classnames'; + +/** + * AddOnTag static component + * + * Used to indicate that a feature is add-on feature and will be charged according to usage. + */ + +import styles from './index.module.scss'; + +type Props = { + readonly className?: string; +}; + +function AddOnTag({ className }: Props) { + return
Add-on
; +} + +export default AddOnTag; diff --git a/packages/console/src/components/FeatureTag/BetaTag.tsx b/packages/console/src/components/FeatureTag/BetaTag.tsx index 4bbe194a095..452eab39d3b 100644 --- a/packages/console/src/components/FeatureTag/BetaTag.tsx +++ b/packages/console/src/components/FeatureTag/BetaTag.tsx @@ -6,7 +6,7 @@ import classNames from 'classnames'; * Used to indicate that a new released feature is in beta. */ -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/FeatureTag/index.tsx b/packages/console/src/components/FeatureTag/index.tsx index 6363a58518a..152ae00f037 100644 --- a/packages/console/src/components/FeatureTag/index.tsx +++ b/packages/console/src/components/FeatureTag/index.tsx @@ -1,14 +1,17 @@ -import { type ReservedPlanId } from '@logto/schemas'; +import { ReservedPlanId } from '@logto/schemas'; import classNames from 'classnames'; import { useContext } from 'react'; +import { isDevFeaturesEnabled } from '@/consts/env'; +import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { TenantsContext } from '@/contexts/TenantsProvider'; -import * as styles from './index.module.scss'; +import AddOnTag from './AddOnTag'; +import styles from './index.module.scss'; export { default as BetaTag } from './BetaTag'; -type Props = { +export type Props = { /** * Whether the tag should be visible. It should be `true` if the tenant's subscription * plan has NO access to the feature (paywall), but it will always be visible for dev @@ -50,6 +53,9 @@ type Props = { function FeatureTag(props: Props) { const { className } = props; const { isDevTenant } = useContext(TenantsContext); + const { + currentSubscription: { planId }, + } = useContext(SubscriptionDataContext); const { isVisible, plan } = props; @@ -59,6 +65,11 @@ function FeatureTag(props: Props) { return null; } + // Show the add-on tag for Pro plan when dev features are enabled. + if (isDevFeaturesEnabled && planId === ReservedPlanId.Pro) { + return ; + } + return
{plan}
; } diff --git a/packages/console/src/components/FileIcon/index.tsx b/packages/console/src/components/FileIcon/index.tsx new file mode 100644 index 00000000000..29c8585ff8b --- /dev/null +++ b/packages/console/src/components/FileIcon/index.tsx @@ -0,0 +1,20 @@ +import { Theme } from '@logto/schemas'; +import { type ReactNode } from 'react'; + +import FileIconDark from '@/assets/icons/file-icon-dark.svg?react'; +import FileIconLight from '@/assets/icons/file-icon.svg?react'; +import useTheme from '@/hooks/use-theme'; + +const themeToRoleIcon = Object.freeze({ + [Theme.Light]: , + [Theme.Dark]: , +} satisfies Record); + +/** Render a role icon according to the current theme. */ +const FileIcon = () => { + const theme = useTheme(); + + return themeToRoleIcon[theme]; +}; + +export default FileIcon; diff --git a/packages/console/src/components/FormCard/FormCardLayout/index.module.scss b/packages/console/src/components/FormCard/FormCardLayout/index.module.scss index a929b9f9f85..cb10864dbc3 100644 --- a/packages/console/src/components/FormCard/FormCardLayout/index.module.scss +++ b/packages/console/src/components/FormCard/FormCardLayout/index.module.scss @@ -25,7 +25,7 @@ $column-width: calc((100% - 23 * $gutter-width) / 24); width: calc($column-width * 16 + $gutter-width * 15); } - @container (max-width: 600px) { + @container (max-width: 800px) { .container { flex-direction: column; justify-content: unset; diff --git a/packages/console/src/components/FormCard/FormCardLayout/index.tsx b/packages/console/src/components/FormCard/FormCardLayout/index.tsx index a19c3c66343..1622ed74cf7 100644 --- a/packages/console/src/components/FormCard/FormCardLayout/index.tsx +++ b/packages/console/src/components/FormCard/FormCardLayout/index.tsx @@ -2,7 +2,7 @@ import { type ReactNode } from 'react'; import Card from '@/ds-components/Card'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly introduction: ReactNode; diff --git a/packages/console/src/components/FormCard/Skeleton/index.module.scss b/packages/console/src/components/FormCard/Skeleton/index.module.scss index 35406566d05..6a11b36f3c9 100644 --- a/packages/console/src/components/FormCard/Skeleton/index.module.scss +++ b/packages/console/src/components/FormCard/Skeleton/index.module.scss @@ -1,27 +1,17 @@ @use '@/scss/underscore' as _; .title { - @include _.shimmering-animation; height: 16px; width: 80px; + @include _.shimmering-animation; } .text { - @include _.shimmering-animation; width: 100%; height: 10px; + @include _.shimmering-animation; } .text + .text { margin-top: _.unit(2); } - -.field { - @include _.shimmering-animation; - width: 100%; - height: 44px; -} - -.field + .field { - margin-top: _.unit(6); -} diff --git a/packages/console/src/components/FormCard/Skeleton/index.tsx b/packages/console/src/components/FormCard/Skeleton/index.tsx index cafb1f21940..c9fc9f5a216 100644 --- a/packages/console/src/components/FormCard/Skeleton/index.tsx +++ b/packages/console/src/components/FormCard/Skeleton/index.tsx @@ -1,6 +1,8 @@ +import FormFieldSkeleton from '@/ds-components/FormField/Skeleton'; + import FormCardLayout from '../FormCardLayout'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly formFieldCount?: number; @@ -21,10 +23,7 @@ function Skeleton({ formFieldCount = 4 }: Props) { } > - {Array.from({ length: formFieldCount }).map((_, index) => ( - // eslint-disable-next-line react/no-array-index-key -
- ))} + ); } diff --git a/packages/console/src/components/FormCard/index.tsx b/packages/console/src/components/FormCard/index.tsx index 565a3615a49..6befab89d5c 100644 --- a/packages/console/src/components/FormCard/index.tsx +++ b/packages/console/src/components/FormCard/index.tsx @@ -6,7 +6,7 @@ import TextLink from '@/ds-components/TextLink'; import type { Props as TextLinkProps } from '@/ds-components/TextLink'; import FormCardLayout from './FormCardLayout'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export type Props = { readonly title: AdminConsoleKey; diff --git a/packages/console/src/components/Guide/GuideCard/index.module.scss b/packages/console/src/components/Guide/GuideCard/index.module.scss index c962c7593e1..bf6890d3348 100644 --- a/packages/console/src/components/Guide/GuideCard/index.module.scss +++ b/packages/console/src/components/Guide/GuideCard/index.module.scss @@ -32,9 +32,14 @@ } .logo { - width: 48px; - height: 48px; + padding: _.unit(1.5); flex-shrink: 0; + + > svg, + > img { + width: 36px; + height: 36px; + } } .tagWrapper { diff --git a/packages/console/src/components/Guide/GuideCard/index.tsx b/packages/console/src/components/Guide/GuideCard/index.tsx index fef3471ecf8..c785aaf1326 100644 --- a/packages/console/src/components/Guide/GuideCard/index.tsx +++ b/packages/console/src/components/Guide/GuideCard/index.tsx @@ -1,4 +1,4 @@ -import { ReservedPlanId } from '@logto/schemas'; +import { ReservedPlanId, Theme } from '@logto/schemas'; import classNames from 'classnames'; import { Suspense, useCallback, useContext } from 'react'; @@ -7,9 +7,10 @@ import FeatureTag, { BetaTag } from '@/components/FeatureTag'; import { isCloud } from '@/consts/env'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import Button from '@/ds-components/Button'; +import useTheme from '@/hooks/use-theme'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export type SelectedGuide = { id: Guide['id']; @@ -29,11 +30,13 @@ function GuideCard({ data, onClick, hasBorder, hasButton }: Props) { const { id, Logo, + DarkLogo, metadata: { target, name, description, isThirdParty }, } = data; const buttonText = target === 'API' ? 'guide.get_started' : 'guide.start_building'; const { currentPlan } = useContext(SubscriptionDataContext); + const theme = useTheme(); const showPaywallTag = isCloud && isThirdParty; const showBetaTag = isCloud && isThirdParty; @@ -58,7 +61,9 @@ function GuideCard({ data, onClick, hasBorder, hasButton }: Props) { >
}> - +
+ {theme === Theme.Dark && DarkLogo ? : } +
diff --git a/packages/console/src/components/Guide/GuideCardGroup/index.tsx b/packages/console/src/components/Guide/GuideCardGroup/index.tsx index e23fb721fef..a979d68e5d0 100644 --- a/packages/console/src/components/Guide/GuideCardGroup/index.tsx +++ b/packages/console/src/components/Guide/GuideCardGroup/index.tsx @@ -5,7 +5,7 @@ import { type Guide } from '@/assets/docs/guides/types'; import GuideCard, { type SelectedGuide } from '../GuideCard'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/Guide/ModalFooter/index.tsx b/packages/console/src/components/Guide/ModalFooter/index.tsx index d43a8960221..42862ea5d18 100644 --- a/packages/console/src/components/Guide/ModalFooter/index.tsx +++ b/packages/console/src/components/Guide/ModalFooter/index.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import Button from '@/ds-components/Button'; import DynamicT from '@/ds-components/DynamicT'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly wrapperClassName?: string; diff --git a/packages/console/src/components/Guide/ModalHeader/RequestForm.tsx b/packages/console/src/components/Guide/ModalHeader/RequestForm.tsx index afa1a94c72b..7232c3b6cd8 100644 --- a/packages/console/src/components/Guide/ModalHeader/RequestForm.tsx +++ b/packages/console/src/components/Guide/ModalHeader/RequestForm.tsx @@ -10,7 +10,7 @@ import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; import TextInput from '@/ds-components/TextInput'; import useCurrentUser from '@/hooks/use-current-user'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; type Props = { readonly title: AdminConsoleKey; diff --git a/packages/console/src/components/Guide/ModalHeader/index.tsx b/packages/console/src/components/Guide/ModalHeader/index.tsx index dbef8c32208..99f40efdb20 100644 --- a/packages/console/src/components/Guide/ModalHeader/index.tsx +++ b/packages/console/src/components/Guide/ModalHeader/index.tsx @@ -1,14 +1,14 @@ import { type AdminConsoleKey } from '@logto/phrases'; import { useCallback, useState } from 'react'; -import Box from '@/assets/icons/box.svg'; +import Box from '@/assets/icons/box.svg?react'; import { githubIssuesLink } from '@/consts'; import { isCloud } from '@/consts/env'; import Button from '@/ds-components/Button'; import DsModalHeader from '@/ds-components/ModalHeader'; import RequestForm from './RequestForm'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title: AdminConsoleKey; diff --git a/packages/console/src/components/Guide/StepsSkeleton/index.module.scss b/packages/console/src/components/Guide/StepsSkeleton/index.module.scss index e221c516409..e9c657a4ac3 100644 --- a/packages/console/src/components/Guide/StepsSkeleton/index.module.scss +++ b/packages/console/src/components/Guide/StepsSkeleton/index.module.scss @@ -11,11 +11,11 @@ margin: 0 auto; .index { - @include _.shimmering-animation; width: 28px; height: 28px; border-radius: 50%; margin-right: _.unit(4); + @include _.shimmering-animation; } .wrapper { @@ -24,16 +24,16 @@ flex-direction: column; .title { - @include _.shimmering-animation; width: 140px; height: 24px; + @include _.shimmering-animation; } .subtitle { - @include _.shimmering-animation; width: 400px; height: 20px; margin-top: _.unit(1); + @include _.shimmering-animation; } } } diff --git a/packages/console/src/components/Guide/StepsSkeleton/index.tsx b/packages/console/src/components/Guide/StepsSkeleton/index.tsx index 7d3ed4e75d9..5557f532f57 100644 --- a/packages/console/src/components/Guide/StepsSkeleton/index.tsx +++ b/packages/console/src/components/Guide/StepsSkeleton/index.tsx @@ -1,4 +1,4 @@ -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function StepsSkeleton() { return ( diff --git a/packages/console/src/components/Guide/index.tsx b/packages/console/src/components/Guide/index.tsx index be312df981e..d68f7613928 100644 --- a/packages/console/src/components/Guide/index.tsx +++ b/packages/console/src/components/Guide/index.tsx @@ -7,10 +7,11 @@ import { type GuideMetadata } from '@/assets/docs/guides/types'; import Button from '@/ds-components/Button'; import OverlayScrollbar from '@/ds-components/OverlayScrollbar'; import MdxProvider from '@/mdx-components/MdxProvider'; +import { type ApplicationSecretRow } from '@/pages/ApplicationDetails/ApplicationDetailsContent/EndpointsAndCredentials'; import NotFound from '@/pages/NotFound'; import StepsSkeleton from './StepsSkeleton'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; export type GuideContextType = { metadata: Readonly; @@ -19,6 +20,7 @@ export type GuideContextType = { | ((props: { readonly className?: string }) => JSX.Element); isCompact: boolean; app?: ApplicationResponse; + secrets?: ApplicationSecretRow[]; endpoint?: string; redirectUris?: string[]; postLogoutRedirectUris?: string[]; @@ -40,6 +42,7 @@ export const GuideContext = createContext({ metadata: {} as GuideMetadata, // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, no-restricted-syntax app: {} as ApplicationResponse, + secrets: [], endpoint: '', redirectUris: [], postLogoutRedirectUris: [], diff --git a/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx b/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx new file mode 100644 index 00000000000..b60af048eb6 --- /dev/null +++ b/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx @@ -0,0 +1,62 @@ +import { type Theme } from '@logto/schemas'; +import { type FieldValues, type Control, type UseFormRegister } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import ImageInputs, { type ImageField } from '.'; + +type Field = Pick, 'name' | 'error'>; + +type Props = { + readonly theme: Theme; + readonly control: Control; + readonly register: UseFormRegister; + /** + * Form-related data of the logo input, including the name (field path) and error in the form. + */ + readonly logo: Field; + /** + * Form-related data of the favicon input, including the name (field path) and error in the form. + */ + readonly favicon: Field; + /** The type of the logo. It will affect the translation key. */ + readonly type: 'app_logo' | 'company_logo'; +}; + +/** + * A component that renders the logo and favicon inputs for a form. + * + * When user assets service is available, it will render two image uploader components side-by-side; + * otherwise, it will render two text inputs. + * + * @see {@link ImageInputs} for the implementation of the inner components. + */ +function LogoAndFavicon({ + theme, + control, + register, + logo, + favicon, + type, +}: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + + return ( + + {t(`sign_in_exp.branding.with_${theme}`, { + value: t(`sign_in_exp.branding.${type}_and_favicon`), + })} + + } + control={control} + register={register} + fields={[ + { ...logo, theme, type }, + { ...favicon, theme, type: 'favicon' }, + ]} + /> + ); +} + +export default LogoAndFavicon; diff --git a/packages/console/src/pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader/index.module.scss b/packages/console/src/components/ImageInputs/index.module.scss similarity index 69% rename from packages/console/src/pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader/index.module.scss rename to packages/console/src/components/ImageInputs/index.module.scss index 1f8c81e86ab..4e24be27ac4 100644 --- a/packages/console/src/pages/SignInExperience/PageContent/Branding/BrandingForm/LogoAndFaviconUploader/index.module.scss +++ b/packages/console/src/components/ImageInputs/index.module.scss @@ -1,20 +1,19 @@ @use '@/scss/underscore' as _; .container { - display: flex; - flex-direction: column; -} - -.uploader { display: flex; gap: _.unit(2); - .logoUploader { - flex: 2 0; + > * { + flex: 1; + + &.dark { + background-color: #111; + } } - .faviconUploader { - flex: 1; + .logo { + flex: 3; } } diff --git a/packages/console/src/components/ImageInputs/index.tsx b/packages/console/src/components/ImageInputs/index.tsx new file mode 100644 index 00000000000..6dea4890cba --- /dev/null +++ b/packages/console/src/components/ImageInputs/index.tsx @@ -0,0 +1,169 @@ +import { type LocalePhrase } from '@logto/phrases'; +import { Theme } from '@logto/schemas'; +import { cond, noop } from '@silverhand/essentials'; +import classNames from 'classnames'; +import type React from 'react'; +import { useMemo, useState } from 'react'; +import { + Controller, + type FieldPath, + type FieldValues, + type Control, + type UseFormRegister, + type FieldError, +} from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import FormField from '@/ds-components/FormField'; +import Skeleton from '@/ds-components/FormField/Skeleton'; +import TextInput from '@/ds-components/TextInput'; +import ImageUploader from '@/ds-components/Uploader/ImageUploader'; +import useImageMimeTypes from '@/hooks/use-image-mime-types'; +import useUserAssetsService from '@/hooks/use-user-assets-service'; +import { uriValidator } from '@/utils/validator'; + +import styles from './index.module.scss'; + +export const themeToLogoName = Object.freeze({ + [Theme.Light]: 'logoUrl', + [Theme.Dark]: 'darkLogoUrl', +} as const satisfies Record); + +export type ImageField = { + /** The name (field path) of the field in the form. */ + name: FieldPath; + /** + * The type of the field. It should match the existing structure in the translation file to get + * the correct translations. + */ + type: keyof LocalePhrase['translation']['admin_console']['sign_in_exp']['branding_uploads']; + theme: Theme; + /** The error message of the field in the form. */ + error?: FieldError; +}; + +type Props = { + /** The condensed title when user assets service is available. */ + readonly uploadTitle: React.ComponentProps['title']; + /** + * When user assets service is available, the tip will be displayed for the `uploadTitle`; + * otherwise, it will be displayed for each text input. + */ + readonly tip?: React.ComponentProps['tip']; + readonly control: Control; + readonly register: UseFormRegister; + readonly fields: Array>; +}; + +/** + * A component that renders the logo inputs for a form. + * + * When user assets service is available, it will render the image uploader components side-by-side; + * otherwise, it will render the text inputs. + */ +function ImageInputs({ + uploadTitle, + tip, + control, + register, + fields, +}: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const [uploadErrors, setUploadErrors] = useState>>({}); + const { description } = useImageMimeTypes(); + const { isReady: isUserAssetsServiceReady, isLoading } = useUserAssetsService(); + const uploadErrorChangeHandlers = useMemo( + () => + Object.fromEntries( + fields.map((field) => [ + field.name, + (message?: string) => { + setUploadErrors((previous) => ({ ...previous, [field.name]: message })); + }, + ]) + ), + [fields] + ); + + if (isLoading) { + return ; + } + + if (!isUserAssetsServiceReady) { + return ( + <> + {fields.map((field) => ( + + {t(`sign_in_exp.branding.with_${field.theme}`, { + value: t(`sign_in_exp.branding_uploads.${field.type}.url`), + })} + + } + > + + !value || uriValidator(value) || t('errors.invalid_uri_format'), + shouldUnregister: true, + })} + placeholder={t(`sign_in_exp.branding_uploads.${field.type}.url_placeholder`)} + error={field.error?.message} + /> + + ))} + + ); + } + + return ( + +
+ {fields.map((field) => ( + ( + { + onChange(url); + }} + // Noop fallback should not be necessary, but for TypeScript to be happy + onUploadErrorChange={uploadErrorChangeHandlers[field.name] ?? noop} + onDelete={() => { + onChange(''); + }} + /> + )} + /> + ))} +
+ {fields.map( + (field) => + uploadErrors[field.name] && ( +
+ {t(`sign_in_exp.branding_uploads.${field.type}.error`, { + error: uploadErrors[field.name], + })} +
+ ) + )} + +
{description}
+
+ ); +} + +export default ImageInputs; diff --git a/packages/console/src/components/Index/index.tsx b/packages/console/src/components/Index/index.tsx index d9584593dca..4c9036e0e44 100644 --- a/packages/console/src/components/Index/index.tsx +++ b/packages/console/src/components/Index/index.tsx @@ -1,8 +1,8 @@ import classNames from 'classnames'; -import Tick from '@/assets/icons/tick.svg'; +import Tick from '@/assets/icons/tick.svg?react'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/InlineUpsell/index.tsx b/packages/console/src/components/InlineUpsell/index.tsx index 1f57a042c43..ca2fd0eb152 100644 --- a/packages/console/src/components/InlineUpsell/index.tsx +++ b/packages/console/src/components/InlineUpsell/index.tsx @@ -8,7 +8,7 @@ import useTenantPathname from '@/hooks/use-tenant-pathname'; import ContactUsPhraseLink from '../ContactUsPhraseLink'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/ItemPreview/ApplicationPreview.tsx b/packages/console/src/components/ItemPreview/ApplicationPreview.tsx index e284a5894c6..438d322834d 100644 --- a/packages/console/src/components/ItemPreview/ApplicationPreview.tsx +++ b/packages/console/src/components/ItemPreview/ApplicationPreview.tsx @@ -5,7 +5,7 @@ import ApplicationIcon from '@/components/ApplicationIcon'; import { applicationTypeI18nKey } from '@/types/applications'; import ItemPreview from '.'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; const applicationsPathname = '/applications'; const buildDetailsPathname = (id: string) => `${applicationsPathname}/${id}`; diff --git a/packages/console/src/components/ItemPreview/index.tsx b/packages/console/src/components/ItemPreview/index.tsx index e7447b76e06..2728e184848 100644 --- a/packages/console/src/components/ItemPreview/index.tsx +++ b/packages/console/src/components/ItemPreview/index.tsx @@ -5,7 +5,7 @@ import { Link } from 'react-router-dom'; import useTenantPathname from '@/hooks/use-tenant-pathname'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly title: ReactNode; diff --git a/packages/console/src/components/ListPage/index.tsx b/packages/console/src/components/ListPage/index.tsx index c59d75fea1d..26abe9e481b 100644 --- a/packages/console/src/components/ListPage/index.tsx +++ b/packages/console/src/components/ListPage/index.tsx @@ -2,14 +2,14 @@ import classNames from 'classnames'; import { type ReactNode } from 'react'; import { type FieldValues, type FieldPath } from 'react-hook-form'; -import Plus from '@/assets/icons/plus.svg'; +import Plus from '@/assets/icons/plus.svg?react'; import PageMeta, { type Props as PageMetaProps } from '@/components/PageMeta'; import { type Props as ButtonProps } from '@/ds-components/Button'; import Button from '@/ds-components/Button'; import { type Props as CardTitleProps } from '@/ds-components/CardTitle'; import CardTitle from '@/ds-components/CardTitle'; import Table, { type Props as TableProps } from '@/ds-components/Table'; -import * as pageLayout from '@/scss/page-layout.module.scss'; +import pageLayout from '@/scss/page-layout.module.scss'; type CreateButtonProps = { title: ButtonProps['title']; diff --git a/packages/console/src/components/LivePreviewButton/index.tsx b/packages/console/src/components/LivePreviewButton/index.tsx index 6a1ec955d3f..c773cfaac34 100644 --- a/packages/console/src/components/LivePreviewButton/index.tsx +++ b/packages/console/src/components/LivePreviewButton/index.tsx @@ -3,13 +3,13 @@ import classNames from 'classnames'; import { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import ExternalLinkIcon from '@/assets/icons/external-link.svg'; +import ExternalLinkIcon from '@/assets/icons/external-link.svg?react'; import { AppDataContext } from '@/contexts/AppDataProvider'; import type { Props as ButtonProps, ButtonType } from '@/ds-components/Button'; import Button from '@/ds-components/Button'; import { Tooltip } from '@/ds-components/Tip'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly size?: ButtonProps['size']; diff --git a/packages/console/src/components/ManageOrganizationPermissionModal/index.tsx b/packages/console/src/components/ManageOrganizationPermissionModal/index.tsx index bca34f4503a..153802ff340 100644 --- a/packages/console/src/components/ManageOrganizationPermissionModal/index.tsx +++ b/packages/console/src/components/ManageOrganizationPermissionModal/index.tsx @@ -10,7 +10,7 @@ import FormField from '@/ds-components/FormField'; import ModalLayout from '@/ds-components/ModalLayout'; import TextInput from '@/ds-components/TextInput'; import useApi from '@/hooks/use-api'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { trySubmitSafe } from '@/utils/form'; type Props = { diff --git a/packages/console/src/components/Markdown/index.tsx b/packages/console/src/components/Markdown/index.tsx index c8cf7c6eb97..7fb575a7474 100644 --- a/packages/console/src/components/Markdown/index.tsx +++ b/packages/console/src/components/Markdown/index.tsx @@ -4,6 +4,8 @@ import classNames from 'classnames'; import { memo, useRef } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; + +// TODO: @charles double check if this is still needed /** * Workaround for the markdown crash issue in the parcel dev build. It seems parcel does * something clever in dev mode and messing up the `hastToReact` module. Manually adding @@ -17,7 +19,7 @@ import 'property-information'; import CodeEditor from '@/ds-components/CodeEditor'; import GithubRawImage from './components/GithubRawImage'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly className?: string; diff --git a/packages/console/src/components/MauExceededModal/index.tsx b/packages/console/src/components/MauExceededModal/index.tsx index 3d44dddf692..835a14b4007 100644 --- a/packages/console/src/components/MauExceededModal/index.tsx +++ b/packages/console/src/components/MauExceededModal/index.tsx @@ -4,6 +4,7 @@ import ReactModal from 'react-modal'; import PlanUsage from '@/components/PlanUsage'; import { contactEmailLink } from '@/consts'; +import { isDevFeaturesEnabled } from '@/consts/env'; import { subscriptionPage } from '@/consts/pages'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { TenantsContext } from '@/contexts/TenantsProvider'; @@ -12,16 +13,22 @@ import FormField from '@/ds-components/FormField'; import InlineNotification from '@/ds-components/InlineNotification'; import ModalLayout from '@/ds-components/ModalLayout'; import useTenantPathname from '@/hooks/use-tenant-pathname'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import PlanName from '../PlanName'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function MauExceededModal() { const { currentTenant } = useContext(TenantsContext); const { usage } = currentTenant ?? {}; - const { currentPlan, currentSubscription } = useContext(SubscriptionDataContext); + const { + currentPlan, + currentSubscription, + currentSku, + currentSubscriptionQuota, + currentSubscriptionUsage, + } = useContext(SubscriptionDataContext); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { navigate } = useTenantPathname(); @@ -40,7 +47,10 @@ function MauExceededModal() { name: planName, } = currentPlan; - const isMauExceeded = mauLimit !== null && usage.activeUsers >= mauLimit; + const isMauExceeded = isDevFeaturesEnabled + ? currentSubscriptionQuota.mauLimit !== null && + currentSubscriptionUsage.mauLimit >= currentSubscriptionQuota.mauLimit + : mauLimit !== null && usage.activeUsers >= mauLimit; if (!isMauExceeded) { return null; @@ -76,7 +86,7 @@ function MauExceededModal() { , + planName: , }} > {t('upsell.mau_exceeded_modal.notification')} diff --git a/packages/console/src/components/MfaFactorTitle/index.tsx b/packages/console/src/components/MfaFactorTitle/index.tsx index a32c32339f5..8f75cafb8b4 100644 --- a/packages/console/src/components/MfaFactorTitle/index.tsx +++ b/packages/console/src/components/MfaFactorTitle/index.tsx @@ -1,16 +1,16 @@ import { MfaFactor } from '@logto/schemas'; import { type ReactNode } from 'react'; -import FactorBackupCode from '@/assets/icons/factor-backup-code.svg'; -import FactorTotp from '@/assets/icons/factor-totp.svg'; -import FactorWebAuthn from '@/assets/icons/factor-webauthn.svg'; -import Tip from '@/assets/icons/tip.svg'; +import FactorBackupCode from '@/assets/icons/factor-backup-code.svg?react'; +import FactorTotp from '@/assets/icons/factor-totp.svg?react'; +import FactorWebAuthn from '@/assets/icons/factor-webauthn.svg?react'; +import Tip from '@/assets/icons/tip.svg?react'; import IconButton from '@/ds-components/IconButton'; import { ToggleTip } from '@/ds-components/Tip'; import MfaFactorName, { type Props as MfaFactorNameProps } from '../MfaFactorName'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; const factorIcon: Record = { [MfaFactor.TOTP]: FactorTotp, diff --git a/packages/console/src/components/MultiOptionInput/index.tsx b/packages/console/src/components/MultiOptionInput/index.tsx index 68d5db09eb7..47cc0f85c79 100644 --- a/packages/console/src/components/MultiOptionInput/index.tsx +++ b/packages/console/src/components/MultiOptionInput/index.tsx @@ -2,12 +2,12 @@ import { isKeyInObject, type Nullable } from '@silverhand/essentials'; import classNames from 'classnames'; import { type ReactNode, useRef, useState, useCallback } from 'react'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import IconButton from '@/ds-components/IconButton'; import Tag from '@/ds-components/Tag'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type CanBePromise = T | Promise; diff --git a/packages/console/src/components/MultiTextInputField/index.tsx b/packages/console/src/components/MultiTextInputField/index.tsx index 69791ecaeff..d797a4e1891 100644 --- a/packages/console/src/components/MultiTextInputField/index.tsx +++ b/packages/console/src/components/MultiTextInputField/index.tsx @@ -5,7 +5,7 @@ import FormField from '@/ds-components/FormField'; import type { Props as MultiTextInputProps } from '@/ds-components/MultiTextInput'; import MultiTextInput from '@/ds-components/MultiTextInput'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = MultiTextInputProps & Pick & { diff --git a/packages/console/src/components/OpenExternalLink/index.tsx b/packages/console/src/components/OpenExternalLink/index.tsx index c6e93e6c345..649fbde7fd3 100644 --- a/packages/console/src/components/OpenExternalLink/index.tsx +++ b/packages/console/src/components/OpenExternalLink/index.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; -import ExternalLinkIcon from '@/assets/icons/external-link.svg'; +import ExternalLinkIcon from '@/assets/icons/external-link.svg?react'; import IconButton from '@/ds-components/IconButton'; import { Tooltip } from '@/ds-components/Tip'; diff --git a/packages/console/src/components/OrganizationList/index.tsx b/packages/console/src/components/OrganizationList/index.tsx index a924e830646..aaef30d4f74 100644 --- a/packages/console/src/components/OrganizationList/index.tsx +++ b/packages/console/src/components/OrganizationList/index.tsx @@ -3,8 +3,8 @@ import { type ReactNode, useState } from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import OrganizationIcon from '@/assets/icons/organization-preview.svg'; -import Tip from '@/assets/icons/tip.svg'; +import OrganizationIcon from '@/assets/icons/organization-preview.svg?react'; +import Tip from '@/assets/icons/tip.svg?react'; import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import ItemPreview from '@/components/ItemPreview'; import { RoleOption } from '@/components/OrganizationRolesSelect'; @@ -18,7 +18,7 @@ import { ToggleTip } from '@/ds-components/Tip'; import { type RequestError } from '@/hooks/use-api'; import useTenantPathname from '@/hooks/use-tenant-pathname'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly type: 'user' | 'application'; diff --git a/packages/console/src/components/OrganizationRolesSelect/index.tsx b/packages/console/src/components/OrganizationRolesSelect/index.tsx index 452100f8980..f78a09f0876 100644 --- a/packages/console/src/components/OrganizationRolesSelect/index.tsx +++ b/packages/console/src/components/OrganizationRolesSelect/index.tsx @@ -1,14 +1,14 @@ import { type OrganizationRole, type RoleType } from '@logto/schemas'; import classNames from 'classnames'; -import RoleIcon from '@/assets/icons/organization-role-feature.svg'; +import RoleIcon from '@/assets/icons/organization-role-feature.svg?react'; import MultiSelect, { type Option } from '@/ds-components/Select/MultiSelect'; import useSearchValues from '@/hooks/use-search-values'; import Breakable from '../Breakable'; import ThemedIcon from '../ThemedIcon'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type RoleOptionProps = { readonly title?: string; diff --git a/packages/console/src/components/PaymentOverdueModal/index.tsx b/packages/console/src/components/PaymentOverdueModal/index.tsx index 78029388510..73e31ff27fe 100644 --- a/packages/console/src/components/PaymentOverdueModal/index.tsx +++ b/packages/console/src/components/PaymentOverdueModal/index.tsx @@ -10,11 +10,11 @@ import FormField from '@/ds-components/FormField'; import InlineNotification from '@/ds-components/InlineNotification'; import ModalLayout from '@/ds-components/ModalLayout'; import useSubscribe from '@/hooks/use-subscribe'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import BillInfo from '../BillInfo'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; function PaymentOverdueModal() { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); diff --git a/packages/console/src/components/PermissionsTable/index.tsx b/packages/console/src/components/PermissionsTable/index.tsx index 3097491f369..0a2b62fab26 100644 --- a/packages/console/src/components/PermissionsTable/index.tsx +++ b/packages/console/src/components/PermissionsTable/index.tsx @@ -5,9 +5,9 @@ import { useState } from 'react'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; -import Plus from '@/assets/icons/plus.svg'; -import PermissionsEmptyDark from '@/assets/images/permissions-empty-dark.svg'; -import PermissionsEmpty from '@/assets/images/permissions-empty.svg'; +import Plus from '@/assets/icons/plus.svg?react'; +import PermissionsEmptyDark from '@/assets/images/permissions-empty-dark.svg?react'; +import PermissionsEmpty from '@/assets/images/permissions-empty.svg?react'; import { ApiResourceDetailsTabs } from '@/consts/page-tabs'; import Button from '@/ds-components/Button'; import type { Props as PaginationProps } from '@/ds-components/Pagination'; @@ -24,7 +24,7 @@ import ActionsButton from '../ActionsButton'; import EditScopeModal, { type EditScopeData } from '../EditScopeModal'; import EmptyDataPlaceholder from '../EmptyDataPlaceholder'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type SearchProps = { keyword: string; diff --git a/packages/console/src/components/PlanDescription/index.tsx b/packages/console/src/components/PlanDescription/index.tsx index f39a94c1b66..1db3e078542 100644 --- a/packages/console/src/components/PlanDescription/index.tsx +++ b/packages/console/src/components/PlanDescription/index.tsx @@ -1,4 +1,5 @@ import { ReservedPlanId } from '@logto/schemas'; +import { conditional } from '@silverhand/essentials'; import { type TFuncKey } from 'i18next'; import DynamicT from '@/ds-components/DynamicT'; @@ -11,10 +12,17 @@ const registeredPlanDescriptionPhrasesMap: Record< [ReservedPlanId.Pro]: 'pro_plan_description', }; -type Props = { readonly planId: string }; +type Props = { + /** Temporarily mark as optional. */ + readonly skuId?: string; + /** @deprecated */ + readonly planId: string; +}; -function PlanDescription({ planId }: Props) { - const description = registeredPlanDescriptionPhrasesMap[planId]; +function PlanDescription({ skuId, planId }: Props) { + const description = + conditional(skuId && registeredPlanDescriptionPhrasesMap[skuId]) ?? + registeredPlanDescriptionPhrasesMap[planId]; if (!description) { return null; diff --git a/packages/console/src/components/PlanName/index.tsx b/packages/console/src/components/PlanName/index.tsx index 2dc5f8e77b4..45cdc98d5ff 100644 --- a/packages/console/src/components/PlanName/index.tsx +++ b/packages/console/src/components/PlanName/index.tsx @@ -1,7 +1,8 @@ +import { conditional } from '@silverhand/essentials'; import { type TFuncKey } from 'i18next'; import { useTranslation } from 'react-i18next'; -import { ReservedPlanName } from '@/types/subscriptions'; +import { ReservedPlanName, ReservedSkuId } from '@/types/subscriptions'; const registeredPlanNamePhraseMap: Record< string, @@ -14,13 +15,28 @@ const registeredPlanNamePhraseMap: Record< [ReservedPlanName.Enterprise]: 'enterprise', }; +const registeredSkuIdNamePhraseMap: Record< + string, + TFuncKey<'translation', 'admin_console.subscription'> | undefined +> = { + quotaKey: undefined, + [ReservedSkuId.Free]: 'free_plan', + [ReservedSkuId.Pro]: 'pro_plan', + [ReservedSkuId.Enterprise]: 'enterprise', +}; + type Props = { + /** Temporarily use optional for backward compatibility. */ + readonly skuId?: string; + /** @deprecated */ readonly name: string; }; -function PlanName({ name }: Props) { +// TODO: rename the component once new pricing model is ready, should be `SkuName`. +function PlanName({ skuId, name }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.subscription' }); - const planNamePhrase = registeredPlanNamePhraseMap[name]; + const planNamePhrase = + conditional(skuId && registeredSkuIdNamePhraseMap[skuId]) ?? registeredPlanNamePhraseMap[name]; /** * Note: fallback to the plan name if the phrase is not registered. diff --git a/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.module.scss b/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.module.scss new file mode 100644 index 00000000000..3a5a84e5344 --- /dev/null +++ b/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.module.scss @@ -0,0 +1,35 @@ +@use '@/scss/underscore' as _; + +.card { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: left; + border-radius: 12px; + border: 1px solid var(--color-divider); + background: var(--color-layer-1); + padding: _.unit(5.5) _.unit(6); + gap: _.unit(6); +} + +.title { + font: var(--font-title-3); + color: var(--color-text-secondary); + display: flex; + align-items: center; +} + +.description { + font: var(--font-title-3); + color: var(--color-text); +} + +.usageTip { + font: var(--font-body-2); + color: var(--color-text-secondary); +} + +.tag { + padding-top: 1px; + padding-bottom: 1px; +} diff --git a/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.tsx b/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.tsx new file mode 100644 index 00000000000..70b60b8876e --- /dev/null +++ b/packages/console/src/components/PlanUsage/ProPlanUsageCard/index.tsx @@ -0,0 +1,78 @@ +import { type AdminConsoleKey } from '@logto/phrases'; +import classNames from 'classnames'; +import { Trans, useTranslation } from 'react-i18next'; + +import Tip from '@/assets/icons/tip.svg?react'; +import DynamicT from '@/ds-components/DynamicT'; +import IconButton from '@/ds-components/IconButton'; +import Tag from '@/ds-components/Tag'; +import TextLink from '@/ds-components/TextLink'; +import { ToggleTip } from '@/ds-components/Tip'; + +import { formatNumber } from '../utils'; + +import styles from './index.module.scss'; + +export type Props = { + readonly usage: number | boolean; + readonly quota?: number; + readonly usageKey: AdminConsoleKey; + readonly titleKey: AdminConsoleKey; + readonly tooltipKey: AdminConsoleKey; + readonly className?: string; +}; + +function ProPlanUsageCard({ usage, quota, usageKey, titleKey, tooltipKey, className }: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + + return ( +
+
+ + + + , + }} + > + {t(tooltipKey)} + + } + > + + + + +
+ {typeof usage === 'number' ? ( +
+ , + }} + > + {t(usageKey, { + usage: + quota && typeof quota === 'number' + ? `${formatNumber(usage)} / ${formatNumber(quota)}` + : formatNumber(usage), + })} + +
+ ) : ( +
+ + + +
+ )} +
+ ); +} + +export default ProPlanUsageCard; diff --git a/packages/console/src/components/PlanUsage/index.module.scss b/packages/console/src/components/PlanUsage/index.module.scss index 49bf92bd922..c01e37937d6 100644 --- a/packages/console/src/components/PlanUsage/index.module.scss +++ b/packages/console/src/components/PlanUsage/index.module.scss @@ -18,10 +18,27 @@ align-items: center; } +.newPricingModelUsage { + margin-top: _.unit(1); + display: flex; + flex-wrap: wrap; + gap: _.unit(2); +} + +.cardItem { + flex: 0 0 calc(33.333% - _.unit(2) * 2); + max-width: calc(33.333% - _.unit(2) * 2); + max-height: 112px; +} + .planCycle { font: var(--font-body-2); } +.planCycleNewPricingModel { + color: var(--color-text-secondary); +} + .usageBar { border-radius: 4px; background-color: var(--color-layer-2); diff --git a/packages/console/src/components/PlanUsage/index.tsx b/packages/console/src/components/PlanUsage/index.tsx index 96fb6345b54..6714d41f0c1 100644 --- a/packages/console/src/components/PlanUsage/index.tsx +++ b/packages/console/src/components/PlanUsage/index.tsx @@ -1,30 +1,80 @@ -import { conditional } from '@silverhand/essentials'; +import { ReservedPlanId } from '@logto/schemas'; +import { cond, conditional } from '@silverhand/essentials'; import classNames from 'classnames'; import dayjs from 'dayjs'; +import { useContext } from 'react'; import { type SubscriptionUsage, type Subscription } from '@/cloud/types/router'; +import { isDevFeaturesEnabled } from '@/consts/env'; +import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import DynamicT from '@/ds-components/DynamicT'; import { type SubscriptionPlan } from '@/types/subscriptions'; import { formatPeriod } from '@/utils/subscription'; -import * as styles from './index.module.scss'; +import ProPlanUsageCard, { type Props as ProPlanUsageCardProps } from './ProPlanUsageCard'; +import styles from './index.module.scss'; +import { usageKeys, usageKeyMap, titleKeyMap, tooltipKeyMap } from './utils'; type Props = { + /** @deprecated */ readonly subscriptionUsage: SubscriptionUsage; + /** @deprecated */ readonly currentSubscription: Subscription; + /** @deprecated */ readonly currentPlan: SubscriptionPlan; }; function PlanUsage({ subscriptionUsage, currentSubscription, currentPlan }: Props) { - const { currentPeriodStart, currentPeriodEnd } = currentSubscription; - const { activeUsers } = subscriptionUsage; const { - quota: { mauLimit }, - } = currentPlan; + currentSubscriptionQuota, + currentSubscriptionUsage, + currentSubscription: currentSubscriptionFromNewPricingModel, + } = useContext(SubscriptionDataContext); + + const { currentPeriodStart, currentPeriodEnd } = isDevFeaturesEnabled + ? currentSubscriptionFromNewPricingModel + : currentSubscription; + + const [activeUsers, mauLimit] = isDevFeaturesEnabled + ? [currentSubscriptionUsage.mauLimit, currentSubscriptionQuota.mauLimit] + : [subscriptionUsage.activeUsers, currentPlan.quota.mauLimit]; const usagePercent = conditional(mauLimit && activeUsers / mauLimit); - return ( + const usages: ProPlanUsageCardProps[] = usageKeys.map((key) => ({ + usage: currentSubscriptionUsage[key], + usageKey: `subscription.usage.${usageKeyMap[key]}`, + titleKey: `subscription.usage.${titleKeyMap[key]}`, + tooltipKey: `subscription.usage.${tooltipKeyMap[key]}`, + ...cond( + key === 'tokenLimit' && + currentSubscriptionQuota.tokenLimit && { quota: currentSubscriptionQuota.tokenLimit } + ), + })); + + return isDevFeaturesEnabled && + currentSubscriptionFromNewPricingModel.planId === ReservedPlanId.Pro ? ( +
+
+ +
+
+ {usages.map((props, index) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} +
+
+ ) : (
{`${activeUsers} / `} diff --git a/packages/console/src/components/PlanUsage/utils.ts b/packages/console/src/components/PlanUsage/utils.ts new file mode 100644 index 00000000000..4234148079c --- /dev/null +++ b/packages/console/src/components/PlanUsage/utils.ts @@ -0,0 +1,77 @@ +import { type TFuncKey } from 'i18next'; + +import { type NewSubscriptionQuota } from '@/cloud/types/router'; + +type UsageKey = Pick< + NewSubscriptionQuota, + | 'mauLimit' + | 'organizationsEnabled' + | 'mfaEnabled' + | 'enterpriseSsoLimit' + | 'resourcesLimit' + | 'machineToMachineLimit' + | 'tenantMembersLimit' + | 'tokenLimit' + | 'hooksLimit' +>; + +export const usageKeys: Array = [ + 'mauLimit', + 'organizationsEnabled', + 'mfaEnabled', + 'enterpriseSsoLimit', + 'resourcesLimit', + 'machineToMachineLimit', + 'tenantMembersLimit', + 'tokenLimit', + 'hooksLimit', +]; + +export const usageKeyMap: Record< + keyof UsageKey, + TFuncKey<'translation', 'admin_console.subscription.usage'> +> = { + mauLimit: 'mau.description', + organizationsEnabled: 'organizations.description', + mfaEnabled: 'mfa.description', + enterpriseSsoLimit: 'enterprise_sso.description', + resourcesLimit: 'api_resources.description', + machineToMachineLimit: 'machine_to_machine.description', + tenantMembersLimit: 'tenant_members.description', + tokenLimit: 'tokens.description', + hooksLimit: 'hooks.description', +}; + +export const titleKeyMap: Record< + keyof UsageKey, + TFuncKey<'translation', 'admin_console.subscription.usage'> +> = { + mauLimit: 'mau.title', + organizationsEnabled: 'organizations.title', + mfaEnabled: 'mfa.title', + enterpriseSsoLimit: 'enterprise_sso.title', + resourcesLimit: 'api_resources.title', + machineToMachineLimit: 'machine_to_machine.title', + tenantMembersLimit: 'tenant_members.title', + tokenLimit: 'tokens.title', + hooksLimit: 'hooks.title', +}; + +export const tooltipKeyMap: Record< + keyof UsageKey, + TFuncKey<'translation', 'admin_console.subscription.usage'> +> = { + mauLimit: 'mau.tooltip', + organizationsEnabled: 'organizations.tooltip', + mfaEnabled: 'mfa.tooltip', + enterpriseSsoLimit: 'enterprise_sso.tooltip', + resourcesLimit: 'api_resources.tooltip', + machineToMachineLimit: 'machine_to_machine.tooltip', + tenantMembersLimit: 'tenant_members.tooltip', + tokenLimit: 'tokens.tooltip', + hooksLimit: 'hooks.tooltip', +}; + +export const formatNumber = (number: number): string => { + return number.toString().replaceAll(/\B(?=(\d{3})+(?!\d))/g, ','); +}; diff --git a/packages/console/src/components/ProgressBar/index.tsx b/packages/console/src/components/ProgressBar/index.tsx index 25af66bc818..5977e5fc00e 100644 --- a/packages/console/src/components/ProgressBar/index.tsx +++ b/packages/console/src/components/ProgressBar/index.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly currentStep: number; diff --git a/packages/console/src/components/QuotaGuardFooter/index.tsx b/packages/console/src/components/QuotaGuardFooter/index.tsx index c5d57f0ded6..ecd9408dc3a 100644 --- a/packages/console/src/components/QuotaGuardFooter/index.tsx +++ b/packages/console/src/components/QuotaGuardFooter/index.tsx @@ -3,7 +3,7 @@ import { type ReactNode } from 'react'; import Button from '@/ds-components/Button'; import useTenantPathname from '@/hooks/use-tenant-pathname'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly children: ReactNode; diff --git a/packages/console/src/components/Region/index.tsx b/packages/console/src/components/Region/index.tsx index 9a50cb97ed2..ff4164aa88f 100644 --- a/packages/console/src/components/Region/index.tsx +++ b/packages/console/src/components/Region/index.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; // TODO: This is a copy from `@logto/cloud-models`, make a SSoT for this later export enum RegionName { diff --git a/packages/console/src/components/RequestDataError/index.tsx b/packages/console/src/components/RequestDataError/index.tsx index daed530f2c5..b0134d6f37f 100644 --- a/packages/console/src/components/RequestDataError/index.tsx +++ b/packages/console/src/components/RequestDataError/index.tsx @@ -2,14 +2,14 @@ import { Theme } from '@logto/schemas'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; -import RequestErrorDarkImage from '@/assets/images/request-error-dark.svg'; -import RequestErrorImage from '@/assets/images/request-error.svg'; +import RequestErrorDarkImage from '@/assets/images/request-error-dark.svg?react'; +import RequestErrorImage from '@/assets/images/request-error.svg?react'; import Button from '@/ds-components/Button'; import Card from '@/ds-components/Card'; import type { RequestError } from '@/hooks/use-api'; import useTheme from '@/hooks/use-theme'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly error: RequestError; diff --git a/packages/console/src/components/RoleAssignmentModal/index.tsx b/packages/console/src/components/RoleAssignmentModal/index.tsx index cd2e9fed36b..baa1056ecfe 100644 --- a/packages/console/src/components/RoleAssignmentModal/index.tsx +++ b/packages/console/src/components/RoleAssignmentModal/index.tsx @@ -11,10 +11,10 @@ import DangerousRaw from '@/ds-components/DangerousRaw'; import ModalLayout from '@/ds-components/ModalLayout'; import TextLink from '@/ds-components/TextLink'; import useApi from '@/hooks/use-api'; -import * as modalStyles from '@/scss/modal.module.scss'; +import modalStyles from '@/scss/modal.module.scss'; import { getUserTitle } from '@/utils/user'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = ( | { diff --git a/packages/console/src/components/RoleIcon/index.tsx b/packages/console/src/components/RoleIcon/index.tsx index 10914329113..878a790de51 100644 --- a/packages/console/src/components/RoleIcon/index.tsx +++ b/packages/console/src/components/RoleIcon/index.tsx @@ -1,8 +1,8 @@ import { Theme } from '@logto/schemas'; import { type ReactNode } from 'react'; -import UserRoleIconDark from '@/assets/icons/role-feature-dark.svg'; -import UserRoleIcon from '@/assets/icons/role-feature.svg'; +import UserRoleIconDark from '@/assets/icons/role-feature-dark.svg?react'; +import UserRoleIcon from '@/assets/icons/role-feature.svg?react'; import useTheme from '@/hooks/use-theme'; const themeToRoleIcon = Object.freeze({ diff --git a/packages/console/src/components/RoleScopesTransfer/components/ResourceItem/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/ResourceItem/index.tsx index a0280753fbe..ee93f79ce1b 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/ResourceItem/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/ResourceItem/index.tsx @@ -3,8 +3,8 @@ import classNames from 'classnames'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import CaretExpanded from '@/assets/icons/caret-expanded.svg'; -import CaretFolded from '@/assets/icons/caret-folded.svg'; +import CaretExpanded from '@/assets/icons/caret-expanded.svg?react'; +import CaretFolded from '@/assets/icons/caret-folded.svg?react'; import Checkbox from '@/ds-components/Checkbox'; import IconButton from '@/ds-components/IconButton'; import { onKeyDownHandler } from '@/utils/a11y'; @@ -12,7 +12,7 @@ import { onKeyDownHandler } from '@/utils/a11y'; import type { DetailedResourceResponse } from '../../types'; import SourceScopeItem from '../SourceScopeItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly resource: DetailedResourceResponse; diff --git a/packages/console/src/components/RoleScopesTransfer/components/SourceScopeItem/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/SourceScopeItem/index.tsx index 04f84802ae0..869b746a28a 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/SourceScopeItem/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/SourceScopeItem/index.tsx @@ -3,7 +3,7 @@ import type { ScopeResponse } from '@logto/schemas'; import Checkbox from '@/ds-components/Checkbox'; import { onKeyDownHandler } from '@/utils/a11y'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly scope: ScopeResponse; diff --git a/packages/console/src/components/RoleScopesTransfer/components/SourceScopesBox/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/SourceScopesBox/index.tsx index a67684e135c..aea029d47b3 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/SourceScopesBox/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/SourceScopesBox/index.tsx @@ -7,16 +7,16 @@ import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import Search from '@/assets/icons/search.svg'; +import Search from '@/assets/icons/search.svg?react'; import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import type { DetailedResourceResponse } from '@/components/RoleScopesTransfer/types'; import TextInput from '@/ds-components/TextInput'; import type { RequestError } from '@/hooks/use-api'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import ResourceItem from '../ResourceItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly roleId?: string; diff --git a/packages/console/src/components/RoleScopesTransfer/components/TargetScopeItem/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/TargetScopeItem/index.tsx index 65793d4f2f5..41fbec835aa 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/TargetScopeItem/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/TargetScopeItem/index.tsx @@ -1,9 +1,9 @@ import type { ScopeResponse } from '@logto/schemas'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import IconButton from '@/ds-components/IconButton'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly scope: ScopeResponse; diff --git a/packages/console/src/components/RoleScopesTransfer/components/TargetScopesBox/index.tsx b/packages/console/src/components/RoleScopesTransfer/components/TargetScopesBox/index.tsx index c7edebe2562..bc0319fdd90 100644 --- a/packages/console/src/components/RoleScopesTransfer/components/TargetScopesBox/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/components/TargetScopesBox/index.tsx @@ -1,11 +1,11 @@ import type { ScopeResponse } from '@logto/schemas'; import { useTranslation } from 'react-i18next'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import TargetScopeItem from '../TargetScopeItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly selectedScopes: ScopeResponse[]; diff --git a/packages/console/src/components/RoleScopesTransfer/index.tsx b/packages/console/src/components/RoleScopesTransfer/index.tsx index 5baf6c1205e..ce1610c9e57 100644 --- a/packages/console/src/components/RoleScopesTransfer/index.tsx +++ b/packages/console/src/components/RoleScopesTransfer/index.tsx @@ -1,11 +1,11 @@ import type { ScopeResponse, RoleType } from '@logto/schemas'; import classNames from 'classnames'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import SourceScopesBox from './components/SourceScopesBox'; import TargetScopesBox from './components/TargetScopesBox'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; /** * @deprecated Use `@/ds-component/DataTransferBox` instead. diff --git a/packages/console/src/components/RolesTransfer/SourceRolesBox/SourceRoleItem/index.tsx b/packages/console/src/components/RolesTransfer/SourceRolesBox/SourceRoleItem/index.tsx index c943633cc2e..9a128849bdf 100644 --- a/packages/console/src/components/RolesTransfer/SourceRolesBox/SourceRoleItem/index.tsx +++ b/packages/console/src/components/RolesTransfer/SourceRolesBox/SourceRoleItem/index.tsx @@ -5,7 +5,7 @@ import { onKeyDownHandler } from '@/utils/a11y'; import RoleInformation from '../../components/RoleInformation'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly role: RoleResponse; diff --git a/packages/console/src/components/RolesTransfer/SourceRolesBox/index.tsx b/packages/console/src/components/RolesTransfer/SourceRolesBox/index.tsx index 1da49e842e6..ccc485c3764 100644 --- a/packages/console/src/components/RolesTransfer/SourceRolesBox/index.tsx +++ b/packages/console/src/components/RolesTransfer/SourceRolesBox/index.tsx @@ -7,18 +7,18 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import Search from '@/assets/icons/search.svg'; +import Search from '@/assets/icons/search.svg?react'; import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import { defaultPageSize } from '@/consts'; import Pagination from '@/ds-components/Pagination'; import TextInput from '@/ds-components/TextInput'; import type { RequestError } from '@/hooks/use-api'; import useDebounce from '@/hooks/use-debounce'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import { buildUrl } from '@/utils/url'; import SourceRoleItem from './SourceRoleItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly entityId: string; diff --git a/packages/console/src/components/RolesTransfer/TargetRolesBox/TargetRoleItem/index.tsx b/packages/console/src/components/RolesTransfer/TargetRolesBox/TargetRoleItem/index.tsx index af35c72caa1..ec1690c3e68 100644 --- a/packages/console/src/components/RolesTransfer/TargetRolesBox/TargetRoleItem/index.tsx +++ b/packages/console/src/components/RolesTransfer/TargetRolesBox/TargetRoleItem/index.tsx @@ -1,11 +1,11 @@ import { type RoleResponse } from '@logto/schemas'; -import Close from '@/assets/icons/close.svg'; +import Close from '@/assets/icons/close.svg?react'; import IconButton from '@/ds-components/IconButton'; import RoleInformation from '../../components/RoleInformation'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly role: RoleResponse; diff --git a/packages/console/src/components/RolesTransfer/TargetRolesBox/index.tsx b/packages/console/src/components/RolesTransfer/TargetRolesBox/index.tsx index ea9bcc2e4ea..56c82584ca0 100644 --- a/packages/console/src/components/RolesTransfer/TargetRolesBox/index.tsx +++ b/packages/console/src/components/RolesTransfer/TargetRolesBox/index.tsx @@ -1,10 +1,10 @@ import type { RoleResponse } from '@logto/schemas'; import { useTranslation } from 'react-i18next'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import TargetRoleItem from './TargetRoleItem'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly selectedRoles: RoleResponse[]; diff --git a/packages/console/src/components/RolesTransfer/components/RoleInformation/index.tsx b/packages/console/src/components/RolesTransfer/components/RoleInformation/index.tsx index 4602f85758d..93bd4751218 100644 --- a/packages/console/src/components/RolesTransfer/components/RoleInformation/index.tsx +++ b/packages/console/src/components/RolesTransfer/components/RoleInformation/index.tsx @@ -1,11 +1,11 @@ import { RoleType, type ScopeResponse, isManagementApi, type RoleResponse } from '@logto/schemas'; import useSWR from 'swr'; -import ManagementApiAccessFlag from '@/assets/icons/management-api-access.svg'; +import ManagementApiAccessFlag from '@/assets/icons/management-api-access.svg?react'; import DynamicT from '@/ds-components/DynamicT'; import { Tooltip } from '@/ds-components/Tip'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly role: RoleResponse; diff --git a/packages/console/src/components/RolesTransfer/index.tsx b/packages/console/src/components/RolesTransfer/index.tsx index 152aa6fd41c..c642f628959 100644 --- a/packages/console/src/components/RolesTransfer/index.tsx +++ b/packages/console/src/components/RolesTransfer/index.tsx @@ -2,15 +2,15 @@ import { type RoleResponse, RoleType } from '@logto/schemas'; import classNames from 'classnames'; import { Trans, useTranslation } from 'react-i18next'; -import ManagementApiAccessFlag from '@/assets/icons/management-api-access.svg'; +import ManagementApiAccessFlag from '@/assets/icons/management-api-access.svg?react'; import FormField from '@/ds-components/FormField'; import InlineNotification from '@/ds-components/InlineNotification'; import useUserPreferences from '@/hooks/use-user-preferences'; -import * as transferLayout from '@/scss/transfer.module.scss'; +import transferLayout from '@/scss/transfer.module.scss'; import SourceRolesBox from './SourceRolesBox'; import TargetRolesBox from './TargetRolesBox'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly entityId: string; diff --git a/packages/console/src/components/SignInExperiencePreview/components/ToggleUiThemeButton/index.tsx b/packages/console/src/components/SignInExperiencePreview/components/ToggleUiThemeButton/index.tsx index c0e9dbce37f..ffc859394e8 100644 --- a/packages/console/src/components/SignInExperiencePreview/components/ToggleUiThemeButton/index.tsx +++ b/packages/console/src/components/SignInExperiencePreview/components/ToggleUiThemeButton/index.tsx @@ -1,12 +1,12 @@ import { Theme } from '@logto/schemas'; import classNames from 'classnames'; -import Moon from '@/assets/icons/moon.svg'; -import Sun from '@/assets/icons/sun.svg'; +import Moon from '@/assets/icons/moon.svg?react'; +import Sun from '@/assets/icons/sun.svg?react'; import Button from '@/ds-components/Button'; import type { Props as ButtonProps } from '@/ds-components/Button'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; type Props = { readonly value: Theme; diff --git a/packages/console/src/components/SignInExperiencePreview/index.module.scss b/packages/console/src/components/SignInExperiencePreview/index.module.scss index 7f945749665..c8d4d83a7ea 100644 --- a/packages/console/src/components/SignInExperiencePreview/index.module.scss +++ b/packages/console/src/components/SignInExperiencePreview/index.module.scss @@ -85,4 +85,28 @@ } } } + + &.disabled { + background: url('../../assets/images/blur-preview.svg') 0 0 / 100% auto no-repeat; + } + + .placeholder { + width: 100%; + height: calc(_screenSize.$web-height + _.unit(20)); + padding: _.unit(10); + backdrop-filter: blur(25px); + display: flex; + flex-direction: column; + color: var(--color-static-white); + + .title { + font: var(--font-label-2); + } + + .description { + margin-top: _.unit(1.5); + font: var(--font-body-2); + white-space: pre-wrap; + } + } } diff --git a/packages/console/src/components/SignInExperiencePreview/index.tsx b/packages/console/src/components/SignInExperiencePreview/index.tsx index ca0a61ac8a1..cf20714f359 100644 --- a/packages/console/src/components/SignInExperiencePreview/index.tsx +++ b/packages/console/src/components/SignInExperiencePreview/index.tsx @@ -4,16 +4,24 @@ import type { ConnectorMetadata, SignInExperience, ConnectorResponse } from '@lo import { conditional } from '@silverhand/essentials'; import classNames from 'classnames'; import { format } from 'date-fns'; -import { useContext, useRef, useMemo, useCallback, useEffect, useState } from 'react'; +import { + useContext, + useRef, + useMemo, + useCallback, + useEffect, + useState, + type ReactNode, +} from 'react'; import { useTranslation } from 'react-i18next'; import useSWR from 'swr'; -import PhoneInfo from '@/assets/images/phone-info.svg'; +import PhoneInfo from '@/assets/images/phone-info.svg?react'; import { AppDataContext } from '@/contexts/AppDataProvider'; import type { RequestError } from '@/hooks/use-api'; import useUiLanguages from '@/hooks/use-ui-languages'; -import * as styles from './index.module.scss'; +import styles from './index.module.scss'; import { PreviewPlatform } from './types'; export { default as ToggleUiThemeButton } from './components/ToggleUiThemeButton'; @@ -28,6 +36,16 @@ type Props = { * the `AppDataContext` will be used. */ readonly endpoint?: URL; + /** + * Whether the preview is disabled. If `true`, the preview will be disabled and a placeholder will + * be shown instead. Defaults to `false`. + */ + // eslint-disable-next-line react/boolean-prop-naming + readonly disabled?: boolean; + /** + * The placeholder to show when the preview is disabled. + */ + readonly disabledPlaceholder?: ReactNode; }; function SignInExperiencePreview({ @@ -36,6 +54,8 @@ function SignInExperiencePreview({ language = 'en', signInExperience, endpoint: endpointInput, + disabled = false, + disabledPlaceholder, }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); @@ -97,6 +117,10 @@ function SignInExperiencePreview({ }, []); useEffect(() => { + if (disabled) { + setIframeLoaded(false); + return; + } const iframe = previewRef.current; iframe?.addEventListener('load', iframeOnLoadEventHandler); @@ -104,7 +128,7 @@ function SignInExperiencePreview({ return () => { iframe?.removeEventListener('load', iframeOnLoadEventHandler); }; - }, [iframeLoaded, iframeOnLoadEventHandler]); + }, [iframeLoaded, disabled, iframeOnLoadEventHandler]); useEffect(() => { if (!iframeLoaded) { @@ -122,7 +146,8 @@ function SignInExperiencePreview({
-
-
- {platform !== PreviewPlatform.DesktopWeb && ( -
-
{format(Date.now(), 'HH:mm')}
- -
- )} -