Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add public registration #22125

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open

Conversation

DanielBiegler
Copy link
Contributor

@DanielBiegler DanielBiegler commented Apr 8, 2024

Scope

What's changed:

  • New UI so you can go to register
  • It adds a new user with a specified role
  • Fixed a UI bug on the login page
  • DB migration that adds fields to directus_settings
  • Hide register button depending on directus_settings
  • Email validation via customizable filter
  • Add mailing service to send out the verification code
  • Email template for the verification email
Register Route After Success
image image
  • Fixed UI inconsistency in login & reset-password
Before After
Screencast.from.2024-04-08.20-49-44.webm
Screencast.from.2024-04-08.20-57-14.webm

Potential Risks / Drawbacks

  • todo

Review Notes / Questions

  • Dont forget to run the migration 😉

Fixes #21981

Copy link

changeset-bot bot commented Apr 8, 2024

🦋 Changeset detected

Latest commit: d69c13d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 21 packages
Name Type
@directus/system-data Patch
@directus/api Patch
@directus/app Patch
@directus/sdk Patch
@directus/utils Patch
directus Patch
@directus/composables Patch
@directus/env Patch
@directus/extensions-registry Patch
@directus/extensions-sdk Patch
@directus/extensions Patch
@directus/memory Patch
@directus/pressure Patch
@directus/storage-driver-azure Patch
@directus/storage-driver-cloudinary Patch
@directus/storage-driver-gcs Patch
@directus/storage-driver-s3 Patch
@directus/storage-driver-supabase Patch
@directus/themes Patch
@directus/validation Patch
create-directus-extension Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

email,
password,
role: publicRegistrationRole,
status: hasEmailValidation ? 'draft' : 'active', // TODO: Do we want to have a dedicated "unverified" status?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about renaming the invited-status to a more generic pending-status so that we can use the same status for invited and registered-but-not-yet-verified users but simply using the migration file to rename the status could lead to breaking existing logic in users projects for example somebody might have Flows that expect the users to be invited

Another approach could be to just replace the Invited UI label to Pending and keep the underlying invited string the same to stay backwards compatible.

Mhh. Could also just add a new status to be both precise and backwards compatible. Like so:

image

If we add the new status we can give it a more concrete name too like Unverified for example

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have to take a closer look, but I think a dedicated (hidden) field for the email verification status could make sense. This could also be used, for example, if the user is already active and changes the email address, which would have to be verified again (but the user status should stay "active").


<register-form v-else :provider="provider" @was-successful="wasSuccessful = $event" />

<sso-links v-if="!authenticated && wasSuccessful == false" :providers="auth.providers" />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, do we want to show sso links on the register page? 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, as those fall under the action of "login", right? So a better fit for the login page

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as those fall under the action of "login", right

Not necessarily if you have public registration enabled for SSO it doubles as both login and registration 🤔
I wouldnt be against having the SSO buttons on the registration page if configured as such but feels like someone with more UX insight may have more opinions there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, removing them now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily if you have public registration enabled for SSO it doubles as both login and registration 🤔

Yeah, but you actually login to the provider with an existing account there (at least in most cases)

Comment on lines 504 to 510
const registerSchema = Joi.object<{ email: string; password: string }>({
email: Joi.string().email().required(),
password: Joi.string().required(),
});

