Log user out after loading if unauthorized

Hello, here is how I am loading my Schedule

.AppointmentSettings(fields => fields.Datasource(datasource => datasource.URL("/SomeAction/GetAppointmentsAsync"))
                    .ApplyTimeOffset(false)
                    .Id("Id")
                    .Subject("Subject")
                    .StartTime("StartTime")
                    .EndTime("EndTime")
                    .Description("Description")
                    .Location("Location")
                    .Categorize("Categorize")
                    .AllDay("AllDay")
                    .Recurrence("Recurrence")
                    .RecurrenceRule("RecurrenceRule")
                    )

Inside "GetAppointmentsAsync", the app communicates with the API and API will send "unauthorized (401)" if the user isn't authorized.
If so, I try to log the user out of application by doing the following:

var response = await results;

                if (response.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    result = JsonConvert.DeserializeObject<IEnumerable<SomeModel>>(response.Content.ReadAsStringAsync().Result);
                }
                else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    HttpContext.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
                    return Json(new { redirectUrl = Url.Action("Login", "Account", new { errorMessage = "Token expired. Please re-log in." }), isRedirect = true });
                }


After that, I try to detect if the user is still logged in or not on clientside Create function 

.ScheduleClientSideEvents(eve => eve.Create("onCreate"))

then try to redirect the user if needed.

However, "onCreate" function gets executed before  "HttpContext.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);" from above the code and still thinks the users is logged in.

How can I make this happen?

Thank you




11 Replies

KK Karthigeyan Krishnamurthi Syncfusion Team November 28, 2017 12:14 PM UTC

 
Thank you for contacting Syncfusion support. 
 
In latest version (15.4.0.17) ActionComplete event will be raised after execution of DataManger URL function (GetAppointmentsAsync), kindly try that event. 
 
Regards, 
Karthigeyan 




AJ Andrew Jang November 28, 2017 07:19 PM UTC

Okay so I've tried ActionComplete javascript function but it doesn't get executed at all. Only Create gets executed.
To make sure, I've upgraded all my Syncfusion DLLs to the latest 15.4.0.17 and related javascript/CSS files. 



.ScheduleClientSideEvents(eve => eve.AppointmentWindowOpen("onAppointmentWindowOpen").BeforeAppointmentCreate("onCRUDActions").BeforeAppointmentRemove("onCRUDActions").BeforeAppointmentChange("onCRUDActions").ActionComplete("onCreate2").DragStop("onDragandResizeStop").AppointmentHover("onAppointmentHover").AppointmentClick("onAppointmentClick").Create("onCreate"))

Basically, only "onCreate" gets executed and "onCreate2" doesn't.


KK Karthigeyan Krishnamurthi Syncfusion Team November 29, 2017 04:42 PM UTC

 
Thanks for your update. 
 
In latest source, ActionComplete event will be triggered after the completion of the initial appointment rendering and we would like to inform that control will not wait for the completion of the ajax success (GetAppointmentsAsync) and execute the next code, therefore only Create event is called first. It is default behavior. 
 
Regards, 
Karthigeyan 




AJ Andrew Jang November 29, 2017 07:34 PM UTC

I can't get "ActionComplete" executed at all after the completion of the initial appointment rendering.

Take a look at the following code (index.cshtml):

@{ ViewBag.Title = "Schedule"; }

<script>
        function onCreate2(args) {
        alert("In2");
    }

</script>

@(Html.EJ().Schedule("Schedule1")
        .Width("100%")
        .Height("525px")
        .CurrentDate(new DateTime(2016, 10, 29))
        .Orientation(Orientation.Horizontal)
        .CurrentView(CurrentView.Month)
        .ShowOverflowButton(false)
        .ScheduleClientSideEvents(eve => eve.ActionComplete("onCreate2"))
        .Resources(res =>
        {
            res.Field("OwnerId").Title("Owner").Name("Owners").AllowMultiple(true).ResourceSettings(flds => flds.Datasource(ViewBag.Owner).Text("Text").Id("Id").Color("Color")).Add();
        })
        .Group(gr =>
        {
            gr.Resources(ViewBag.Resources);
        })
        .AppointmentSettings(fields => fields.Datasource(ViewBag.datasource)
                .Id("Id")
                    .Subject("Subject")
                    .StartTime("StartTime")
                    .EndTime("EndTime")
                    .AllDay("AllDay")
                    .Recurrence("Recurrence")
                    .RecurrenceRule("RecurrenceRule")
                    .ResourceFields("OwnerId"))
)

