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

Commit

Permalink
feat: Link the auth_oauth2 module to the auth_provider module
Browse files Browse the repository at this point in the history
  • Loading branch information
Blckbrry-Pi committed Jul 2, 2024
1 parent d347cbb commit 296d866
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ CREATE TABLE "LoginAttempts" (
"state" TEXT NOT NULL,
"codeVerifier" TEXT NOT NULL,
"identifier" TEXT,
"tokenData" JSONB,
"startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"expiresAt" TIMESTAMP(3) NOT NULL,
"completedAt" TIMESTAMP(3),
Expand Down
1 change: 1 addition & 0 deletions modules/auth_oauth2/db/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ model LoginAttempts {
codeVerifier String
identifier String?
tokenData Json?
startedAt DateTime @default(now())
expiresAt DateTime
Expand Down
10 changes: 10 additions & 0 deletions modules/auth_oauth2/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@
"name": "Get Status",
"description": "Check the status of a OAuth login using the flow token. Returns the status of the login flow.",
"public": true
},
"add_to_user": {
"name": "Add OAuth Login to User",
"description": "Use a finished OAuth flow to add the OAuth login to an already-authenticated users.",
"public": true
},
"login_to_user": {
"name": "Login to or Create User with OAuth",
"description": "Use a finished OAuth flow to login to a user, creating a new one if it doesn't exist.",
"public": true
}
},
"errors": {
Expand Down
1 change: 1 addition & 0 deletions modules/auth_oauth2/routes/login_callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export async function handle(
},
data: {
identifier: ident,
tokenData: { ...tokens },
completedAt: new Date(),
},
});
Expand Down
64 changes: 64 additions & 0 deletions modules/auth_oauth2/scripts/add_to_user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { RuntimeError, ScriptContext } from "../module.gen.ts";

export interface Request {
flowToken: string;
userToken: string;
}

export type Response = ReturnType<ScriptContext["modules"]["authProviders"]["addProviderToUser"]>;

export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
await ctx.modules.rateLimit.throttlePublic({});

if (!req.flowToken) throw new RuntimeError("missing_token", { statusCode: 400 });

const { tokens: [flowToken] } = await ctx.modules.tokens.fetchByToken({ tokens: [req.flowToken] });
if (!flowToken) {
throw new RuntimeError("invalid_token", { statusCode: 400 });
}
if (new Date(flowToken.expireAt ?? 0) < new Date()) {
throw new RuntimeError("expired_token", { statusCode: 400 });
}

const flowId = flowToken.meta.flowId;
if (!flowId) throw new RuntimeError("invalid_token", { statusCode: 400 });

const flow = await ctx.db.loginAttempts.findFirst({
where: {
id: flowId,
}
});
if (!flow) throw new RuntimeError("invalid_token", { statusCode: 400 });

if (!flow.identifier || !flow.tokenData) {
throw new RuntimeError("flow_not_complete", { statusCode: 400 });
}

await ctx.modules.users.authenticateToken({ userToken: req.userToken });

const tokenData = flow.tokenData;
if (!tokenData) {
throw new RuntimeError("internal_error", { statusCode: 500 });
}
if (typeof tokenData !== "object") {
throw new RuntimeError("internal_error", { statusCode: 500 });
}
if (Array.isArray(tokenData)) {
throw new RuntimeError("internal_error", { statusCode: 500 });
}

return await ctx.modules.authProviders.addProviderToUser({
userToken: req.userToken,
info: {
providerType: "oauth2",
providerId: flow.providerId,
},
uniqueData: {
identifier: flow.identifier,
},
additionalData: tokenData,
});
}
2 changes: 1 addition & 1 deletion modules/auth_oauth2/scripts/get_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function run(
});
if (!flow) throw new RuntimeError("invalid_token", { statusCode: 400 });

if (flow.identifier) {
if (flow.identifier && flow.tokenData) {
return { status: "complete" };
} else if (new Date(flow.expiresAt) < new Date()) {
return { status: "expired" };
Expand Down
60 changes: 60 additions & 0 deletions modules/auth_oauth2/scripts/login_to_user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { RuntimeError, ScriptContext } from "../module.gen.ts";

export interface Request {
flowToken: string;
}

export type Response = ReturnType<ScriptContext["modules"]["authProviders"]["getOrCreateUserFromProvider"]>;

export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
await ctx.modules.rateLimit.throttlePublic({});

if (!req.flowToken) throw new RuntimeError("missing_token", { statusCode: 400 });

const { tokens: [flowToken] } = await ctx.modules.tokens.fetchByToken({ tokens: [req.flowToken] });
if (!flowToken) {
throw new RuntimeError("invalid_token", { statusCode: 400 });
}
if (new Date(flowToken.expireAt ?? 0) < new Date()) {
throw new RuntimeError("expired_token", { statusCode: 400 });
}

const flowId = flowToken.meta.flowId;
if (!flowId) throw new RuntimeError("invalid_token", { statusCode: 400 });

const flow = await ctx.db.loginAttempts.findFirst({
where: {
id: flowId,
}
});
if (!flow) throw new RuntimeError("invalid_token", { statusCode: 400 });

if (!flow.identifier || !flow.tokenData) {
throw new RuntimeError("flow_not_complete", { statusCode: 400 });
}

const tokenData = flow.tokenData;
if (!tokenData) {
throw new RuntimeError("internal_error", { statusCode: 500 });
}
if (typeof tokenData !== "object") {
throw new RuntimeError("internal_error", { statusCode: 500 });
}
if (Array.isArray(tokenData)) {
throw new RuntimeError("internal_error", { statusCode: 500 });
}

return await ctx.modules.authProviders.getOrCreateUserFromProvider({
info: {
providerType: "oauth2",
providerId: flow.providerId,
},
uniqueData: {
identifier: flow.identifier,
},
additionalData: tokenData,
});
}

0 comments on commit 296d866

Please sign in to comment.