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

Commit 39e4201

Browse files
committed
feat: Create the auth_provider module
1 parent bbb15a8 commit 39e4201

File tree

11 files changed

+267
-0
lines changed

11 files changed

+267
-0
lines changed

modules/auth_providers/config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export interface Config {
2+
providers: Record<string, ProviderEndpoints | string>;
3+
}
4+
5+
export interface ProviderEndpoints {
6+
authorization: string;
7+
token: string;
8+
userinfo: string;
9+
scopes: string;
10+
userinfoKey: string;
11+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- CreateTable
2+
CREATE TABLE "ProviderEntries" (
3+
"userId" UUID NOT NULL,
4+
"providerType" TEXT NOT NULL,
5+
"providerId" TEXT NOT NULL,
6+
"providerData" JSONB NOT NULL,
7+
8+
CONSTRAINT "ProviderEntries_pkey" PRIMARY KEY ("userId","providerType","providerId")
9+
);
10+
11+
-- CreateIndex
12+
CREATE INDEX "ProviderEntries_userId_idx" ON "ProviderEntries"("userId");
13+
14+
-- CreateIndex
15+
CREATE INDEX "ProviderEntries_providerType_providerId_idx" ON "ProviderEntries"("providerType", "providerId");
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Please do not edit this file manually
2+
# It should be added in your version-control system (i.e. Git)
3+
provider = "postgresql"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Do not modify this `datasource` block
2+
datasource db {
3+
provider = "postgresql"
4+
url = env("DATABASE_URL")
5+
}
6+
7+
model ProviderEntries {
8+
userId String @db.Uuid
9+
providerType String
10+
providerId String
11+
providerData Json
12+
13+
@@id([userId, providerType, providerId])
14+
@@index([userId])
15+
@@index([providerType, providerId])
16+
}

modules/auth_providers/module.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "OAuth2 Authentication Provider",
3+
"description": "Authenticate users with OAuth 2.0.",
4+
"icon": "key",
5+
"tags": [
6+
"core",
7+
"user",
8+
"auth"
9+
],
10+
"authors": [
11+
"rivet-gg",
12+
"Skyler Calaman"
13+
],
14+
"status": "beta",
15+
"dependencies": {
16+
"rate_limit": {},
17+
"users": {},
18+
"tokens": {}
19+
},
20+
"scripts": {
21+
"list_providers": {
22+
"name": "List Providers",
23+
"description": "List all providers the user is identified with.",
24+
"public": true
25+
},
26+
"get_provider_data": {
27+
"name": "Get Provider Data",
28+
"description": "Get the data associated with a specific provider for a user."
29+
},
30+
"set_provider_data": {
31+
"name": "Set Provider Data",
32+
"description": "Set the data associated with a specific provider for a user."
33+
},
34+
"add_provider_to_user": {
35+
"name": "Add Provider To User",
36+
"description": "Add a new provider and its associated data to a user."
37+
}
38+
},
39+
"routes": {},
40+
"errors": {
41+
"provider_not_found": {
42+
"name": "Provider Not Found"
43+
},
44+
"provider_already_added": {
45+
"name": "Provider Already Added"
46+
}
47+
}
48+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { RuntimeError, ScriptContext, prisma } from "../module.gen.ts";
2+
import { ProviderData, ProviderInfo } from "../utils/types.ts";
3+
4+
export interface Request {
5+
userToken: string;
6+
info: ProviderInfo;
7+
data: ProviderData & prisma.Prisma.InputJsonValue;
8+
}
9+
10+
export interface Response {
11+
providers: ProviderInfo[];
12+
}
13+
14+
export async function run(
15+
ctx: ScriptContext,
16+
req: Request,
17+
): Promise<Response> {
18+
await ctx.modules.rateLimit.throttle({
19+
key: req.userToken,
20+
period: 10,
21+
requests: 10,
22+
type: "user",
23+
});
24+
25+
// Ensure the user token is valid and get the user ID
26+
const { userId } = await ctx.modules.users.authenticateToken({ userToken: req.userToken } );
27+
28+
// Error if this provider is ALREADY associated with the user
29+
const { data: prevData } = await ctx.modules.authProviders.getProviderData({ userToken: req.userToken, info: req.info });
30+
if (prevData) throw new RuntimeError("provider_already_added");
31+
32+
// Add a new entry to the table with the associated data
33+
await ctx.db.providerEntries.create({
34+
data: {
35+
userId,
36+
providerType: req.info.providerType,
37+
providerId: req.info.providerId,
38+
providerData: req.data,
39+
},
40+
});
41+
42+
return await ctx.modules.authProviders.listProviders({ userToken: req.userToken });
43+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { ScriptContext } from "../module.gen.ts";
2+
import { ProviderData, ProviderInfo } from "../utils/types.ts";
3+
4+
export interface Request {
5+
userToken: string;
6+
info: ProviderInfo;
7+
}
8+
9+
export interface Response {
10+
data: ProviderData | null;
11+
}
12+
13+
export async function run(
14+
ctx: ScriptContext,
15+
req: Request,
16+
): Promise<Response> {
17+
await ctx.modules.rateLimit.throttle({
18+
key: req.userToken,
19+
period: 10,
20+
requests: 10,
21+
type: "user",
22+
});
23+
24+
const { userId } = await ctx.modules.users.authenticateToken({ userToken: req.userToken } );
25+
26+
const provider = await ctx.db.providerEntries.findFirst({
27+
where: {
28+
userId,
29+
providerType: req.info.providerType,
30+
providerId: req.info.providerId,
31+
},
32+
select: {
33+
providerData: true,
34+
}
35+
});
36+
37+
const data = provider?.providerData;
38+
39+
if (data && typeof data === 'object' && !Array.isArray(data)) {
40+
return { data };
41+
} else {
42+
return { data: null };
43+
}
44+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ScriptContext } from "../module.gen.ts";
2+
import { ProviderInfo } from "../utils/types.ts";
3+
4+
export interface Request {
5+
userToken: string;
6+
}
7+
8+
export interface Response {
9+
providers: ProviderInfo[];
10+
}
11+
12+
export async function run(
13+
ctx: ScriptContext,
14+
req: Request,
15+
): Promise<Response> {
16+
await ctx.modules.rateLimit.throttlePublic({});
17+
18+
const { userId } = await ctx.modules.users.authenticateToken({ userToken: req.userToken } );
19+
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+
};
31+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { ScriptContext, Empty, RuntimeError, prisma } from "../module.gen.ts";
2+
import { ProviderData, ProviderInfo } from "../utils/types.ts";
3+
4+
export interface Request {
5+
userToken: string;
6+
info: ProviderInfo;
7+
data: ProviderData & prisma.Prisma.InputJsonValue;
8+
}
9+
10+
export type Response = Empty;
11+
12+
export async function run(
13+
ctx: ScriptContext,
14+
req: Request,
15+
): Promise<Response> {
16+
await ctx.modules.rateLimit.throttle({
17+
key: req.userToken,
18+
period: 10,
19+
requests: 10,
20+
type: "user",
21+
});
22+
23+
// Ensure the user token is valid and get the user ID
24+
const { userId } = await ctx.modules.users.authenticateToken({ userToken: req.userToken } );
25+
26+
// Error if this provider is not associated with the user
27+
const { data: prevData } = await ctx.modules.authProviders.getProviderData({ userToken: req.userToken, info: req.info });
28+
if (!prevData) throw new RuntimeError("provider_not_found");
29+
30+
// Update the provider data where userId, providerType, and providerId match
31+
await ctx.db.providerEntries.update({
32+
where: {
33+
userId_providerType_providerId: {
34+
userId,
35+
providerType: req.info.providerType,
36+
providerId: req.info.providerId,
37+
}
38+
},
39+
data: {
40+
providerData: req.data,
41+
},
42+
});
43+
44+
return {};
45+
}

modules/auth_providers/utils/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export type ProviderData = Record<string, unknown>;
2+
3+
export interface ProviderInfo {
4+
providerType: string;
5+
providerId: string;
6+
}
7+
8+

0 commit comments

Comments
 (0)