diff --git a/app/(clean)/auth/providers/[name]/callback/page.tsx b/app/(clean)/auth/providers/[name]/callback/page.tsx
index 9b9cf13..b9dc0d8 100644
--- a/app/(clean)/auth/providers/[name]/callback/page.tsx
+++ b/app/(clean)/auth/providers/[name]/callback/page.tsx
@@ -1,20 +1,7 @@
-import React from "react";
+import AuthCallbackIndex from "@/features/authCallback";
-const page = async ({
- params,
- searchParams,
-}: {
- params: { name: string };
- searchParams: { [key: string]: string | string[] | undefined };
-}) => {
- const resolvedParams = await params;
- const resolvedSearchParams = await searchParams;
-
- return (
-
-
Loading....
-
- );
+const page = async () => {
+ return ;
};
export default page;
diff --git a/bun.lock b/bun.lock
index 74d0f3c..f6ebf33 100644
--- a/bun.lock
+++ b/bun.lock
@@ -16,6 +16,7 @@
"react": "19.2.3",
"react-dom": "19.2.3",
"shadcn": "^3.6.3",
+ "sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"tw-animate-css": "^1.4.0",
},
@@ -1276,6 +1277,8 @@
"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-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
diff --git a/features/authCallback/index.tsx b/features/authCallback/index.tsx
new file mode 100644
index 0000000..012b0b7
--- /dev/null
+++ b/features/authCallback/index.tsx
@@ -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 (
+
+
+
+
Please wait...
+
+ {textDescription}
+
+
+
+ );
+};
+
+export default AuthCallbackIndex;
diff --git a/package.json b/package.json
index 11eab16..188d0c4 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"react": "19.2.3",
"react-dom": "19.2.3",
"shadcn": "^3.6.3",
+ "sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"tw-animate-css": "^1.4.0"
},
diff --git a/shared/libs/shadcn/ui/sonner.tsx b/shared/libs/shadcn/ui/sonner.tsx
new file mode 100644
index 0000000..9280ee5
--- /dev/null
+++ b/shared/libs/shadcn/ui/sonner.tsx
@@ -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 (
+
+ ),
+ info: (
+
+ ),
+ warning: (
+
+ ),
+ error: (
+
+ ),
+ loading: (
+
+ ),
+ }}
+ 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 }
diff --git a/shared/widgets/signin/actions/submitProviderCallback.ts b/shared/widgets/signin/actions/submitProviderCallback.ts
new file mode 100644
index 0000000..f75d849
--- /dev/null
+++ b/shared/widgets/signin/actions/submitProviderCallback.ts
@@ -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,
+ };
+ }
+};