diff --git a/package.json b/package.json
index f4ec1383600febcd493bf20a6911e22b7b350eb9..16142e8280803f66a40f8d862132a98cbd79ec67 100644
--- a/package.json
+++ b/package.json
@@ -39,10 +39,12 @@
     "!dist/**/*.spec.*"
   ],
   "peerDependencies": {
-    "@lucia-auth/adapter-sqlite": "^3.0.1",
+    "@lucia-auth/adapter-drizzle": "1",
+    "@lucia-auth/adapter-sqlite": "3",
     "@sveltejs/kit": "2",
-    "better-sqlite3": "^9.4.0 || ^11.0.0",
-    "lucia": "^3.2.0",
+    "better-sqlite3": "8 || 9",
+    "drizzle-orm": "0.33",
+    "lucia": "3",
     "svelte": "4"
   },
   "peerDependenciesMeta": {
@@ -51,6 +53,12 @@
     },
     "better-sqlite3": {
       "optional": true
+    },
+    "@lucia-auth/adapter-drizzle": {
+      "optional": true
+    },
+    "drizzle-orm": {
+      "optional": true
     }
   },
   "devDependencies": {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f97fa93d687935247c5b356d337714aac330f4bd..e1fcdc53c39f8d053c54c27e468bd19639dfd270 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,9 +8,15 @@ importers:
 
   .:
     dependencies:
+      '@lucia-auth/adapter-drizzle':
+        specifier: '1'
+        version: 1.1.0(drizzle-orm@0.33.0(@types/better-sqlite3@7.6.9)(better-sqlite3@9.6.0))(lucia@3.2.0)
       better-sqlite3:
-        specifier: ^9.4.0 || ^11.0.0
-        version: 11.0.0
+        specifier: 8 || 9
+        version: 9.6.0
+      drizzle-orm:
+        specifier: '0.33'
+        version: 0.33.0(@types/better-sqlite3@7.6.9)(better-sqlite3@9.6.0)
       openid-client:
         specifier: ^5.6.4
         version: 5.6.4
@@ -20,7 +26,7 @@ importers:
     devDependencies:
       '@lucia-auth/adapter-sqlite':
         specifier: ^3.0.1
-        version: 3.0.1(better-sqlite3@11.0.0)(lucia@3.2.0)
+        version: 3.0.1(better-sqlite3@9.6.0)(lucia@3.2.0)
       '@sveltejs/adapter-node':
         specifier: ^5.0.0
         version: 5.0.1(@sveltejs/kit@2.5.0(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.10)(vite@5.0.12(@types/node@20.14.0)))(svelte@4.2.10)(vite@5.0.12(@types/node@20.14.0)))
@@ -308,6 +314,12 @@ packages:
   '@jridgewell/trace-mapping@0.3.25':
     resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
 
+  '@lucia-auth/adapter-drizzle@1.1.0':
+    resolution: {integrity: sha512-iCTnZWvfI5lLZOdUHZYiXA1jaspIFEeo2extLxQ3DjP3uOVys7IPwBi7zezLIRu9dhro4H4Kji+7gSYyjcef2A==}
+    peerDependencies:
+      drizzle-orm: '>= 0.29 <1'
+      lucia: 3.x
+
   '@lucia-auth/adapter-sqlite@3.0.1':
     resolution: {integrity: sha512-bzr8+HALrbiYMb/+oL1SAnjbgFqlPs/Kj4lO57t/VvbXzmbpQEKk5Nv6hMpvWSkGAR9LbxYeQAtecikpKZVB0w==}
     peerDependencies:
@@ -789,8 +801,8 @@ packages:
   base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
-  better-sqlite3@11.0.0:
-    resolution: {integrity: sha512-1NnNhmT3EZTsKtofJlMox1jkMxdedILury74PwUbQBjWgo4tL4kf7uTAjU55mgQwjdzqakSTjkf+E1imrFwjnA==}
+  better-sqlite3@9.6.0:
+    resolution: {integrity: sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==}
 
   binary-extensions@2.2.0:
     resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
@@ -930,6 +942,95 @@ packages:
   devalue@4.3.2:
     resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==}
 
