mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
Render OAuth form with JSX
This commit is contained in:
parent
d825f9d7cb
commit
79a523d528
6 changed files with 62 additions and 23 deletions
|
|
@ -6,7 +6,8 @@
|
||||||
"test": "deno test"
|
"test": "deno test"
|
||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"@/": "./src/"
|
"@/": "./src/",
|
||||||
|
"react/jsx-runtime": "https://esm.sh/react@18.2.0/jsx-runtime"
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"include": ["src/"],
|
"include": ["src/"],
|
||||||
|
|
@ -23,5 +24,9 @@
|
||||||
"semiColons": true,
|
"semiColons": true,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"proseWrap": "preserve"
|
"proseWrap": "preserve"
|
||||||
|
},
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "react"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { z } from '@/deps.ts';
|
import { z } from '@/deps.ts';
|
||||||
import { AppController } from '@/app.ts';
|
import { AppController } from '@/app.ts';
|
||||||
|
import OAuthPage from '@/pages/oauth.tsx';
|
||||||
|
import { renderPage } from '@/pages/mod.tsx';
|
||||||
import { parseBody } from '@/utils.ts';
|
import { parseBody } from '@/utils.ts';
|
||||||
|
|
||||||
const passwordGrantSchema = z.object({
|
const passwordGrantSchema = z.object({
|
||||||
|
|
@ -48,31 +50,10 @@ const oauthController: AppController = (c) => {
|
||||||
|
|
||||||
const redirectUri = decodeURIComponent(encodedUri);
|
const redirectUri = decodeURIComponent(encodedUri);
|
||||||
|
|
||||||
// Poor man's XSS check.
|
|
||||||
// TODO: Render form with JSX.
|
|
||||||
try {
|
|
||||||
new URL(redirectUri);
|
|
||||||
} catch (_e) {
|
|
||||||
return c.text('Invalid `redirect_uri`.', 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
c.res.headers.set('content-security-policy', 'default-src \'self\'');
|
c.res.headers.set('content-security-policy', 'default-src \'self\'');
|
||||||
|
|
||||||
// TODO: Login with `window.nostr` (NIP-07).
|
// TODO: Login with `window.nostr` (NIP-07).
|
||||||
return c.html(`<!DOCTYPE html>
|
return c.html(renderPage(OAuthPage({ redirectUri })));
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Log in with Ditto</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<form action="/oauth/authorize" method="post">
|
|
||||||
<input type="text" placeholder="npub1... or nsec1..." name="nostr_id" autocomplete="off">
|
|
||||||
<input type="hidden" name="redirect_uri" id="redirect_uri" value="${redirectUri}" autocomplete="off">
|
|
||||||
<button type="submit">Authorize</button>
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const oauthAuthorizeController: AppController = async (c) => {
|
const oauthAuthorizeController: AppController = async (c) => {
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,7 @@ import 'npm:linkify-plugin-hashtag@^4.1.0';
|
||||||
export { default as mime } from 'npm:mime@^3.0.0';
|
export { default as mime } from 'npm:mime@^3.0.0';
|
||||||
export { unfurl } from 'npm:unfurl.js@^6.3.1';
|
export { unfurl } from 'npm:unfurl.js@^6.3.1';
|
||||||
export { default as TTLCache } from 'npm:@isaacs/ttlcache@^1.4.0';
|
export { default as TTLCache } from 'npm:@isaacs/ttlcache@^1.4.0';
|
||||||
|
// @deno-types="npm:@types/react@18.2.6"
|
||||||
|
export { default as React } from 'npm:react@^18.2.0';
|
||||||
|
// @deno-types="npm:@types/react-dom@18.2.4/server"
|
||||||
|
export { renderToString } from 'npm:react-dom@^18.2.0/server';
|
||||||
|
|
|
||||||
22
src/pages/layout.tsx
Normal file
22
src/pages/layout.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { React } from '@/deps.ts';
|
||||||
|
|
||||||
|
interface ILayout {
|
||||||
|
title: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Layout: React.FC<ILayout> = ({ children, title }) => {
|
||||||
|
return (
|
||||||
|
<html lang='en'>
|
||||||
|
<head>
|
||||||
|
<title>{title}</title>
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1.0, user-scalable=no' />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id='root'>{children}</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Layout;
|
||||||
7
src/pages/mod.tsx
Normal file
7
src/pages/mod.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { React, renderToString } from '@/deps.ts';
|
||||||
|
|
||||||
|
function renderPage(page: React.ReactNode): string {
|
||||||
|
return '<!DOCTYPE html>' + renderToString(<>{page}</>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { renderPage };
|
||||||
20
src/pages/oauth.tsx
Normal file
20
src/pages/oauth.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { React } from '@/deps.ts';
|
||||||
|
import Layout from './layout.tsx';
|
||||||
|
|
||||||
|
interface IOAuthPage {
|
||||||
|
redirectUri: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OAuthPage: React.FC<IOAuthPage> = ({ redirectUri }) => {
|
||||||
|
return (
|
||||||
|
<Layout title='Log in with Ditto'>
|
||||||
|
<form action='/oauth/authorize' method='post'>
|
||||||
|
<input type='text' placeholder='npub1... or nsec1...' name='nostr_id' autoComplete='off' />
|
||||||
|
<input type='hidden' name='redirect_uri' id='redirect_uri' value={redirectUri} autoComplete='off' />
|
||||||
|
<button type='submit'>Authorize</button>
|
||||||
|
</form>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OAuthPage;
|
||||||
Loading…
Add table
Reference in a new issue