Securing specific pages in Angular front-end: A guide to implementing authentication

Interested in developing a web application using Node.js that allows users to log in (authentication). The app will have 3 non-secure pages (/home, /contact, /about) and one secure page (/admin). I've been consulting the Mean Machine book from scotch.io for guidance.

I'm facing an issue with setting up the authentication. While the login feature works correctly and redirects me to /admin upon logging in, I can still access the /admin page by directly entering the URL without logging in. I need help figuring out where to implement the actual protection mechanism.

Here's an overview of how I structured my app. I'm looking for a conceptual answer on the correct approach rather than just a code snippet.

Services:

  • The auth service sends the inputted username/password to the server and returns either false or success (along with user info and JWT token).
  • The auth service also injects the token into each HTTP header as AuthInterceptor

Router:

angular.module('routerRoutes', ['ngRoute'])
    .config(function($routeProvider, $locationProvider) {
        $routeProvider
            .when('/', {
                templateUrl:    'views/home.html',
                controller:     'homeController',
                controllerAs:   'home'
            })
            // More route configurations here...
            .when('/admin', {
                templateUrl:    'views/admin/admin.html',
                controller:     'adminController',
                controllerAs:   'admin'
            });

        $locationProvider.html5Mode(true);

    });

Controllers:

  • homeController, aboutController, contactController are currently empty

  • adminController:

    .controller('adminController', function($rootScope, $location, Auth) {

    // Controller logic here
    
    });
    

    });

Below is a snippet of my index.html file:

<body class="container" ng-app="meanApp" ng-controller="adminController as admin">

    // HTML content here

</body>

If you have suggestions on improving my setup and any best practices I should follow, please let me know.

Lastly, I have a small question regarding the visibility of elements based on conditions in Angular:

  • I expected that elements with "ng-if" directives wouldn't appear in the 'view source' if the condition wasn't met, but they do show up. Is this normal behavior?

Answer №1

Implementing a custom property route has been integral to ensuring the security of the routes within my application. Each state change is monitored for this specific property, and if it is present, the user's login status is checked. If the user is not logged in, they are redirected to the 'login' state.

In my current project, I utilized UI-ROUTER and created a custom parameter named "data" to be used within the routes.

Here is an example of how I declared the initial routes within a .config block:

$stateProvider
.state('login', {
    url: '/login',
    templateUrl: 'login/login.html',
    controller: 'LoginController',
    controllerAs: 'vm'
})
.state('home', {
    url: '',
    templateUrl: 'layout/shell.html',
    controller: 'ShellController',
    controllerAs: 'vm',
    data: {
        requireLogin: true
    }
})

I then added the following code to a .run function in the application to handle the $stateChangeStart event and check for my custom property ('data') in the state declaration:

$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {

var requireLogin = toState.hasOwnProperty('data') && toState.data.requireLogin;
if (requireLogin && !authService.isLoggedIn()) {
    event.preventDefault();
    authService.setDestinationState(toState.name);
    $state.go('login');
}

if (toState.name !== 'login') {
    authService.setDestinationState(toState.name);
}
});

If you're wondering about the functionality of the authService.setDestinationState method, it saves the URL that the user was trying to access. After successfully logging in, the user is automatically directed to that saved state (as shown below):

function login() {
authService.authLogin(vm.credentials)
.then(loginComplete)
.catch(loginFailed);

function loginComplete(data, status, headers, config) {
    vm.user = data;
    $rootScope.$broadcast('authorized');
    $state.go(authService.getDestinationState());
}

function loginFailed(status) {
    console.log('XHR Failed for login.');
    vm.user = undefined;
    vm.error = 'Error: Invalid user or password. ' + status.error;
    toastr.error(vm.error, {closeButton: true} );
}
}

Answer №2

When setting up your Admin route, you have the option to include a property called resolve. Each item within resolve should be a function (which can also be an injectable function). This function needs to return a promise, whose result can then be injected into the controller.

If you want more details about resolve, check out this article.

You can utilize resolve in order to perform an authentication check like this:

var authenticateRoute = ['$q', '$http' , function ($q, $http) {
    var deferred = $q.defer();
    $http.get("http://api.domain.com/whoami")
         .then(function(response) { 
                   if (response.data.userId) deferred.resolve(response.data.userId);  
                   else window.location.href = "#/Login"
               });
    return deferred.promise();
}]

// ...

.when('/admin', {
    templateUrl: 'views/admin/admin.html',
    controller: 'adminController',
    controllerAs: 'admin',
    resolve: {
        authenticatedAs: authenticateRoute
    }
});

By doing this, you can pass the authenticated User Id - even if it's null - and let the controller handle it, perhaps to display a contextual message. Alternatively, you can follow the approach mentioned above and redirect to the login page only if there is no user Id from the authentication response.

I hope this explanation helps! /AJ

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to ensure a div occupies all available space within a column using the vh-100 class in bootstrap?

In the <div class="bg-primary"></div> element, I'm trying to make it take up the remaining empty space without exceeding the vh-100. I've experimented with various solutions but haven't been able to find a fix yet. b ...

AngularJS - the element of surprise in execution sequence