+  drizzle-orm@0.33.0:
+    resolution: {integrity: sha512-SHy72R2Rdkz0LEq0PSG/IdvnT3nGiWuRk+2tXZQ90GVq/XQhpCzu/EFT3V2rox+w8MlkBQxifF8pCStNYnERfA==}
+    peerDependencies:
+      '@aws-sdk/client-rds-data': '>=3'
+      '@cloudflare/workers-types': '>=3'
+      '@electric-sql/pglite': '>=0.1.1'
+      '@libsql/client': '*'
+      '@neondatabase/serverless': '>=0.1'
+      '@op-engineering/op-sqlite': '>=2'
+      '@opentelemetry/api': ^1.4.1
+      '@planetscale/database': '>=1'
+      '@prisma/client': '*'
+      '@tidbcloud/serverless': '*'
+      '@types/better-sqlite3': '*'
+      '@types/pg': '*'
+      '@types/react': '>=18'
+      '@types/sql.js': '*'
+      '@vercel/postgres': '>=0.8.0'
+      '@xata.io/client': '*'
+      better-sqlite3: '>=7'
+      bun-types: '*'
+      expo-sqlite: '>=13.2.0'
+      knex: '*'
+      kysely: '*'
+      mysql2: '>=2'
+      pg: '>=8'
+      postgres: '>=3'
+      prisma: '*'
+      react: '>=18'
+      sql.js: '>=1'
+      sqlite3: '>=5'
+    peerDependenciesMeta:
+      '@aws-sdk/client-rds-data':
+        optional: true
+      '@cloudflare/workers-types':
+        optional: true
+      '@electric-sql/pglite':
+        optional: true
+      '@libsql/client':
+        optional: true
+      '@neondatabase/serverless':
+        optional: true
+      '@op-engineering/op-sqlite':
+        optional: true
+      '@opentelemetry/api':
+        optional: true
+      '@planetscale/database':
+        optional: true
+      '@prisma/client':
+        optional: true
+      '@tidbcloud/serverless':
+        optional: true
+      '@types/better-sqlite3':
+        optional: true
+      '@types/pg':
+        optional: true
+      '@types/react':
+        optional: true
+      '@types/sql.js':
+        optional: true
+      '@vercel/postgres':
+        optional: true
+      '@xata.io/client':
+        optional: true
+      better-sqlite3:
+        optional: true
+      bun-types:
+        optional: true
+      expo-sqlite:
+        optional: true
+      knex:
+        optional: true
+      kysely:
+        optional: true
+      mysql2:
+        optional: true
+      pg:
+        optional: true
+      postgres:
+        optional: true
+      prisma:
+        optional: true
+      react:
+        optional: true
+      sql.js:
+        optional: true
+      sqlite3:
+        optional: true
+
   end-of-stream@1.4.4:
     resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
 
@@ -2005,11 +2106,16 @@ snapshots:
       '@jridgewell/resolve-uri': 3.1.1
       '@jridgewell/sourcemap-codec': 1.5.0
 
-  '@lucia-auth/adapter-sqlite@3.0.1(better-sqlite3@11.0.0)(lucia@3.2.0)':
+  '@lucia-auth/adapter-drizzle@1.1.0(drizzle-orm@0.33.0(@types/better-sqlite3@7.6.9)(better-sqlite3@9.6.0))(lucia@3.2.0)':
+    dependencies:
+      drizzle-orm: 0.33.0(@types/better-sqlite3@7.6.9)(better-sqlite3@9.6.0)
+      lucia: 3.2.0
+
+  '@lucia-auth/adapter-sqlite@3.0.1(better-sqlite3@9.6.0)(lucia@3.2.0)':
     dependencies:
       lucia: 3.2.0
     optionalDependencies:
-      better-sqlite3: 11.0.0
+      better-sqlite3: 9.6.0
 
   '@node-rs/argon2-android-arm-eabi@1.7.0':
     optional: true
@@ -2470,7 +2576,7 @@ snapshots:
 
   base64-js@1.5.1: {}
 
-  better-sqlite3@11.0.0:
+  better-sqlite3@9.6.0:
     dependencies:
       bindings: 1.5.0
       prebuild-install: 7.1.1
@@ -2601,6 +2707,11 @@ snapshots:
 
   devalue@4.3.2: {}
 
+  drizzle-orm@0.33.0(@types/better-sqlite3@7.6.9)(better-sqlite3@9.6.0):
+    optionalDependencies:
+      '@types/better-sqlite3': 7.6.9
+      better-sqlite3: 9.6.0
+
   end-of-stream@1.4.4:
     dependencies:
       once: 1.4.0
diff --git a/src/app.d.ts b/src/app.d.ts
index d5b5ecf20e7be0f9ceaf79d2abbfdc54e65bf651..663d8612f2db4a8322092fd200ee1261ad5ca492 100644
--- a/src/app.d.ts
+++ b/src/app.d.ts
@@ -1,12 +1,9 @@
 // See https://kit.svelte.dev/docs/types#app
-import { Locals as ILocals } from "$lib/types.ts";
+import { AIDCLocals } from "@arise/aidc-sveltekit/types.ts";
 
 declare global {
   namespace App {
-    interface Locals extends ILocals {
-      user: import("lucia").User | null;
-      session: import("lucia").Session | null;
-    }
+    interface Locals extends AIDCLocals {}
   }
 }
 
diff --git a/src/auth.ts b/src/auth.ts
index 9c41a2f67d2016d4a5c7548a207ca91b082f2ec6..f91758a65a5444e2ba9d6e68b8d2b26e95d1edce 100644
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -1,13 +1,13 @@
 import { env } from "$env/dynamic/private";
-import { AriseIdConnect } from "$lib/index.js";
-import { sqliteMemoryAdapter } from "$lib/adapters/sqlite-memory.js";
+import { AriseIdConnect } from "@arise/aidc-sveltekit";
+import { adapter } from "@arise/aidc-sveltekit/adapters/sqlite-memory-drizzle.js";
 
 export const aidc = await AriseIdConnect.init({
   client_id: env.AIDC_CLIENT_ID!,
   client_secret: env.AIDC_CLIENT_SECRET!,
   scope: "openid offline profile",
-  adapter: sqliteMemoryAdapter,
+  adapter,
   // issuer: "http://localhost:4444/.well-known/openid-configuration"
 });
 
