Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth cookies not set using SSB #138

Closed
c4l3b opened this issue Jan 20, 2020 · 10 comments
Closed

Auth cookies not set using SSB #138

c4l3b opened this issue Jan 20, 2020 · 10 comments
Labels
bug Something isn't working

Comments

@c4l3b
Copy link

c4l3b commented Jan 20, 2020

When running the app locally using SSB, I can't get the auth cookies to set. CSB is fine.
I can log in and make some authenticated requests, but when I refresh the page I am logged out.
Have tried both Mac and Windows with every browser I can find.
Neither the .AspNetCore.Identity.Application or idsrv.session cookies are being set.

Is it just me, or is anyone else experiencing this issue too?

Where are these cookies being set right now?

@enkodellc
Copy link
Owner

@c4l3b most of the time I have seen similar issues of not authenticating it is a browser cache or your appsettings.json / connection string. Please try a different browser first, especially when switching from CSB to SSB, I have some glitches with logging in that solves it. Try that first.

@c4l3b
Copy link
Author

c4l3b commented Jan 20, 2020

Thanks @enkodellc
Unfortunately I've tried every browser and combination I can.
Do you have a live SSB demo that I could try? Or just the CSB one?

@enkodellc enkodellc added the bug Something isn't working label Jan 20, 2020
@enkodellc
Copy link
Owner

@c4l3b Sorry I was busy and assumed it was a user specific issue. I tested and can recreate it. Personally I don't use SSB, only CSB. I will take a look to see what I can sort out. @MarkStega any ideas?

@c4l3b
Copy link
Author

c4l3b commented Jan 21, 2020

I did come across this in my research. 6th paragraph talks about the need for a redirect-style flow when using SSB. With how bleeding edge blazor is right now, I wasn't sure if it was still relevant or not.
https://www.oqtane.org/Resources/Blog/PostId/527/exploring-authentication-in-blazor

@MarkStega
Copy link
Contributor

I am looking and see that GetUserInfo() is not getting called when the page is refreshed. I have a morning appt so will have to do a deeper dive later today.

@marcotana
Copy link

marcotana commented Jan 27, 2020

Because the cookies are not set on the browser in SSB, when you refresh the page, the _httpClient used to call the APIs are essentially not used since it's registered as Scoped. This _httpClient gets its cookies when it calls the APIs. It works in CSB because those cookies are set in the browser. To make this work:

  1. Set the client-side cookies using a JS Interop.
  2. When the user refreshes page, read the request cookies and assign them back to the _httpClient.
  3. Remove cookies when logged out.

NOTE: You may want to be specific which cookies (.AspNetCore.Identity.Application and idsrv.session) you are setting and removing as other cookies may exist.

Set the client-side cookies using a JS Interop
For instance, I changed the Login call to:

    public async Task<ApiResponseDto> Login(LoginDto loginParameters)
    {
        ApiResponseDto resp;
        var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, GetApiUrl("api/Account/Login"));
        httpRequestMessage.Content = new StringContent(JsonConvert.SerializeObject(loginParameters));
        httpRequestMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

        using (var response = await _httpClient.SendAsync(httpRequestMessage))
        {
            response.EnsureSuccessStatusCode();

            #if ServerSideBlazor
            if (response.Headers.TryGetValues("Set-Cookie", out var cookieEntries))
            {
                var uri = response.RequestMessage.RequestUri;
                var cookieContainer = new CookieContainer();

                foreach (var cookieEntry in cookieEntries)
                {
                    cookieContainer.SetCookies(uri, cookieEntry);
                }

                var cookies = cookieContainer.GetCookies(uri).Cast<Cookie>();

                foreach (var cookie in cookies)
                {
                   await _jsRuntime.InvokeVoidAsync("jsInterops.setCookie", cookie.ToString());
                }
            }
           #endif

            var content = await response.Content.ReadAsStringAsync();
            resp = JsonConvert.DeserializeObject<ApiResponseDto>(content);
        }

        return resp;
    }

This calls a JS function client-side that basically sets the cookie: document.cookie = cookie.

Read the request cookies and assign them back to the _httpClient
Basically, I updated the App.razor's OnInitializedAsync():

protected override async Task OnInitializedAsync()
{
    await base.OnInitializedAsync();

    _httpClient.BaseAddress = new Uri(_navigationManager.BaseUri);

   #if ServerSideBlazor
    if (_http != null && _http.HttpContext.Request.Cookies.Any())
    {
        var cks = new List<string>();

        foreach (var cookie in _http.HttpContext.Request.Cookies)
        {
            cks.Add($"{cookie.Key}={cookie.Value}");
        }

        _httpClient.DefaultRequestHeaders.Add("Cookie", String.Join(';', cks));
    }
   #endif
}

Remove Cookies on Logout
Finally, we need to remove the cookies from both the browser and _httpClient when user logs out successfully. In the AuthorizeApi, I updated:

    public async Task<ApiResponseDto> Logout()
    {
        var cookies = _httpClient.DefaultRequestHeaders.GetValues("Cookie").ToList();

        var resp = await _httpClient.PostJsonAsync<ApiResponseDto>(GetApiUrl("api/Account/Logout"), null);

        #if ServerSideBlazor
        if (resp.StatusCode == 200 && cookies.Any())
        {
            _httpClient.DefaultRequestHeaders.Remove("Cookie");

            foreach (var cookie in cookies[0].Split(';'))
            {
                var cookieParts = cookie.Split('=');
                await _jsRuntime.InvokeVoidAsync("jsInterops.removeCookie", cookieParts[0]);
            }
        }
      #endif

        return resp;
    }

and create the JS Interop function removeCookie to basically have:

   function (cookieName) {
       document.cookie = cookieName + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
   }

I tested this both in SSB and CSB. You'll notice that I included the #if directives as well as they are needed particularly when using the IHttpContextAccesor which doesn't exist in CSB mode.

@enkodellc
Copy link
Owner

@marcotana I appreciate the thorough. Do you or @c4l3b want to test and submit a PR?

@marcotana
Copy link

You're welcome. I'll fork it and submit a PR.

@rc-marcotana
Copy link

FYI, this was resolved with the merge SSB Auth Cookies

@enkodellc
Copy link
Owner

@marcotana Thanks for the PR. Just merged and worked great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants