diff --git a/.gitignore b/.gitignore
index ac7211b4033ca0cd15c21967204484e016ea6739..fe96ad8f818cc304550cc6aba7550865bd69589e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,5 @@ node_modules
 !.env.example
 vite.config.js.timestamp-*
 vite.config.ts.timestamp-*
+
+*.tgz
diff --git a/package.json b/package.json
index 6fb2ffba58fa05a982ba320da09ef3f116c222d5..f4ec1383600febcd493bf20a6911e22b7b350eb9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@arise/aidc-sveltekit",
-  "version": "0.4.1",
+  "version": "0.5.0",
   "type": "module",
   "scripts": {
     "dev": "vite dev",
@@ -21,9 +21,9 @@
       "types": "./dist/index.d.ts",
       "svelte": "./dist/index.js"
     },
-    "./default": {
-      "types": "./dist/default.d.ts",
-      "svelte": "./dist/default.js"
+    "./adapters/*": {
+      "types": "./dist/adapters/*.d.ts",
+      "svelte": "./dist/adapters/*.js"
     },
     "./lucia": {
       "types": "./dist/lucia.d.ts",
diff --git a/src/app.d.ts b/src/app.d.ts
index 20ce2461dc0ba6700925e64659eb2acf7ccf85c4..a979ab2be484be64a92551147722672394eeba01 100644
--- a/src/app.d.ts
+++ b/src/app.d.ts
@@ -1,10 +1,11 @@
 // See https://kit.svelte.dev/docs/types#app
 
 import type { Locals as AriseLocals } from "$lib/types.ts";
+import type { aidc } from "./auth.ts";
 
 declare global {
   namespace App {
-    interface Locals extends AriseLocals {}
+    interface Locals extends AriseLocals<typeof aidc> {}
   }
 }
 
diff --git a/src/auth.ts b/src/auth.ts
index acc1d46cfd412ad182cfa0048eb08cd85efe1e7d..9c41a2f67d2016d4a5c7548a207ca91b082f2ec6 100644
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -1,11 +1,13 @@
 import { env } from "$env/dynamic/private";
 import { AriseIdConnect } from "$lib/index.js";
-import { defaultLucia } from "$lib/default.js";
+import { sqliteMemoryAdapter } from "$lib/adapters/sqlite-memory.js";
 
 export const aidc = await AriseIdConnect.init({
   client_id: env.AIDC_CLIENT_ID!,
   client_secret: env.AIDC_CLIENT_SECRET!,
   scope: "openid offline profile",
-  wrapper: defaultLucia,
+  adapter: sqliteMemoryAdapter,
   // issuer: "http://localhost:4444/.well-known/openid-configuration"
 });
+
+sqliteMemoryAdapter.initDatabase();
diff --git a/src/lib/default.ts b/src/lib/adapters/sqlite-memory.ts
similarity index 80%
rename from src/lib/default.ts
rename to src/lib/adapters/sqlite-memory.ts
index 4ded99106d77d72229f558af3c524d117e68232b..19aa4ba1756538c8dcc2109d8897b22b2932a5f5 100644
--- a/src/lib/default.ts
+++ b/src/lib/adapters/sqlite-memory.ts
@@ -2,15 +2,18 @@ import { BetterSqlite3Adapter } from "@lucia-auth/adapter-sqlite";
 import sqlite from "better-sqlite3";
 import type { Database as SqLiteConnection } from "better-sqlite3";
 import {
-  LuciaWrapper,
+  LuciaAdapter,
   type DefaultSessionAttributes,
   type DefaultUserAttributes,
-} from "./lucia.js";
+} from "../lucia.js";
 import type { IdTokenClaims, UserinfoResponse } from "openid-client";
 
-class DefaultLucia<
+class SqliteMemoryLuciaAdapter<
   UserInfo extends Record<string, never> = Record<string, never>,
-> extends LuciaWrapper<Omit<DatabaseUser<UserInfo>, "id">> {
+> extends LuciaAdapter<
+  DefaultSessionAttributes,
+  Omit<DatabaseUser<UserInfo>, "id">
+> {
   db: SqLiteConnection;
 
   constructor() {
@@ -63,21 +66,8 @@ class DefaultLucia<
   }
 }
 
-export const defaultLucia = new DefaultLucia();
+export const sqliteMemoryAdapter = new SqliteMemoryLuciaAdapter();
 
-declare module "lucia" {
-  interface Register {
-    Lucia: typeof defaultLucia.lucia;
-    DatabaseUserAttributes: Omit<InternalUser, "id">;
-    DatabaseSessionAttributes: Omit<DatabaseSession, "id">;
-  }
-}
-
-interface InternalUser {
-  id: string;
-  subject: string;
-  claims: string;
-}
 export interface DatabaseUser<T extends Record<string, never>>
   extends DefaultUserAttributes {
   id: string;
diff --git a/src/lib/index.ts b/src/lib/index.ts
index 40e520e23ef2606c1442d122e82be17ed54a4c42..f35f9de02b973a6fb380d6df29192101f3db0eca 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -1,6 +1,6 @@
 import { redirect, type Handle, error, type RequestEvent } from "@sveltejs/kit";
 import { sequence } from "@sveltejs/kit/hooks";
-import { generateId } from "lucia";
+import { generateId, type Session } from "lucia";
 import type { Client, TokenSet } from "openid-client";
 import { Issuer, errors, generators } from "openid-client";
 import { SEE_OTHER } from "readable-http-codes";
@@ -12,14 +12,41 @@ import {
   addBasePath,
 } from "./helpers.js";
 import type { Config, CookieNames, Paths } from "./types.js";
+import type {
+  DefaultSessionAttributes,
+  DefaultUserAttributes,
+  LuciaAdapter,
+} from "./lucia.js";
+
+interface InternalUser {
+  id: string;
+  subject: string;
+  claims: string;
+}
+
+export interface DatabaseSession extends DefaultSessionAttributes {
+  id: string;
+  id_token: string;
+}
+
+declare module "lucia" {
+  interface Register {
+    Lucia: LuciaAdapter;
+    DatabaseUserAttributes: Omit<InternalUser, "id">;
+    DatabaseSessionAttributes: Omit<DatabaseSession, "id">;
+  }
+}
 
-export class AriseIdConnect {
+export class AriseIdConnect<
+  SessionAttributes extends DefaultSessionAttributes,
+  UserAttributes extends DefaultUserAttributes,
+> {
   readonly client: Client;
   readonly paths: Paths;
   protected cookieNames: CookieNames;
 
   constructor(
-    readonly config: Config,
+    readonly config: Config<SessionAttributes, UserAttributes>,
     issuer: Issuer,
   ) {
     this.client = new issuer.Client({
@@ -43,14 +70,16 @@ export class AriseIdConnect {
     };
   }
 
-  static async init(config: Config): Promise<AriseIdConnect> {
+  static async init<
+    Session extends DefaultSessionAttributes,
+    User extends DefaultUserAttributes,
+  >(config: Config<Session, User>): Promise<AriseIdConnect<Session, User>> {
     const issuer = await Issuer.discover(
       config.issuer ||
         "https://oidc.iiens.net/.well-known/openid-configuration",
     );
     const aidc = new AriseIdConnect(config, issuer);
 
-    await aidc.config.wrapper.initDatabase();
     return aidc;
   }
 
@@ -118,14 +147,15 @@ export class AriseIdConnect {
       redirect(SEE_OTHER, this.paths.home);
     }
 
-    const { lucia } = this.config.wrapper;
+    const { adapter: lucia } = this.config;
 
     const { session } = await lucia.validateSession(event.locals.session.id);
+    const typedSession = session as SessionAttributes | null;
 
     const postLogoutRedirectURI = new URL(this.paths.logoutCallback, event.url);
     const endSessionUrl = this.client.endSessionUrl({
       post_logout_redirect_uri: postLogoutRedirectURI.toString(),
-      id_token_hint: session?.id_token,
+      id_token_hint: typedSession?.id_token,
     });
 
     if (this.config.on?.logout) {
@@ -148,7 +178,7 @@ export class AriseIdConnect {
 
     const claims = tokenSet.claims();
     const { sub } = claims;
-    const { wrapper } = this.config;
+    const { adapter: wrapper } = this.config;
     const existingUserId = await wrapper.getUserId(sub);
 
     let userId = existingUserId ?? generateId(15);
@@ -157,10 +187,10 @@ export class AriseIdConnect {
       userId = await wrapper.createUser(sub, userId, claims);
     }
 
-    const session = await wrapper.lucia.createSession(userId, {
+    const session = await wrapper.createSession(userId, {
       id_token: tokenSet.id_token,
     });
-    setLuciaCookie(event, wrapper.lucia.createSessionCookie(session.id));
+    setLuciaCookie(event, wrapper.createSessionCookie(session.id));
 
     if (this.config.on?.login) {
       return this.config.on.login(event, tokenSet.claims());
@@ -170,7 +200,7 @@ export class AriseIdConnect {
   };
 
   protected sessionHandler: Handle = async ({ event, resolve }) => {
-    const { lucia } = this.config.wrapper;
+    const { adapter: lucia } = this.config;
 
     const sessionId = event.cookies.get(lucia.sessionCookieName);
     if (!sessionId) {
@@ -189,7 +219,7 @@ export class AriseIdConnect {
     }
 
     event.locals.user = user;
-    event.locals.session = session;
+    event.locals.session = session as (SessionAttributes & Session) | null;
     return resolve(event);
   };
 
diff --git a/src/lib/lucia.ts b/src/lib/lucia.ts
index 79a2f7172b09bc00453559de9a3e2c30bcdb6dea..2763c0c4439ecc06b7fcb7ca083bf0b87b2ab1e0 100644
--- a/src/lib/lucia.ts
+++ b/src/lib/lucia.ts
@@ -18,18 +18,16 @@ export interface DatabaseSession {
   id_token: string;
 }
 
-export type DefaultUserAttributes = Record<string, unknown>;
+export type DefaultUserAttributes = Record<never, never>;
 export interface DefaultSessionAttributes {
   id_token: string;
 }
 
-export abstract class LuciaWrapper<
-  _UserAttributes extends DefaultUserAttributes = DefaultUserAttributes,
+export abstract class LuciaAdapter<
   _SessionAttributes extends
     DefaultSessionAttributes = DefaultSessionAttributes,
-> {
-  lucia: Lucia<_SessionAttributes, _UserAttributes>;
-
+  _UserAttributes extends DefaultUserAttributes = DefaultUserAttributes,
+> extends Lucia<_SessionAttributes, _UserAttributes> {
   constructor(
     adapter: Adapter,
     options?: {
@@ -43,26 +41,17 @@ export abstract class LuciaWrapper<
       ) => _UserAttributes;
     },
   ) {
-    this.lucia = new Lucia(adapter, {
+    super(adapter, {
       sessionCookie: {
         attributes: {
           secure: !dev,
         },
         name: "aidc_session",
       },
-      getUserAttributes() {
-        return {};
-      },
-      getSessionAttributes(attributes) {
-        return {
-          id_token: attributes.id_token,
-        };
-      },
       ...options,
     });
   }
 
-  initDatabase(): MaybePromise<void> {}
   abstract getUserId(subject: string): MaybePromise<string | undefined>;
   abstract createUser(
     subject: string,
diff --git a/src/lib/types.ts b/src/lib/types.ts
index d664534c3311f6bcfc8b0d098e975acc66d310a0..44d55e4c93167a409a5981e2ad8967e32fb10de2 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -1,8 +1,17 @@
 import type { MaybePromise, RequestEvent } from "@sveltejs/kit";
 import type { ClientMetadata, UserinfoResponse, errors } from "openid-client";
-import type { LuciaWrapper } from "./lucia.js";
+import type {
+  DefaultSessionAttributes,
+  DefaultUserAttributes,
+  LuciaAdapter,
+} from "./lucia.js";
+import { AriseIdConnect } from "./index.js";
+import type { Session, User } from "lucia";
 
-export interface Config extends ClientMetadata {
+export interface Config<
+  Session extends DefaultSessionAttributes,
+  User extends DefaultUserAttributes,
+> extends ClientMetadata {
   client_secret: string;
   scope: string;
   issuer?: string;
@@ -11,7 +20,7 @@ export interface Config extends ClientMetadata {
     login?: (event: RequestEvent, userInfo: UserinfoResponse) => never;
     logout?: (event: RequestEvent) => MaybePromise<void>;
   };
-  wrapper: LuciaWrapper;
+  adapter: LuciaAdapter<Session, User>;
   cookieNames?: Partial<CookieNames>;
   paths?: Partial<Paths>;
 }
@@ -29,8 +38,22 @@ export type CookieNames = {
   oauthCodeVerifier: string;
 };
 
-export interface Locals {
-  user: import("lucia").User | null;
-  session: import("lucia").Session | null;
+export interface Locals<
+  T extends AriseIdConnect<DefaultSessionAttributes, DefaultUserAttributes>,
+> {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  user:
+    | ((T extends AriseIdConnect<any, infer UserAttributes>
+        ? UserAttributes
+        : never) &
+        User)
+    | null;
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  session:
+    | ((T extends AriseIdConnect<infer SessionAttributes, any>
+        ? SessionAttributes
+        : never) &
+        Session)
+    | null;
   authPaths: Paths;
 }