WebApp Template login page

I’m using the React WebApp Template and what I’m trying to do is to check if the user is authenticated and if not present then with a html form for them to login. I can login however I get the user auth details which I don’t want, I’ve tried looking for ways to redirect but have been unsuccessful.

Am going about this the wrong way?

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, maximum-scale=1, minimum-scale=1">
    {{ ifDebug | select: <script>{ '/js/hot-fileloader.js' | includeFile }</script> }}
<link href="/dist/vendor.dll.css" rel="stylesheet"></head>
<body class="bg-light">
    <div class="container">
        <div id="app"></div>
        {{ ifNotAuthenticated | select: <form action="/auth/credentials" method="POST" class="form-signin"><h1 class="h3 mb-3 font-weight-normal">Please sign in</h1><label for="inputEmail" class="sr-only">Email address</label><input type="email" id="inputEmail" name="UserName" class="form-control" placeholder="Email address" required autofocus /><label for="inputPassword" class="sr-only">Password</label><input name="Password" type="password" id="inputPassword" class="form-control" placeholder="Password" required/><div class="checkbox mb-3"><input type="checkbox" value="remember-me"> <label>Remember me</label></div><button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button></form> }}
        <div id="footer" style="text-align:center; position:absolute; bottom:50px; width:100%;">
            <h4>
                <img src="/dist/img/logo.png" />

        </div>
    </div>

<script type="text/javascript" src="/dist/vendor.dll.js"></script><script type="text/javascript" src="/dist/app.bundle.js"></script></body>
</html>

Ways to redirect in ServiceStack if they’re not Authenticated, create a Service with routes for all the pages you want to redirect, e.g

Creates Auth Only Service with Routes of Pages you want to redirect

[Route("/authonly")]
[Route("/contact")]
public class RedirectToLoginPage {}

[Authenticate(HtmlRedirect="~/login")]
public class AuthOnlyServices : Service
{
    public object Any(RedirectToLoginPage request) => request;
}

HtmlRedirect can also be globally declared in your AuthFeature plugin

Add a Metadata Redirect rule

If you want to do this for all pages you can emit a HTML refresh meta tag if they’re not authenticated and the they’re not on the login page, e.g:

<head>
    {{ if(and(!matchesPathInfo("/login"), !isAuthenticated)) 
       | show: <meta http-equiv="refresh" content="0;URL='/login'" />  
       | raw }}
</head>

Use JavaScript

If you want to redirect adhoc pages you can use JavaScript to redirect, e.g:

{{ `<script>
    location.href = "/login";
</script>` | ifNotAuthenticated | appendTo: scripts }}

Wrap this in a partial

For snippets like this I’d prefer to wrap it in a partial, e.g. copy the above fragment in a file like ensure-authenticated.html then embed it in pages you want to redirect if they’re not authenticated:

{{ 'ensure-authenticated' | partial }}

Thanks for that, I’ve implemented the metadadta redirect rule, but after a successful login it keeps redirecting as though I’m not authenticated.

If I remove the redirect template code and navigate manually to login and authenticate I can called services that require authentication. Whats the best way to debug the isAuthenticated filter?
The session cookie is there matches to the corresponding cache table.

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, maximum-scale=1, minimum-scale=1">
    <title>Parcel Scan</title>
    {{ if(and(!matchesPathInfo("/login"), !isAuthenticated)) 
    | show: <meta http-equiv="refresh" content="0;URL='/login'" />  
    | raw }}

</head>
<body class="bg-light">
    <div class="container">
        <div id="app"></div> 
        <div id="footer" style="text-align:center; position:absolute; bottom:50px; width:100%;">
            <h4>
                <img src="<%=require('./src/assets/img/logo.png')%>" />
                <!-- <a href="https://servicestack.net/vs-templates/ReactApp">Learn about this React VS.NET template</a> -->
            </h4>
            <div>Copyright &copy; {{ now | dateFormat('yyyy') }}</div>
        </div>
    </div>

</body>
</html>

It suggests you’re not authenticated, have a look at info filters to try inspect the session info.

I’ve copied the TemplateCode on to the acutal index.html page and below is the output.
There is no information on the session except for the ss-id, aswell as the appHostServiceName and other fields blank.

Service Name              {{ appHost.ServiceName }}
                    Handler Path              {{ appConfig.HandlerFactoryPath }}
                    VirtualFiles Path         {{ appVirtualFilesPath }}
                    VirtualFileSources Path   {{ appVirtualFileSourcesPath }}
                    OS Environment Variable   Windows_NT
                    ServiceStack Version      5.10
                    
                    Request: 
                      - RemoteIp              {{ request.RemoteIp }}
                      - UserHostAddress       {{ request.UserHostAddress }}
                      - PathInfo              {{ request.PathInfo }}
                      - UserAgent             {{ request.UserAgent }}
                    
                    Session:
                      - ss-id                 LYqQG4ri2OCytRRcjP3k
                      - ss-pid                
                      - ss-opt                
                    
                    User: 
                      - IsAuthenticated       
                      - UserName              
                      - LastName              
                      - Is Admin              False
                      - Has Permission        False
                    
                    Plugins: 
      - HtmlFormat
      - CsvFormat
      - PredefinedRoutesFeature
      - MetadataFeature
      - NativeTypesFeature
      - HttpCacheFeature
      - RequestInfoFeature
      - PostmanFeature
      - CorsFeature
      - SessionFeature
      - TemplatePagesFeature
      - AuthFeature
      - SessionFeature

