What's the Nodejs backend routine for "Register / Signin with Google"? - node.js

I'm using a Express Nodejs backend + React frontend set up and tried to implement a "Register / Signin with Google" function, but I do not understand what to store in the database after the user is authenticated. In the ordinary register with email approach, I send the email + password to the backend when I register and check if both the email & password match when the user login.
However, I don't know what to store in the db if one is registered with Google. I have already implement part of the google auth with google by following this tutorial in the frontend side, here's my code:
import * as queryString from 'query-string';
const stringifiedParams = queryString.stringify({
client_id: 'MY_CLIENT_ID'
redirect_uri: 'http://localhost:8000/protected_home',
scope: [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile'
].join(' '), // space seperated string
response_type: 'code',
access_type: 'offline',
prompt: 'consent'
});
const googleLoginUrl = `https://accounts.google.com/o/oauth2/v2/auth?${stringifiedParams}`;
return (
<a href={googleLoginUrl}>Login with Google</a>
)
But then what's next? I've successfully pass through the auth process and redirected back to the protected_home page. But this only means that this user is a google user, what kind of information should I store their information in backend so that it indicated that the user has registered an account in my backend with this google account?
Also, is is possible to move the logic above to backend? Since google will redirect back to my redirect_urilike this http://localhost:8000/protected_home?code=CODE_IS_HERE, I need to browser to extract the information in CODE_IS_HERE. So it seems impossible to move the login logic to backend, any solution?

What you need to save is user's unique id, his email or phone, and some other user data for your project.
This is just to know if the user already registered or to know the current user in backend.
From backend, you can just set a middleware to verify the token assigned from google.
Then you will get the user's id and you can find a user from your database, if exists, the user is authenticated.
Signin with google.
Get redirected with CODE
Send CODE to backend
Backend will get user id and email with the CODE using google api.
Save user and generate token.
Send the token back to your frontend.
Then the login or signup process is finished.

Basically you should not write it from scratch as there are libraries that deal with Oauth2: PassportJS, openid-client, Grant, OAuth2-client-js. Those should handle all the below steps except storing actual details in your own database.
In general there are some basic steps when implementing third party oauth2 authentication:
Understand well how Oauth2 works https://www.rfc-editor.org/rfc/rfc6749
Redirect to your server from client (react)
Request authorization by redirecting to Google
Get authorization code which will be added to a url in a redirect from google back to your server
Exchange code for an access token (POST)
Use access token to get user profile details from Google (POST)
Save user details if they do not exist yet in your database - in theory you could skip this part
Create session/token and send it back to client (React) - it is another redirect.
You could also not store any user details on your server and just pass back the token obtained from Google to your React app (client), but then you'd need to check if it is valid on every request to your server. Which is why it is simpler to create your own session token and send it to the client.
There are more details as this is quite a topic to start with but RFC6749 should fill in the gaps.

Related

Login functionality from external API in React with Node.js

I’m having trouble figuring out how to get Node.js backend tokens into React.js frontend local storage. To login a user will use their credentials though an external websites API using the Oauth2 flow, this will be the only way to login into the application.
Currently, the user clicks a button which opens a new window in the authorization URL where the user will grant privilege. Once granted, the user is redirected to the backend endpoint which goes through passport.js and gets the required access and refresh tokens sent from the external API. This is then stored in a session on the backend database. What I want, instead, is to not store a session on a database but instead implement JWT and store the user’s data in local storage. With the current flow, its just not possible to do this and I haven’t found the right documentation to work it out.
There are many websites that implement it the exact way I want but tracking down the way they do it has appeared to be a challenge in on itself.
So instead of using passport.js, which was causing a plethora of issues, I decided to implement the Oauth2 flow myself. Instead of doing ALL the work in the backend, I broke the flow into different parts.
Originally, I sent the user to the backend where they would recieve an authorization token there. This turned out to be troublesome, instead, request an authorization code on the front end. For example, send the user to the Auth path and redirect the user back the the front end once privileges have been granted. Wait at the frontend callback for a code, once obtained, send a post request to the backend with that code and any other data in the body.
When obtained at the backend, trade that code for the access token and respond to the post requst with the neccassary token and any other data that needs to be sent back e.g. profile name, picture, date of birth. You can the implementn the JWT flow and no database is required to store any session or tokens, all can be stored client side securely.

Salesforce Authentication using Node JS API With Access Token