Encountering a puzzling issue that exclusively affects Internet Explorer (any version) and not Chrome. An "items" array is stored within the "doc" object. Users have the capability to edit items, which essentially deletes the item but retains its content ...

The ineffective operation of LoopBack ACL

I have created a custom model called MyUser which inherits from the LoopBack User model. In my boot script, I created a user in the MyUser model, a custom role, and mapped that role to it. However, the ACL (Access Control List) is not working properly afte ...

What are the steps to effectively utilize <ul> for showcasing scrolling content?

I stumbled upon and found it to be a great inspiration for my project. I tried replicating the layout of the listed items on the site: .wrap { display: block; list-style: none; position: relative; padding: 0; margin: 0; border: ...

Updating variable storage in React components

This is a project built with Next.js and React. Below is the folder structure: components > Navbar.js pages > index.js (/ route)(includes Navbar) > submitCollection.js (/submitCollection)(includes Navbar) The goal is to allow users to inpu ...

What could be the reason for react-query searching for dispatch even when redux is not activated or present in the component?

I am currently working on a component that is supposed to fetch logged-in users from the server. Despite Swagger indicating that the server code returns correctly, the component fails to make the necessary fetch request when loaded. Below is the code snip ...

When you include ng-href in a button using AngularJS, it causes a shift in the alignment of the text within the button

Recently, I've been delving into Angularjs with angular-material and encountered a slight issue with ng-href. I created a Toolbar at the top of my webpage, but the moment I include the "ng-href" attribute to a button, the text inside the Button loses ...

jquery logic for iterating through all elements in a select menu encountering issues

In search of a solution to iterate through all options in a dropdown list using code, comparing each to a variable. When a match is found, I aim to set that particular value as the selected item in the dropdown and then exit the loop. Here's what I&ap ...

How can I change the background color of the initial word in a textbox?

In my HTML, I have a text box input. While I am familiar with how to use CSS to set the background color of the entire textbox using background-color, I am wondering if it is possible to specifically target and change the background color of only the first ...

Using AngularJS to apply filters to JSON data

I'm having trouble filtering a JSON array. Here's an example of what my JSON array looks like: vm.users = [{ "fname": "Antoan", "lname": "Jonson", "Address": "Address1" }, ... ] How do I filter by last name starting with a specific term (e.g. & ...

Tips for providing support to a website without an internet connection

I am in the process of creating a sales platform for a grocery store that utilizes PHP/MySQL. I have come across some websites that are able to fully reload and function even without internet access. For instance, when I initially visited abc.com, everyth ...

several parameters for the `ts-node -r` options

Can I include multiple require statements in the package.json script before running with ts-node -r? "scripts": { "start": "ts-node -r dotenv/config newrelic src/index.ts", } I'm having trouble adding both "dotenv/config" and "newrelic" si ...

Utilizing the .on() method to select elements based on a unique attribute

It is possible to use the .on() method to attach a single click event to an element and then specify which child elements receive the click. For example: $(this.el).on("click", "span", function () { alert("Bloop!"); }); If you need to be more specifi ...

Which is better for managing checkbox state in React - react-bootstrap or material-ui?

Currently, I am working on a project that involves using checkboxes within a component to display products based on specific features selected by the user. I am seeking advice on how to effectively manage the checkboxes and their corresponding state inform ...

Filtering substrings in an Angular data resource

DEFAULT_RECORDS = [{ id: 1, name: 'John Evans', number: '01928 356115' },{ id: 16, name: 'Murbinator', number: '053180 080000' }]; - retrieveEntries: function (name) { var foundRecords = {}; ...

Having trouble accessing undefined properties in ReactJs? Specifically, encountering issues when trying to read the property "name"?

I am facing an issue with using the data in my console even though I can log it. The structure of my data object is as follows: {_id: '616bf82d16a2951e53f10da4', name: 'abd', email: '[email protected]', phone: '123456789 ...

The code encountered an error with message TS2345 stating that the argument type '(a: Test, b: Test) => boolean | 1' cannot be assigned to a parameter type of '(a: Test, b: Test) => number'

Apologies for the lengthy subject, but I am having trouble understanding the response. Here is my code snippet: this.rezerwacjeFilteredByseaarchInput.sort(function (a, b) { if (a[5]===null) { // console.log(a[5]); return 1; } ...

Ways to retrieve the chosen option from a dropdown menu within an AngularJS controller

I have a drop down (combo box) in my application that is populated with values from a JSON array object. Can someone please explain how to retrieve the selected value from the drop down in an AngularJS controller? Appreciate the help. ...

Bootstrap Tags Input - the tagsinput does not clear values when removed

I am attempting to manually remove the input value from bootstrap-tags-input when the x button is clicked, but the values are not changing in either the array or the inputs. This is the code I have tried: $('input').tagsinput({ allowDuplica ...

Looking for a powerful filtering menu similar to Excel or Kendo UI Grid in Datatables?

Is there a way to add Excel-like filtering to DataTables? It's surprising that such a widely used and advanced plugin doesn't have this feature already. If not, is there an easy way to implement it? Below are examples of advanced menu filters sim ...