I trying to build an Authentication function in angular with node js as my server,
and i have issues with the identification of the token that sent from angular.
I using the angular HttpInterceptor service to handle with the headers, and i am created a middelware function inside my node js. the error that i receiving is:
{headers: HttpHeaders, status: 401, statusText: "Unauthorized"..
Any help will be appreciated
Middleware function
const jwt = require("jsonwebtoken");
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(" ")[1];
jwt.verify(token, "this_is_the_secret");
next();
} catch (error) {
res.status(401).json({ message: "Auth failed!" });
}
};
Middleware implantation
router.post('/orders', checkAuth, function(req,res,next){
Order.create(req.body.order, function(err, createdOrder){
if (err) return next(err)
.then()
Show.findByIdAndUpdate({"_id": req.body.showId}, {"$push":{"takenSeats": req.body.takenSeatsIds}})
.exec(function(err, updatadShow){
if (err) return next(err)
console.log(updatadShow)
})
res.json(createdOrder)
})
})
Auth service in angular
import { Injectable } from "../../../node_modules/#angular/core";
import { HttpClient } from "../../../node_modules/#angular/common/http";
import { AuthData } from "../models/auth-data.model";
#Injectable({ providedIn: "root"})
export class AuthService {
private token: string
signupUrl = "http://localhost:3000/signup";
loginUrl = "http://localhost:3000/login"
constructor(private http: HttpClient){}
getToken(){
return this.token
}
createUser(email: string, password:string){
const authData: AuthData = {email: email, password: password}
this.http.post(this.signupUrl, authData)
.subscribe(response => {
console.log(response)
});
}
login(email: string, password){
const authData: AuthData = {email: email, password: password}
this.http.post<{token: string}>(this.loginUrl,authData)
.subscribe(response => {
console.log(response)
const token = response.token
this.token = token;
console.log("token" + this.token)
});
}
}
AuthInterceptor service
import { HttpInterceptor, HttpRequest, HttpHandler } from "../../../node_modules/#angular/common/http";
import { Injectable } from "../../../node_modules/#angular/core";
import { AuthService } from "./auth.service";
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService:AuthService){}
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authToken = this.authService.getToken();
const authRequest = req.clone({
headers: req.headers.set('Authorization', "Bearer" + authToken)
})
return next.handle(authRequest)
}
}
[Edit] Sates of the Authentication token
console.log of the login response
{token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6I…Q3OH0.fCgCuHQkDHHgJHq8LFqeVxLayyr-9U-Y6_23_9FGHkU", expiresIn: 3600}
auth.service.ts:35 tokeneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imlnb3JybzYyMUBnbWFpbC5jb20iLCJ1c2VySWQiOiI1YjU4YjYwYTUxMDkyNDI4Njg1MDM3MzIiLCJpYXQiOjE1MzI1NDc4NzgsImV4cCI6MTUzMjU1MTQ3OH0.fCgCuHQkDHHgJHq8LFqeVxLayyr-9U-Y6_23_9FGHkU
console.log of the req.headers.authorization after posting to a route without implementation of the middleware
BearereyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imlnb3JybzYyMUBnbWFpbC5jb20iLCJ1c2VySWQiOiI1YjU4YjYwYTUxMDkyNDI4Njg1MDM3MzIiLCJpYXQiOjE1MzI1NDc4NzgsImV4cCI6MTUzMjU1MTQ3OH0.fCgCuHQkDHHgJHq8LFqeVxLayyr-9U-Y6_23_9FGHkU
console.log of the states inside the middleware function
logging points
const jwt = require("jsonwebtoken");
module.exports = (req, res, next) => {
try {
console.log(" Before the split " + req.headers.authorization)
const token = req.headers.authorization.split(" ")[1];
console.log(" After The split " + req.headers.authorization)
jwt.verify(token, "this_is_the_secret");
next();
} catch (error) {
res.status(401).json({ message: "Auth failed!" });
}
};
Result
Before the split BearereyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imlnb3JybzYyMUBnbWFpbC5jb20iLCJ1c2VySWQiOiI1YjU4YjYwYTUxMDkyNDI4Njg1MDM3MzIiLCJpYXQiOjE1MzI1NDc4NzgsImV4cCI6MTUzMjU1MTQ3OH0.fCgCuHQkDHHgJHq8LFqeVxLayyr-9U-Y6_23_9FGHkU
After The split BearereyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imlnb3JybzYyMUBnbWFpbC5jb20iLCJ1c2VySWQiOiI1YjU4YjYwYTUxMDkyNDI4Njg1MDM3MzIiLCJpYXQiOjE1MzI1NDc4NzgsImV4cCI6MTUzMjU1MTQ3OH0.fCgCuHQkDHHgJHq8LFqeVxLayyr-9U-Y6_23_9FGHkU
Ok. I found up what was the problem, I didnt put space after the "Bearer "
In the HttpInterceptor
Related
In am having trouble passing the token information to my controller for authorization. In the below code, I have console logged the relevant information and I am able to get the token correctly, and the decoded information, but not the req.user information. When I console.log for that information I receive null, and when I console.log for decoded.id, I get undefined. I believe this is what is hanging up my authorization, however I'm not sure what to look at to fix it? Any thoughts very helpful!
Here's a github link:https://github.com/roxanneweber/projectmanager
const jwt = require('jsonwebtoken');
const asyncHandler = require('express-async-handler');
const User = require('../models/userModel');
const protect = asyncHandler(async (req, res, next) => {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
try {
// Get token from header
token = req.headers.authorization.split(' ')[1];
console.log(token);
// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log(decoded);
// Get user from token
req.user = await User.findById(decoded.id).select('-password');
console.log(req.user);
console.log(decoded.id);
next();
} catch (error) {
console.log(error);
res.status(401);
throw new Error('Not authorized');
}
}
if (!token) {
res.status(401);
throw new Error('Not authorized');
}
});
module.exports = { protect };
hi i am handling the token like this in my auth.middleware.ts
import jwt from 'jsonwebtoken';
export default function (req, res, next) {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
// add userData object to request
req.userData = {
email: decodedToken.email,
userId: decodedToken.userId,
username: decodedToken.username,
role: decodedToken.role,
};
next();
} catch (error) {
return res.status(401).json({
message: 'not authenticated',
});
}
}
then my frontend handles setting the token like this:
import { HttpInterceptor, HttpRequest, HttpHandler, HttpHeaders } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { AuthenticationService } from '../services/authentication.service';
export interface HttpConfig {
body?: any;
headers?: HttpHeaders;
observe?: any;
}
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthenticationService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authToken = this.authService.getToken();
const authRequest = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + authToken),
});
return next.handle(authRequest);
}
}
then i am using the auth.middleware.ts in my backend route files like this:
import express from 'express';
import authMiddleware from '../middleware/auth.middleware';
import FooController from './foo.controller';
class FooRoutes {
router = express.Router();
fooController = FooController;
constructor() {
this.configureRoutes();
}
configureRoutes() {
this.router.post('/foo/start', authMiddleware, this.fooController.start);
this.router.put('/foo/stop/:id', authMiddleware, this.fooController.stop);
this.router.get('/foo/:userId', authMiddleware, this.fooController.getAll);
this.router.delete('/foo/delete/:id', authMiddleware, this.fooController.delete);
}
}
export default new FooRoutes().router;
the Authorization header is always undefined when I am trying to login. Tried setting AOT = false in my angular.json file but to no avail. What is the issue here?
Auth-Interceptor
import { HttpHandler, HttpInterceptor, HttpRequest } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { AuthService } from "./auth.service";
#Injectable()
export class AuthInterceptor implements HttpInterceptor{
constructor(private authService: AuthService){}
intercept(req: HttpRequest<any>, next: HttpHandler){
const authToken = this.authService.getToken();
const authRequest = req.clone({
headers: req.headers.set("Authorization", "Bearer " + authToken)
})
return next.handle(authRequest)
}
}
checkAuth middleware on backend
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try{
const token = req.headers.authorization.split(" ")[1]
jwt.verify(token, "asderfghtyu")
next();
}
catch(error) {
res.status(401).json({
message: "Authorization failed macha"
})
}
}
auth-service.ts
export class AuthService {
private token: string;
constructor(private http: HttpClient) { }
getToken(){
return this.token;
}
createUser(FullName: string, email: string, role: string, password: string) {
const authData: AuthData = { FullName: FullName, email: email, role: role, password: password }
this.http.post("http://localhost:3300/api/user/signup", authData)
.subscribe(result => {
console.log(result);
})
}
login(email: string, password: string){
this.http.post<{token: string}>("http://localhost:3300/api/user/login", {email: email, password: password})
.subscribe(response=>{
const token = response.token;
this.token = token;
})
}
Here is the auth-service.ts file where getToken() function is present
In your Auth-Interceptor, replace -
const authRequest = req.clone({
headers: req.headers.set("Authorization", "Bearer " + authToken)
})
with -
const authRequest = !authToken ? req : req.clone({
setHeaders: { Authorization: `Bearer ${authToken}` }
});
This will add an Authorization header only if you have a token. If authService.getToken() returns undefined, no Authorization header will be added to the request.
I am trying to learn backend and frontend and so far I was okey. But now I have a problem.
In my backend which I use Express, I send cookie with res.cookie as shown below
auth.js (/auth route)
const register = asyncErrorWrapper(async (req, res, next) => {
const {name, email, password} = req.body;
const user = await User.create({
name,
email,
password
});
sendJwtToClient(user, res);
});
tokenHelper.js
const sendJwtToClient = (user, res) => {
// user modeline gore jwt olusturma
const token = user.generateJwtFromUser();
const {JWT_COOKIE, NODE_ENV} = process.env;
return res
.status(200)
.cookie('access_token', token, {
httpOnly:true,
expires: new Date(Date.now() + parseInt(JWT_COOKIE) * 1000 * 60),
secure: NODE_ENV === 'development' ? false : true
})
.json({
success: true,
access_token: token,
data: {
name: user.name,
email: user.email
},
message: 'Your registration is succesful!'
});
};
Then I get response with Angular app in register.component which is connected with Auth Service
from Register.component
registerUser() {
this.sendButton.nativeElement.disabled = true;
this._authService.registerUser(this.user).subscribe(
res => {
if(res.status === 200) {
console.log(res);
}
}, err => {
console.log(err);
this.showError.nativeElement.className += ' alert alert-danger';
this.errMessage = err.error.message;
this.sendButton.nativeElement.disabled = false;
})
}
Auth Service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class AuthService {
_registerURI = 'http://localhost:3000/auth/register';
_loginURI = 'http://localhost:3000/auth/login';
constructor(private http: HttpClient) { }
registerUser(user) {
return this.http.post<any>(this._registerURI, user, {observe: 'response'});
};
loginUser(user) {
return this.http.post<any>(this._loginURI, user, {observe: 'response'});
};
};
Well I get this response when I click register button: response
Btw I use cors package on backend. Thanks in advance.
I can also share other codes, pages whatever you want. I just thought that this is enough.
Okey, got it.
The problem occurred because of the cors policy. To send cookies to the front-end you need to let in cors package.
Here is the options of cors package that used by express:
app.use(cors( {
origin: 'http://localhost:4200',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
} ));
And this is the Auth Service method posting registration
registerUser(user) {
return this.http.post<any>(this._registerURI, user, {observe: 'response', withCredentials: true});
};
I'm trying to store data into /profile after logging in. However, I keep getting these 2 errors in the console after logging in.
"VM276:1 GET http://localhost:4200/user/profile 401 (Unauthorized)"
and "HttpErrorResponse {headers: HttpHeaders, status: 401, statusText: "Unauthorized"".
Since i'm following different tutorials to work this out so I know that I've made some mistake with the token code, I just don't know how to fix it. And I would like to thank anyone who's spending time to help! This is my authentication.service.ts
import { Injectable } from '#angular/core';
import {HttpClient} from "#angular/common/http";
import {Observable, of} from 'rxjs';
import { map } from 'rxjs/operators';
import {Router} from '#angular/router';
export interface UserDetails{
username: string
email: string
password: string
exp: number
iat: number
}
interface TokenResponse{
token: string
}
export interface TokenPayload{
username: string
email: string
password: string
}
#Injectable({
providedIn: 'root'
})
export class AuthenticationService {
private token: string
constructor(private http: HttpClient, private router: Router) {}
private saveToken(token: string): void{
localStorage.setItem('usertoken', token)
this.token = token
}
private getToken(): string{
if(!this.token){
this.token = localStorage.getItem('usertoken')
}
return this.token
}
public getUserDetails(): UserDetails{
const token = this.getToken()
let payload
if(token){
payload = token.split('.')[1]
payload = window.atob(payload)
return JSON.parse(payload)
}else{
return null
}
}
public isLoggedIn(): boolean{
const user = this.getUserDetails()
if(user){
return user.exp > Date.now()/ 1000
}
else{
return false
}
}
public login(user: TokenPayload): Observable<any>{
const base = this.http.post('/user/login', user)
const request = base.pipe(
map((data: TokenResponse) => {
if(data.token){
this.saveToken(data.token)
}return data
})
)
return request
}
public register(user: TokenPayload) : Observable<any>{
const base = this.http.post('/user/register', user)
const request = base.pipe(
map((data: TokenResponse) => {
if(data.token){
this.saveToken(data.token)
}
return data
})
)
return request
}
public profile(): Observable<any>{
return this.http.get('/user/profile',{
headers: {Authorization: `${this.getToken()}`}
})
}
public logout(): void{
this.token = ''
window.localStorage.removeItem('usertoken')
this.router.navigateByUrl('/')
}
}
This is my profile.component.ts
export class ProfileComponent implements OnInit {
details: UserDetails
constructor(private auth: AuthenticationService) {}
ngOnInit() {
this.auth.profile().subscribe(
user => {
this.details = user
},
err => {
console.error(err)
}
)
}
}
this is my auth.js
const jwt = require("jsonwebtoken");
module.exports = function(req, res, next) {
const token = req.header("token");
if (!token) return res.status(401).json({ message: "Auth Error" });
try {
const decoded = jwt.verify(token, "randomString");
req.user = decoded.user;
next();
} catch (e) {
console.error(e);
res.status(500).send({ message: "Invalid Token" });
}
};
and this is my /routes/user.js
// Filename : user.js
const express = require("express");
const {check, validationResult} = require("express-validator");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const router = express.Router();
const auth = require("../middleware/auth");
const User = require("../model/User");
/**
* #method - POST
* #param - /signup
* #description - User SignUp
*/
router.post(
"/register",
[
check("username", "Please Enter a Valid Username")
.not()
.isEmpty(),
check("email", "Please enter a valid email").isEmail(),
check("password", "Please enter a valid password").isLength({
min: 6
})
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const {
username,
email,
password
} = req.body;
try {
let user = await User.findOne({
email
});
if (user) {
return res.status(400).json({
msg: "User Already Exists"
});
}
user = new User({
username,
email,
password
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
"randomString", {
expiresIn: 10000
},
(err, token) => {
if (err) throw err;
res.status(200).json({
token
});
}
);
} catch (err) {
console.log(err.message);
res.status(500).send("Error in Saving");
}
}
);
router.post(
"/login",
[
check("email", "Please enter a valid email").isEmail(),
check("password", "Please enter a valid password").isLength({
min: 6
})
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const { email, password } = req.body;
try {
let user = await User.findOne({
email
});
if (!user)
return res.status(400).json({
message: "User Does Not Exist"
});
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch)
return res.status(400).json({
message: "Incorrect Password!"
});
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
"randomString",
{
expiresIn: 3600
},
(err, token) => {
if (err) throw err;
res.status(200).json({
token
});
}
);
} catch (e) {
console.error(e);
res.status(500).json({
message: "Server Error"
});
}
}
);
/**
* #method - POST
* #description - Get LoggedIn User
* #param - /user/me
*/
router.get("/profile", auth, async (req, res) => {
try {
// request.user is getting fetched from Middleware after token authentication
const user = await User.findById(req.user.id);
res.json(user);
} catch (e) {
res.send({ message: "Error in Fetching user" });
}
});
module.exports = router;
In your angular app you are sending the token with the authorization header
Authorization: token
But in your API (auth.js), you are trying to access the token header
req.header("token")
Replace you api code with
req.header("authorization")
so I have tested my API route /getUser using Postman which is receiving data successfully being received in json format. However the Jwt token which is stored in localstorage and past in the headers is not being verified as within my browser I receive 'Access denied. No JWT provided' which is sent with a 401 status.
Nodejs API is below this includes my authentication route and /getUserwhich users a middleware file found below also which verifies the token
const express = require('express');
const users = express.Router();
const cors = require('cors');
const moment = require('moment');
const jwt = require('jsonwebtoken');
// var exjwt = require('express-jwt');
const auth = require('../middleware/auth');
const bcrypt = require('bcrypt');
const Sequelize = require('sequelize');
const bodyParser = require('body-parser');
const User = require('../models/User');
const config = require('config');
// const secret = 'dassdfdd';
users.use(
bodyParser.urlencoded({
extended: false
})
);
users.use(bodyParser.json());
users.use(cors());
users.post('/authenticate', (req, res) => {
User.findOne({
where: {
email: req.body.email
}
}).then(user => {
if (user) {
if (bcrypt.compareSync(req.body.password, user.password)) {
const payload = {
id: user.id,
name: user.first_name
};
var token = jwt.sign(payload, config.get('secret'), {
expiresIn: 1440 // expires in 24 hours
});
// res.cookie('auth', token);
res.cookie('jwt', token, { httpOnly: true, secure: true });
// return the information including token as JSON
// // res.setHeader('token', token);
// res.setHeader('Authorization', 'Bearer ' + token);
res.send({
message: 'authentication done ',
token: token,
user: user.toJSON()
});
console.log(token);
console.log('Successful Login');
console.log(user.first_name);
} else {
res.json({ message: 'please check your password !' });
console.log('incorrect password');
}
} else {
res.json({ message: 'user not found !' });
console.log('user cannot be found');
}
});
});
users.get('/protected', (req, res) => {
res.send('protected');
});
users.get('/getUser', auth, function(req, res) {
// const currentUser = req.;
// const id = parseInt(req.params.id);
const users = User.findOne({
where: { id: req.user.id }
});
// }
// });
// if (!users) {
// return res.status(404).send('Cannot find your team players');
// }
console;
res.status(200).json(users);
});
module.exports = users;
Login Component
import React, { Component } from 'react';
import axios from 'axios';
import { withRouter } from 'react-router-dom';
class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
// const user = {
// email: this.state.email,
// password: this.state.password
// };
axios
.post('http://localhost:5000/api/authenticate', {
email: this.state.email,
password: this.state.password
})
.then(res => {
localStorage.setItem('token', res.data.token);
this.props.history.push('/Profile');
});
}
auth.js this is my middleware file
const jwt = require('jsonwebtoken');
const config = require('config');
module.exports = function(req, res, next) {
const token = req.header('Authorization');
if (!token) {
return res.status(401).send('Access denied. No JWT provided.');
}
try {
const decoded = jwt.verify(token, config.get('secret'));
res.set('Authorization', token);
req.user = decoded;
next();
} catch (ex) {
res.status(400).send('Invalid JWT.');
}
};
Profile Component( this is the page , i want the users data to appear)
import React, { Component } from 'react';
import { getJwt } from '../helpers/jwt';
import axios from 'axios';
import { withRouter } from 'react-router-dom';
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
// users: []
};
}
componentDidMount() {
const jwt = getJwt();
if (!jwt) {
this.props.history.push('/Login');
}
axios
.get('http://localhost:5000/api/getUser', {
headers: { Authorization: `Bearer ${jwt}` }
})
.then(res => {
this.profile = res.data;
console.log('profile is:', res.data);
})
.catch(error => console.log(error));
}
Inside your auth.js middleware file, you have const token = req.header('Authorization');. This includes the Bearer prefix which is not part of the JWT itself, and will need to be removed before the token can be parsed by the JWT library.
The Bearer prefix identifies the token type as a Bearer token under the OAuth 2.0 Authorization Framework. If you wish to support other token types the prefix will be different to identify the respective type and framework.