mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
formatting the PR
This commit is contained in:
28
packages/adapter-d1/README.md
Normal file
28
packages/adapter-d1/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://authjs.dev" target="_blank">
|
||||
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
|
||||
</a>
|
||||
<a href="https://developers.cloudflare.com/d1/" target="_blank">
|
||||
<img height="64px" src="https://authjs.dev/img/adapters/d1.svg"/>
|
||||
</a>
|
||||
<h3 align="center"><b>Cloudflare D1 Adapter</b> - NextAuth.js / Auth.js</a></h3>
|
||||
<p align="center" style="align: center;">
|
||||
<a href="https://npm.im/@auth/drizzle-adapter">
|
||||
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
|
||||
</a>
|
||||
<a href="https://npm.im/@auth/d1-adapter">
|
||||
<img alt="npm" src="https://img.shields.io/npm/v/@auth/d1-adapter?color=green&label=@auth/d1-adapter&style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmtrends.com/@auth/d1-adapter">
|
||||
<img src="https://img.shields.io/npm/dm/@auth/d1-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
<a href="https://github.com/nextauthjs/next-auth/stargazers">
|
||||
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
|
||||
</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/d1).
|
||||
@@ -37,18 +37,18 @@
|
||||
"test": "jest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next-auth": "^4"
|
||||
"@auth/core": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20230321.0",
|
||||
"@miniflare/d1": "^2.12.2",
|
||||
"@next-auth/adapter-test": "workspace:*",
|
||||
"@next-auth/tsconfig": "workspace:*",
|
||||
"@auth/adapter-test": "workspace:*",
|
||||
"@auth/tsconfig": "workspace:*",
|
||||
"better-sqlite3": "^7.0.0",
|
||||
"jest": "^27.0.3",
|
||||
"next-auth": "workspace:*"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
"preset": "@auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,89 +5,81 @@
|
||||
* <img style={{display: "block"}} src="/img/adapters/d1.svg" width="48" />
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
*
|
||||
* ## Warning
|
||||
* This adapter is not developed or maintained by Clouflare and they haven't declared the D1 api stable. The author will make an effort to keep this adapter up to date.
|
||||
* The adapter is compatible with the D1 api as of March 22, 2023.
|
||||
*
|
||||
*
|
||||
* ## Installation
|
||||
*
|
||||
* ```bash npm2yarn2pnpm
|
||||
* npm install next-auth @next-auth/d1-adapter
|
||||
* npm install next-auth @auth/d1-adapter
|
||||
* ```
|
||||
*
|
||||
* @module @next-auth/d1-adapter
|
||||
* @module @auth/d1-adapter
|
||||
*/
|
||||
|
||||
import { D1Database } from "@cloudflare/workers-types";
|
||||
import { D1Database as MiniflareD1Database } from "@miniflare/d1";
|
||||
import * as crypto from 'crypto';
|
||||
import {
|
||||
import type { D1Database as WorkerDatabase } from "@cloudflare/workers-types"
|
||||
import type { D1Database as MiniflareD1Database } from "@miniflare/d1"
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterSession,
|
||||
AdapterUser,
|
||||
AdapterAccount,
|
||||
} from "next-auth/adapters";
|
||||
VerificationToken as AdapterVerificationToken,
|
||||
} from "@auth/core/adapters"
|
||||
|
||||
|
||||
export { up } from "./migrations";
|
||||
|
||||
// some handy internal type aliases
|
||||
export { up } from "./migrations"
|
||||
|
||||
/**
|
||||
* @type @cloudflare/workers-types.D1Database | @miniflare/d1.D1Database
|
||||
*/
|
||||
export type BothDB = D1Database | MiniflareD1Database;
|
||||
|
||||
interface AdapterVerificationToken {
|
||||
expires: Date;
|
||||
identifier: string;
|
||||
token: string;
|
||||
}
|
||||
export type D1Database = WorkerDatabase | MiniflareD1Database
|
||||
|
||||
// all the sqls
|
||||
// USER
|
||||
export const CREATE_USER_SQL = `INSERT INTO authjs_users (id, name, email, emailVerified, image) VALUES (?, ?, ?, ?, ?)`;
|
||||
export const GET_USER_BY_ID_SQL = `SELECT * FROM authjs_users WHERE id = ?`;
|
||||
export const GET_USER_BY_EMAIL_SQL = `SELECT * FROM authjs_users WHERE email = ?`;
|
||||
export const CREATE_USER_SQL = `INSERT INTO users (id, name, email, emailVerified, image) VALUES (?, ?, ?, ?, ?)`
|
||||
export const GET_USER_BY_ID_SQL = `SELECT * FROM users WHERE id = ?`
|
||||
export const GET_USER_BY_EMAIL_SQL = `SELECT * FROM users WHERE email = ?`
|
||||
export const GET_USER_BY_ACCOUNTL_SQL = `
|
||||
SELECT u.*
|
||||
FROM authjs_users u JOIN authjs_accounts a ON a.userId = u.id
|
||||
WHERE a.providerAccountId = ? AND a.provider = ?`;
|
||||
FROM users u JOIN accounts a ON a.userId = u.id
|
||||
WHERE a.providerAccountId = ? AND a.provider = ?`
|
||||
export const UPDATE_USER_BY_ID_SQL = `
|
||||
UPDATE authjs_users
|
||||
UPDATE users
|
||||
SET name = ?, email = ?, emailVerified = ?, image = ?
|
||||
WHERE id = ? `;
|
||||
export const DELETE_USER_SQL = `DELETE FROM authjs_users WHERE id = ?`;
|
||||
WHERE id = ? `
|
||||
export const DELETE_USER_SQL = `DELETE FROM users WHERE id = ?`
|
||||
|
||||
// SESSION
|
||||
export const CREATE_SESSION_SQL = 'INSERT INTO authjs_sessions (id, sessionToken, userId, expires) VALUES (?,?,?,?)';
|
||||
// SESSION
|
||||
export const CREATE_SESSION_SQL =
|
||||
"INSERT INTO sessions (id, sessionToken, userId, expires) VALUES (?,?,?,?)"
|
||||
export const GET_SESSION_BY_TOKEN_SQL = `
|
||||
SELECT id, sessionToken, userId, expires
|
||||
FROM authjs_sessions
|
||||
WHERE sessionToken = ?`;
|
||||
export const UPDATE_SESSION_BY_SESSION_TOKEN_SQL = `UPDATE authjs_sessions SET expires = ? WHERE sessionToken = ?`;
|
||||
export const DELETE_SESSION_SQL = `DELETE FROM authjs_sessions WHERE sessionToken = ?`;
|
||||
export const DELETE_SESSION_BY_USER_ID_SQL = `DELETE FROM authjs_sessions WHERE userId = ?`;
|
||||
FROM sessions
|
||||
WHERE sessionToken = ?`
|
||||
export const UPDATE_SESSION_BY_SESSION_TOKEN_SQL = `UPDATE sessions SET expires = ? WHERE sessionToken = ?`
|
||||
export const DELETE_SESSION_SQL = `DELETE FROM sessions WHERE sessionToken = ?`
|
||||
export const DELETE_SESSION_BY_USER_ID_SQL = `DELETE FROM sessions WHERE userId = ?`
|
||||
|
||||
// ACCOUNT
|
||||
export const CREATE_ACCOUNT_SQL = `
|
||||
INSERT INTO authjs_accounts (
|
||||
INSERT INTO accounts (
|
||||
id, userId, type, provider,
|
||||
providerAccountId, refresh_token, access_token,
|
||||
expires_at, token_type, scope, id_token, session_state,
|
||||
oauth_token, oauth_token_secret
|
||||
)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`;
|
||||
export const GET_ACCOUNT_BY_ID_SQL = `SELECT * FROM authjs_accounts WHERE id = ? `
|
||||
export const GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `SELECT * FROM authjs_accounts WHERE provider = ? AND providerAccountId = ?`;
|
||||
export const DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `DELETE FROM authjs_accounts WHERE provider = ? AND providerAccountId = ?`;
|
||||
export const DELETE_ACCOUNT_BY_USER_ID_SQL = `DELETE FROM authjs_accounts WHERE userId = ?`;
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
|
||||
export const GET_ACCOUNT_BY_ID_SQL = `SELECT * FROM accounts WHERE id = ? `
|
||||
export const GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `SELECT * FROM accounts WHERE provider = ? AND providerAccountId = ?`
|
||||
export const DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `DELETE FROM accounts WHERE provider = ? AND providerAccountId = ?`
|
||||
export const DELETE_ACCOUNT_BY_USER_ID_SQL = `DELETE FROM accounts WHERE userId = ?`
|
||||
|
||||
// VERIFICATION_TOKEN
|
||||
export const GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL= `SELECT * FROM authjs_verification_tokens WHERE identifier = ? AND token = ?`;
|
||||
export const CREATE_VERIFICATION_TOKEN_SQL = `INSERT INTO authjs_verification_tokens (identifier, expires, token) VALUES (?,?,?)`;
|
||||
export const DELETE_VERIFICATION_TOKEN_SQL = `DELETE FROM authjs_verification_tokens WHERE identifier = ? and token = ?`;
|
||||
export const GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL = `SELECT * FROM verification_tokens WHERE identifier = ? AND token = ?`
|
||||
export const CREATE_VERIFICATION_TOKEN_SQL = `INSERT INTO verification_tokens (identifier, expires, token) VALUES (?,?,?)`
|
||||
export const DELETE_VERIFICATION_TOKEN_SQL = `DELETE FROM verification_tokens WHERE identifier = ? and token = ?`
|
||||
|
||||
// helper functions
|
||||
|
||||
@@ -104,11 +96,11 @@ function format<T>(obj: Record<string, any>): T {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete obj[key];
|
||||
delete obj[key]
|
||||
}
|
||||
|
||||
if (isDate(value)) {
|
||||
obj[key] = new Date(value);
|
||||
obj[key] = new Date(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,56 +109,82 @@ function format<T>(obj: Record<string, any>): T {
|
||||
|
||||
// D1 doesnt like undefined, it wants null when calling bind
|
||||
function cleanBindings(bindings: any[]) {
|
||||
return bindings.map(e=> e === undefined ? null : e)
|
||||
return bindings.map((e) => (e === undefined ? null : e))
|
||||
}
|
||||
|
||||
export async function createRecord<RecordType>(db:BothDB, CREATE_SQL:string, bindings: any[], GET_SQL:string, getBindings: any[]):Promise<RecordType | null> {
|
||||
export async function createRecord<RecordType>(
|
||||
db: D1Database,
|
||||
CREATE_SQL: string,
|
||||
bindings: any[],
|
||||
GET_SQL: string,
|
||||
getBindings: any[]
|
||||
) {
|
||||
try {
|
||||
bindings = cleanBindings(bindings)
|
||||
await db.prepare(CREATE_SQL).bind(...bindings).run()
|
||||
await db
|
||||
.prepare(CREATE_SQL)
|
||||
.bind(...bindings)
|
||||
.run()
|
||||
return await getRecord<RecordType>(db, GET_SQL, getBindings)
|
||||
} catch(e:any) {
|
||||
console.log(`Error creating record with ${CREATE_SQL} and bindings ${bindings}`)
|
||||
console.log(`D1 ERROR MESSAGE`, e.message, e.cause?.message)
|
||||
} catch (e: any) {
|
||||
console.error(e.message, e.cause?.message)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRecord<RecordType>(db:BothDB, SQL:string, bindings:any[]):Promise<RecordType | null> {
|
||||
export async function getRecord<RecordType>(
|
||||
db: D1Database,
|
||||
SQL: string,
|
||||
bindings: any[]
|
||||
): Promise<RecordType | null> {
|
||||
try {
|
||||
bindings = cleanBindings(bindings)
|
||||
const res:any = await db.prepare(SQL).bind(...bindings).first()
|
||||
if(res) {
|
||||
const res: any = await db
|
||||
.prepare(SQL)
|
||||
.bind(...bindings)
|
||||
.first()
|
||||
if (res) {
|
||||
return format<RecordType>(res)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} catch(e:any) {
|
||||
console.log(`Error getting record with ${SQL} and bindings ${bindings}`)
|
||||
console.log(`D1 ERROR MESSAGE`, e.message, e.cause?.message)
|
||||
} catch (e: any) {
|
||||
console.error(e.message, e.cause?.message)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateRecord(db:BothDB, SQL:string, bindings:any[]) {
|
||||
export async function updateRecord(
|
||||
db: D1Database,
|
||||
SQL: string,
|
||||
bindings: any[]
|
||||
) {
|
||||
try {
|
||||
bindings = cleanBindings(bindings)
|
||||
return await db.prepare(SQL).bind(...bindings).run()
|
||||
} catch(e:any) {
|
||||
console.log(`Error updating record with ${SQL} and bindings ${bindings}`)
|
||||
console.log(`D1 ERROR MESSAGE`, e.message, e.cause?.message)
|
||||
return await db
|
||||
.prepare(SQL)
|
||||
.bind(...bindings)
|
||||
.run()
|
||||
} catch (e: any) {
|
||||
console.error(e.message, e.cause?.message)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteRecord(db:BothDB, SQL:string, bindings:any[]) {
|
||||
export async function deleteRecord(
|
||||
db: D1Database,
|
||||
SQL: string,
|
||||
bindings: any[]
|
||||
) {
|
||||
// eslint-disable-next-line no-useless-catch
|
||||
try {
|
||||
bindings = cleanBindings(bindings)
|
||||
await db.prepare(SQL).bind(...bindings).run()
|
||||
} catch(e:any) {
|
||||
console.log(`Error deleting record with ${SQL} and bindings ${bindings}`)
|
||||
console.log(`D1 ERROR MESSAGE`, e.message, e.cause?.message)
|
||||
await db
|
||||
.prepare(SQL)
|
||||
.bind(...bindings)
|
||||
.run()
|
||||
} catch (e: any) {
|
||||
console.error(e.message, e.cause?.message)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -181,7 +199,7 @@ export async function deleteRecord(db:BothDB, SQL:string, bindings:any[]) {
|
||||
*
|
||||
* ```javascript title="pages/api/auth/[...nextauth].js"
|
||||
* import NextAuth from "next-auth"
|
||||
* import { D1Adapter, up } from "@next-auth/d1-adapter"
|
||||
* import { D1Adapter, up } from "@auth/d1-adapter"
|
||||
*
|
||||
*
|
||||
* // For more information on each option (and a full list of options) go to
|
||||
@@ -198,14 +216,14 @@ export async function deleteRecord(db:BothDB, SQL:string, bindings:any[]) {
|
||||
*
|
||||
* Somewhere in the initialization of your application you need to run the `up(env.db)` function to create the tables in D1.
|
||||
* It will create 4 tables if they don't already exist:
|
||||
* `authjs_accounts`, `authjs_sessions`, `authjs_users`, `authjs_verification_tokens`.
|
||||
*
|
||||
* The table prefix "authjs_" is not configurable at this time.
|
||||
*
|
||||
* `accounts`, `sessions`, `users`, `verification_tokens`.
|
||||
*
|
||||
* The table prefix "" is not configurable at this time.
|
||||
*
|
||||
* You can use something like the following to attempt the migration once each time your worker starts up. Running migrations more than once will not erase your existing tables.
|
||||
* ```javascript
|
||||
* import { up } from "@next-auth/d1-adapter"
|
||||
*
|
||||
* import { up } from "@auth/d1-adapter"
|
||||
*
|
||||
* let migrated = false;
|
||||
* async function migrationHandle({event, resolve}) {
|
||||
* if(!migrated) {
|
||||
@@ -219,118 +237,173 @@ export async function deleteRecord(db:BothDB, SQL:string, bindings:any[]) {
|
||||
* return resolve(event)
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
*
|
||||
* You can also initialize your tables manually. Look in [init.ts](https://github.com/nextauthjs/next-auth/packages/adapter-d1/src/migrations/init.ts) for the relevant sql.
|
||||
* Paste and run the SQL into your D1 dashboard query tool.
|
||||
*
|
||||
*
|
||||
**/
|
||||
export function D1Adapter(db:BothDB): Adapter {
|
||||
|
||||
export function D1Adapter(db: D1Database): Adapter {
|
||||
// we need to run migrations if we dont have the right tables
|
||||
|
||||
return {
|
||||
async createUser(user) {
|
||||
console.log('Creating User:', user)
|
||||
const id: string = crypto.randomUUID();
|
||||
const createBindings = [id, user.name , user.email, user.emailVerified?.toISOString(), user.image];
|
||||
const getBindings = [id];
|
||||
const id: string = crypto.randomUUID()
|
||||
const createBindings = [
|
||||
id,
|
||||
user.name,
|
||||
user.email,
|
||||
user.emailVerified?.toISOString(),
|
||||
user.image,
|
||||
]
|
||||
const getBindings = [id]
|
||||
|
||||
const newUser = await createRecord<AdapterUser>(
|
||||
db,
|
||||
CREATE_USER_SQL, createBindings,
|
||||
GET_USER_BY_ID_SQL, getBindings
|
||||
);
|
||||
// this method cant return null, gotta throw
|
||||
if(newUser) return newUser;
|
||||
throw new Error(`Couldn't create a new user`);
|
||||
db,
|
||||
CREATE_USER_SQL,
|
||||
createBindings,
|
||||
GET_USER_BY_ID_SQL,
|
||||
getBindings
|
||||
)
|
||||
if (newUser) return newUser
|
||||
throw new Error("Error creating user: Cannot get user after creation.")
|
||||
},
|
||||
async getUser(id) {
|
||||
return await getRecord<AdapterUser>(db,GET_USER_BY_ID_SQL,[id]);
|
||||
return await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [id])
|
||||
},
|
||||
async getUserByEmail(email) {
|
||||
return await getRecord<AdapterUser>(db,GET_USER_BY_EMAIL_SQL,[email]);
|
||||
return await getRecord<AdapterUser>(db, GET_USER_BY_EMAIL_SQL, [email])
|
||||
},
|
||||
async getUserByAccount({ providerAccountId, provider }) {
|
||||
return await getRecord<AdapterUser>(db,GET_USER_BY_ACCOUNTL_SQL,[providerAccountId,provider]);
|
||||
|
||||
return await getRecord<AdapterUser>(db, GET_USER_BY_ACCOUNTL_SQL, [
|
||||
providerAccountId,
|
||||
provider,
|
||||
])
|
||||
},
|
||||
async updateUser(user) {
|
||||
const params = await getRecord<AdapterUser>(db,GET_USER_BY_ID_SQL,[user.id]);
|
||||
if(params) {
|
||||
const params = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
|
||||
user.id,
|
||||
])
|
||||
if (params) {
|
||||
// copy any properties not in the update into the existing one and use that for bind params
|
||||
// covers the scenario where the user arg doesnt have all of the current users properties
|
||||
Object.assign(params,user);
|
||||
const res = await updateRecord(db,UPDATE_USER_BY_ID_SQL,[params.name, params.email, params.emailVerified?.toISOString(), params.image, params.id]);
|
||||
if(res.success) {
|
||||
// we could probably just return
|
||||
const user = await getRecord<AdapterUser>(db,GET_USER_BY_ID_SQL,[params.id]);
|
||||
if(user) return user;
|
||||
throw new Error(`couldnt find user after update with id ${params.id}`);
|
||||
}
|
||||
throw new Error(`couldnt update user with data ${JSON.stringify(user)}`);
|
||||
Object.assign(params, user)
|
||||
const res = await updateRecord(db, UPDATE_USER_BY_ID_SQL, [
|
||||
params.name,
|
||||
params.email,
|
||||
params.emailVerified?.toISOString(),
|
||||
params.image,
|
||||
params.id,
|
||||
])
|
||||
if (res.success) {
|
||||
// we could probably just return
|
||||
const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
|
||||
params.id,
|
||||
])
|
||||
if (user) return user
|
||||
throw new Error(
|
||||
"Error updating user: Cannot get user after updating."
|
||||
)
|
||||
}
|
||||
}
|
||||
throw new Error(`couldnt find user to update from id ${user.id}`);
|
||||
throw new Error("Error updating user: Failed to run the update SQL.")
|
||||
},
|
||||
async deleteUser(userId) {
|
||||
// this should probably be in a db.batch but batch has problems right now in miniflare
|
||||
// no multi line sql statements
|
||||
await deleteRecord(db, DELETE_ACCOUNT_BY_USER_ID_SQL, [userId]);
|
||||
await deleteRecord(db, DELETE_SESSION_BY_USER_ID_SQL, [userId]);
|
||||
await deleteRecord(db, DELETE_USER_SQL, [userId]);
|
||||
return null;
|
||||
await deleteRecord(db, DELETE_ACCOUNT_BY_USER_ID_SQL, [userId])
|
||||
await deleteRecord(db, DELETE_SESSION_BY_USER_ID_SQL, [userId])
|
||||
await deleteRecord(db, DELETE_USER_SQL, [userId])
|
||||
return null
|
||||
},
|
||||
async linkAccount(a) {
|
||||
// convert user_id to userId and provider_account_id to providerAccountId
|
||||
const id = crypto.randomUUID();
|
||||
const createBindings = [id, a.userId, a.type, a.provider,
|
||||
a.providerAccountId, a.refresh_token , a.access_token,
|
||||
a.expires_at, a.token_type, a.scope, a.id_token, a.session_state,
|
||||
a.oauth_token?? null, a.oauth_token_secret?? null];
|
||||
const getBindings = [id];
|
||||
const id = crypto.randomUUID()
|
||||
const createBindings = [
|
||||
id,
|
||||
a.userId,
|
||||
a.type,
|
||||
a.provider,
|
||||
a.providerAccountId,
|
||||
a.refresh_token,
|
||||
a.access_token,
|
||||
a.expires_at,
|
||||
a.token_type,
|
||||
a.scope,
|
||||
a.id_token,
|
||||
a.session_state,
|
||||
a.oauth_token ?? null,
|
||||
a.oauth_token_secret ?? null,
|
||||
]
|
||||
const getBindings = [id]
|
||||
return await createRecord<AdapterAccount>(
|
||||
db,
|
||||
CREATE_ACCOUNT_SQL, createBindings,
|
||||
GET_ACCOUNT_BY_ID_SQL, getBindings
|
||||
);
|
||||
CREATE_ACCOUNT_SQL,
|
||||
createBindings,
|
||||
GET_ACCOUNT_BY_ID_SQL,
|
||||
getBindings
|
||||
)
|
||||
},
|
||||
async unlinkAccount({ providerAccountId, provider }) {
|
||||
await deleteRecord(db, DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL, [provider, providerAccountId]);
|
||||
await deleteRecord(
|
||||
db,
|
||||
DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
|
||||
[provider, providerAccountId]
|
||||
)
|
||||
},
|
||||
async createSession({ sessionToken, userId, expires }) {
|
||||
const id = crypto.randomUUID();
|
||||
const createBindings = [id, sessionToken, userId, expires.toISOString()];
|
||||
const getBindings = [sessionToken];
|
||||
const session = await createRecord<AdapterSession>(
|
||||
const id = crypto.randomUUID()
|
||||
const createBindings = [id, sessionToken, userId, expires.toISOString()]
|
||||
const getBindings = [sessionToken]
|
||||
const session = await createRecord<AdapterSession>(
|
||||
db,
|
||||
CREATE_SESSION_SQL, createBindings,
|
||||
GET_SESSION_BY_TOKEN_SQL, getBindings
|
||||
);
|
||||
if(session) return session;
|
||||
throw new Error(`Couldn't create session`);
|
||||
CREATE_SESSION_SQL,
|
||||
createBindings,
|
||||
GET_SESSION_BY_TOKEN_SQL,
|
||||
getBindings
|
||||
)
|
||||
if (session) return session
|
||||
throw new Error(`Couldn't create session`)
|
||||
},
|
||||
async getSessionAndUser(sessionToken) {
|
||||
const session:any = await getRecord<AdapterSession>(db,GET_SESSION_BY_TOKEN_SQL,[sessionToken]);
|
||||
const session: any = await getRecord<AdapterSession>(
|
||||
db,
|
||||
GET_SESSION_BY_TOKEN_SQL,
|
||||
[sessionToken]
|
||||
)
|
||||
// no session? no user!
|
||||
if(session === null) return null;
|
||||
if (session === null) return null
|
||||
|
||||
// this shouldnt happen, but just in case
|
||||
const user = await getRecord<AdapterUser>(db,GET_USER_BY_ID_SQL,[session.userId]);
|
||||
if(user === null) return null;
|
||||
const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
|
||||
session.userId,
|
||||
])
|
||||
if (user === null) return null
|
||||
|
||||
return {session, user};
|
||||
return { session, user }
|
||||
},
|
||||
async updateSession({ sessionToken, expires }) {
|
||||
// kinda strange that we have to deal with an undefined expires,
|
||||
// we dont have any policy to enforce, lets just expire it now.
|
||||
if(expires === undefined) {
|
||||
if (expires === undefined) {
|
||||
await deleteRecord(db, DELETE_SESSION_SQL, [sessionToken])
|
||||
return null
|
||||
}
|
||||
const session = await getRecord<AdapterSession>(db, GET_SESSION_BY_TOKEN_SQL,[sessionToken])
|
||||
if(!session) return null
|
||||
session.expires = expires;
|
||||
await updateRecord(db, UPDATE_SESSION_BY_SESSION_TOKEN_SQL, [expires?.toISOString(), sessionToken])
|
||||
return await db.prepare(UPDATE_SESSION_BY_SESSION_TOKEN_SQL).bind(expires?.toISOString(), sessionToken).first()
|
||||
const session = await getRecord<AdapterSession>(
|
||||
db,
|
||||
GET_SESSION_BY_TOKEN_SQL,
|
||||
[sessionToken]
|
||||
)
|
||||
if (!session) return null
|
||||
session.expires = expires
|
||||
await updateRecord(db, UPDATE_SESSION_BY_SESSION_TOKEN_SQL, [
|
||||
expires?.toISOString(),
|
||||
sessionToken,
|
||||
])
|
||||
return await db
|
||||
.prepare(UPDATE_SESSION_BY_SESSION_TOKEN_SQL)
|
||||
.bind(expires?.toISOString(), sessionToken)
|
||||
.first()
|
||||
},
|
||||
async deleteSession(sessionToken) {
|
||||
await deleteRecord(db, DELETE_SESSION_SQL, [sessionToken])
|
||||
@@ -338,14 +411,20 @@ export function D1Adapter(db:BothDB): Adapter {
|
||||
},
|
||||
async createVerificationToken({ identifier, expires, token }) {
|
||||
return await createRecord(
|
||||
db,
|
||||
CREATE_VERIFICATION_TOKEN_SQL,[identifier, expires.toISOString(), token],
|
||||
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,[identifier, token]
|
||||
db,
|
||||
CREATE_VERIFICATION_TOKEN_SQL,
|
||||
[identifier, expires.toISOString(), token],
|
||||
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
|
||||
[identifier, token]
|
||||
)
|
||||
},
|
||||
async useVerificationToken({ identifier, token }) {
|
||||
const verificationToken = await getRecord<AdapterVerificationToken>(db, GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL, [identifier, token])
|
||||
if(!verificationToken) return null
|
||||
const verificationToken = await getRecord<AdapterVerificationToken>(
|
||||
db,
|
||||
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
|
||||
[identifier, token]
|
||||
)
|
||||
if (!verificationToken) return null
|
||||
await deleteRecord(db, DELETE_VERIFICATION_TOKEN_SQL, [identifier, token])
|
||||
return verificationToken
|
||||
},
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export const up = [
|
||||
`CREATE TABLE IF NOT EXISTS "authjs_accounts" (
|
||||
import type { D1Database } from "."
|
||||
|
||||
export const upSQLStatements = [
|
||||
`CREATE TABLE IF NOT EXISTS "accounts" (
|
||||
"id" text NOT NULL,
|
||||
"userId" text NOT NULL DEFAULT NULL,
|
||||
"type" text NOT NULL DEFAULT NULL,
|
||||
@@ -16,14 +18,14 @@ export const up = [
|
||||
"oauth_token" text DEFAULT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);`,
|
||||
`CREATE TABLE IF NOT EXISTS "authjs_sessions" (
|
||||
`CREATE TABLE IF NOT EXISTS "sessions" (
|
||||
"id" text NOT NULL,
|
||||
"sessionToken" text NOT NULL,
|
||||
"userId" text NOT NULL DEFAULT NULL,
|
||||
"expires" datetime NOT NULL DEFAULT NULL,
|
||||
PRIMARY KEY (sessionToken)
|
||||
);`,
|
||||
`CREATE TABLE IF NOT EXISTS "authjs_users" (
|
||||
`CREATE TABLE IF NOT EXISTS "users" (
|
||||
"id" text NOT NULL DEFAULT '',
|
||||
"name" text DEFAULT NULL,
|
||||
"email" text DEFAULT NULL,
|
||||
@@ -31,23 +33,36 @@ export const up = [
|
||||
"image" text DEFAULT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);`,
|
||||
`CREATE TABLE IF NOT EXISTS "authjs_verification_tokens" (
|
||||
`CREATE TABLE IF NOT EXISTS "verification_tokens" (
|
||||
"identifier" text NOT NULL,
|
||||
"token" text NOT NULL DEFAULT NULL,
|
||||
"expires" datetime NOT NULL DEFAULT NULL,
|
||||
PRIMARY KEY (token)
|
||||
);`
|
||||
];
|
||||
);`,
|
||||
]
|
||||
|
||||
export const down = [
|
||||
`DROP TABLE IF EXISTS "authjs_accounts";`,
|
||||
`DROP TABLE IF EXISTS "authjs_sessions";`,
|
||||
`DROP TABLE IF EXISTS "authjs_users";`,
|
||||
`DROP TABLE IF EXISTS "authjs_verification_token";`
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
`DROP TABLE IF EXISTS "accounts";`,
|
||||
`DROP TABLE IF EXISTS "sessions";`,
|
||||
`DROP TABLE IF EXISTS "users";`,
|
||||
`DROP TABLE IF EXISTS "verification_token";`,
|
||||
]
|
||||
|
||||
/**
|
||||
*
|
||||
* @param db
|
||||
*/
|
||||
async function up(db: D1Database) {
|
||||
// run the migration
|
||||
upSQLStatements.forEach(async (sql) => {
|
||||
try {
|
||||
console.log("applying db migration sql", sql)
|
||||
const res = await db.prepare(sql).run()
|
||||
console.log("migration result", res)
|
||||
} catch (e: any) {
|
||||
console.log(e.cause?.message, e.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export { up }
|
||||
@@ -1,24 +0,0 @@
|
||||
import {up as upSQLStatements} from "./init";
|
||||
// @ts-ignore
|
||||
import { D1Database, D1PreparedStatement } from "@cloudflare/workers-types";
|
||||
import type { BothDB } from "..";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param db
|
||||
*/
|
||||
async function up(db: BothDB) {
|
||||
// run the migration
|
||||
upSQLStatements.forEach(async (sql)=>{
|
||||
try {
|
||||
console.log('applying db migration sql', sql)
|
||||
const res = await db.prepare(sql).run();
|
||||
console.log('migration result', res)
|
||||
} catch(e:any) {
|
||||
console.log(e.cause?.message, e.message)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export { up }
|
||||
@@ -1,33 +1,46 @@
|
||||
import {
|
||||
import {
|
||||
D1Adapter,
|
||||
up,
|
||||
getRecord,
|
||||
GET_USER_BY_ID_SQL,
|
||||
GET_SESSION_BY_TOKEN_SQL,
|
||||
GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
|
||||
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL
|
||||
} from "../src";
|
||||
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
|
||||
} from "../src"
|
||||
import {
|
||||
AdapterSession,
|
||||
AdapterUser,
|
||||
AdapterAccount
|
||||
} from "next-auth/adapters";
|
||||
import { D1Database, D1DatabaseAPI } from "@miniflare/d1";
|
||||
import { runBasicTests } from "@next-auth/adapter-test";
|
||||
import Database from "better-sqlite3";
|
||||
AdapterAccount,
|
||||
} from "@auth/core/adapters"
|
||||
import { D1Database, D1DatabaseAPI } from "@miniflare/d1"
|
||||
import { runBasicTests } from "@auth/adapter-test"
|
||||
import Database from "better-sqlite3"
|
||||
|
||||
const sqliteDB = new Database(":memory:");
|
||||
let db = new D1Database(new D1DatabaseAPI(sqliteDB));
|
||||
let adapter = D1Adapter(db);
|
||||
const sqliteDB = new Database(":memory:")
|
||||
let db = new D1Database(new D1DatabaseAPI(sqliteDB as any))
|
||||
let adapter = D1Adapter(db)
|
||||
|
||||
// put stuff here if we need some async init
|
||||
beforeAll(async ()=> await up(db))
|
||||
beforeAll(async () => await up(db))
|
||||
runBasicTests({
|
||||
adapter: adapter,
|
||||
adapter,
|
||||
db: {
|
||||
user: async id => await getRecord<AdapterUser>(db,GET_USER_BY_ID_SQL,[id]),
|
||||
session: async sessionToken => await getRecord<AdapterSession>(db,GET_SESSION_BY_TOKEN_SQL,[sessionToken]),
|
||||
account: async ({ provider, providerAccountId }) => await getRecord<AdapterAccount>(db,GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,[provider, providerAccountId]),
|
||||
verificationToken: async ({ identifier, token }) => await getRecord(db,GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,[identifier, token])
|
||||
user: async (id) =>
|
||||
await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [id]),
|
||||
session: async (sessionToken) =>
|
||||
await getRecord<AdapterSession>(db, GET_SESSION_BY_TOKEN_SQL, [
|
||||
sessionToken,
|
||||
]),
|
||||
account: async ({ provider, providerAccountId }) =>
|
||||
await getRecord<AdapterAccount>(
|
||||
db,
|
||||
GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
|
||||
[provider, providerAccountId]
|
||||
),
|
||||
verificationToken: async ({ identifier, token }) =>
|
||||
await getRecord(db, GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL, [
|
||||
identifier,
|
||||
token,
|
||||
]),
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
{
|
||||
"extends": "@next-auth/tsconfig/tsconfig.adapters.json",
|
||||
"extends": "@auth/tsconfig/tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"baseUrl": ".",
|
||||
"isolatedModules": true,
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"outDir": ".",
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"types": ["@cloudflare/workers-types"]
|
||||
"skipDefaultLibCheck": true,
|
||||
"strictNullChecks": true,
|
||||
"stripInternal": true,
|
||||
"declarationMap": true,
|
||||
"declaration": true
|
||||
},
|
||||
"exclude": ["tests", "dist", "jest.config.js"]
|
||||
"include": ["src/**/*", "tests/migrations"],
|
||||
"exclude": ["*.js", "*.d.ts"]
|
||||
}
|
||||
|
||||
572
pnpm-lock.yaml
generated
572
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user