router.post(
'/register',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice if we would add a way to specify more user fields because I can see people using this service to build their own login pages with more fields 🤔

Allowing other user fields would need to be sanitized though we cant let them override role for example

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean like require a phone number or similar? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes for example a number or even simpler first & last name. Avatar could also be useful, stuff like that. It'd be nice to also allow populating user-provided fields for when users have manually extended the directus_users table so it'd have to be dynamic but I'm a little hesitant there because of security concerns. Mhh

Copy link
Member

@paescuj paescuj Apr 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting ❤️ Well, actually it would "just" be another (public/shared) form where we could use existing validation logic, etc. 🤔 Of course would need some proxying of the corresponding fields.

@DanielBiegler DanielBiegler marked this pull request as ready for review April 30, 2024 15:57
@DanielBiegler DanielBiegler marked this pull request as draft April 30, 2024 16:02
@@ -5,6 +5,7 @@ export type ServerInfoOutput = {
project: {
project_name: string;
default_language: string;
is_public_registration_enabled: boolean; // TODO Get opinion from @Brainslug
Copy link
Member

@paescuj paescuj Apr 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be sufficient IMO, the type already indicates that it's a toggle state:

Suggested change
is_public_registration_enabled: boolean; // TODO Get opinion from @Brainslug
public_registration: boolean;

E.g. comparable to bool states in Collection metadata:

hidden: boolean;
singleton: boolean;
icon: string | null;
color: string | null;
translations: CollectionTranslations[] | null;
display_template: string | null;
preview_url: string | null;
versioning: boolean;

email,
password,
role: publicRegistrationRole,
status: hasEmailValidation ? 'draft' : 'active', // TODO: Do we want to have a dedicated "unverified" status?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have to take a closer look, but I think a dedicated (hidden) field for the email verification status could make sense. This could also be used, for example, if the user is already active and changes the email address, which would have to be verified again (but the user status should stay "active").

Comment on lines 504 to 510
const registerSchema = Joi.object<{ email: string; password: string }>({
email: Joi.string().email().required(),
password: Joi.string().required(),
});

router.post(
'/register',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean like require a phone number or similar? 🤔

@DanielBiegler DanielBiegler marked this pull request as ready for review April 30, 2024 16:10
@DanielBiegler DanielBiegler changed the title WIP: Add public registration Add public registration Apr 30, 2024
Comment on lines +1367 to +1368
public_registration: Public registration
is_public_registration_enabled: Public registration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should probably both be title case

Suggested change
public_registration: Public registration
is_public_registration_enabled: Public registration
public_registration: Public Registration
is_public_registration_enabled: Public Registration

is_public_registration_enabled: Public registration
public_registration_role: Role
public_registration_email_filter: Email Filter
is_public_registration_email_validation_enabled: Send verification email
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to public_registration_send_verification_email? Also, title case.

public_registration: Public registration
is_public_registration_enabled: Public registration
public_registration_role: Role
public_registration_email_filter: Email Filter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably make it a bit clearer that it is a filter for the email address and does not have anything to do with the "Send verification email".

Suggested change
public_registration_email_filter: Email Filter
public_registration_email_filter: Email Address Filter

display_options:
template: '{{ name }}'

- field: is_public_registration_email_validation_enabled
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I'd swap the "Send Verification Email" and "Email Filter" field order, since the email filter is related more closely to the user, vs the verification email toggle.

Please note that before you can sign in, you may need to verify your email address by clicking on the verification link sent to you.
dont_have_an_account: Don't have an account?
already_have_an_account: Already have an account?
sign_up_now: Sign up now
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Title Case :)

password.value === null ||
passwordsMatch.value === false
) {
error.value = 'INVALID_PAYLOAD';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use ErrorCode.InvalidPayload here?

await this.createOne(partialUser);
} catch (error: unknown) {
// To avoid giving attackers infos about registered emails we dont fail for violated unique constraints
if (isDirectusError(error) && error.code === 'RECORD_NOT_UNIQUE') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be simplified to isDirectusError(error, ErrorCode.RecordNotUnique)

if (hasEmailValidation) {
const mailService = new MailService(serviceOptions);
const payload = { email: input.email, scope: 'pending-registration' };
const token = jwt.sign(payload, env['SECRET'] as string, { expiresIn: '7d', issuer: 'directus' });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the expiresIn here and in the user invite be moved to a common constant?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 🔖 Ready
Development

Successfully merging this pull request may close these issues.

Implement Public User Registration
4 participants