-sqliteMemoryAdapter.initDatabase();
+adapter.initDatabase();
diff --git a/src/lib/adapters/abstract.ts b/src/lib/adapters/abstract.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc74134d18139680f2ae801f6e85f65c438bd0fa
--- /dev/null
+++ b/src/lib/adapters/abstract.ts
@@ -0,0 +1,54 @@
+import { dev } from "$app/environment";
+import type { Empty } from "$lib/types.js";
+import type { MaybePromise } from "@sveltejs/kit";
+import type {
+  Adapter,
+  RegisteredDatabaseSessionAttributes,
+  RegisteredDatabaseUserAttributes,
+  Session,
+  SessionCookieOptions,
+  TimeSpan,
+  UserId,
+} from "lucia";
+import { Lucia } from "lucia";
+import type { TokenSet, UserinfoResponse } from "openid-client";
+
+export abstract class LuciaAdapter<
+  _SessionAttributes extends Empty = Empty,
+  _UserAttributes extends Empty = Empty,
+> extends Lucia<_SessionAttributes, _UserAttributes> {
+  constructor(
+    adapter: Adapter,
+    options?: {
+      sessionExpiresIn?: TimeSpan;
+      sessionCookie?: SessionCookieOptions;
+      getSessionAttributes?: (
+        databaseSessionAttributes: RegisteredDatabaseSessionAttributes,
+      ) => _SessionAttributes;
+      getUserAttributes?: (
+        databaseUserAttributes: RegisteredDatabaseUserAttributes,
+      ) => _UserAttributes;
+    },
+  ) {
+    super(adapter, {
+      sessionCookie: {
+        attributes: {
+          secure: !dev,
+        },
+        name: "aidc_session",
+      },
+      ...options,
+    });
+  }
+
+  abstract getUserId(subject: string): MaybePromise<string | undefined>;
+  abstract newUser(
+    subject: string,
+    userId: string,
+    claims: UserinfoResponse,
+  ): MaybePromise<string>;
+  abstract newSession(userId: UserId, tokenSet: TokenSet): Promise<Session>;
+  abstract getIdToken(
+    session: Session | null,
+  ): MaybePromise<string | undefined>;
+}
diff --git a/src/lib/adapters/sqlite-memory-drizzle.ts b/src/lib/adapters/sqlite-memory-drizzle.ts
new file mode 100644
index 0000000000000000000000000000000000000000..26d923ed7f51f8d1d4607e06cb6b58d75db16799
--- /dev/null
+++ b/src/lib/adapters/sqlite-memory-drizzle.ts
@@ -0,0 +1,113 @@
+import { LuciaAdapter } from "./abstract.js";
+import type { IdTokenClaims, TokenSet, UserinfoResponse } from "openid-client";
+import { DrizzleSQLiteAdapter } from "@lucia-auth/adapter-drizzle";
+import sqlite from "better-sqlite3";
+import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
+import {
+  drizzle,
+  type BetterSQLite3Database,
+} from "drizzle-orm/better-sqlite3";
+import { eq, sql } from "drizzle-orm";
+import type { Session, UserId } from "lucia";
+import type { MaybePromise } from "@sveltejs/kit";
+import type { Empty } from "../types.js";
+
+const userTable = sqliteTable("user", {
+  id: text("id").primaryKey(),
+  subject: text("subject").notNull().unique(),
+  claims: text("claims").notNull(),
+});
+const sessionTable = sqliteTable("session", {
+  id: text("id").primaryKey(),
+  userId: text("user_id")
+    .notNull()
+    .references(() => userTable.id),
+  expiresAt: integer("expires_at").notNull(),
+  idToken: text("id_token").notNull(),
+});
+
+class SqliteDrizzleAdapter extends LuciaAdapter<Empty, User> {
+  db: BetterSQLite3Database;
+
+  constructor() {
+    const sqliteDB = sqlite(":memory:");
+    const db = drizzle(sqliteDB);
+
+    const adapter = new DrizzleSQLiteAdapter(db, sessionTable, userTable);
+
+    super(adapter, {
+      getUserAttributes(attributes) {
+        return {
+          subject: attributes.subject,
+          claims: JSON.parse(attributes.claims),
+        };
+      },
+    });
+
+    this.db = db;
+  }
+
+  initDatabase() {
+    this.db.run(sql`CREATE TABLE IF NOT EXISTS user (
+      id TEXT NOT NULL PRIMARY KEY,
+      subject TEXT NOT NULL UNIQUE,
+      claims TEXT NOT NULL
+    )`);
+
+    this.db.run(sql`CREATE TABLE IF NOT EXISTS session (
+      id TEXT NOT NULL PRIMARY KEY,
+      expires_at INTEGER NOT NULL,
+      user_id TEXT NOT NULL,
+      id_token TEXT NOT NULL,
+      FOREIGN KEY (user_id) REFERENCES user(id)
+    )`);
+  }
+
+  async getUserId(subject: string) {
+    const user = await this.db
+      .select()
+      .from(userTable)
+      .where((user) => eq(user.subject, subject));
+
+    return user.at(0)?.id;
+  }
+
+  async newUser(subject: string, userId: string, claims: IdTokenClaims) {
+    await this.db
+      .insert(userTable)
+      .values([{ id: userId, subject, claims: JSON.stringify(claims) }]);
+    return userId;
+  }
+
+  newSession(
+    userId: UserId,
+    tokenSet: TokenSet & { id_token: string },
+  ): Promise<Session> {
+    return this.createSession(userId, {
+      idToken: tokenSet.id_token,
+    });
+  }
+
+  getIdToken(session: Session): MaybePromise<string | undefined> {
+    return session.idToken;
+  }
+}
+
+export const adapter = new SqliteDrizzleAdapter();
+
+interface User {
+  subject: string;
+  claims: UserinfoResponse;
+}
+interface ISession {
+  idToken: string;
+}
+
+declare module "@arise/aidc-sveltekit" {
+  interface Types {
+    User: User;
+    Session: ISession;
+    DatabaseUserAttributes: typeof userTable.$inferSelect;
+    DatabaseSessionAttributes: typeof sessionTable.$inferSelect;
+  }
+}
diff --git a/src/lib/adapters/sqlite-memory.ts b/src/lib/adapters/sqlite-memory.ts
index b9c63ed4153f6f664bbf3f0244a9184842ec1606..4114ce1a5122bd3b67f829f631b6c8b45b7b9df7 100644
--- a/src/lib/adapters/sqlite-memory.ts
+++ b/src/lib/adapters/sqlite-memory.ts
@@ -1,18 +1,14 @@
 import { BetterSqlite3Adapter } from "@lucia-auth/adapter-sqlite";
 import sqlite from "better-sqlite3";
 import type { Database as SqLiteConnection } from "better-sqlite3";
