Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

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,