Chapter 9. Authentication and sessions

This chapter covers

  • Introducing authentication
  • Challenging and determining the authenticity of a user’s claim to an identity
  • Using sessions to save authentication state between requests
  • Creating a way to route between application pages based on authenticated state

We’re now well into the implementation of an identity, authorization, personalization, and access control system designed to make the world a safe place for cat videos. A user can now set up and manage their identity. The Brushfire frontend is also personalized based on a user’s simulated authenticated status. In this chapter, you’ll create the authentication component and replace the simulated state with a user’s actual authenticated state. The goal of this authentication, along with the work done in chapters 68, is to require a Brushfire user to be authenticated to add video content. That way, if a user violates the content policy and posts a forbidden dog video, our investor can disable the account with extreme prejudice. In chapter 10, we’ll wrap up the client’s requirements by locking down our backend API based on the user’s authenticated state.

9.1. What is authentication?

When we use the term authentication or authenticated state, we’re referring to whether a user’s claim, on behalf of a user-agent (in our case, a browser), is genuine or not. To provide authentication, you first needed a user identity. So you added a unique email address and a unique username to the user model as key attributes of a user’s identity. You also added a password attribute that will later be used as proof of a claim to a particular identity. Thus, your authentication component has three distinct subcomponents:

  • The sign-in process challenges the authenticity of a user’s claim to a specific identity from the frontend via the login page and determines whether the claim is genuine on the backend via a controller action.
  • The session store stores the results of the challenge, which is the authenticated state, between requests using sessions on the backend.
  • The page controller routes requests between pages on the backend using a controller action that takes the authenticated state from the session and passes that state via locals to views used by the frontend framework, Angular.

In summary, on each request of a view, the page controller checks whether the user is authenticated in the session store. It then passes the locals to the view template that gets rendered on the page, thus storing the authenticated state on the page. That authenticated state is either a user’s record id (if they’re authenticated, or logged in) or null (if they’re unauthenticated, or logged out). You’ll spend the remainder of the chapter implementing each of these subcomponents. A summary of where you’ve been and where you’re going can be found in figure 9.1.

Figure 9.1. The four components of an identity, authentication, personalization, and access control system

9.2. The login process

You first need to challenge the user to authenticate, and then test whether the proof—the password provided—is genuine. You know this will consist of some frontend components making requests to your backend API. As we’ve done throughout the book, let’s look at the frontend for guidance on the requirements of your requests, routes, and controller actions.

9.2.1. Obtaining the example materials for the chapter

You have two options with regard to setting up your Brushfire assets for this chapter. If you’ve completed chapter 8, you’re all set and can use your existing project here. If you haven’t completed chapter 8, you can clone the end of the chapter 8 GitHub repo located at https://github.com/sailsinaction/brushfire-ch8-end.git and start from there. Remember to use npm install in the terminal window from the root of the project after you clone the repo.

If you do choose the cloning option, don’t forget to add the brushfire/config/local.js file with your Google API key from chapter 5 (section 5.4.6) as well as to start your local PostgreSQL brushfire database from chapter 6 (section 6.4.2).

9.2.2. Understanding the backend for a login form

Before you lift Sails, take a look at the requirements for the login action. In figure 9.2, the homepage contains a sign-in form that can trigger a PUT request to /login that passes in either an email address or a username along with a password as parameters.

Figure 9.2. The layout navigation partial has a sign-in form that executes a PUT request to /login when the sign-in button is clicked.

The backend requirements of the login request are as follows:

  • Query the user model for a record using email as a query criteria.
  • If a user is not found, send a 404 status as a response.
  • If a user record is found, compare the encryptedPassword of that user record with the password provided in the request.
  • If the password doesn’t match the encryptedPassword, send a 404 status as a response.
  • If the password does match, create a session, and send a 200 status as a response.

Now that you have the requirements of the request, you can create the custom Sails backend route and controller action that will be triggered when the frontend makes the request.

9.2.3. Creating a /login route

For your authentication system, you’ll create custom routes to trigger each controller action of the system. By doing this, you can have a central place for backend routes to your API. In Sublime, open brushfire/config/routes.js, and add the following route.

Example 9.1. Adding the login route

Now a PUT request to /login will trigger a login action within UserController.js. Next, you’ll create the login action that will be triggered when the user clicks the sign-in button.

9.2.4. Handling a login form