But when I use the /metadata/debug I get the following

This is the login page, funnily enough when I hit this page Templates report that I’m authenticated…

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0, maximum-scale=1, minimum-scale=1">
    <title>Parcel Scan</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
    <style>
        .headline {
            margin: 45px 0 45px 0;
            padding: 40px 0 45px 0;
            border-top: #e0e3e6 1px solid;
            border-bottom: #e0e3e6 1px solid;
            text-align: center;
            font-size: 24px;
        } 
    </style>
    </head>
<body class="bg-light">
<script
    src="https://code.jquery.com/jquery-3.3.1.min.js"
    integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
    crossorigin="anonymous"></script>


<div class="container">
    <div class="headline"> Sign-in to your account. </div>
    User: 
  - IsAuthenticated       {{ userSession | select: { it.IsAuthenticated } }}
  - UserName              {{ userSession | select: { it.UserName } }}
  - LastName              {{ userSession | select: { it.LastName } }}
  - Is Admin              {{ userHasRole('Admin') }}
  - Has Permission        {{ userHasPermission('ThePermission') }}
    <form id="login-form" action="/auth/login" method="POST" style="text-align: center; padding: 0 0 300px 0" _lpchecked="1">
        <div style="font-size: 16px; width: 400px; text-align: left; display: inline-block;">
            <p class="error-summary alert alert-danger"></p>
            <div class="form-group"> <label for="email">Email Address</label> <input class="form-control input-lg" type="text" name="UserName" value="" style="background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII=&quot;); cursor: auto;"> </div>
            <div class="form-group"> <label for="password">Password</label> <input class="form-control input-lg" type="password" name="Password" value="" placeholder="" style="background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII=&quot;); cursor: auto;"> </div>
            <div class="form-group" style="text-align: center; padding: 20px 0 0 0">
                <button class="btn btn-grove-one btn-xlg btn-bold" style="width: 200px" type="submit">Sign In</button>
                <p> <a class="forgot-password" href="/forgot-password">forgot password?</a> </p>
            </div>
        </div>
        <div class="clearfix"></div>
    </form>
</div>
    
<script src="/js/ss-utils.js"></script>

<script>

    $("#login-form").bindForm({
        success: function(r) {
            location.href = '/';
        }, 
        error: function(data) {
            console.log(data);
        },
    });
</script>

</body>
</html>

Not sure what I can be doing wrong.

What else can you suggest I try to work around this problem?

I don’t have any other suggestions, inspect the raw HTTP Headers to make sure the ss-id/ss-pid Cookies are being sent which point to an Authenticated UserSession in the registered ICacheClient and that calling /auth returns a session, if it returns a 401 it means you’re not authenticated.

The session is registered in the ICacheClient I can see it in the db. The debug page /metadata/debug also confirms I’ve authenticated and so does every other html pages EXCEPT INDEX.HTML

I have even placed the login html snippet in index.html and {{ userSession | select: { it.IsAuthenticated } }} still comes back with nothing BUT it will work on another htm page.

I’m not doing anything fancy just want to ensure that the user is logged on before accessing the react app. Is there any chance you can provide me with a modified react-spa project that includes code to log in via Index.html.

I maybe wrong but I have a feeling that this is bug…

I don’t have the spare capacity to create customized starting templates. If you’re using React you can use JavaScript to check if they’re authenticated and redirect if not, e.g:

try {
    var session = await client.get(new Authenticate());
} catch (e) {
    location.href = '/login';
}

I think you misunderstood me, I was asking if you can use the react-spa app and provide some login features so I can compare and see where I’m going wrong.

To save you some time I have created a new react-spa app and put in two html pages index and login.
I was able to replicate the issue.
https://github.com/laitang2000/test-login-templates

The login for the user is

username:testman@test.com
pw:!Abc1234

Ok the issue is that the FallbackForClientRoutes page doesn’t have access to the IRequest context, you can inject it with:

public object Any(FallbackForClientRoutes request) => 
     new PageResult(Request.GetPage("/")) { Args = { [nameof(Request)] = Request } };

I’ve also added a new Request.GetPageResult("/") which you can use in future, e.g:

public object Any(FallbackForClientRoutes request) => 
    Request.GetPageResult("/");

The new GetPageResult is available from v5.1.1 that’s now on MyGet.

:raised_hands: :raised_hands: :raised_hands: