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

Commit c8e4b6a

Browse files
committed
refactor(auth_providers): split providerData into uniqueData and additionalData
1 parent d179f19 commit c8e4b6a

File tree

9 files changed

+139
-30
lines changed

9 files changed

+139
-30
lines changed

modules/auth_providers/db/migrations/20240629231731_init/migration.sql renamed to modules/auth_providers/db/migrations/20240701022604_init/migration.sql

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ CREATE TABLE "ProviderEntries" (
33
"userId" UUID NOT NULL,
44
"providerType" TEXT NOT NULL,
55
"providerId" TEXT NOT NULL,
6-
"providerData" JSONB NOT NULL,
6+
"uniqueData" JSONB NOT NULL,
7+
"additionalData" JSONB NOT NULL,
78

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

1415
-- CreateIndex
1516
CREATE INDEX "ProviderEntries_providerType_providerId_idx" ON "ProviderEntries"("providerType", "providerId");
17+
18+
-- CreateIndex
19+
CREATE INDEX "ProviderEntries_providerType_providerId_uniqueData_idx" ON "ProviderEntries"("providerType", "providerId", "uniqueData");
20+
21+
-- CreateIndex
22+
CREATE UNIQUE INDEX "ProviderEntries_providerType_providerId_uniqueData_key" ON "ProviderEntries"("providerType", "providerId", "uniqueData");

modules/auth_providers/db/schema.prisma

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,21 @@ datasource db {
66

77
model ProviderEntries {
88
userId String @db.Uuid
9+
910
providerType String
1011
providerId String
11-
providerData Json
12+
uniqueData Json
1213
13-
@@id([userId, providerType, providerId])
14+
additionalData Json
15+
16+
// Additional indexes for speed
1417
@@index([userId])
1518
@@index([providerType, providerId])
19+
20+
// Each user should only have one identity per provider (for now)
21+
@@id([userId, providerType, providerId])
22+
23+
// Each provider identity should only be linked to one user
24+
@@index([providerType, providerId, uniqueData])
25+
@@unique([providerType, providerId, uniqueData])
1626
}

modules/auth_providers/module.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
"name": "Set Provider Data",
3232
"description": "Set the data associated with a specific provider for a user."
3333
},
34+
"get_or_create_user_from_provider": {
35+
"name": "Get or Create User From Provider",
36+
"description": "Using provider info and data, match an existing user or create a new one based on the data."
37+
},
3438
"add_provider_to_user": {
3539
"name": "Add Provider To User",
3640
"description": "Add a new provider and its associated data to a user."

modules/auth_providers/scripts/add_provider_to_user.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { RuntimeError, ScriptContext, prisma } from "../module.gen.ts";
2-
import { ProviderData, ProviderInfo } from "../utils/types.ts";
1+
import { RuntimeError, ScriptContext } from "../module.gen.ts";
2+
import { ProviderDataInput, ProviderInfo } from "../utils/types.ts";
33

44
export interface Request {
55
userToken: string;
66
info: ProviderInfo;
7-
data: ProviderData & prisma.Prisma.InputJsonValue;
7+
uniqueData: ProviderDataInput;
8+
additionalData: ProviderDataInput;
89
}
910

1011
export interface Response {
@@ -35,7 +36,8 @@ export async function run(
3536
userId,
3637
providerType: req.info.providerType,
3738
providerId: req.info.providerId,
38-
providerData: req.data,
39+
uniqueData: req.uniqueData,
40+
additionalData: req.additionalData,
3941
},
4042
});
4143

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { ScriptContext } from "../module.gen.ts";
2+
import { ProviderDataInput, ProviderInfo } from "../utils/types.ts";
3+
4+
export interface Request {
5+
info: ProviderInfo;
6+
uniqueData: ProviderDataInput;
7+
additionalData: ProviderDataInput;
8+
9+
suggestedUsername?: string;
10+
}
11+
12+
export interface Response {
13+
userToken: string;
14+
}
15+
16+
export async function run(
17+
ctx: ScriptContext,
18+
req: Request,
19+
): Promise<Response> {
20+
const key = req.info.providerType + ":" + req.info.providerId + ":" + JSON.stringify(req.uniqueData);
21+
await ctx.modules.rateLimit.throttle({
22+
key,
23+
period: 10,
24+
requests: 10,
25+
type: "user",
26+
});
27+
28+
// Get users the provider is associated with
29+
const providers = await ctx.db.providerEntries.findFirst({
30+
where: {
31+
providerType: req.info.providerType,
32+
providerId: req.info.providerId,
33+
uniqueData: { equals: req.uniqueData },
34+
},
35+
select: {
36+
userId: true,
37+
},
38+
});
39+
40+
// If the provider is associated with a user, generate a user token and
41+
// return it
42+
if (providers) {
43+
const { token: { token } } = await ctx.modules.users.createToken({ userId: providers.userId });
44+
return { userToken: token };
45+
}
46+
47+
// If the provider is not associated with a user, create a new user
48+
const { user } = await ctx.modules.users.create({ username: req.suggestedUsername });
49+
50+
// Insert the provider data with the newly-created user
51+
await ctx.db.providerEntries.create({
52+
data: {
53+
userId: user.id,
54+
providerType: req.info.providerType,
55+
providerId: req.info.providerId,
56+
uniqueData: req.uniqueData,
57+
additionalData: req.additionalData,
58+
},
59+
});
60+
61+
// Generate a user token and return it
62+
const { token: { token } } = await ctx.modules.users.createToken({ userId: user.id });
63+
64+
return { userToken: token };
65+
}

modules/auth_providers/scripts/get_provider_data.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ export interface Request {
77
}
88

99
export interface Response {
10-
data: ProviderData | null;
10+
data: {
11+
uniqueData: ProviderData;
12+
additionalData: ProviderData;
13+
} | null;
1114
}
1215

1316
export async function run(
@@ -21,24 +24,35 @@ export async function run(
2124
type: "user",
2225
});
2326

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

30+
// Get provider data
2631
const provider = await ctx.db.providerEntries.findFirst({
2732
where: {
2833
userId,
2934
providerType: req.info.providerType,
3035
providerId: req.info.providerId,
3136
},
3237
select: {
33-
providerData: true,
38+
uniqueData: true,
39+
additionalData: true
3440
}
3541
});
3642

37-
const data = provider?.providerData;
43+
// Type checking to make typescript happy
44+
const data = provider ?? null;
45+
if (!data) {
46+
return { data: null };
47+
}
3848

39-
if (data && typeof data === 'object' && !Array.isArray(data)) {
40-
return { data };
41-
} else {
49+
const { uniqueData, additionalData } = data;
50+
if (typeof uniqueData !== 'object' || Array.isArray(uniqueData) || uniqueData === null) {
4251
return { data: null };
4352
}
53+
if (typeof additionalData !== 'object' || Array.isArray(additionalData) || additionalData === null) {
54+
return { data: null };
55+
}
56+
57+
return { data: { uniqueData, additionalData } };
4458
}

modules/auth_providers/scripts/list_providers.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ export async function run(
1515
): Promise<Response> {
1616
await ctx.modules.rateLimit.throttlePublic({});
1717

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

20-
return {
21-
providers: await ctx.db.providerEntries.findMany({
22-
where: {
23-
userId,
24-
},
25-
select: {
26-
providerType: true,
27-
providerId: true,
28-
}
29-
}),
30-
};
21+
// Select providerType and providerId entries that match the userId
22+
const providers = await ctx.db.providerEntries.findMany({
23+
where: {
24+
userId,
25+
},
26+
select: {
27+
providerType: true,
28+
providerId: true,
29+
}
30+
});
31+
32+
return { providers };
3133
}

modules/auth_providers/scripts/set_provider_data.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { ScriptContext, Empty, RuntimeError, prisma } from "../module.gen.ts";
2-
import { ProviderData, ProviderInfo } from "../utils/types.ts";
1+
import { ScriptContext, Empty, RuntimeError } from "../module.gen.ts";
2+
import { ProviderDataInput, ProviderInfo } from "../utils/types.ts";
33

44
export interface Request {
55
userToken: string;
66
info: ProviderInfo;
7-
data: ProviderData & prisma.Prisma.InputJsonValue;
7+
uniqueData?: ProviderDataInput;
8+
additionalData: ProviderDataInput;
89
}
910

1011
export type Response = Empty;
@@ -36,9 +37,10 @@ export async function run(
3637
providerId: req.info.providerId,
3738
}
3839
},
39-
data: {
40-
providerData: req.data,
41-
},
40+
data: req.uniqueData ? {
41+
uniqueData: req.uniqueData,
42+
additionalData: req.additionalData,
43+
} : { additionalData: req.additionalData },
4244
});
4345

4446
return {};

modules/auth_providers/utils/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import { prisma } from "../module.gen.ts";
2+
13
export type ProviderData = Record<string, unknown>;
4+
export type ProviderDataInput = ProviderData & prisma.Prisma.InputJsonValue;
25

36
export interface ProviderInfo {
47
providerType: string;

0 commit comments

Comments
 (0)