When the user clicks the sign-in button, the login action will be triggered. Let’s create that action. In Sublime, open brushfire/api/controllers/UserController.js, and add the login action shown in the following listing.

Example 9.2. Adding the login action

Your login action fulfills the requirements you established earlier by analyzing the frontend request. To summarize, you use the User.findOne() model method to query the user model for a record using the submitted email field via req.param ('email'), or the username field via req.param('username') as criteria for the query.

Note

In the past, your query dictionary contained a single parameter and value, for example, User.findOne({email: req.param('email')}). This query dictionary uses the or property with an array of values to use as criteria. Sails has a powerful query language through Waterline where you can build up sophisticated queries. For a complete overview, see http://mng.bz/qEo9.

The User.findOne() model method then returns either an error or a user record dictionary of the query results. You handle any errors with res.negotiate(err).

Note

Recall that res.negotiate() is a function that examines the provided error (err), determines the appropriate error-handling behavior via the error’s status property, and routes it to one of the following responses on the res dictionary: res.badRequest() (400 errors), res.forbidden() (403 errors), res.notFound() (404 errors), or res.serverError() (500 errors).

If a user isn’t found, you respond with a 404 status and your frontend code displays a message that the email address or username/password combination is invalid.

In chapters 5 and 6, we introduced the concept of machines and machinepacks. In chapter 7, you used a machine in machinepack-passwords to encrypt passwords in your signup action. You’ll now use another machine in the same machinepack named .checkPassword(). The Passwords.checkPassword() machine compares the two incoming parameters as inputs—the submitted password attribute and the encrypted-Password attribute you found in the user record. The machine provides three possible exits: an error exit, an incorrect exit if the passwords don’t match, and a success exit if the passwords do match.

On success, you first check whether a user has been deleted by checking the deleted property of the user dictionary . If the deleted property is true, you respond with a 403 forbidden status and message. Next, you check whether the user record has been banned by inspecting the banned property . If the banned property is true, you again respond with a 403 forbidden status and message. Finally, if the user isn’t deleted or banned, you send a 200 status via res.ok() .

To see this in action, you need to change the simulated state of the root route to null. In Sublime, open brushfire/config/routes.js, and change the id property to null, as shown in the next listing.

Example 9.3. Simulating an unauthenticated state in the root route

Now that you’ve set up the route that will load the homepage and the backend route that will trigger the login action, let’s look at your work in action. Restart Sails via sails lift, and navigate your browser to localhost:1337. Try to log in using the sign-in form with an email address of [email protected] and a password of abc123. Your browser should display a message similar to figure 9.3.

Figure 9.3. A successful challenge to the claim of identity of [email protected]

So far, you’ve created a sign-in form where a user can present an identity in the form of an email address or username and provided proof, in the form of a password, claiming a particular identity. You also have a custom route and controller action that can test whether the proof is genuine. Because requests are stateless, you need a way to store the result of this test between requests. But what do we mean when we say that requests are stateless?

9.2.5. What does “stateless” mean?

Imagine that the Sails web server (and every other web server on the planet) has a lousy memory when it comes to differentiating between requests. For example, a request is made from Nikola Tesla’s browser, and Sails routes it to a controller action that checks whether a submitted password matches a value stored in a user record. The controller action responds to the browser that the values match. In the next instant, another request is made from the same user-agent to the server. Sails has no idea that the previous request involved the successful submission of a matching password. Therefore, the state of that successful submission wasn’t stored anywhere, making the request stateless. Moving forward, it would be impractical to ask the user for their password each time they wanted to access something that required authentication. Fortunately, you don’t have to. You can use sessions to temporarily save their authenticated state.

9.2.6. Understanding the Sails session

Sessions are a way to store information like the authenticated state of a user between HTTP requests or Sails socket requests. The session uses several components to make all this happen. The good news is that sessions are built into Sails and are created and maintained for you with every request. This means that you can set and retrieve values on a property of the req dictionary called session and know that any values set on the session are associated with each request of a particular user-agent. This concept would benefit greatly from an actual example. Open brushfire/api/controllers/UserController.js in Sublime, and add the following controller actions.

Example 9.4. Adding setSession and getSession actions to better understand sessions

Note

Recall that blueprint action routes are enabled, so Sails will generate a route for the setSession and getSession actions automatically for you.

