Skip to content
This repository has been archived by the owner on Sep 17, 2024. It is now read-only.

Commit

Permalink
refactor(auth_providers): split providerData into uniqueData and …
Browse files Browse the repository at this point in the history
…`additionalData`
  • Loading branch information
Blckbrry-Pi committed Jul 2, 2024
1 parent b41f94e commit 30af057
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 30 deletions.
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

0 comments on commit 30af057

Please sign in to comment.