AT Protocol OAuth template in Deno, Hono, HTMX
18
fork

Configure Feed

Select the types of activity you want to include in your feed.

update readme, add /profile page

+77 -24
+16
README.md
··· 1 + # AT Protocol OAuth Template 2 + ## for Deno, Hono, and HTMX 3 + 4 + ### Getting Started 5 + 1. Set up `.env` file with a `COOKIE_SECRET` variable 6 + ``` 7 + COOKIE_SECRET=[run `openssl rand -base64 64` in the terminal] 8 + ``` 9 + 10 + 2. Run the web application 1 11 ``` 2 12 deno task start 3 13 ``` 14 + 15 + ### Packages 16 + - [`Deno`](https://deno.com) - Runtime and package manager 17 + - [`Hono`](https://hono.dev) - Web application framework 18 + - [`HTMX`](https://htmx.org) - AJAX library 19 + - [`@tijs/atproto-oauth`](https://jsr.io/@tijs/atproto-oauth) - AT Protocol OAuth library
+9 -3
components.tsx
··· 11 11 <script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js" integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz" crossorigin="anonymous"></script> 12 12 </head> 13 13 <body> 14 - { session ? <LogoutForm /> : <LoginForm /> } 14 + <nav style={{ display: "flex", "flex-direction": "row", gap: "8px" }}> 15 + { session ? <LogoutForm /> : <LoginForm /> } 16 + <a href="/">README</a> 17 + <a href="/profile">Profile</a> 18 + </nav> 19 + <hr /> 15 20 {children} 21 + <div id="error"></div> 16 22 </body> 17 23 </html> 18 24 ); ··· 20 26 21 27 export const LoginForm: FC = () => { 22 28 return ( 23 - <form method="get" action="/login"> 29 + <form id="authForm" hx-swap-oob="true" method="get" action="/login"> 24 30 <input name="handle" type="text" /> 25 31 <button type="submit">Login</button> 26 32 </form> ··· 29 35 30 36 export const LogoutForm: FC = () => { 31 37 return ( 32 - <form hx-post="/api/auth/logout" hx-swap="outerHTML"> 38 + <form id="authForm" hx-post="/api/auth/logout" hx-swap="none"> 33 39 <button type="submit">Logout</button> 34 40 </form> 35 41 )
+52 -21
main.tsx
··· 1 - import { Context, Hono } from "hono"; 1 + import { Hono } from "hono"; 2 2 import { createATProtoOAuth } from "@tijs/atproto-oauth"; 3 3 import { MemoryStorage } from "@tijs/atproto-storage"; 4 4 import { LoginForm, SiteLayout } from "./components.tsx"; ··· 7 7 8 8 export const oauth = createATProtoOAuth({ 9 9 baseUrl: "http://127.0.0.1:8000/", 10 - appName: "Pedro", 10 + appName: "AT Protocol OAuth Example App", 11 11 cookieSecret: Deno.env.get("COOKIE_SECRET")!, 12 12 storage: new MemoryStorage(), 13 13 sessionTtl: 60 * 60 * 24 * 14, // 14 days ··· 23 23 if (success) { 24 24 return c.html(<LoginForm />) 25 25 } 26 - return res; 26 + else { 27 + return c.html(<p hx-swap-oob="innerHTML:#error">Error: Logout unsuccessful</p>) 28 + } 27 29 }); 28 30 29 - // Mount OAuth routes 31 + // Routes 30 32 app.get("/", (c) => { 31 33 return c.html( 32 34 <SiteLayout context={c}> 33 - <h1>Hello</h1> 35 + <h1>AT Protocol OAuth Template</h1> 36 + <h2>for Deno, Hono, and HTMX</h2> 37 + 38 + <h3>Getting Started</h3> 39 + <ol> 40 + <li> 41 + <div style={{ display: "flex", "flex-direction": "column", gap: "0.25em" }}> 42 + <p>Set up <code>.env</code> file with a <code>COOKIE_SECRET</code> variable</p> 43 + <code style={{ background: "lightgray", padding: "1em", width: "fit-content" }}> 44 + COOKIE_SECRET=[run `openssl rand -base64 64` in the terminal`] 45 + </code> 46 + </div> 47 + </li> 48 + 49 + <li> 50 + <div style={{ display: "flex", "flex-direction": "column", gap: "0.25em" }}> 51 + <p>Run the web application</p> 52 + <code style={{ background: "lightgray", padding: "1em", width: "fit-content" }}> 53 + deno task start 54 + </code> 55 + </div> 56 + </li> 57 + </ol> 58 + 59 + <h3>Packages</h3> 60 + <ul> 61 + <li><a href="https://deno.com">Deno</a> - Runtime and package manager</li> 62 + <li><a href="https://hono.dev">Hono</a> - Web application framework</li> 63 + <li><a href="https://htmx.org">HTMX</a> - AJAX library</li> 64 + <li> 65 + <a href="https://jsr.io/@tijs/atproto-oauth">@tijs/atproto-oauth</a> - AT Protocol OAuth library 66 + </li> 67 + </ul> 34 68 </SiteLayout> 35 69 ) 36 70 }); 37 71 38 72 // Protected route example 39 - app.get("/api/profile", async (c) => { 40 - const { session, setCookieHeader, error } = await oauth.getSessionFromRequest( 73 + app.get("/profile", async (c) => { 74 + const { session, error } = await oauth.getSessionFromRequest( 41 75 c.req.raw, 42 76 ); 43 77 44 - if (!session) { 45 - return c.json({ error: error?.message || "Not authenticated" }, 401); 78 + if (!session || error) { 79 + return c.html( 80 + <SiteLayout context={c}> 81 + <p>No profile logged in</p> 82 + </SiteLayout> 83 + ) 46 84 } 47 85 48 - // Make authenticated API call 49 - const response = await session.makeRequest( 50 - "GET", 51 - `${session.pdsUrl}/xrpc/app.bsky.actor.getProfile?actor=${session.did}`, 86 + return c.html( 87 + <SiteLayout context={c}> 88 + <p>PDS: {session.pdsUrl}</p> 89 + <p>DID: {session.did}</p> 90 + </SiteLayout> 52 91 ); 53 - 54 - const profile = await response.json(); 55 - 56 - const res = c.json(profile); 57 - if (setCookieHeader) { 58 - res.headers.set("Set-Cookie", setCookieHeader); 59 - } 60 - return res; 61 92 }); 62 93 63 94 Deno.serve(app.fetch);