Restart Sails via sails lift and navigate your browser to http://localhost:1337/user/getSession. Your browser should return not set yet. This makes sense, because you haven’t assigned any values to req.session. Next, navigate your browser to http://localhost:1337/user/setSession?sessionVar=12345. This passes the parameter sessionVar to the controller action with a value of 12345. Your browser should return 12345. Finally, navigate your browser back to http://localhost:1337/user/getSession and your browser should return 12345. By storing a value on req.session.userId, you’ve given the request and, more importantly, the user-agent a state, which can be accessed between requests. Therefore, you have a way of storing the state of your password check for each authenticated user-agent.

Let’s take a somewhat closer look at the mechanics of the session without getting overwhelmed in how Sails is handling the session behind the scenes. Figure 9.4 illustrates the process at a high level.

Figure 9.4. The session consists of a session cookie and the user-agent, the session database, and a process for using a value stored on the session cookie to securely identify each request with a particular user-agent.

Each user-agent request to Sails contains a value stored in the session cookie that identifies a particular place in the session database where values are stored on behalf of that user-agent. Sails also provides a way of accessing, adding, or changing values stored in the session database via the req dictionary within an action. Storing the authenticated state of a user-agent is as simple as setting the value of a property on req.session like in an action.

Note

But what does the user-agent have to do with it? Because the user-agent for Brushfire is a browser, the browser window is tied to the user’s claim of identity through a browser cookie. A user, therefore, can’t claim more than one identity from the same user-agent browser because of this relationship with the cookie. Note that a browser window in incognito mode extends the browser’s ability to identify as an additional user because a different cookie is used for the incognito window.

Now that you have a better understanding of the session, you can put it to use in Brushfire to store a user’s authenticated state within the login, logout, removeProfile, restoreProfile, and signup actions. But before moving on, go back to brushfire/api/controllers/UserController.js, and remove the setSession and getSession actions.

9.2.7. Saving the user’s logged-in status

You’ll start using the session in the login action. Open brushfire/api/controllers/UserController.js in Sublime, and add the req.session.userId property to the login action, as shown in the following listing.

Example 9.5. Adding a userId property to the session on a successful password check

Now, when a user successfully authenticates via the sign-in form, a userId property is added to the session, which contains the id of the found user record. You now have a way to log in and store this authenticated state, so let’s create a way to log out.

9.2.8. Creating the logout endpoint

Let’s implement the logout route and action and assign the null value to req.session.userId to indicate the user-agent is not authenticated. First, you’ll create a custom route that will trigger the logout action. In Sublime, open brushfire/config/routes.js, and add the logout route shown here.

Example 9.6. Creating the logout route
...
'PUT /login': 'UserController.login',
'GET /logout': 'UserController.logout',
...

Next, create the logout action in the user controller. In Sublime, open brushfire/api/controllers/UserController.js, and add the logout action shown in the next listing.

Example 9.7. Adding the logout action in the user controller

When a GET request to /logout is made, the user’s authenticated state is set to null in the session, and the browser is redirected to the root route. You can test whether a user-agent is authenticated by assessing the value of req.session.userId. If it has a value, you know a user is authenticated, and if it’s null, you know it’s not authenticated. Next, you need to change the authenticated state when a user’s account is removed and restored.

9.2.9. Updating the session when a user is deleted or restored

When you soft-delete a user, their authenticated state should be changed. In Sublime, open brushfire/api/controllers/UserController.js, and set the user’s authenticated state in the removeProfile controller action, as shown here.

Example 9.8. Changing the user’s authenticated state to null when deleted

Similarly, when the user is restored, a user’s authenticated state should change. In Sublime, open brushfire/api/controllers/UserController.js, and set the user’s authenticated state in the restoreProfile controller action as follows.

Example 9.9. Changing the user’s authenticated state to their user id when restored

You have one other place to establish the authenticated state: the signup controller action.

9.2.10. Authenticating a user after signup

So far, you’ve assigned the appropriate session value for the user-agent’s authenticated state for the login, logout, removeProfile, and restoreProfile actions. Finally, when a user signs up for a user account, you want them to be authenticated if their user record is successfully created. In Sublime, open brushfire/api/UserController.js, and add the req.session.userId property to the signup action, as shown here.

Example 9.10. Adding req.session.userId to the signup action

You’ve now implemented a process for the user to submit proof of their identity and for you to test its validity. You also have a way to store the results of that test between requests. Before moving on, let’s look at how to configure the session itself.

9.2.11. Configuring the session store

