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

Tela de login - Unauthorized

Terminei o curso e realizei exatamente todo o passo a passo do último capítulo sobre autenticação.

Porém, quando acesso a página de login (/login) e simplesmente atualizo (sem preencher nada), ela aparece a mensagem "Unauthorized".

Como solucionar esse problema?

19 respostas

Difícil saber onde está o problema em seu código só com essa informação. Quando você acessa a página de login e clica sem preencher nada você diz que exibe a mesangem Unauthorized. Mas é no navegador ou no servidor?

Quando você efetua o login corretamente, tudo funciona conforme o esperado?

Até agora quem já terminou o curso passou sem problemas nesta parte, mas não se preocupe, a gente descobre o que há com seu código.

Aparece uma tela branca apenas escrito "Unauthorized" no navegador.

Vou baixar o projeto final e testar se não acontece o mesmo!!

A gente descobre! Já tenho uma ideia. O problema está na criação do interceptador. Ele parece não estar ativado. Veja o console do navegador também, veja se alguma mensagem de erro é exibida.

Você pode verificar se no main.js você ativou o interceptador? É legal descobrirmos oproblema para você ficar ainda melhor em MEAN. Pode ficar tranquilo.. basta só você ir me passando as informações.

Aliás, pode colocar o código do seu main.js e o código do seu intercepador?

Eu já fiz algumas modificações acompanhando o curso. Esse é o main.js (/src/public/assets/js/main.js)

angular.module('areapetsite', ['ui.bootstrap', 'ngRoute', 'ngResource'])
    .config(function($routeProvider, $locationProvider, $httpProvider) {

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

        $locationProvider.html5Mode(true);

        $routeProvider.when('/', {
            templateUrl: 'partials/home.html'
        });

        $routeProvider.when('/login', {
            templateUrl: 'partials/login.html',
            controller: 'LoginController'
        });


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

    });

Esse é o arquivo /src/app/api/auth.js

module.exports = function(app){

    var mongoose = require('mongoose');
    var jwt = require('jsonwebtoken');

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

    api.authenticate = function(req, res) {

        model
            .findOne({
                login: req.body.login,
                password: req.body.password
            })
            .then(function(user) {

                if(!user) {
                    console.log('Login e/ou senha inválidos.');
                    res.sendStatus(401);
                } else {
                    var token = jwt.sign( {login: user.login}, app.get('secret'), {
                        expiresIn: 86400 // valor em segundo, aqui temos um total de 24 horas
                     });

                     console.log('Token criado e sendo enviado no header de resposta');
                     res.set('x-access-token', token);
                     res.end();
                }

            }, function(error) {
                console.log('Login e/ou senha inválidos.');
                res.sendStatus(401);

            });
    };


    api.verifyToken = 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 inválido');
                    res.sendStatus(401);
                }

                req.user = decoded;
                next(); //avisa pro express que o próximo middleware pode ser carregado
            });    
        } else {

            console.log('Token não foi enviado');
            res.sendStatus(401);
        }
    };

    return api;
};

Você pode verificar e ter certeza absoluta que o arquivo com o código do interceptador está sendo carregado em index.html? Por exemplo, Aliás, tem como colar o código dele?

Veja que você faz em main.js:

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

O Interceptador tem que estar sendo carregado. Aliás, alguma mensagem no log do seu chrome é exibida, geralmente em vermelho no console?

Eu to achando que o erro está na rota e não no interceptor. Antes de implementar o auth, estava com um erro assim:

Quando eu clicava no link da página /login ele carrega tranquilo, porém, quando digitava manualmente a rota na URL (localhost:3000/login), aparecia "CANNOT GET /login"

De qualquer forma, o erro que aparece bem rápido e já some no console, é esse:

http://prntscr.com/al5pbk

Pablo, eu pude verificar que você fez o curso de Angular do Alura, mas não fez todos os exercícios. Aliás, pulou praticamente todos eles. Olha, os exercícios são importantes, ainda mais que no curso de MEAN você volta ver o conteúdo de Angular.

Aliás, no curso de MEAN você também não fez nenhum exercício.

