Securing Angular+Node.js Applications using App ID
Authenticates the user using App ID
Once Authenticated provides access to protected resources on the backend
App ID allows developers to easily add authentication, authorization and user profile services to apps and APIs running on IBM Cloud. With App ID SDKs and APIs, you can get a sign-in flow working in minutes, enable social log-in through Google and Facebook, and add email/password sign-in using App ID Cloud Directory. The App ID User Profiles feature can be used to store information about your users, such as their preferences. In short, App ID enables your application to be used only by authorized users and that authorized users have access to only what they should have access to. The experience is custom, personalized and most importantly, secure.
What about non Angular based Single Page Applications?
Although we are using Angular to implement our frontend, the architecture and concepts that we cover in this blog are agnostic of the language and can be supported by other frameworks such as React, Backbone.js, and Ember.js.
Note : You can download the code to our sample SPA application.
While integrating user authentication into an application, it is critical to consider the trust level and threat surface of the different components of the application. This affects how and where confidential data such as secrets and user data are stored.
In contrast, a backend application is hosted on a secure remote server and only visible to the external user via controlled APIs. Because of this restricted access, any secret data or logic that is hosted on the server-side can be considered trusted.
This trust model makes it clear that:
No sensitive data should be stored on the frontend i.e., in the SPA
Sensitive data should be stored only on the backend
Frontend should obtain sensitive data from the backend if and only if necessary and only after authentication and authorization.
Our Sample Cloud Land Application
The sample application that we will be building today is a fictional Cloud Land application. It allows a user to sign in to the application with a social identity provider, such as Google and Facebook, or use email to sign-in using App ID’s Cloud Directory. Once the user is signed in, it displays basic information about the user and also their Cloud Land reward points. Each user’s reward points can be accessed only by that user. To summarize, our application will use App ID to personalize the user experience and control the application’s access to the user data (i.e., reward points). Click on the link below to download all the sample code. Refer to the README.md file in the downloaded zip file for instructions to run the Cloud Land Application.
Architecture of a Single Page Application with App ID
At a high-level, our Cloud Land application consists of an Angular based SPA and a Node.js REST API running on separate servers. The SPA makes Ajax requests to the Node.js REST API to access resources. The user must be authenticated before providing access to protected resources.
To handle user authentication, we will integrate App ID with our Node.js backend. App ID makes it easy to add authentication, authorization, and user profile services to applications with several SDKs it offers. Specifically, to authenticate a user, App ID establishes an OIDC/OAuth2 Authorization code flow with the identity provider, e.g., Google. For more information on setting up App ID, please refer to the App ID documentation.
In the next section, let’s see how we can authenticate a user from the frontend and ultimately provide access to protected resources on the backend to authenticated users.
Authenticating users with App ID
App ID provides a customizable login widget for user authentication. Please follow this tutorial on how to customize your App ID login widget. When a user wants to sign in, they are redirected to the App ID login widget. The user can then choose to authenticate against an identity provider. Once the authentication flow is complete, the application obtains both an App ID access token as well as an App ID identity token. The tokens are formatted as JSON Web Tokens (JWTs). In addition to access and identity tokens, based on your App ID configuration, refresh tokens are also shared with the application.
Refresh Tokens : Refresh tokens are optional and is used to obtain new access and identity tokens without prompting the user to re-authenticate. These refresh tokens are shared with the backend application by the App ID SDK. Due to the sensitivity of the refresh token, it must never be shared with the frontend.
Identity token: Identity tokens represent authentication and contain information about the user. It provides information such as name, email, profile picture about the authenticated user. For more information regarding access and identity tokens, refer to the App ID Documentation.
Since refresh tokens, access token and identity token all contain sensitive information (in order of priority from most to least), it is important that they are protected both in transit and at rest. It is a good practice for these tokens (especially the refresh token) to be stored only the trusted modules of the app, such as the backend. Developers must consider the tradeoff between security and token usage when transferring the App ID access tokens to untrusted modules such as the frontend.
Session Based Authentication
Now that we have authenticated the user and obtained the tokens, we need to restrict access to the protected resources only to authenticated users. This can be achieved in several ways either by using cookies, sessions, or tokens. The below sections describe more about how it can be achieved.
Once our application has obtained access and identity tokens from App ID after user authenticates with the identity provider, the backend can setup a session for the user and return the session id to the frontend. On all subsequent requests from the user to access protected resources, the user session can be verified at the backend before granting access to the resources. While using sessions, it is extremely important to keep in mind that session cookies are susceptible to CSRF attacks. Luckily, there are a multitude of mitigation strategies available to prevent CSRF attacks. Discussing those strategies here is in itself a whole new ballgame and beyond the scope of this blog.
For the purposes of the sample application, we will be using Node.js Express session management to keep track of the authenticated users. More robust architectures can use session stores to store and manage user sessions.
An alternative and stateless method of managing the interaction between frontend and backend is through the use of tokens. In this method, upon authenticating the user, the backend will generate and return a secure token to the frontend. The frontend then includes the token in all subsequent requests, and the backend verifies the authenticity of the token before responding to the request.
Retrieving User Info in Frontend
As mentioned earlier, App ID returns the user information in the identity token. Although it is possible for the backend to simply pass the identity token to the frontend, the frontend accesses this information by calling an API in the backend. The backend can pick and choose what information in the identity token it shares with the frontend in the form of a JSON response to an API in the backend.
In the sample application that we will build today, the frontend obtains the user identity information stored in Express sessions, extracted from the identity token, by calling an endpoint. This endpoint checks if the user is authenticated by checking if a user session is present in Express and ultimately returns a JSON payload that includes the user authentication state. If the user is authenticated, the payload will also include the user identity information such as name and email.
App ID User Authentication Sequence Flow
Now that we understand the basic concepts, let’s see how a single page app with an Angular frontend and a Node.js backend can be integrated with App ID. Before we dive into some code snippets, let’s briefly review the Cloud Land application’s user authentication flow with App ID.
Note: All the authentication flow in the box labeled “User Authentication Provided by App ID” is fully managed by App ID SDKs behind the scenes.
User requests a protected resource, in our case the Cloud Land rewards points endpoint. If the user is not signed in, the application prompts the user to sign in to view any protected resources/access endpoints.
User is redirected to the App ID login widget; the user chooses the identity provider they wish to authenticate against. The browser is then redirected to the identity provider login website.
Upon successful user authentication with identity provider, the identity provider redirects back to the App ID callback url with the authorization code. App ID exchanges the authorization code for the access and identity tokens and redirects the user back to the application callback url registered with the App ID and a session is setup in express.
On every successive call to the backend, the user’s authentication is verified by looking up the session associated with the user, if any.
If the user is authenticated, the requested resource is sent back to the user.
Implementing the flow
Let’s start by securing the backend first:
Load all the required dependencies express, express-sessions, App ID SDK, passport.js, helmet
Setup an explicit login end point, for the App ID authentication. Configure passport to use App ID WebAppStrategy and specify the success redirect URL to the client application. More information on setting up App ID can be found here.
Lock down the /user/rewardpoints protected backend service as show below. If the user is not authenticated return back a 401 response.
Expose an endpoint to check if the current session is authenticated and to retrieve the user profile once logged.
Next, we need to verify on the client side, with the backend/server the user is actually authenticated before allowing access to any protected resources or routes.
Note: This blog post uses Angular for the Cloud Land application
Implement an Authentication service to verify with the server if the user is actually authenticated. If authenticated, it also retrieves the user profile, which contains a subset of attributes from the identity token.
Let’s create a Cloud Land home component, which prompts the user to sign in if they are not authenticated and finally display their profile information and their Cloud Land rewards point when they sign in.
Create a reward points service, that fetches points from the Cloud Land rewards endpoint.
Check out another one of our blogs which describes a simpler yet less scalable architecture for single page apps. In contrast to Cloud Land’s architecture where the frontend and backend are running on separate servers, this blog’s architecture serves the frontend and the backend from the same server.
Questions and feedback
- If you have technical questions about App ID, post your question on Stack Overflow and tag your question with ibm-appid.
- For questions about the service and getting started instructions, use the IBM Developer Answers forum. Include the appid tag.
- Open a support ticket in the IBM Cloud menu.
- Reach out directly to the development team on Slack!
To get started with App ID, check it out in the IBM Cloud Catalog.