Skip to content
This repository was archived by the owner on Sep 17, 2024. It is now read-only.
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ CREATE TABLE "ProviderEntries" (
"userId" UUID NOT NULL,
"providerType" TEXT NOT NULL,
"providerId" TEXT NOT NULL,
"providerData" JSONB NOT NULL,
"uniqueData" JSONB NOT NULL,
"additionalData" JSONB NOT NULL,

CONSTRAINT "ProviderEntries_pkey" PRIMARY KEY ("userId","providerType","providerId")
);
Expand All @@ -13,3 +14,9 @@ CREATE INDEX "ProviderEntries_userId_idx" ON "ProviderEntries"("userId");

-- CreateIndex
CREATE INDEX "ProviderEntries_providerType_providerId_idx" ON "ProviderEntries"("providerType", "providerId");

-- CreateIndex
CREATE INDEX "ProviderEntries_providerType_providerId_uniqueData_idx" ON "ProviderEntries"("providerType", "providerId", "uniqueData");

-- CreateIndex
CREATE UNIQUE INDEX "ProviderEntries_providerType_providerId_uniqueData_key" ON "ProviderEntries"("providerType", "providerId", "uniqueData");
14 changes: 12 additions & 2 deletions modules/auth_providers/db/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@ datasource db {

model ProviderEntries {
userId String @db.Uuid

providerType String
providerId String
providerData Json
uniqueData Json

@@id([userId, providerType, providerId])
additionalData Json

// Additional indexes for speed
@@index([userId])
@@index([providerType, providerId])

// Each user should only have one identity per provider (for now)
@@id([userId, providerType, providerId])

// Each provider identity should only be linked to one user
@@index([providerType, providerId, uniqueData])
@@unique([providerType, providerId, uniqueData])
}
4 changes: 4 additions & 0 deletions modules/auth_providers/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
"name": "Set Provider Data",
"description": "Set the data associated with a specific provider for a user."
},
"get_or_create_user_from_provider": {
"name": "Get or Create User From Provider",
"description": "Using provider info and data, match an existing user or create a new one based on the data."
},
"add_provider_to_user": {
"name": "Add Provider To User",
"description": "Add a new provider and its associated data to a user."
Expand Down
10 changes: 6 additions & 4 deletions modules/auth_providers/scripts/add_provider_to_user.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { RuntimeError, ScriptContext, prisma } from "../module.gen.ts";
import { ProviderData, ProviderInfo } from "../utils/types.ts";
import { RuntimeError, ScriptContext } from "../module.gen.ts";
import { ProviderDataInput, ProviderInfo } from "../utils/types.ts";

export interface Request {
userToken: string;
info: ProviderInfo;
data: ProviderData & prisma.Prisma.InputJsonValue;
uniqueData: ProviderDataInput;
additionalData: ProviderDataInput;
}

export interface Response {
Expand Down Expand Up @@ -35,7 +36,8 @@ export async function run(
userId,
providerType: req.info.providerType,
providerId: req.info.providerId,
providerData: req.data,
uniqueData: req.uniqueData,
additionalData: req.additionalData,
},
});

Expand Down
65 changes: 65 additions & 0 deletions modules/auth_providers/scripts/get_or_create_user_from_provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { ScriptContext } from "../module.gen.ts";
import { ProviderDataInput, ProviderInfo } from "../utils/types.ts";

export interface Request {
info: ProviderInfo;
uniqueData: ProviderDataInput;
additionalData: ProviderDataInput;

suggestedUsername?: string;
}

export interface Response {
userToken: string;
}

export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
const key = req.info.providerType + ":" + req.info.providerId + ":" + JSON.stringify(req.uniqueData);
await ctx.modules.rateLimit.throttle({
key,
period: 10,
requests: 10,
type: "user",
});

// Get users the provider is associated with
const providers = await ctx.db.providerEntries.findFirst({
where: {
providerType: req.info.providerType,
providerId: req.info.providerId,
uniqueData: { equals: req.uniqueData },
},
select: {
userId: true,
},
});

