@farmsdotmarket/openauth 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @farmsdotmarket/openauth might be problematic. Click here for more details.

Files changed (266) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/commit.cjs +4 -0
  3. package/.changeset/config.json +11 -0
  4. package/.changeset/farms-openauth-release-0-4-3-farms-2.md +10 -0
  5. package/.changeset/popular-geese-reply.md +5 -0
  6. package/.changeset/stupid-boats-play.md +5 -0
  7. package/.changeset/ten-pans-invent.md +5 -0
  8. package/.github/CODE_OF_CONDUCT +15 -0
  9. package/.github/workflows/docs.yml +39 -0
  10. package/.github/workflows/format.yml +26 -0
  11. package/.github/workflows/release.yml +28 -0
  12. package/.github/workflows/test.yml +20 -0
  13. package/.prettierrc +3 -0
  14. package/CNAME +1 -0
  15. package/FARMS_RELEASE.md +82 -0
  16. package/LICENSE +22 -0
  17. package/PATCHES.md +32 -0
  18. package/README.md +311 -0
  19. package/bun.lockb +0 -0
  20. package/bunfig.toml +2 -0
  21. package/examples/README.md +28 -0
  22. package/examples/client/astro/.vscode/extensions.json +4 -0
  23. package/examples/client/astro/.vscode/launch.json +11 -0
  24. package/examples/client/astro/README.md +7 -0
  25. package/examples/client/astro/astro.config.mjs +10 -0
  26. package/examples/client/astro/package.json +15 -0
  27. package/examples/client/astro/public/favicon.svg +9 -0
  28. package/examples/client/astro/src/assets/astro.svg +1 -0
  29. package/examples/client/astro/src/assets/background.svg +1 -0
  30. package/examples/client/astro/src/auth.ts +23 -0
  31. package/examples/client/astro/src/components/Welcome.astro +209 -0
  32. package/examples/client/astro/src/env.d.ts +10 -0
  33. package/examples/client/astro/src/layouts/Layout.astro +22 -0
  34. package/examples/client/astro/src/middleware.ts +31 -0
  35. package/examples/client/astro/src/pages/callback.ts +19 -0
  36. package/examples/client/astro/src/pages/index.astro +11 -0
  37. package/examples/client/astro/tsconfig.json +5 -0
  38. package/examples/client/cloudflare-api/api.ts +80 -0
  39. package/examples/client/cloudflare-api/package.json +5 -0
  40. package/examples/client/jwt-api/CHANGELOG.md +8 -0
  41. package/examples/client/jwt-api/README.md +13 -0
  42. package/examples/client/jwt-api/index.ts +45 -0
  43. package/examples/client/jwt-api/package.json +15 -0
  44. package/examples/client/lambda-api/api.ts +69 -0
  45. package/examples/client/lambda-api/package.json +5 -0
  46. package/examples/client/nextjs/CHANGELOG.md +46 -0
  47. package/examples/client/nextjs/README.md +26 -0
  48. package/examples/client/nextjs/app/actions.ts +61 -0
  49. package/examples/client/nextjs/app/api/callback/route.ts +11 -0
  50. package/examples/client/nextjs/app/auth.ts +29 -0
  51. package/examples/client/nextjs/app/favicon.ico +0 -0
  52. package/examples/client/nextjs/app/globals.css +42 -0
  53. package/examples/client/nextjs/app/layout.tsx +32 -0
  54. package/examples/client/nextjs/app/page.module.css +169 -0
  55. package/examples/client/nextjs/app/page.tsx +97 -0
  56. package/examples/client/nextjs/next.config.ts +7 -0
  57. package/examples/client/nextjs/package.json +23 -0
  58. package/examples/client/nextjs/public/file.svg +1 -0
  59. package/examples/client/nextjs/public/globe.svg +1 -0
  60. package/examples/client/nextjs/public/next.svg +1 -0
  61. package/examples/client/nextjs/public/vercel.svg +1 -0
  62. package/examples/client/nextjs/public/window.svg +1 -0
  63. package/examples/client/nextjs/tsconfig.json +27 -0
  64. package/examples/client/react/README.md +29 -0
  65. package/examples/client/react/index.html +13 -0
  66. package/examples/client/react/package.json +30 -0
  67. package/examples/client/react/public/vite.svg +1 -0
  68. package/examples/client/react/src/App.tsx +39 -0
  69. package/examples/client/react/src/AuthContext.tsx +153 -0
  70. package/examples/client/react/src/assets/react.svg +1 -0
  71. package/examples/client/react/src/main.tsx +12 -0
  72. package/examples/client/react/src/vite-env.d.ts +1 -0
  73. package/examples/client/react/tsconfig.app.json +26 -0
  74. package/examples/client/react/tsconfig.json +11 -0
  75. package/examples/client/react/tsconfig.node.json +24 -0
  76. package/examples/client/react/vite.config.ts +7 -0
  77. package/examples/client/sveltekit/package.json +24 -0
  78. package/examples/client/sveltekit/src/app.d.ts +15 -0
  79. package/examples/client/sveltekit/src/app.html +12 -0
  80. package/examples/client/sveltekit/src/hooks.server.ts +29 -0
  81. package/examples/client/sveltekit/src/lib/auth.server.ts +29 -0
  82. package/examples/client/sveltekit/src/routes/+page.server.ts +5 -0
  83. package/examples/client/sveltekit/src/routes/+page.svelte +5 -0
  84. package/examples/client/sveltekit/src/routes/callback/+server.ts +17 -0
  85. package/examples/client/sveltekit/static/favicon.png +0 -0
  86. package/examples/client/sveltekit/svelte.config.js +18 -0
  87. package/examples/client/sveltekit/tsconfig.json +19 -0
  88. package/examples/client/sveltekit/vite.config.ts +6 -0
  89. package/examples/issuer/bun/issuer.ts +43 -0
  90. package/examples/issuer/bun/package.json +7 -0
  91. package/examples/issuer/cloudflare/issuer.ts +47 -0
  92. package/examples/issuer/cloudflare/package.json +8 -0
  93. package/examples/issuer/cloudflare/sst-env.d.ts +17 -0
  94. package/examples/issuer/cloudflare/sst.config.ts +23 -0
  95. package/examples/issuer/custom-frontend/auth/issuer.ts +43 -0
  96. package/examples/issuer/custom-frontend/auth/package.json +7 -0
  97. package/examples/issuer/custom-frontend/frontend/frontend.tsx +57 -0
  98. package/examples/issuer/custom-frontend/frontend/package.json +6 -0
  99. package/examples/issuer/custom-frontend/package.json +5 -0
  100. package/examples/issuer/lambda/issuer.ts +35 -0
  101. package/examples/issuer/lambda/package.json +8 -0
  102. package/examples/issuer/lambda/sst-env.d.ts +9 -0
  103. package/examples/issuer/lambda/sst.config.ts +15 -0
  104. package/examples/issuer/node/authorizer.ts +38 -0
  105. package/examples/issuer/node/package.json +5 -0
  106. package/examples/quickstart/sst/README.md +36 -0
  107. package/examples/quickstart/sst/app/actions.ts +62 -0
  108. package/examples/quickstart/sst/app/api/callback/route.ts +15 -0
  109. package/examples/quickstart/sst/app/auth.ts +29 -0
  110. package/examples/quickstart/sst/app/favicon.ico +0 -0
  111. package/examples/quickstart/sst/app/globals.css +42 -0
  112. package/examples/quickstart/sst/app/layout.tsx +32 -0
  113. package/examples/quickstart/sst/app/page.module.css +200 -0
  114. package/examples/quickstart/sst/app/page.tsx +53 -0
  115. package/examples/quickstart/sst/auth/index.ts +37 -0
  116. package/examples/quickstart/sst/auth/subjects.ts +8 -0
  117. package/examples/quickstart/sst/next.config.ts +7 -0
  118. package/examples/quickstart/sst/package-lock.json +1165 -0
  119. package/examples/quickstart/sst/package.json +26 -0
  120. package/examples/quickstart/sst/public/file.svg +1 -0
  121. package/examples/quickstart/sst/public/globe.svg +1 -0
  122. package/examples/quickstart/sst/public/next.svg +1 -0
  123. package/examples/quickstart/sst/public/vercel.svg +1 -0
  124. package/examples/quickstart/sst/public/window.svg +1 -0
  125. package/examples/quickstart/sst/sst-env.d.ts +18 -0
  126. package/examples/quickstart/sst/sst.config.ts +21 -0
  127. package/examples/quickstart/sst/tsconfig.json +27 -0
  128. package/examples/quickstart/standalone/README.md +36 -0
  129. package/examples/quickstart/standalone/app/actions.ts +62 -0
  130. package/examples/quickstart/standalone/app/api/callback/route.ts +15 -0
  131. package/examples/quickstart/standalone/app/auth.ts +28 -0
  132. package/examples/quickstart/standalone/app/favicon.ico +0 -0
  133. package/examples/quickstart/standalone/app/globals.css +42 -0
  134. package/examples/quickstart/standalone/app/layout.tsx +32 -0
  135. package/examples/quickstart/standalone/app/page.module.css +200 -0
  136. package/examples/quickstart/standalone/app/page.tsx +53 -0
  137. package/examples/quickstart/standalone/auth/index.ts +32 -0
  138. package/examples/quickstart/standalone/auth/subjects.ts +8 -0
  139. package/examples/quickstart/standalone/bun.lockb +0 -0
  140. package/examples/quickstart/standalone/next.config.ts +7 -0
  141. package/examples/quickstart/standalone/package.json +25 -0
  142. package/examples/quickstart/standalone/public/file.svg +1 -0
  143. package/examples/quickstart/standalone/public/globe.svg +1 -0
  144. package/examples/quickstart/standalone/public/next.svg +1 -0
  145. package/examples/quickstart/standalone/public/vercel.svg +1 -0
  146. package/examples/quickstart/standalone/public/window.svg +1 -0
  147. package/examples/quickstart/standalone/tsconfig.json +27 -0
  148. package/examples/subjects.ts +8 -0
  149. package/examples/tsconfig.json +10 -0
  150. package/package.json +23 -0
  151. package/packages/openauth/CHANGELOG.md +310 -0
  152. package/packages/openauth/bunfig.toml +2 -0
  153. package/packages/openauth/package.json +51 -0
  154. package/packages/openauth/script/build.ts +25 -0
  155. package/packages/openauth/src/client-native.ts +204 -0
  156. package/packages/openauth/src/client.ts +776 -0
  157. package/packages/openauth/src/css.d.ts +4 -0
  158. package/packages/openauth/src/error.ts +120 -0
  159. package/packages/openauth/src/index.ts +26 -0
  160. package/packages/openauth/src/issuer.ts +1156 -0
  161. package/packages/openauth/src/jwt.ts +17 -0
  162. package/packages/openauth/src/keys.ts +139 -0
  163. package/packages/openauth/src/pkce.ts +40 -0
  164. package/packages/openauth/src/provider/apple.ts +127 -0
  165. package/packages/openauth/src/provider/arctic.ts +66 -0
  166. package/packages/openauth/src/provider/code.ts +227 -0
  167. package/packages/openauth/src/provider/cognito.ts +74 -0
  168. package/packages/openauth/src/provider/discord.ts +45 -0
  169. package/packages/openauth/src/provider/facebook.ts +84 -0
  170. package/packages/openauth/src/provider/github.ts +45 -0
  171. package/packages/openauth/src/provider/google.ts +85 -0
  172. package/packages/openauth/src/provider/index.ts +3 -0
  173. package/packages/openauth/src/provider/jumpcloud.ts +45 -0
  174. package/packages/openauth/src/provider/keycloak.ts +75 -0
  175. package/packages/openauth/src/provider/linkedin.ts +12 -0
  176. package/packages/openauth/src/provider/microsoft.ts +100 -0
  177. package/packages/openauth/src/provider/oauth2.ts +297 -0
  178. package/packages/openauth/src/provider/oidc.ts +179 -0
  179. package/packages/openauth/src/provider/password.ts +672 -0
  180. package/packages/openauth/src/provider/provider.ts +33 -0
  181. package/packages/openauth/src/provider/slack.ts +67 -0
  182. package/packages/openauth/src/provider/spotify.ts +45 -0
  183. package/packages/openauth/src/provider/twitch.ts +45 -0
  184. package/packages/openauth/src/provider/x.ts +46 -0
  185. package/packages/openauth/src/provider/yahoo.ts +45 -0
  186. package/packages/openauth/src/random.ts +28 -0
  187. package/packages/openauth/src/storage/aws.ts +59 -0
  188. package/packages/openauth/src/storage/cloudflare.ts +77 -0
  189. package/packages/openauth/src/storage/dynamo.ts +193 -0
  190. package/packages/openauth/src/storage/memory.ts +135 -0
  191. package/packages/openauth/src/storage/storage.ts +46 -0
  192. package/packages/openauth/src/subject.ts +130 -0
  193. package/packages/openauth/src/ui/base.tsx +118 -0
  194. package/packages/openauth/src/ui/code.tsx +212 -0
  195. package/packages/openauth/src/ui/form.tsx +40 -0
  196. package/packages/openauth/src/ui/icon.tsx +95 -0
  197. package/packages/openauth/src/ui/password.tsx +403 -0
  198. package/packages/openauth/src/ui/select.tsx +221 -0
  199. package/packages/openauth/src/ui/theme.ts +319 -0
  200. package/packages/openauth/src/ui/ui.css +252 -0
  201. package/packages/openauth/src/util.ts +58 -0
  202. package/packages/openauth/test/client-native.test.ts +30 -0
  203. package/packages/openauth/test/client.test.ts +177 -0
  204. package/packages/openauth/test/issuer.test.ts +393 -0
  205. package/packages/openauth/test/scrap.test.ts +85 -0
  206. package/packages/openauth/test/storage.test.ts +94 -0
  207. package/packages/openauth/test/util.test.ts +103 -0
  208. package/packages/openauth/tsconfig.json +13 -0
  209. package/scripts/format +15 -0
  210. package/www/.vscode/extensions.json +4 -0
  211. package/www/.vscode/launch.json +11 -0
  212. package/www/README.md +55 -0
  213. package/www/astro.config.mjs +136 -0
  214. package/www/bun.lockb +0 -0
  215. package/www/config.ts +4 -0
  216. package/www/generate.ts +911 -0
  217. package/www/package.json +24 -0
  218. package/www/public/favicon-dark.svg +3 -0
  219. package/www/public/favicon.ico +0 -0
  220. package/www/public/favicon.svg +3 -0
  221. package/www/public/social-share.png +0 -0
  222. package/www/src/assets/logo-dark.svg +11 -0
  223. package/www/src/assets/logo-light.svg +11 -0
  224. package/www/src/components/Hero.astro +11 -0
  225. package/www/src/components/Lander.astro +176 -0
  226. package/www/src/content/config.ts +6 -0
  227. package/www/src/content/docs/docs/client.mdx +650 -0
  228. package/www/src/content/docs/docs/index.mdx +325 -0
  229. package/www/src/content/docs/docs/issuer.mdx +512 -0
  230. package/www/src/content/docs/docs/provider/apple.mdx +233 -0
  231. package/www/src/content/docs/docs/provider/code.mdx +163 -0
  232. package/www/src/content/docs/docs/provider/cognito.mdx +173 -0
  233. package/www/src/content/docs/docs/provider/discord.mdx +139 -0
  234. package/www/src/content/docs/docs/provider/facebook.mdx +233 -0
  235. package/www/src/content/docs/docs/provider/github.mdx +139 -0
  236. package/www/src/content/docs/docs/provider/google.mdx +233 -0
  237. package/www/src/content/docs/docs/provider/jumpcloud.mdx +139 -0
  238. package/www/src/content/docs/docs/provider/keycloak.mdx +176 -0
  239. package/www/src/content/docs/docs/provider/microsoft.mdx +252 -0
  240. package/www/src/content/docs/docs/provider/oauth2.mdx +173 -0
  241. package/www/src/content/docs/docs/provider/oidc.mdx +113 -0
  242. package/www/src/content/docs/docs/provider/password.mdx +237 -0
  243. package/www/src/content/docs/docs/provider/slack.mdx +157 -0
  244. package/www/src/content/docs/docs/provider/spotify.mdx +139 -0
  245. package/www/src/content/docs/docs/provider/twitch.mdx +139 -0
  246. package/www/src/content/docs/docs/provider/x.mdx +139 -0
  247. package/www/src/content/docs/docs/provider/yahoo.mdx +139 -0
  248. package/www/src/content/docs/docs/start/nextjs-dark.png +0 -0
  249. package/www/src/content/docs/docs/start/nextjs-light.png +0 -0
  250. package/www/src/content/docs/docs/start/sst.mdx +423 -0
  251. package/www/src/content/docs/docs/start/standalone.mdx +376 -0
  252. package/www/src/content/docs/docs/storage/cloudflare.mdx +63 -0
  253. package/www/src/content/docs/docs/storage/dynamo.mdx +127 -0
  254. package/www/src/content/docs/docs/storage/memory.mdx +77 -0
  255. package/www/src/content/docs/docs/subject.mdx +139 -0
  256. package/www/src/content/docs/docs/themes-dark.png +0 -0
  257. package/www/src/content/docs/docs/themes-light.png +0 -0
  258. package/www/src/content/docs/docs/ui/code.mdx +248 -0
  259. package/www/src/content/docs/docs/ui/password.mdx +410 -0
  260. package/www/src/content/docs/docs/ui/select.mdx +99 -0
  261. package/www/src/content/docs/docs/ui/theme.mdx +284 -0
  262. package/www/src/content/docs/index.mdx +12 -0
  263. package/www/src/custom.css +0 -0
  264. package/www/src/env.d.ts +2 -0
  265. package/www/src/styles/lander.css +15 -0
  266. package/www/tsconfig.json +3 -0
