- {session.user.name && (
-
{session.user.name}
+ {username && (
+
{username}
)}
- {session.user.email && (
+ {did && (
- {session.user.email}
+ {did}
)}
@@ -91,7 +99,7 @@ export function Navbar() {
signOut()}
+ onClick={handleSignOut}
>
Sign out
diff --git a/src/components/Providers.tsx b/src/components/Providers.tsx
index 90c1891..2e3804b 100644
--- a/src/components/Providers.tsx
+++ b/src/components/Providers.tsx
@@ -1,13 +1,13 @@
"use client";
-import { SessionProvider } from "next-auth/react";
+import { AuthProvider } from "./AuthProvider";
import { Toaster } from "sonner";
export function Providers({ children }: { children: React.ReactNode }) {
return (
-
+
{children}
-
+
);
}
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
index c140e18..89a48cb 100644
--- a/src/lib/auth.ts
+++ b/src/lib/auth.ts
@@ -1,78 +1,57 @@
-import NextAuth from "next-auth";
-import Credentials from "next-auth/providers/credentials";
-import { PrismaAdapter } from "@auth/prisma-adapter";
-import { prisma } from "./prisma";
-import { verifyEncryptIDToken } from "./encryptid";
+import { verifyEncryptIDToken } from '@encryptid/sdk/server';
+import { cookies } from 'next/headers';
+import { prisma } from './prisma';
-export const { handlers, signIn, signOut, auth } = NextAuth({
- adapter: PrismaAdapter(prisma),
- trustHost: true,
- session: {
- strategy: "jwt",
- },
- pages: {
- signIn: "/auth/signin",
- error: "/auth/error",
- },
- providers: [
- // EncryptID passkey login — sole auth provider
- Credentials({
- id: "encryptid",
- name: "EncryptID Passkey",
- credentials: {
- token: { label: "Token", type: "text" },
+const SERVER_URL =
+ process.env.ENCRYPTID_SERVER_URL || 'https://encryptid.jeffemmett.com';
+
+interface AuthSession {
+ user: {
+ id: string;
+ email: string;
+ name: string | null;
+ did: string | null;
+ };
+}
+
+/**
+ * Get the current user session.
+ * Works in both server components and API route handlers.
+ * Reads the encryptid_token cookie, verifies it, and upserts the user.
+ *
+ * Drop-in replacement for the old NextAuth `auth()` function.
+ */
+export async function auth(): Promise
{
+ const cookieStore = await cookies();
+ const token = cookieStore.get('encryptid_token')?.value;
+ if (!token) return null;
+
+ try {
+ const claims = await verifyEncryptIDToken(token, { serverUrl: SERVER_URL });
+ const did: string | undefined = claims.did || claims.sub;
+ if (!did) return null;
+
+ const user = await prisma.user.upsert({
+ where: { did },
+ update: { name: claims.username || undefined },
+ create: {
+ did,
+ email: `${did}@encryptid.local`,
+ name: claims.username || null,
+ credits: 50,
+ emailVerified: new Date(),
},
- async authorize(credentials) {
- if (!credentials?.token) {
- return null;
- }
+ });
- // Verify the EncryptID JWT
- const claims = await verifyEncryptIDToken(credentials.token as string);
- if (!claims) {
- return null;
- }
-
- const did = claims.did || claims.sub;
-
- // Find existing user by DID or create a new one
- let user = await prisma.user.findFirst({
- where: { did },
- });
-
- if (!user) {
- // Create new passkey-only user
- user = await prisma.user.create({
- data: {
- email: `${did}@encryptid.local`, // Placeholder email for DID-only users
- did,
- name: claims.username || null,
- credits: 50, // Starting credits
- emailVerified: new Date(),
- },
- });
- }
-
- return {
- id: user.id,
- email: user.email,
- name: user.name,
- };
+ return {
+ user: {
+ id: user.id,
+ email: user.email,
+ name: user.name,
+ did: user.did,
},
- }),
- ],
- callbacks: {
- async jwt({ token, user }) {
- if (user) {
- token.id = user.id;
- }
- return token;
- },
- async session({ session, token }) {
- if (session.user) {
- session.user.id = token.id as string;
- }
- return session;
- },
- },
-});
+ };
+ } catch {
+ return null;
+ }
+}
diff --git a/src/lib/encryptid.ts b/src/lib/encryptid.ts
deleted file mode 100644
index f6977fc..0000000
--- a/src/lib/encryptid.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * EncryptID configuration for rvote-online
- *
- * Uses @encryptid/sdk for token verification instead of manual HTTP calls.
- */
-
-import { verifyEncryptIDToken as sdkVerify } from '@encryptid/sdk/server';
-
-export const ENCRYPTID_SERVER_URL =
- process.env.ENCRYPTID_SERVER_URL || 'https://encryptid.jeffemmett.com';
-
-/**
- * Verify an EncryptID JWT token.
- * Returns claims if valid, null if invalid.
- */
-export async function verifyEncryptIDToken(token: string): Promise<{
- sub: string;
- username?: string;
- did?: string;
- exp?: number;
-} | null> {
- try {
- const claims = await sdkVerify(token, {
- serverUrl: ENCRYPTID_SERVER_URL,
- });
- return {
- sub: claims.sub,
- username: claims.username,
- did: claims.did,
- exp: claims.exp,
- };
- } catch {
- return null;
- }
-}
diff --git a/src/lib/space-role.ts b/src/lib/space-role.ts
index 1fc5bfc..edd7936 100644
--- a/src/lib/space-role.ts
+++ b/src/lib/space-role.ts
@@ -1,12 +1,11 @@
/**
* Space Role bridge for rVote
*
- * Bridges NextAuth session + EncryptID SDK SpaceRole system.
+ * Bridges EncryptID auth + SDK SpaceRole system.
* Resolves the user's effective SpaceRole in the current space
* by querying the EncryptID membership server.
*/
-import { auth } from './auth';
import { prisma } from './prisma';
import {
SpaceRole,
@@ -14,7 +13,6 @@ import {
type ResolvedRole,
} from '@encryptid/sdk/types';
import { RVOTE_PERMISSIONS } from '@encryptid/sdk/types/modules';
-import type { Session } from 'next-auth';
const ENCRYPTID_SERVER = process.env.ENCRYPTID_SERVER_URL || 'https://encryptid.jeffemmett.com';
@@ -105,7 +103,7 @@ export async function resolveUserSpaceRole(
* Check if the current session user has a specific rVote capability.
*/
export async function checkVoteCapability(
- session: Session | null,
+ session: { user: { id: string } } | null,
spaceSlug: string,
capability: keyof typeof RVOTE_PERMISSIONS.capabilities,
): Promise {
diff --git a/src/types/next-auth.d.ts b/src/types/next-auth.d.ts
deleted file mode 100644
index 564bc5b..0000000
--- a/src/types/next-auth.d.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { DefaultSession } from "next-auth";
-
-declare module "next-auth" {
- interface Session {
- user: {
- id: string;
- did?: string | null;
- } & DefaultSession["user"];
- }
-}