Context :
Plan choice [on pricing page on website](https://twenty.com/pricing)
should redirect you the right plan on app /plan-required page (after
sign in), thanks to query parameters and BillingCheckoutSessionState
sync.
With email verification, an other session starts at CTA click in
verification email. Initial BillingCheckoutSessionState is lost and user
can't submit to the plan he choose.
Solution :
Pass a nextPath query parameter in email verification link
To test :
- Modify .env to add IS_BILLING_ENABLED (+ reset db + sync billing) +
IS_EMAIL_VERIFICATION_REQUIRED
- Start test from this page
http://app.localhost:3001/welcome?billingCheckoutSession={%22plan%22:%22ENTERPRISE%22,%22interval%22:%22Year%22,%22requirePaymentMethod%22:true}
- After verification, check you arrive on /plan-required page with
Enterprise plan on a yearly interval (default is Pro/monthly).
closes https://github.com/twentyhq/twenty/issues/12288
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/591
Same than for `twenty-shared` made in
https://github.com/twentyhq/twenty/pull/11083.
## TODO
- [x] Manual migrate twenty-website twenty-ui imports
## What's next:
- Generate barrel and migration script factorization within own package
+ tests
- Refactoring using preconstruct ? TimeBox
- Lint circular dependencies
- Lint import from barrel and forbid them
### Preconstruct
We need custom rollup plugins addition, but preconstruct does not expose
its rollup configuration. It might be possible to handle this using the
babel overrides. But was a big tunnel.
We could give it a try afterwards ! ( allowing cjs interop and stuff
like that )
Stuck to vite lib app
Closed related PRs:
- https://github.com/twentyhq/twenty/pull/11294
- https://github.com/twentyhq/twenty/pull/11203
# Introduction
In this PR we've migrated `twenty-shared` from a `vite` app
[libary-mode](https://vite.dev/guide/build#library-mode) to a
[preconstruct](https://preconstruct.tools/) "atomic" application ( in
the future would like to introduce preconstruct to handle of all our
atomic dependencies such as `twenty-emails` `twenty-ui` etc it will be
integrated at the monorepo's root directly, would be to invasive in the
first, starting incremental via `twenty-shared`)
For more information regarding the motivations please refer to nor:
- https://github.com/twentyhq/core-team-issues/issues/587
-
https://github.com/twentyhq/core-team-issues/issues/281#issuecomment-2630949682
close https://github.com/twentyhq/core-team-issues/issues/589
close https://github.com/twentyhq/core-team-issues/issues/590
## How to test
In order to ease the review this PR will ship all the codegen at the
very end, the actual meaning full diff is `+2,411 −114`
In order to migrate existing dependent packages to `twenty-shared` multi
barrel new arch you need to run in local:
```sh
yarn tsx packages/twenty-shared/scripts/migrateFromSingleToMultiBarrelImport.ts && \
npx nx run-many -t lint --fix -p twenty-front twenty-ui twenty-server twenty-emails twenty-shared twenty-zapier
```
Note that `migrateFromSingleToMultiBarrelImport` is idempotent, it's atm
included in the PR but should not be merged. ( such as codegen will be
added before merging this script will be removed )
## Misc
- related opened issue preconstruct
https://github.com/preconstruct/preconstruct/issues/617
## Closed related PR
- https://github.com/twentyhq/twenty/pull/11028
- https://github.com/twentyhq/twenty/pull/10993
- https://github.com/twentyhq/twenty/pull/10960
## Upcoming enhancement: ( in others dedicated PRs )
- 1/ refactor generate barrel to export atomic module instead of `*`
- 2/ generate barrel own package with several files and tests
- 3/ Migration twenty-ui the same way
- 4/ Use `preconstruct` at monorepo global level
## Conclusion
As always any suggestions are welcomed !
This pull request focuses on improving localization by replacing
hardcoded strings with translatable strings using the `Trans` component
from `@lingui/react/macro`. Additionally, it introduces locale support
to several email components. Here are the most important changes:
### Localization Improvements:
* Replaced hardcoded strings with `Trans` components in various email
templates to support localization.
(`packages/twenty-emails/src/emails/clean-suspended-workspace.email.tsx`,
`packages/twenty-emails/src/emails/password-reset-link.email.tsx`,
`packages/twenty-emails/src/emails/password-update-notify.email.tsx`,
`packages/twenty-emails/src/emails/send-email-verification-link.email.tsx`,
`packages/twenty-emails/src/emails/send-invite-link.email.tsx`,
`packages/twenty-emails/src/emails/warn-suspended-workspace.email.tsx`)
[[1]](diffhunk://#diff-ca227a03c0aa66428daff938c743435e8a4dc3ffa960c0952f2697a23e280fdbR6-R25)
[[2]](diffhunk://#diff-ca227a03c0aa66428daff938c743435e8a4dc3ffa960c0952f2697a23e280fdbL42-R45)
[[3]](diffhunk://#diff-523cd37f5680ce418450946f62b7804b6586158efb190ced73920ef0fdf96bc8L1)
[[4]](diffhunk://#diff-523cd37f5680ce418450946f62b7804b6586158efb190ced73920ef0fdf96bc8L23-R23)
[[5]](diffhunk://#diff-cf16aa55d3eeb6be606bbe93de4c83b6f146c49b60d6f512d4b87e49fe14338cL29-R29)
[[6]](diffhunk://#diff-cf16aa55d3eeb6be606bbe93de4c83b6f146c49b60d6f512d4b87e49fe14338cL46-R46)
[[7]](diffhunk://#diff-16b613160f937563ec108176f595d8f275a1d87a5b8245d84df60d775f3efebeL1)
[[8]](diffhunk://#diff-16b613160f937563ec108176f595d8f275a1d87a5b8245d84df60d775f3efebeL22-R22)
[[9]](diffhunk://#diff-0da62e7cc5cfcb32cc25f067fa1d50123047c239af210398f065455ab6700886L1)
[[10]](diffhunk://#diff-0da62e7cc5cfcb32cc25f067fa1d50123047c239af210398f065455ab6700886L42-R41)
[[11]](diffhunk://#diff-0da62e7cc5cfcb32cc25f067fa1d50123047c239af210398f065455ab6700886L57-R56)
[[12]](diffhunk://#diff-483346065c074946a43c18492334bd680422a1d4cb994dc8c3cd39d0208e6016L1-R21)
[[13]](diffhunk://#diff-483346065c074946a43c18492334bd680422a1d4cb994dc8c3cd39d0208e6016L28-R31)
[[14]](diffhunk://#diff-483346065c074946a43c18492334bd680422a1d4cb994dc8c3cd39d0208e6016L53-R55)
### Locale Support:
* Added `locale` prop to email components to dynamically set the locale.
(`packages/twenty-emails/src/emails/clean-suspended-workspace.email.tsx`,
`packages/twenty-emails/src/emails/warn-suspended-workspace.email.tsx`)
[[1]](diffhunk://#diff-ca227a03c0aa66428daff938c743435e8a4dc3ffa960c0952f2697a23e280fdbR6-R25)
[[2]](diffhunk://#diff-483346065c074946a43c18492334bd680422a1d4cb994dc8c3cd39d0208e6016L1-R21)
### SnackBar Messages:
* Replaced hardcoded SnackBar messages with translatable strings using
the `t` function from `@lingui/react/macro`.
(`packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx`,
`packages/twenty-front/src/modules/auth/hooks/useVerifyLogin.ts`,
`packages/twenty-front/src/modules/auth/sign-in-up/hooks/useHandleResendEmailVerificationToken.ts`,
`packages/twenty-front/src/modules/auth/sign-in-up/hooks/useHandleResetPassword.ts`,
`packages/twenty-front/src/modules/object-record/record-field/components/LightCopyIconButton.tsx`,
`packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/PhonesFieldDisplay.tsx`)
[[1]](diffhunk://#diff-551f2f94eacd8856d22bab7e63dd3ad693f87e9fa9b289864802ebc387f72b42R7)
[[2]](diffhunk://#diff-551f2f94eacd8856d22bab7e63dd3ad693f87e9fa9b289864802ebc387f72b42L24-R29)
[[3]](diffhunk://#diff-551f2f94eacd8856d22bab7e63dd3ad693f87e9fa9b289864802ebc387f72b42L43-R51)
[[4]](diffhunk://#diff-428199461992a01325159f5fbf826d845f05f3361279eccd3f1ce416e0114845R7-R15)
[[5]](diffhunk://#diff-428199461992a01325159f5fbf826d845f05f3361279eccd3f1ce416e0114845L24-R26)
[[6]](diffhunk://#diff-cde42d6abfed63e52c2bda09d537a6577148d0baf957fde75ceaa8657ed58403R5)
[[7]](diffhunk://#diff-cde42d6abfed63e52c2bda09d537a6577148d0baf957fde75ceaa8657ed58403L16-R17)
[[8]](diffhunk://#diff-cde42d6abfed63e52c2bda09d537a6577148d0baf957fde75ceaa8657ed58403L28-R33)
[[9]](diffhunk://#diff-9332c1988864863f12516c2fb77e814af60bedb37c36ffa094f49afc335d5457R5-R17)
[[10]](diffhunk://#diff-9332c1988864863f12516c2fb77e814af60bedb37c36ffa094f49afc335d5457L27-R33)
[[11]](diffhunk://#diff-9332c1988864863f12516c2fb77e814af60bedb37c36ffa094f49afc335d5457L42-R44)
[[12]](diffhunk://#diff-8d64afa825b47ab71d18e3e284408e2097f5fd2365eae84d9d25d3568c48e49cR7)
[[13]](diffhunk://#diff-8d64afa825b47ab71d18e3e284408e2097f5fd2365eae84d9d25d3568c48e49cR20-R28)
[[14]](diffhunk://#diff-6e4361ded2b5656afaeb1befa8b1d23a45b490a1118550da290e27cdb8ebcdceR6)
[[15]](diffhunk://#diff-6e4361ded2b5656afaeb1befa8b1d23a45b490a1118550da290e27cdb8ebcdceR19-R20)
[[16]](diffhunk://#diff-6e4361ded2b5656afaeb1befa8b1d23a45b490a1118550da290e27cdb8ebcdceL29-R38)
- Return only SSO providers with an `activate` status
- If only 1 SSO provider is enabled for auth, redirect the user to the
provider login page.
- if only SSO auth is available set the step to SSO selection.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Closestwentyhq/twenty#8240
This PR introduces email verification for non-Microsoft/Google Emails:
## Email Verification SignInUp Flow:
https://github.com/user-attachments/assets/740e9714-5413-4fd8-b02e-ace728ea47ef
The email verification link is sent as part of the
`SignInUpStep.EmailVerification`. The email verification token
validation is handled on a separate page (`AppPath.VerifyEmail`). A
verification email resend can be triggered from both pages.
## Email Verification Flow Screenshots (In Order):



## Sent Email Details (Subject & Template):


### Successful Email Verification Redirect:

### Unsuccessful Email Verification (invalid token, invalid email, token
expired, user does not exist, etc.):

### Force Sign In When Email Not Verified:

# TODOs:
## Sign Up Process
- [x] Introduce server-level environment variable
IS_EMAIL_VERIFICATION_REQUIRED (defaults to false)
- [x] Ensure users joining an existing workspace through an invite are
not required to validate their email
- [x] Generate an email verification token
- [x] Store the token in appToken
- [x] Send email containing the verification link
- [x] Create new email template for email verification
- [x] Create a frontend page to handle verification requests
## Sign In Process
- [x] After verifying user credentials, check if user's email is
verified and prompt to to verify
- [x] Show an option to resend the verification email
## Database
- [x] Rename the `emailVerified` colum on `user` to to `isEmailVerified`
for consistency
## During Deployment
- [x] Run a script/sql query to set `isEmailVerified` to `true` for all
users with a Google/Microsoft email and all users that show an
indication of a valid subscription (e.g. linked credit card)
- I have created a draft migration file below that shows one possible
approach to implementing this change:
```typescript
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UpdateEmailVerifiedForActiveUsers1733318043628
implements MigrationInterface
{
name = 'UpdateEmailVerifiedForActiveUsers1733318043628';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE core."user_email_verified_backup" AS
SELECT id, email, "isEmailVerified"
FROM core."user"
WHERE "deletedAt" IS NULL;
`);
await queryRunner.query(`
-- Update isEmailVerified for users who have been part of workspaces with active subscriptions
UPDATE core."user" u
SET "isEmailVerified" = true
WHERE EXISTS (
-- Check if user has been part of a workspace through userWorkspace table
SELECT 1
FROM core."userWorkspace" uw
JOIN core."workspace" w ON uw."workspaceId" = w.id
WHERE uw."userId" = u.id
-- Check for valid subscription indicators
AND (
w."activationStatus" = 'ACTIVE'
-- Add any other subscription-related conditions here
)
)
AND u."deletedAt" IS NULL;
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
UPDATE core."user" u
SET "isEmailVerified" = b."isEmailVerified"
FROM core."user_email_verified_backup" b
WHERE u.id = b.id;
`);
await queryRunner.query(`DROP TABLE core."user_email_verified_backup";`);
}
}
```
---------
Co-authored-by: Antoine Moreaux <moreaux.antoine@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
## Summary
- [x] Remove defaultWorkspace in user
- [x] Remove all occurrence of defaultWorkspace and defaultWorkspaceId
- [x] Improve activate workspace flow
- [x] Improve security on social login
- [x] Add `ImpersonateGuard`
- [x] Allow to use impersonation with couple `User/Workspace`
- [x] Prevent unexpected reload on activate workspace
- [x] Scope login token with workspaceId
Fix https://github.com/twentyhq/twenty/issues/9033#event-15714863042
We have recently introduced the possibility to specify workspace
specific auth providers.
I'm:
- introducing system wide auth providers (provided by clientConfig)
- making sure workspace specific auth providers belong to system wide
auth providers set
## Summary
Add support for multi-workspace feature and adjust configurations and
states accordingly.
- Introduced new state isMultiWorkspaceEnabledState.
- Updated ClientConfigProviderEffect component to handle
multi-workspace.
- Modified GraphQL schema and queries to include multi-workspace related
configurations.
- Adjusted server environment variables and their respective
documentation to support multi-workspace toggle.
- Updated server-side logic to handle new multi-workspace configurations
and conditions.
## What it does
### Backend
- [x] Add a mutation to create OIDC and SAML configuration
- [x] Add a mutation to delete an SSO config
- [x] Add a feature flag to toggle SSO
- [x] Add a mutation to activate/deactivate an SSO config
- [x] Add a mutation to delete an SSO config
- [x] Add strategy to use OIDC or SAML
- [ ] Improve error management
### Frontend
- [x] Add section "security" in settings
- [x] Add page to list SSO configurations
- [x] Add page and forms to create OIDC or SAML configuration
- [x] Add field to "connect with SSO" in the signin/signup process
- [x] Trigger auth when a user switch to a workspace with SSO enable
- [x] Add an option on the security page to activate/deactivate the
global invitation link
- [ ] Add new Icons for SSO Identity Providers (okta, Auth0, Azure,
Microsoft)
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Charles Bochet <charles@twenty.com>