-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(auth): email password flow (#340)
* feat(login): merge login-with-email into login (#335) * feat(login): merge login-with-email into login * Thomas/create-user (#333) * fix(Collapsible): can't have nested buttons * refactor(user): introduce inbox repository * feat(login): add signup page (#334) * feat(login): add signup page * feat(sign-in-link): send sign in link from settings * chore(sentry): log to console in dev * refactor(auth): use sign-in instead of login * fix(routes): import missing getRoute * feat(auth): add send reset password feature (#336) feat(auth): add send reset password feature * fix(auth): use ErrUnknownUser error code for no account * feat(auth): add description on sign up page
- Loading branch information
Showing
116 changed files
with
1,254 additions
and
3,077 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2,573 changes: 0 additions & 2,573 deletions
2,573
packages/app-builder/public/img/login_background.svg
This file was deleted.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"great_rules_right_tools": "Great rules are built with the <RightTools>right tools</RightTools>.", | ||
"marble_description": "Marble is a real time rule engine for fraud and compliance monitoring, designed for fintech companies and financial institutions.", | ||
"sign_in.google": "Sign in with Google", | ||
"errors.no_account": "No Marble account found for this address.", | ||
"sign_in.email": "Email", | ||
"sign_in.password": "Password", | ||
"sign_in": "Sign in", | ||
"sign_up": "Sign up", | ||
"sign_in.dont_have_an_account": "Don't have an account ? <SignUp>{{signUp}}</SignUp>.", | ||
"sign_in.forgot_password": "Forgot password?", | ||
"sign_in.errors.user_not_found": "No user account found for this address.", | ||
"sign_in.errors.wrong_password_error": "Wrong password.", | ||
"sign_in.errors.invalid_login_credentials": "Invalid login credentials.", | ||
"sign_up.description": "before creating your account, make sure an admin has added you to your organization.", | ||
"sign_up.already_have_an_account_sign_up": "Already have an account ? <SignIn>{{signIn}}</SignIn>.", | ||
"sign_up.errors.weak_password_error": "Password is too weak. It should be at least 6 characters long.", | ||
"sign_up.errors.email_already_exists": "This email is already in use. Please sign in or reset your password.", | ||
"email-verification.description": "Please check your email and click on the link to verify your account. After verifying your email, <strong>you will need to login again.</strong>", | ||
"email-verification.resend": "resend verification email", | ||
"email-verification.wrong_place": "Wrong place ? go back to <SignIn>{{signIn}}</SignIn>.", | ||
"reset-password.send": "send reset password email", | ||
"reset-password.email_sent": "An email has been sent to you with a link to reset your password.", | ||
"reset-password.wrong_place": "Wrong place ? go back to <SignIn>{{signIn}}</SignIn>." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,4 @@ | ||
{ | ||
"title": "Connectez-vous à votre compte", | ||
"help.no_account": "Vous n'avez pas de compte ?", | ||
"help.contact_us": "Contactez-nous", | ||
"sign_in.google": "Connectez-vous avec Google", | ||
"errors.no_account": "Aucun compte Marble trouvé pour cette adresse." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { type AuthErrors } from '@app-builder/models'; | ||
import clsx from 'clsx'; | ||
import { type ParseKeys } from 'i18next'; | ||
import { useTranslation } from 'react-i18next'; | ||
|
||
import { authI18n } from './auth-i18n'; | ||
|
||
const errorLabels: Record<AuthErrors, ParseKeys<typeof authI18n>> = { | ||
NoAccount: 'auth:errors.no_account', | ||
Unknown: 'common:errors.unknown', | ||
}; | ||
|
||
export function AuthError({ | ||
error, | ||
className, | ||
}: { | ||
error: AuthErrors; | ||
className?: string; | ||
}) { | ||
const { t } = useTranslation(authI18n); | ||
return ( | ||
<p | ||
className={clsx( | ||
'text-m bg-red-05 w-full rounded-sm p-2 font-normal text-red-100', | ||
className, | ||
)} | ||
> | ||
{t(errorLabels[error])} | ||
</p> | ||
); | ||
} |
95 changes: 95 additions & 0 deletions
95
packages/app-builder/src/components/Auth/ResetPassword.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { | ||
FormControl, | ||
FormError, | ||
FormField, | ||
FormItem, | ||
FormLabel, | ||
} from '@app-builder/components/Form'; | ||
import { useSendPasswordResetEmail } from '@app-builder/services/auth/auth.client'; | ||
import { clientServices } from '@app-builder/services/init.client'; | ||
import { zodResolver } from '@hookform/resolvers/zod'; | ||
import * as Sentry from '@sentry/remix'; | ||
import { FormProvider, useForm, useFormContext } from 'react-hook-form'; | ||
import toast from 'react-hot-toast'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { ClientOnly } from 'remix-utils/client-only'; | ||
import { Button, Input } from 'ui-design-system'; | ||
import * as z from 'zod'; | ||
|
||
const resetPasswordFormSchema = z.object({ | ||
email: z.string().email(), | ||
}); | ||
type ResetPasswordFormValues = z.infer<typeof resetPasswordFormSchema>; | ||
|
||
export function ResetPassword() { | ||
const { t } = useTranslation(['auth', 'common']); | ||
|
||
const formMethods = useForm<z.infer<typeof resetPasswordFormSchema>>({ | ||
resolver: zodResolver(resetPasswordFormSchema), | ||
defaultValues: { | ||
email: '', | ||
}, | ||
}); | ||
const { control } = formMethods; | ||
|
||
const children = ( | ||
<> | ||
<FormField | ||
control={control} | ||
name="email" | ||
render={({ field }) => ( | ||
<FormItem className="flex flex-col items-start gap-2"> | ||
<FormLabel>{t('auth:sign_in.email')}</FormLabel> | ||
<FormControl> | ||
<Input className="w-full" type="email" {...field} /> | ||
</FormControl> | ||
<FormError /> | ||
</FormItem> | ||
)} | ||
/> | ||
<Button type="submit">{t('auth:reset-password.send')}</Button> | ||
</> | ||
); | ||
|
||
return ( | ||
<FormProvider {...formMethods}> | ||
<ClientOnly fallback={<ResetPasswordForm>{children}</ResetPasswordForm>}> | ||
{() => <ClientResetPasswordForm>{children}</ClientResetPasswordForm>} | ||
</ClientOnly> | ||
</FormProvider> | ||
); | ||
} | ||
|
||
function ResetPasswordForm(props: React.ComponentPropsWithoutRef<'form'>) { | ||
return <form noValidate className="flex w-full flex-col gap-4" {...props} />; | ||
} | ||
|
||
function ClientResetPasswordForm({ children }: { children: React.ReactNode }) { | ||
const { t } = useTranslation(['auth', 'common']); | ||
|
||
const sendPasswordResetEmail = useSendPasswordResetEmail( | ||
clientServices.authenticationClientService, | ||
); | ||
|
||
const { handleSubmit } = useFormContext<ResetPasswordFormValues>(); | ||
|
||
const handleResetPassword = handleSubmit(async ({ email }) => { | ||
try { | ||
await sendPasswordResetEmail(email); | ||
toast.success(t('auth:reset-password.email_sent')); | ||
} catch (error) { | ||
Sentry.captureException(error); | ||
toast.error(t('common:errors.unknown')); | ||
} | ||
}); | ||
|
||
return ( | ||
<ResetPasswordForm | ||
onSubmit={(e) => { | ||
void handleResetPassword(e); | ||
}} | ||
> | ||
{children} | ||
</ResetPasswordForm> | ||
); | ||
} |
61 changes: 61 additions & 0 deletions
61
packages/app-builder/src/components/Auth/SendEmailVerification.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { useResendEmailVerification } from '@app-builder/services/auth/auth.client'; | ||
import { clientServices } from '@app-builder/services/init.client'; | ||
import { getRoute } from '@app-builder/utils/routes'; | ||
import { useNavigate } from '@remix-run/react'; | ||
import * as Sentry from '@sentry/remix'; | ||
import toast from 'react-hot-toast'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { ClientOnly } from 'remix-utils/client-only'; | ||
import { useHydrated } from 'remix-utils/use-hydrated'; | ||
import { Button } from 'ui-design-system'; | ||
|
||
function SendEmailVerificationButton({ onClick }: { onClick?: () => void }) { | ||
const { t } = useTranslation(['auth']); | ||
const isHydrated = useHydrated(); | ||
|
||
return ( | ||
<Button | ||
variant="primary" | ||
className="w-full capitalize" | ||
onClick={onClick} | ||
disabled={!isHydrated} | ||
> | ||
{t('auth:email-verification.resend')} | ||
</Button> | ||
); | ||
} | ||
|
||
function ClientSendEmailVerificationButton() { | ||
const { t } = useTranslation(['common']); | ||
|
||
const resendEmailVerification = useResendEmailVerification( | ||
clientServices.authenticationClientService, | ||
); | ||
|
||
const navigate = useNavigate(); | ||
async function onSendClick() { | ||
try { | ||
const logout = () => navigate(getRoute('/ressources/auth/logout')); | ||
await resendEmailVerification(logout); | ||
} catch (error) { | ||
Sentry.captureException(error); | ||
toast.error(t('common:errors.unknown')); | ||
} | ||
} | ||
|
||
return ( | ||
<SendEmailVerificationButton | ||
onClick={() => { | ||
void onSendClick(); | ||
}} | ||
/> | ||
); | ||
} | ||
|
||
export function SendEmailVerification() { | ||
return ( | ||
<ClientOnly fallback={<SendEmailVerificationButton />}> | ||
{() => <ClientSendEmailVerificationButton />} | ||
</ClientOnly> | ||
); | ||
} |
Oops, something went wrong.