I have a requirement to retrieve the Auth Code from SalesForce API. I have created the scenario in Postman as below.
Configuring the new Token using the below parameters
Once I press "Get New Access Token", Postman opens a popup and asks to type the Username and Password in the login prompt. It shows the login page to SalesForce.
Once login success, Postman asks to use the token and it will be added here(See below image)
Then I hit the endpoint with the JSON body as a POST request.
I need to recreate this scenario in NodeJS in order to work the whole process as a single process bypassing all the login prompts.
I am using the below method to initiate this task in order to get the Token. However, the resources I found didn't match my requirement.
As the first step, I used salesforce-oauth2 npm package as below.
oauth2 = require('salesforce-oauth2');
var callbackUrl = "https://test.salesforce.com/services/oauth2/success",
consumerKey = "3MVG9sLbBx**********************2Qi.v***Vlhg3",
consumerSecret = "3MV**bBx**********************2Qi.v***Vlhg3";
var uri = oauth2.getAuthorizationUrl({
redirect_uri: callbackUrl,
client_id: consumerKey,
scope: 'api', // 'id api web refresh_token'
// You can change loginUrl to connect to sandbox or prerelease env.
//base_url: 'https://test.my.salesforce.com'
});
return response.redirect(uri);
When I debug I above code, it returns a URL pointing to the login page. I didn't want to pass this step since my requirement is to get the Auth-Code without opening any intermediate authentication popups.
How can I proceed with this? Any idea to program until the 3rd step to get the Auth Token from the SalesForce API?
Thanks in advance.
You tagged this salesforce-communities. It matters, is it really for community ("experience cloud") users or internals? Salesforce has lots of OAuth2 flows to chose from: https://help.salesforce.com/articleView?id=sf.remoteaccess_oauth_flows.htm&type=5
If you know the password and it's internal user (maybe real human, maybe you have some dedicated "Integration User") - you can work with Username-Password flow. There's no login page and no OAuth consent step. But
This flow doesn’t support scopes or refresh tokens. Experience Cloud
sites don’t support the OAuth 2.0 username-password flow.
You might be able to use JWT Flow. You need username (no password) and your Node app would be sending a message signed with certificate that you uploaded earlier to SF "connected app". You could even mark the users as preauthorised so there's no consent screen.
Other than that I think all OAuth2 flows available for community need a human to actually type the password in. You can pass login hint in the url to save them the username but pass they need to provide on SF login page before coming back to your app.
Dig a bit in help, happy to be proven wrong.

How to provide authentication for a single user (Node, Express)?

I am currently working on an app with a React frontend and a Node/Express backend. There are certain functions of the app that I only want an admin user to be able to use, so I want to implement an admin login form that takes a username and password. This would be the only user that this app has.
How would I go about implementing this? I've looked at technologies like passport.js and express-basic-auth, but I'm wondering if there's a simpler way to implement this since there is only a single user. Any advice would be greatly appreciated!
If you wanna do it by yourself then whenever the user logs in the application, create an authtoken for the user and save it in the backend and send the authoken as the response for successful login. Further save the authtoken as a cookie on the frontend. Next will be while performing any action you need to send the authtoken to the server. If that authtoken is present on the server side you go on with the action or else send the response error saying that the authtoken is invalid of something like that. On loggin out of the application remember to expire the cookie and also remove the authtoken from the backend.
You can add this if you want where if the authoken provided is incorrect you can simple remove the authtoken from the backend and redirect the user to login page expiring the cookie from the frontend.

Is this password-less auth flow secure?

I'd like to implement a passwordless auth flow for my mobile app that only requires a user clicking a link in their email to log in. Similar to how Slack handles auth. I'll be using node and jwt for this implementation.
I think I've come up with a secure design, but I'm sure I'm missing something. I would love some critique from the community 🙏.
Here we go:
User opens the mobile app.
We check to see if user has a token in their local storage.
If they do, we add that token to their headers and send to the home page of the app.
Else, we prompt them to enter their email to get started
When they click "Submit", we POST that email address to the requestMagicLink endpoint on our server.
The server checks the database for a user with that email address
If we find a user with that email, we take the id from that user
If the user does not exist, we create a new user, and get that id
We use JWT to generate a token with the id, and our secret that expires after 1 hour
We send that token to the user via a link in an email.
Upon being clicked, that link sends a GET request to our server at the magicLogin endpoint with the token in a query param
We verify that the token is correct using JWT and our secret.
If it fails verification, we redirect the user to the screen where we prompt them with their email to get started.
If it's successful, we generate a new JWT token using their id, and our secret that doesn't have an expiration, then pass that back to the user in the params of a URL that redirects them to a success page in our app.
The app takes the token from the param and stores it in local storage until the user chooses to logout, and the user is redirected to the home page.
The requests to the api all now contain the token in the headers, and the user is good to go.

Facebook login flow with to nodejs

I am working on a REST API backend service for an app that uses both email and facebook login. Lets just pretend this app is a todo list app and a user can sign in and add notes which they could later view on may different devices.
The "Login with email" is pretty simple, the app would make a request to:
URL: /v1/login
Params: email, password
Then the serivce returns an access token if all this information is correct so we know the identity of the user creating, updating or deleting a note/list item.
Now for the facebook side. I've seen several differnet answers all over StackOverflow and Facebook's documentation. Some people say, just pass in the id and login the user with the matching id. This would mean calling the login function from the Facebook SDK and just keeping that id to send in a request.
URL: /v1/login/facebook
Params: id
That would work but seems highly unsecure. Anyone could get someone else's Facebook id and just send a request to the server. Facebook's documentation mentions also using the account email. We'll what if the user ever changes their email on Facebook, they could never login to this app again. Another hint of security would be passing in the token from Facebook every time. Since those tokens can change or expire, there really wouldn't be a way login past the expiration date.
The final method I know of would be passing in the app secret:
URL: /v1/login/facebook
Params: id, secret
This seems by far the most secure, if you are using https to connect to the server. The downside to this method is, what if the Facebook secret token is ever reset? I mean you could always make a call to the server to request and check if token was reset and if so pass back the new one. But then if anyone had access to the endpoint to check out of date tokens, it could give them the new secret token.
I know we are dealing with the internet here and there will always be security flaws and there isn't a perfect way to do this. But what is the best way?
Try to send facebook_token for that user.
URL: /v1/login/facebook
Params: facebook_token
and service side make a service call to facebook graph api to get information about that user using facebook_token.
get the facebook id from that response and use it to provide data to that user.

Resources