Skip to content

Articles

Sécuriser AngularJS avec les Claims

14 février 2015 • 6 min de lecture

Sécuriser AngularJS avec les Claims

À un moment donné, une application a besoin d’autorisation. Cela signifie que différents niveaux d’accès se comportent différemment sur un site web (ou n’importe quoi d’ailleurs). Cela peut aller de la visualisation de données à des zones entières qui ne sont pas accessibles par un groupe d’utilisateurs.

Dans les applications non Single Page (SPA), un claim ou un rôle est associé à des données ou à une zone de l’application, soit l’utilisateur a ce rôle ou ce claim, soit il ne l’a pas. Dans une SPA c’est pareil, mais avec un énorme avertissement. Une SPA est téléchargée vers le navigateur. À ce moment-là, le navigateur a un contrôle total sur le code. Une personne malveillante peut modifier le code pour qu’il fasse ce qu’elle veut.

Parce que les SPAs ne peuvent pas être sécurisées, l’authentification et l’autorisation dans une SPA ne sont qu’une expérience utilisateur. Toute sécurité significative doit être faite sur le serveur web. Cet article ne couvre pas la sécurisation de votre API contre les attaques. Je recommande de regarder une vidéo de Pluralsight ou de lire un document qui traite de la sécurité pour votre technologie serveur.

L’intention de cet article est de vous montrer comment j’ai ajouté une expérience utilisateur d’autorisation à ma SPA Angular 1.x.

Portées de Sécurité

J’ai identifié 3 zones de l’interface utilisateur qui nécessitent une autorisation : Éléments (HTML), Routes, et Données.

Juste un rappel, sécuriser une SPA ne remplace pas la sécurisation du serveur. Les permissions sur le client servent simplement à garder les gens honnêtes honnêtes et à fournir à l’utilisateur une bonne expérience.

Les 3 zones en détail :

Éléments

Vous devrez masquer des éléments HTML spécifiques. Cela peut être un label, un tableau avec des données, un bouton, ou n’importe quel élément sur la page.

Routes

Vous voudrez masquer des routes entières. Dans certains cas, vous ne voulez pas que l’utilisateur accède à une vue. En sécurisant la route, un utilisateur ne peut pas naviguer vers la vue. Il verra à la place un message “Vous n’êtes pas autorisé à naviguer vers cette vue”.

Données

Parfois, masquer les éléments dans la vue ne suffit pas. Un utilisateur astucieux peut simplement voir le source et voir les données cachées dans le source HTML ou les regarder arriver dans le navigateur. Ce que nous voulons, c’est que les données ne soient pas récupérées en premier lieu.

Ajouter de la sécurité est délicat. Au début, j’ai essayé de contraindre l’accès au niveau de l’API HTTP (côté client). J’ai rapidement réalisé que cela ne fonctionnerait pas. Un utilisateur pourrait ne pas avoir un accès direct aux données, mais cela ne signifie pas qu’il n’a pas un accès indirect aux données. Au niveau de la couche API HTTP (généralement l’une des plus basses dans l’application), nous ne pouvons pas dire le contexte de l’appel et donc ne pouvons pas appliquer les préoccupations de sécurité à celui-ci.

Ci-dessous, j’ai fourni des exemples de code :

Code

J’ai créé un service pour le code de vérification d’autorisation. C’est le cœur de l’autorisation. Toutes les demandes d’autorisation utilisent ce service pour vérifier si l’utilisateur est autorisé pour l’action particulière.

angular.module('services')
    .service('AuthorizationContext',function(_, Session){

        this.authorizedExecution = function(key, action){

            //Looking for the claim key that was passed in. If it exists in the claim set, then execute the action.
            Session.claims(function(claims){
                var claim = findKey(key, claims);

                //If Claim was found then execute the call.
                //If it was not found, do nothing
                if(claim !== undefined){
                    action();
                }
            });
        };

        this.authorized = function(key, callback){
            //Looking for the claim key that was passed in. If it exists in the claim set, then execute the action.
            Session.claims(function(claims){
                var claim = findKey(key, claims);

                //If they don't have any security key, then move forward and authorization.
                var valid = claim !== undefined;
                callback(valid);
            });
        };

        //this.agencyViewKey = '401D91E7-6EA0-46B4-9A10-530E3483CE15';

        function findKey(key, claims){
            var claim = _.find(claims, function(item){
                return item.value === key;
            });

            return claim;
        }
    });

