···1111## Conference Profile
1212For starters, we're going to have a custom conference profile. Attendees and speakers (and anyone else!) can login and create an extended profile. Inspired by [Discover Toronto](https://discover.toronto.inc/), we had an [initial discussion in the forum](https://discourse.atprotocol.community/t/conference-profiles/186) and are going to work on fleshing this out here with detailed issues.
13131414-# Astro Starter Kit: Minimal
1414+# Astro ATProto OAuth Starter
1515+1616+A minimal [Astro](https://astro.build) starter template demonstrating OAuth authentication with AT Protocol (ATProto), the decentralized social networking protocol used by Bluesky and other services.
1717+1818+This starter includes:
1919+- Complete OAuth authentication flow using `@atproto/oauth-client-node`
2020+- Cookie-based session management
2121+- Profile display after authentication
2222+- Login/logout endpoints
2323+- Tailwind CSS and DaisyUI styling
2424+2525+## 🚀 Getting Started
2626+2727+1. **Install dependencies:**
2828+ ```sh
2929+ npm install
3030+ ```
15311616-```sh
1717-npm create astro@latest -- --template minimal
1818-```
3232+2. **Configure environment variables:**
3333+ ```sh
3434+ cp .env.template .env
3535+ ```
3636+ Edit `.env` if you need to change the port (default: 4321) or set a public URL.
19372020-> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
3838+3. **Start the development server:**
3939+ ```sh
4040+ npm run dev
4141+ ```
4242+ The app will be available at `http://localhost:4321`
21432222-## 🚀 Project Structure
4444+4. **Try logging in:**
4545+ Enter your AT Protocol handle (e.g., `alice.bsky.social`) to authenticate.
23462424-Inside of your Astro project, you'll see the following folders and files:
4747+## 📁 Project Structure
25482649```text
2750/
2851├── public/
2952├── src/
3030-│ └── pages/
3131-│ └── index.astro
5353+│ ├── lib/
5454+│ │ ├── context.ts # OAuth client singleton
5555+│ │ ├── oauth.ts # OAuth client configuration
5656+│ │ ├── session.ts # Session management
5757+│ │ └── storage.ts # Cookie-based stores
5858+│ ├── pages/
5959+│ │ ├── api/
6060+│ │ │ ├── login.ts # Login endpoint
6161+│ │ │ ├── logout.ts # Logout endpoint
6262+│ │ │ └── oauth/
6363+│ │ │ └── callback.ts # OAuth callback handler
6464+│ │ └── index.astro # Main page with login UI
6565+│ └── styles.css
3266└── package.json
3367```
3434-3535-Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
3636-3737-There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
3838-3939-Any static assets, like images, can be placed in the `public/` directory.
40684169## 🧞 Commands
4270···5179| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
5280| `npm run astro -- --help` | Get help using the Astro CLI |
53815454-## 👀 Want to learn more?
8282+## 📚 Learn More
55835656-Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
8484+- [Astro Documentation](https://docs.astro.build)
8585+- [AT Protocol Documentation](https://atproto.com)
8686+- [@atproto/oauth-client-node](https://github.com/bluesky-social/atproto/tree/main/packages/oauth/oauth-client-node)
8787+- [Bluesky](https://bsky.app)
+4-14
src/lib/context.ts
···11-import { NodeOAuthClient } from '@atproto/oauth-client-node'
11+import type { AstroCookies } from 'astro'
22import { createOAuthClient } from './oauth'
3344-export type AppContext = {
55- oauthClient: NodeOAuthClient
66-}
77-88-let _ctx: AppContext | null = null
99-1010-export async function getAppContext(): Promise<AppContext> {
1111- if (_ctx) return _ctx
1212-1313- const oauthClient = await createOAuthClient()
1414-1515- _ctx = { oauthClient }
1616- return _ctx
44+// Create a request-scoped OAuth client with cookie-based storage
55+export function getOAuthClient(cookies: AstroCookies) {
66+ return createOAuthClient(cookies)
177}
+5-4
src/lib/oauth.ts
···11+import type { AstroCookies } from 'astro'
12import {
23 atprotoLoopbackClientMetadata,
34 NodeOAuthClient,
45} from "@atproto/oauth-client-node";
56import { env } from "./env";
66-import { SessionStore, StateStore } from "./storage";
77+import { CookieSessionStore, CookieStateStore } from "./storage";
7888-export async function createOAuthClient() {
99+export function createOAuthClient(cookies: AstroCookies) {
910 const clientMetadata = atprotoLoopbackClientMetadata(
1011 `http://localhost?${new URLSearchParams([
1112 ["redirect_uri", `http://127.0.0.1:${env.PORT}/api/oauth/callback`],
···15161617 return new NodeOAuthClient({
1718 clientMetadata,
1818- stateStore: new StateStore(),
1919- sessionStore: new SessionStore(),
1919+ stateStore: new CookieStateStore(cookies),
2020+ sessionStore: new CookieSessionStore(cookies),
2021 });
2122}