Com base nisso, a gente sempre aqui fica na dúvida se o Aluno conseguiria descobrir o problema sozinho se tivesse feitos os exercícios.

Bom, no seu caso, aguardo seu feedback sobre o interceptador para ver se realmente é algum problema nele. . Poste o código aqui.

Mas de coração, o ideal é que você faça sempre os exercícios. Sei que muitas vezes o aluno vem seco pelo treinamento e quer ir logo ir implementando a aplicação. Contudo, se o atalho fosse o melhor caminho ele seria o melhor caminho, não é mesmo?

Na index estou chamando o arquivo, tranquilo!

<script src="assets/js/services/token-interceptor.js"></script>

Aqui está o código do interceptor /public/assets/js/services/token-interceptor.js

angular.module('areapetsite')
    .factory('tokenInterceptor', function($window, $q, $location) {

        var interceptor = {};

        interceptor.response = function(response) {

            var token = response.headers('x-access-token');
            if(token) {
                //sessionStorage = fechou a aba do site, perde o token
                //localStorage = fechar e abrir o navegador, continua o token gravado
                $window.sessionStorage.token = token;
                console.log('Token armazenado no navegador');
            }

            return response;
        };

        interceptor.request = function(config) {

            config.headers = config.headers || {};
            if($window.sessionStorage.token) {
                config.headers['x-access-token'] = $window.sessionStorage.token;
                console.log('Adicionando token no header da requisição para ser enviado para o servidor');
            }

            return config;
        };

        interceptor.responseError = function(rejection) {

            if(rejection != null && rejection.status == 401) {
                delete $window.sessionStorage.token;
                $location.path('/login'); 
            }

            return $q.reject(rejection);
        };

        return interceptor;
    });

Só preciso entender melhor o fluxo: você diz que abre a aplicação Angular, aparece a página de login e quando efetua recebe a mensagem Unothorized?. Se o seu interceptador tivesse funcionando, você seria redirecionando novamente para a página de login.

Aliás, estou reparando na estrutura do seu projeto que algumas módulos mudaram de nome.. locais de assets.

Isso não é o projeto do curso, está me parecendo um projeto pessoal. Certo?

Se for, você precisa verificar zilhões de coisas. Ver se o módulo que vc declarou seu interceptador é importado na lista de dependências do main, se outras alterações que você impactaram no projeto.

Eu sempre peço para o aluno terminar o curso antes, com o projeto do curso.Porque se ele começa a "inventar", o que é algo justo, as vezes perco semanas ou dias tentando entender a aplicação do aluno. Mas quando ele termina a aplicação do zero e depois "inventa", ele tem certeza que é o código dele e não o que ele aprendeu foi aplicado indevidamente.

Agora, não sei. Vou passar checklist ai para você verificar:

1 - Mudou nome dos módulos? Cada módulo está sendo importando corretamente via script e inclusive sendo adicionando na lista de dependências de main?

2- Alterou rotas? Está seguindo uma ordem de precedência?

3 - Alterou nome dos controller? Viu se está referênciando todos eles corretamente>

O seu problema é um só: seu interceptador não está funcionando, ou a rota no lado do cliente /login não existe ou tá zikada. Só isso que consigo enxergar por agora.

Passou por essa checklist Pablo?

Eu estou me virando aqui. Uma coisa que eu reparei é que no curso de angular, é ensinado sobre retirar a # da URL, através do:

$locationProvider.html5Mode(true);

Porém, no curso de MEAN, o mesmo arquivo (main.js) não possui mais essa linha.

Após refatorar o meu projeto e remover isso, acabou funcionando!

Porém, no projeto do alurapic, se eu remover o # e colocar direto o /login, aparece a mesma tela, escrito "unauthorized".

Existe alguma forma de corrigir isso?

Não quero obrigar que as url utilizem o #.

Obrigado.

É isso mesmo. No curso de MEAN, a aplicação Angular que é fornecida faz uso do #. Qual o motivo disso, você deve estar se perguntando se no curso de Angular aprendemos a criar rotas no client sem o #.

