Skip to content

Commit

Permalink
Add OpenAuth server template (#175)
Browse files Browse the repository at this point in the history
- Template deploys an OpenAuth server that other client Workers can
  connect to via service binding

Other changes:
- Set "moduleResolution" to "bundler" in all other templates
  - This prevents confusion when users try to add dependencies to our
    (currently) zero-dependency templates
  • Loading branch information
maxwellpeterson authored Jan 2, 2025
1 parent 05196cb commit a23357f
Show file tree
Hide file tree
Showing 16 changed files with 350 additions and 0 deletions.
1 change: 1 addition & 0 deletions d1-template/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"target": "esnext",
"lib": ["esnext"],
"module": "esnext",
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types", "./worker-configuration.d.ts"],
"noEmit": true,
"isolatedModules": true,
Expand Down
1 change: 1 addition & 0 deletions image-classification-template/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"target": "esnext",
"lib": ["esnext"],
"module": "esnext",
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types", "./worker-configuration.d.ts"],
"noEmit": true,
"isolatedModules": true,
Expand Down
1 change: 1 addition & 0 deletions llm-template/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"target": "esnext",
"lib": ["esnext"],
"module": "esnext",
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types", "./worker-configuration.d.ts"],
"noEmit": true,
"isolatedModules": true,
Expand Down
15 changes: 15 additions & 0 deletions openauth-template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# OpenAuth Server

Deploy an [OpenAuth](https://openauth.js.org/) server on Cloudflare Workers.

## Develop Locally

Use this template with [C3](https://developers.cloudflare.com/pages/get-started/c3/) (the `create-cloudflare` CLI):

```
npm create cloudflare@latest -- --template=cloudflare/templates/openauth-template
```

## Preview Deployment

A live public deployment of this template is available at [https://openauth-template.templates.workers.dev](https://openauth-template.templates.workers.dev)
6 changes: 6 additions & 0 deletions openauth-template/migrations/0001_create_user_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- Migration number: 0001 2024-12-27T22:04:18.794Z
CREATE TABLE IF NOT EXISTS user (
id TEXT PRIMARY KEY NOT NULL DEFAULT (lower(hex(randomblob(16)))),
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
30 changes: 30 additions & 0 deletions openauth-template/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "openauth-template",
"description": "Deploy an OpenAuth server on Cloudflare Workers.",
"cloudflare": {
"label": "OpenAuth Server",
"products": [
"Workers"
],
"icon_urls": [
"https://imagedelivery.net/wSMYJvS3Xw-n339CbDyDIA/5ca0ca32-e897-4699-d4c1-6b680512f000/public"
],
"preview_image_url": "https://imagedelivery.net/wSMYJvS3Xw-n339CbDyDIA/b2ff10c6-8f7c-419f-8757-e2ccf1c84500/public"
},
"dependencies": {
"@openauthjs/openauth": "0.2.5",
"valibot": "^1.0.0-beta.9"
},
"devDependencies": {
"@cloudflare/workers-types": "4.20241216.0",
"typescript": "5.6.3",
"wrangler": "3.97.0"
},
"scripts": {
"check": "tsc && wrangler --experimental-json-config deploy --dry-run",
"deploy": "wrangler --experimental-json-config deploy",
"dev": "wrangler --experimental-json-config dev",
"predeploy": "wrangler --experimental-json-config d1 migrations apply openauth-template-auth-db --remote",
"types": "wrangler --experimental-json-config types"
}
}
95 changes: 95 additions & 0 deletions openauth-template/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { authorizer, createSubjects } from "@openauthjs/openauth";
import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare";
import { PasswordAdapter } from "@openauthjs/openauth/adapter/password";
import { PasswordUI } from "@openauthjs/openauth/ui/password";
import { object, string } from "valibot";

// This value should be shared between the OpenAuth server Worker and other
// client Workers that you connect to it, so the types and schema validation are
// consistent.
const subjects = createSubjects({
user: object({
id: string(),
}),
});

export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// This top section is just for demo purposes. In a real setup another
// application would redirect the user to this Worker to be authenticated,
// and after signing in or registering the user would be redirected back to
// the application they came from. In our demo setup there is no other
// application, so this Worker needs to do the initial redirect and handle
// the callback redirect on completion.
const url = new URL(request.url);
if (url.pathname === "/") {
url.searchParams.set("redirect_uri", url.origin + "/callback");
url.searchParams.set("client_id", "your-client-id");
url.searchParams.set("response_type", "code");
url.pathname = "/authorize";
return Response.redirect(url.toString());
} else if (url.pathname === "/callback") {
return Response.json({
message: "OAuth flow complete!",
params: Object.fromEntries(url.searchParams.entries()),
});
}

// The real OpenAuth server code starts here:
return authorizer({
storage: CloudflareStorage({
namespace: env.AUTH_STORAGE,
}),
subjects,
providers: {
password: PasswordAdapter(
PasswordUI({
// eslint-disable-next-line @typescript-eslint/require-await
sendCode: async (email, code) => {
// This is where you would email the verification code to the
// user, e.g. using Resend:
// https://resend.com/docs/send-with-cloudflare-workers
console.log(`Sending code ${code} to ${email}`);
},
copy: {
input_code: "Code (check Worker logs)",
},
}),
),
},
theme: {
title: "myAuth",
primary: "#0051c3",
favicon: "https://workers.cloudflare.com//favicon.ico",
logo: {
dark: "https://imagedelivery.net/wSMYJvS3Xw-n339CbDyDIA/db1e5c92-d3a6-4ea9-3e72-155844211f00/public",
light:
"https://imagedelivery.net/wSMYJvS3Xw-n339CbDyDIA/fa5a3023-7da9-466b-98a7-4ce01ee6c700/public",
},
},
success: async (ctx, value) => {
return ctx.subject("user", {
id: await getOrCreateUser(env, value.email),
});
},
}).fetch(request, env, ctx);
},
} satisfies ExportedHandler<Env>;

async function getOrCreateUser(env: Env, email: string): Promise<string> {
const result = await env.AUTH_DB.prepare(
`
INSERT INTO user (email)
VALUES (?)
ON CONFLICT (email) DO UPDATE SET email = email
RETURNING id;
`,
)
.bind(email)
.first<{ id: string }>();
if (!result) {
throw new Error(`Unable to process user: ${email}`);
}
console.log(`Found or created user ${result.id} with email ${email}`);
return result.id;
}
15 changes: 15 additions & 0 deletions openauth-template/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "esnext",
"lib": ["esnext"],
"module": "esnext",
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types", "./worker-configuration.d.ts"],
"noEmit": true,
"isolatedModules": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"strict": true
},
"include": ["src"]
}
6 changes: 6 additions & 0 deletions openauth-template/worker-configuration.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Generated by Wrangler by running `wrangler --experimental-json-config types`

interface Env {
AUTH_STORAGE: KVNamespace;
AUTH_DB: D1Database;
}
23 changes: 23 additions & 0 deletions openauth-template/wrangler.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compatibility_date": "2024-11-01",
"main": "src/index.ts",
"name": "openauth-template",
"upload_source_maps": true,
"kv_namespaces": [
{
"binding": "AUTH_STORAGE",
"id": "afec91ff3f7e4b0b9b9323fc6cf5ff85"
}
],
"d1_databases": [
{
"binding": "AUTH_DB",
"database_name": "openauth-template-auth-db",
"database_id": "d4dfb2e9-2fd3-4d04-9c83-57b4336a5958"
}
],
"observability": {
"enabled": true
},
"compatibility_flags": ["nodejs_compat"]
}
Loading

0 comments on commit a23357f

Please sign in to comment.