1

So I decided to create a simple web app which sends a login request to my login endpoint on my Web API like this.

  await fetch("https://localhost:1234/api/login", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ Username: "Admin", Password: "Password" }),
  })
    .then((response) => response.json())
    .then((jsondata) => {
      console.log(jsondata);
    });
}

And what happens on my Web API is this

public IActionResult Login([FromBody] UserLogin userLogin)
{
    var user = Authenticate(userLogin);

    if (user != null)
    {
        var token = Generate(user);
        Response.Cookies.Append("Authorization", token, new CookieOptions { HttpOnly = true, Secure = true });
        
        return Ok();
    }

    return NotFound("User not found.");
}

So when the user logs in with a valid username and password, it generates a JWT and adds it to the response cookies. When I make a request with Postman/Insomnia, I can see the cookie in the response which means that on the client, when I make the same request I should get the same response. However, the issue is that I have no idea how to retrieve that cookie and store it so that I can send it in later requests when trying to access pages that require a JWT to authorize.

When I check the browsers and I got to Application > Cookies there is nothing there.

The goal is to be able to receive it, store is as a httpOnly cookie (because that's that's slightly safer than storing it on session/localStorage I've read) and then be able to use it to access other pages which requires me to send it to authorize.

That seems to be an issue because you can read or write httpOnly cookies using JavaScript https://stackoverflow.com/a/14691716/5693405

What's the proper way of dealing with this?

Liam
  • 27,717
  • 28
  • 128
  • 190
JohnA
  • 564
  • 1
  • 5
  • 20
  • You appear to be reinventing authorisation, which has many implementations, and would you be better off using. Generally, you wouldn't return authorisation in a cookie, it would be returned in the response body, and then the caller would then put the token in the header of each subsequent API request. – Neil Mar 25 '22 at 08:11
  • 2
    The **response** cookie won't get magically into your clients browser, a better aproach is that your API returns Ok(token) and then on your front end ,store the token in cookies if you want but be sure to append it in header of subsequents api calls – J.Salas Mar 25 '22 at 08:12
  • @Neil Could you please elaborate? I'm slightly confused. Let's say I were to return it in the body like so `Ok(token)`. I would still have to store it somewhere and somehow so that I can use it from the caller when making requests, I'm pretty sure I read somewhere that returning it like that `"(Ok(token))"` is generally speaking "unsafe"? – JohnA Mar 25 '22 at 08:17
  • Does this answer your question? [Persist variables between page loads](https://stackoverflow.com/questions/29986657/persist-variables-between-page-loads) – Liam Mar 25 '22 at 08:33
  • @Liam It does not, no. – JohnA Mar 25 '22 at 08:48
  • *store is as a httpOnly cookie* Have you read up on [httpOnly Cookies?](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies) *A cookie with the HttpOnly attribute is inaccessible to the JavaScript*. so you can't store or access a http only cookie from JS. – Liam Mar 25 '22 at 09:10
  • I know, I linked that in my question. – JohnA Mar 25 '22 at 09:10
  • So what your asking for makes no sense? You want to access and store something you don't have access to. If you know all of this, surely you can understand your asking for the impossible – Liam Mar 25 '22 at 09:11
  • Why are you using a cookie at all? You want to get a JWT and store it, yes? So just do that? – Liam Mar 25 '22 at 09:13
  • I think I just found what I was looking for, you send the token in the response, on the client you create a cookie and add the tag `httpOnly` and then it's automatically sent with every request I'm assuming? – JohnA Mar 25 '22 at 09:15
  • [No, that won't work](https://stackoverflow.com/a/14691716/542251). A JWT is returned to the server using the `Authourization: Bearer ...` http header, NOT A COOKIE. Seriously just forget about cookies. I think you need to read the [OAuth JWT spec](https://jwt.io/introduction) – Liam Mar 25 '22 at 09:18
  • Your assumption that `httpOnly` is somehow secure, is also flawed. The only secure communication channel is SSL > TLS 1.2. HttpOnly is an added layer of security **in the browser** but really it's just a http header. If I write a proxy/browser and chose to ignore that http header, then it does nothing. – Liam Mar 25 '22 at 09:20
  • Here's a through [explanation of how to do this from a web-api perspective](https://stackoverflow.com/a/40284152/542251) – Liam Mar 25 '22 at 09:24
  • 1
    @Liam and Neil, I'm sorry to say, but you tell the wrong things. Storing JWT in cookies is a widely applied practice. When returning JWT in the body, the client has to store it in Session Storage that can be explored by the user and accessed by JavaScript. It's easy to steal it from there. On the other hand, returning it in the HttpOnly cookie guarantees it won't be accessed nor by code, not by the user, and will be automatically sent to the server with each request. It's open to CSRF attack, though, so need to implement a CSRF token. – Artur Mar 26 '22 at 09:35
  • But @Neil is right, you are reinventing the wheel by writing the token manually into the cookie. You are better to read about [Authentication and Authorization in Asp.Net Core](https://learn.microsoft.com/en-us/aspnet/core/security/?view=aspnetcore-6.0). – Artur Mar 26 '22 at 09:48
  • Yes, it's fine to **store** in a cookie, if you want. OP wants to **return** the JWT in a cookie, that's not in the spec @Artur – Liam Mar 26 '22 at 19:44
  • *It's easy to steal it from there. On the other hand, returning it in the HttpOnly cookie guarantee* erm, since when are cookies more secure than session storage? This is nonesense – Liam Mar 26 '22 at 19:45
  • 1
    @Liam server can set the cookie on the browser by **returning** `set-cookie` header. If you write jwt in this header and add the `httpOnly` attribute, the jwt will be essentially **stored** in a cookie that is accessible by the server only. This is how you do that with [Okta](https://github.com/okta/samples-aspnetcore/blob/master/samples-aspnetcore-3x/okta-hosted-login/okta-aspnetcore-mvc-example/Startup.cs#L25-L35) for example. If you dig into okta sdk you will find that it instructs asp.net core to store tokens in the cookie (`id_token`, `access_token`, and `refresh_token`) – Artur Mar 27 '22 at 06:18
  • 1
    _since when are cookies more secure than session storage_. Session/Local storage is vulnerable to XSS attacks that are **very hard** to make sure you are protected against. Cookies are vulnerable to CSRF attacks, but it's easy to protect against, and not less important, it's easy to verify you are protected. You can read a little bit more here: https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage – Artur Mar 27 '22 at 06:32

1 Answers1

1

You can create a WEB API method Login to return token:

[Route("api/auth")]
[ApiController]
public class AuthController : ControllerBase
{
    // GET api/values
    [HttpPost, Route("login")]
    public IActionResult Login([FromBody]LoginModel user)
    {
        // ... other code is omitted for the brevity
        return Ok(new { Token = tokenString });
    }
}

and then you can just store token at local storage of your browser:

login(form: NgForm) {
    // ... other code is omitted for the brevity
    localStorage.setItem("jwt", token);
    // ... other code is omitted for the brevity
    
}
StepUp
  • 36,391
  • 15
  • 88
  • 148