I have an express backend that uses express-sessions. I have an angular front-end that is unconnected to the backend. Everytime a my frontend makes a request express-sessions creates a new session. I cant figure out why. I think the issue might be angular not sending the cookie back. I dont see any cookies in req headers in the chrome network tab. I dont know why angular would be doing this or if its even the problem. Any help would be much appreciated!Below is all relevant code
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {NgbModule} from '#ng-bootstrap/ng-bootstrap';
import {HttpClientModule, HTTP_INTERCEPTORS} from '#angular/common/http';
import {FormsModule} from '#angular/forms';
import { NavbarComponent } from './navbar/navbar.component';
import { SignUpComponent } from './sign-up/sign-up.component';
import { LogInComponent } from './log-in/log-in.component';
import { WithCredentialsInterceptorService } from './with-credentials-interceptor.service';
#NgModule({
declarations: [
AppComponent,
NavbarComponent,
SignUpComponent,
LogInComponent
],
imports: [
BrowserModule,
AppRoutingModule,
NgbModule,
FormsModule,
HttpClientModule
],
providers: [{provide : HTTP_INTERCEPTORS, useClass : WithCredentialsInterceptorService,
multi : true}],
bootstrap: [AppComponent]
})
export class AppModule { }
angular interceptor
import { Injectable } from '#angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '#angular/common/http';
#Injectable()
export class WithCredentialsInterceptorService implements HttpInterceptor{
intercept(req : HttpRequest<any>, next : HttpHandler){
let modifiedReq = req.clone({headers : req.headers.set('withCredentials', 'true')});
console.log(req.headers.get('cookie'));
return next.handle(modifiedReq);
}
constructor() { }
}
app.js
const express = require('express');
const app = express();
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const corsOptions = {
credentials : true
};
const session = require('express-session');
const mongoDbStore = require('connect-mongodb-session')(session);
const store = new mongoDbStore({
uri : 'mongodb+srv://Shlomo:Shizam11192#cluster0-32bfg.mongodb.net/test?retryWrites=true&w=majority',
databaseName : 'test',
collection : 'sessions'
},error=>{
if(error){
console.log(error);
}
});
const authRoutes = require('./routes/authRoutes');
app.options('*', cors(corsOptions));
app.use(cors(corsOptions));
app.use(bodyParser.json());
app.use(session({
secret : 'You are doing great Shlo!!!',
resave : false,
saveUninitialized : true,
cookie : {
maxAge : 3600000,
secure : false,
httpOnly : false
}
}));
app.use(authRoutes);
mongoose.connect('mongodb+srv://#cluster0-32bfg.mongodb.net/test?
retryWrites=true&w=majority')
.then(()=>{console.log('mongoose connected!')});
app.listen(process.env.PORT || 3000, ()=>{
console.log('Listening!!! Keep it up Shlo!!!');
});
auth routes
router.get('/', (req,res,next) =>{console.log(req.session.id);res.json({message :
'whatever'})});
˚
in the interceptor the code should be
req.clone({withCredentials : true});
for some reason the headers.set doesnt work
Related
I am using the npm cors package with my react app and express (OOP approach), but I still get the CORS error:
Access to XMLHttpRequest at 'http://localhost:8000/api/auth/authenticate' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have cors() set up on the backend, at localhost:8000. I am calling the api with { withCredentials: true } on the front end, at localhost:3000.
So I am not sure where did it go wrong.
My setup below. Thanks,
Backend - express.middleware.ts
import cors from "cors";
import express from "express";
module.exports = (app: any) => {
app.use(
cors({
origin: "http://localhost:3000",
methods: ["POST", "PUT", "GET", "OPTIONS", "HEAD"],
credentials: true,
})
);
app.use(express.static("public"));
};
Backend - app.ts
import express from "express";
import dotenv from "dotenv";
import path from "path";
class App {
private _app: express.Application;
private readonly _port: number | string = process.env.PORT || 8000;
constructor(controllers: any[]) {
this._app = express();
dotenv.config();
this.initializeControllers(controllers);
this.initializeMiddleWares();
}
public start() {
this._app.listen(this._port, () => {
console.log(`App listening on the port ${this._port}`);
});
}
private initializeControllers(controllers: any[]) {
controllers.forEach((controller) => {
this._app.use("/api", controller.router);
});
}
public initializeMiddleWares() {
require("./src/middleware/express.middleware")(this._app);
}
}
export default App;
Backend - server.ts
import App from "./app";
import AuthController from "./src/modules/auth/auth.controller";
import { AuthService } from "./src/modules/auth/auth.service";
const server = new App([new AuthController(new AuthService())]);
server.start();
Frontend - useGet.ts(custom hook to call api)
import React from "react";
import { useHistory } from "react-router-dom";
import axios from "axios";
import { server_url } from "../constants";
const useGet = () => {
const history = useHistory();
const doGet = (path: string, cb?: Function) => {
axios
.get(`${server_url}${path}`, { withCredentials: true })
.then((response) => {
if (cb) {
cb(response.data);
}
})
.catch((err) => {
console.log(err);
});
};
return [doGet];
};
export default useGet;
Initialize the middleware before configuring the routes.
From the docs:
If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.
In your case, the controllers do end the request-response cycle, thus the middleware never gets to act.
To fix it, change this:
class App {
// ...
constructor(controllers: any[]) {
// ...
this.initializeControllers(controllers);
this.initializeMiddleWares();
}
into this (notice the order of the lines):
class App {
// ...
constructor(controllers: any[]) {
// ...
this.initializeMiddleWares();
this.initializeControllers(controllers);
}
And you should be good to go.
I am trying to add SSR to my web site but the content is not loaded. I am using Angular Universal, I followed this guide for the initial configuration. http://localhost:4200/ it is not finishing loading and no errors been shown. http://localhost:4200/index.html is returning a empty view.
The build process successfully.
server.ts
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '#nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
const path = require('path');
const fs = require('fs');
const domino = require('domino');
const templateA = fs.readFileSync(path.join('dist/web/browser', 'index.html')).toString();
const win = domino.createWindow(templateA);
global['window'] = win;
global['document'] = win.document;
// Express server
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '#angular/common';
import { existsSync } from 'fs';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/web/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index.html';
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
tsconfig.server.json
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "./out-tsc/server",
"target": "es2016",
"types": [
"node"
],
"module": "commonjs"
},
"files": [
"src/main.server.ts",
"server.ts"
],
"angularCompilerOptions": {
"entryModule": "./src/app/app.server.module#AppServerModule"
}
}
I found a solución.
I followed this steps to realize what was wrong:
Comment all imports and routings from app.module.ts, I just leve the app.component.html with a simple tag just to show something. In this step the view start loading.
Add module per module and see where stops loading.
Where stops I comment line per line to see where is the mistake.
The main mistakes that I found was the order of the routing imports on app.module.ts and sub-modules,
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
FormsModule,
ReactiveFormsModule,
BrowserAnimationsModule,
HttpClientModule,
AppRoutingModule, // <-- here
],
providers: [{ provide: HTTP_INTERCEPTORS, useClass: RequestInterceptorService, multi: true }],
bootstrap: [AppComponent]
})
export class AppModule {}
another was that in some constructor components I was doing an api call but fails because the authentication. So I need to add a validation for only do it if is a platform browser:
constructor(#Inject(PLATFORM_ID) private platformId: Object) {
if (isPlatformBrowser(this.platformId)) {
// api call
}
}
Fortunately my project it isn't very big yet.
So I'm trying to implement server side rendering on my angular 4 website and I think I'm pretty close. I have gotten all routes to render server side except for 1 specific route and the default route. The routes that do not work are /home, /anythingthatisntdefined, and (no route at all loads, but does not server side render the home page).
So the no route, just isn't getting picked up by my catch all route... the anything that isn't defined I'm assuming the server is not correctly processing the default route... and the /home route not loading, just doesn't make sense to me. Any tips?
Here is my server.ts
import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { platformServer, renderModuleFactory } from '#angular/platform-server';
import { enableProdMode } from '#angular/core';
import { AppServerModuleNgFactory } from '../dist/ngfactory/src/app/app.server.module.ngfactory';
import * as express from 'express';
import { readFileSync } from 'fs';
import { join } from 'path';
const PORT = 4000;
enableProdMode();
const app = express();
let template = readFileSync(join(__dirname, '..', 'dist', 'index.html')).toString();
app.engine('html', (_, options, callback) => {
console.log('url: ', options.req.url);
const opts = { document: template, url: options.req.url };
renderModuleFactory(AppServerModuleNgFactory, opts)
.then(html => callback(null, html));
});
app.set('view engine', 'html');
app.set('views', 'src')
app.use(express.static(join(__dirname, '..', 'dist')));
app.get('*', (req, res) => {
console.log('caught by *');
res.render('../dist/index.html', {
req: req,
res: res
});
});
app.listen(PORT, () => {
console.log(`listening on http://localhost:${PORT}!`);
});
Here is my router
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { ServicesComponent } from './services/services.component';
import { ResourcesComponent } from './resources/resources.component';
import { ChurchesComponent } from './churches/churches.component';
import { GetAQuoteComponent } from './get-a-quote/get-a-quote.component';
const routes: Routes = [
{
path: 'home',
component: HomeComponent
},
{
path: 'services',
component: ServicesComponent
},
{
path: 'resources',
component: ResourcesComponent
},
{
path: 'about',
component: AboutComponent
},
{
path: 'churches',
component: ChurchesComponent
},
{
path: 'get-a-quote',
component: GetAQuoteComponent
},
{
path: '**',
component: HomeComponent
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Good news, I figured out why the one route/ random routes weren't loading. The ngx-bootstrap carousel (which I had on my home route) has an interval that needs to be disabled server side. Otherwise the route will hang forever and never load.
Reference: https://github.com/valor-software/ngx-bootstrap/issues/1353
And last, the fact of the no route not working was because of my static assets route which was intercepting the request before my express.get was.
I have a few issues.
I'm currently coding a CMS using React for client side with Webpack and Express for server side with Babel.
What I am trying to do is setup a common X-CSRF-Token header on the axios module on my client side that gets a token generated from a shared file that the server side uses to verify the token.
The problem is that, when I use two different compilers, the static class inheritance doesn't work, so instead of verifying the existing generated token inside the class, it instead generates a whole new.
Would it work if I somehow managed to use the same Webpack config for both server and client side?
When I start up my server, I use this npm script:
nodemon --watch server --exec babel-node -- server/index.js
For the client side, I use Webpack HMR with this config:
import path from 'path'
import webpack from 'webpack'
const clientPath = path.join(__dirname, 'client')
//const bundlePath = path.join(__dirname, 'static', 'js')
export default {
devtool: 'eval-source-map',
entry: [
'webpack-hot-middleware/client',
clientPath
],
output: {
filename: 'bundle.js',
path: '/',
publicPath: '/'
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin()
],
module: {
loaders: [{
test: /\.js$/,
include: clientPath,
loaders: ['react-hot-loader', 'babel-loader']
}]
},
resolve: {
extensions: ['.js']
}
}
server/index.js
So in my server index file, I send the index.html file that has the bundle.js required.
import express, {app} from './app'
import config from '../config.json'
import path from 'path'
import './database'
const staticPath = path.join(__dirname, '..', 'static')
app.use((err, req, res, next) => {
if (err) {
res.status(err.statusCode || err.status || 500)
.send(err.data || err.message || {})
} else {
next()
}
})
app.use('/api', require('./api'))
app.use(express.static(staticPath))
app.get('*', (req, res) =>
res.sendFile(path.join(staticPath, 'index.html'))
)
app.listen(config.port, () =>
console.log(`Listening on port: ${config.port}`)
)
server/app.js
And in my server app file I initialize the Webpack and other middleware:
import webpackHotMiddleware from 'webpack-hot-middleware'
import webpackMiddleware from 'webpack-dev-middleware'
import expressSession from 'express-session'
import bodyParser from 'body-parser'
import webpack from 'webpack'
import express from 'express'
import cors from 'cors'
import path from 'path'
import webpackConfig from '../webpack.config'
const compiler = webpack(webpackConfig)
export const router = express.Router()
export const app = express()
.use(cors())
.use(bodyParser.urlencoded({ extended: false }))
.use(bodyParser.json())
.use(expressSession({
secret: 'test',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
.use(webpackMiddleware(compiler, {
hot: true,
publicPath: webpackConfig.output.publicPath
}))
.use(webpackHotMiddleware(compiler))
export default express
utils/csrf.js
This is the CSRF file that's shared between both the client and server side.
import CryptoJS from 'crypto-js'
import randomString from 'crypto-random-string'
import config from '../config.json'
export default class CSRF {
static secret: String
static csrf: String
static tokenCounter: Number
static generate(counter: Number = 10): String {
if (!this.csrf || this.tokenCounter >= (config.csrf.counter || counter)) {
// after 10 or defined times the token has been used, regenerate it
this.secret = config.csrf.secret !== '' ? config.csrf.secret : randomString(8)
let token = randomString(12)
this.csrf = CryptoJS.AES.encrypt(token, this.secret).toString()
this.tokenCounter = 0
}
return this.csrf
}
static verify(req: Object): Boolean {
const csrf = req.headers['x-csrf-token']
if (!csrf) return false
this.tokenCounter++
//const bytes = CryptoJS.AES.decrypt(csrf, this.secret)
//const decrypted = bytes.toString(CryptoJS.enc.Utf8)
return csrf === this.csrf
}
}
So in my client/index.js file, I setup the axios header like so:
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import axios from 'axios'
import Routes from './routes'
import store from './store'
import config from '../config.json'
import CSRF from '../utils/csrf'
if (config.production) process.NODE_ENV = "production"
axios.defaults.headers.common['X-CSRF-Token'] = CSRF.generate()
ReactDOM.render(
<Provider store={store}>
<Routes />
</Provider>,
document.querySelector('#root')
)
Then from a Redux action I send a post request:
import axios from 'axios'
export function userSignUpRequest(data) {
return (dispatch) => {
return new Promise((resolve, reject) => {
axios.post('/api/users/signup', data)
.then(res => res.data)
.then(data => {
if (data.status) {
resolve(data.user)
} else {
reject(data.errors)
}
})
})
}
}
And in server/api/users/signup I verify the CSRF token:
import Users from '../../database/models/users'
import Validate from '../../../utils/validate'
import CSRF from '../../../utils/csrf'
export default async function (req, res) {
if (!CSRF.verify(req)) {
return res.status(405).json({ status: false, reason: 'Invalid CSRF token' })
}
const {validationErrors, isValid} = Validate.registerInput(req.body)
if (!isValid) {
res.json({ status: false, errors: validationErrors })
} else {
const {email, username, password} = req.body
let errors = {}
try {
await Users.query().where('mail', email)
} catch(err) {
errors.email = 'This email is already taken'
}
try {
await Users.query().where('username', username)
} catch(err) {
errors.username = 'This username is already taken'
}
if (Validate.isObjEmpty(errors)) {
Users.query().insert({
mail: email, username, password
})
.then(user => res.status(200).json({ status: true, user }))
.catch(errors => res.json({ status: false, errors }))
} else {
res.json({ status: false, errors })
}
}
}
Whole source code: Github
I am trying to implement routing with id parameter on the url such as localhost:1000/register/id
Using that path will always trigger system is not defined while other urls without parameters are working fine. I even tried following the guide from angular.io's routing format doesn't seems to fix the problem. What am I missing in my code?
app.routing.ts
import { ModuleWithProviders } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { RegisterComponent } from './registration/register.component';
import { CodeComponent } from './registration/code.component';
const appRoutes: Routes = [
{
path: 'register/:id',
component: RegisterComponent
},
{
path: '',
component: CodeComponent
}
];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HttpModule } from '#angular/http';
import { FormsModule } from '#angular/forms';
import { routing } from './app.routing';
import { AppComponent } from './app.component';
import { CodeComponent } from './registration/code.component';
import { RegisterComponent } from './registration/register.component';
#NgModule({
imports: [BrowserModule, HttpModule, FormsModule, routing],
declarations: [AppComponent, CodeComponent, RegisterComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
server.js
var express = require('express'),
app = express(),
mongoose = require('mongoose'),
bodyParser = require('body-parser'),
path = require('path'),
passport = require('passport'),
session = require('express-session'),
port = process.env.PORT || 1000;
db = require('./config/db');
mongoose.Promise = global.Promise;
mongoose.connect(db.url);
app.use(session({
secret: 'test',
saveUninitialized: true,
resave: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
require('./config/passport')(passport);
require('./routes/main')(app, passport);
app.use(express.static(path.join(__dirname)));
app.use('/node_modules', express.static(path.join(__dirname+ '/node_modules')));
console.log(path.join(__dirname+ '/node_modules'))
app.all('/*', function (req, res, next) {
res.sendFile('/view/index.html', { root: __dirname });
});
app.listen(port);
console.log('Magic happens on port ' + port);
UPDATE:
By using this.router.navigate(['/register', '1']); works perfectly, but by typing on the url localhost:1000/register/1 is not working
From the picture above, there is mouse hover showing the url to be
localhost:1000/register/node_modules/core-js....
- I think there is something I've missed in my server NodeJS side.
I've also added
app.use('/node_modules', express.static(__dirname + '/node_modules'));
But no changes
Note:
Server side (NodeJS,Express)