OAuth2: How to pass session data to the front end? - node.js

In my OAuth flow, I am using the auth code grant type.
The front end (React.js) app directs to the OAuth server's login and scope grant pages
A redirect happens to an Express.js client app
The client app receives the auth code and does token exchange.
I am stuck at this point. i have saved the user and token data to a database. But I have no way of redirecting back to the front end (React.js) app while safely passing a user session:
Cookies can't be passed cross domain
Query strings are available but are captured in server logs and browser history.
Redirects are GET requests so I don't have access to a POST request body.
How do you safely pass session data to a front end after your OAuth process? I suspect my Auth flow is wrong at one or more points.

Sounds like you could simplify by using a client side flow in your ReactJS app:
Login uses Authorization Code Flow (PKCE)
SPA uses the OIDC client library
The SPA receives an access token and can make cross domain API calls with it
It is difficult to see what value your ExpressJS client app brings - feels like it is adding unnecessary complexity.
RESOURCES OF MINE
Here are some notes that might be useful. The SPA code is quite a bit simpler than older solutions that had to switch between front and back ends' to handle security processing:
SPA and API code sample
Blog post on OAuth / HTTP messages

Related

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

Is it possible to capture an OAuth form post redirect in browser?

I am trying to integrate third-party (TP) APIs (https://api.icicidirect.com/apiuser/ICICIDirectAPIDOC.htm) in my react/node.js app. The APIs use OAuth 2 for authentication and after authentication.
The sequence of actions is something like this - a user clicks on the TP image on my react app and gets redirected to the TP's login page. After authentication, TP does a redirect with a form post. Based on my current understanding, only the backend server can host a post method where the TP can post the form. From here, it is not clear how to trigger the react app to detect that the user has logged in and render their dashboard.
I have two questions:
Assuming I register a frontend URL as the redirect URL, is there a way I can capture this form post on the frontend?
If #1 is not possible, what's the most elegant way of letting the frontend know that the login was successful, after receiving the redirect on the backend?
I would check a couple of things since it seems your app needs to use OAuth tokens as well as to sign in:
Is PKCE used and / or a client secret? This will determine whether you can complete the login in the browser and swap the code for tokens.
Can you add a response_mode=query query parameter when you trigger the redirect, which will make the response a GET (if the provider supports it).
Q1
The usual solution is to return the authorization result on this type of URL, which is SPA friendly. The browser cannot receive POST requests.
GET https://www.example.com?code=xxx&state=yyy
The SPA can then perform one of these actions and get tokens:
Exchange the code for tokens if PKCE (a runtime secret) is used
Otherwise proxy the request via an API, which attaches the client secret
Q2
If the provider only supports POST responses you can can write a small amount of code in the web host (as a workaround) when it serves the index.html page. This could check whether it is a login response, and if so trigger a redirect with the above query parameters.
WEB HOST RESPONSE PROCESSING
Do the login response processing and code exchange in the web host, then redirect the SPA to a location such as https://www.example.com/loggedin, which will notify the SPA so that it can update its UI state.
ALTERNATIVE OPTION: OUT OF INTEREST
The above could require quite a bit of code in the web host, which could potentially impact your web deployment options, eg if you wanted to serve web static content from a Content Delivery Network.
So another option the web host could use is to just perform a redirect to https://www.example.com?code=xxx&state=yyy. The SPA could then call an API to complete the OAuth processing. This requires more security due diligence (eg ensure that PKCE is used), but is how a Back End for Front End SPA solution would work.

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

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.

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;

Is there a way to use CSRF protection and JWT in a sails app together but not at the same time?

I'm working on an application using sails. web and mobile.
I want to use CSRF protection that sails provides when the app is visiting on the web. And if a request is send by the mobile app. send with the payload a jwt.
On the sails' documentation I found a property csrf.routesDisabled that disabled the CSRF for some routes. But that is not what I want. I'm trying to find a way to for example, check if the parameter jwt is send in the post request. And if the parameter was send then check and validate it. else, check for _csrf value of the form. Is this possible?
or the csrf protecction works before any information is send to the server?
my better choose is use jwt in the web app too?
any other good idea for solving this problem is welcome
thanks
Sounds like you've built the web app with SailsJS and you're trying to reuse the controller actions as REST endpoints for external applications.
Really what you should do, is decouple the data access from the front-end. Have an isolated REST API - using token authentication - which is used by both a web front-end (and any other applications).
For example, I'm currently working with a SailsJS REST API, used by an EmberJS front-end and an iOS app. Both front ends login using user credentials, in order to receive an authentication token. This token is then used for any future requests. A policy locks down all but the login authentication endpoint, to validate the token

Resources