GettingStarted
Get fully setup with the Athena-Auth-UI instance
auth.ts
This is heavily abstracted and then ATHENA_AUTH_UPSTREAM_URL should be
the backend athena auth url that the Proxy API forwards to.
ATHENA_AUTH_UPSTREAM_URL=https://auth.athena-cluster.com
import { createAthenaServerAuthClient } from "@xylex-group/athena-auth-ui/athena/client"
export const auth = createAthenaServerAuthClient({
upstreamUrl: process.env.ATHENA_AUTH_UPSTREAM_URL
})
```ts
Importing the styles into tailwind
```css title="styles/app.css"
@import "tailwindcss";
@import "@heroui/styles";
@import "@xylex-group/athena-auth-ui/styles";
html,
body {
@apply bg-background text-foreground;
}Required ENV vars
# Copy values from .env.example and fill with real local secrets.
ATHENA_AUTH_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
DATABASE_URL="postgresql://postgres:xxxxxxxxxxxxxxxxxxx@x.x.x.net:31091/xxxxxxxx"
DATABASE_KEY="xxxxxxxxxxxxxxxxxxx"
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
APPLE_CLIENT_ID="com.xxxxxxxxxxxxx"
APPLE_CLIENT_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
MICROSOFT_CLIENT_ID="xxxxxxxxxxxxxxxxxxxxxxxxxx"
MICROSOFT_SECRET="xxxxxxxxxxxxxxxxxxx"
GOOGLE_CLIENT_ID="xxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Public URL used by @xylex-group/athena auth client in the example app.
NEXT_PUBLIC_ATHENA_AUTH_BASE_URL=/api/auth
# Backend Athena Auth URL that the example API proxy forwards to.
ATHENA_AUTH_UPSTREAM_URL=https://auth.athena-cluster.comAthenaAuthProvider
Create the provider
"use client";
import { Toast } from "@heroui/react";
import { QueryClientProvider } from "@tanstack/react-query";
import {
AuthProvider,
type AuthProviderProps,
} from "@xylex-group/athena-auth-ui";
import { createAthenaAuthClient } from "@xylex-group/athena-auth-ui/athena/client";
import { getAthenaQueryClient } from "@xylex-group/athena-auth-ui/athena/query-client";
import { createAthenaAuthPlugins } from "@xylex-group/athena-auth-ui/plugins";
import { useRouter } from "next/navigation";
import { ThemeProvider, useTheme } from "next-themes";
import type { ReactNode } from "react";
type NavigateArgs = Parameters<NonNullable<AuthProviderProps["navigate"]>>[0];
const authClient = createAthenaAuthClient({
baseUrl: process.env.NEXT_PUBLIC_ATHENA_AUTH_BASE_URL || "/api/auth",
});
export function Providers({ children }: { children: ReactNode }) {
const router = useRouter();
const queryClient = getAthenaQueryClient();
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider
attribute="data-theme"
defaultTheme="system"
disableTransitionOnChange
enableSystem
>
<AuthProvider
authClient={authClient}
redirectTo="/settings/account"
emailAndPassword={{
confirmPassword: true,
enabled: true,
forgotPassword: true,
requireEmailVerification: true,
}}
socialProviders={["google", "github"]}
navigate={({ to, replace }: NavigateArgs) =>
replace ? router.replace(to) : router.push(to)
}
plugins={createAthenaAuthPlugins({ theme: { useTheme } })}
>
{children}
<Toast.Provider />
</AuthProvider>
</ThemeProvider>
</QueryClientProvider>
);
}Wrap the layout with the provider
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import type { ReactNode } from "react";
import "@/styles/app.css";
import { Header } from "@/components/header";
import { Providers } from "@/components/providers";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Athena Auth UI",
description: "An initiative of the Athena project",
};
export default function RootLayout({
children,
}: Readonly<{
children: ReactNode;
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased min-h-svh flex flex-col`}
>
<Providers>
<Header />
{children}
</Providers>
</body>
</html>
);
}Start out with this one
import { UserButton } from "@xylex-group/athena-auth-ui";
export default function Home() {
return (
<div className="flex justify-end">
<UserButton />
</div>
);
}Setup the Proxy API to callback to the Athena Auth server
import { createAthenaAuthProxyHandlers } from "@xylex-group/athena-auth-ui/athena/proxy";
export const { DELETE, GET, PATCH, POST, PUT } = createAthenaAuthProxyHandlers({
upstreamUrl: process.env.ATHENA_AUTH_UPSTREAM_URL,
});Setup the auth pages, (login, password reset etc)
import { AuthPage as AuthRoutePage } from "@xylex-group/athena-auth-ui/pages"
export default async function AuthPage({ params }:
{
params: Promise<{ path: string }>
}
) {
const { path } = await params
return <AuthRoutePage path={path} />
}Setup the settings pages
import { pickAthenaAuthRequestHeaders } from "@xylex-group/athena-auth-ui/athena/request-headers";
import { SettingsPage as SettingsRoutePage } from "@xylex-group/athena-auth-ui/pages";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { auth } from "@/lib/auth";
export default async function SettingsPage({
params,
}: {
params: Promise<{
path: string;
}>;
}) {
const { path } = await params;
const requestHeaders = await headers();
const sessionResponse = await auth.getSession({
fetchOptions: {
headers: pickAthenaAuthRequestHeaders(requestHeaders),
},
});
if (!sessionResponse.data?.user) {
redirect(
`/auth/sign-in?redirectTo=${encodeURIComponent(`/settings/${path}`)}`,
);
}
return <SettingsRoutePage path={path} />;
}