-import {
-  LuciaAdapter,
-  type DefaultSessionAttributes,
-  type DefaultUserAttributes,
-} from "../lucia.js";
-import type { IdTokenClaims, UserinfoResponse } from "openid-client";
+import { LuciaAdapter } from "./abstract.js";
+import type { IdTokenClaims, TokenSet, UserinfoResponse } from "openid-client";
+import type { UserId, Session } from "lucia";
+import type { MaybePromise } from "@sveltejs/kit";
 
-class SqliteMemoryLuciaAdapter<
-  UserInfo extends Record<string, never> = Record<string, never>,
-> extends LuciaAdapter<
-  DefaultSessionAttributes,
-  Omit<DatabaseUser<UserInfo>, "id">
+class SqliteMemoryLuciaAdapter extends LuciaAdapter<
+  Record<never, never>,
+  User
 > {
   db: SqLiteConnection;
 
@@ -58,30 +54,49 @@ class SqliteMemoryLuciaAdapter<
     return user?.id;
   }
 
-  createUser(subject: string, userId: string, claims: IdTokenClaims) {
+  newUser(subject: string, userId: string, claims: IdTokenClaims) {
     this.db
       .prepare("INSERT INTO user (id, subject, claims) VALUES (?, ?, ?)")
       .run(userId, subject, JSON.stringify(claims));
     return userId;
   }
+
+  newSession(
+    userId: UserId,
+    tokenSet: TokenSet & { id_token: string },
+  ): Promise<Session> {
+    return this.createSession(userId, {
+      idToken: tokenSet.id_token,
+    });
+  }
+
+  getIdToken(session: Session): MaybePromise<string | undefined> {
+    return session.idToken;
+  }
 }
 
-export const sqliteMemoryAdapter = new SqliteMemoryLuciaAdapter();
+export const adapter = new SqliteMemoryLuciaAdapter();
 
