Skip to content
This repository has been archived by the owner on Nov 27, 2024. It is now read-only.

Commit

Permalink
use lucia auth
Browse files Browse the repository at this point in the history
  • Loading branch information
syhner committed Mar 26, 2024
1 parent 1688597 commit a0e3d95
Show file tree
Hide file tree
Showing 20 changed files with 247 additions and 534 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Config } from 'drizzle-kit';
import { env } from '~/env';
import { env } from './src/env';

export default {
schema: './src/db/schemas/*.ts',
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@
"db:view": "drizzle-kit studio"
},
"dependencies": {
"@auth/core": "^0.14.0",
"@auth/drizzle-adapter": "^0.3.2",
"@elysiajs/html": "^1.0.2",
"@elysiajs/static": "^1.0.2",
"@elysiajs/swagger": "^1.0.3",
"@libsql/client": "^0.3.4",
"@lucia-auth/adapter-sqlite": "^3.0.1",
"arctic": "^1.2.1",
"better-sqlite3": "^8.6.0",
"drizzle-orm": "^0.28.6",
"drizzle-typebox": "^0.1.1",
"elysia": "^1.0.7",
"lucia": "^3.1.1",
"oslo": "^1.1.3",
"zod": "^3.22.2"
},
"devDependencies": {
Expand Down
119 changes: 111 additions & 8 deletions src/app/api/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,116 @@
import { Auth } from '@auth/core';
import { authConfig } from '~/lib/auth';
import { generateState, OAuth2RequestError } from 'arctic';
import { generateId } from 'lucia';
import { parseCookies, serializeCookie } from 'oslo/cookie';
import { z } from 'zod';
import { users } from '~/db/schemas/auth';
import { env } from '~/env';
import { github, lucia } from '~/lib/auth';
import { db } from '~/lib/db';
import { createElysia } from '~/util/elysia';

// const githubUserSchema = z.object({
// id: z.string(),
// login: z.string(),
// });

export const routes = createElysia({ prefix: '/auth' })
.get('/*', async (ctx) => {
const res = await Auth(ctx.request, authConfig);
return res;
.get('/login/github', async (ctx) => {
const state = generateState();
const url = await github.createAuthorizationURL(state);
return new Response(null, {
status: 302,
headers: {
Location: url.toString(),
'Set-Cookie': serializeCookie('github_oauth_state', state, {
httpOnly: true,
secure: env.NODE_ENV === 'production', // set `Secure` flag in HTTPS
maxAge: 60 * 10, // 10 minutes
path: '/',
}),
},
});
})
.get('/login/github/callback', async (ctx) => {
const cookies = parseCookies(ctx.request.headers.get('Cookie') ?? '');
const stateCookie = cookies.get('github_oauth_state') ?? null;

const url = new URL(ctx.request.url);
const state = url.searchParams.get('state');
const code = url.searchParams.get('code');

// verify state
if (!state || !stateCookie || !code || stateCookie !== state) {
return new Response(null, {
status: 400,
});
}

try {
const tokens = await github.validateAuthorizationCode(code);
const githubUserResponse = await fetch('https://api.github.com/user', {
headers: {
Authorization: `Bearer ${tokens.accessToken}`,
},
});
const githubUser = await githubUserResponse.json();

const existingUser = await db.query.users.findFirst({
where: (users, { eq }) => eq(users.githubId, githubUser.id),
});

if (existingUser) {
const session = await lucia.createSession(existingUser.id, {});
const sessionCookie = lucia.createSessionCookie(session.id);
return new Response(null, {
status: 302,
headers: {
Location: '/',
'Set-Cookie': sessionCookie.serialize(),
},
});
}

const userId = generateId(15);
await db.insert(users).values({
id: userId,
githubId: githubUser.id,
});

const session = await lucia.createSession(userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
return new Response(null, {
status: 302,
headers: {
Location: '/',
'Set-Cookie': sessionCookie.serialize(),
},
});
} catch (e) {
console.log(e);
if (e instanceof OAuth2RequestError) {
// bad verification code, invalid credentials, etc
return new Response(null, {
status: 400,
});
}
return new Response(null, {
status: 500,
});
}
})
.post('/*', async (ctx) => {
const res = await Auth(ctx.request, authConfig);
return res;
.post('/logout', async (ctx) => {
if (!ctx.session) {
return new Response(null, {
status: 401,
});
}

await lucia.invalidateSession(ctx.session.id);
return new Response(null, {
status: 302,
headers: {
Location: '/',
'Set-Cookie': lucia.createBlankSessionCookie().serialize(),
},
});
});
5 changes: 1 addition & 4 deletions src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { html } from '@elysiajs/html';
import { swagger } from '@elysiajs/swagger';
import { SignIn, SignOut } from '~/components/auth';
import { Layout } from '~/components/Layout';
import { getSession } from '~/lib/auth';
import { createElysia } from '~/util/elysia';
import { routes as apiRoutes } from './api';
import { routes as todosRoutes } from './todos';
Expand All @@ -20,12 +19,10 @@ export const app = createElysia()
// Page routes
.use(todosRoutes)
.get('/', async (ctx) => {
const session = await getSession(ctx.request);

return (
<Layout>
<div class='px-6 py-6'>
{session ? (
{ctx.session ? (
<SignOut></SignOut>
) : (
<SignIn>Sign in to modify todos</SignIn>
Expand Down
7 changes: 2 additions & 5 deletions src/app/todos/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { eq } from 'drizzle-orm';
import { insertTodoSchema, patchTodoSchema, todos } from '~/db/schemas/todo';
import { isAuthenticated } from '~/hooks/isAuthenticated';
import { getSession } from '~/lib/auth';
import { db, idParamsSchema } from '~/lib/db';
import { createElysia } from '~/util/elysia';
import { TodoForm, TodoItem, TodoList } from './components';

export const routes = createElysia({ prefix: '/todos' })
.get('/', async (ctx) => {
const session = await getSession(ctx.request);

const allTodos = await db.select().from(todos).all();
return (
<div class='flex flex-col'>
<TodoForm enabled={!!session} />
<TodoList todos={allTodos} enabled={!!session} />
<TodoForm enabled={!!ctx.session} />
<TodoList todos={allTodos} enabled={!!ctx.session} />
</div>
);
})
Expand Down
15 changes: 6 additions & 9 deletions src/components/auth.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
export const SignIn: Html.Component = ({ children }) => {
return (
<button
onclick="window.location.href='/api/auth/signin'"
onclick="window.location.href='/api/auth/login/github'"
class='rounded-sm bg-primary text-primary-foreground hover:bg-primary/90 px-3'
safe
>
{children ?? 'Sign in'}
</button>
Expand All @@ -12,12 +11,10 @@ export const SignIn: Html.Component = ({ children }) => {

export const SignOut: Html.Component = ({ children }) => {
return (
<button
onclick="window.location.href='/api/auth/signout'"
class='rounded-sm bg-primary text-primary-foreground hover:bg-primary/90 px-3'
safe
>
{children ?? 'Sign out'}
</button>
<form method='post' action='/api/auth/logout'>
<button class='rounded-sm bg-primary text-primary-foreground hover:bg-primary/90 px-3'>
{children ?? 'Sign out'}
</button>
</form>
);
};
5 changes: 0 additions & 5 deletions src/db/migrations/0000_zippy_rage.sql

This file was deleted.

37 changes: 0 additions & 37 deletions src/db/migrations/0001_silent_blizzard.sql

This file was deleted.

45 changes: 0 additions & 45 deletions src/db/migrations/meta/0000_snapshot.json

This file was deleted.

Loading

0 comments on commit a0e3d95

Please sign in to comment.