From the previous section, you know you can add properties to the session. You can change a user’s authenticated state by changing the value assigned to req.session.userId. But what about the session itself, and how do you control a session’s lifecycle? By default, Sails sessions persist until the session database is restarted or the session cookie expires.

The Sails session database is currently configured to reside in memory. So while the session is configured in memory, the session for a request will be reset each time the Sails server starts. You can transition this in-memory store to a database like Redis in brushfire/config.session.js. Once configured, the session will exist independently of Sails so long as the Redis database is running. In fact, you’ll be implementing a Redis session store in chapter 15.

By default, Sails doesn’t place an expiration date/time on the session cookie, but you can change this in brushfire/config/session.js. In this file, you can create a maxAge parameter on the cookie property and set it to whatever expiration you desire.

Tip

The session cookie can’t be copied or altered via JavaScript on the browser. The cookie is also protected against tampering. That is not to say that if someone has access to the computer, the cookie can’t be copied directly.

The final step for authentication is to implement a controller, which routes requests between pages and passes the authenticated state via locals to the views.

9.3. Personalizing page content for logged-in users

In chapter 8, you made the transition for page navigation from using client-side routes and static assets to Sails custom routes and server-rendered views. With the custom routes, you also added static locals that hardcoded a simulated authenticated state of a user in the view. This gave you a chance to understand how custom routes could trigger server-rendered views directly with values via locals. Now that you have the user’s actual authenticated state, you can move the logic out of the custom route and into an action for each page or view of Brushfire. Figure 9.5 illustrates the progression.

Figure 9.5. You started off using client-side routing before transitioning to custom backend routes serving server-rendered views. Now that you have the user’s authenticated state, you can move to a combination of custom routes that trigger a custom action for each view.

You’ll update the route so that instead of directly loading the view, the route will trigger an action. For example, the homepage view will have an action named show-HomePage. In this action, you have greater flexibility to expand the logic than you do in a custom route. You can now determine if the user is authenticated by checking req.session.userId and sending locals that will trigger the display of the appropriate markup in the view. Figure 9.6 illustrates an example of this process.

Figure 9.6. When the user makes a GET request to /, it will be matched with a custom route that triggers an action that determines the user’s authenticated state and sends the appropriate locals to the layout view, which are then added to the frontend via an Angular controller and displayed based on a directive in the view.

When a user-agent makes a GET request to localhost:1337 , it’s matched in a custom route , which triggers a showHomePage action in the PageController. The action will determine that req.session.userId is null and render the homepage view with locals set to null. The frontend (controller) will read the authenticated state from the window dictionary and set an internal $scope.me property . Finally, an Angular directive will then display the appropriate form based on the user’s authenticated state.

You’ll spend the remainder of this chapter adding a custom action for each page in Brushfire that will correspond with your existing custom routes. This action will contain the logic to determine the user’s authenticated state and send locals to the view based on that state. Let’s start with the root route and the homepage view.

9.3.1. Introducing PageController

Before you start creating custom actions for each page view, you need to create a controller that will contain them. The easiest way to set up a boilerplate controller is via the command line. To create the controller, open a terminal window and type

~/brushfire $ sails generate controller page

Sails will generate an empty PageController.js file in brushfire/api/controllers. With the controller generated, you can start to transition the homepage view and route.

9.3.2. Using a custom action to serve the homepage

You’re now going to move the locals from each route to a custom controller action. First, you need to change the route that triggers an action instead of the view. In Sublime, open brushfire/config/routes.js, and alter the root route, as follows.

