mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
OAuth callback no longer redirects to itself if things fail
This commit is contained in:
parent
29a24dcf3b
commit
51665a4162
@ -16,10 +16,12 @@ import { useEffect } from 'react';
|
||||
export function AuthPage({
|
||||
fullPage=false,
|
||||
type,
|
||||
automaticRedirect,
|
||||
mockProject,
|
||||
}: {
|
||||
fullPage?: boolean,
|
||||
type: 'sign-in' | 'sign-up',
|
||||
automaticRedirect?: boolean,
|
||||
mockProject?: {
|
||||
config: {
|
||||
credentialEnabled: boolean,
|
||||
@ -36,10 +38,12 @@ export function AuthPage({
|
||||
const project = mockProject || projectFromHook;
|
||||
|
||||
useEffect(() => {
|
||||
if (user && !mockProject) {
|
||||
runAsynchronously(type === 'sign-in' ? stackApp.redirectToAfterSignIn() : stackApp.redirectToAfterSignUp());
|
||||
if (automaticRedirect) {
|
||||
if (user && !mockProject) {
|
||||
runAsynchronously(type === 'sign-in' ? stackApp.redirectToAfterSignIn() : stackApp.redirectToAfterSignUp());
|
||||
}
|
||||
}
|
||||
}, [user, mockProject, stackApp]);
|
||||
}, [user, mockProject, stackApp, automaticRedirect]);
|
||||
|
||||
if (user && !mockProject) {
|
||||
return <PredefinedMessageCard type='signedIn' fullPage={fullPage} />;
|
||||
|
||||
@ -5,6 +5,7 @@ import { useStackApp } from "..";
|
||||
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { MessageCard } from "../components/message-cards/message-card";
|
||||
import { StyledLink } from "@stackframe/stack-ui";
|
||||
import { captureError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
|
||||
export function OAuthCallback(props: { fullPage?: boolean }) {
|
||||
const app = useStackApp();
|
||||
@ -19,10 +20,11 @@ export function OAuthCallback(props: { fullPage?: boolean }) {
|
||||
try {
|
||||
hasRedirected = await app.callOAuthCallback();
|
||||
} catch (e: any) {
|
||||
captureError("<OAuthCallback />", e);
|
||||
setError(e);
|
||||
}
|
||||
if (!hasRedirected && (!error || process.env.NODE_ENV === 'production')) {
|
||||
await app.redirectToSignIn();
|
||||
await app.redirectToSignIn({ noRedirectBack: true });
|
||||
}
|
||||
}), []);
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { SignUp } from "./sign-up";
|
||||
import { SignIn } from "./sign-in";
|
||||
import { RedirectType, notFound, redirect } from 'next/navigation';
|
||||
import { EmailVerification } from "./email-verification";
|
||||
import { StackServerApp } from "..";
|
||||
import { AuthPage, StackServerApp } from "..";
|
||||
import { MessageCard } from "../components/message-cards/message-card";
|
||||
import { HandlerUrls } from "../lib/stack-app";
|
||||
import { SignOut } from "./sign-out";
|
||||
@ -67,11 +67,11 @@ export default async function StackHandler<HasTokenStore extends boolean>({
|
||||
switch (path) {
|
||||
case availablePaths.signIn: {
|
||||
redirectIfNotHandler('signIn');
|
||||
return <SignIn fullPage={fullPage} />;
|
||||
return <AuthPage fullPage={fullPage} type='sign-in' automaticRedirect />;
|
||||
}
|
||||
case availablePaths.signUp: {
|
||||
redirectIfNotHandler('signUp');
|
||||
return <SignUp fullPage={fullPage} />;
|
||||
return <AuthPage fullPage={fullPage} type='sign-up' automaticRedirect />;
|
||||
}
|
||||
case availablePaths.emailVerification: {
|
||||
redirectIfNotHandler('emailVerification');
|
||||
|
||||
@ -45,14 +45,14 @@ export function PredefinedMessageCard({
|
||||
case 'passwordReset': {
|
||||
title = "Password reset successfully!";
|
||||
message = 'Your password has been reset. You can now sign in with your new password.';
|
||||
primaryAction = () => stackApp.redirectToSignIn();
|
||||
primaryAction = () => stackApp.redirectToSignIn({ noRedirectBack: true });
|
||||
primaryButton = "Sign in";
|
||||
break;
|
||||
}
|
||||
case 'emailVerified': {
|
||||
title = "Email verified!";
|
||||
message = 'Your have successfully verified your email.';
|
||||
primaryAction = () => stackApp.redirectToSignIn();
|
||||
primaryAction = () => stackApp.redirectToSignIn({ noRedirectBack: true });
|
||||
primaryButton = "Sign in";
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { StackClientInterface } from "@stackframe/stack-shared";
|
||||
import { InternalSession } from "@stackframe/stack-shared/dist/sessions";
|
||||
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { StackAssertionError, captureError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { neverResolve } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { constructRedirectUrl } from "../utils/url";
|
||||
import { getVerifierAndState, saveVerifierAndState } from "./cookie";
|
||||
@ -64,6 +64,7 @@ function consumeOAuthCallbackQueryParams(expectedState: string): null | URL {
|
||||
const originalUrl = new URL(window.location.href);
|
||||
for (const param of requiredParams) {
|
||||
if (!originalUrl.searchParams.has(param)) {
|
||||
captureError("consumeOAuthCallbackQueryParams", new Error(`Missing required query parameter on OAuth callback: ${param}`));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -71,6 +72,7 @@ function consumeOAuthCallbackQueryParams(expectedState: string): null | URL {
|
||||
if (expectedState !== originalUrl.searchParams.get("state")) {
|
||||
// If the state doesn't match, then the callback wasn't meant for us.
|
||||
// Maybe the website uses another OAuth library?
|
||||
captureError("consumeOAuthCallbackQueryParams", new Error(`Invalid OAuth callback state: Was this meant for someone else, or did cookies fail?`));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -881,29 +881,31 @@ class _StackClientAppImpl<HasTokenStore extends boolean, ProjectId extends strin
|
||||
throw new Error(`No URL for handler name ${handlerName}`);
|
||||
}
|
||||
|
||||
if (handlerName === "afterSignIn" || handlerName === "afterSignUp") {
|
||||
if (isReactServer || typeof window === "undefined") {
|
||||
try {
|
||||
await this._checkFeatureSupport("rsc-handler-" + handlerName, {});
|
||||
} catch (e) {}
|
||||
} else {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
url = queryParams.get("after_auth_return_to") || url;
|
||||
}
|
||||
} else if (handlerName === "signIn" || handlerName === "signUp") {
|
||||
if (isReactServer || typeof window === "undefined") {
|
||||
try {
|
||||
await this._checkFeatureSupport("rsc-handler-" + handlerName, {});
|
||||
} catch (e) {}
|
||||
} else {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const nextUrl = new URL(url, currentUrl);
|
||||
if (currentUrl.searchParams.has("after_auth_return_to")) {
|
||||
nextUrl.searchParams.set("after_auth_return_to", currentUrl.searchParams.get("after_auth_return_to")!);
|
||||
} else if (currentUrl.protocol === nextUrl.protocol && currentUrl.host === nextUrl.host) {
|
||||
nextUrl.searchParams.set("after_auth_return_to", getRelativePart(currentUrl));
|
||||
if (!options?.noRedirectBack) {
|
||||
if (handlerName === "afterSignIn" || handlerName === "afterSignUp") {
|
||||
if (isReactServer || typeof window === "undefined") {
|
||||
try {
|
||||
await this._checkFeatureSupport("rsc-handler-" + handlerName, {});
|
||||
} catch (e) {}
|
||||
} else {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
url = queryParams.get("after_auth_return_to") || url;
|
||||
}
|
||||
} else if (handlerName === "signIn" || handlerName === "signUp") {
|
||||
if (isReactServer || typeof window === "undefined") {
|
||||
try {
|
||||
await this._checkFeatureSupport("rsc-handler-" + handlerName, {});
|
||||
} catch (e) {}
|
||||
} else {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const nextUrl = new URL(url, currentUrl);
|
||||
if (currentUrl.searchParams.has("after_auth_return_to")) {
|
||||
nextUrl.searchParams.set("after_auth_return_to", currentUrl.searchParams.get("after_auth_return_to")!);
|
||||
} else if (currentUrl.protocol === nextUrl.protocol && currentUrl.host === nextUrl.host) {
|
||||
nextUrl.searchParams.set("after_auth_return_to", getRelativePart(currentUrl));
|
||||
}
|
||||
url = getRelativePart(nextUrl);
|
||||
}
|
||||
url = getRelativePart(nextUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2555,6 +2557,7 @@ type _______________VARIOUS_______________ = never; // this is a marker for VSC
|
||||
|
||||
type RedirectToOptions = {
|
||||
replace?: boolean,
|
||||
noRedirectBack?: boolean,
|
||||
};
|
||||
|
||||
type AsyncStoreProperty<Name extends string, Args extends any[], Value, IsMultiple extends boolean> =
|
||||
|
||||
Loading…
Reference in New Issue
Block a user