Microsoft Login is mostly working now
This commit is contained in:
BIN
public/icons/microsoft.png
Normal file
BIN
public/icons/microsoft.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
@ -1,5 +1,5 @@
|
|||||||
|
// src/app/(auth-pages)/auth/callback/route.ts
|
||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import 'server-only';
|
import 'server-only';
|
||||||
import { createServerClient } from '@/utils/supabase';
|
import { createServerClient } from '@/utils/supabase';
|
||||||
import { type EmailOtpType } from '@supabase/supabase-js';
|
import { type EmailOtpType } from '@supabase/supabase-js';
|
||||||
@ -7,13 +7,26 @@ import { type NextRequest } from 'next/server';
|
|||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
export const GET = async (request: NextRequest) => {
|
export const GET = async (request: NextRequest) => {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams, origin } = new URL(request.url);
|
||||||
|
const code = searchParams.get('code');
|
||||||
const token_hash = searchParams.get('token');
|
const token_hash = searchParams.get('token');
|
||||||
const type = searchParams.get('type') as EmailOtpType | null;
|
const type = searchParams.get('type') as EmailOtpType | null;
|
||||||
const redirectTo = searchParams.get('redirect_to') ?? '/';
|
const redirectTo = searchParams.get('redirect_to') ?? '/';
|
||||||
|
|
||||||
if (token_hash && type) {
|
|
||||||
const supabase = await createServerClient();
|
const supabase = await createServerClient();
|
||||||
|
|
||||||
|
// Handle OAuth callback (Microsoft, Apple, etc.)
|
||||||
|
if (code) {
|
||||||
|
const { error } = await supabase.auth.exchangeCodeForSession(code);
|
||||||
|
if (error) {
|
||||||
|
console.error('OAuth error:', error);
|
||||||
|
return redirect(`/sign-in?error=${encodeURIComponent(error.message)}`);
|
||||||
|
}
|
||||||
|
return redirect(redirectTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle email OTP (existing logic)
|
||||||
|
if (token_hash && type) {
|
||||||
const { error } = await supabase.auth.verifyOtp({
|
const { error } = await supabase.auth.verifyOtp({
|
||||||
type,
|
type,
|
||||||
token_hash,
|
token_hash,
|
||||||
@ -25,9 +38,9 @@ export const GET = async (request: NextRequest) => {
|
|||||||
return redirect('/profile');
|
return redirect('/profile');
|
||||||
if (type === 'invite')
|
if (type === 'invite')
|
||||||
return redirect('/sign-up');
|
return redirect('/sign-up');
|
||||||
else return redirect(`/?Could not identify type ${type as string}`)
|
|
||||||
}
|
}
|
||||||
else return redirect(`/?${error.message}`);
|
return redirect(`/?error=${encodeURIComponent(error?.message || 'Unknown error')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect('/');
|
return redirect('/');
|
||||||
};
|
};
|
||||||
|
@ -71,7 +71,7 @@ const ForgotPassword = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card className='min-w-xs md:min-w-sm'>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className='text-2xl font-medium'>Reset Password</CardTitle>
|
<CardTitle className='text-2xl font-medium'>Reset Password</CardTitle>
|
||||||
<CardDescription className='text-sm text-foreground'>
|
<CardDescription className='text-sm text-foreground'>
|
||||||
|
@ -18,11 +18,13 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
} from '@/components/ui';
|
} from '@/components/ui';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { signIn } from '@/lib/actions';
|
import { signIn, signInWithMicrosoft } from '@/lib/actions';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useAuth } from '@/components/context/auth';
|
import { useAuth } from '@/components/context/auth';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { StatusMessage, SubmitButton } from '@/components/default';
|
import { StatusMessage, SubmitButton } from '@/components/default';
|
||||||
|
import { Separator } from '@/components/ui';
|
||||||
|
import { SignInWithMicrosoft } from '@/components/default/auth/SignInWithMicrosoft';
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
email: z.string().email({
|
email: z.string().email({
|
||||||
@ -91,7 +93,7 @@ const Login = () => {
|
|||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(handleSignIn)}
|
onSubmit={form.handleSubmit(handleSignIn)}
|
||||||
className='flex flex-col min-w-64 space-y-6'
|
className='flex flex-col min-w-64 space-y-6 pb-4'
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
@ -144,7 +146,8 @@ const Login = () => {
|
|||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
<Separator className='my-4' />
|
||||||
|
<SignInWithMicrosoft />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
59
src/components/default/auth/SignInWithMicrosoft.tsx
Normal file
59
src/components/default/auth/SignInWithMicrosoft.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// src/components/default/auth/SignInWithMicrosoft.tsx
|
||||||
|
'use client';
|
||||||
|
import { signInWithMicrosoft } from '@/lib/actions';
|
||||||
|
import { StatusMessage, SubmitButton } from '@/components/default';
|
||||||
|
import { useAuth } from '@/components/context/auth';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
export const SignInWithMicrosoft = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { isLoading, refreshUserData } = useAuth();
|
||||||
|
const [statusMessage, setStatusMessage] = useState('');
|
||||||
|
const [isSigningIn, setIsSigningIn] = useState(false);
|
||||||
|
|
||||||
|
const handleSignInWithMicrosoft = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
setStatusMessage('');
|
||||||
|
setIsSigningIn(true);
|
||||||
|
|
||||||
|
const result = await signInWithMicrosoft();
|
||||||
|
|
||||||
|
if (result?.success && result.data) {
|
||||||
|
// Redirect to Microsoft OAuth page
|
||||||
|
window.location.href = result.data;
|
||||||
|
} else {
|
||||||
|
setStatusMessage(`Error: ${result.error}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setStatusMessage(
|
||||||
|
`Error: ${error instanceof Error ? error.message : 'Could not sign in!'}`
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setIsSigningIn(false);
|
||||||
|
await refreshUserData();
|
||||||
|
router.push('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSignInWithMicrosoft}>
|
||||||
|
<SubmitButton
|
||||||
|
className='w-full cursor-pointer'
|
||||||
|
disabled={isLoading || isSigningIn}
|
||||||
|
pendingText='Redirecting...'
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<div className='flex items-center gap-2'>
|
||||||
|
<Image src='/icons/microsoft.png' alt='Microsoft logo' width={20} height={20} />
|
||||||
|
<p>Sign in with Microsoft</p>
|
||||||
|
</div>
|
||||||
|
</SubmitButton>
|
||||||
|
{statusMessage && (
|
||||||
|
<StatusMessage message={{ error: statusMessage }} />
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
@ -1,2 +1,2 @@
|
|||||||
export { StatusMessage, type Message } from '@/components/default/status-message';
|
export { StatusMessage, type Message } from '@/components/default/StatusMessage';
|
||||||
export { SubmitButton } from '@/components/default/submit-button';
|
export { SubmitButton } from '@/components/default/SubmitButton';
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export { CodeBlock } from './code-block';
|
export { CodeBlock } from './CodeBlock';
|
||||||
export { FetchDataSteps } from './fetch-data-steps';
|
export { FetchDataSteps } from './FetchDataSteps';
|
||||||
export { TutorialStep } from './tutorial-step';
|
export { TutorialStep } from './TutorialStep';
|
||||||
|
@ -62,6 +62,18 @@ export const signIn = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const signInWithMicrosoft = async (): Promise<Result<string>> => {
|
||||||
|
const supabase = await createServerClient();
|
||||||
|
const { data, error } = await supabase.auth.signInWithOAuth({
|
||||||
|
provider: 'azure',
|
||||||
|
options: {
|
||||||
|
scopes: 'email',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (error) return { success: false, error: error.message };
|
||||||
|
return { success: true, data: data.url};
|
||||||
|
};
|
||||||
|
|
||||||
export const forgotPassword = async (formData: FormData): Promise<Result<string | null>> => {
|
export const forgotPassword = async (formData: FormData): Promise<Result<string | null>> => {
|
||||||
const email = formData.get('email') as string;
|
const email = formData.get('email') as string;
|
||||||
const supabase = await createServerClient();
|
const supabase = await createServerClient();
|
||||||
|
Reference in New Issue
Block a user