-export interface DatabaseUser<T extends Record<string, never>>
-  extends DefaultUserAttributes {
+interface DatabaseUser {
   id: string;
   subject: string;
-  claims: UserinfoResponse<T>;
+  claims: string;
 }
-export interface DatabaseSession extends DefaultSessionAttributes {
-  id: string;
-  id_token: string;
+interface User {
+  subject: string;
+  claims: UserinfoResponse;
+}
+interface DatabaseSession {
+  idToken: string;
 }
 
-declare module "../index.js" {
-  interface Foo {
-    Session: DefaultSessionAttributes;
-    User: Omit<DatabaseUser<Record<never, never>>, "id">;
+declare module "@arise/aidc-sveltekit" {
+  interface Types {
+    Session: DatabaseSession;
+    User: User;
+    // À supprimer si vous copiez-collez ce code
+    // @ts-expect-error - Tous les adapters redéfinissent ces attributs
+    DatabaseSessionAttributes: DatabaseSession;
+    DatabaseUserAttributes: DatabaseUser;
   }
 }
diff --git a/src/lib/aidc_wip.ts b/src/lib/aidc_wip.ts
new file mode 100644
index 0000000000000000000000000000000000000000..71975f481935dcd212c29abbbdbc9c133007f374
--- /dev/null
+++ b/src/lib/aidc_wip.ts
@@ -0,0 +1,14 @@
+// const test = "user:id email openid" as const;
+
+// const groups = {
+//   email: ["user:email"],
+// } as const satisfies Record<string, string[]>;
+
+// type Split<T> = T extends `${infer F} ${infer R}` ? F | Split<R> : T;
+
+// type Obj<T extends string> = {
+//   [K in T]: string;
+// };
+
+// type X = Split<typeof test>;
+// type Y = Obj<X>;
diff --git a/src/lib/handler.ts b/src/lib/handler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..276ca3f0adad340e5d3cc81dfd1cc4a510dfb55a
--- /dev/null
+++ b/src/lib/handler.ts
@@ -0,0 +1,215 @@
+import { redirect, type Handle, error, type RequestEvent } from "@sveltejs/kit";
+import { sequence } from "@sveltejs/kit/hooks";
+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";
+import {
+  method,
+  route,
+  setLuciaCookie,
+  setTempCookie,
+  addBasePath,
+} from "./helpers.js";
+import type { Config, CookieNames, Empty, Paths } from "./types.js";
+
+export class AriseIdConnect<
+  SessionAttributes extends Empty,
+  UserAttributes extends Empty,
+> {
+  readonly client: Client;
+  readonly paths: Paths;
+  protected cookieNames: CookieNames;
+
+  constructor(
+    readonly config: Config<SessionAttributes, UserAttributes>,
+    issuer: Issuer,
+  ) {
+    this.client = new issuer.Client({
+      ...config,
+      grant_types: ["authorization_code", "refresh_token"],
+      response_types: ["code", "id_token"],
+    });
+
+    this.cookieNames = {
+      oauthState: config.cookieNames?.oauthState ?? "aidc_state",
+      oauthCodeVerifier:
+        config.cookieNames?.oauthCodeVerifier ?? "aidc_code_verifier",
+    };
+
+    this.paths = {
+      home: addBasePath(config.paths?.home ?? "/"),
+      callback: addBasePath(config.paths?.callback ?? "/auth/callback"),
+      logoutCallback: addBasePath(config.paths?.logoutCallback ?? "/"),
+      login: addBasePath(config.paths?.login ?? "/auth/login"),
+      logout: addBasePath(config.paths?.logout ?? "/auth/logout"),
+    };
+  }
+
+  static async init<
+    Session extends Record<never, never>,
+    User extends Record<never, never>,
+  >(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);
+
+    return aidc;
+  }
+
+  protected async getTokens(event: RequestEvent): Promise<TokenSet> {
+    const state = event.cookies.get(this.cookieNames.oauthState) ?? null;
+    const codeVerifier =
+      event.cookies.get(this.cookieNames.oauthCodeVerifier) ?? null;
+    const params = this.client.callbackParams(event.url.toString());
+
+    if (!codeVerifier || !state) {
+      redirect(SEE_OTHER, this.paths.login);
+    }
+
+    const redirectURI = new URL(this.paths.callback, event.url).toString();
+    try {
+      const tokenSet = await this.client.callback(redirectURI, params, {
+        code_verifier: codeVerifier,
+        state: state,
+      });
+
+      return tokenSet;
+    } catch (err) {
+      if (err instanceof errors.OPError) {
+        if (err.error === "access_denied") {
+          if (this.config.on?.accessDenied) {
+            return this.config.on.accessDenied(event, err);
+          }
+
+          redirect(SEE_OTHER, this.paths.home);
+        }
+
+        error(500, err);
+      } else if (err instanceof errors.RPError) {
+        error(500, err);
+      }
+
+      throw err;
+    }
+  }
+
+  protected loginHandler: Handle = ({ event }) => {
+    const codeVerifier = generators.codeVerifier();
+    const state = generators.state();
+    // const nonce = generators.nonce();
+
+    const redirectURI = new URL(this.paths.callback, event.url).toString();
+    const authorizationUrl = this.client.authorizationUrl({
+      response_type: "code",
+      scope: this.config.scope,
+      code_challenge: generators.codeChallenge(codeVerifier),
+      code_challenge_method: "S256",
+      redirect_uri: redirectURI,
+      state,
+      // nonce,
+    });
+
+    setTempCookie(event, this.cookieNames.oauthState, state);
+    setTempCookie(event, this.cookieNames.oauthCodeVerifier, codeVerifier);
+
+    redirect(SEE_OTHER, authorizationUrl);
+  };
+
+  protected logoutHandler: Handle = async ({ event }) => {
+    if (!event.locals.session) {
+      redirect(SEE_OTHER, this.paths.home);
+    }
+
+    const { adapter: lucia } = this.config;
+
+    const { session } = await lucia.validateSession(event.locals.session.id);
+
+    const postLogoutRedirectURI = new URL(this.paths.logoutCallback, event.url);
+    const idToken = await lucia.getIdToken(session);
+    const endSessionUrl = this.client.endSessionUrl({
+      post_logout_redirect_uri: postLogoutRedirectURI.toString(),
+      id_token_hint: idToken,
+    });
+
+    if (this.config.on?.logout) {
+      await this.config.on.logout(event);
+    }
+
+    await lucia.invalidateSession(event.locals.session.id);
+
+    setLuciaCookie(event, lucia.createBlankSessionCookie());
+
+    redirect(SEE_OTHER, endSessionUrl);
+  };
+
+  protected callbackHandler: Handle = async ({ event }) => {
+    const tokenSet = await this.getTokens(event);
+
+    if (!tokenSet.id_token) {
+      throw new Error("No id_token in tokenSet");
+    }
+
+    const claims = tokenSet.claims();
+    const { sub } = claims;
+    const lucia = this.config.adapter;
+    const existingUserId = await lucia.getUserId(sub);
+
+    let userId = existingUserId ?? generateId(15);
+
+    if (existingUserId === undefined) {
+      userId = await lucia.newUser(sub, userId, claims);
+    }
+
+    const session = await lucia.newSession(userId, tokenSet);
+    setLuciaCookie(event, lucia.createSessionCookie(session.id));
+
+    if (this.config.on?.login) {
+      return this.config.on.login(event, tokenSet.claims());
+    }
+
+    redirect(SEE_OTHER, this.paths.home);
+  };
+
+  protected sessionHandler: Handle = async ({ event, resolve }) => {
+    const lucia = this.config.adapter;
+
+    const sessionId = event.cookies.get(lucia.sessionCookieName);
+    if (!sessionId) {
+      event.locals.user = null;
+      event.locals.session = null;
+      return resolve(event);
+    }
+
+    const { session, user } = await lucia.validateSession(sessionId);
+
+    if (session && session.fresh) {
+      setLuciaCookie(event, lucia.createSessionCookie(session.id));
+    }
+    if (!session) {
+      setLuciaCookie(event, lucia.createBlankSessionCookie());
+    }
+
+    event.locals.user = user;
+    event.locals.session = session as (SessionAttributes & Session) | null;
+    return resolve(event);
+  };
+
+  protected miscHandler: Handle = ({ event, resolve }) => {
+    event.locals.authPaths = this.paths;
+
+    return resolve(event);
+  };
+
+  handler(): Handle {
+    return sequence(
+      this.miscHandler.bind(this),
+      route(this.paths.login, this.loginHandler.bind(this)),
+      route(this.paths.logout, method("POST", this.logoutHandler.bind(this))),
+      route(this.paths.callback, this.callbackHandler.bind(this)),
+      this.sessionHandler.bind(this),
+    );
+  }
+}
diff --git a/src/lib/index.ts b/src/lib/index.ts
index f065c6b849fd1cd0a35ed3bfcaeddfe323ecc243..46bb6a26496210f063798da155a11e792fcb9a55 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -1,254 +1,50 @@
-import { redirect, type Handle, error, type RequestEvent } from "@sveltejs/kit";
-import { sequence } from "@sveltejs/kit/hooks";
-import { generateId, Lucia, 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";
-import {
-  method,
-  route,
-  setLuciaCookie,
-  setTempCookie,
-  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;
-}
+import { Lucia } from "lucia";
+import type { Empty } from "./types.js";
+export { AriseIdConnect } from "./handler.js";
 
 // eslint-disable-next-line @typescript-eslint/no-empty-object-type
-export interface Foo {}
+export interface Types {}
 
-interface Bar<
-  User extends Record<never, never>,
-  Session extends Record<never, never>,
+interface RegisterTypes<
+  Session extends Empty,
+  User extends Empty,
+  DatabaseSessionAttributes extends Empty,
+  DatabaseUserAttributes extends Empty,
 > {
-  User: User;
   Session: Session;
+  User: User;
+  DatabaseSessionAttributes: DatabaseSessionAttributes;
+  DatabaseUserAttributes: DatabaseUserAttributes;
 }
 
 declare module "lucia" {
   interface Register {
-    Lucia: Foo extends Bar<infer _User, infer _Session>
+    Lucia: Types extends RegisterTypes<
+      infer _Session,
+      infer _User,
+      Empty,
+      Empty
+    >
       ? Lucia<_Session, _User>
       : never;
-    DatabaseUserAttributes: Omit<InternalUser, "id">;
-    DatabaseSessionAttributes: Omit<DatabaseSession, "id">;
-  }
-}
-
-export class AriseIdConnect<
-  SessionAttributes extends DefaultSessionAttributes,
-  UserAttributes extends DefaultUserAttributes,
-> {
-  readonly client: Client;
-  readonly paths: Paths;
-  protected cookieNames: CookieNames;
-
-  constructor(
-    readonly config: Config<SessionAttributes, UserAttributes>,
-    issuer: Issuer,
-  ) {
-    this.client = new issuer.Client({
-      ...config,
-      grant_types: ["authorization_code", "refresh_token"],
-      response_types: ["code", "id_token"],
-    });
-
-    this.cookieNames = {
-      oauthState: config.cookieNames?.oauthState ?? "aidc_state",
-      oauthCodeVerifier:
-        config.cookieNames?.oauthCodeVerifier ?? "aidc_code_verifier",
-    };
-
-    this.paths = {
-      home: addBasePath(config.paths?.home ?? "/"),
-      callback: addBasePath(config.paths?.callback ?? "/auth/callback"),
-      logoutCallback: addBasePath(config.paths?.logoutCallback ?? "/"),
-      login: addBasePath(config.paths?.login ?? "/auth/login"),
-      logout: addBasePath(config.paths?.logout ?? "/auth/logout"),
-    };
-  }
-
-  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);
-
-    return aidc;
-  }
-
-  protected async getTokens(event: RequestEvent): Promise<TokenSet> {
-    const state = event.cookies.get(this.cookieNames.oauthState) ?? null;
-    const codeVerifier =
-      event.cookies.get(this.cookieNames.oauthCodeVerifier) ?? null;
-    const params = this.client.callbackParams(event.url.toString());
-
-    if (!codeVerifier || !state) {
-      redirect(SEE_OTHER, this.paths.login);
-    }
-
-    const redirectURI = new URL(this.paths.callback, event.url).toString();
-    try {
-      const tokenSet = await this.client.callback(redirectURI, params, {
-        code_verifier: codeVerifier,
-        state: state,
-      });
-
-      return tokenSet;
-    } catch (err) {
-      if (err instanceof errors.OPError) {
-        if (err.error === "access_denied") {
-          if (this.config.on?.accessDenied) {
-            return this.config.on.accessDenied(event, err);
-          }
-
-          redirect(SEE_OTHER, this.paths.home);
-        }
-
-        error(500, err);
-      } else if (err instanceof errors.RPError) {
-        error(500, err);
-      }
-
-      throw err;
-    }
-  }
-
-  protected loginHandler: Handle = ({ event }) => {
-    const codeVerifier = generators.codeVerifier();
-    const state = generators.state();
-    // const nonce = generators.nonce();
-
-    const redirectURI = new URL(this.paths.callback, event.url).toString();
-    const authorizationUrl = this.client.authorizationUrl({
-      response_type: "code",
-      scope: this.config.scope,
-      code_challenge: generators.codeChallenge(codeVerifier),
-      code_challenge_method: "S256",
-      redirect_uri: redirectURI,
-      state,
-      // nonce,
-    });
-
-    setTempCookie(event, this.cookieNames.oauthState, state);
-    setTempCookie(event, this.cookieNames.oauthCodeVerifier, codeVerifier);
-
-    redirect(SEE_OTHER, authorizationUrl);
-  };
-
-  protected logoutHandler: Handle = async ({ event }) => {
-    if (!event.locals.session) {
-      redirect(SEE_OTHER, this.paths.home);
-    }
-
-    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: typedSession?.id_token,
-    });
-
-    if (this.config.on?.logout) {
-      await this.config.on.logout(event);
-    }
-
-    await lucia.invalidateSession(event.locals.session.id);
-
-    setLuciaCookie(event, lucia.createBlankSessionCookie());
-
-    redirect(SEE_OTHER, endSessionUrl);
-  };
-
-  protected callbackHandler: Handle = async ({ event }) => {
-    const tokenSet = await this.getTokens(event);
-
-    if (!tokenSet.id_token) {
-      throw new Error("No id_token in tokenSet");
-    }
-
-    const claims = tokenSet.claims();
-    const { sub } = claims;
-    const { adapter: wrapper } = this.config;
-    const existingUserId = await wrapper.getUserId(sub);
-
-    let userId = existingUserId ?? generateId(15);
-
-    if (existingUserId === undefined) {
-      userId = await wrapper.createUser(sub, userId, claims);
-    }
-
-    const session = await wrapper.createSession(userId, {
-      id_token: tokenSet.id_token,
-    });
-    setLuciaCookie(event, wrapper.createSessionCookie(session.id));
-
-    if (this.config.on?.login) {
-      return this.config.on.login(event, tokenSet.claims());
-    }
-
-    redirect(SEE_OTHER, this.paths.home);
-  };
-
-  protected sessionHandler: Handle = async ({ event, resolve }) => {
-    const { adapter: lucia } = this.config;
-
-    const sessionId = event.cookies.get(lucia.sessionCookieName);
-    if (!sessionId) {
-      event.locals.user = null;
-      event.locals.session = null;
-      return resolve(event);
-    }
-
-    const { session, user } = await lucia.validateSession(sessionId);
-
-    if (session && session.fresh) {
-      setLuciaCookie(event, lucia.createSessionCookie(session.id));
-    }
-    if (!session) {
-      setLuciaCookie(event, lucia.createBlankSessionCookie());
-    }
-
-    event.locals.user = user;
-    event.locals.session = session as (SessionAttributes & Session) | null;
-    return resolve(event);
-  };
-
-  protected miscHandler: Handle = ({ event, resolve }) => {
-    event.locals.authPaths = this.paths;
-
-    return resolve(event);
-  };
-
-  handler(): Handle {
-    return sequence(
-      this.miscHandler.bind(this),
-      route(this.paths.login, this.loginHandler.bind(this)),
-      route(this.paths.logout, method("POST", this.logoutHandler.bind(this))),
-      route(this.paths.callback, this.callbackHandler.bind(this)),
-      this.sessionHandler.bind(this),
-    );
+    DatabaseUserAttributes: Types extends RegisterTypes<
+      Empty,
+      Empty,
+      Empty,
+      infer _DatabaseUserAttributes
+    >
+      ? _DatabaseUserAttributes
+      : never;
+    DatabaseSessionAttributes: Types extends RegisterTypes<
+      Empty,
+      Empty,
+      infer _DatabaseSessionAttributes,
+      Empty
+    >
+      ? Omit<
+          _DatabaseSessionAttributes,
+          "id" | "user_id" | "userId" | "expires_at" | "expiresAt"
+        >
+      : never;
   }
 }
