How to do an app logout using nodejs and okta - node.js

I am currently using Okta with OIDC and Node Express to log into a web app that I made. I want my users to be able to logout of just my app and not okta itself as the okta is for my company and that would log them out of all the other websites that use the company okta. Okta recommends doing this on their website.
app.get('/local-logout', (req, res) => {
req.logout();
res.redirect('/');
});
(https://developer.okta.com/docs/guides/sign-users-out/nodeexpress/sign-out-of-your-app/)
I have tried implementing this but it doesn't work. I have also tried using req.session.destroy, res.clearCookie("connect.sid"), req.session = null and many different combinations of these with or without callbacks. Whenever I redirect, it just goes back to the homepage and the same user is logged in. Whenever I try deleting the connect.sid cookie it deletes and then is reinitialized when the user is redirected. I'm not really sure what to do. My code with irrelevant endpoints removed is below.
require('dotenv').config();
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '#nguniversal/express-engine';
import { join } from 'path';
import * as express from 'express';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '#angular/common';
import { existsSync, read } from 'fs';
// 30 minutes
const sessionMaxAge = 1800000;
import * as e from 'cors';
import { resolveForwardRef } from '#angular/core';
import { User } from '#okta/okta-sdk-nodejs';
const session = require('express-session');
const { ExpressOIDC } = require('#okta/oidc-middleware');
const cookieParser = require("cookie-parser");
/** Creates the OpenID Connect Middleware and configures it to work with Okta */
const oidc = new ExpressOIDC({
appBaseUrl: process.env.HOST_URL,
issuer: `${process.env.OKTA_ORG_URL}/oauth2/default`,
client_id: process.env.OKTA_CLIENT_ID,
client_secret: process.env.OKTA_CLIENT_SECRET,
redirect_uri: process.env.REDIRECT_URL,
scope: 'openid profile',
routes: {
loginCallback: {
path: '/authorization-code/callback'
},
}
});
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
var root_folder: string = process.env.ROOT_FOLDER || "dist/angular-app/browser"
const server = express();
const distFolder = join(process.cwd(), root_folder);
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found # https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_angular_universal_tree_master_modules_express-2Dengine&d=DwIGAg&c=-35OiAkTchMrZOngvJPOeA&r=03NdPO1x-l0QAZ_R9TNGwA&m=WF5ia-YADjCituVWMV5vLoZ5wg7d_W1qhCYDTbJNGT0&s=WPOkeRsetPDQ6TrD26RKLo1m9_zxBfQXGhUUSkth0Ew&e= )
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// Configure Session Support
server.use( session({
secret: process.env.APP_SECRET,
resave: false,
saveUninitialized: false,
cookie: {maxAge: sessionMaxAge}
})
);
server.use(cookieParser());
server.use(oidc.router);
// Log the user out of the local session
server.get('/local-logout',(req:any, res:any) => {
req.logout();
res.redirect('/');
});
// All regular routes use the Universal engine
server.get('*', oidc.ensureAuthenticated({ redirectTo: '/login' }), (req: any, res: any) => {
if(req.session && req.session.username != null) {
console.log(process.env);
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
}
else {
console.log("getting user info");
getUserInfo(req)
.then(userInfo => {
req.session.username = userInfo.username;
req.session.group = userInfo.group;
if (userInfo.group=="unauthorized") {
res.sendFile('./403.html', {root: "."})
}
else{
console.log(process.env);
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 https://urldefense.proofpoint.com/v2/url?u=http-3A__localhost-3A-24-257Bport-257D&d=DwIGAg&c=-35OiAkTchMrZOngvJPOeA&r=03NdPO1x-l0QAZ_R9TNGwA&m=WF5ia-YADjCituVWMV5vLoZ5wg7d_W1qhCYDTbJNGT0&s=PUb7XMS4uP9ICqUY28QgXNRxoWk6sGatPdmZMqmgbJs&e= `);
});
}
// 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';

After an exhaustive search, I don't think it is possible. I ended up doing req.logout() and then redirecting to a page that says "you have been logged out of the app but may still be logged into your single sign on provider."

Related

Angular + Node + Universal remove # with working refresh

I am using Angular 13 + Angular Universal + Node.js.
I have deployed app on server, but I have big problem with removing # from URL.
I have built app using
npm run build:ssr
And I am serving it using node.js app on my server using :
app.use(express.static(__dirname + '/dist/app-name/browser'));
app.get('/', (req, res) => res.sendFile(path.join(__dirname)));
When I use HashLocationStrategy, then everything works fine. Problem appears when I am trying to remove hash from link.
I changed it to PathLocationStrategy and I am getting error 404 on refresh on other path than '/'.
I was trying to add different routes in node.js file to sendFile (f.e.)
app.get('/subpage', (req, res) => res.sendFile(path.join(__dirname)));
But it didn't worked for me.
I was also trying to overwite # with node.js, but as it comes from angular app it's impossible to do it from node.js code.
My files :
app.server.module.ts
import { NgModule } from '#angular/core';
import { ServerModule } from '#angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import {UniversalInterceptor} from './universal-interceptor.service';
import {HTTP_INTERCEPTORS} from '#angular/common/http';
import {NgxSsrTimeoutModule} from '#ngx-ssr/timeout';
#NgModule({
imports: [
AppModule,
ServerModule,
NgxSsrTimeoutModule.forRoot({ timeout: 5000 }),
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: UniversalInterceptor,
multi: true,
}
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
server.ts
import 'zone.js/dist/zone-node';
import {ngExpressEngine} from '#nguniversal/express-engine';
import * as express from 'express';
import {join} from 'path';
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/app-name/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// 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) => {
res.status(404).send('data requests are not yet supported');
});
// 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';
How to to deploy app without '#' in url with working refresh?
Okay, so I finally found solution for this. My problem was, that I needed to give absolute path to my index.html file for all angular routes. So finally i changed
app.get('/', (req, res) => res.sendFile(path.join(__dirname)));
to
app.get('/**', (req, res) => {
res.sendFile(path.join(__dirname + '/dist/app-name/browser/index.html'))
});
Try this:
app.get('*', (req, res) => res.sendFile(path.join(__dirname)));
put it near the end middleware chain

Error: ENOENT: no such file or directory, open 'dist/index.html'

So, I've been trying for days to get a angular universal app running but I keep getting this issue when I try to run the server like
npm run dev:ssr
I have set my server.ts file as in the below link
https://github.com/Angular-RU/angular-universal-starter/blob/master/server.ts
My server.ts file is as below
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '#nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '#angular/common';
import { existsSync } from 'fs';
// ssr DOM
const domino = require('domino');
const fs = require('fs');
const path = require('path');
// index from browser build!
const template = fs.readFileSync(path.join('.', 'dist', 'index.html')).toString();
// for mock global window by domino
const win = domino.createWindow(template);
// from server build
const files = fs.readdirSync(`${process.cwd()}/dist-server`);
// mock
global['window'] = win;
// not implemented property and functions
Object.defineProperty(win.document.body.style, 'transform', {
value: () => {
return {
enumerable: true,
configurable: true,
};
},
});
// mock documnet
global['document'] = win.document;
// othres mock
global['CSS'] = null;
// global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['Prism'] = null;
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
const server = express();
const distFolder = join(process.cwd(), 'dist');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// 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() {
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';
my app.server.module.ts file:
import { NgModule } from '#angular/core';
import { ServerModule } from '#angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
#NgModule({
imports: [
AppModule,
ServerModule,
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
I am using Angular 9 and at a stage where I am thinking of dropping the idea of using angular universal at all. Seems it its way too unstable to be used at the moment.
Contents of dist folder:
Does anyone here have a solution to this?
You incorrectly set the distFolder variable. The distFolder from server.ts must point to the files containing the client side app, which is dist\YourProjectName\browser in your app. Note that this configured in angular.json file.
To correct your error, try changing the distFolder path in your server.ts
const distFolder = join(process.cwd(), 'dist','YourProjectName', 'browser');

Angular Universal page loading/refreshing issue

I have added Angular Universal in my project(upgraded to angular 8). Which is comparatively a big project. After solving all the window, jquery and localstorage issue I have successfully able to run the app. But the problem is, the server runs on localhost:4000. when I type localhost:4000 in browser nothing happens. But if I put localhost:4000/index.html, It starting to load fine and return to base localhost:4000. Then every angular route works fine. But if I try to refresh any page it doesn't load anything.
my server.ts
import 'zone.js/dist/zone-node';
// ssr DOM
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.join('dist/browser', 'index.html')).toString();
const win = domino.createWindow(template);
global['window'] = win;
Object.defineProperty(win.document.body.style, 'transform', {
value: () => {
return {
enumerable: true,
configurable: true,
};
},
});
// mock documnet
global['document'] = win.document;
// othres mock
global['CSS'] = null;
// global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['Prism'] = null;
import { ngExpressEngine } from '#nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
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() {
const server = express();
const distFolder = join(process.cwd(), 'dist/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// 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
// app.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() {
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;
if (mainModule && mainModule.filename === __filename) {
run();
}
export * from './src/main.server';
I think i need some change with this file to load correctly.

React intercepts /auth/twitter/when HTTPS. I want to access /auth/twitter/ from my node server not the react application

New to React/Node.
I have an implementation of React(React-boiler-plate with webpack etc)/Node running on the same host on Heroku.
I am using passport and twitter-oath sessions.
When I hit the endpoint http://example.heroku.com/auth/twitter/callback everything works accordingly (as well as running local dev server).
When I try to access it via HTTPS https://example.heroku.com/auth/twitter/callback React intercepts it and shows a page not found page.
I am trying to get an understanding to understand why this happens and the best way to handle this in a "production" like environment. I would like to be handle /auth/twitter and /auth/twitter/callback all on the same host.
I have tried adding http proxy in misc places as well as package.json and to no avail I am spinning my wheels.
Thank you in advance.
auth routes
module.exports = app => {
app.get('/api/logout', (req, res) => {
// Takes the cookie that contains the user ID and kills it - thats it
req.logout();
// res.redirect('/');
res.send(false);
// res.send({ response: 'logged out' });
});
app.get('/auth/twitter', passport.authenticate('twitter'));
app.get(
'/auth/twitter/callback',
passport.authenticate('twitter', {
failureRedirect: '/'
}),
(req, res) => {
res.redirect('/dashboard');
}
);
app.get('/api/current_user', (req, res) => {
// res.send(req.session);
// res.send({ response: req.user });
res.send(req.user);
});
};
index.js
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(
//
cookieSession({
// Before automatically expired - 30 days in MS
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [keys.COOKIE_KEY]
})
);
app.use(passport.initialize());
app.use(passport.session());
require('./routes/authRoutes')(app);
// They export a function - they turn into a function - then immediately call with express app object
app.use('/api/test', (req, res) => {
res.json({ test: 'test' });
});
setup(app, {
outputPath: resolve(process.cwd(), 'build'),
publicPath: '/',
});
// get the intended host and port number, use localhost and port 3000 if not provided
const customHost = argv.host || process.env.HOST;
const host = customHost || null; // Let http.Server use its default IPv6/4 host
const prettyHost = customHost || 'localhost';
/ Start your app.
app.listen(port, host, async err => {
if (err) {
return logger.error(err.message);
}
// Connect to ngrok in dev mode
if (ngrok) {
let url;
try {
url = await ngrok.connect(port);
} catch (e) {
return logger.error(e);
}
logger.appStarted(port, prettyHost, url);
} else {
logger.appStarted(port, prettyHost);
}
});
console.log('Server listening on:', port);
/**
* Front-end middleware
*/
module.exports = (app, options) => {
const isProd = process.env.NODE_ENV === 'production';
if (isProd) {
const addProdMiddlewares = require('./addProdMiddlewares');
addProdMiddlewares(app, options);
} else {
const webpackConfig = require('../../internals/webpack/webpack.dev.babel');
const addDevMiddlewares = require('./addDevMiddlewares');
addDevMiddlewares(app, webpackConfig);
}
return app;
};
const path = require('path');
const express = require('express');
const compression = require('compression');
module.exports = function addProdMiddlewares(app, options) {
// messing around here
const proxy = require('http-proxy-middleware');
const apiProxy = proxy('/api', { target: 'http://localhost:3000' });
const apiProxy2 = proxy('/auth', { target: 'http://localhost:3000' });
app.use('/api', apiProxy);
app.use('/auth/*', apiProxy2);
const publicPath = options.publicPath || '/';
const outputPath = options.outputPath || path.resolve(process.cwd(), 'build');
// compression middleware compresses your server responses which makes them
// smaller (applies also to assets). You can read more about that technique
// and other good practices on official Express.js docs http://mxs.is/googmy
app.use(compression());
app.use(publicPath, express.static(outputPath));
app.get('*', (req, res) =>
res.sendFile(path.resolve(outputPath, 'index.html')),
);
};
const path = require('path');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
function createWebpackMiddleware(compiler, publicPath) {
return webpackDevMiddleware(compiler, {
logLevel: 'warn',
publicPath,
silent: true,
stats: 'errors-only',
});
}
module.exports = function addDevMiddlewares(app, webpackConfig) {
const compiler = webpack(webpackConfig);
const middleware = createWebpackMiddleware(
compiler,
webpackConfig.output.publicPath,
);
app.use(middleware);
app.use(webpackHotMiddleware(compiler));
// Since webpackDevMiddleware uses memory-fs internally to store build
// artifacts, we use it instead
const fs = middleware.fileSystem;
app.get('*', (req, res) => {
fs.readFile(path.join(compiler.outputPath, 'index.html'), (err, file) => {
if (err) {
res.sendStatus(404);
} else {
res.send(file.toString());
}
});
});
};
Chances are you have a service worker that is running client side and intercepting the requests, then serving your react app from it's cache.
One hint that gives it away is that the service worker will only be installed / run over https https://developers.google.com/web/fundamentals/primers/service-workers/#you_need_https
Solution would be to either edit the service worker code to have it not serve for the auth urls or disable it all together if you are not planning to build an app around it, it may be more trouble than it is worth.

NodeJS compiler server/client static class inheritance error

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

Resources