diff --git a/app/admin/dashboard/clients/new/page.tsx b/app/admin/dashboard/clients/new/page.tsx
index 6de6b0f..604c6d0 100644
--- a/app/admin/dashboard/clients/new/page.tsx
+++ b/app/admin/dashboard/clients/new/page.tsx
@@ -1,5 +1,13 @@
-import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
+import {
+ Card,
+ CardHeader,
+ CardTitle,
+ CardDescription,
+ CardContent,
+} from "@/components/ui/card";
import { ClientForm } from "@/components/admin/client-form";
+import { OAuthEndpointsCard } from "@/components/admin/oauth-endpoints-card";
+import { getOpenIDConfiguration } from "@/lib/oauth/discovery";
export const metadata = {
title: "New OAuth Client - Admin",
@@ -7,17 +15,30 @@ export const metadata = {
};
export default function NewClientPage() {
+ const config = getOpenIDConfiguration();
+
+ const endpoints = {
+ issuer: config.issuer,
+ authorizationEndpoint: config.authorization_endpoint,
+ tokenEndpoint: config.token_endpoint,
+ discoveryUrl: `${config.issuer}/.well-known/openid-configuration`,
+ };
+
return (
-
-
- Create OAuth Client
-
- Register a new application that can use OAuth to authenticate users
-
-
-
-
-
-
+
+
+
+
+
+ Create OAuth Client
+
+ Register a new application that can use OAuth to authenticate users
+
+
+
+
+
+
+
);
}
diff --git a/components/admin/oauth-endpoints-card.tsx b/components/admin/oauth-endpoints-card.tsx
new file mode 100644
index 0000000..f546546
--- /dev/null
+++ b/components/admin/oauth-endpoints-card.tsx
@@ -0,0 +1,87 @@
+"use client";
+
+import { useState } from "react";
+import { Copy, Check } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import {
+ Card,
+ CardHeader,
+ CardTitle,
+ CardDescription,
+ CardContent,
+} from "@/components/ui/card";
+
+interface OAuthEndpointsCardProps {
+ endpoints: {
+ issuer: string;
+ authorizationEndpoint: string;
+ tokenEndpoint: string;
+ discoveryUrl: string;
+ };
+}
+
+export function OAuthEndpointsCard({ endpoints }: OAuthEndpointsCardProps) {
+ const [copied, setCopied] = useState(null);
+
+ const handleCopy = (text: string, key: string) => {
+ navigator.clipboard.writeText(text);
+ setCopied(key);
+ setTimeout(() => setCopied(null), 2000);
+ };
+
+ const urlFields = [
+ { key: "issuer", label: "Issuer URL", value: endpoints.issuer },
+ {
+ key: "authorization",
+ label: "Authorization Endpoint",
+ value: endpoints.authorizationEndpoint,
+ },
+ { key: "token", label: "Token Endpoint", value: endpoints.tokenEndpoint },
+ { key: "discovery", label: "Discovery URL", value: endpoints.discoveryUrl },
+ ];
+
+ return (
+
+
+ OAuth Server Endpoints
+
+ Use these URLs to configure your OAuth client application
+
+
+
+ {urlFields.map(({ key, label, value }) => (
+
+
+
+
+
+
+
+ ))}
+
+ For automatic configuration, most OAuth libraries can use the
+ Discovery URL.
+
+
+
+ );
+}
diff --git a/e2e/admin/clients.spec.ts b/e2e/admin/clients.spec.ts
index 53c61bd..b0b6956 100644
--- a/e2e/admin/clients.spec.ts
+++ b/e2e/admin/clients.spec.ts
@@ -9,11 +9,42 @@ async function adminLogin(page: import("@playwright/test").Page) {
await expect(page).toHaveURL("/admin/dashboard");
}
-test.describe("Client Form Scope Selection", () => {
+test.describe("OAuth Endpoints Display", () => {
test.beforeEach(async ({ page }) => {
await adminLogin(page);
});
+ test("should display OAuth endpoints on new client page", async ({ page }) => {
+ await page.goto("/admin/dashboard/clients/new");
+
+ // Verify OAuth endpoints card is visible
+ await expect(page.getByText("OAuth Server Endpoints")).toBeVisible();
+
+ // Verify key endpoints are displayed
+ await expect(page.getByTestId("oauth-endpoint-issuer")).toBeVisible();
+ await expect(page.getByTestId("oauth-endpoint-authorization")).toBeVisible();
+ await expect(page.getByTestId("oauth-endpoint-token")).toBeVisible();
+ await expect(page.getByTestId("oauth-endpoint-discovery")).toBeVisible();
+
+ // Verify endpoints have values
+ const issuerValue = await page.getByTestId("oauth-endpoint-issuer").inputValue();
+ expect(issuerValue).toBeTruthy();
+
+ const authValue = await page.getByTestId("oauth-endpoint-authorization").inputValue();
+ expect(authValue).toContain("/api/oauth/authorize");
+
+ const tokenValue = await page.getByTestId("oauth-endpoint-token").inputValue();
+ expect(tokenValue).toContain("/api/oauth/token");
+
+ const discoveryValue = await page.getByTestId("oauth-endpoint-discovery").inputValue();
+ expect(discoveryValue).toContain("/.well-known/openid-configuration");
+ });
+});
+
+test.describe("Client Form Scope Selection", () => {
+ test.beforeEach(async ({ page }) => {
+ await adminLogin(page);
+ });
test("should create client with all scopes selected", async ({ page }) => {
await page.goto("/admin/dashboard/clients/new");
diff --git a/e2e/oauth/consent.spec.ts b/e2e/oauth/consent.spec.ts
index c53e24f..9aa69f2 100644
--- a/e2e/oauth/consent.spec.ts
+++ b/e2e/oauth/consent.spec.ts
@@ -48,8 +48,8 @@ test.describe("OAuth Consent Flow", () => {
await page.getByRole("button", { name: "Create Application" }).click();
await expect(page.getByText("Client Created Successfully")).toBeVisible();
- clientId = await page.locator('input[readonly]').first().inputValue();
- clientSecret = await page.locator('input[readonly]').nth(1).inputValue();
+ clientId = await page.getByTestId("client-id-display").inputValue();
+ clientSecret = await page.getByTestId("client-secret-display").inputValue();
await page.close();
await context.close();