diff --git a/src/lib/lucia.ts b/src/lib/lucia.ts
index 2763c0c4439ecc06b7fcb7ca083bf0b87b2ab1e0..5dfb4a7c30734e3adc8e03c0c2521b5a2d934b06 100644
--- a/src/lib/lucia.ts
+++ b/src/lib/lucia.ts
@@ -1,61 +1 @@
-import { dev } from "$app/environment";
-import type { MaybePromise } from "@sveltejs/kit";
-import type {
-  Adapter,
-  RegisteredDatabaseSessionAttributes,
-  RegisteredDatabaseUserAttributes,
-  SessionCookieOptions,
-  TimeSpan,
-} from "lucia";
-import { Lucia } from "lucia";
-import type { UserinfoResponse } from "openid-client";
-
-export interface DatabaseUser {
-  id: string;
-}
-export interface DatabaseSession {
-  id: string;
-  id_token: string;
-}
-
-export type DefaultUserAttributes = Record<never, never>;
-export interface DefaultSessionAttributes {
-  id_token: string;
-}
-
-export abstract class LuciaAdapter<
-  _SessionAttributes extends
-    DefaultSessionAttributes = DefaultSessionAttributes,
-  _UserAttributes extends DefaultUserAttributes = DefaultUserAttributes,
-> extends Lucia<_SessionAttributes, _UserAttributes> {
-  constructor(
-    adapter: Adapter,
-    options?: {
-      sessionExpiresIn?: TimeSpan;
-      sessionCookie?: SessionCookieOptions;
-      getSessionAttributes?: (
-        databaseSessionAttributes: RegisteredDatabaseSessionAttributes,
-      ) => _SessionAttributes;
-      getUserAttributes?: (
-        databaseUserAttributes: RegisteredDatabaseUserAttributes,
-      ) => _UserAttributes;
-    },
-  ) {
-    super(adapter, {
-      sessionCookie: {
-        attributes: {
-          secure: !dev,
-        },
-        name: "aidc_session",
-      },
-      ...options,
-    });
-  }
-
-  abstract getUserId(subject: string): MaybePromise<string | undefined>;
-  abstract createUser(
-    subject: string,
-    userId: string,
-    claims: UserinfoResponse,
-  ): MaybePromise<string>;
-}
+export * from "lucia";
diff --git a/src/lib/types.ts b/src/lib/types.ts
index 3b4cd2531b8100c3b1b2cb940c423f20e2d6ae1d..c7aab1a843ecd407b3f2a91e7dc0343165f510e9 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -1,14 +1,12 @@
 import type { MaybePromise, RequestEvent } from "@sveltejs/kit";
 import type { ClientMetadata, UserinfoResponse, errors } from "openid-client";
