3

I simply want to save cookies containing security tokens, but they are not persisted in the browser. Here is my AuthController method for saving the cookie (simplified):

[AllowAnonymous]
[HttpPost("authorize")]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<IActionResult> AuthorizeAsync()
{
    //generating access token ommited for brevity
    SetTokenCookie(accessToken);
    return Ok(userIdentity);
}

And SetTokenCookie method:

private void SetTokenCookie(string accessToken)
    {
        var options = _jwtOptions.AccessToken;
        var cookieOptions = new CookieOptions()
        {
            HttpOnly = true,
            IsEssential = true,
            Secure = false,
            SameSite = SameSiteMode.Strict,
            Domain = options.Issuer, //http://localhost:3394 by default
            Expires = DateTime.UtcNow.AddDays(14)
        };

        Response.Cookies.Append(options.Name, accessToken, cookieOptions);
    }

Now when I analyze the response from my Api, I see that Set-Cookie header and token itself are fine: Response headers Decoded token:

{
  "id": "70132f61-4d83-4772-9685-7a77a9204685",
  "name": "admin",
  "email": "xyz@xyz.pl",
  "role": "Administrator",
  "persist": "True",
  "nbf": 1646336045,
  "exp": 1646336945,
  "iat": 1646336045,
  "iss": "http://localhost:3394",
  "aud": [
    "blog.webgateway",
    "blog.blogging",
    "blog.comments",
    "blog.users"
  ]
}

But when I check the cookies, nothing is saved.

I know that there are many topics related to this issue, but I already ran out of ideas I could find online:

  1. I wiped storage in my browser for localhost
  2. I added custom entry in my hosts file and changed cookie domain accordingly
  3. I tried setting cookie domain = null or different path
  4. I added Cors and allowed credentials, any methods, any headers, any origin
  5. I tried more permissive settings in my browser
  6. I tried changing cookie options (HttpOnly, Secure, SameSite)
  7. I removed UseHttpsRedirection() from my Startup.cs and made sure I connet via HTTP

Nothing seems to work. I'm using Firefox 97.0.1. Do you know what else I could try?

toffik325
  • 161
  • 3
  • 11
  • The domain attribute seems a little odd... should just be something like "your_site.com" no need for "http://" or a port. How are you checking for the cookies? Sounds like this would be a session authorization cookie... there should be a built-in mechanism for that. – pcalkins Mar 03 '22 at 21:27
  • @pcalkins, thank you. I also tried adding such line in my hosts file ```127.0.0.1 blog.com``` and changed domain in my cookie to ```blog.com``` or ```.blog.com``` and nothing worked... I'm checking for the cookies in development tools (ctrl + shit + i) -> data -> cookies. It's empty... I also added ```HttpContext.SignAsync()``` in the controller's function to check whether Asp.Net cookie works and it's also not persisted in the browser... – toffik325 Mar 03 '22 at 21:52
  • I would just exclude the domain. The browser should set it to the origin. (which in this case is localhost I guess... ) – pcalkins Mar 03 '22 at 22:16
  • I've also tried that with no luck... – toffik325 Mar 03 '22 at 22:22
  • the type as JSON is a little odd. Is this call made via ajax? (blazor client?) If so you probably want to write the cookie from your Javascript. The server's response will go to the script... – pcalkins Mar 03 '22 at 22:46
  • @pcalkins, I guess that the type is a Json because I'm also returning the user's identity in the body. You can see that in the ```AuthorizeAsync``` function: ```return Ok(userIdentity)```. ```userIdentity``` is just a class containing some user details and it's returned properly. The call is made via Developer Tools in Firefox as described here: https://stackoverflow.com/a/26848256/14614166 – toffik325 Mar 04 '22 at 00:28
  • you should try to keep things async all the way through if you're setting this as async (with awaits), but I don't think that's the problem. I think your request to set the cookie is being gobbled up in a JS callback. It doesn't ever get to the browser. You can send a standard http request to set the header to the browser and it won't change the page. (no callback)Then do whatever else you needed to do. OR just set it via javascript if it's not tied to a server-side session variable. – pcalkins Mar 04 '22 at 18:10

3 Answers3

2

Did you try to change the Domain to localhost?

Per my test, using `` didn't work for me, and then I found that other cookies showed they belong to domain localhost, so I use localhost instead, then I can see the newly created cookie. I test to call the api by tools in chrome, so I think it should be similar to your scenario.

public string saveCookie() {
    var cookieOptions = new CookieOptions()
    {
        HttpOnly = true,
        IsEssential = true,
        Secure = false,
        SameSite = SameSiteMode.Strict,
        Domain = "localhost", //using https://localhost:44340/ here doesn't work
        Expires = DateTime.UtcNow.AddDays(14)
    };

    Response.Cookies.Append("testCookie", "Cookie content", cookieOptions);
    return "hello world";
}

enter image description here

Tiny Wang
  • 10,423
  • 1
  • 11
  • 29
  • 1
    Thank you. I tried this one as well and no luck unfortunately :( – toffik325 Mar 04 '22 at 12:16
  • @toffik325 Thank you for your reply then if [this answer](https://stackoverflow.com/a/52539179) can help you? I test it in my newly created asp.net core MVC app, so there's no other policies or middleware. Can you check with your startup file? – Tiny Wang Mar 07 '22 at 01:36
  • thanks for your second hint. I have to revise my statement about the first one as it indeed did a job for me, but the way I was calling my app was causing an issue as well (Post method directly from Firefox). – toffik325 Mar 07 '22 at 19:32
1

I've finally managed to solve the issue... Here are the steps I've made:

  1. Changed the controller method to HttpGet, so it just looks like this now:
[AllowAnonymous]
[HttpGet("authorize")] // <-- notice the difference
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<IActionResult> AuthorizeAsync()
{
    //generating access token ommited for brevity
    SetTokenCookie(accessToken);
    return Ok(userIdentity);
}

For some reason calling a Post request directly from the browser (Firefox at least in my case) doesn't seem to work for setting the cookies, even if the response looks fine, but when I changed it to Get method and accessed in a standard way (URL) it works. I will have to double-check if Post method works from the client (JavaScript).

  1. Apply above solution from Tiny Wang, which means changing the cookie domain to localhost. Having the full URL indeed prevent cookie from being saved.

Edit: As pointed out by SSchmid in the comments, changing method to "Get" was only a temporary workaround while using Development Tools in Firefox. For further development or production it is not recommend to keep it as a "Get" method.

toffik325
  • 161
  • 3
  • 11
  • im afraid this is no solution for everyone since the httpget is not good for using passwords etc. – SSchmid May 31 '22 at 12:22
  • @SSchmid, you are completely right and I wouldn't recommend that - I updated my answer. The issue was only related to the Developer Tools I used in Firefox to call "Post" method - for some reason cookies obtain in this way were not saved in the browser. For further development or production I would definitely not go with "Get". – toffik325 May 31 '22 at 16:10
1

I managed to get it running by using the [FromBody] Attribute. This works for me :

    [HttpPost("Login")]
    public async Task<IActionResult> Login([FromBody] LoginData data)
    {

        var user = await userManager.FindByEmailAsync(data.Email);
        if (user == null || !await userManager.CheckPasswordAsync(user, data.Password))
            return Unauthorized("Invalid Authentication");

        await signInManager.SignInAsync(user, data.rememberMe);
        var roles = (await userManager.GetRolesAsync(user)).ToList();
        var sUser = new Models.User(user, roles);
        return Ok(sUser);
    }

apparently if u use HTTPPOST in combination with parameters it doesnt work.

SSchmid
  • 431
  • 4
  • 3