121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
import { action } from "../_generated/server";
|
|
import { v } from "convex/values";
|
|
import { internal } from "../_generated/api";
|
|
import { getAuthUserId } from "@convex-dev/auth/server";
|
|
import {
|
|
activateSession,
|
|
getSecondaryToken,
|
|
getSessions,
|
|
oauthToken,
|
|
validateSession,
|
|
} from "./client";
|
|
|
|
function randomUuid(): string {
|
|
return crypto.randomUUID();
|
|
}
|
|
|
|
export const start = action({
|
|
args: {
|
|
zugangsnummer: v.string(),
|
|
pin: v.string(),
|
|
},
|
|
returns: v.object({
|
|
challengeType: v.string(),
|
|
photoTanPngBase64: v.optional(v.string()),
|
|
}),
|
|
handler: async (ctx, args) => {
|
|
const userId = await getAuthUserId(ctx);
|
|
if (!userId) throw new Error("Nicht angemeldet");
|
|
|
|
const clientId = process.env.COMDIRECT_CLIENT_ID;
|
|
const clientSecret = process.env.COMDIRECT_CLIENT_SECRET;
|
|
if (!clientId || !clientSecret) {
|
|
throw new Error("comdirect API-Zugangsdaten nicht konfiguriert");
|
|
}
|
|
|
|
const sessionUuid = randomUuid();
|
|
const tokenResponse = await oauthToken(
|
|
{
|
|
grant_type: "password",
|
|
username: args.zugangsnummer,
|
|
password: args.pin,
|
|
},
|
|
clientId,
|
|
clientSecret,
|
|
);
|
|
|
|
const sessions = await getSessions(tokenResponse.access_token, sessionUuid);
|
|
const session = sessions[0];
|
|
if (!session) throw new Error("Keine comdirect-Session gefunden");
|
|
|
|
const challenge = await validateSession(
|
|
tokenResponse.access_token,
|
|
sessionUuid,
|
|
session.identifier,
|
|
);
|
|
|
|
await ctx.runMutation(internal.comdirect.internal.upsertSession, {
|
|
userId,
|
|
sessionUuid,
|
|
identifier: session.identifier,
|
|
accessToken: tokenResponse.access_token,
|
|
refreshToken: tokenResponse.refresh_token,
|
|
secondaryActive: false,
|
|
challengeId: challenge.challengeId,
|
|
challengeType: challenge.challengeType,
|
|
status: "challenged",
|
|
expiresAt: Date.now() + tokenResponse.expires_in * 1000,
|
|
});
|
|
|
|
return {
|
|
challengeType: challenge.challengeType,
|
|
photoTanPngBase64:
|
|
challenge.challengeType === "P_TAN" ? challenge.challenge : undefined,
|
|
};
|
|
},
|
|
});
|
|
|
|
export const confirm = action({
|
|
args: { tan: v.optional(v.string()) },
|
|
returns: v.object({ success: v.boolean() }),
|
|
handler: async (ctx, args) => {
|
|
const userId = await getAuthUserId(ctx);
|
|
if (!userId) throw new Error("Nicht angemeldet");
|
|
|
|
const clientId = process.env.COMDIRECT_CLIENT_ID;
|
|
const clientSecret = process.env.COMDIRECT_CLIENT_SECRET;
|
|
if (!clientId || !clientSecret) {
|
|
throw new Error("comdirect API-Zugangsdaten nicht konfiguriert");
|
|
}
|
|
|
|
const session = await ctx.runQuery(internal.comdirect.internal.getSession, { userId });
|
|
if (!session?.accessToken || !session.identifier || !session.challengeId) {
|
|
throw new Error("Keine aktive comdirect-Session");
|
|
}
|
|
|
|
await activateSession(
|
|
session.accessToken,
|
|
session.sessionUuid,
|
|
session.identifier,
|
|
session.challengeId,
|
|
args.tan,
|
|
);
|
|
|
|
const secondary = await getSecondaryToken(session.accessToken, clientId, clientSecret);
|
|
|
|
await ctx.runMutation(internal.comdirect.internal.upsertSession, {
|
|
userId,
|
|
sessionUuid: session.sessionUuid,
|
|
identifier: session.identifier,
|
|
accessToken: secondary.access_token,
|
|
refreshToken: secondary.refresh_token,
|
|
secondaryActive: true,
|
|
challengeId: session.challengeId,
|
|
challengeType: session.challengeType,
|
|
status: "active",
|
|
});
|
|
|
|
return { success: true };
|
|
},
|
|
});
|