4
votes

I am using Identity server 4 for user identity and token service. And my client application written in .Net core React template. Everything is working fine, however when end user hit the client side sub URL page (received from email), it is redirecting to STS identity server for authentication and return back to home page instead of to Sub page from where user hit the URL in the begging.

example when user hit client side URL (https://localhost:44309/bills)(which is received through email) it is going login page at (https://localhost:44318/Login) and after user authentication it is redirecting to (https://localhost:44309/Home) instead of (https://localhost:44309/bills).

I used Identity server 4 code written similar to below link

https://github.com/damienbod/IdentityServer4AspNetCoreIdentityTemplate/tree/master/content/StsServerIdentity

Identity server added client


{
        "ClientId": "reactclient",
        "ClientName": "React Client",
        "Enabled": true,
        "RequireClientSecret": false,
        "EnableLocalLogin": true,
        "RequireConsent": false,
        "AllowedGrantTypes": [ "authorization_code", "hybrid", "client_credentials" ],
        "RedirectUris": [ "https://localhost:44309/signin-oidc" ],
        "PostLogoutRedirectUris": [ "https://localhost:44309/logout/callback" ],
        "AccessTokenType": "Jwt",
        "AllowAccessTokensViaBrowser": true,
        //"UpdateAccessTokenClaimsOnRefresh": true,
        "AllowOfflineAccess": true,
        "AccessTokenLifetime": 14400,
        "IdentityTokenLifetime": 7200,
        "AllowedScopes": [
          "openid",
          "profile",
          "email",
          "offline_access"
        ]
      }

Client side

export const IDENTITY_CONFIG = {
    authority: process.env.REACT_APP_AUTH_URI,
    client_id: process.env.REACT_APP_IDENTITY_CLIENT_ID, 
    redirect_uri: process.env.REACT_APP_BASE_URI + process.env.REACT_APP_REDIRECT_PATH,
    automaticSilentRenew: true, 
    filterProtocolClaims: true,
    loadUserInfo: true, 
    silent_redirect_uri: process.env.REACT_APP_BASE_URI + process.env.REACT_APP_SILENT_REDIRECT_PATH, 
    post_logout_redirect_uri: process.env.REACT_APP_BASE_URI + process.env.REACT_APP_LOGOFF_REDIRECT_PATH, 
    response_type: 'code',
    scope: process.env.REACT_APP_SCOPE
};
"base": {
    "REACT_APP_TESTENV": "1",
    "REACT_APP_IDENTITY_CLIENT_ID": "reactclient",
    "REACT_APP_REDIRECT_PATH": "signin-oidc",
    "REACT_APP_SILENT_REDIRECT_PATH": "silentrenew",
    "REACT_APP_LOGOFF_REDIRECT_PATH": "logout/callback",
    "REACT_APP_SCOPE": "openid profile email",
    "NODE_TLS_REJECT_UNAUTHORIZED": "0"
  },
  "development": {
    "REACT_APP_TESTENV": "development",
    "REACT_APP_AUTH_URI": "https://localhost:44318",
    "REACT_APP_AUTH_ISSUER": "https://localhost:44318",
    "REACT_APP_BASE_URI": "https://localhost:44309/",
    "REACT_APP_SERVICE_MEMBER_BASE_URI": "https://localhost:44320/"
  },

Identity server side code. similar to https://github.com/IdentityServer/IdentityServer4.Demo/blob/master/src/IdentityServer4Demo/Quickstart/Account/AccountController.cs

public async Task<IActionResult> Login(LoginInputModel model)
        {
            var returnUrl = model.ReturnUrl;

            ViewData["ReturnUrl"] = returnUrl;
            if (ModelState.IsValid)
            {
                // This doesn't count login failures towards account lockout
                // To enable password failures to trigger account lockout, set lockoutOnFailure: true
                //
                var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberLogin, lockoutOnFailure: false);               
                if (result.Succeeded)
                {
                    Logit("User logged in.");
                    return RedirectToLocal(returnUrl);
                }               
                else if (result.RequiresTwoFactor)
                {
                    return RedirectToAction(nameof(VerifyCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberLogin });
                }
                else if (result.IsLockedOut)
                {

                    Logit("User account locked out.");
                    return View("Lockout");
                }
                else
                {
                    //check if user exists in BIZZ db

                    ModelState.AddModelError(string.Empty, _sharedLocalizer["INVALID_LOGIN_ATTEMPT"]);

                    return View(await BuildLoginViewModelAsync(model));
                }
            }

            // If we got this far, something failed, redisplay form
            return View(await BuildLoginViewModelAsync(model));
        }

Can anyone explain how I can redirect to specific page instead of going to home page each time after login. I would want to solve this at identity server instead at client side.

1

1 Answers

1
votes

You have to use history in reactjs to get previous path and need to save in sessionStorage.

May be this will help you :

const SAVED_URI = 'APP_PLU';
const FORBIDDEN_URIS = ['/login-response', '/'];
const DEFAULT_URI = '/';

function setPostLoginUri() {
  // using just the pathname for demo, should be more detailed in production to
  // include query params, hash bangs, etc
  const currentPath = window.location.pathname;
  const savedURI = sessionStorage.getItem(SAVED_URI);

  if (FORBIDDEN_URIS.includes(currentPath) || savedURI) {
    return;
  }

  sessionStorage.setItem(SAVED_URI, currentPath);
}

function getPostLoginUri(retain) {
  const savedURI = sessionStorage.getItem(SAVED_URI);

  if (!retain) {
    sessionStorage.removeItem(SAVED_URI);
  }

  return savedURI || DEFAULT_URI;
}

export default {
  get: getPostLoginUri,
  set: setPostLoginUri
};

And set in the app.js

and then in your login response page add this code ,

function LoginResponse({ history, setUser }) {
  const [error, setError] = useState(null);

  useEffect(() => {
    // the login redirect has been completed and we call the
    // signinRedirectCallback to fetch the user data
    userManager.signinRedirectCallback().then(user => {
      // received the user data so we set it in the app state and push the
      // router to the secure or bookmarked route
      setUser(user);
      history.push(postLoginUri.get());
    }, ({ message }) => {
      // userManager throws if someone tries to access the route directly or if
      // they refresh on a failed request so we just send them to the app root
      if (message && redirectErrors.includes(message)) {
        history.push('/');
        return;
      }

      // for all other errors just display the message in production it would be
      // a good idea to initiate a sign out after a countdown
      setError(message);
    });
  }, []);

  return error;
}

export default LoginResponse;