mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-10-13 00:27:47 +00:00
Hide the password fields for a passwordless invite
When the invite code the form resolved is `passwordless`, the password fields are replaced by a note explaining that a verification code will be sent to the email address instead, which is also why the email field becomes mandatory.
This commit is contained in:
@@ -255,6 +255,7 @@
|
||||
"or_separator": "Or",
|
||||
"password_confirm_label": "Confirm password",
|
||||
"password_label": "Password",
|
||||
"passwordless_notice": "This account doesn't need a password. We'll send a verification code to your email address instead.",
|
||||
"terms_of_service": "I agree to the <a>Terms and Conditions</a>",
|
||||
"token_checking": "Checking invite code…",
|
||||
"token_invalid": "This invite code is invalid or has expired",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { QueryClient, useQuery } from "@tanstack/react-query";
|
||||
import { Form, InlineSpinner } from "@vector-im/compound-web";
|
||||
import { Form, InlineSpinner, Text } from "@vector-im/compound-web";
|
||||
import { Suspense, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import * as v from "valibot";
|
||||
@@ -129,6 +129,7 @@ const REGISTRATION_TOKEN_QUERY = graphql(`
|
||||
valid
|
||||
username
|
||||
email
|
||||
passwordless
|
||||
}
|
||||
}
|
||||
`);
|
||||
@@ -138,6 +139,7 @@ type TokenInfo = {
|
||||
valid: boolean;
|
||||
username?: string | null;
|
||||
email?: string | null;
|
||||
passwordless: boolean;
|
||||
};
|
||||
|
||||
/** The settled result of the token lookup, rendered in a live region. */
|
||||
@@ -606,8 +608,16 @@ const PasswordRegisterForm: React.FC<{ data: Data }> = ({ data }) => {
|
||||
const forcedUsername = tokenInfo?.valid ? tokenInfo.username : undefined;
|
||||
const forcedEmail = tokenInfo?.valid ? tokenInfo.email : undefined;
|
||||
|
||||
// A passwordless token skips password creation entirely; the email address is
|
||||
// used to send a verification code instead
|
||||
const passwordless = tokenInfo?.valid === true && tokenInfo.passwordless;
|
||||
|
||||
// Show the email field when the server requires one, when the token pins one,
|
||||
// or when it is needed to send the passwordless verification code
|
||||
const showEmail =
|
||||
data.features.password_registration_email_required || !!forcedEmail;
|
||||
data.features.password_registration_email_required ||
|
||||
!!forcedEmail ||
|
||||
passwordless;
|
||||
|
||||
// With no provider to pick from there is nothing to choose, so the whole form
|
||||
// is shown at once
|
||||
@@ -794,11 +804,19 @@ const PasswordRegisterForm: React.FC<{ data: Data }> = ({ data }) => {
|
||||
</Form.Field>
|
||||
)}
|
||||
|
||||
<PasswordFields
|
||||
minimumPasswordComplexity={data.features.minimum_password_complexity}
|
||||
serverErrors={fields.password?.errors ?? []}
|
||||
confirmServerErrors={fields.password_confirm?.errors ?? []}
|
||||
/>
|
||||
{passwordless ? (
|
||||
<Text size="sm" className="text-secondary">
|
||||
{t("frontend.register.passwordless_notice")}
|
||||
</Text>
|
||||
) : (
|
||||
<PasswordFields
|
||||
minimumPasswordComplexity={
|
||||
data.features.minimum_password_complexity
|
||||
}
|
||||
serverErrors={fields.password?.errors ?? []}
|
||||
confirmServerErrors={fields.password_confirm?.errors ?? []}
|
||||
/>
|
||||
)}
|
||||
|
||||
{data.branding.tos_uri && (
|
||||
<Form.InlineField
|
||||
|
||||
@@ -49,7 +49,7 @@ type Documents = {
|
||||
"\n fragment UserEmailList_siteConfig on SiteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n }\n": typeof types.UserEmailList_SiteConfigFragmentDoc,
|
||||
"\n fragment BrowserSessionsOverview_user on User {\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n": typeof types.BrowserSessionsOverview_UserFragmentDoc,
|
||||
"\n query UsernameAvailable($username: String!) {\n usernameAvailable(username: $username) {\n available\n reason\n violationCodes\n }\n }\n": typeof types.UsernameAvailableDocument,
|
||||
"\n query RegistrationToken($token: String!) {\n registrationToken(token: $token) {\n valid\n username\n email\n }\n }\n": typeof types.RegistrationTokenDocument,
|
||||
"\n query RegistrationToken($token: String!) {\n registrationToken(token: $token) {\n valid\n username\n email\n passwordless\n }\n }\n": typeof types.RegistrationTokenDocument,
|
||||
"\n query UserProfile {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n user {\n ...AddEmailForm_user\n ...UserEmailList_user\n ...AccountDeleteButton_user\n hasPassword\n emails(first: 0) {\n totalCount\n }\n }\n }\n }\n\n siteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n accountDeactivationAllowed\n ...AddEmailForm_siteConfig\n ...UserEmailList_siteConfig\n ...PasswordChange_siteConfig\n ...AccountDeleteButton_siteConfig\n }\n }\n": typeof types.UserProfileDocument,
|
||||
"\n query PlanManagementTab {\n siteConfig {\n planManagementIframeUri\n }\n }\n": typeof types.PlanManagementTabDocument,
|
||||
"\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n $lastActive: DateFilter\n ) {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n\n user {\n id\n\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n lastActive: $lastActive\n state: ACTIVE\n ) {\n totalCount\n\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n }\n }\n": typeof types.BrowserSessionListDocument,
|
||||
@@ -107,7 +107,7 @@ const documents: Documents = {
|
||||
"\n fragment UserEmailList_siteConfig on SiteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n }\n": types.UserEmailList_SiteConfigFragmentDoc,
|
||||
"\n fragment BrowserSessionsOverview_user on User {\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n": types.BrowserSessionsOverview_UserFragmentDoc,
|
||||
"\n query UsernameAvailable($username: String!) {\n usernameAvailable(username: $username) {\n available\n reason\n violationCodes\n }\n }\n": types.UsernameAvailableDocument,
|
||||
"\n query RegistrationToken($token: String!) {\n registrationToken(token: $token) {\n valid\n username\n email\n }\n }\n": types.RegistrationTokenDocument,
|
||||
"\n query RegistrationToken($token: String!) {\n registrationToken(token: $token) {\n valid\n username\n email\n passwordless\n }\n }\n": types.RegistrationTokenDocument,
|
||||
"\n query UserProfile {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n user {\n ...AddEmailForm_user\n ...UserEmailList_user\n ...AccountDeleteButton_user\n hasPassword\n emails(first: 0) {\n totalCount\n }\n }\n }\n }\n\n siteConfig {\n emailChangeAllowed\n passwordLoginEnabled\n accountDeactivationAllowed\n ...AddEmailForm_siteConfig\n ...UserEmailList_siteConfig\n ...PasswordChange_siteConfig\n ...AccountDeleteButton_siteConfig\n }\n }\n": types.UserProfileDocument,
|
||||
"\n query PlanManagementTab {\n siteConfig {\n planManagementIframeUri\n }\n }\n": types.PlanManagementTabDocument,
|
||||
"\n query BrowserSessionList(\n $first: Int\n $after: String\n $last: Int\n $before: String\n $lastActive: DateFilter\n ) {\n viewerSession {\n __typename\n ... on BrowserSession {\n id\n\n user {\n id\n\n browserSessions(\n first: $first\n after: $after\n last: $last\n before: $before\n lastActive: $lastActive\n state: ACTIVE\n ) {\n totalCount\n\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n }\n }\n\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n }\n }\n": types.BrowserSessionListDocument,
|
||||
@@ -270,7 +270,7 @@ export function graphql(source: "\n query UsernameAvailable($username: String!)
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query RegistrationToken($token: String!) {\n registrationToken(token: $token) {\n valid\n username\n email\n }\n }\n"): typeof import('./graphql').RegistrationTokenDocument;
|
||||
export function graphql(source: "\n query RegistrationToken($token: String!) {\n registrationToken(token: $token) {\n valid\n username\n email\n passwordless\n }\n }\n"): typeof import('./graphql').RegistrationTokenDocument;
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
||||
@@ -361,7 +361,7 @@ export type RegistrationTokenQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type RegistrationTokenQuery = { registrationToken: { valid: boolean, username: string | null, email: string | null } | null };
|
||||
export type RegistrationTokenQuery = { registrationToken: { valid: boolean, username: string | null, email: string | null, passwordless: boolean } | null };
|
||||
|
||||
export type UserProfileQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -1084,6 +1084,7 @@ export const RegistrationTokenDocument = new TypedDocumentString(`
|
||||
valid
|
||||
username
|
||||
email
|
||||
passwordless
|
||||
}
|
||||
}
|
||||
`) as unknown as TypedDocumentString<RegistrationTokenQuery, RegistrationTokenQueryVariables>;
|
||||
|
||||
Reference in New Issue
Block a user