I was expecting to see alert("In2") getting executed when loading or after loading but  "ActionComplete" never gets executed at any initial loading stage.
ActionComplete only gets executed after I perform actions like "changing dates" or "change month view to week view".

I've used the sample solution that Syncfusion provided for the other issue to make sure that it's using the latest 15.4.0.17.

And I may have found a bug in this forum.
I tried to attach sample solution(.7z) which is less than 30 MB, but it fails to attach every time when I upload.(29.5MB)
I can provide the file if you need me to.




KK Karthigeyan Krishnamurthi Syncfusion Team November 30, 2017 03:20 PM UTC

Hi Andrew, 
 
Thanks for your update. 
 
In your code example, Data Manager is not used which is the cause for the issue. We have prepared the sample and video demo for your reference which can be download from the below location. 
 
<Code> 
@(Html.EJ().Schedule("Schedule1") 
        .Width("100%") 
        .Height("525px") 
        .CurrentDate(new DateTime(2016, 10, 29)) 
        .ScheduleClientSideEvents(eve => eve.ActionComplete("onCreate2")) 
        .AppointmentSettings(fields => fields.Datasource(ds => ds.URL("/Home/GetData").CrudURL("/Home/Batch").Adaptor("UrlAdaptor")) 
           .ApplyTimeOffset(false) 
           .Id("Id") 
           .Subject("Subject") 
           .StartTime("StartTime") 
           .EndTime("EndTime") 
           .AllDay("AllDay") 
           .Recurrence("Recurrence") 
           .RecurrenceRule("RecurrenceRule")) 
) 
</Code> 
 
We request you to share the image/video demo of the issue or else you can share the files/code example instead of sample to serve you better. 
 
Regards, 
Karthigeyan   
 
 
                                                                               



AJ Andrew Jang November 30, 2017 07:20 PM UTC

Hello, thank you for your reply.

Using your sample code, I was able to figure out why ActionComplete wasn't getting executed at all in the beginning.
Long story short, it had something to do with 

return Json(new { redirectUrl = Url.Action("Login", "Account", new { errorMessage = "Token expired. Please re-log in." }), isRedirect = true });

in my code. I had to restructure how Json is returned to the Schedule.

So okay, going back to the original discussion, can I use "ActionComplete" to redirect users after data load?
I'm a bit puzzled, because of some of the replies that I got. 
On Nov 28th reply, the reply suggests using "ActionComplete", but on 29th reply says "ActionComplete" will not wait for Ajax success.
I need further clarification if this is a suitable option to resolve the issue.
If not, is there another function that I can consume?
What would be the alternative way to get around this?


KK Karthigeyan Krishnamurthi Syncfusion Team December 1, 2017 10:35 AM UTC

Hi Andrew,  
 
Thanks for your update. 
 
Create event only will not wait for Ajax success, kindly try ActionComplete event for your requirement as it will be raised after the initial execution of DataManger URL function (GetAppointmentsAsync). There is no alternate way since no event (other than ActionComplete) will be raised after the initial execution of DataManger URL function. 
 
Regards, 
Karthigeyan 



AJ Andrew Jang December 5, 2017 06:48 PM UTC

Hello Karthigeyan,

Thank you for your reply.
I've tried "Action Complete" and found an interesting behavior. 
Basically, I am trying set a value for ViewBag.Test while DataManager URL is being executed as following:

public JsonResult GetData()
        {
            IEnumerable data = new DataClasses1DataContext().ScheduleDatas.Take(100); // nw.Appointment.Take(5);
            ViewBag.Test = "Testing";
            return Json(data, JsonRequestBehavior.AllowGet);
        }

And then print @ViewBag.Test in "Action Complete".


