Skip to content

Commit e51d839

Browse files
committed
Retry OAuth endpoints
1 parent f549b95 commit e51d839

File tree

12 files changed

+207
-32
lines changed

12 files changed

+207
-32
lines changed

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
"mtsx",
2424
"nextjs",
2525
"Nicifiable",
26+
"nicification",
27+
"nicified",
2628
"nicify",
2729
"oidc",
2830
"openapi",

apps/backend/sentry.client.config.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
44

55
import * as Sentry from "@sentry/nextjs";
6+
import { nicify } from "@stackframe/stack-shared/dist/utils/strings";
67

78
Sentry.init({
89
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
@@ -29,4 +30,26 @@ Sentry.init({
2930
blockAllMedia: false,
3031
}),
3132
],
33+
34+
// Add exception metadata to the event
35+
beforeSend(event, hint) {
36+
const error = hint.originalException;
37+
let nicified;
38+
try {
39+
nicified = nicify(error);
40+
} catch (e) {
41+
nicified = `Error occurred during nicification: ${e}`;
42+
}
43+
if (error instanceof Error) {
44+
event.extra = {
45+
...event.extra,
46+
cause: error.cause,
47+
errorProps: {
48+
...error,
49+
},
50+
nicifiedError: nicify(error),
51+
};
52+
}
53+
return event;
54+
},
3255
});

apps/backend/sentry.edge.config.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
55

66
import * as Sentry from "@sentry/nextjs";
7+
import { nicify } from "@stackframe/stack-shared/dist/utils/strings";
78

