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.
Related
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.
My goal is to tell if a user is authenticated, and get their name and email. So far, I can only do the first.
My server app is a NodeJS/Express router. The OIDC server is provided by my company, I believe is compliant, but not under my control. I can do an "Authorization code flow": response_type="code". My Node app eventually responds to the callback route and I can get the "code" and "grant_id" query string values. Other than knowing they exist and presuming that means the user is authorized, I have no idea what I can do with those values. It appears that the only scope that works is "openid".
If I can get the access_code, I can call the UserInfo service and get these values.
My other choice is to do an implicit call. Unfortuantely, the OIDC service it provides the "access_code" and other values after a hash mark on the callback. I believe the flow to be like this:
User makes call to Node app. Detects a lack of authentication, issues redirect to SSO service implicit authorization
User's browser follows redirect to SSO service implicit authorization. User fills it out and is successfully authenticated.
W3 returns a redirect to the provided callback URL.
User needs to cooperate with the app, somehow parse the query string parameters to get the access token and pass this back to the Node application.
The browser calls the provided Node callback application, but without the necessary access token.
I think I could make a proxy server to force OIDC to give my node server the request, just so I can get the access_token. It seems like a very convoluted way to do this, so I have to think there's some simpler way.
Again, all I want is to know the user is authorized to use the app, and what their name and email is. It should not be this hard.
You can use the ID-token to get the details about the user.
It is also important that your identity provider is all about authentication. Final authorization checks should be done in the client, by examining the scopes/claims in the token.
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
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;
Hi I am working with Open Id Connect protocol for authentication scheme. I just want to know what is the exact difference between basic client and implicit client scenario's in Open Id Connect.
The difference is that basic client uses OAuth2 Authorization code flow, while Implicit client uses OAuth2 implicit flow.
You can find the differences between these two flow in OAuth2 RFC (https://www.rfc-editor.org/rfc/rfc6749), but basically:
Authorization code flow is composed by two requests and responses. The first request (response_type=code) asks for a authorization code. The provider responses (if authorizated) with a authorization code. Then a second request is made (response_type=token) sending this code and asking for the access token. This flow is used from server-side.
Implicit flow is composed by one request and response. The request (reponse_type=token) asks directly for the access token, and the response injects the access token into the redirection URL. This flow is used form client-side (script).
Hope this help!