✨ feat: create provider callback handler
This commit is contained in:
@ -1,20 +1,7 @@
|
|||||||
import React from "react";
|
import AuthCallbackIndex from "@/features/authCallback";
|
||||||
|
|
||||||
const page = async ({
|
const page = async () => {
|
||||||
params,
|
return <AuthCallbackIndex />;
|
||||||
searchParams,
|
|
||||||
}: {
|
|
||||||
params: { name: string };
|
|
||||||
searchParams: { [key: string]: string | string[] | undefined };
|
|
||||||
}) => {
|
|
||||||
const resolvedParams = await params;
|
|
||||||
const resolvedSearchParams = await searchParams;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>Loading....</h1>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default page;
|
export default page;
|
||||||
|
|||||||
3
bun.lock
3
bun.lock
@ -16,6 +16,7 @@
|
|||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"shadcn": "^3.6.3",
|
"shadcn": "^3.6.3",
|
||||||
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
},
|
},
|
||||||
@ -1276,6 +1277,8 @@
|
|||||||
|
|
||||||
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||||
|
|
||||||
|
"sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="],
|
||||||
|
|
||||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|||||||
40
features/authCallback/index.tsx
Normal file
40
features/authCallback/index.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"use client";
|
||||||
|
import { Spinner } from "@/shared/libs/shadcn/ui/spinner";
|
||||||
|
import { submitProviderCallback } from "@/shared/widgets/signin/actions/submitProviderCallback";
|
||||||
|
import { useParams, useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const AuthCallbackIndex = () => {
|
||||||
|
const { name } = useParams();
|
||||||
|
const queries = useSearchParams().toString();
|
||||||
|
const router = useRouter();
|
||||||
|
const [textDescription, setTextDescription] = useState(
|
||||||
|
"We are processing your authentication."
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const response = await submitProviderCallback(name as string, queries);
|
||||||
|
if (response.success) {
|
||||||
|
setTextDescription("Authentication successful! Redirecting...");
|
||||||
|
router.push("/");
|
||||||
|
} else {
|
||||||
|
setTextDescription("Authentication failed. Please try again.");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full flex flex-col items-center gap-2 pt-8">
|
||||||
|
<Spinner className="size-6" />
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-neutral-200 text-base">Please wait...</h1>
|
||||||
|
<p className="font-normal text-neutral-400 text-sm">
|
||||||
|
{textDescription}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AuthCallbackIndex;
|
||||||
@ -20,6 +20,7 @@
|
|||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"shadcn": "^3.6.3",
|
"shadcn": "^3.6.3",
|
||||||
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"tw-animate-css": "^1.4.0"
|
"tw-animate-css": "^1.4.0"
|
||||||
},
|
},
|
||||||
|
|||||||
49
shared/libs/shadcn/ui/sonner.tsx
Normal file
49
shared/libs/shadcn/ui/sonner.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useTheme } from "next-themes"
|
||||||
|
import { Toaster as Sonner, type ToasterProps } from "sonner"
|
||||||
|
import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react"
|
||||||
|
|
||||||
|
const Toaster = ({ ...props }: ToasterProps) => {
|
||||||
|
const { theme = "system" } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sonner
|
||||||
|
theme={theme as ToasterProps["theme"]}
|
||||||
|
className="toaster group"
|
||||||
|
icons={{
|
||||||
|
success: (
|
||||||
|
<CircleCheckIcon className="size-4" />
|
||||||
|
),
|
||||||
|
info: (
|
||||||
|
<InfoIcon className="size-4" />
|
||||||
|
),
|
||||||
|
warning: (
|
||||||
|
<TriangleAlertIcon className="size-4" />
|
||||||
|
),
|
||||||
|
error: (
|
||||||
|
<OctagonXIcon className="size-4" />
|
||||||
|
),
|
||||||
|
loading: (
|
||||||
|
<Loader2Icon className="size-4 animate-spin" />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--normal-bg": "var(--popover)",
|
||||||
|
"--normal-text": "var(--popover-foreground)",
|
||||||
|
"--normal-border": "var(--border)",
|
||||||
|
"--border-radius": "var(--radius)",
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
toastOptions={{
|
||||||
|
classNames: {
|
||||||
|
toast: "cn-toast",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Toaster }
|
||||||
54
shared/widgets/signin/actions/submitProviderCallback.ts
Normal file
54
shared/widgets/signin/actions/submitProviderCallback.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { backendFetch, BackendResponse } from "@/shared/helper/backendFetch";
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
|
||||||
|
export const submitProviderCallback = async (
|
||||||
|
providerName: string,
|
||||||
|
queries?: unknown
|
||||||
|
): Promise<
|
||||||
|
BackendResponse<{
|
||||||
|
authToken: string;
|
||||||
|
}>
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const envKey = providerName.toUpperCase() + "_CALLBACK_URL";
|
||||||
|
|
||||||
|
const authClientCallbackUrl = (await backendFetch(
|
||||||
|
"auth/providers/" + providerName + "/callback"
|
||||||
|
)) as BackendResponse<{
|
||||||
|
callback_url: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
if (!authClientCallbackUrl.success)
|
||||||
|
throw new Error("Failed to get auth client callback URL");
|
||||||
|
|
||||||
|
const responseProvision = (await backendFetch(
|
||||||
|
`${authClientCallbackUrl.data?.callback_url!}?callbackURI=${
|
||||||
|
process.env.APP_URL
|
||||||
|
}${process.env[envKey]}&${queries}`
|
||||||
|
)) as BackendResponse<{
|
||||||
|
authToken: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
if (!responseProvision.success)
|
||||||
|
throw new Error("Failed to submit provider callback");
|
||||||
|
|
||||||
|
(await cookies()).set({
|
||||||
|
name: "auth_token",
|
||||||
|
value: responseProvision.data?.authToken!,
|
||||||
|
httpOnly: true,
|
||||||
|
path: "/",
|
||||||
|
secure: process.env.NODE_ENV === "production",
|
||||||
|
maxAge: Number(process.env.SESSION_EXPIRE),
|
||||||
|
});
|
||||||
|
|
||||||
|
return responseProvision;
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Error submitting provider callback",
|
||||||
|
error: error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user