web-dev-qa-db-fra.com

Routage côté client AngularJS et authentification par jeton avec API Web

Je veux créer un exemple d'authentification et d'autorisation dans une application SPA angularjs utilisant asp.net mvc webapi comme backend et routage côté client (pas de cshtml). Voici un exemple de fonctions qui peuvent être utilisées pour configurer l'exemple complet. Mais je ne peux pas tout mettre ensemble. Toute aide appréciée.

Des questions:

  1. Quelle est la meilleure pratique: basée sur les cookies ou les jetons?
  2. Comment créer le jeton porteur dans angular pour autoriser à chaque demande?
  3. Validation des fonctions API?
  4. Comment puis-je conserver l'authentification authentifiée de l'utilisateur sur le client?

Exemple de code:

  1. Formulaire de connexion

    <form name="form" novalidate>
     <input type="text" ng-model="user.userName" />
     <input type="password" ng-model="user.password" />
     <input type="submit" value="Sign In" data-ng-click="signin(user)">
    </form>
    
  2. Authentification Angular

    $scope.signin = function (user) {
    $http.post(uri + 'account/signin', user)
        .success(function (data, status, headers, config) {
            user.authenticated = true;
            $rootScope.user = user;
            $location.path('/');
        })
        .error(function (data, status, headers, config) {
    
            alert(JSON.stringify(data));
            user.authenticated = false;
            $rootScope.user = {};
        });
    };
    
  3. Mon code API backend API.

    [HttpPost]
    public HttpResponseMessage SignIn(UserDataModel user)
    {
        //FormsAuthetication is just an example. Can I use OWIN Context to create a session and cookies or should I just use tokens for authentication on each request? How do I preserve the autentication signed in user on the client?
        if (this.ModelState.IsValid)
        {
            if (true) //perform authentication against db etc.
            {
                var response = this.Request.CreateResponse(HttpStatusCode.Created, true);
                FormsAuthentication.SetAuthCookie(user.UserName, false);
    
                return response;
            }
    
            return this.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Invalid username or password");
        }
        return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState);
    }
    
  4. Autorisation Utilisation de la bibliothèque JWT pour restreindre le contenu.

    config.MessageHandlers.Add(new JsonWebTokenValidationHandler
    {
      Audience = "123",
      SymmetricKey = "456"
    });
    
  5. Mes méthodes API

    [Authorize]
    public IEnumerable<string> Get()
    {
     return new string[] { "value1", "value2" };
    }
    
32

L'utilisation de l'authentification par cookie ou des jetons (au porteur) dépend toujours du type d'application que vous avez. Et pour autant que je sache, il n'y a pas encore de meilleure pratique. Mais puisque vous travaillez sur un SPA et que vous utilisez déjà une bibliothèque JWT, je préférerais l'approche basée sur les jetons.

Malheureusement, je ne peux pas vous aider avec ASP.NET, mais généralement les bibliothèques JWT génèrent et vérifient le jeton pour vous. Tout ce que vous avez à faire est d'appeler generate ou encode sur les informations d'identification (et le secret) et verify ou decode sur le jeton envoyé avec chaque demande. Et vous n'avez pas besoin de stocker un état sur le serveur et vous n'avez pas besoin d'envoyer un cookie, ce que vous avez probablement fait avec FormsAuthentication.SetAuthCookie(user.UserName, false).

Je suis sûr que votre bibliothèque fournit un exemple sur la façon d'utiliser les jetons de génération/codage et de vérification/décodage.

La génération et la vérification ne sont donc pas quelque chose que vous faites du côté client.

Le flux va quelque chose comme ceci:

  1. Le client envoie les informations de connexion fournies par l'utilisateur au serveur.
  2. Le serveur authentifie les informations d'identification et répond avec un jeton généré.
  3. Le client stocke le jeton quelque part (stockage local, cookies ou simplement en mémoire).
  4. Le client envoie le jeton comme en-tête d'autorisation à chaque demande au serveur.
  5. Le serveur vérifie le jeton et agit en conséquence en envoyant la ressource demandée ou un 401 (ou quelque chose de similaire).

Étape 1 et 3:

app.controller('UserController', function ($http, $window, $location) {
    $scope.signin = function(user) {
    $http.post(uri + 'account/signin', user)
        .success(function (data) {
            // Stores the token until the user closes the browser window.
            $window.sessionStorage.setItem('token', data.token);
            $location.path('/');
        })
        .error(function () {
            $window.sessionStorage.removeItem('token');
            // TODO: Show something like "Username or password invalid."
        });
    };
});

sessionStorage conserve les données tant que l'utilisateur a la page ouverte. Si vous souhaitez gérer vous-même les délais d'expiration, vous pouvez utiliser localStorage à la place. L'interface est la même.

Étape 4:

Pour envoyer le token à chaque requête au serveur, vous pouvez utiliser ce que Angular appelle un Interceptor . Tout ce que vous avez à faire est d'obtenir le token précédemment stocké (le cas échéant) ) et le joindre en tant qu'en-tête à toutes les demandes sortantes:

app.factory('AuthInterceptor', function ($window, $q) {
    return {
        request: function(config) {
            config.headers = config.headers || {};
            if ($window.sessionStorage.getItem('token')) {
                config.headers.Authorization = 'Bearer ' + $window.sessionStorage.getItem('token');
            }
            return config || $q.when(config);
        },
        response: function(response) {
            if (response.status === 401) {
                // TODO: Redirect user to login page.
            }
            return response || $q.when(response);
        }
    };
});

// Register the previously created AuthInterceptor.
app.config(function ($httpProvider) {
    $httpProvider.interceptors.Push('AuthInterceptor');
});

Et assurez-vous de toujours utiliser SSL!

91
bernhardw