Skip to content

Conversation

@iamlokanath
Copy link

@iamlokanath iamlokanath commented Jan 28, 2026

show toast on issuers page when user continues as guest

  • Pass fromGuest in navigation state when user clicks "Continue as Guest"
  • On /issuers, show warning toast (top-right) prompting login to share with verifiers
  • Add guestToastMessage to IssuersPage locales (en, ta, hi, fr, ar, pt, kn)
  • Wrap IssuersPage tests in MemoryRouter; add guest-toast test; update Login test expectations

Summary by CodeRabbit

  • Bug Fixes

    • Fixed login redirect flow to properly remember your original destination and return you there after successful authentication.
  • Improvements

    • Added error messages for guest login attempts to provide clearer guidance when login is restricted from certain pages.
    • Enhanced session handling for improved user experience during login and navigation workflows.

✏️ Tip: You can customize this high-level summary in your review settings.

Lkanath Panda and others added 2 commits January 9, 2026 15:53
@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

Walkthrough

The changes implement origin path preservation for protected route redirects and add guest login validation. When users access protected routes while unauthenticated, their original path is saved in navigation state. Guest login from "/user/authorize" now triggers an error, and post-login redirects use the saved origin path.

Changes

Cohort / File(s) Summary
Authentication Flow
inji-web/src/components/Common/LoginSessionStatusChecker.ts
Redirect to ROOT now includes location state object with from.pathname to preserve the original path for later redirect after login.
Guest Login Guard & State
inji-web/src/components/Login/Login.tsx
Adds guestError state to track error messages; blocks guest login from "/user/authorize" path with user-facing error; derives redirect target from origin path with "/issuers" fallback; renders error message block when error is set; adjusts login-note padding.

Sequence Diagram

sequenceDiagram
    participant User
    participant Router
    participant LoginSessionStatusChecker
    participant Login as Login Component
    participant OAuth as OAuth Provider

    User->>Router: Access protected route without auth
    Router->>LoginSessionStatusChecker: Check session
    LoginSessionStatusChecker->>Router: Redirect to ROOT with {from: originalPath}
    Router->>Login: Navigate with location state
    Login->>Login: Extract origin path from state
    alt Origin path is "/user/authorize"
        Login->>Login: Set guestError message
        Login->>User: Display error, block guest login
    else Valid origin path
        Login->>OAuth: Guest login initiated
        OAuth-->>Login: OAuth callback
        Login->>Router: Redirect to origin path (or "/issuers")
        Router->>User: Navigate to original destination
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested reviewers

  • jackjain

Poem