-import type {
-  DefaultSessionAttributes,
-  DefaultUserAttributes,
-  LuciaAdapter,
-} from "./lucia.js";
+import type { LuciaAdapter } from "./adapters/abstract.js";
+
+export type Empty = Record<never, never>;
 
 export interface Config<
-  Session extends DefaultSessionAttributes,
-  User extends DefaultUserAttributes,
+  Session extends Record<never, never>,
+  User extends Record<never, never>,
 > extends ClientMetadata {
   client_secret: string;
   scope: string;
@@ -36,6 +34,8 @@ export type CookieNames = {
   oauthCodeVerifier: string;
 };
 
-export interface Locals {
+export interface AIDCLocals {
   authPaths: Paths;
+  user: import("lucia").User | null;
+  session: import("lucia").Session | null;
 }
diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts
index 0cc8fd13fec638095a30c70d4c33abb57ccad467..c465c92b7b6e7e18958b1491b34d00bf1eea0ade 100644
--- a/src/routes/login/+page.server.ts
+++ b/src/routes/login/+page.server.ts
@@ -1,7 +1,6 @@
 import { redirect } from "@sveltejs/kit";
-type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
+
 export async function load(event) {
-  type User = Expand<import("lucia").User>;
   if (event.locals.user) {
     return redirect(302, "/");
   }
diff --git a/svelte.config.js b/svelte.config.js
index acec4fd1e7fb55e71bc39e4d6d3af315e7fc9177..9174400aead6d01ad46444994baf0e0016481e4a 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -12,6 +12,11 @@ const config = {
     // If your environment is not supported or you settled on a specific environment, switch out the adapter.
     // See https://kit.svelte.dev/docs/adapters for more information about adapters.
     adapter: adapter(),
+
+    alias: {
+      "@arise/aidc-sveltekit": "src/lib/index.js",
+      "@arise/aidc-sveltekit/*": "src/lib/*",
+    },
   },
 };