@(Html.EJ().Schedule("Schedule1")
        .Width("100%")
        .Height("525px")
        .CurrentDate(new DateTime(2016, 10, 29))
        .ScheduleClientSideEvents(eve => eve.ActionComplete("onCreate2"))
        .AppointmentSettings(fields => fields.Datasource(ds => ds.URL("/Home/GetData").CrudURL("/Home/Batch").Adaptor("UrlAdaptor"))
           .ApplyTimeOffset(false)
           .Id("Id")
           .Subject("Subject")
           .StartTime("StartTime")
           .EndTime("EndTime")
           .AllDay("AllDay")
           .Recurrence("Recurrence")
           .RecurrenceRule("RecurrenceRule"))
)
<script>
    function onCreate2(args) {
        alert('@ViewBag.Test');
    }
</script>

But it seems like alert is always printing an empty.
Is there a way to populate ViewBag while GetData()?

Thank you.


KK Karthigeyan Krishnamurthi Syncfusion Team December 6, 2017 06:44 AM UTC

 
Thanks for your update. 
 
ViewBag value can be accessed only if the View is returned, therefore kindly use the below code example to access the ViewBag value within Scheduler event. 
 
<Code> 
public ActionResult Index() 
{ 
    ViewBag.Test = "Testing"; 
    return View(); 
} 
function onCreate2(args) { 
    alert('@ViewBag.Test'); 
} 
</Code> 
 
Regards, 
Karthigeyan 



AJ Andrew Jang December 6, 2017 05:34 PM UTC

Hello Karthigeyan,

Ok, so I think I need to explain a little more about what I want to do.
What I am wanting do is that when the Data manager URL is executed, the application pings the API to obtain data.
However, if user token is expired, the API will send "401 Unauthorized" reply.

At that point, I am wishing to log the user out and then redirect to login page.

Here's what I've tried to do in GetAppointmentAsync function.

else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    HttpContext.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
                    System.Web.HttpContext.Current.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
                    //ClientScript 
                    //return Json(new { redirectUrl = Url.Action("Login", "Account", new { errorMessage = "Token expired. Please re-log in." }), isRedirect = true });
                }
 
After doing that, I tried to check if a user is still logged in or not in ActionComplete Javascript function.
    function afterLoad(args) {
        var isRequestAuthenticated = '@Request.IsAuthenticated';
        alert('@HttpContext.Current.GetOwinContext().Authentication.User.Identity.IsAuthenticated');
        alert(isRequestAuthenticated);
    }

However, it always returns false.

Here's how I declare client-side event for my Schedule.

 .ScheduleClientSideEvents(eve => eve.ActionComplete("afterLoad"))

I thought to pass a value using a ViewBag to indicate if logged out or not would be easier but I guess I was wrong.

Is there a way to make this happen?



KK Karthigeyan Krishnamurthi Syncfusion Team December 7, 2017 07:15 AM UTC

Hi Andrew,   
  
Thanks for your update. 
 
In your project, if CrudURL is not required then ajax post can be used within Create event to load the appointments initially where we can access the controller function values as shown below. 
 
<Code> 
.ScheduleClientSideEvents(eve => eve.Create("onCreate2")) 
 
function onCreate2() { 
    $.ajax({ 
        url: "/Home/GetData", type: 'POST', success: function (result) { 
            alert(result.status); 
            var object = []; 
            // Stores the result values to the object collection. 
            for (var i = 0; i < result.dataColl.length; i++) { 
                object[i] = { 
                    Id: result.dataColl[i].Id, 
                    Subject: result.dataColl[i].Subject, 
                    StartTime: new Date(result.dataColl[i].StartTime.match(/\d+/)[0] * 1), 
                    EndTime: new Date(result.dataColl[i].EndTime.match(/\d+/)[0] * 1), 
                    Description: result.dataColl[i].Description, 
                    AllDay: result.dataColl[i].AllDay, 
                    Recurrence: result.dataColl[i].Recurrence, 
                    RecurrenceRule: result.dataColl[i].RecurrenceRule 
                } 
            } 
            $("#Schedule1").ejSchedule("option", "appointmentSettings.dataSource", object); // Assigns the DataSource to the Schedule control. 
        }, 
    }); 
} 
 
public JsonResult GetData() 
{ 
    IEnumerable data = new DataClasses1DataContext().ScheduleDatas.Take(100); 
    var values = new { dataColl = data, status = "Testing" }; 
    return Json(values, JsonRequestBehavior.AllowGet); 
} 
 
</Code> 
 
Regards, 
Karthigeyan 





Loader.
Up arrow icon