diff --git a/package.json b/package.json
index e930e0c..abb2973 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "next-shadcn-auth-template",
+ "name": "discourse-oauth",
"version": "0.1.0",
"private": true,
"scripts": {
diff --git a/src/actions/add-client.ts b/src/actions/add-client.ts
index 3a3ad5f..591c0e3 100644
--- a/src/actions/add-client.ts
+++ b/src/actions/add-client.ts
@@ -1,8 +1,8 @@
"use server";
-import { prisma } from "@/lib/prisma";
+import { createClient, getClientByClientId } from "@/lib/dto/client";
import { getCurrentUser } from "@/lib/session";
-import { generateClientKeyId, generateSecretKey } from "@/lib/utils";
+import { generateRandomKey, generateSecretWords } from "@/lib/utils";
export async function AddClientAction(formData: FormData) {
const name = formData.get("name") as string;
@@ -14,24 +14,22 @@ export async function AddClientAction(formData: FormData) {
const user = await getCurrentUser();
// Generate a unique client ID and secret
- let clientId = generateClientKeyId();
- while (await findClientByClientId(clientId)) {
- clientId = generateClientKeyId();
+ let clientId = generateRandomKey();
+ while (await getClientByClientId(clientId)) {
+ clientId = generateRandomKey();
}
- const clientSecret = generateSecretKey();
+ const clientSecret = generateSecretWords();
try {
- const newClient = await prisma.client.create({
- data: {
- name,
- home,
- logo,
- redirectUri,
- description,
- clientId,
- clientSecret,
- userId: user?.id,
- },
+ const newClient = await createClient({
+ name,
+ home,
+ logo,
+ redirectUri,
+ description,
+ clientId,
+ clientSecret,
+ userId: user?.id || "",
});
console.log("New client created:", newClient);
@@ -41,11 +39,3 @@ export async function AddClientAction(formData: FormData) {
return { success: false, error: "Failed to create client" };
}
}
-
-async function findClientByClientId(clientId: string) {
- return await prisma.client.findUnique({
- where: {
- clientId,
- },
- });
-}
diff --git a/src/actions/discourse-callback.ts b/src/actions/discourse-callback.ts
new file mode 100644
index 0000000..3227fe0
--- /dev/null
+++ b/src/actions/discourse-callback.ts
@@ -0,0 +1,45 @@
+"use server";
+
+import WordArray from "crypto-js/lib-typedarrays";
+
+import { verify } from "@/lib/discourse-verify";
+import { getClientByClientId } from "@/lib/dto/client";
+import { createCode } from "@/lib/dto/code";
+
+export async function handleDiscourseCallbackAction(searchParams: string) {
+ const params = new URLSearchParams(searchParams);
+ const sig = params.get("sig") as string;
+ const sso = params.get("sso") as string;
+ const oauth = params.get("oauth") as string;
+
+ const user = await verify(sso, sig);
+ // code redirect ...
+ const oauthParams = new URLSearchParams(atob(oauth));
+ const client = await getClientByClientId(
+ oauthParams.get("client_id") as string,
+ );
+ if (!client) {
+ throw new Error("Client Id invalid (code: -1004).");
+ }
+
+ const redirect_uri = new URL(client.redirectUri);
+ if (oauthParams.has("state")) {
+ redirect_uri.searchParams.append("state", oauthParams.get("state") || "");
+ }
+ const code = WordArray.random(32).toString();
+ redirect_uri.searchParams.append("code", code);
+
+ // storage
+ try {
+ await createCode({
+ code,
+ expiresAt: new Date(Date.now() + 10 * 60 * 1000),
+ clientId: client.id,
+ userId: user.id,
+ });
+ } catch {
+ throw new Error("Create code error (code: -1005).");
+ }
+
+ return redirect_uri.toString();
+}
diff --git a/src/actions/discourse-sso-url.ts b/src/actions/discourse-sso-url.ts
new file mode 100644
index 0000000..dfce5db
--- /dev/null
+++ b/src/actions/discourse-sso-url.ts
@@ -0,0 +1,23 @@
+"use server";
+
+import { cookies } from "next/headers";
+import Hex from "crypto-js/enc-hex";
+import hmacSHA256 from "crypto-js/hmac-sha256";
+import WordArray from "crypto-js/lib-typedarrays";
+
+import { AUTH_NONCE } from "@/lib/constants";
+
+const appHost = process.env.NEXT_PUBLIC_HOST_URL;
+const oauthSecret = process.env.DISCOUSE_SECRET || "";
+
+export async function getDiscourseSSOUrl(searchParams: string) {
+ console.log(`searchParams: ${searchParams}`);
+
+ const nonce = WordArray.random(16).toString();
+ const return_url = `${appHost}/discourse/callback?oauth=${btoa(searchParams)}`;
+ const sso = btoa(`nonce=${nonce}&return_sso_url=${encodeURI(return_url)}`);
+ const sig = hmacSHA256(sso, oauthSecret).toString(Hex);
+ cookies().set(AUTH_NONCE, nonce, { maxAge: 60 * 10 });
+
+ return `https://shuzimumin.com/session/sso_provider?sso=${sso}&sig=${sig}`;
+}
diff --git a/src/app/(dashboard)/dashboard/clients/page.tsx b/src/app/(dashboard)/dashboard/clients/page.tsx
index 2a36009..55161ea 100644
--- a/src/app/(dashboard)/dashboard/clients/page.tsx
+++ b/src/app/(dashboard)/dashboard/clients/page.tsx
@@ -1,4 +1,7 @@
-import { prisma } from "@/lib/prisma";
+import { redirect } from "next/navigation";
+
+import { getClientsByUserId } from "@/lib/dto/client";
+import { getCurrentUser } from "@/lib/session";
import { Button } from "@/components/ui/button";
import {
Table,
@@ -11,25 +14,16 @@ import {
import { AddClientButton } from "@/components/clients/add-client";
// 创建 Prisma 客户端实例
-async function fetchClients() {
- return await prisma.client.findMany({
- select: {
- id: true,
- name: true,
- clientId: true,
- redirectUri: true,
- user: {
- select: {
- name: true,
- email: true,
- },
- },
- },
- });
+async function fetchClients(userId: string) {
+ return await getClientsByUserId(userId);
}
export default async function ClientsPage() {
- const clients = await fetchClients();
+ const user = await getCurrentUser();
+ if (!user) {
+ redirect("sign-in");
+ }
+ const clients = await fetchClients(user.id as string);
return (
@@ -52,6 +46,7 @@ export default async function ClientsPage() {
Name
Client ID
+ Client Secret Key
Redirect URI
Actions
@@ -61,10 +56,8 @@ export default async function ClientsPage() {
{client.name}
{client.clientId}
+ {client.clientSecret}
{client.redirectUri}
-
- {client.user.name} ({client.user.email})
-