🐰 A rabbit hops through auth's refrain,
Paths remembered, not in vain,
Guest logins blocked from authorize's door,
Redirects to where you were before! 🔄

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a toast notification on the issuers page when a user continues as a guest, which aligns with the PR objectives and code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@inji-web/src/components/Login/Login.tsx`:
- Around line 24-26: Replace the hardcoded guest-blocked message in the Login
component where you check if fromPath === "/user/authorize" and call
setGuestError(...) with a localized string: add a "guestLoginBlocked" key under
the Login section in every locale JSON (en.json, ar.json, fr.json, hi.json,
kn.json, pt.json, ta.json) and then call the i18n translation helper (e.g.,
t("Login.guestLoginBlocked")) when passing the message to setGuestError; ensure
the translation key name matches exactly in all locale files and update any
imports/usages in Login.tsx to use the existing translation function.
🧹 Nitpick comments (2)
inji-web/src/components/Common/LoginSessionStatusChecker.ts (1)

31-37: Consider preserving query parameters and hash in the navigation state.

The current implementation only preserves pathname, but if users access a protected route with query parameters (e.g., /user/issuers?filter=recent) or a hash, those will be lost after login redirect.

Consider whether preserving the full location is needed:

♻️ Optional: Preserve full location details
             navigate(ROUTES.ROOT, {
                 state: {
                     from: {
-                        pathname: location.pathname
+                        pathname: location.pathname,
+                        search: location.search,
+                        hash: location.hash
                     }
                 }
             });
inji-web/src/components/Login/Login.tsx (1)

23-23: Type the navigation state for safer from access.

location.state is typically unknown in react-router-dom. Defining a location state type avoids unsafe property access and improves TS safety.

♻️ Suggested change
+type LocationState = {
+  from?: { pathname?: string };
+};
+
 export const Login: React.FC = () => {
   const { t } = useTranslation("HomePage");
   const navigate = useNavigate();
   const [guestClicked, setGuestClicked] = useState(false);
   const [guestError, setGuestError] = useState<string | null>(null);
-  const location = useLocation();
+  const location = useLocation<LocationState>();

Comment on lines +24 to +26
if (fromPath === "/user/authorize") {
setGuestError("To continue this verification request, please sign in to your wallet instead of continuing as guest.");
return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

❓ Verification inconclusive

Script executed:

# First, find and explore the Login.tsx file
fd "Login.tsx" --type f

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 0

stdout:

inji-web/src/components/Login/Login.tsx

Script executed:

# Check the structure of the src/components directory
git ls-files "inji-web/src/components" | head -20

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 0

stdout:

inji-web/src/components/Common/BackgroundDecorator.tsx
inji-web/src/components/Common/Buttons/BackArrowButton.tsx
inji-web/src/components/Common/Buttons/BorderedButton.tsx
inji-web/src/components/Common/Buttons/BorderedButtonStyles.ts
inji-web/src/components/Common/Buttons/ButtonStyles.ts
inji-web/src/components/Common/Buttons/CloseIconButton.tsx
inji-web/src/components/Common/Buttons/CollapseButton.tsx
inji-web/src/components/Common/Buttons/CrossIconButton.tsx
inji-web/src/components/Common/Buttons/GoogleSignInButton.tsx
inji-web/src/components/Common/Buttons/GoogleSignInButtonStyles.ts
inji-web/src/components/Common/Buttons/NavBackArrowButton.tsx
inji-web/src/components/Common/Buttons/PlainButton.tsx
inji-web/src/components/Common/Buttons/RedirectionButton.tsx
inji-web/src/components/Common/Buttons/ResponsiveIconButtonWithText.tsx
inji-web/src/components/Common/Buttons/SolidButton.tsx
inji-web/src/components/Common/Buttons/SolidButtonStyles.ts
inji-web/src/components/Common/Buttons/TertiaryButton.tsx
inji-web/src/components/Common/CircleSkeleton.tsx
inji-web/src/components/Common/Clickable.tsx
inji-web/src/components/Common/DropdownArrowIcon.tsx

Script executed:

# Look for i18n configuration files
fd -e "json" -e "ts" -e "tsx" -e "js" | grep -i "i18n\|locale\|translation" | head -20

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 0

stdout:

inji-web/src/__tests__/utils/i18n.test.tsx
inji-web/src/locales/ar.json
inji-web/src/locales/en.json
inji-web/src/locales/fr.json
inji-web/src/locales/hi.json
inji-web/src/locales/kn.json
inji-web/src/locales/pt.json
inji-web/src/locales/ta.json
inji-web/src/utils/i18n.ts

Script executed:

# Read the Login.tsx file to verify the hardcoded message
cat -n "inji-web/src/components/Login/Login.tsx" | head -70

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 0

stdout:

     1	import React, { useState } from "react";
     2	import {useNavigate, useLocation} from "react-router-dom";
     3	import '../../index.css';
     4	import {useTranslation} from "react-i18next";
     5	import {BorderedButton} from "../Common/Buttons/BorderedButton";
     6	import {GoogleSignInButton} from "../Common/Buttons/GoogleSignInButton";
     7	
     8	export const Login: React.FC = () => {
     9	  const { t } = useTranslation("HomePage");
    10	  const navigate = useNavigate();
    11	  const [guestClicked, setGuestClicked] = useState(false);
    12	  const [guestError, setGuestError] = useState<string | null>(null);
    13	  const location = useLocation();
    14	
    15	  const handleGoogleLogin = () => {
    16	      // Use replace() instead of href to prevent adding Google IdP to browser history
    17	      // This way, pressing back won't navigate to Google's OAuth page
    18	      window.location.replace(`${window._env_.MIMOTO_URL}/oauth2/authorize/google`);
    19	  };
    20	
    21	  const handleGuestLogin = () => {
    22	    if (guestClicked) return; // guard against double click
    23	    const fromPath = location.state?.from?.pathname;
    24	    if (fromPath === "/user/authorize") {
    25	      setGuestError("To continue this verification request, please sign in to your wallet instead of continuing as guest.");
    26	      return;
    27	    }
    28	    setGuestClicked(true);
    29	    const redirectPath = fromPath || "/issuers";
    30	    navigate(redirectPath, { replace: true });
    31	  };
    32	
    33	  const Separator:React.FC=()=>{
    34	    return (
    35	      <div className="flex items-center w-full my-2 sm:my-5">
    36	        <hr className="flex-grow border-t border-gray-300" />
    37	        <span className="px-4 text-gray-500 font-medium text-[14px] leading-[20px]">OR</span>
    38	        <hr className="flex-grow border-t border-gray-300" />
    39	      </div>
    40	    );
    41	  }
    42	
    43	  return (
    44	    <div className="flex flex-col items-center justify-center w-[100%] max-w-[400px] mx-auto rounded-2xl">
    45	        <div data-testid="login-logo" className="flex justify-center items-center">
    46	          <img src={require("../../assets/Logomark.png")} alt="Inji Web Logo" />
    47	        </div>
    48	
    49	        <div data-testid="login-title" className="text-[36px] leading-[44px] tracking-[-0.02em] text-black font-bold text-center py-4">
    50	          {t("Login.loginTitle")}
    51	        </div>
    52	
    53	        <div data-testid="login-description" className="sm:mt-3 text-[18px] leading-[28px] text-muted text-ellipsis text-center pb-0">
    54	          {t("Login.loginDescription")}
    55	        </div>
    56	
    57	        <div data-testid="login-note" className="sm:my-3 text-[14px] leading-[20px] font-medium text-ellipsis text-center pb-1">
    58	          {t("Login.loginNote")}
    59	        </div >
    60	
    61	        {guestError && (
    62	          <div
    63	            data-testid="login-guest-error"
    64	            className="mb-3 text-[12px] leading-[18px] text-red-600 text-center"
    65	          >
    66	            {guestError}
    67	          </div>
    68	        )}
    69	
    70	        <GoogleSignInButton handleGoogleLogin={handleGoogleLogin} loadingText={t("Login.loggingIn")} text={t("Login.loginGoogle")}/>

Script executed:

# Check the i18n configuration
cat -n "inji-web/src/utils/i18n.ts"

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 0

stdout:

     1	import i18n from "i18next";
     2	import {initReactI18next} from "react-i18next";
     3	import en from '../locales/en.json';
     4	import fr from '../locales/fr.json';
     5	import ta from '../locales/ta.json';
     6	import hi from '../locales/hi.json';
     7	import kn from '../locales/kn.json';
     8	import ar from '../locales/ar.json';
     9	import pt from '../locales/pt.json';
    10	import {AppStorage} from "./AppStorage";
    11	import {
    12	    CredentialTypeDisplayArrayObject,
    13	    IssuerWellknownDisplayArrayObject,
    14	    LanguageObject
    15	} from "../types/data";
    16	
    17	const resources = {en, ta, kn, hi, fr, ar, pt};
    18	
    19	export const LanguagesSupported: LanguageObject[] = [
    20	    {label: "English", value: 'en'},
    21	    {label: "தமிழ்", value: 'ta'},
    22	    {label: "ಕನ್ನಡ", value: 'kn'},
    23	    {label: "हिंदी", value: 'hi'},
    24	    {label: "Français", value: 'fr'},
    25	    {label: "عربي", value: 'ar'},
    26	    {label: "Português", value:'pt'}
    27	]
    28	
    29	export const defaultLanguage = window._env_.DEFAULT_LANG;
    30	
    31	const selected_language = AppStorage.getItem(AppStorage.SELECTED_LANGUAGE);
    32	i18n
    33	    .use(initReactI18next) // passes i18n down to react-i18next
    34	    .init({
    35	        resources,
    36	        lng: selected_language ?? defaultLanguage,
    37	        fallbackLng: defaultLanguage,
    38	        interpolation: {
    39	            escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
    40	        },
    41	    });
    42	
    43	export const switchLanguage = async (language: string) => {
    44	    AppStorage.setItem(AppStorage.SELECTED_LANGUAGE, language);
    45	    await i18n.changeLanguage(language);
    46	}
    47	export const getIssuerDisplayObjectForCurrentLanguage = (displayArray: IssuerWellknownDisplayArrayObject[], language: string = i18n.language) => {
    48	    let resp = displayArray.filter(displayObj => (displayObj.language === language))[0];
    49	    if (!resp) {
    50	        resp = displayArray.filter(displayObj => (displayObj.language === defaultLanguage))[0];
    51	    }
    52	    return resp;
    53	};
    54	
    55	export const getCredentialTypeDisplayObjectForCurrentLanguage = (
    56	    displayArray: CredentialTypeDisplayArrayObject[],language: string = i18n.language) => {
    57	    let resp = displayArray.filter((displayObj) => displayObj.locale === language)[0];
    58	    if (!resp) {
    59	        resp = displayArray.filter(displayObj => displayObj.locale === defaultLanguage)[0];
    60	    }
    61	    return resp;
    62	};
    63	
    64	export const getDirCurrentLanguage = (language: string) => {
    65	    return isRTL(language) ? 'rtl' : 'ltr';
    66	}
    67	
    68	export const isRTL = (language:string) => {
    69	    return language === 'ar';
    70	}
    71	
    72	export default i18n;

Script executed:

# Look at the structure of the English locale file
head -50 "inji-web/src/locales/en.json"

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 0

stdout:

{
  "PageTemplate": {
    "Header": {
      "home": "Home",
      "faq": "FAQ",
      "login": "Login",
      "logout": "Logout"
    },
    "Footer": {
      "product": "A MOSIP Product",
      "copyRight": "© 2024 MOSIP. All rights reserved."
    }
  },
  "HomePage": {
    "Banner": {
      "heading": "Effortlessly Access and Manage Your Digital Credentials",
      "description": "Make your data portable with trust. Securely manage your digital documents with our easy-to-use web-based wallet.",
      "buttontext": "Get Started"
    },
    "Features": {
      "heading": "Features of Inji Wallet",
      "description1": "Seamless and secure management of verifiable credentials",
      "description2": "for trusted digital interactions."
    },
    "FeatureItem1": {
      "heading": "Access Your Credentials, Anywhere You Need",
      "item1": "Credentials simplified",
      "description1": "Keep all your essential documents",
      "item2": "No more paperwork, all in one place",
      "description2": "Effortlessly download, store, and retrieve your documents with ease. Win, win, win."
    },
    "FeatureItem2": {
      "heading": "Your Documents Downloaded in Seconds",
      "item1": "Download with confidence",
      "description1": "Save your important documents instantly",
      "item2": "Safe and sound",
      "description2": "Download IDs, licenses, and more, in one secure location."
    },
    "FeatureItem3": {
      "heading": "Easy Sharing",
      "item1": "Share with a QR code",
      "description1": "Seamlessly share your IDs and certificates on the go. Just present the QR code of your document, to be scanned and shared.",
      "item2": "Read, set, share",
      "description2": "QR code verified in real time."
    },
    "FeatureItem4": {
      "heading": "Secure and Private",
      "item1": "Your credentials, protected",
      "description1": "Your data stays private, with the built-in security mechanism.",
      "item2": "Rest easy",

Script executed:

# Search for existing Login-related keys in locale files
rg "Login\." "inji-web/src/locales/" -A 2 -B 2

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 1


Script executed:

# Search for Login keys in the English locale file
rg "Login" "inji-web/src/locales/en.json" -A 5 -B 2

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 0

stdout:

      "home": "Home",
      "faq": "FAQ",
      "login": "Login",
      "logout": "Logout"
    },
    "Footer": {
      "product": "A MOSIP Product",
      "copyRight": "© 2024 MOSIP. All rights reserved."
--
      "buttontext":"Learn More"
    },
    "Login":{
      "loginTitle":"Log In",
      "loginDescription":"Log in with your account or continue as a guest to access your credentials.",
      "loginNote":"Some features may be limited in guest mode.",
      "loggingIn":"Logging In",
      "loginGoogle":"Continue With Google",
      "loginGuest":"Continue as Guest"
    },
    "LoginFailedModal":{
      "failureMessage":"Login Failed",
      "failureDescription":"Your login attempt has failed. Please check your credentials and try again",
      "retry":"Retry"
    }
  },
  "Layout": {

Script executed:

# Get the full structure of the HomePage section in en.json to see all Login keys
python3 << 'EOF'
import json

with open('inji-web/src/locales/en.json', 'r') as f:
    data = json.load(f)
    
if 'HomePage' in data and 'Login' in data['HomePage']:
    print(json.dumps(data['HomePage']['Login'], indent=2))
else:
    print("Login section not found in HomePage")
    # Check what's in HomePage
    if 'HomePage' in data:
        print("HomePage keys:", list(data['HomePage'].keys())[:20])
EOF

Repository: inji/inji-web


Repository: inji/inji-web
Script execution failed with error: Request timed out after 120000ms (requestId=56753f5d-1fb3-402d-a95e-2c2def76d533)


Script executed:

# Check if Login exists at the top level
python3 << 'EOF'
import json

with open('inji-web/src/locales/en.json', 'r') as f:
    data = json.load(f)
    
print("Top-level keys:", list(data.keys()))

# Check for Login at any level
def find_login_keys(obj, path=""):
    if isinstance(obj, dict):
        for key, value in obj.items():
            if key == "Login" or "login" in key.lower():
                print(f"Found at {path}.{key}:")
                if isinstance(value, dict):
                    print(json.dumps(value, indent=2))
            if isinstance(value, dict):
                find_login_keys(value, f"{path}.{key}")

find_login_keys(data)
EOF

Repository: inji/inji-web


Repository: inji/inji-web
Script execution failed with error: Request timed out after 120000ms (requestId=6b4c1b7f-c7f5-4332-b721-17351b4a74fd)


Script executed:

# Check if Login section exists in other locale files
python3 << 'EOF'
import json
import glob

locale_files = glob.glob('inji-web/src/locales/*.json')

for locale_file in sorted(locale_files):
    try:
        with open(locale_file, 'r', encoding='utf-8') as f:
            data = json.load(f)
            if 'HomePage' in data and 'Login' in data['HomePage']:
                lang = locale_file.split('/')[-1].replace('.json', '')
                print(f"\n{lang}.json - Login keys:")
                login_keys = list(data['HomePage']['Login'].keys())
                print(f"  {login_keys}")
    except Exception as e:
        print(f"Error reading {locale_file}: {e}")
EOF

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 0

stdout:


ar.json - Login keys:
  ['loginTitle', 'loginDescription', 'loginNote', 'loggingIn', 'loginGoogle', 'loginGuest']

en.json - Login keys:
  ['loginTitle', 'loginDescription', 'loginNote', 'loggingIn', 'loginGoogle', 'loginGuest']

fr.json - Login keys:
  ['loginTitle', 'loginDescription', 'loginNote', 'loggingIn', 'loginGoogle', 'loginGuest']

hi.json - Login keys:
  ['loginTitle', 'loginDescription', 'loginNote', 'loggingIn', 'loginGoogle', 'loginGuest']

kn.json - Login keys:
  ['loginTitle', 'loginDescription', 'loginNote', 'loggingIn', 'loginGoogle', 'loginGuest']

pt.json - Login keys:
  ['loginTitle', 'loginDescription', 'loginNote', 'loggingIn', 'loginGoogle', 'loginGuest']

ta.json - Login keys:
  ['loginTitle', 'loginDescription', 'loginNote', 'loggingIn', 'loginGoogle', 'loginGuest']

Script executed:

# Verify the exact line and context around the hardcoded message
sed -n '21,31p' "inji-web/src/components/Login/Login.tsx"

Repository: inji/inji-web


Repository: inji/inji-web
Exit code: 0

stdout:

  const handleGuestLogin = () => {
    if (guestClicked) return; // guard against double click
    const fromPath = location.state?.from?.pathname;
    if (fromPath === "/user/authorize") {
      setGuestError("To continue this verification request, please sign in to your wallet instead of continuing as guest.");
      return;
    }
    setGuestClicked(true);
    const redirectPath = fromPath || "/issuers";
    navigate(redirectPath, { replace: true });
  };

Add localization for the guest-blocked error message.

This user-facing error message is hardcoded. Route it through i18n by adding "guestLoginBlocked" to the Login section in all locale files (inji-web/src/locales/*.json), then use the translation key in the component.

🔧 Suggested change
-      setGuestError("To continue this verification request, please sign in to your wallet instead of continuing as guest.");
+      setGuestError(t("Login.guestLoginBlocked"));

Also add to all locale files (en.json, ar.json, fr.json, hi.json, kn.json, pt.json, ta.json):

  "Login": {
    "loginTitle": "...",
+   "guestLoginBlocked": "To continue this verification request, please sign in to your wallet instead of continuing as guest.",
    ...
  }
🤖 Prompt for AI Agents
In `@inji-web/src/components/Login/Login.tsx` around lines 24 - 26, Replace the
hardcoded guest-blocked message in the Login component where you check if
fromPath === "/user/authorize" and call setGuestError(...) with a localized
string: add a "guestLoginBlocked" key under the Login section in every locale
JSON (en.json, ar.json, fr.json, hi.json, kn.json, pt.json, ta.json) and then
call the i18n translation helper (e.g., t("Login.guestLoginBlocked")) when
passing the message to setGuestError; ensure the translation key name matches
exactly in all locale files and update any imports/usages in Login.tsx to use
the existing translation function.

@iamlokanath
Copy link
Author

INJIWEB-1761.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant