Skip to content

Commit a67b4fc

Browse files
Merge pull request #72 from datum-cloud/fix/sentry-errors
fix: fix errors from sentry
2 parents 806252c + 9569c1f commit a67b4fc

7 files changed

Lines changed: 69 additions & 20 deletions

File tree

apps/login/sentry.server.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ Sentry.init({
1212
replaysSessionSampleRate: 0.1,
1313
replaysOnErrorSampleRate: 1.0,
1414

15+
ignoreErrors: [
16+
// Malformed RSC requests from bots/crawlers sending invalid Next-Router-State-Tree headers
17+
"The router state header was sent but could not be parsed",
18+
// Node.js TransformStream race condition during RSC streaming (node:internal/webstreams bug)
19+
"transformAlgorithm is not a function",
20+
],
21+
1522
// Enable sending user PII (Personally Identifiable Information)
1623
// https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#sendDefaultPii
1724
sendDefaultPii: true,

apps/login/src/app/(main)/(boxed)/accounts/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { SessionsList } from "@/components/sessions-list";
22
import { Translated } from "@/components/translated";
33
import { getAllSessionCookieIds } from "@/lib/cookies";
44
import { generateRouteMetadata } from "@/lib/metadata";
5+
import { toPlainObject } from "@/lib/serialize";
56
import { getServiceUrlFromHeaders } from "@/lib/service-url";
67
import {
78
getBrandingSettings,
@@ -55,7 +56,7 @@ export default async function Page(props: {
5556
}
5657
}
5758

58-
let sessions = await loadSessions({ serviceUrl });
59+
let sessions = toPlainObject(await loadSessions({ serviceUrl }));
5960

6061
const branding = await getBrandingSettings({
6162
serviceUrl,

apps/login/src/app/(main)/(boxed)/idp/[provider]/success/page.tsx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,27 @@ export default async function Page(props: {
146146
return loginFailed("IDP context missing");
147147
}
148148

149-
const intent = await retrieveIDPIntent({
150-
serviceUrl,
151-
id,
152-
token,
153-
});
149+
let intent;
150+
try {
151+
intent = await retrieveIDPIntent({
152+
serviceUrl,
153+
id,
154+
token,
155+
});
156+
} catch (error) {
157+
if (
158+
error &&
159+
typeof error === "object" &&
160+
"code" in error &&
161+
error.code === 9 /* FAILED_PRECONDITION */
162+
) {
163+
// Intent was already consumed by the createNewSessionFromIdpIntent
164+
// server action. This re-render is expected — the client is already
165+
// processing the redirect, so return a benign loading state.
166+
return loginFailed("Session is being created, please wait…");
167+
}
168+
throw error;
169+
}
154170

155171
const { idpInformation, userId: intentUserId } = intent;
156172
const resolvedUserId = intentUserId || qpUserId;

apps/login/src/app/(main)/(boxed)/logout/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { SessionsClearList } from "@/components/sessions-clear-list";
22
import { Translated } from "@/components/translated";
33
import { getAllSessionCookieIds } from "@/lib/cookies";
44
import { generateRouteMetadata } from "@/lib/metadata";
5+
import { toPlainObject } from "@/lib/serialize";
56
import { getServiceUrlFromHeaders } from "@/lib/service-url";
67
import { getDefaultOrg, listSessions } from "@/lib/zitadel";
78
import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb";
@@ -47,7 +48,7 @@ export default async function Page(props: {
4748
}
4849
}
4950

50-
let sessions = await loadSessions({ serviceUrl });
51+
let sessions = toPlainObject(await loadSessions({ serviceUrl }));
5152

5253
const params = new URLSearchParams();
5354

apps/login/src/components/session-item.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ export function SessionItem({
6666
if (valid && session?.factors?.user) {
6767
try {
6868
const resp = await continueWithSession({
69-
...session,
69+
sessionId: session.id,
70+
loginName: session.factors?.user?.loginName,
71+
organizationId: session.factors?.user?.organizationId,
7072
requestId: requestId,
7173
});
7274

apps/login/src/lib/serialize.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Converts protobuf message objects into plain JSON-serializable objects
3+
* suitable for passing across the React Server Component → Client Component
4+
* boundary. Protobuf-ES v2 messages can contain bigint values and
5+
* null-prototype objects that RSC serialization rejects.
6+
*/
7+
export function toPlainObject<T>(obj: T): T {
8+
return JSON.parse(
9+
JSON.stringify(obj, (_, v) => (typeof v === "bigint" ? Number(v) : v)),
10+
);
11+
}

apps/login/src/lib/server/session.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
} from "@/lib/zitadel";
1111
import { Duration } from "@zitadel/client";
1212
import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_pb";
13-
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
1413
import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
1514
import { headers } from "next/headers";
1615
import { getNextUrl } from "../client";
@@ -71,38 +70,50 @@ export async function skipMFAAndContinueWithNextUrl({
7170

7271
export async function continueWithSession({
7372
requestId,
74-
...session
75-
}: Session & { requestId?: string }) {
73+
sessionId,
74+
loginName,
75+
organizationId,
76+
}: {
77+
requestId?: string;
78+
sessionId: string;
79+
loginName?: string;
80+
organizationId?: string;
81+
}) {
7682
const _headers = await headers();
7783
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
7884

7985
const loginSettings = await getLoginSettings({
8086
serviceUrl,
81-
organization: session.factors?.user?.organizationId,
87+
organization: organizationId,
8288
});
8389

8490
const url =
85-
requestId && session.id && session.factors?.user
91+
requestId && sessionId
8692
? await getNextUrl(
8793
{
88-
sessionId: session.id,
89-
requestId: requestId,
90-
organization: session.factors.user.organizationId,
94+
sessionId,
95+
requestId,
96+
organization: organizationId,
9197
},
9298
loginSettings?.defaultRedirectUri,
9399
)
94-
: session.factors?.user
100+
: loginName
95101
? await getNextUrl(
96102
{
97-
loginName: session.factors.user.loginName,
98-
organization: session.factors.user.organizationId,
103+
loginName,
104+
organization: organizationId,
99105
},
100106
loginSettings?.defaultRedirectUri,
101107
)
102108
: null;
103109

104110
if (!url) {
105-
console.error("Could not get next url", { requestId, session });
111+
console.error("Could not get next url", {
112+
requestId,
113+
sessionId,
114+
loginName,
115+
organizationId,
116+
});
106117
}
107118

108119
return url ? { redirect: url } : null;

0 commit comments

Comments
 (0)