Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 51 additions & 47 deletions src/runtime/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { generateRandomString, alphabet } from "oslo/crypto";
import { checkDbAndTables, type tableNames } from "../database";
import { getOAuthAccountsTableSchema, getSessionsTableSchema, getUsersTableSchema } from "../database/lib/schema";
import { drizzle as drizzleIntegration } from "db0/integrations/drizzle/index";
import type { checkDbAndTablesParameters, ICreateOrLoginParams, ISlipAuthCoreOptions, SchemasMockValue, SlipAuthSession } from "./types";
import type { checkDbAndTablesParameters, DrizzleTransaction, ICreateOrLoginParams, ISlipAuthCoreOptions, SchemasMockValue, SlipAuthSession } from "./types";
import { createSlipHooks } from "./hooks";
import { UsersRepository } from "./repositories/UsersRepository";
import { SessionsRepository } from "./repositories/SessionsRepository";
Expand Down Expand Up @@ -71,52 +71,56 @@ export class SlipAuthCore {
public async registerUserIfMissingInDb(
params: ICreateOrLoginParams,
): Promise<[ string, SlipAuthSession]> {
const existingUser = await this.#repos.users.findByEmail(params.email);

if (!existingUser) {
const userId = this.#createRandomUserId();

await this.#repos.users.insert(userId, params.email);

const _insertedOAuthAccount = await this.#repos.oAuthAccounts.insert(params.email, {
provider_id: params.providerId,
provider_user_id: params.providerUserId,
user_id: userId,
});

const sessionFromRegistrationId = this.#createRandomSessionId();
const sessionFromRegistration = await this.#repos.sessions.insert(sessionFromRegistrationId, {
userId,
expiresAt: Date.now() + this.#sessionMaxAge,
ip: params.ip,
ua: params.ua,
});

return [userId, sessionFromRegistration as SlipAuthSession];
}

const existingAccount = await this.#repos.oAuthAccounts.findByProviderData(
params.providerId, params.providerUserId,
);

if (existingUser && existingAccount?.provider_id !== params.providerId) {
throw new Error("user already have an account with another provider");
}

if (existingAccount) {
const sessionFromLoginId = this.#createRandomSessionId();
const sessionFromLogin = await this.#repos.sessions.insert(sessionFromLoginId, {
userId: existingUser.id,
expiresAt: Date.now() + this.#sessionMaxAge,
ua: params.ua,
ip: params.ip,
});
const { id, expires_at } = sessionFromLogin;

return [existingUser.id, { id, expires_at }];
}

throw new Error("could not find oauth user");
return await this.#orm.transaction(async (trx: DrizzleTransaction) => {
const existingUser = await this.#repos.users.findByEmail(params.email, trx);

if (!existingUser) {
const userId = this.#createRandomUserId();

await this.#repos.users.insert({ userId, email: params.email }, trx);

const _insertedOAuthAccount = await this.#repos.oAuthAccounts.insert(params.email, {
provider_id: params.providerId,
provider_user_id: params.providerUserId,
user_id: userId,
}, trx);

const sessionFromRegistrationId = this.#createRandomSessionId();
const sessionFromRegistration = await this.#repos.sessions.insert(sessionFromRegistrationId, {
userId,
expiresAt: Date.now() + this.#sessionMaxAge,
ip: params.ip,
ua: params.ua,
}, trx);

throw new Error("could not find oauth user");
return [userId, sessionFromRegistration as SlipAuthSession];
}

const existingAccount = await this.#repos.oAuthAccounts.findByProviderData(
params.providerId, params.providerUserId,
);

if (existingUser && existingAccount?.provider_id !== params.providerId) {
throw new Error("user already have an account with another provider");
}

if (existingAccount) {
const sessionFromLoginId = this.#createRandomSessionId();
const sessionFromLogin = await this.#repos.sessions.insert(sessionFromLoginId, {
userId: existingUser.id,
expiresAt: Date.now() + this.#sessionMaxAge,
ua: params.ua,
ip: params.ip,
}, trx);

const { id, expires_at } = sessionFromLogin;

return [existingUser.id, { id, expires_at }];
}

throw new Error("could not find oauth user");
});
}

public setCreateRandomUserId(fn: () => string) {
Expand Down
6 changes: 4 additions & 2 deletions src/runtime/core/repositories/OAuthAccountsRepository.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { eq, and } from "drizzle-orm";
import { TableRepository } from "./_repo";
import type { DrizzleTransaction } from "../types";

export class OAuthAccountsRepository extends TableRepository<"oauthAccounts"> {
async insert(email: string, values: typeof this.table.$inferInsert): Promise<typeof this.table.$inferSelect> {
await this._orm
async insert(email: string, values: typeof this.table.$inferInsert, trx?: DrizzleTransaction): Promise<typeof this.table.$inferSelect> {
const orm = trx || this._orm;
await orm
.insert(this.table)
.values(values)
.run();
Expand Down
7 changes: 4 additions & 3 deletions src/runtime/core/repositories/SessionsRepository.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { eq, sql } from "drizzle-orm";
import { TableRepository } from "./_repo";
import type { ICreateSessionsParams } from "../types";
import type { ICreateSessionsParams, DrizzleTransaction } from "../types";

export class SessionsRepository extends TableRepository<"sessions"> {
async insert(sessionId: string, { userId, expiresAt, ip, ua }: ICreateSessionsParams): Promise<typeof this.table.$inferSelect> {
await this._orm
async insert(sessionId: string, { userId, expiresAt, ip, ua }: ICreateSessionsParams, trx?: DrizzleTransaction): Promise<typeof this.table.$inferSelect> {
const orm = trx || this._orm;
await orm
.insert(this.table)
.values({
id: sessionId,
Expand Down
6 changes: 4 additions & 2 deletions src/runtime/core/repositories/UsersRepository.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { eq } from "drizzle-orm";
import { TableRepository } from "./_repo";
import type { DrizzleTransaction } from "../types";

export class UsersRepository extends TableRepository<"users"> {
async insert(userId: string, email: string): Promise<typeof this.table.$inferSelect> {
await this._orm
async insert({ userId, email }: { userId: string, email: string }, trx?: DrizzleTransaction): Promise<typeof this.table.$inferSelect> {
const orm = trx ?? this._orm;
await orm
.insert(this.table)
.values({
id: userId,
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import type { drizzle as drizzleIntegration } from "db0/integrations/drizzle/index";
import type { SQLiteTable } from "drizzle-orm/sqlite-core";
import type { checkDbAndTables, tableNames } from "../database";
import { getOAuthAccountsTableSchema, getSessionsTableSchema, getUsersTableSchema } from "../database/lib/schema";

export type { tableNames };
export type { supportedConnectors } from "../database";

export type DrizzleTransaction = Parameters<Parameters<ReturnType<typeof drizzleIntegration>["transaction"]>["0"]>[0];

export type checkDbAndTablesParameters = Parameters<typeof checkDbAndTables>;

export interface ICreateOrLoginParams {
Expand Down