Solucionado (ver solução)
Solucionado
(ver solução)
30
respostas

interceptor.responseError (rejection)

O interceptor não está identificando o erro 401, o console.log() do back-end roda a cada requisição porém não está redirecionando o usuário a tela de login,

Função de erro do Interceptor

interceptor.responseError = function(rejection) {
      if (rejection != null && rejection.status === 401) {
          console.log('Removendo token da sessão')
          delete $window.localStorage.token;
          $location.path("/login");
      } 
      return $q.reject(rejection);
  }

API auth.js

  api.verificaToken = function(req, res, next){

    var token = req.headers['x-access-token'];

    if(token){
      console.log('Verificando Token...');
      jwt.verify(token, app.get('secret'), function(err, decoded){
        if (err) {
          console.log('Token Rejeitado');
          return res.sendStatus(401);
        }
        req.user = decoded;
        console.log('Usuario Aprovado');
        next();
      });
    } else {
      console.log('Token não enviado');
      return res.sendStatus(401);
    }
  }
  return api;
};

main.js

var app = angular.module('app',['ngRoute','services','directives','ngResource']);

app.config(function($routeProvider, $locationProvider, $httpProvider) {

    $httpProvider.interceptors.push('tokenInterceptor');

...
30 respostas

O responseError é pelo menos chamado no seu código? Pode ficar tranquilo, que mais de 500 alunos já passaram por essa parte sem problema. A gente descobre.

Olá Flávio tudo bem?

Não, o responseError não está sendo chamado mas o log "token não enviado" está no terminal.

Atenciosamente,

Renan Lopes

Cole o código completo do seu interceptador.

Além do código completo do seu interceptador, o código completo de Index.html!

Flavio,

Código completo interceptador,

app.factory('tokenInterceptor', function($q, $window, $location){
  var interceptor = {};

  interceptor.request = function(config) {
    config.headers = config.headers || {};
    if($window.localStorage.token){
      console.log('Enviando token já obtido em cada requisição');
      config.headers['x-access-token'] =  $window.localStorage.token;
    }
    return config;
  }

  interceptor.response = function(response){
    var token = response.headers('x-access-token');
    if(token != null){
      $window.localStorage.token = token;
      console.log('Token no local storage: ', token);
    }
    return response;
  }

  interceptor.responseError = function(rejection) {
      if (rejection != null && rejection.status === 401) {
          console.log('Removendo token da sessão')
          delete $window.localStorage.token;
          $location.path("/login");
      } 
      return $q.reject(rejection);
  }

  return interceptor;
});

Atenciosamente,

Quem é app? Em nenhum momento no curso eu declaro app. Sei que é um módulo, mas que módulo? No curso faço angular.module('alurapic') justamente para não ter que declarar uma variável global. Você mudou mais alguma coisa?

Algum método do interceptador é chamado? Se nada é chamado, é porque ele não foi importado corretamente. Já olhou console log do Chrome por mensagens?

Flavio,

Sim, o console do Chrome me retorna essa mensagem apenas,

[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.

Sobre o app é uma variável global definida em main.js

var app = angular.module('app',['ngRoute','services','directives','ngResource']);

app.config(function($routeProvider, $locationProvider, $httpProvider) {

    $httpProvider.interceptors.push('tokenInterceptor');

    $routeProvider.when('/', {
        templateUrl : 'templates/main.html',
    });

    $routeProvider.when('/login', {
        templateUrl : 'templates/forms/login.html',
        controller: "UserController"
    });

...

Suponho que você tenha olhado o log do Chrome. Para que o interceptador não funcione, ou é você ter adicionado no push um interceptador com outro nome ou ter esqueci de importar o script do interceptador. Seu código de back, pelo menos esse trecho que você mostrou esta ok.

Aguardo seu feedback.

Cole o código de index.html também.

Ainda aguardando index.html. Poste o código para eu ver.

Flavio,

Código do Index.html

<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no"/>
  <title>Main View</title>

  <!-- Fonts  -->
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <link href="https://fonts.googleapis.com/css?family=Bevan|Cinzel|Russo+One" rel="stylesheet">
  <!-- CSS -->
  <link href="assets/css/materialize.css" type="text/css" rel="stylesheet" media="screen,projection"/>
  <link href="css/style.css" type="text/css" rel="stylesheet" media="screen,projection"/>
</head>
<body>
  <div id="page-content-wrapper">
    <div ng-view class="view-animate"></div>
  </div>
  <!--  Scripts-->
  <script src="assets/js/jquery.min.js"></script>
  <script src="assets/js/angular.min.js" charset="utf-8"></script>
  <script src="assets/js/angular-route.min.js" charset="utf-8"></script>
  <script src="assets/js/angular-resource.min.js"></script>
  <script src="js/main.js" charset="utf-8"></script>
  <script src="js/controllers/UserController.js" charset="utf-8"></script>
  <script src="js/directives/directives.js" charset="utf-8"></script>
  <script src="js/services/services.js" charset="utf-8"></script>
  <script src="js/services/token-interceptor.js"></script>
  <script src="assets/js/materializecss/materialize.js"></script>
  <script src="assets/js/materializecss/init.js"></script>
  </body>
</html>

Você precisa deixar claro para mim:

a) algum método do seu interceptador é chamado?

b) Você importou corretamente o script do interceptador?

c) Você diz que o log é chamado, mas tem mais alguma coisa diferente que você fez no seu server? Alguma outra mudança?

Flavio,

a) O método de verificação do meu interceptador é chamado e quando envia o token criado também,

b) Sim o import está correto,

