Authentication with JWT in React and Node-Express using Github OAuth - node.js

I have been trying to implement Google and Github OAuth authentication in one of my projects which uses React on the client side and NodeJS-Express on the backend. After going through dozens of articles and several Youtube videos later, here's what I have discovered about OAuth.
There are two main flows in OAuth. Authorization Code Grant Flow and Implicit Flow. To be on the same page, I would elaborate what I understood about both of these flows in short:
Authorization Code Grant Flow : User(resource owner) clicks on Login with Google/Github and is then redirected to the consent screen. Once they give consent, user(resource owner) is redirected back to the callback_url with a authorization_code in the URL query parameter. The authorization code is then send to the backend server. The server then makes a request to the authorization server of the OAuth provider along with the client_id,client_secret and the authorization code and then receives a access_token as well as a refresh_token required to access the resource server.
Implicit Flow : It is sort of a hacky way that was proposed back in the day for Single Page Applications as CORS wasn't properly implemented in all the browsers. Here the Single Page Application is given the access_token once the resource owner has given consent to the OAuth provider and it is now the duty of the SPA to store the access_token in a protected manner. However, since browsers aren't really trustworthy, and CORS is a real thing now, the Implicit flow is not really recommended anymore.If someone wants to implement the Implicit FLow, PKCE (Proof Key for Code Exchange) is sort of the standard now.
What I tried to implement
I decided to go ahead with Authorization Code Grant Flow as it seemed the most secure to me and also I have a backend server to handle the authorization code exchange.
Here is what most people suggest in order to implement Authorization Code Grant Flow with React.
Render a link like this.
<a href='http://localhost:8000/auth'>Login With Github</a>
Now handle this endpoint in the backend
app.get('/auth',(req,res)=>{
res.redirect(githubOAuthConsentScreenURL)
})
Now handle the redirect_uri in the backend and make a post request to the authorization server to get a access_token
app.get('/auth/callback',(req,res)=>{
//Extract the authorization code from query params and make a POST request to get back the access_token
})
Passport JS also implements this approach which is handling the callback url on the server.
Here is what I thought of doing:
Handle the callback URL on client side i.e. with React. Extract the authorization code from the parameters and then make a XHR call to the server with it. The server will now exchange the authorization code for an access_token and make a request to get the user's profile data. If everything succeeds, the Express backend will return a short lived access_token and a long lived refresh_token which will then be used by the React application to access the REST API.
My question here is : Which method is correct and the standard way to do authentication, handling the callback_url on the server or on the client side?. The method I propose over here seems more logical to me. Does my method have any security flaws compared to the other one? Am I missing something?
Other things that I have confusions about :
How is OAuth vulnerable to CSRF? From what I read, the state parameter can be used to protect the OAuth flow against CSRF. However, if I am redirecting the user from my backend server and then handling the callback_url in the server as well, how do I remember the state variable apart from storing it in some sort of session/db. Is it not more logical to redirect the user from the browser? Then the state parameter can be stored in the localStorage and can be matched later during the callback.
Also, I am implementing a short lived access_token and a long lived refresh_token for authentication and storing both the tokens as httpOnly cookie. If my access_token is stored as a httpOnly cookie, then how do I know if I am logged in or not and persist the state in React. One solution (proposed by Ben Awad in this video) was to query for the user during initial load and if the query succeeds, store the state (maybe in Redux) and then conditionally render the routes.
Is this the correct way of doing this? If not what is the standard manner that is followed by React applications which are actually in production? Again, am I missing something here?
Please Note : I am pretty new to authentication, would appreciate all the help and detailed explanations.

Related

Node.js Cognito sends # before token in URL

I have a cognito setup (with only Implicit Grant enabled) and have http://localhost:8000/login as callback login URL specified.
I have a backend side(Node.js) which expects GET request on /login endpoint and I plan to parse/verify token there.
When login is done it redirects me to http://localhost:8000/login#id_token=...
The problem is that as I know the part of the URL starting with the # symbol is never sent to the server. So how can I receive the token from Node.js side?
You shouldn’t be using implicit flow. It’s an outdated and insecure feature of OAuth. The token is passed in the hash specifically because it is meant for client side applications. Since you have a nodejs backend as your client you should be going through a authorization code (with PKCE) grant.
What is the response type used? Is it response_type=id_token? In that case, in order to have it sent to a backend for a simple OIDC Login flow you have two options
add response_mode=form_post, which will result in the IdP triggering a self submitting POST form to your redirect_uri
add a "re-post" handler to your GET redirect_uri that will read the fragment portion and POST it to your backend.
Each of the two has its benefits as well as drawbacks.

Is client secret ever a secure method to refresh access token for an SPA in authorization code flow?