@@ -0,0 +1,69 @@
1
+ import { Context, Hono } from "hono"
2
+ import { getCookie, setCookie } from "hono/cookie"
3
+ import { createClient } from "@farmsdotmarket/openauth/client"
4
+ import { handle } from "hono/aws-lambda"
5
+ import { subjects } from "../../subjects"
6
+
7
+ const client = createClient({
8
+ clientID: "lambda-api",
9
+ })
10
+
11
+ const app = new Hono()
12
+ .get("/authorize", async (c) => {
13
+ const origin = new URL(c.req.url).origin
14
+ const { url } = await client.authorize(origin + "/callback", "code")
15
+ return c.redirect(url, 302)
16
+ })
17
+ .get("/callback", async (c) => {
18
+ const origin = new URL(c.req.url).origin
19
+ try {
20
+ const code = c.req.query("code")
21
+ if (!code) throw new Error("Missing code")
22
+ const exchanged = await client.exchange(code, origin + "/callback")
23
+ if (exchanged.err)
24
+ return new Response(exchanged.err.toString(), {
25
+ status: 400,
26
+ })
27
+ setSession(c, exchanged.tokens.access, exchanged.tokens.refresh)
28
+ return c.redirect("/", 302)
29
+ } catch (e: any) {
30
+ return new Response(e.toString())
31
+ }
32
+ })
33
+ .get("/", async (c) => {
34
+ const access = getCookie(c, "access_token")
35
+ const refresh = getCookie(c, "refresh_token")
36
+ try {
37
+ const verified = await client.verify(subjects, access!, {
38
+ refresh,
39
+ })
40
+ if (verified.err) throw new Error("Invalid access token")
41
+ if (verified.tokens)
42
+ setSession(c, verified.tokens.access, verified.tokens.refresh)
43
+ return c.json(verified.subject)
44
+ } catch (e) {
45
+ console.error(e)
46
+ return c.redirect("/authorize", 302)
47
+ }
48
+ })
49
+
50
+ export const handler = handle(app)
51
+
52
+ function setSession(c: Context, accessToken?: string, refreshToken?: string) {
53
+ if (accessToken) {
54
+ setCookie(c, "access_token", accessToken, {
55
+ httpOnly: true,
56
+ sameSite: "Strict",
57
+ path: "/",
58
+ maxAge: 34560000,
59
+ })
60
+ }
61
+ if (refreshToken) {
62
+ setCookie(c, "refresh_token", refreshToken, {
63
+ httpOnly: true,
64
+ sameSite: "Strict",
65
+ path: "/",
66
+ maxAge: 34560000,
67
+ })
68
+ }
69
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "lambda-api",
3
+ "version": "0.0.0",
4
+ "private": true
5
+ }
@@ -0,0 +1,46 @@
1
+ # nextjs
2
+
3
+ ## 0.1.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [8b5f490]
8
+ - @openauthjs/openauth@0.2.4
9
+
10
+ ## 0.1.5
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [80238de]
15
+ - @openauthjs/openauth@0.2.3
16
+
17
+ ## 0.1.4
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies [6da8647]
22
+ - @openauthjs/openauth@0.2.2
23
+
24
+ ## 0.1.3
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [83125f1]
29
+ - @openauthjs/openauth@0.2.1
30
+
31
+ ## 0.1.2
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies [8c3f050]
36
+ - Updated dependencies [0f93def]
37
+ - @openauthjs/openauth@0.2.0
38
+
39
+ ## 0.1.1
40
+
41
+ ### Patch Changes
42
+
43
+ - Updated dependencies [584728f]
44
+ - Updated dependencies [41acdc2]
45
+ - Updated dependencies [2aa531b]
46
+ - @openauthjs/openauth@0.1.2
@@ -0,0 +1,26 @@
1
+ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2
+
3
+ ## Getting Started
4
+
5
+ Make sure your OpenAuth server is running at `http://localhost:3000`.
6
+
7
+ Then start the development server:
8
+
9
+ ```bash
10
+ npm run dev
11
+ # or
12
+ yarn dev
13
+ # or
14
+ pnpm dev
15
+ # or
16
+ bun dev
17
+ ```
18
+
19
+ Open [http://localhost:3001](http://localhost:3001) with your browser and click **Login with OpenAuth** to start the auth flow.
20
+
21
+ ## Files
22
+
23
+ - [`app/auth.ts`](app/auth.ts): OpenAuth client and helper to set tokens in cookies.
24
+ - [`app/actions.ts`](app/actions.ts): Actions to get current logged in user, and to login and logout.
25
+ - [`app/api/callback/route.ts`](app/api/callback/route.ts): Callback route for OpenAuth.
26
+ - [`app/page.tsx`](app/page.tsx): Shows login and logout buttons and the current user.
@@ -0,0 +1,61 @@
1
+ "use server"
2
+
3
+ import { redirect } from "next/navigation"
4
+ import { headers as getHeaders, cookies as getCookies } from "next/headers"
5
+ import { client, subjects, setTokens } from "./auth"
6
+
7
+ export async function auth() {
8
+ const cookies = await getCookies()
9
+ const accessToken = cookies.get("access_token")
10
+ const refreshToken = cookies.get("refresh_token")
11
+
12
+ if (!accessToken) {
13
+ return false
14
+ }
15
+
16
+ const verified = await client.verify(subjects, accessToken.value, {
17
+ refresh: refreshToken?.value,
18
+ })
19
+
20
+ if (verified.err) {
21
+ return false
22
+ }
23
+ if (verified.tokens) {
24
+ await setTokens(verified.tokens.access, verified.tokens.refresh)
25
+ }
26
+
27
+ return verified.subject
28
+ }
29
+
30
+ export async function login() {
31
+ const cookies = await getCookies()
32
+ const accessToken = cookies.get("access_token")
33
+ const refreshToken = cookies.get("refresh_token")
34
+
35
+ if (accessToken) {
36
+ const verified = await client.verify(subjects, accessToken.value, {
37
+ refresh: refreshToken?.value,
38
+ })
39
+ if (!verified.err && verified.tokens) {
40
+ await setTokens(verified.tokens.access, verified.tokens.refresh)
41
+ redirect("/")
42
+ }
43
+ }
44
+
45
+ const headers = await getHeaders()
46
+ const host = headers.get("host")
47
+ const protocol = host?.includes("localhost") ? "http" : "https"
48
+ const { url } = await client.authorize(
49
+ `${protocol}://${host}/api/callback`,
50
+ "code",
51
+ )
52
+ redirect(url)
53
+ }
54
+
55
+ export async function logout() {
56
+ const cookies = await getCookies()
57
+ cookies.delete("access_token")
58
+ cookies.delete("refresh_token")
59
+
60
+ redirect("/")
61
+ }
@@ -0,0 +1,11 @@
1
+ import { client, setTokens } from "../../auth"
2
+ import { type NextRequest, NextResponse } from "next/server"
3
+
4
+ export async function GET(req: NextRequest) {
5
+ const url = new URL(req.url)
6
+ const code = url.searchParams.get("code")
7
+ const exchanged = await client.exchange(code!, `${url.origin}/api/callback`)
8
+ if (exchanged.err) return NextResponse.json(exchanged.err, { status: 400 })
9
+ await setTokens(exchanged.tokens.access, exchanged.tokens.refresh)
10
+ return NextResponse.redirect(`${url.origin}/`)
11
+ }
@@ -0,0 +1,29 @@
1
+ import { createClient } from "@farmsdotmarket/openauth/client"
2
+ import { cookies as getCookies } from "next/headers"
3
+ export { subjects } from "../../../subjects"
4
+
5
+ export const client = createClient({
6
+ clientID: "nextjs",
7
+ issuer: "http://localhost:3000",
8
+ })
9
+
10
+ export async function setTokens(access: string, refresh: string) {
11
+ const cookies = await getCookies()
12
+
13
+ cookies.set({
14
+ name: "access_token",
15
+ value: access,
16
+ httpOnly: true,
17
+ sameSite: "lax",
18
+ path: "/",
19
+ maxAge: 34560000,
20
+ })
21
+ cookies.set({
22
+ name: "refresh_token",
23
+ value: refresh,
24
+ httpOnly: true,
25
+ sameSite: "lax",
26
+ path: "/",
27
+ maxAge: 34560000,
28
+ })
29
+ }
@@ -0,0 +1,42 @@
1
+ :root {
2
+ --background: #ffffff;
3
+ --foreground: #171717;
4
+ }
5
+
6
+ @media (prefers-color-scheme: dark) {
7
+ :root {
8
+ --background: #0a0a0a;
9
+ --foreground: #ededed;
10
+ }
11
+ }
12
+
13
+ html,
14
+ body {
15
+ max-width: 100vw;
16
+ overflow-x: hidden;
17
+ }
18
+
19
+ body {
20
+ color: var(--foreground);
21
+ background: var(--background);
22
+ font-family: Arial, Helvetica, sans-serif;
23
+ -webkit-font-smoothing: antialiased;
24
+ -moz-osx-font-smoothing: grayscale;
25
+ }
26
+
27
+ * {
28
+ box-sizing: border-box;
29
+ padding: 0;
30
+ margin: 0;
31
+ }
32
+
33
+ a {
34
+ color: inherit;
35
+ text-decoration: none;
36
+ }
37
+
38
+ @media (prefers-color-scheme: dark) {
39
+ html {
40
+ color-scheme: dark;
41
+ }
42
+ }
@@ -0,0 +1,32 @@
1
+ import type { Metadata } from "next"
2
+ import { Geist, Geist_Mono } from "next/font/google"
3
+ import "./globals.css"
4
+
5
+ const geistSans = Geist({
6
+ variable: "--font-geist-sans",
7
+ subsets: ["latin"],
8
+ })
9
+
10
+ const geistMono = Geist_Mono({
11
+ variable: "--font-geist-mono",
12
+ subsets: ["latin"],
13
+ })
14
+
15
+ export const metadata: Metadata = {
16
+ title: "Create Next App",
17
+ description: "Generated by create next app",
18
+ }
19
+
20
+ export default function RootLayout({
21
+ children,
22
+ }: Readonly<{
23
+ children: React.ReactNode
24
+ }>) {
25
+ return (
26
+ <html lang="en">
27
+ <body className={`${geistSans.variable} ${geistMono.variable}`}>
28
+ {children}
29
+ </body>
30
+ </html>
31
+ )
32
+ }
@@ -0,0 +1,169 @@
1
+ .page {
2
+ --gray-rgb: 0, 0, 0;
3
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.08);
4
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.05);
5
+
6
+ --button-primary-hover: #383838;
7
+ --button-secondary-hover: #f2f2f2;
8
+
9
+ display: grid;
10
+ grid-template-rows: 20px 1fr 20px;
11
+ align-items: center;
12
+ justify-items: center;
13
+ min-height: 100svh;
14
+ padding: 80px;
15
+ gap: 64px;
16
+ font-family: var(--font-geist-sans);
17
+ }
18
+
19
+ @media (prefers-color-scheme: dark) {
20
+ .page {
21
+ --gray-rgb: 255, 255, 255;
22
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.145);
23
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.06);
24
+
25
+ --button-primary-hover: #ccc;
26
+ --button-secondary-hover: #1a1a1a;
27
+ }
28
+ }
29
+
30
+ .main {
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 32px;
34
+ grid-row-start: 2;
35
+ }
36
+
37
+ .main ol {
38
+ font-family: var(--font-geist-mono);
39
+ padding-left: 0;
40
+ margin: 0;
41
+ font-size: 14px;
42
+ line-height: 24px;
43
+ letter-spacing: -0.01em;
44
+ list-style-position: inside;
45
+ }
46
+
47
+ .main li:not(:last-of-type) {
48
+ margin-bottom: 8px;
49
+ }
50
+
51
+ .main code {
52
+ font-family: inherit;
53
+ background: var(--gray-alpha-100);
54
+ padding: 2px 4px;
55
+ border-radius: 4px;
56
+ font-weight: 600;
57
+ }
58
+
59
+ .ctas {
60
+ display: flex;
61
+ gap: 16px;
62
+ }
63
+
64
+ .ctas button {
65
+ appearance: none;
66
+ background: transparent;
67
+ border-radius: 128px;
68
+ height: 48px;
69
+ padding: 0 20px;
70
+ border: none;
71
+ border: 1px solid transparent;
72
+ transition:
73
+ background 0.2s,
74
+ color 0.2s,
75
+ border-color 0.2s;
76
+ cursor: pointer;
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ font-size: 16px;
81
+ line-height: 20px;
82
+ font-weight: 500;
83
+ }
84
+
85
+ button.primary {
86
+ background: var(--foreground);
87
+ color: var(--background);
88
+ gap: 8px;
89
+ }
90
+
91
+ button.secondary {
92
+ border-color: var(--gray-alpha-200);
93
+ min-width: 180px;
94
+ }
95
+
96
+ .footer {
97
+ grid-row-start: 3;
98
+ display: flex;
99
+ gap: 24px;
100
+ }
101
+
102
+ .footer a {
103
+ display: flex;
104
+ align-items: center;
105
+ gap: 8px;
106
+ }
107
+
108
+ .footer img {
109
+ flex-shrink: 0;
110
+ }
111
+
112
+ /* Enable hover only on non-touch devices */
113
+ @media (hover: hover) and (pointer: fine) {
114
+ a.primary:hover {
115
+ background: var(--button-primary-hover);
116
+ border-color: transparent;
117
+ }
118
+
119
+ a.secondary:hover {
120
+ background: var(--button-secondary-hover);
121
+ border-color: transparent;
122
+ }
123
+
124
+ .footer a:hover {
125
+ text-decoration: underline;
126
+ text-underline-offset: 4px;
127
+ }
128
+ }
129
+
130
+ @media (max-width: 600px) {
131
+ .page {
132
+ padding: 32px;
133
+ padding-bottom: 80px;
134
+ }
135
+
136
+ .main {
137
+ align-items: center;
138
+ }
139
+
140
+ .main ol {
141
+ text-align: center;
142
+ }
143
+
144
+ .ctas {
145
+ flex-direction: column;
146
+ }
147
+
148
+ .ctas a {
149
+ font-size: 14px;
150
+ height: 40px;
151
+ padding: 0 16px;
152
+ }
153
+
154
+ a.secondary {
155
+ min-width: auto;
156
+ }
157
+
158
+ .footer {
159
+ flex-wrap: wrap;
160
+ align-items: center;
161
+ justify-content: center;
162
+ }
163
+ }
164
+
165
+ @media (prefers-color-scheme: dark) {
166
+ .logo {
167
+ filter: invert();
168
+ }
169
+ }
@@ -0,0 +1,97 @@
1
+ import Image from "next/image"
2
+ import { auth, login, logout } from "./actions"
3
+ import styles from "./page.module.css"
4
+
5
+ export default async function Home() {
6
+ const subject = await auth()
7
+
8
+ return (
9
+ <div className={styles.page}>
10
+ <main className={styles.main}>
11
+ <Image
12
+ className={styles.logo}
13
+ src="/next.svg"
14
+ alt="Next.js logo"
15
+ width={180}
16
+ height={38}
17
+ priority
18
+ />
19
+ <ol>
20
+ {subject ? (
21
+ <>
22
+ <li>
23
+ Logged in as <code>{subject.properties.id}</code>.
24
+ </li>
25
+ <li>
26
+ And then check out <code>app/page.tsx</code>.
27
+ </li>
28
+ </>
29
+ ) : (
30
+ <>
31
+ <li>Login with your email and password.</li>
32
+ <li>
33
+ And then check out <code>app/page.tsx</code>.
34
+ </li>
35
+ </>
36
+ )}
37
+ </ol>
38
+
39
+ <div className={styles.ctas}>
40
+ {subject ? (
41
+ <form action={logout}>
42
+ <button className={styles.secondary}>Logout</button>
43
+ </form>
44
+ ) : (
45
+ <form action={login}>
46
+ <button className={styles.primary}>Login with OpenAuth</button>
47
+ </form>
48
+ )}
49
+ </div>
50
+ </main>
51
+ <footer className={styles.footer}>
52
+ <a
53
+ href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
54
+ target="_blank"
55
+ rel="noopener noreferrer"
56
+ >
57
+ <Image
58
+ aria-hidden
59
+ src="/file.svg"
60
+ alt="File icon"
61
+ width={16}
62
+ height={16}
63
+ />
64
+ Learn
65
+ </a>
66
+ <a
67
+ href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
68
+ target="_blank"
69
+ rel="noopener noreferrer"
70
+ >
71
+ <Image
72
+ aria-hidden
73
+ src="/window.svg"
74
+ alt="Window icon"
75
+ width={16}
76
+ height={16}
77
+ />
78
+ Examples
79
+ </a>
80
+ <a
81
+ href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
82
+ target="_blank"
83
+ rel="noopener noreferrer"
84
+ >
85
+ <Image
86
+ aria-hidden
87
+ src="/globe.svg"
88
+ alt="Globe icon"
89
+ width={16}
90
+ height={16}
91
+ />
92
+ Go to nextjs.org →
93
+ </a>
94
+ </footer>
95
+ </div>
96
+ )
97
+ }
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next"
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* config options here */
5
+ }
6
+
7
+ export default nextConfig
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@openauthjs/example-nextjs",
3
+ "version": "0.1.6",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@farmsdotmarket/openauth": "workspace:*",
13
+ "next": "15.1.0",
14
+ "react": "19.0.0",
15
+ "react-dom": "19.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "22.10.1",
19
+ "@types/react": "19.0.1",
20
+ "@types/react-dom": "19.0.2",
21
+ "typescript": "5.6.3"
22
+ }
23
+ }
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ },
25
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26
+ "exclude": ["node_modules"]
27
+ }