c) O código de verificação do token em minha API não teve outra alteração.

Código completo da API

module.exports = function(app) {
  var mongoose =  require('mongoose');

  //--> Segurança

  //--> Criptografia para senhas
  var bcrypt = require('bcrypt');
  const saltRounds = 10;

  //--> Autenticação por Token
  var jwt = require('jsonwebtoken');

  var api = {};
  var model = mongoose.model('User');

  api.autentica = function(req,res){

    console.log(req.body.username);
    console.log(req.body.password);

    model.findOne({username: req.body.username}).then(function(user){
      bcrypt.compare(req.body.password, user.password).then(function(response) {
        if(response == true){
          var token = jwt.sign({user:user.username}, app.get('secret'),{
            expiresIn: 84600
          });
          console.log('Token criado e sendo enviado no header http');
          res.set('x-access-token', token); 
          return res.end();
        }
        else{ 
          console.log('Invalid Username or Password');
          return res.sendStatus(401);
        }
      })
    });
  };

  api.createUser = function(req,res){
    console.log(req.body.password)
    bcrypt.hash(req.body.password, saltRounds,function(err,hash){
      model.create({username : req.body.username,password:hash,email:req.body.email}).then(function(user){
        res.json(user);
        }, function(error){
          console.log(error);
          res.status(401).json(error);
        });
    });
  };

//next so podemos usar com use deixar usar o middleware

  api.verificaToken = function(req, res, next){

    var token = req.headers['x-access-token'];

    if(token){
      console.log('Verificando Token...');
      jwt.verify(token, app.get('secret'), function(err, decoded){
        if (err) {
          console.log('Token Rejeitado');
          return res.sendStatus(401);
        }
        req.user = decoded;
        console.log('Usuario Aprovado');
        next();
      });
    } else {
      console.log('Token não enviado');
      return res.sendStatus(401);
    }
  }
  return api;
};

Agora fiquei confuso. O código que você compartilhou não é igual ao código que você compartilhou no início, Qual código eu me baseio?

O código é igual sim, desculpe. E que há mudanças em outras partes do seu código que não fazem parte do curso.

Você mudou em outro lugar? Tem outras rotas???

Flavio,

É o mesmo, porém esse último é o código completo para uma visão mais ampla do problema,

Atenciosamente,

Você tem esse código no github?

Como você esta tentando acessar a aplicação? Através de qual URL????????? Coloque um exemplo da URL que esta acessando.

Flavio,

Esquema de rotas completo para uma visão mais ampla, estou usando essas rotas para teste,

http://localhost:3000/#/cardlist
var app = angular.module('app',['ngRoute','services','directives','ngResource']);

app.config(function($routeProvider, $locationProvider, $httpProvider) {

    $httpProvider.interceptors.push('tokenInterceptor');

    $routeProvider.when('/', {
        templateUrl : 'templates/main.html',
    });

    $routeProvider.when('/login', {
        templateUrl : 'templates/forms/login.html',
        controller: "UserController"
    });

    $routeProvider.when('/dashboard', {
        templateUrl : 'templates/auth/dashboard.auth.html',
        controller: "UserController"
    });

    $routeProvider.when('/forms', {
        templateUrl : 'templates/forms/forms.form.html',
    });

    $routeProvider.when('/cardlist', {
        templateUrl : 'templates/lists/collections.list.html',
        controller: "UserController"
    });

    $routeProvider.when('/documentation', {
        templateUrl : 'templates/documentation.html',
    });

//    $routeProvider.otherwise ({ redirectTo: '/' });
});