In the article at MSDN, it says that the following request needs to be performed to get a new access token using a refresh token for a web app.
POST /.../v2.0/token
Content-Type: application/x-www-form-urlencoded
?client_id=...
&scope=wide_and_proud
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFP...
&grant_type=refresh_token
&client_secret=hakuna_matata
It means that we'd need to distribute the client's secret to the frontend application, which, to me, opens a huge security gap. I want to argue that we should never-ever feed our SPA with the information on client's secret.
I've been googling it for a few days and all the resources somehow point to a case where we do provide client's secret. However, I can't shake of the sensation that it's not what's supposed to be applied in the case of an SPA authenticating using authorization code flow. However, I see no documentation specifically discussing the combo: "spa refresh token authorization code flow".
You are right that the SPA should not use a client secret. It should also avoid use of refresh tokens. The problem is that the SPA has nowhere secure to store this information.
PKCE
An SPA is a public client and can use a one time use runtime secret rather than the client secret field. See this Proof Key for Code Exchange summary for details. This would solve the initial problem.
The Token Handler Pattern
If you want to go further, there is a wider issue is that an SPA needs a server component (API) that will look after its secrets and tokens. The SPA can then make a request such as this and the API can deal with sensitive data:
POST /login/end { url: location.href }
If this is done in an API driven manner it also fits very nicely with the goals of an SPA architecture, though it does add more moving parts. See these resources for further info:
React Code Example
Simplified SPA OAuth Code
Docs
Note also that this is a general design pattern. It will work with Microsoft and any other Authorization Server.
Token Handler First Steps
You could start with just these steps, which would also keep refresh tokens out of the SPA:
SPA calls API at /login/start to get the redirect URI
API sets a temp cookie with state + PKCE parameters
SPA redirects and receives the response
SPA calls API again, at /login/end
API then supplies the client secret to the Authorization Server
API stores refresh tokens in a secure cookie
API returns short lived access tokens to the SPA

How to let frontend know your server has successfully retrieved an access token

I've been studying the OAuth 2.0 authorization code flow and am trying to write a React application with an Express backend that displays what a user would see on their own Instagram profile. I'm trying to do so with minimal external libraries (i.e. not using passport-js) and without bringing a database into the mix.
This is my flow as of now:
Resource owner clicks an <a> tag on the React application (port 3000) which redirects them to the /auth/instagram endpoint of my Express server (port 8000)
res.redirect(AUTHORIZATON_URL) sends them to Instagram's authorization server
Resource owner consents and the authorization code is sent back to the predefined redirect-url /auth/instagram/callback with the authorization code set as a query parameter
I strip the authorization code off the url and make a POST request to https://api.instagram.com/oauth/access_token to grab the access token
Now that I have the access token, how do I reach out to the React frontend to let them know that everything worked and that the user was successfully authenticated?
From what I've read, this is where the idea of sessions and cookies come into play, but I haven't had luck finding documentation on how to achieve what I want without bringing in third party libraries.
In the end, I would like for my app to support multiple users viewing their profiles simultaneously. Since I imagine passing the access token to the frontend defeats the purpose of securely retrieving it on the backend, I'm guessing I will somehow need to pass a session id between the frontend and backend that is somehow linked to an access token.
Any ideas as to what my next steps should be are greatly appreciated, as well as any articles or documentation you see fit. Thanks!
Since you're doing the OAuth authentication on the server side, you have to pass some parameter to the redirect_uri, identifying the user session (see: Adding a query parameter to the Instagram auth redirect_uri doesn't work? );
When the redirect uri is called from the authority server, you will know which user was authorized. To notify the browser there are two options: 1) Notify the client using web sockets; 2) Pull the state from the client using a timer triggered function;

Security concerns about using Facebook implicit token for server side resource server OAuth2 authentication

I have poured over the OAuth2 docs and seen how the Facebook Javascript SDK uses Implicit Grant.
I am building a ReactJs application, which communicates with a PHP-Symfony API.
What I want to do is offer the "Login with Facebook" option on the frontend.
What I need on my PHP server is the Facebook user id and email and other data of the user so I can initially create a user record for them in my DB and then on returning visit, use the auth token to get that info again on the server and use it to match it to existing records and log the user in.
We have done this previously using the Authorization Code Grant method to redirect the frontend to our server, then to facebook and then back to us with the auth code. We then use that on the server with our Secret Key to get the Access Token and get the user info directly from Facebook to our server and then authenticate the user.
The redirection is a bit of a pain for a single page application.
Facebook's Javascript SDK handles a lot of that automatically, but uses Implicit Grant, returning an Access Token directly to the frontend.
What I want to know is, can I just send that Access Token to my server to do the same type of authentication that I did before? Or is that a massive security hole that I am opening up?
Comparing the two, the Auth Code from the Authorization Code Grant flow also goes via the frontend, but very quickly, not directly to JavaScript and is much shorter lived. So it feels much more secure.
If intercepted in time and with matching state, it could be used to authenticate someone on our server, but not access someone's Facebook data directly.
Reusing the frontend Access Token from the Implicit Grant flow feels like it is open to messing with, but I can't put my finger on the exact scenario that would make it more vulnerable to attack. The token would potentially give people access to not only authenticating on our server but also to accessing people's Facebook info.
So this is ultimately a question of best practice and security.
We think that we should be able to implement our own popout window that does the Authorization Code Grant style flow and retrieves our server cookie which can then be used by the page that spawned it, but it is going to be tricky and most of the work seems to be done for the Implicit Grant method, if it is safe to use as we intend to use it.
Best Practices AND According to the RFC 6749
However, this convenience should be weighed against the security
implications of using implicit grants, such as those described in
Sections 10.3 and 10.16, especially when the authorization code
grant type is available.

JWT for CSRF protection on unauthenicated endpoints?

I have a load of api endpoints that I want to protect from CSRF. I'd like to do this in a stateless way, so naturally JWT comes to mind.
The problem is, these endpoints do not require the user to be logged in.
So, my problem is, I can use JWT, but I have no way of verifying the token on the server side -- I have no logged in user I can match it to.
Is there any way JWT can be used in such cases?
CSRF is only a problem for requests where the browser implicitly authenticates the request (cookies or basic authentication). But if you do not have any authentication, any site could potentially call your API.
So if I understand you correctly, you are looking for a way to make sure that the request to your API is coming from your web application.
Take a look at the client credentials grant in OAuth 2.0. It basically issues a token to authenticate the application instead of the user.

Resources