Directive Authorize

La directive authorize peut être appliquée à n’importe quel élément HTML que vous voulez masquer aux utilisateurs sans un niveau d’accès spécifique. Si l’utilisateur a le token d’accès dans le cadre de ses claims, il est autorisé à voir l’élément. S’il ne l’a pas, il est masqué.

angular.module('directives')
    .directive('authorize', ['$compile', 'AuthorizationContext', function($compile, AuthorizationContext) {
        return {
            restrict: 'A',
            replace: true,
            //can't have isolated the scope in a shared directive
            link:function ($scope, element, attributes) {

                var securityKey = attributes.authorize;
                AuthorizationContext.authorized(securityKey, function(authorized){
                    var el = angular.element(element);
                    el.attr('ng-show', authorized);

                    //remove the attribute, otherwise it creates an infinite loop.
                    el.removeAttr('authorize');
                    $compile(el)($scope);
                });
            }
        };
    }]);

Éléments

Je m’appuie beaucoup sur les onglets dans mon application. J’applique la directive authorize à l’onglet que je veux masquer aux utilisateurs sans les claims appropriés.

<tabset>
<tab ng-cloak heading="Users" authorize="{{allowUserManagement}}">
...html content
</tab>
</tabset>

Routes

J’utilise le ui-router. Malheureusement pour ceux qui ne l’utilisent pas, je n’ai pas de code pour le routeur AngularJS par défaut.

Dans le $stateChangeStart j’authentifie la route. Voici le code dans cet événement.

$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){
   AuthenticationManager.authenticate(event, toState, toParams);
});

La fonction qui autorise la route. Si elle est autorisée, la route est autorisée à continuer. Si elle n’est pas autorisée, un message est affiché à l’utilisateur et il est dirigé vers la page d’accueil.

function authorizedRoute(toState, location, toaster, breadCrumbs){
   if(toState.authorization !== undefined){
       AuthorizationContext.authorized(toState.authorization, function(authorized){
           if(!authorized){
               toaster.pop('error', 'Error', 'You are not authorized to view this page.');
               location.path("/search");
           } else {
               breadCrumbs();
           }
       });
   } else{
       breadCrumbs();
   }
}

Dans cette définition de routeur, vous remarquerez une propriété appelée ‘authorization’. Si l’utilisateur a ce claim, il est autorisé à procéder.

angular.module('agency',
    [
        'ui.router',
        'services'
    ])
    .config(function config($stateProvider){
    $stateProvider.state( 'agency', {
        url: '/agency',
        controller: 'agency.index',
        templateUrl: 'agency/agency.tpl.html',
        authenticate: true,
        authorization:'401d91e7-6ea0-46b4-9a10-530e3483ce15',
        data:{ pageTitle: 'Agency' }
    });
});

Données

Dans certains cas, vous ne voulez pas faire une demande au serveur pour les données. Si l’utilisateur a le claim, il sera autorisé à faire la demande.

L’AuthorizationContext ci-dessus au début de l’article montre le code pour authoriedExecution. Ici vous voyez son utilisation.

AuthorizationContext.authorizedExecution(Keys.authorization.allowUserManagement, function(){
    //execute code, if the loggedin user has rights.

                });

Comme je l’ai mentionné ci-dessus, ceci ne remplace pas la sécurisation du serveur. Ce code fonctionne pour fournir une expérience utilisateur merveilleuse.

Auteur : Chuck Conway se spécialise dans l’ingénierie logicielle et l’IA générative. Connectez-vous avec lui sur les réseaux sociaux : X (@chuckconway) ou visitez-le sur YouTube.

↑ Retour en haut

Vous pourriez aussi aimer