Example 9.11. Altering the root route to trigger the showHomePage action
module.exports = {
...
/************************************************************
* Server Rendered HTML Pages                                *
*************************************************************/

  'GET /': 'PageController.showHomePage',
...

Notice that you’re no longer passing static locals to the homepage view. Instead, a GET request to the root (for example, /) triggers the showHomePage action of the page controller. So let’s create the action. In Sublime, open brushfire/api/controllers/PageController.js, and add the following action.

Example 9.12. Creating a showHomePage action

This action responds with a server-rendered view using res.view(). Table 9.1 contains the two (optional) arguments to res.view().

Table 9.1. The path to the view template in res.view

Argument

Type

Details

pathToView String The path to the desired view file relative to your views/ folder, without the file extension .ejs and with no trailing slash
locals Dictionary {} Data to pass to the view template

Without arguments, Sails will derive the pathToView argument as a combination of the controller name and the action name. You can also provide an explicit path to the view template. Figure 9.7 provides examples of how pathToView is resolved.

Figure 9.7. Examples of how pathToView is resolved based on the configuration used

You want to explicitly provide the path to the view as an argument. You also want to pass various locals to the view template. You can pass locals as a dictionary and the second argument of res.view(). So if req.session.userId is null, res.view('home-page') will render brushfire/views/homepage.ejs. If req.session.userId has a value indicating that the user is authenticated, you’ll use the user id as a criteria object argument in the User.findOne() model method. If the User.findOne() method returns an error, you’ll let res.negotiate(err) determine which status code to return. If instead User.findOne() doesn’t return a user record, you’ll log a message using sails.log.verbose.

Note

sails.log is similar to logging via console.log, but you can set the visibility of the message via config.log.js. This is useful when you want to display certain messages during development but hide those messages in a production environment. In chapter 15, we’ll discuss setting up different environments.

Finally, if the findOne() method returns a user record, you’ll compile a server-rendered view with res.view(), passing the homepage view with the locals including the id, email address, Gravatar URL, and admin property of the user record.

Note

The admin property was added so the administration link will be displayed only to users with the admin property set to true.

Instead of displaying the toastr message and remaining on the homepage, let’s redirect to the videos page on a successful login. In Sublime, open brushfire/assets/js/controllers/navPageController.js, and make the following changes to the navPageController.

Example 9.13. Redirecting to the videos page after successfully logging in

Let’s give this new controller a spin. Restart Sails using sails lift, and navigate your browser to localhost:1337. Next, sign in to Brushfire using [email protected] as the email address and abc123 as the password. The browser should redirect to the videos page, similar to figure 9.8.

Figure 9.8. The browser was redirected to the videos page, but the navigation partial isn’t displaying the markup for the authenticated state.

Although the browser redirected to the videos page, the navigation partial isn’t displaying the authenticated state. That’s because you haven’t transitioned the custom route and action for the videos page. Let’s do that next.

9.4. Implementing the backend application flow

Now that you have PageController as your foundation for managing the frontend user experience, let’s transition the remainder of your pages. Figure 9.9 illustrates each mockup page and its associated route.

Figure 9.9. The Brushfire mockup pages with custom routes

Let’s transition the videos page route and add a new action to the page controller.

9.4.1. Personalizing your list of videos

In chapter 8, we addressed what should be displayed on the videos page based on the authenticated state of the user. Essentially, if the user is authenticated, the submit-videos form is displayed. If the user isn’t authenticated, the form isn’t displayed and the user can’t add videos.

Note

In fact, the user can add videos using Postman making a POST request to /videos. In chapter 10, we’ll address how to lock down access to controller actions with policies.

Now you want your page controller action to bootstrap the user’s authenticated state. First, modify your existing GET request to /videos. In Sublime, open brushfire/config/routes.js, and make the following changes to the route.

Example 9.14. Adding the /videos route
module.exports = {
  'GET /': 'PageController.showHomePage',
  'GET /videos': 'PageController.showVideosPage',
...

Notice that you’re no longer passing static locals to the homepage view. Instead, a GET request to /videos triggers the showVideosPage action of the page controller. So let’s create a showVideosPage action. In Sublime, open brushfire/api/controllers/PageController.js, and add the following action.

Example 9.15. Creating a showVideosPage action
module.exports = {
...

  showVideosPage: function (req, res) {

    if (!req.session.userId) {
      return res.view('videos', {
        me: null
      });
    }

    User.findOne(req.session.userId, function (err, user){
      if (err) {
        return res.negotiate(err);
      }

      if (!user) {
        sails.log.verbose('Session refers to a user who no longer exists- did
         you delete a user, then try to refresh the page with an open tab
         logged-in as that user?');
        return res.view('videos', {
          me: null
        });
      }

      return res.view('videos', {
        me: {
          id: user.id,
          email: user.email,
          gravatarURL: user.gravatarURL,
          admin: user.admin
        }
      });
    });
  }
};

9.4.2. Securing your administration page

In order for the administration view to be displayed, a user must not only be authenticated but also have the admin attribute in their user record set to true. You currently don’t have a user record with the admin property set to true. Let’s change that by making your test user an administrator. In Sublime, open brushfire/config/bootstrap.js, and change the admin property to true.

Example 9.16. Making the test user account an administrator

Now you can set up the administration page. First, create the route that will trigger the showAdminPage action. In Sublime, open brushfire/config/routes.js, and make the following changes to the route.

Example 9.17. Altering the route to trigger an administration page route

Unlike the other pages in Brushfire, the administration page is displayed only if the admin attribute is set to true. In Sublime, open brushfire/api/controllers/Page-Controller.js, and add the following action.

Example 9.18. Creating a showAdminPage action

Now that you have the administration page controller completed, let’s move on to the profile view.

9.4.3. Personalizing the user profile page

The profile page doesn’t have an unauthenticated state. Therefore, if the user isn’t authenticated and attempts to make a GET request to /profile, you want the browser redirected to the root route or homepage. In Sublime, open brushfire/config/routes.js, and make the following changes to the route.

Example 9.19. Adding the profile custom route
module.exports = {
  'GET /': 'PageController.showHomePage',
...
  'GET /profile': 'PageController.showProfilePage',
...

Next, let’s implement the controller action. In Sublime, open brushfire/api/controllers/PageController.js, and add the following action.

Example 9.20. Creating a showProfilePage action

Because you don’t have a requirement to display the profile page in an unauthenticated state, if the request is unauthenticated, you’ll redirect it to the root route. Now that you’ve addressed the profile, you can implement the GET requests to /edit-profile.

9.4.4. Securing the edit-profile page

The edit-profile page doesn’t have an unauthenticated state. Therefore, if the user is unauthenticated and attempts to make a GET request to /edit-profile, you want the browser redirected to the root route or homepage. In Sublime, open brushfire/config/routes.js, and make the following changes to the route.

Example 9.21. Altering edit-profile to trigger the showEditProfilePage action
module.exports = {
  'GET /': 'PageController.showHomePage',
  ...
  'GET /edit-profile': 'PageController.showEditProfilePage',
...

Next, let’s implement the showEditProfilePage controller action. In Sublime, open brushfire/api/controllers/PageController.js, and add the following action.

Example 9.22. Creating a showEditProfilePage action

Because you don’t have a requirement to display the profile page in an unauthenticated state, if the request is unauthenticated, you’ll redirect it to the root route. You did add the username property because it’s used on the edit-profile page. Now that you’ve addressed the edit-profile page, you can implement the GET requests to /restore-profile.

9.4.5. Securing other account-related pages

The restore-profile page doesn’t have an authenticated state. Therefore, if the user is authenticated and attempts to make a GET request to /restore-profile, you want the browser redirected to the root route or homepage. In Sublime, open brushfire/config/routes.js, and make the following changes to the route.

Example 9.23. Altering restore-profile to trigger the showRestorePage action
module.exports = {
  'GET /': 'PageController.showHomePage',
  ...
  'GET /restore-profile': 'PageController.showRestorePage',
...

Next, you’ll create the showRestorePage controller action. In Sublime, open brushfire/api/controllers/PageController.js, and add the following action.

Example 9.24. Creating a showRestorePage action

The restore-profile page doesn’t have an authenticated state; therefore, there’s no need to look up a user record. This makes the action simple to implement. Next, let’s implement the page controller for the signup page.

9.4.6. Implementing business rules for the signup page

The signup page doesn’t have an authenticated state. Therefore, if the user is authenticated and attempts to make a GET request to /signup, you want the browser redirected to the root route or homepage. In Sublime, open brushfire/config/routes.js, and make the following changes to the route.

Example 9.25. Altering the signup route to trigger the showSignupPage action
module.exports = {
  'GET /': 'PageController.showHomePage',
  ...
  'GET /signup': 'PageController.showSignupPage',
...

Notice that you’re no longer passing static locals to the homepage view and instead routing the request directly to the showSignupPage action. In Sublime, open brushfire/api/controllers/PageController.js, and add the following action.

Example 9.26. Creating a showSignupPage action

At this point, the frontend of Brushfire responds with an appropriate UI based on the user’s authenticated state. In the next chapter, we’ll implement access control on the backend, locking down endpoints based on the user’s authenticated state.

9.5. Summary

  • Authentication is the process of allowing the user to prove their identity.
  • Requests are considered stateless because nothing is saved inherently between requests.
  • A session consists of a data store, middleware, and a session cookie that all combine to allow a user’s authenticated state to be saved between requests.
  • Creating an action for each frontend page view provides complete flexibility in personalizing the frontend based on the user’s identity and authenticated state.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset