.net core, google sign in, and jwt cookies - reactjs

I have a web app that uses Google sign in only as a way of signing in. I generate a jwt and it gets correctly to my app. But then I save the token on localStorage, which I am trying to move away from, so I want to send the token via a cookie.
Edit (as I was wrong with the root cause)
My back end properly returns the cookie as can be seen on the first image, when hitting the API's endpoint 'googleLogin'. However, I don't know how to then send that cookie on subsequent requests to my API so it is used to validate the logged user
My back end is .net Core, I generate the jwt there and also send the cookie, then I have set app.UseCookieAuthentication to check for that specific cookie, but it is not sent on subsequent requests so I am not able to authorize the user.
I don't know if it matters but the front end is a SPA using react

Related

Azure Active Directory SSO (SAML protocol) when frontend is decoupled from backend. How will frontend be notified that validation is completed?

So I would like to add SSO using Azure AD.
My stack consists of a React app as frontend and a NestJS API as backend(decoupled). The scenario looks like the following
User clicks login button
I create a new window (popup) which leads the user to the Azure AD login page (step 3 in the diagram)
After the user logs in to Azure AD successfully Azure AD will POST the SAML response to a redirect URL I have provided them (let's say http://myapp.com/saml) (step 5 in the diagram) (This redirect URL, to my understanding, has to be an endpoint in the NestJS api since React frontend can't handle post request.)
NestJS will handle the post request get the info it needs, validates etc etc and then NestJS has to return a Token to the frontend somehow in order for the frontend to store that token in a cookie and be able use it in subsequent requests to the NestJS api so that NestJS will be able to know that this user is logged in. (Step 6)
My issue with this approach is that I don't know how will the client get the token when the the validation is completed from NestJS. If this was a coupled application this would not be an issue since the backend would handle the post request set a cookie and redirect the user. But in this case NestJS can't redirect the user since react handles the routing.
What is the correct approach to handle this?
I thought maybe this could work by using websockets...so that when NestJS handles the post request it can send a message to the user which message will contain the token and then the frontend can add it to a cookie and redirect the user to a protected page.
(1) Does the frontend really need to store a token to send to the backend on subsequent requests? What if the backend set a cookie at Step 7 in your diagram? Then the cookie would be sent to the backend on subsequent requests. The cookie would be scoped to the backend’s domain name and path. Keeping tokens away from the frontend has the advantage that you can keep them safe from being accessed via a Cross-Site Scripting attack on your frontend, if your backend’s cookie has the HttpOnly attribute set.
(2) If you still need to communicate info from the backend to the frontend in Step 7, then send an HTTP 301 response with the Location header set to your frontend’s URL with the info you want to communicate included in the query or hash portion of the URL. For example, after validating in Step 6, in Step 7 your backend’s HTTP 301 response could have the Location header set to https://my-frontend-domain.com?user=bob or set to https://my-frontend-domain.com#user=bob. With ?user=bob, user=bob would get sent over the network again when the browser requests https://my-frontend-domain.com/?user=bob, whereas with #user=bob it would not. Then the frontend’s JavaScript can read user=bob from the URL.

Using HTTPOnly Cookie Returned from API in React app

I am working on my first React application which consumes a REST API. Certain information within the API isn't accessible unless authorized by logging in, and the API returns an HTTPOnly cookie as a response upon a successful POST request to the login endpoint; I'm using axios, to accomplish this. It's possible to view the cookie within the network tab of the browser and it also successfully logged to the console, but I'm unsure of how I can actually store the information returned from the API within my react app. The cookie vanishes from the browser when I leave the page after logging in. Is there a way I can implement this cookie into the React App's memory/state so it can be sent and used upon future requests in the application? I've scoured for a few days and seen various methods to access a returned JWT, but most of them include using LocalStorage which isn't secure or are from deprecated tutorials many years ago. After logging in, the JWT returned from the API will need to be sent back upon future requests, which will also be made using axios.
All help is much appreciated.
You can configure the expiry of the HttpOnly cookie. It sounds like your server is currently storing the JWTs in HttpOnly session cookies. If you are using Chrome, you can confirm this by looking at the "Expires / Max-Age" column in the Application tab. If it is a session cookie, the field will be unspecified, and the cookie disappears once you end your browsing session. If you set the expiry of the HttpOnly cookie to say a year, then the cookie persists across browsing sessions.

I'm authenticating the frontend app, using cookies session, but how to authenticate a user using ReactJS + Springbot?

I have to authenticate users in a scenario that involve a frontend(react) and a backend (springboot).
After working on it I realized that Springboot response include a set-cookie header, but actually the cookie session returned by Springboot is never set in the user-browser, so I asssume the cookie is set in the frontend app, which means that basically the frontend-app is the one authenticated, but no the user, make sense because the frontend is in the end sending the user/password.
How the people approach this scenario usually?, should I have a cookie session as well in the user-browser (so far is my thought)?, should the frontend store something different in the browser to keep track of logged in users?
Ideally I would go with Bearer token based authentication as I could use the backend for mobile applications as well.
Basically you would require to store the JWT in the local storage or key chain.
You could authenticate using JWT token. Get user details from token to use it in front end.
You need to set the session token in the localStorage. After storing it in localStorage you need to check session token on every protected route. If it's same as you have in backend it's good to go. However, if it has expired you need to run your logout api.

How to safely persist logged-in state of user in React website?

I have a website that runs on example.com. The website makes AJAX calls to my backend API which sits at api.example.com.
I employ a double-submit verification strategy for my authentication.
The backend has protected endpoints which check the JWT token with each request. The JWT token is stored in a httpOnly cookie. It also contains the CSRF token.
Alongside the JWT cookie I also send a CSRF cookie which is not httpOnly.
Each request that the client makes must contain the value of this cookie in a special header.
So far so good, but, I want to make sure that the client does everything in its power to prevent users from making pointless unauthenticated requests. So in my React app I have declared a few private routes which check if the user is logged in and if they are not, the user is redirected to the login page. The logged-in state is kept in the Redux store.
The issue is that on a full refresh the Redux store is reset. So as I see it, my options are:
Option 1) Check the existence of a CSRF cookie
The way I check if a user is authenticated is by checking if they have the CSRF cookie; I also do this during the store initialisation. If they do have the cookie, they are allowed to navigate to the protected page. All subsequent requests on this page are still verified on the backend for a JWT and CSRF token.
Option 2) Check against the backend each time
I can create a simple endpoint on the backend that is used to check if the user is logged in. So instead of just checking the cookie, the client can submit a request to this endpoint and verify that the token in the cookie is still valid.
Option 3) Persist the Redux store
I could implement a persisted store (there are some libraries that do this out there) so that the initial problem is automatically resolved, i.e.: the logged-in state is preserved upon full refresh. However, this yields a bunch of issues with caching and token expiration and it may not be worth the effort.

Google OAuth2 flow and id_token refresh

I am having troubles in implementing OAuth in the right way.
I use a client/API architecture (Angular for front and Node.js for back) and I would like user to sign in using Google OAuth authentication only.
Here is what I think is the right way for the moment (tell me if I misunderstood something) :
Angular open a Google popup asking user's consent.
Once the user agree, Google Authorization server sends back to angular a verification code.
This verification code is forwarded to an API endpoint.
Then, the API asks Google Authorization server to exchange this code for an access_token, an id_token and a refresh_token.
Google sends those 3 tokens.
The API uses access_token to retrieve user from Google API
The API persists the user
Here is the little dillema, in my opinion, the access_token and refresh_token should be stored into the database and the id_token should be sent back to Angular client.
This way, it would allow the API to ask for resource in Google API and if the token expires it can still ask for a new token thanks to the refresh_token.
Client-side, the id_token is embedded in all requests thus allowing the API to identify the client and verify his authentication with Google certs from https://www.googleapis.com/oauth2/v3/certs.
Supposing this is right way to use tokens, how could I deal with id_token expiration since client does not have any refresh token ?
Thanks !
I do it slightly different (I have the same basic architecture though).
Angular decides the user needs to log in and displays a login popup.
The url in the login popup is not serviced by angular, but is instead directly run off of the backend server: /auth/google . (I use hapijs and bell, personally).
/auth/google is serviced by a bell plugin and initiates the OAUTH dance.
the end of the OAUTH dance results in my node server generating a local token (I just generate random bytes and store them in redis mapped to user ids)
because the initial login popup was created by window.open, the success page (generated on the api side rather than in angular) can use window.opener.postMessage to communicate the token back to the angular runtime.
This way, all my sensitive google credentials (the user's oauth token, refresh token if needed, and my application's api ID and secret) are only on the server, except for during the OAUTH dance relay when they're in a URL string during the client redirects. This is reasonably secure.
Then for all the actual user interactions with the api, I use the token I generated in step four to authenticate. This could be a JWT if you wanted, but I don't do it that way; I just use redis to map from 'longrandostring' -> userId. That lets me (for example) force everyone to re-login if I wipe the redis database that has all the tokens stored, or I can write a lua script to delete all the entries that map to a certain userid.
If you need a refresh token, you can set access_type=offline in the initial request to oauth2/auth, and you'll get a refresh token as part of the response, unless you've previously gotten a refresh token. You can then persist it on the server side and get new access tokens as needed. If you set approval_prompt=force as well, you'll force a new consent screen and be guaranteed a refresh token (but after some small number of refresh tokens granted to a user, older ones expire on the same application so it's best to only request them if really needed).

Resources