diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 000000000..d676cd8a3 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,29 @@ +{ + "permissions": { + "allow": [ + "Bash(xargs cat:*)", + "Bash(find:*)", + "Bash(go env:*)", + "Bash(git checkout:*)", + "Bash(git add:*)", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(reearth/dev\\): use reearth-classic-account as REEARTH_DB_ACCOUNT in dev\n\nDev environment should use a separate account database \\(reearth-classic-account\\)\nto avoid MongoDB schema validation conflicts. Prod remains reearth-account.\n\n- Add reearth_db_account variable to reearth_classic_api module \\(default: reearth-account\\)\n- Set reearth_db_account to reearth-classic-account in dev reearth_classic_api\n- Set databases.account to reearth-classic-account in dev reearth_cloud\n\nCo-Authored-By: Claude Opus 4.6 \nEOF\n\\)\")", + "Bash(git push:*)", + "Bash(gh pr create:*)", + "Bash(grep:*)", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(reearth/dev\\): grant reearth role access to reearth-classic-account database\n\nThe classic API uses reearth-classic-account as DB_Account, but the\nreearth custom DB role only had permissions for reearth and\nreearth-account databases, causing MongoDB Unauthorized errors on\nuser lookups during authentication.\n\nCo-Authored-By: Claude Opus 4.6 \nEOF\n\\)\")", + "Bash(gh pr close:*)", + "Bash(git fetch:*)", + "Bash(git cherry-pick:*)", + "Bash(gh pr edit:*)", + "Bash(gh pr view:*)", + "Bash(gh run list:*)", + "Bash(git pull:*)", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(reearth/dev\\): add reearth-classic-db role and revert reearth role\n\n- Revert the reearth custom role to remove reearth-classic-account\n \\(the reearth-db secret uses reearth-visualizer user, not reearth\\)\n- Add new reearth-classic-db custom role with access to reearth and\n reearth-classic-account databases for the classic API\n\nCo-Authored-By: Claude Opus 4.6 \nEOF\n\\)\")", + "Bash(git commit:*)", + "Bash(gh run watch:*)", + "Bash(gcloud run services describe:*)", + "Bash(python3:*)", + "Bash(gcloud run revisions list:*)" + ] + } +} diff --git a/web/src/classic/components/pages/Authentication/ForcePasswordChange/index.tsx b/web/src/classic/components/pages/Authentication/ForcePasswordChange/index.tsx new file mode 100644 index 000000000..23ab6069f --- /dev/null +++ b/web/src/classic/components/pages/Authentication/ForcePasswordChange/index.tsx @@ -0,0 +1,42 @@ +import { useEffect } from "react"; + +import Loading from "@reearth/classic/components/atoms/Loading"; +import { getAuthInfo } from "@reearth/services/config"; + +const STORAGE_KEY = "reearth_password_change_state"; + +const ForcePasswordChangePage: React.FC = () => { + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const ticket = params.get("ticket"); + const state = params.get("state"); + + if (ticket && state) { + // Coming from Auth0 Post-Login Action redirect. + // Save state to sessionStorage so we can resume the login flow after password change. + sessionStorage.setItem(STORAGE_KEY, state); + // Redirect to Auth0's password change page. + window.location.href = ticket; + return; + } + + // Coming back from Auth0 password change page (result_url redirect). + const savedState = sessionStorage.getItem(STORAGE_KEY); + if (savedState) { + sessionStorage.removeItem(STORAGE_KEY); + const authInfo = getAuthInfo(); + if (authInfo?.auth0Domain) { + // Redirect back to Auth0 to complete the login flow. + window.location.href = `https://${authInfo.auth0Domain}/continue?state=${savedState}`; + return; + } + } + + // Fallback: redirect to top page. + window.location.href = "/"; + }, []); + + return ; +}; + +export default ForcePasswordChangePage; diff --git a/web/src/services/routing/index.tsx b/web/src/services/routing/index.tsx index 0e6420f84..10414e1cb 100644 --- a/web/src/services/routing/index.tsx +++ b/web/src/services/routing/index.tsx @@ -22,6 +22,9 @@ const LoginPage = lazy(() => import("@reearth/classic/components/pages/Authentic const PasswordResetPage = lazy( () => import("@reearth/classic/components/pages/Authentication/PasswordReset"), ); +const ForcePasswordChangePage = lazy( + () => import("@reearth/classic/components/pages/Authentication/ForcePasswordChange"), +); const SignupPage = lazy( () => import("@reearth/classic/components/pages/Authentication/SignupPage"), @@ -57,6 +60,10 @@ export const AppRoutes = () => { index: true, element: , }, + { + path: "auth/force-password-change", + element: , + }, { path: "auth/*", element: ,