Se você chega no seu browser e coloca direto http://localhost:3000/#/cardlist não esta indo para o login. No Chrome, aparece pelo menos a mensagem not authorized? O que aparece?

O ideal é você colocar o código no github devido as modificações que você fez. Há zilhões de coisas que podem dar errado, por exemplo, ativando o sistema de rotas em uma ordem errada... etc.

Flavio,

Vou disponibilizar o código no GitHub e coloco aqui o link,

Atenciosamente,

OK, você diz que não esta enviando para login, mas pelo menos aparece no console do seu chrome a mensagem not authorired (401). Se não aparecer, é porque por mais que o seu servidor tenha respondido corretamente (pelo menos, você disse que ele exibe o log corretamente) tem algum middleware no servidor que esta zuando com sua resposta.

Pelo código que vi até agora do seus posts esta tudo correto. No entanto, se o rota que você estiver acessando não fizer acesso a API do backend logo na inicialização você não será redirecionado para a página de login. Apenas acesso a API fazem isso.

Talvez isso seja a resposta (mas vc diz que a API é chamada.. então.. não é). Fora isso, só vendo seu código por completo.

Flavio,

Sim, estarei fazendo uma nova análise e logo estarei dando retorno, muito obrigado pelos esclarecimentos,

Atenciosamente,

Renan Lopes

Só compartilhar no GITHUB que eu olho o projeto completo.

Flavio,

Segue código no GitHub, como está em desenvolvimento tem vários ajustes em andamento =)

https://github.com/renanlopescoder/javascript.mean.blog

Atenciosamente,

Renan Lopes

solução!

Oi Renan! Descobri :)

Olha, tive bastante dificuldade em entender seu projeto no estágio que esta (entendo que esta em estágio inicial, tranquilo!), mas o que eu desconfiava se manteve.

(aliás, tinha mais mensagem no log que você não me havia passado, importante para eu entender o problema).

Você tem dois controllers, um que controla o login e outro que controla o resto da sua aplicação que é o DocumenteController.

O único que faz uma requisição Ajax para o servidor é DocumentController, mas ele nem é usado!.

Então, se nenhuma requisição é feita para um recurso protegido do servidor, não será disparado 401. O 401 que é disparado é do favicon que esta sendo buscado e não da sua aplicação. Era essa info que era importante você ter me passado, que aparece no console log do Chrome.

Ou seja, sua aplicação, pelo o que eu entendi, nada faz com o servidor e por isso não é feito o redirecionamento.

Outro ponto, no seu teste, você me passou a URL do cardlist, mas o cardlist pelo o que esta no seu código é para criar usuário e tals. E esse controller do card list não faz requisição para o servidor na inicialização.

Se eu forço o acesso a uma URL protegida em um dos controllers, o responseError é disparado corretamente.

Então, continue a aplicação, depois que ela estiver com tudo no lugar se preocupe com a autenticação. Essa é minha sugestão.

Por fim, vai um alerta que já dei para outros alunos. Só faz criar uma SPA se você trabalha ferrenhamente com componente e tem muito reuso, porque os problemas que você terá que resolver criados pelo SPA são muitos. Cito um deles, o mais importante para um blog que é a indexação feita por robôs de search engine como Google, Bing, etc. O Google até indexa SPA, mas o resultado deixa a desejar. Terá que elaborar uma infraestrutura de pré-renderização que não é trivial de construir.

Flavio,

Sim o código está em desenvolvimento, estarei aplicando todas boas práticas logo, estou montando primeiramente a estrutura, segurança etc. Entendi perfeitamente o problema, muito obrigado, realmente como não estou buscando um recurso protegido pelo servidor não vai dar o retorno de não autorizado.

Atenciosamente,

Renan Lopes

Se quiser se livrar do 401 do favicon que lhe confundiu você pode usar o módulo

https://www.npmjs.com/package/express-favicon

Daí você configura uma resposta com um ícone em uma rota não protegida. Com isso, você verá que o 401 não será mais chamado, porque era o browser, que sempre chama um favicon, tentava acessar algo que não existia.

Esse 401 maroto também me enganou :)

Flavio,

Hahaha pois é não imaginei, vou usar o módulo recomendado, mais uma vez muito obrigado =)

Abraços,