mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-05-25 18:34:20 +00:00
frontend: fetch min password complexity and enforce locally
This commit is contained in:
committed by
reivilibre
parent
df2271630a
commit
bf8bd85170
@@ -152,6 +152,7 @@
|
||||
"sequences": "Avoid common character sequences.",
|
||||
"use_words": "Use multiple words, but avoid common phrases."
|
||||
},
|
||||
"too_weak": "This password is too weak",
|
||||
"warning": {
|
||||
"common": "This is a commonly used password.",
|
||||
"common_names": "Common names and surnames are easy to guess.",
|
||||
|
||||
@@ -52,6 +52,7 @@ const documents = {
|
||||
"\n query CurrentViewerQuery {\n viewer {\n __typename\n ... on Node {\n id\n }\n }\n }\n": types.CurrentViewerQueryDocument,
|
||||
"\n query DeviceRedirectQuery($deviceId: String!, $userId: ID!) {\n session(deviceId: $deviceId, userId: $userId) {\n __typename\n ... on Node {\n id\n }\n }\n }\n": types.DeviceRedirectQueryDocument,
|
||||
"\n query VerifyEmailQuery($id: ID!) {\n userEmail(id: $id) {\n ...UserEmail_verifyEmail\n }\n }\n": types.VerifyEmailQueryDocument,
|
||||
"\n query PasswordChangeQuery {\n viewer {\n __typename\n ... on Node {\n id\n }\n }\n\n siteConfig {\n id\n minimumPasswordComplexity\n }\n }\n": types.PasswordChangeQueryDocument,
|
||||
"\n mutation ChangePassword(\n $userId: ID!\n $oldPassword: String!\n $newPassword: String!\n ) {\n setPassword(\n input: {\n userId: $userId\n currentPassword: $oldPassword\n newPassword: $newPassword\n }\n ) {\n status\n }\n }\n": types.ChangePasswordDocument,
|
||||
"\n mutation AllowCrossSigningReset($userId: ID!) {\n allowUserCrossSigningReset(input: { userId: $userId }) {\n user {\n id\n }\n }\n }\n": types.AllowCrossSigningResetDocument,
|
||||
};
|
||||
@@ -226,6 +227,10 @@ export function graphql(source: "\n query DeviceRedirectQuery($deviceId: 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 VerifyEmailQuery($id: ID!) {\n userEmail(id: $id) {\n ...UserEmail_verifyEmail\n }\n }\n"): (typeof documents)["\n query VerifyEmailQuery($id: ID!) {\n userEmail(id: $id) {\n ...UserEmail_verifyEmail\n }\n }\n"];
|
||||
/**
|
||||
* 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 PasswordChangeQuery {\n viewer {\n __typename\n ... on Node {\n id\n }\n }\n\n siteConfig {\n id\n minimumPasswordComplexity\n }\n }\n"): (typeof documents)["\n query PasswordChangeQuery {\n viewer {\n __typename\n ... on Node {\n id\n }\n }\n\n siteConfig {\n id\n minimumPasswordComplexity\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
||||
@@ -1630,6 +1630,11 @@ export type VerifyEmailQueryQuery = { __typename?: 'Query', userEmail?: (
|
||||
& { ' $fragmentRefs'?: { 'UserEmail_VerifyEmailFragment': UserEmail_VerifyEmailFragment } }
|
||||
) | null };
|
||||
|
||||
export type PasswordChangeQueryQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type PasswordChangeQueryQuery = { __typename?: 'Query', viewer: { __typename: 'Anonymous', id: string } | { __typename: 'User', id: string }, siteConfig: { __typename?: 'SiteConfig', id: string, minimumPasswordComplexity: number } };
|
||||
|
||||
export type ChangePasswordMutationVariables = Exact<{
|
||||
userId: Scalars['ID']['input'];
|
||||
oldPassword: Scalars['String']['input'];
|
||||
@@ -1685,5 +1690,6 @@ export const OAuth2ClientQueryDocument = {"kind":"Document","definitions":[{"kin
|
||||
export const CurrentViewerQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CurrentViewerQuery"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Node"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode<CurrentViewerQueryQuery, CurrentViewerQueryQueryVariables>;
|
||||
export const DeviceRedirectQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"DeviceRedirectQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"deviceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"session"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"deviceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"deviceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Node"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode<DeviceRedirectQueryQuery, DeviceRedirectQueryQueryVariables>;
|
||||
export const VerifyEmailQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"VerifyEmailQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userEmail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserEmail_verifyEmail"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserEmail_verifyEmail"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"UserEmail"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]} as unknown as DocumentNode<VerifyEmailQueryQuery, VerifyEmailQueryQueryVariables>;
|
||||
export const PasswordChangeQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PasswordChangeQuery"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Node"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"siteConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"minimumPasswordComplexity"}}]}}]}}]} as unknown as DocumentNode<PasswordChangeQueryQuery, PasswordChangeQueryQueryVariables>;
|
||||
export const ChangePasswordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ChangePassword"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"oldPassword"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"newPassword"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"setPassword"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userId"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"currentPassword"},"value":{"kind":"Variable","name":{"kind":"Name","value":"oldPassword"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"newPassword"},"value":{"kind":"Variable","name":{"kind":"Name","value":"newPassword"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]} as unknown as DocumentNode<ChangePasswordMutation, ChangePasswordMutationVariables>;
|
||||
export const AllowCrossSigningResetDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AllowCrossSigningReset"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allowUserCrossSigningReset"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userId"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode<AllowCrossSigningResetMutation, AllowCrossSigningResetMutationVariables>;
|
||||
@@ -37,14 +37,19 @@ import {
|
||||
estimatePasswordComplexity,
|
||||
} from "../utils/password_complexity";
|
||||
|
||||
const CURRENT_VIEWER_QUERY = graphql(/* GraphQL */ `
|
||||
query CurrentViewerQuery {
|
||||
const QUERY = graphql(/* GraphQL */ `
|
||||
query PasswordChangeQuery {
|
||||
viewer {
|
||||
__typename
|
||||
... on Node {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
siteConfig {
|
||||
id
|
||||
minimumPasswordComplexity
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -68,13 +73,13 @@ const CHANGE_PASSWORD_MUTATION = graphql(/* GraphQL */ `
|
||||
|
||||
export const Route = createFileRoute("/password/change/")({
|
||||
async loader({ context, abortController: { signal } }) {
|
||||
const viewer = await context.client.query(
|
||||
CURRENT_VIEWER_QUERY,
|
||||
const queryResult = await context.client.query(
|
||||
QUERY,
|
||||
{},
|
||||
{ fetchOptions: { signal } },
|
||||
);
|
||||
if (viewer.error) throw viewer.error;
|
||||
if (viewer.data?.viewer.__typename !== "User") throw notFound();
|
||||
if (queryResult.error) throw queryResult.error;
|
||||
if (queryResult.data?.viewer.__typename !== "User") throw notFound();
|
||||
},
|
||||
|
||||
component: ChangePassword,
|
||||
@@ -108,11 +113,13 @@ const usePasswordComplexity = (password: string): PasswordComplexity => {
|
||||
|
||||
function ChangePassword(): React.ReactNode {
|
||||
const { t } = useTranslation();
|
||||
const [viewer] = useQuery({ query: CURRENT_VIEWER_QUERY });
|
||||
const [queryResult] = useQuery({ query: QUERY });
|
||||
const router = useRouter();
|
||||
if (viewer.error) throw viewer.error;
|
||||
if (viewer.data?.viewer.__typename !== "User") throw notFound();
|
||||
const userId = viewer.data.viewer.id;
|
||||
if (queryResult.error) throw queryResult.error;
|
||||
if (queryResult.data?.viewer.__typename !== "User") throw notFound();
|
||||
const userId = queryResult.data.viewer.id;
|
||||
const minPasswordComplexity =
|
||||
queryResult.data.siteConfig.minimumPasswordComplexity;
|
||||
|
||||
const currentPasswordRef = useRef<HTMLInputElement>(null);
|
||||
const newPasswordRef = useRef<HTMLInputElement>(null);
|
||||
@@ -263,6 +270,12 @@ function ChangePassword(): React.ReactNode {
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
/>
|
||||
|
||||
<Form.ErrorMessage
|
||||
match={() => passwordComplexity.score < minPasswordComplexity}
|
||||
>
|
||||
{t("frontend.password_strength.too_weak")}
|
||||
</Form.ErrorMessage>
|
||||
|
||||
<Progress
|
||||
size="sm"
|
||||
getValueLabel={() => passwordComplexity.scoreText}
|
||||
|
||||
Reference in New Issue
Block a user