Protect REST API after social login with Node.js and Express.js
TL;DR
Do want to protect your REST API? Use Json Web Tokens. Servers issue a token, and the clients must use it to access protected resources. It’s easy to issue a token after a username/password login, but…
How can you issue a user token when users log in from social networks like Facebook or Google? How can you trust their identity and let them use your protected API?
Here’s how I did it.
Scenario
You have your REST API written with Node.js and express.js.
You have your beautiful, modern and awesome Javascript/mobile App.
You have your social login implemented with Google, Facebook, Twitter and the best social networks in the world.
You are now officially in trouble! As you need to protect your API and find out a way to trust your users after social login.
You study and do a lot of research, and find out the best solution is to use Json Web Tokens aka JWT.
You are still in trouble unless you can answer these questions:
How can you issue a token for a user that logs in from a social network?
and
How can your server trust the identity of users that comes from social networks? How can the server identify them?
What I mean is, when users log in with username/password from your back-end, it’s easy to issue a token, because it’s your own server and you know who the users are, you know their identity.
When users log in from a social network you want them to call your protected APIs, so you must be sure they are who they say they are, as well as being sure nobody altered or stole their information.
To find the solution to this problem, I tried to use Oauth, with client_id, client_secret and client_credential scopes, but there is another problem: the client_secret cannot be stored on Javascript or mobile Apps.
Next I thought about writing a full Oauth application, as a wrapper to social network logins, but it would have took too much time and there are some frameworks (auth0.com is the best example) that solve this problems, unfortunately you have to spend money to use them. So finally I jumped to a third easier solution to accomplish this goal.
This flow solves your troubles!
Before jumping into code, we need to stop a moment and describe the flow we are going to implement. If you’re too lazy to read the flow, just skip this section!
- when a User does the social login, the Social network will give back a “social access token”
- the App sends the “social access token” to the Server
- the Server validate the “social access token” against the Social network API
- the Social network API will return the user profile
- the Server issues a “user token”, and gives it to the App, along with the “user profile“ information
- the App can display the “user profile” information, that comes from you Server
- the App asks the Server for a protected resource, using the “user token”
- the Server validates the “user token”, and returns the protected resource
- the App can now display the resource, everyone is happy and secure.
1. User does the social login
After a social login, your App will receive the user profile information it asked for (unless the user didn’t permit it).
The response from the login contains a token, called an access_token, which looks something like this.
with a google login:
{
“token_type”: “Bearer”,
“access_token”: “[a complex string I won’t share]”,
“scope”: “https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/plus.me openid email profile”,
“login_hint”: “[a complex string I don’t want to share]”,
“expires_in”: 3600,
“id_token”: “[a even more complex token I won’t share]”,
“session_state”: {
“extraQueryParams”: {
“authuser”: “0”
}
}
}
or this with a facebook login:
{
"authResponse": {
"accessToken": "[a complex string I won’t share]",
"userID": "[your user id]",
"expiresIn": 4003,
"signedRequest": "[a even more complex string I won’t share]"
},
"status": "connected"
}
2. App sends the “social access token” to the Server
Now you have the “social access token”, the App has to send it to the server.
By the way this “social access token” is the one you can use on social network API.
In google login the “social access token” is the id_token field, we are going to send this string to our server.
3. Server validates the “social access token” against the Social API
The server receives the “social access token” on its dedicated API, and validates it against the social network API. We are going to ask google:
Did you issue this token? Is it valid? Was it issued for me? Give me the “user profile” associated with it.
Let’s go ahead writing some code to validate the google token, with a function that uses the google-auth-library. It looks like this:
Next we create an express.js endpoint to receive the “social access token” and return a “user token” and user profile.
The response of this API will be a JSON object containing a token
and a user
created by the server.
token
is the “user token” created by the server, user
is the user profile the server retrieved from the google API. It looks like this:
{
"token": "fyJtbGyiOiJIUzI1NiIsInR5cCI6IkpXVCJ9.k_yJzdWIieqIxMTA3MTc1NDExMzA0MzQ3MTY1OTYiLCJleHAiOjE1MTc0OTM0MzQsImlhdCI6MTUxNzQ4OTgzNH0.lttUzw4zm7BmKzQxuyhoN94Dc0T3arqu8d12YzMeSE8",
"user": {
"name": "your name",
"pic": "https://lh5.googleusercontent.com/-QB9JJeft6oE/AAAAAAAAAAI/AAAAAAAAAAA/ACSILjV0dkHDv9VERWljVuekRs8GNZW-ew/s96-c/photo.jpg",
"id": "[your google id]",
"email_verified": true,
"email": "your.email@gmail.com"
}
}
The token
field is a Json Web Token, to create it we use a function (in the previous code snippet: jwt.createToken()
) described in section 7.
4. Social API returns the user profile
As we have already seen, the google-auth-library returns the user profile after validating the token.
Facebook has a slightly different behavior; you have to call their API three times in order to validate the token and get the user profile.
- The first time is to get an “app access token” authenticate your client (Facebook App)
- The second time is to validate the “social access token” using the “app access token”
- The last time is to get the user profile
Here’s a code example:
5 . The Server issues a “user token”, and gives it to the App, along with the “user profile“ information
As we already said, this is the response of the authentication API we just created:
{
"token": "fyJtbGyiOiJIUzI1NiIsInR5cCI6IkpXVCJ9.k_yJzdWIieqIxMTA3MTc1NDExMzA0MzQ3MTY1OTYiLCJleHAiOjE1MTc0OTM0MzQsImlhdCI6MTUxNzQ4OTgzNH0.lttUzw4zm7BmKzQxuyhoN94Dc0T3arqu8d12YzMeSE8",
"user": {
"name": "your name",
"pic": "https://lh5.googleusercontent.com/-QB9JJeft6oE/AAAAAAAAAAI/AAAAAAAAAAA/ACSILjV0dkHDv9VERWljVuekRs8GNZW-ew/s96-c/photo.jpg",
"id": "[your google id]",
"email_verified": true,
"email": "your.email@gmail.com"
}
}
The token
section is created at line 17
of the express-authentication-endpoint.js
code snippet, by the function jwt.createToken()
.
jwt
is the following node module that uses jsonwebtoken. It creates and validates Json Web Tokens with generateToken
and verify
methods. (Notice that I use a secret password to sign my JWT in this example, but it’s better to use a private/public key pair):
6. The App can display the “user profile” information, that comes from your Server
The App now has the user profile and the user token so it can display user information, such as: name, email and profile picture.
The user information came from your own server, not from the social network login.
This means the Server is aware of the user profile, so it can do any operation on it, such as save or update it in a database, send emails, start an activation flow… whatever your marketing boss asks.
7. The App asks the Server for a protected resource, using the “user token”
The App now has the user profile and the user token, so it’s possible for it to get a protected resource from the server.
8. The Server validates the “user token”, and returns the protected resource
In order to grant access to a resource the server must:
- check the token existence
- validate the token signature
- check the token expiration
- check user grants for a specific resource (optional but useful)
Below you can see a code example of how the server protects the resources and validates tokens.
First of all let’s create an express.js middleware.
Middleware functions are functions that have access to the request object (
req
), the response object (res
), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable namednext
.
Our middleware function checks any request made to the server. If the requested resource is protected the function checks:
- the presence of the token
- signature and temporal validity of the token
Next it will grant access to the resource, or returns a 401
HTTP status code, if validation fails.
9. The App can now display the resource, everyone is happy.
Our REST API now has the requested resource from the server, everyone is happy and secure.
A demo application showing this flow is online at: https://authenticated-social-login.herokuapp.com/
The code of the demo application is online at: https://github.com/Spyna/protect-rest-api-after-social-login
Congratulations!! 😎 You made it to the end. And if you liked 👌this article, hit that clap button below 👏. It means a lot to me and it helps other people see the story.