O problema é que se o Aluno cai de paraquedas no curso de MEAN sem ter feito o de Angular (pré-requisito) ele não saberá diferenciar uma rota do client de uma API do servidor. E mesmo quem saiba a diferença, voltar com o # ajuda ao aluno a separar bem as duas coisas. POr fim, outro motivo é a compatibilidade de todos os navegadores. Quando o navegador não suporta o HTML5Mode, ele volta a usar a #. Se o aluno que cai de paraquedas no MEAN vê uma explicação sem o # e depois aparece o # em seu pobre navegador ele ficaria confuso. Foi só por causa disso.

No seu caso, quando você digita localhost:3000/login não vai funcionar, ele vai achar que é uma rota do servidor, porque ele está esperando a rota como localhost:3000/#/login. Agora entendo porque você acessando rota de login direto estava dando não autorizado.

Contudo, se o seu interceptador estivesse funcionando, qualquer parcial que você acessasse que fizesse uso de Ajax o redirecionadia para login.

Então, quando não logado, se você acessar localhost:3000 ele te redireciona para localhost:3000/#/login. Você pode querer usar o HTML5Mode, mas tem que se garantir que não vai confundir as coisas durante o processo.

Quando usamos HTML5Mode, o servidor precisa sempre retornar para qualquer rota index.html. Como assim? Se você acessa o endereço localhost:3000/fotos ele retorna uma lista de fotos, certo? Mas se você acessar uma URL que não existe? Sempre tem que ser devolvia o index.html.

Você pode ver no projeto do curso de Angular lá no arquivo de configuração do express como faz para retornar sempre a página index.html para rotas inexistentes.

Mas eu explico tudo isso no curso de Angular, sobre o # e como não usar. Aliás, reforço isso nos exercícios.

Então, o que você precisa fazer é ter certeza absoluta se o seu interceptador está sendo chamado (coloca um console.log em todas as chamadas) e olhe no terminal se ele está imprimindo. Depois, voltar com o # e verificar se tudo funciona. Tudo funcionando, dai você pode tentar voltar para o HTML5Mode Aliás, muitas empresas como MS, Twitter, e Google ainda usam o # como padrão.

Espero ter esclarecido parte do problema aqui.

solução!

Eu vou colocar o trecho do código que ativa o HTML5 no server, como seu código está funcionando, você pode tentar agora, mas atenção!, essa rota tem que ser adicionada depois do carregamento de rotas com o consign. E atenção novamente, se você mudou a estrutura de diretório tem que ajustar path.resolve! :)

// recarregamento de rotas com cosign antes

    // habilitando HTML5MODE
    app.all('/*', function(req, res) {
        res.sendFile(path.resolve('public/index.html'));
    });

:) Aguardo seu feedback!

É isso mesmo! Obrigado.

Beleza! Mas testa lá e depois passa o feedback para a gente!

E mais uma coisa (sou chato, né?)! Faz os exercícios pelo menos do MEAN. Tem muito conteúdo e explicação extra de REST, API, ETC lá. E o certificado só sai, se não me engano, se você termina todos os exercícios. Eu investi zilhões de horas elaborando todos eles, você vai gostar! :)

Estou meio sem tempo pois estou no meio de um projeto, mas assim que der irei fazer. Obrigado!!

Olá Flávio e Pablo... Estou com o mesmo problema e já tinha identificado que era por causa $locationProvider.html5Mode(true);

Porém não consegui solucionar o problema.

Eu já utilizava o:

app.all('/*', function(req, res) { res.sendFile(path.resolve('public/index.html')); });

Ele ficava junto com as rotas do servidor, em routes/index.js

Após criar a autenticação do token com o:

app.use( '/*', api.verificaToken );

Ele parou de ler o app.all, o que faz sentido, claro.

O Flávio pediu para inserir após o carregamento de rotas do Consign, mas também tentei e não consegui nenhum resultado. Eu inseri no express.js após a chamada das rotas. A ordem das chamadas das rotas pelo Consign estão exatamente iguais a do curso. Talvez tenha feito de várias formas erradas.

Poderiam me explicar melhor o local onde ficará o res.sendFile(path.resolve('public/index.html')) ?

Obrigado!