···11+MIT License
22+33+Copyright (c) 2025 Slices Network
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy of
66+this software and associated documentation files (the "Software"), to deal in
77+the Software without restriction, including without limitation the rights to
88+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
99+the Software, and to permit persons to whom the Software is furnished to do so,
1010+subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
1717+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
1818+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
1919+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2020+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+57
packages/oauth/README.md
···11+# @slices/oauth
22+33+[AIP](https://github.com/graze-social/aip) OAuth 2.1 client with PKCE support
44+for Typescript.
55+66+## Features
77+88+- OAuth 2.1 with PKCE flow
99+- Token refresh handling
1010+- Pluggable storage backends (SQLite, Deno KV, others can be added)
1111+1212+## Installation
1313+1414+### Deno
1515+1616+```bash
1717+deno add jsr:@slices/oauth
1818+```
1919+2020+```typescript
2121+import { OAuthClient, SQLiteOAuthStorage } from "@slices/oauth";
2222+```
2323+2424+For node based package managers:
2525+2626+```bash
2727+# pnpm (10.9+)
2828+pnpm install jsr:@slices/oauth
2929+3030+# Yarn (4.9+)
3131+yarn add jsr:@slices/oauth
3232+```
3333+3434+## Usage
3535+3636+```typescript
3737+import { OAuthClient, SQLiteOAuthStorage } from "@slices/oauth";
3838+3939+// Set up storage
4040+const storage = new SQLiteOAuthStorage("oauth.db");
4141+4242+// Create OAuth client
4343+const client = new OAuthClient({
4444+ clientId: "your-client-id",
4545+ clientSecret: "your-client-secret",
4646+ authBaseUrl: "https://auth.example.com",
4747+ redirectUri: "http://localhost:8000/oauth/callback",
4848+ scopes: ["atproto:atproto"],
4949+}, storage);
5050+5151+// Start authorization flow
5252+const result = await client.authorize({ loginHint: "user.bsky.social" });
5353+// Redirect user to result.authorizationUrl
5454+5555+// Handle callback
5656+const tokens = await client.handleCallback({ code, state });
5757+```
···11+MIT License
22+33+Copyright (c) 2025 Slices Network
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy of
66+this software and associated documentation files (the "Software"), to deal in
77+the Software without restriction, including without limitation the rights to
88+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
99+the Software, and to permit persons to whom the Software is furnished to do so,
1010+subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
1717+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
1818+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
1919+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2020+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+217
packages/session/README.md
···11+# @slices/session
22+33+Session management for Slice applications with OAuth integration.
44+55+## Features
66+77+- **Multiple Storage Adapters**: Memory, SQLite, PostgreSQL
88+- **OAuth Integration**: Works seamlessly with `@slices/oauth`
99+- **Automatic Token Refresh**: Keeps OAuth tokens valid
1010+- **Secure Cookie Handling**: HttpOnly, Secure, SameSite defaults
1111+- **Automatic Cleanup**: Expired session cleanup
1212+- **Framework Agnostic**: Works with any Deno web framework
1313+1414+## Installation
1515+1616+```bash
1717+deno add @slices/session
1818+```
1919+2020+## Quick Start
2121+2222+### Basic Usage
2323+2424+```typescript
2525+import { SessionStore, SQLiteAdapter } from "@slices/session";
2626+2727+const sessionStore = new SessionStore({
2828+ adapter: new SQLiteAdapter("./sessions.db"),
2929+ cookieOptions: {
3030+ httpOnly: true,
3131+ secure: true,
3232+ sameSite: "lax"
3333+ }
3434+});
3535+3636+// Create a session
3737+const sessionId = await sessionStore.createSession("user123", "alice.bsky.social");
3838+3939+// Get session from request
4040+const session = await sessionStore.getSessionFromRequest(request);
4141+if (session) {
4242+ console.log("User:", session.handle);
4343+}
4444+```
4545+4646+### With OAuth Integration
4747+4848+```typescript
4949+import { SessionStore, SQLiteAdapter, withOAuthSession } from "@slices/session";
5050+import { OAuthClient } from "@slices/oauth";
5151+5252+const sessionStore = new SessionStore({
5353+ adapter: new SQLiteAdapter("./sessions.db")
5454+});
5555+5656+const oauthClient = new OAuthClient({
5757+ clientId: "your-client-id",
5858+ clientSecret: "your-client-secret",
5959+ authBaseUrl: "https://auth.example.com"
6060+});
6161+6262+const oauthSessions = withOAuthSession(sessionStore, oauthClient);
6363+6464+// Create OAuth session
6565+const sessionId = await oauthSessions.createOAuthSession(request);
6666+6767+// Get session with auto token refresh
6868+const session = await oauthSessions.getOAuthSession(sessionId);
6969+```
7070+7171+### Storage Adapters
7272+7373+#### Memory Adapter (Development)
7474+7575+```typescript
7676+import { MemoryAdapter } from "@slices/session";
7777+7878+const adapter = new MemoryAdapter();
7979+```
8080+8181+#### SQLite Adapter (Production Single Instance)
8282+8383+```typescript
8484+import { SQLiteAdapter } from "@slices/session";
8585+8686+const adapter = new SQLiteAdapter("./sessions.db");
8787+// or with URL
8888+const adapter = new SQLiteAdapter("sqlite://./sessions.db");
8989+```
9090+9191+#### PostgreSQL Adapter (Production Distributed)
9292+9393+```typescript
9494+import { PostgresAdapter } from "@slices/session";
9595+9696+const adapter = new PostgresAdapter("postgresql://user:pass@localhost/db");
9797+9898+// Initialize the database table
9999+await adapter.initialize();
100100+```
101101+102102+## API Reference
103103+104104+### SessionStore
105105+106106+Main session management class.
107107+108108+```typescript
109109+const store = new SessionStore({
110110+ adapter: SessionAdapter,
111111+ cookieName?: string, // Default: "slice-session"
112112+ cookieOptions?: CookieOptions,
113113+ sessionTTL?: number, // Default: 30 days (ms)
114114+ cleanupInterval?: number, // Default: 1 hour (ms)
115115+ generateId?: () => string // Default: crypto.randomUUID()
116116+});
117117+```
118118+119119+#### Methods
120120+121121+- `createSession(userId, handle?, data?)` - Create new session
122122+- `getSession(sessionId)` - Get session by ID
123123+- `updateSession(sessionId, updates)` - Update session data
124124+- `deleteSession(sessionId)` - Delete session
125125+- `getSessionFromRequest(request)` - Extract session from HTTP request
126126+- `getCurrentUser(request)` - Get user info from request
127127+- `createSessionCookie(sessionId)` - Create Set-Cookie header
128128+- `createLogoutCookie()` - Create logout cookie header
129129+- `cleanup()` - Remove expired sessions
130130+131131+### OAuthSessionManager
132132+133133+OAuth-enabled session management.
134134+135135+```typescript
136136+const manager = withOAuthSession(sessionStore, oauthClient, {
137137+ autoRefresh: true, // Auto-refresh expired tokens
138138+ onTokenRefresh: async (sessionId, tokens) => {
139139+ // Handle token refresh
140140+ },
141141+ onLogout: async (sessionId) => {
142142+ // Handle logout
143143+ }
144144+});
145145+```
146146+147147+#### Methods
148148+149149+- `createOAuthSession(request)` - Create session with OAuth tokens
150150+- `getOAuthSession(sessionId)` - Get session with token refresh
151151+- `logout(sessionId)` - OAuth logout and session cleanup
152152+- `hasValidOAuthTokens(sessionId)` - Check token validity
153153+- `getAccessToken(sessionId)` - Get access token for API calls
154154+155155+### Session Data Structure
156156+157157+```typescript
158158+interface SessionData {
159159+ sessionId: string;
160160+ userId: string; // User's DID or ID
161161+ handle?: string; // User's handle (e.g., alice.bsky.social)
162162+ isAuthenticated: boolean;
163163+ data?: Record<string, unknown>; // Custom session data
164164+ createdAt: number; // Timestamp
165165+ expiresAt: number; // Timestamp
166166+ lastAccessedAt: number; // Timestamp
167167+}
168168+```
169169+170170+## Framework Integration
171171+172172+### Deno Fresh
173173+174174+```typescript
175175+// routes/_middleware.ts
176176+import { SessionStore, SQLiteAdapter } from "@slices/session";
177177+178178+const sessionStore = new SessionStore({
179179+ adapter: new SQLiteAdapter("./sessions.db")
180180+});
181181+182182+export async function handler(req: Request, ctx: FreshContext) {
183183+ const user = await sessionStore.getCurrentUser(req);
184184+ ctx.state.user = user;
185185+ return ctx.next();
186186+}
187187+```
188188+189189+### Hono
190190+191191+```typescript
192192+import { Hono } from "hono";
193193+import { SessionStore, SQLiteAdapter } from "@slices/session";
194194+195195+const app = new Hono();
196196+const sessionStore = new SessionStore({
197197+ adapter: new SQLiteAdapter("./sessions.db")
198198+});
199199+200200+app.use("*", async (c, next) => {
201201+ const user = await sessionStore.getCurrentUser(c.req.raw);
202202+ c.set("user", user);
203203+ await next();
204204+});
205205+```
206206+207207+## Security
208208+209209+- Sessions are stored with secure, httpOnly cookies by default
210210+- Automatic cleanup of expired sessions
211211+- CSRF protection through SameSite cookies
212212+- Secure session ID generation using crypto.randomUUID()
213213+- Optional token refresh to keep OAuth sessions valid
214214+215215+## License
216216+217217+MIT