89
Sentry.init({
910
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
@@ -15,4 +16,26 @@ Sentry.init({
1516
debug: false,
1617

1718
enabled: process.env.NODE_ENV !== "development" && !process.env.CI,
19+
20+
// Add exception metadata to the event
21+
beforeSend(event, hint) {
22+
const error = hint.originalException;
23+
let nicified;
24+
try {
25+
nicified = nicify(error);
26+
} catch (e) {
27+
nicified = `Error occurred during nicification: ${e}`;
28+
}
29+
if (error instanceof Error) {
30+
event.extra = {
31+
...event.extra,
32+
cause: error.cause,
33+
errorProps: {
34+
...error,
35+
},
36+
nicifiedError: nicify(error),
37+
};
38+
}
39+
return event;
40+
},
1841
});

apps/backend/sentry.server.config.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
44

55
import * as Sentry from "@sentry/nextjs";
6+
import { nicify } from "@stackframe/stack-shared/dist/utils/strings";
67

78
Sentry.init({
89
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
@@ -14,4 +15,26 @@ Sentry.init({
1415
debug: false,
1516

1617
enabled: process.env.NODE_ENV !== "development" && !process.env.CI,
18+
19+
// Add exception metadata to the event
20+
beforeSend(event, hint) {
21+
const error = hint.originalException;
22+
let nicified;
23+
try {
24+
nicified = nicify(error);
25+
} catch (e) {
26+
nicified = `Error occurred during nicification: ${e}`;
27+
}
28+
if (error instanceof Error) {
29+
event.extra = {
30+
...event.extra,
31+
cause: error.cause,
32+
errorProps: {
33+
...error,
34+
},
35+
nicifiedError: nicify(error),
36+
};
37+
}
38+
return event;
39+
},
1740
});

apps/dashboard/sentry.client.config.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
44

55
import * as Sentry from "@sentry/nextjs";
6+
import { nicify } from "@stackframe/stack-shared/dist/utils/strings";
67

78
Sentry.init({
89
dsn: "https://6e618f142965a385267f1030793e0400@o4507084192022528.ingest.us.sentry.io/4507084192219136",
@@ -29,4 +30,26 @@ Sentry.init({
2930
blockAllMedia: false,
3031
}),
3132
],
33+
34+
// Add exception metadata to the event
35+
beforeSend(event, hint) {
36+
const error = hint.originalException;
37+
let nicified;
38+
try {
39+
nicified = nicify(error);
40+
} catch (e) {
41+
nicified = `Error occurred during nicification: ${e}`;
42+
}
43+
if (error instanceof Error) {
44+
event.extra = {
45+
...event.extra,
46+
cause: error.cause,
47+
errorProps: {
48+
...error,
49+
},
50+
nicifiedError: nicify(error),
51+
};
52+
}
53+
return event;
54+
},
3255
});

apps/dashboard/sentry.edge.config.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
55

66
import * as Sentry from "@sentry/nextjs";
7+
import { nicify } from "@stackframe/stack-shared/dist/utils/strings";
78

89
Sentry.init({
910
dsn: "https://6e618f142965a385267f1030793e0400@o4507084192022528.ingest.us.sentry.io/4507084192219136",
@@ -15,4 +16,26 @@ Sentry.init({
1516
debug: false,
1617

1718
enabled: process.env.NODE_ENV !== "development" && !process.env.CI,
19+
20+
// Add exception metadata to the event
21+
beforeSend(event, hint) {
22+
const error = hint.originalException;
23+
let nicified;
24+
try {
25+
nicified = nicify(error);
26+
} catch (e) {
27+
nicified = `Error occurred during nicification: ${e}`;
28+
}
29+
if (error instanceof Error) {
30+
event.extra = {
31+
...event.extra,
32+
cause: error.cause,
33+
errorProps: {
34+
...error,
35+
},
36+
nicifiedError: nicify(error),
37+
};
38+
}
39+
return event;
40+
},
1841
});

apps/dashboard/sentry.server.config.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
44

55
import * as Sentry from "@sentry/nextjs";
6+
import { nicify } from "@stackframe/stack-shared/dist/utils/strings";
67

78
Sentry.init({
89
dsn: "https://6e618f142965a385267f1030793e0400@o4507084192022528.ingest.us.sentry.io/4507084192219136",
@@ -14,4 +15,26 @@ Sentry.init({
1415
debug: false,
1516

1617
enabled: process.env.NODE_ENV !== "development" && !process.env.CI,
18+
19+
// Add exception metadata to the event
20+
beforeSend(event, hint) {
21+
const error = hint.originalException;
22+
let nicified;
23+
try {
24+
nicified = nicify(error);
25+
} catch (e) {
26+
nicified = `Error occurred during nicification: ${e}`;
27+
}
28+
if (error instanceof Error) {
29+
event.extra = {
30+
...event.extra,
31+
cause: error.cause,
32+
errorProps: {
33+
...error,
34+
},
35+
nicifiedError: nicify(error),
36+
};
37+
}
38+
return event;
39+
},
1740
});

examples/demo/src/app/page-client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export default function PageClient() {
2727
<div className='flex flex-col gap-5 justify-center items-center'>
2828
<Typography type='h3'>Logged in as: <span className='font-bold'>{user.primaryEmail}</span></Typography>
2929
<Typography>Click on your user&apos;s image at the top right to see your account settings.</Typography>
30-
<Typography>Like what you see? <Link href="https://app.stackframe.co">Create your own project</Link> on our dashboard.</Typography>
30+
<Typography>Like what you see? <Link href="https://app.stack-auth.com">Create your own project</Link> on our dashboard.</Typography>
3131
<Link href={app.urls.signOut}>
3232
Sign Out
3333
</Link>

packages/stack-shared/src/interface/clientInterface.ts

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,13 @@ export class StackClientInterface {
6666
}
6767
});
6868
const prodDashboard = await tryRequest(async () => {
69-
const res = await fetch("https://app.stackframe.com/health");
69+
const res = await fetch("https://app.stack-auth.com/health");
7070
if (!res.ok) {
7171
throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`);
7272
}
7373
});
7474
const prodBackend = await tryRequest(async () => {
75-
const res = await fetch("https://api.stackframe.com/health");
75+
const res = await fetch("https://api.stack-auth.com/health");
7676
if (!res.ok) {
7777
throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`);
7878
}
@@ -86,6 +86,35 @@ export class StackClientInterface {
8686
};
8787
}
8888

89+
protected async _networkRetry<T>(cb: () => Promise<Result<T, any>>, session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<T> {
90+
const retriedResult = await Result.retry(
91+
cb,
92+
5,
93+
{ exponentialDelayBase: 1000 },
94+
);
95+
96+
// try to diagnose the error for the user
97+
if (retriedResult.status === "error") {
98+
if (!navigator.onLine) {
99+
throw new Error("Failed to send Stack network request. It seems like you are offline. (window.navigator.onLine is falsy)", { cause: retriedResult.error });
100+
}
101+
throw new Error(deindent`
102+
Stack is unable to connect to the server. Please check your internet connection and try again.
103+
104+
If the problem persists, please contact Stack support and provide a screenshot of your entire browser console.
105+
106+
${retriedResult.error}
107+
108+
${JSON.stringify(await this.runNetworkDiagnostics(session, requestType), null, 2)}
109+
`, { cause: retriedResult.error });
110+
}
111+
return retriedResult.data;
112+
}
113+
114+
protected async _networkRetryException<T>(cb: () => Promise<T>, session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<T> {
115+
return await this._networkRetry(async () => await Result.fromThrowingAsync(cb), session, requestType);
116+
}
117+
89118
public async fetchNewAccessToken(refreshToken: RefreshToken) {
90119
if (!('publishableClientKey' in this.options)) {
91120
// TODO support it
@@ -103,10 +132,12 @@ export class StackClientInterface {
103132
token_endpoint_auth_method: 'client_secret_post',
104133
};
105134

106-
const rawResponse = await oauth.refreshTokenGrantRequest(
107-
as,
108-
client,
109-
refreshToken.token,
135+
const rawResponse = await this._networkRetryException(
136+
async () => await oauth.refreshTokenGrantRequest(
137+
as,
138+
client,
139+
refreshToken.token,
140+
)
110141
);
111142
const response = await this._processResponse(rawResponse);
112143

@@ -147,29 +178,11 @@ export class StackClientInterface {
147178
});
148179

149180

150-
const retriedResult = await Result.retry(
181+
return await this._networkRetry(
151182
() => this.sendClientRequestInner(path, requestOptions, session!, requestType),
152-
5,
153-
{ exponentialDelayBase: 1000 },
183+
session,
184+
requestType,
154185
);
155-
156-
// try to diagnose the error for the user
157-
if (retriedResult.status === "error") {
158-
if (!navigator.onLine) {
159-
throw new Error("Failed to send Stack request. It seems like you are offline. (window.navigator.onLine is falsy)", { cause: retriedResult.error });
160-
}
161-
throw new Error(deindent`
162-
Stack is unable to connect to the server. Please check your internet connection and try again.
163-
164-
If the problem persists, please contact Stack support and provide a screenshot of your entire browser console.
165-
166-
${retriedResult.error}
167-
168-
${JSON.stringify(await this.runNetworkDiagnostics(session, requestType), null, 2)}
169-
`, { cause: retriedResult.error });
170-
}
171-
172-
return retriedResult.data;
173186
}
174187

175188
public createSession(options: Omit<ConstructorParameters<typeof InternalSession>[0], "refreshAccessTokenCallback">): InternalSession {
@@ -286,7 +299,7 @@ export class StackClientInterface {
286299
} catch (e) {
287300
if (e instanceof TypeError) {
288301
// Network error, retry
289-
console.log("Stack detected a network error, retrying.", e);
302+
console.warn(`Stack detected a network error while fetching ${url}, retrying.`, e, { url });
290303
return Result.error(e);
291304
}
292305
throw e;
@@ -689,7 +702,9 @@ export class StackClientInterface {
689702
client_secret: this.options.publishableClientKey,
690703
token_endpoint_auth_method: 'client_secret_post',
691704
};
692-
const params = oauth.validateAuthResponse(as, client, options.oauthParams, options.state);
705+
const params = await this._networkRetryException(
706+
async () => oauth.validateAuthResponse(as, client, options.oauthParams, options.state),
707+
);
693708
if (oauth.isOAuth2Error(params)) {
694709
throw new StackAssertionError("Error validating outer OAuth response", { params }); // Handle OAuth 2.0 redirect error
695710
}

packages/stack-shared/src/utils/arrays.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,7 @@ export function shuffle<T>(arr: readonly T[]): T[] {
8181
export function outerProduct<T, U>(arr1: readonly T[], arr2: readonly U[]): [T, U][] {
8282
return arr1.flatMap((item1) => arr2.map((item2) => [item1, item2] as [T, U]));
8383
}
84+
85+
export function unique<T>(arr: readonly T[]): T[] {
86+
return [...new Set(arr)];
87+
}

0 commit comments

Comments
 (0)