-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add oauth config url in the ui #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,44 @@ | ||
| 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", | ||
| description: "Create a new OAuth client application", | ||
| }; | ||
|
|
||
| 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 ( | ||
| <Card> | ||
| <CardHeader> | ||
| <CardTitle>Create OAuth Client</CardTitle> | ||
| <CardDescription> | ||
| Register a new application that can use OAuth to authenticate users | ||
| </CardDescription> | ||
| </CardHeader> | ||
| <CardContent> | ||
| <ClientForm /> | ||
| </CardContent> | ||
| </Card> | ||
| <div className="space-y-6"> | ||
| <OAuthEndpointsCard endpoints={endpoints} /> | ||
|
|
||
| <Card> | ||
| <CardHeader> | ||
| <CardTitle>Create OAuth Client</CardTitle> | ||
| <CardDescription> | ||
| Register a new application that can use OAuth to authenticate users | ||
| </CardDescription> | ||
| </CardHeader> | ||
| <CardContent> | ||
| <ClientForm /> | ||
| </CardContent> | ||
| </Card> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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<string | null>(null); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const handleCopy = (text: string, key: string) => { | ||||||||||||||||||||||||||||||||||||||
| navigator.clipboard.writeText(text); | ||||||||||||||||||||||||||||||||||||||
| setCopied(key); | ||||||||||||||||||||||||||||||||||||||
| setTimeout(() => setCopied(null), 2000); | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+31
|
||||||||||||||||||||||||||||||||||||||
| const handleCopy = (text: string, key: string) => { | |
| navigator.clipboard.writeText(text); | |
| setCopied(key); | |
| setTimeout(() => setCopied(null), 2000); | |
| const handleCopy = async (text: string, key: string) => { | |
| if (!navigator?.clipboard?.writeText) { | |
| window.alert("Copy to clipboard is not supported in this browser."); | |
| return; | |
| } | |
| try { | |
| await navigator.clipboard.writeText(text); | |
| setCopied(key); | |
| setTimeout(() => setCopied(null), 2000); | |
| } catch (error) { | |
| console.error("Failed to copy to clipboard:", error); | |
| window.alert("Failed to copy to clipboard. Please copy the text manually."); | |
| } |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The copy buttons lack accessible labels. Screen reader users won't know what these buttons do. Add an aria-label attribute to the Button components that describes what will be copied, for example: aria-label={Copy ${label}}.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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"); | ||
| }); | ||
|
Comment on lines
+17
to
+41
|
||
| }); | ||
|
|
||
| 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"); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getOpenIDConfiguration function is called without any error handling. If the OAUTH_ISSUER_URL environment variable is not set, this will result in an undefined issuer value that could cause runtime errors. While the page is a Server Component and should fail at build/startup time, consider adding validation to provide a clearer error message for misconfiguration.