Skip to content

Commit

Permalink
feat: add unstorage adapter (#662)
Browse files Browse the repository at this point in the history
Co-authored-by: pilcrowOnPaper <[email protected]>
  • Loading branch information
Hebilicious and pilcrowonpaper authored Jul 16, 2023
1 parent cd794a1 commit f9201e3
Show file tree
Hide file tree
Showing 35 changed files with 379 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const auth = lucia({
- [`postgres`](/database-adapters/postgres): PostgreSQL
- [Prisma](/database-adapters/prisma): MongoDB, MySQL, PostgreSQL, SQLite
- [Redis](/database-adapters/redis): Redis
- [Unstorage](/database-adapters/unstorage): Azure, Cloudflare KV, Memory, MongoDB, Planetscale, Redis, Vercel KV

### Provider specific adapters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const auth = lucia({
- [`postgres`](/database-adapters/postgres): PostgreSQL
- [Prisma](/database-adapters/prisma): MongoDB, MySQL, PostgreSQL, SQLite
- [Redis](/database-adapters/redis): Redis
- [Unstorage](/database-adapters/unstorage): Azure, Cloudflare KV, Memory, MongoDB, Planetscale, Redis, Vercel KV

### Provider specific adapters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const auth = lucia({
- [`postgres`](/database-adapters/postgres): PostgreSQL
- [Prisma](/database-adapters/prisma): MongoDB, MySQL, PostgreSQL, SQLite
- [Redis](/database-adapters/redis): Redis
- [Unstorage](/database-adapters/unstorage): Azure, Cloudflare KV, Memory, MongoDB, Planetscale, Redis, Vercel KV

### Provider specific adapters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const auth = lucia({
- [`postgres`](/database-adapters/postgres): PostgreSQL
- [Prisma](/database-adapters/prisma): MongoDB, MySQL, PostgreSQL, SQLite
- [Redis](/database-adapters/redis): Redis
- [Unstorage](/database-adapters/unstorage): Azure, Cloudflare KV, Memory, MongoDB, Planetscale, Redis, Vercel KV

### Provider specific adapters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const auth = lucia({
- [`postgres`](/database-adapters/postgres): PostgreSQL
- [Prisma](/database-adapters/prisma): MongoDB, MySQL, PostgreSQL, SQLite
- [Redis](/database-adapters/redis): Redis
- [Unstorage](/database-adapters/unstorage): Azure, Cloudflare KV, Memory, MongoDB, Planetscale, Redis, Vercel KV

### Provider specific adapters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const auth = lucia({
- [`postgres`](/database-adapters/postgres): PostgreSQL
- [Prisma](/database-adapters/prisma): MongoDB, MySQL, PostgreSQL, SQLite
- [Redis](/database-adapters/redis): Redis
- [Unstorage](/database-adapters/unstorage): Azure, Cloudflare KV, Memory, MongoDB, Planetscale, Redis, Vercel KV

### Provider specific adapters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const auth = lucia({
- [`postgres`](/database-adapters/postgres): PostgreSQL
- [Prisma](/database-adapters/prisma): MongoDB, MySQL, PostgreSQL, SQLite
- [Redis](/database-adapters/redis): Redis
- [Unstorage](/database-adapters/unstorage): Azure, Cloudflare KV, Memory, MongoDB, Planetscale, Redis, Vercel KV

### Provider specific adapters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const auth = lucia({
- [`postgres`](/database-adapters/postgres): PostgreSQL
- [Prisma](/database-adapters/prisma): MongoDB, MySQL, PostgreSQL, SQLite
- [Redis](/database-adapters/redis): Redis
- [Unstorage](/database-adapters/unstorage): Azure, Cloudflare KV, Memory, MongoDB, Planetscale, Redis, Vercel KV

### Provider specific adapters

Expand Down
2 changes: 1 addition & 1 deletion documentation-v2/content/main/1.basics/0.database.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ We currently provide the following adapters:
- [PlanetScale serverless](/database-adapters/planetscale-serverless)
- [Prisma](/database-adapters/prisma)
- [Redis](/database-adapters/redis)
- [Upstash](/database-adapters/upstash)
- [Unstorage](/database-adapters/unstorage)

You can also use query builders like Drizzle ORM and Kysely since they rely on underlying drivers that we provide adapters for.

Expand Down
8 changes: 8 additions & 0 deletions documentation-v2/content/main/2.database-adapters/redis.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ const redis: (
| `client` | `RedisClientType` | | Redis client |
| `prefixes` | `Record<string, string>` || Key prefixes |

## Installation

```
npm i @lucia-auth/adapter-session-redis
pnpm add @lucia-auth/adapter-session-redis
yarn add @lucia-auth/adapter-session-redis
```

### Key prefixes

Key are defined as a combination of a prefix and an id so everything can be stored in a single Redis instance. By default, sessions are stored as `session:<session_id>` and user-sessions relationships are stored as `user_sessions:<user_id>`.
Expand Down
58 changes: 58 additions & 0 deletions documentation-v2/content/main/2.database-adapters/unstorage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
menuTitle: "Unstorage"
title: "Unstorage session adapter"
description: "Learn how to use Unstorage with Lucia"
---

Session adapter for [Unstorage](https://github.com/unjs/unstorage). This only handles sessions, and not users or keys. Supports many key-value databases, including Azure, Cloudflare KV, MongoDB, Planetscale, Redis, and Vercel KV, as well as in-memory.

```ts
import { unstorage } from "@lucia-auth/adapter-session-unstorage";
```

```ts
const unstorage: (
storage: Storage,
prefixes?: {
session: string;
userSession: string;
}
) => InitializeAdapter<SessionAdapter>;
```

##### Parameters

| name | type | optional | description |
| ---------- | ------------------------ | :------: | ------------ |
| `storage` | `Storage` | | |
| `prefixes` | `Record<string, string>` || Key prefixes |

## Installation

```
npm i @lucia-auth/adapter-session-unstorage
pnpm add @lucia-auth/adapter-session-unstorage
yarn add @lucia-auth/adapter-session-unstorage
```

### Key prefixes

Key are defined as a combination of a prefix and an id so everything can be stored in a single storage instance. By default, sessions are stored as `session:<session_id>` and user-sessions relationships are stored as `user_sessions:<user_id>`.

## Usage

```ts
import { lucia } from "lucia";
import { unstorage } from "@lucia-auth/adapter-session-unstorage";
import { createStorage } from "unstorage";

const storage = createStorage()

const auth = lucia({
adapter: {
user: userAdapter, // any normal adapter for storing users/keys
session: unstorage(storage)
}
// ...
});
```
4 changes: 4 additions & 0 deletions documentation-v2/content/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ order: -1
### `@lucia-auth/adapter-session-redis`

- [`redis()`](/database-adapters/redis)

### `@lucia-auth/adapter-session-unstorage`

- [`redis()`](/database-adapters/unstorage)
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ We currently support the following database/ORM options:
- [PlanetScale serverless](/adapters/planetscale)
- [PostgreSQL](/adapters/postgresql)
- [Prisma](/adapters/prisma)
- [Redis](/adapters/redis)
- [SQLite](/adapters/sqlite)
- [Redis](/adapters/redis)

## Initialize Lucia

Expand Down
2 changes: 1 addition & 1 deletion documentation/content/main/start-here/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ We currently support the following database/ORM options:
- [PlanetScale serverless](/adapters/planetscale)
- [PostgreSQL](/adapters/postgresql)
- [Prisma](/adapters/prisma)
- [Redis](/adapters/redis)
- [SQLite](/adapters/sqlite)
- [Redis](/adapters/redis)

## Initialize Lucia

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ We currently support the following database/ORM options:
- [PlanetScale serverless](/adapters/planetscale)
- [PostgreSQL](/adapters/postgresql)
- [Prisma](/adapters/prisma)
- [Redis](/adapters/redis)
- [SQLite](/adapters/sqlite)
- [Redis](/adapters/redis)

## Initialize Lucia

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ We currently support the following database/ORM options:
- [PlanetScale serverless](/adapters/planetscale)
- [PostgreSQL](/adapters/postgresql)
- [Prisma](/adapters/prisma)
- [Redis](/adapters/redis)
- [SQLite](/adapters/sqlite)
- [Redis](/adapters/redis)

## Initialize Lucia

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ We currently support the following database/ORM options:
- [PlanetScale serverless](/adapters/planetscale)
- [PostgreSQL](/adapters/postgresql)
- [Prisma](/adapters/prisma)
- [Redis](/adapters/redis)
- [SQLite](/adapters/sqlite)
- [Redis](/adapters/redis)

## Initialize Lucia

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ We currently support the following database/ORM options:
- [PlanetScale serverless](/adapters/planetscale)
- [PostgreSQL](/adapters/postgresql)
- [Prisma](/adapters/prisma)
- [Redis](/adapters/redis)
- [SQLite](/adapters/sqlite)
- [Redis](/adapters/redis)

## Initialize Lucia

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ We currently support the following database/ORM options:
- [PlanetScale serverless](/adapters/planetscale)
- [PostgreSQL](/adapters/postgresql)
- [Prisma](/adapters/prisma)
- [Redis](/adapters/redis)
- [SQLite](/adapters/sqlite)
- [Redis](/adapters/redis)

## Initialize Lucia

Expand Down
2 changes: 1 addition & 1 deletion documentation/content/main/start-here/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Lucia is a sever-side library, so it does not provide any client side helpers.
- MySQL adapters: `@lucia-auth/adapter-mysql`
- PostgreSQL adapters: `@lucia-auth/adapter-postgresql`
- Prisma adapter: `@lucia-auth/adapter-prisma`
- Redis adapter: `@lucia-auth/adapter-session-redis`
- SQLite adapters: `@lucia-auth/adapter-sqlite`
- OAuth integration: `@lucia-auth/oauth`
- Tokens integration: `@lucia-auth/tokens`
- Redis adapter: `@lucia-auth/adapter-session-redis`
77 changes: 62 additions & 15 deletions documentation/content/main/start-here/username-password.nuxt.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const handleSubmit = async (e: Event) => {
});
navigateTo("/");
} catch (error) {
console.log(error);
console.error(error);
}
};
</script>
Expand Down Expand Up @@ -194,7 +194,7 @@ const handleSubmit = async (e: Event) => {
});
navigateTo("/");
} catch (error) {
console.log(error);
console.error(error);
}
};
</script>
Expand Down Expand Up @@ -294,11 +294,10 @@ In both the signup and login page, fetch the current user with `useFetch()`, and
<!-- pages/login.vue -->
<script lang="ts" setup>
const { data } = await useFetch("/api/user");
if (!data.value) throw createError("Failed to fetch data");
const user = data.value.user;
if (user) {
await navigateTo("/");
const { data, error } = await useFetch("/api/user");
if (error.value) throw createError("Failed to fetch data");
if (data?.value?.user) {
navigateTo("/");
}
// ...
</script>
Expand All @@ -316,19 +315,16 @@ Create `pages/index.vue`. This page will show the user's data. Redirect the user
<!-- pages/index.vue -->
<script lang="ts" setup>
const { data } = await useFetch("/api/user");
if (!data.value) throw createError("Failed to fetch data");
const user = data.value.user;
if (!user) await navigateTo("/login");
const { data, error } = await useFetch("/api/user");
if (error.value) throw createError(error.value.message);
if (!data.value?.user) navigateTo("/login");
const handleSubmit = async () => {
try {
await $fetch("/api/logout", {
method: "POST"
});
await $fetch("/api/logout", { method: "POST" });
navigateTo("/login");
} catch (error) {
console.log(error);
console.error(error);
}
};
</script>
Expand Down Expand Up @@ -363,3 +359,54 @@ export default defineEventHandler(async (event) => {
return null;
});
```

### Handle authentication with state and middleware

Using middlewares and composables is a really neat way to avoid duplicating your authorization logic.

Create a `useAuth` composable :

```ts
// composables/useAuth.ts
import type { User } from "lucia-auth";

export const useAuth = () => {
const { data, execute: fetchUser } = useFetch("/api/user", {
immediate: false
});
return { user: computed(() => data.value?.user), fetchUser };
};
```

Create an `auth` middleware :

```ts
// middlewares/auth.ts
export default defineNuxtRouteMiddleware(async () => {
const { user, fetchUser } = useAuth();
await fetchUser();
if (!user.value) {
// if there's no user found, navigate to the login page.
return navigateTo("/login");
}
});
```

Then in your vue files ...

```vue
<script lang="ts" setup>
definePageMeta({ middleware: "auth" });
const { user } = useAuth(); // Grab the user from your composable ...
</script>
<template>
<h2>Secret page</h2>
<p>
This page is protected by a nuxt middleware and can only be accessed by
authenticated users.
</p>
<pre class="code">{{ JSON.stringify(user, null, 2) }}</pre>
<NuxtLink to="/" class="button">Home</NuxtLink>
</template>
```
11 changes: 6 additions & 5 deletions documentation/content/oauth/start-here/getting-started.nuxt.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,22 @@ The state may not be returned depending on the provider, and it may return PKCE
```ts
// server/api/oauth/index.get.ts
export default defineEventHandler(async (event) => {
// const { provider } = getQuery(event); You can grab the provider like this /api/oauth?provider=github
const [url, state] = await githubAuth.getAuthorizationUrl();
setCookie(event, "oauth_state", state, {
path: "/",
maxAge: 60 * 60,
httpOnly: true,
secure: process.dev ? false : true
});
return await sendRedirect(event, url.toString(), 302);
return sendRedirect(event, url.toString(), 302);
});
```

Alternatively, you can embed the url from `getAuthorizationUrl()` inside an anchor tag.

```svelte
<a href={providerAuthorizationUrl}>Sign in with provider</a>
```html
<a :href="providerAuthorizationUrl">Sign in with provider</a>
```

> (red) Keep in mind while sending the result of `getAuthorizationUrl()` to the client is fine, **the provider oauth instance (`providerAuth`) should only be inside a server context**. You will leak your API keys if you import it in the client.
Expand All @@ -77,8 +78,8 @@ export default defineEventHandler(async (event) => {
const authRequest = auth.handleRequest(event);
// get code and state params from url
const query = getQuery(event);
const code = query.code?.toString() ?? null;
const state = query.state?.toString() ?? null;
const code = query.code?.toString();
const state = query.state?.toString();

// get stored state from cookies
const storedState = getCookie(event, "oauth_state");
Expand Down
2 changes: 1 addition & 1 deletion examples/nuxt/github-oauth/.npmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
shamefully-hoist=true
shamefully-hoist=true
2 changes: 1 addition & 1 deletion examples/nuxt/username-and-password/.npmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
shamefully-hoist=true
shamefully-hoist=true
4 changes: 4 additions & 0 deletions packages/adapter-session-unstorage/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/node_modules
/dist
.DS_Store
.env
Loading

1 comment on commit f9201e3

@vercel
Copy link

@vercel vercel bot commented on f9201e3 Jul 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.