Skip to content

Commit b3d6981

Browse files
authored
Fix 460 missing providers (#464)
* Display an error message in the frontend * Make a backup client side call to getProviders
1 parent fa8ac7c commit b3d6981

File tree

2 files changed

+85
-41
lines changed

2 files changed

+85
-41
lines changed

public/locales/en/common.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"errors": {
8989
"name_required": "Name is required",
9090
"saving_expense": "Error while saving expense",
91+
"no_providers": "Error when fetching authentication providers. Please check backend logs.",
9192
"something_went_wrong": "Something went wrong",
9293
"group_balances_malformed": "Group balances data is malformed, please report an issue or try recalculating.",
9394
"valid_email": "Enter valid email"

src/pages/auth/signin.tsx

Lines changed: 84 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
FormMessage,
1717
} from '~/components/ui/form';
1818
import { Input } from '~/components/ui/input';
19+
import { LoadingSpinner } from '~/components/ui/spinner';
1920
import { LanguageSelector } from '~/components/LanguageSelector';
2021
import { env } from '~/env';
2122
import { getServerAuthSession } from '~/server/auth';
@@ -53,10 +54,12 @@ const Home: NextPage<{
5354
feedbackEmail: string;
5455
providers: ClientSafeProvider[];
5556
callbackUrl?: string;
56-
}> = ({ error, providers, feedbackEmail, callbackUrl }) => {
57+
}> = ({ error, providers: serverProviders, feedbackEmail, callbackUrl }) => {
5758
const { t } = useTranslation('signin');
5859
const [emailStatus, setEmailStatus] = useState<'idle' | 'sending' | 'success'>('idle');
5960
const [showVerificationStep, setShowVerificationStep] = useState(false);
61+
const [providers, setProviders] = useState<ClientSafeProvider[]>(serverProviders);
62+
const [isLoadingProviders, setIsLoadingProviders] = useState(false);
6063

6164
const emailForm = useForm<EmailFormValues>({
6265
resolver: zodResolver(emailSchema(t)),
@@ -65,6 +68,30 @@ const Home: NextPage<{
6568
},
6669
});
6770

71+
// Client-side fallback for getProviders when server-side call fails
72+
useEffect(() => {
73+
if (serverProviders.length > 0) {
74+
return;
75+
}
76+
77+
void (async () => {
78+
setIsLoadingProviders(true);
79+
try {
80+
const clientProviders = await getProviders();
81+
if (clientProviders && Object.keys(clientProviders).length > 0) {
82+
setProviders(Object.values(clientProviders));
83+
} else {
84+
throw new Error('No providers returned from getProviders()');
85+
}
86+
} catch (error) {
87+
console.error('Error fetching providers client-side:', error);
88+
toast.error(t('errors.no_providers'), { duration: 8000 });
89+
} finally {
90+
setIsLoadingProviders(false);
91+
}
92+
})();
93+
}, [serverProviders.length, t]);
94+
6895
useEffect(() => {
6996
if (error) {
7097
if ('SignupDisabled' === error) {
@@ -145,51 +172,62 @@ const Home: NextPage<{
145172
<LanguageSelector />
146173
</div>
147174

148-
{providers
149-
.filter((provider) => 'email' !== provider.id)
150-
.map((provider) => (
151-
<Button
152-
className="mx-auto flex w-[300px] items-center gap-3 bg-white hover:bg-gray-100 focus:bg-gray-100"
153-
onClick={handleProviderSignIn(provider.id)}
154-
key={provider.id}
155-
>
156-
{providerSvgs[provider.id as keyof typeof providerSvgs]}
157-
{t('auth.continue_with', { provider: provider.name })}
158-
</Button>
159-
))}
160-
{providers && 2 === providers.length && (
161-
<div className="mt-6 flex w-[300px] items-center justify-between gap-2">
162-
<p className="bg-background z-10 ml-[150px] -translate-x-1/2 px-4 text-sm">
163-
{t('ui.or', { ns: 'common' })}
164-
</p>
165-
<div className="absolute h-px w-[300px] bg-linear-to-r from-zinc-800 via-zinc-300 to-zinc-800" />
175+
{isLoadingProviders ? (
176+
<div className="flex h-[200px] w-[300px] items-center justify-center">
177+
<LoadingSpinner className="h-8 w-8" />
166178
</div>
167-
)}
168-
{providers.find((provider) => 'email' === provider.id) ? (
179+
) : (
169180
<>
170-
<Form {...emailForm}>
171-
<form onSubmit={emailForm.handleSubmit(onEmailSubmit)} className="mt-6 space-y-8">
172-
<FormField control={emailForm.control} name="email" render={field} />
181+
{providers
182+
.filter((provider) => 'email' !== provider.id)
183+
.map((provider) => (
173184
<Button
174-
className="mt-6 w-[300px] bg-white hover:bg-gray-100 focus:bg-gray-100"
175-
type="submit"
176-
disabled={'sending' === emailStatus}
185+
className="mx-auto flex w-[300px] items-center gap-3 bg-white hover:bg-gray-100 focus:bg-gray-100"
186+
onClick={handleProviderSignIn(provider.id)}
187+
key={provider.id}
177188
>
178-
{'sending' === emailStatus ? t('auth.sending') : t('auth.send_magic_link')}
189+
{providerSvgs[provider.id as keyof typeof providerSvgs]}
190+
{t('auth.continue_with', { provider: provider.name })}
179191
</Button>
180-
</form>
181-
</Form>
192+
))}
193+
{providers && 2 === providers.length && (
194+
<div className="mt-6 flex w-[300px] items-center justify-between gap-2">
195+
<p className="bg-background z-10 ml-[150px] -translate-x-1/2 px-4 text-sm">
196+
{t('ui.or', { ns: 'common' })}
197+
</p>
198+
<div className="absolute h-px w-[300px] bg-linear-to-r from-zinc-800 via-zinc-300 to-zinc-800" />
199+
</div>
200+
)}
201+
{providers.find((provider) => 'email' === provider.id) ? (
202+
<>
203+
<Form {...emailForm}>
204+
<form
205+
onSubmit={emailForm.handleSubmit(onEmailSubmit)}
206+
className="mt-6 space-y-8"
207+
>
208+
<FormField control={emailForm.control} name="email" render={field} />
209+
<Button
210+
className="mt-6 w-[300px] bg-white hover:bg-gray-100 focus:bg-gray-100"
211+
type="submit"
212+
disabled={'sending' === emailStatus}
213+
>
214+
{'sending' === emailStatus ? t('auth.sending') : t('auth.send_magic_link')}
215+
</Button>
216+
</form>
217+
</Form>
218+
</>
219+
) : null}
220+
{feedbackEmail && (
221+
<p className="text-muted-foreground mt-6 w-[300px] text-center text-sm">
222+
{t('auth.trouble_logging_in')}
223+
<br />
224+
{/* oxlint-disable-next-line next/no-html-link-for-pages */}
225+
<a className="underline" href={feedbackEmailLink}>
226+
{feedbackEmail ?? ''}
227+
</a>
228+
</p>
229+
)}
182230
</>
183-
) : null}
184-
{feedbackEmail && (
185-
<p className="text-muted-foreground mt-6 w-[300px] text-center text-sm">
186-
{t('auth.trouble_logging_in')}
187-
<br />
188-
{/* oxlint-disable-next-line next/no-html-link-for-pages */}
189-
<a className="underline" href={feedbackEmailLink}>
190-
{feedbackEmail ?? ''}
191-
</a>
192-
</p>
193231
)}
194232
</div>
195233
</main>
@@ -201,7 +239,12 @@ export default Home;
201239

202240
export const getServerSideProps: GetServerSideProps = async (context) => {
203241
const session = await getServerAuthSession(context);
204-
const providers = await getProviders();
242+
let providers: Record<string, ClientSafeProvider> | null = null;
243+
try {
244+
providers = await getProviders();
245+
} catch (error) {
246+
console.error(error);
247+
}
205248
const { callbackUrl, error } = context.query;
206249

207250
if (session) {

0 commit comments

Comments
 (0)