// If the provider is associated with a user, generate a user token and
// return it
if (providers) {
const { token: { token } } = await ctx.modules.users.createToken({ userId: providers.userId });
return { userToken: token };
}

// If the provider is not associated with a user, create a new user
const { user } = await ctx.modules.users.create({ username: req.suggestedUsername });

// Insert the provider data with the newly-created user
await ctx.db.providerEntries.create({
data: {
userId: user.id,
providerType: req.info.providerType,
providerId: req.info.providerId,
uniqueData: req.uniqueData,
additionalData: req.additionalData,
},
});

// Generate a user token and return it
const { token: { token } } = await ctx.modules.users.createToken({ userId: user.id });

return { userToken: token };
}
26 changes: 20 additions & 6 deletions modules/auth_providers/scripts/get_provider_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ export interface Request {
}

export interface Response {
data: ProviderData | null;
data: {
uniqueData: ProviderData;
additionalData: ProviderData;
} | null;
}

export async function run(
Expand All @@ -21,24 +24,35 @@ export async function run(
type: "user",
});

// Ensure the user token is valid and get the user ID
const { userId } = await ctx.modules.users.authenticateToken({ userToken: req.userToken } );

// Get provider data
const provider = await ctx.db.providerEntries.findFirst({
where: {
userId,
providerType: req.info.providerType,
providerId: req.info.providerId,
},
select: {
providerData: true,
uniqueData: true,
additionalData: true
}
});

const data = provider?.providerData;
// Type checking to make typescript happy
const data = provider ?? null;
if (!data) {
return { data: null };
}

if (data && typeof data === 'object' && !Array.isArray(data)) {
return { data };
} else {
const { uniqueData, additionalData } = data;
if (typeof uniqueData !== 'object' || Array.isArray(uniqueData) || uniqueData === null) {
return { data: null };
}
if (typeof additionalData !== 'object' || Array.isArray(additionalData) || additionalData === null) {
return { data: null };
}

return { data: { uniqueData, additionalData } };
}
24 changes: 13 additions & 11 deletions modules/auth_providers/scripts/list_providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ export async function run(
): Promise<Response> {
await ctx.modules.rateLimit.throttlePublic({});

// Ensure the user token is valid and get the user ID
const { userId } = await ctx.modules.users.authenticateToken({ userToken: req.userToken } );

return {
providers: await ctx.db.providerEntries.findMany({
where: {
userId,
},
select: {
providerType: true,
providerId: true,
}
}),
};
// Select providerType and providerId entries that match the userId
const providers = await ctx.db.providerEntries.findMany({
where: {
userId,
},
select: {
providerType: true,
providerId: true,
}
});

return { providers };
}
14 changes: 8 additions & 6 deletions modules/auth_providers/scripts/set_provider_data.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ScriptContext, Empty, RuntimeError, prisma } from "../module.gen.ts";
import { ProviderData, ProviderInfo } from "../utils/types.ts";
import { ScriptContext, Empty, RuntimeError } from "../module.gen.ts";
import { ProviderDataInput, ProviderInfo } from "../utils/types.ts";

export interface Request {
userToken: string;
info: ProviderInfo;
data: ProviderData & prisma.Prisma.InputJsonValue;
uniqueData?: ProviderDataInput;
additionalData: ProviderDataInput;
}

export type Response = Empty;
Expand Down Expand Up @@ -36,9 +37,10 @@ export async function run(
providerId: req.info.providerId,
}
},
data: {
providerData: req.data,
},
data: req.uniqueData ? {
uniqueData: req.uniqueData,
additionalData: req.additionalData,
} : { additionalData: req.additionalData },
});

return {};
Expand Down
3 changes: 3 additions & 0 deletions modules/auth_providers/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { prisma } from "../module.gen.ts";

export type ProviderData = Record<string, unknown>;
export type ProviderDataInput = ProviderData & prisma.Prisma.InputJsonValue;

export interface ProviderInfo {
providerType: string;
Expand Down