I am having issue in middleware for authentication users token it says Converting circular structure to JSON Converting circular structure to JSON
at stringify (/app/node_modules/express/lib/response.js), if the authorization is successful I redirected it to my callback url, my callback url is like this. "https://www.myapp.com/callback"
This is the code for middleware
callback(s, f){
router.get("/callback", (req, res, next) => {
const code = req.query.code;
const state = req.query.state;
const friendship_status_changed = req.query.friendship_status_changed;
if (!code){
debug("Authorization failed.");
return f(new Error("Authorization failed."));
}
if (req.session.line_login_state !== state){
debug("Authorization failed. State does not match.");
return f(new Error("Authorization failed. State does not match."));
}
debug("Authorization succeeded.");
this.issue_access_token(code).then((token_response) => {
if (this.verify_id_token && token_response.id_token){
let decoded_id_token;
try {
decoded_id_token = jwt.verify(
token_response.id_token,
this.channel_secret,
{
audience: this.channel_id,
issuer: "https://access.line.me",
algorithms: ["HS256"]
}
);
debug("id token verification succeeded.");
token_response.id_token = decoded_id_token;
console.log(token_response.id_token);
} catch(exception) {
debug("id token verification failed.");
if (f) return f(req, res, next, new Error("Verification of id token failed."));
throw new Error("Verification of id token failed.");
}
}
s(req, res, next, token_response);
}).catch((error) => {
debug(error);
if (f) return f(req, res, next, error);
throw error;
});
});
return router;
}
and in my server.js this is how I call it using app.use
app.use("/callback", login.callback((req, res, next, token_response) => {
// Success callback
res.json(token_response);
},(req, res, next, error) => {
// Failure callback
res.send(req);
//res.status(400).json(error);
}));
These are my dependencies and devdepencies
"dependencies": {
"bluebird": "^3.5.1",
"body-parser": "^1.18.2",
"circular-json": "^0.5.1",
"debug": "^3.1.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"express-session": "^1.15.6",
"jsonwebtoken": "^8.1.1",
"line-login": "^1.0.8",
"node-fetch": "^1.7.3",
"nodemon": "^1.14.11",
"serve-static": "^1.13.1"
},
"devDependencies": {
"request": "^2.83.0",
"tape": "^4.7.0"
},
Related
I'm having trouble getting redirects to work after accepting a post request from Axios. I do know that the request is being sent and that it at least gets some response from the '/' route, because my console logs "index", "user verified" which is what should happen when someone makes a get request to '/'. The problem is that the page doesn't load. I've even seen in the networking tab on google chrome that index.js is loaded but the page will not change no matter what I've tried! Is there any reason for this?
Other redirects that I've made do seem to work. For example, the index page will reroute to /login if the user is not logged in. This seems to only be an issue with the post request and I have tested it with and without passport authentication (obviously changed that you would need to be logged in to redirect) and it's the same result. So I do not think passport is causing the issue.
You can refer to package.json below to see what I am using
axios code:
axios.post('/login', {username: username, password: password})
/*.then(response => res.redirect('/'))*/
.then(function (response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
})
express side: I have console logs to alert myself during testing
server.get('/', (req,res) =>{
console.log("Index");
if (req.user){
console.log("user verified");
res.redirect('/');
app.render(req,res, '/',req.query);
} else {
console.log("user not logged in");
res.redirect('/login');
}
})
server.post('/login', passport.authenticate('local'), (req, res, next) => {
if (req.user) {
console.log("Logging in");
res.redirect('/');
} else {
console.log("Passwrod Incorrect");
return res.redirect('/login');
}
})
package.json
{
"name": "layout-component",
"version": "1.0.0",
"scripts": {
"dev": "node ./server.js",
"build": "next build",
"start": "NODE_ENV=production node ./server.js"
},
"dependencies": {
"#zeit/next-css": "^0.1.5",
"axios": "^0.18.0",
"bcryptjs": "^2.4.3",
"body-parser": "^1.18.2",
"connect-flash": "^0.1.1",
"connect-mongo": "^2.0.1",
"cookie-parser": "^1.4.3",
"express": "^4.16.3",
"express-session": "^1.15.6",
"express-validator": "^5.1.0",
"file-loader": "^1.1.11",
"hoist-non-react-statics": "^2.5.0",
"jsonwebtoken": "^8.2.0",
"mongodb": "^3.0.5",
"mongoose": "^5.0.12",
"next": "^5.1.0",
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"prop-types": "^15.6.1",
"react": "^16.3.0",
"react-dom": "^16.3.0",
"semantic-ui-css": "^2.3.1",
"semantic-ui-react": "^0.79.0",
"url-loader": "^1.0.1"
},
"license": "ISC"
}
I figured this out after. Apparently, you cannot do a redirect from the server when you make an Axios post request. At least not the way that I was doing it (with the default Axios config.) You need to do the page change on the client side. Here's how I did it.
This really stumped me because I was receiving data from my redirect route using the other method but the page wasn't loading.
Also, for some reason using Next.js, the passport.js "successRedirect" and "failureRedirect" JSON doesn't seem to work. That's why I've written the routing the way I have and did not include those in the passport.authenticate() function. I hope this helps somebody!
My Axios submit function:
onSubmit = (e) => {
e.preventDefault()
const {username, password} = this.state;
axios.post('/login', {username: username, password: password})
.then(function (response) {
if (response.data.redirect == '/') {
window.location = "/index"
} else if (response.data.redirect == '/login'){
window.location = "/login"
}
})
.catch(function(error) {
window.location = "/login"
})
}
The post request in my Express server
server.post('/login', passport.authenticate('local'), (req, res, next) => {
if (req.user) {
var redir = { redirect: "/" };
return res.json(redir);
} else {
var redir = { redirect: '/login'};
return res.json(redir);
}
})
Although it won't be a server-side redirect, this can be accomplished cleanly on the client-side with Axios' "interceptor" which can intercept requests or responses before they get passed to then/catch:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
https://github.com/axios/axios#interceptors
I'm trying to use JWT verification for authentication with middleware, but unfortunately, I'm getting some errors that cannot find a solution for.
./node_modules/jwa/index.js:3:0
Module not found: Can't resolve 'crypto'
Import trace for requested module:
./node_modules/jws/lib/sign-stream.js
./node_modules/jws/index.js
./node_modules/jsonwebtoken/verify.js
./middleware.ts
https://nextjs.org/docs/messages/module-not-found
You're using a Node.js module (crypto) which is not supported in the Edge Runtime.
Learn more: https://nextjs.org/docs/api-reference/edge-runtime
package.json
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"#emotion/react": "^11.9.3",
"#emotion/styled": "^11.9.3",
"#mui/material": "^5.8.6",
"#prisma/client": "^4.0.0",
"axios": "^0.27.2",
"buffer": "^6.0.3",
"cookie": "^0.5.0",
"jsonwebtoken": "^8.5.1",
"next": "12.2.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.33.0"
},
"devDependencies": {
"#types/node": "18.0.0",
"#types/react": "18.0.14",
"#types/react-dom": "18.0.5",
"eslint": "8.18.0",
"eslint-config-next": "12.2.0",
"prisma": "^4.0.0",
"typescript": "4.7.4"
}
}
middleware.ts
import { NextResponse } from "next/server";
import verify from "jsonwebtoken/verify";
import { urlToHttpOptions } from "url";
import type { NextRequest } from 'next/server'
const secret = process.env.SECRET;
export default function middleware(req: NextRequest) {
const { cookies } = req;
const { search, protocol, host } = req.nextUrl
const jwt = cookies.OutsiteJWT;
const url = req.url;
if (url.includes('/dashboard')) {
if (jwt === undefined) {
return NextResponse.redirect("http://localhost:3000/login");
}
try {
verify(jwt, secret); // <---- ERROR COMES FROM HERE
return NextResponse.next();
} catch (error) {
return NextResponse.redirect("/login");
}
}
return NextResponse.next();
}
/pages/api/auth/login.ts
/* eslint-disable import/no-anonymous-default-export */
import { sign } from "jsonwebtoken";
import { serialize } from "cookie";
const secret = process.env.SECRET;
export default async function (req, res) {
const { username, password } = req.body;
// Check in the database
// if a user with this username
// and password exists
if (username === "Admin" && password === "Admin") {
const token = sign(
{
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30, // 30 days
username: username,
},
secret
);
const serialised = serialize("OursiteJWT", token, {
httpOnly: true,
secure: process.env.NODE_ENV !== "development",
sameSite: "strict",
maxAge: 60 * 60 * 24 * 30,
path: "/",
});
res.setHeader("Set-Cookie", serialised);
res.status(200).json({ message: "Success!" });
} else {
res.status(401).json({ message: "Invalid credentials!" });
}
}
I've tried commenting out the try/catch to check if the token is set, and it works correctly, but when I try to verify in the middleware it fails.
In version 12.2.0 the middleware is stable but also has some changes.
Does somebody else encounter similar issues or know how to solve this?
Based on the discussion with a user in GitHub, apparently jose library works better while running Edge functions in the middleware while jsonwebtoken does not.
/middleware.ts
import { NextResponse } from "next/server";
import { urlToHttpOptions } from "url";
import type { NextRequest } from 'next/server'
import { verify } from "./services/jwt_sign_verify";
const secret = process.env.SECRET || "secret";
export default async function middleware(req: NextRequest) {
const jwt = req.cookies.get("OutsiteJWT");
const url = req.url;
const {pathname} = req.nextUrl;
if (pathname.startsWith("/dashboard")) {
if (jwt === undefined) {
req.nextUrl.pathname = "/login";
return NextResponse.redirect(req.nextUrl);
}
try {
await verify(jwt, secret);
return NextResponse.next();
} catch (error) {
req.nextUrl.pathname = "/login";
return NextResponse.redirect(req.nextUrl);
}
}
return NextResponse.next();
}
/services/jwt_sign_verify.ts
import { SignJWT, jwtVerify, type JWTPayload } from 'jose';
import { Token } from "#typescript-eslint/types/dist/generated/ast-spec";
export async function sign(payload: string, secret: string): Promise<string> {
const iat = Math.floor(Date.now() / 1000);
const exp = iat + 60 * 60; // one hour
return new SignJWT({ payload })
.setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
.setExpirationTime(exp)
.setIssuedAt(iat)
.setNotBefore(iat)
.sign(new TextEncoder().encode(secret));
}
export async function verify(token: string, secret: string): Promise<JWTPayload> {
const { payload } = await jwtVerify(token, new TextEncoder().encode(secret));
// run some checks on the returned payload, perhaps you expect some specific values
// if its all good, return it, or perhaps just return a boolean
return payload;
}
/pages/api/auth/login.ts
/* eslint-disable import/no-anonymous-default-export */
import { serialize } from "cookie";
import { sign } from "../../../services/jwt_sign_verify";
const secret = process.env.SECRET || "secret";
export default async function (req, res) {
const { username, password } = req.body;
// Check in the database
if (username === "Admin" && password === "Admin") {
const token = await sign(
"testing",
secret
);
const serialised = serialize("OursiteJWT", token, {
httpOnly: true,
secure: process.env.NODE_ENV !== "development",
sameSite: "strict",
maxAge: 60 * 60 * 24 * 30,
path: "/",
});
res.setHeader("Set-Cookie", serialised);
res.status(200).json({ message: "Success!" });
} else {
res.status(401).json({ message: "Invalid credentials!" });
}
}
I am trying for quite some time to stream a video from MongoDB. Read tons of api DOCs and examples just can't get it to work
This is my front-end video Handler :
import { useState, useEffect } from "react";
import ReactPlayer from "react-player";
const CatVideos = () => {
const [videoList, setvideoList] = useState(null);
useEffect(() => {
const getVideos=async ()=> {
const response = await fetch('http://localhost:8000/cat-videos/videos');
const data = await response.json();
setvideoList(data);
console.log(data);
}
getVideos();
}, [])
return (
<div>
<h1>Cat videos</h1>
{videoList!=null && videoList.map((video)=>{
return <ReactPlayer key={video._id} url={'http://localhost:8000/cat-videos/videos/'+video._id}/>
})}
</div>
);
};
export default CatVideos;
Backend stream function :
exports.getVideoStream = (req, res, next) => {
var id = req.params.id;
let gfs = Grid(conn.db, mongoose.mongo);
gfs.collection("videos");
gfs.files
.findOne({
_id: mongoose.Types.ObjectId(id),
})
.then((result) => {
const file = result;
if (!file) {
return res.status(404).send({
err: "Unavailable.",
});
}
if (req.headers["range"]) {
var parts = req.headers["range"].replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
var start = parseInt(partialstart, 10);
var end = partialend ? parseInt(partialend, 10) : file.length - 1;
var chunksize = end - start + 1;
res.header("Accept-Ranges", "bytes");
res.header("Content-Length", chunksize);
res.header(
"Content-Range",
"bytes " + start + "-" + end + "/" + result.length
);
console.log(result.contentType)
res.header("Content-Type", result.contentType);
gfs.createReadStream({
_id: result._id,
range: {
startPos: start,
endPos: end,
},
}).readStream.pipe(res);
} else {
console.log("#################before####################");
res.header("Content-Length", result.length);
res.header("Content-Type", result.contentType);
console.log(result._id);
gfs
.createReadStream({
_id: result._id,
})
.pipe(res);
}
})
.catch((err) => {
res.json(err);
});
};
I do get a response from this function , and it appears that the "Content-Type" remains unchanged.
HTTP/1.1 206 Partial Content
X-Powered-By: Express
Access-Control-Allow-Origin: *
Accept-Ranges: bytes
Date: Thu, 23 Dec 2021 19:38:25 GMT
Content-Type: application/json; charset=utf-8
ETag: W/"2-vyGp6PvFo4RvsFtPoIWeCReyIC8"
Content-Range: bytes 0-1/2
Content-Length: 2
Backend dependencies:
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.1",
"cors": "^2.8.5",
"ejs": "^3.1.6",
"express": "^4.17.2",
"gridfs-stream": "^1.1.1",
"method-override": "^3.0.0",
"mongodb": "^4.2.2",
"mongoose": "^6.1.2",
"multer": "^1.4.4",
"multer-gridfs-storage": "^5.0.2"
}
Frontend dependencies:
"axios": "^0.24.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-player": "^2.9.0",
"react-router-dom": "^6.2.1",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.2"
I managed to fix the issue. Note that GridFSBucket has a default bucket name.
Going over the API docs it says it appends the bucket name ".files".
My issues were that I did not define it and start end inside download stream were not defined correctly causing an error.
You may use it as well to stream Images,videos just change the content type on the frontend. Pretty generic stream.
exports.getVideoStream = (req, res, next) => {
mongodb.MongoClient.connect(url, function (error, client) {
if (error) {
res.status(500).json(error);
return;
}
// Check for range headers to find our start time
const range = req.headers.range;
if (!range) {
res.status(400).send("Requires Range header");
}
const db = client.db('videos');
// GridFS Collection
console.log(req.params.id);
db.collection('videos.files').findOne({_id:mongoose.Types.ObjectId(req.params.id)}, (err, video) => {
if (!video) {
res.status(404).send("No video uploaded!");
return;
}
// Create response headers
const videoSize = video.length;
const start = Number(range.replace(/\D/g, ""));
const end = videoSize - 1;
const contentLength = end - start + 1;
const headers = {
"Content-Range": `bytes ${start}-${end}/${videoSize}`,
"Accept-Ranges": "bytes",
"Content-Length": contentLength,
"Content-Type": "video/mp4",
};
// HTTP Status 206 for Partial Content
res.writeHead(206, headers);
// Get the bucket and download stream from GridFS
const bucket = new mongodb.GridFSBucket(db,{bucketName:"videos"});
const downloadStream = bucket.openDownloadStream(video._id, {
start:start,
end:end
});
// Finally pipe video to response
console.log(streamCounter," start ",start," end ",end)
streamCounter++;
downloadStream.pipe(res);
});
});
};
I tried to get this working for several days and getting now where. I do believe that that the client side is function properly but I'm not hundred percent certain. I know the resolver on the server is getting called. At one point I was able to destructure the file param in the resolver. But now when I destructure, I get undefined. Not sure what is up with that at one point it was destructuring just fine. The client project is setup to connect to two different GraphQL servers, one is for standard database and subscriptions and the other is file uploader. That is why I left the subscription in the dependences of the client.
I wanna say thank you for taking the time to look at this.
My Server Version Info
"#graphql-tools/schema": "^7.1.5",
"apollo-cache-inmemory": "^1.6.6",
"apollo-server-express": "^2.25.0",
"express": "^4.17.1",
"express-jwt": "^6.0.0",
"graphql": "^15.5.0",
"graphql-middleware": "^6.0.10",
"graphql-shield": "^7.5.0",
"graphql-upload": "^12.0.0",
My Main File
import { ApolloError, ApolloServer, makeExecutableSchema } from "apollo-server-express"
import { applyMiddleware } from "graphql-middleware"
import express from "express"
import { graphqlUploadExpress } from 'graphql-upload'
import makeDir from 'make-dir'
const app = express()
const cors = require('cors')
app.use(cors())
const bodyParser = require('body-parser')
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(graphqlUploadExpress({ maxFileSize: 10000000, maxFiles: 10 }));
const apolloServer = new ApolloServer({
uploads: false,
...
schema: applyMiddleware(
makeExecutableSchema({ typeDefs, resolvers }),
permissions
),
context: ({ req, res }) => {
...
},
});
apolloServer.applyMiddleware({ app });
...
async function StartServer() {
await makeDir(UPLOAD_DIR)
app.listen({ host: HOST, port: PORT }, () => {
console.log(`
ππππππππππππππππππππππππππππππππππππππππππππππππππππ
Apollo File Server ready at http://${HOST}:${PORT}${apolloServer.graphqlPath}
Started at ${datetime}
isDevelopmentMode ${isDevelopmentMode}
`)
});
}
StartServer()
My Schema
type File {
name: String!
size: Int!
}
extend type Mutation {
singleUpload(file: Upload!): File!,
}
My Resolver
singleUpload: async (parent, { file }) => {
console.log('singleUpload------------------------------------------------------------------------------>>>', file)
console.log('singleUpload------------------------------------------------------------------------------>>> 0')
const { createReadStream, filename, mimetype, encoding } = await file;
console.log('singleUpload------------------------------------------------------------------------------>>> 1', filename)
const stream = createReadStream();
console.log('singleUpload------------------------------------------------------------------------------>>> 2')
const pathName = `${UPLOAD_DIR}${filename}`;
console.log('singleUpload------------------------------------------------------------------------------>>> 3')
await stream.pipe(fs.createWriteStream(pathName));
console.log('singleUpload------------------------------------------------------------------------------>>> 4')
return {
name: `http://localhost:4001/images/${filename}`,
size: 1,
};
},
My Client side
Version Info
"#apollo/client": "^3.2.7",
"#apollo/react-hoc": "^4.0.0",
"#apollo/react-hooks": "^4.0.0",
"#graphql-codegen/introspection": "^1.18.1",
"#graphql-codegen/schema-ast": "^1.18.1",
"#graphql-tools/load-files": "^6.2.5",
"#graphql-tools/merge": "^6.2.6",
"apollo-boost": "^0.4.9",
"apollo-link-ws": "^1.0.20",
"apollo-upload-client": "^16.0.0",
"graphql": "^14.7.0",
"graphql-scalars": "^1.7.0",
"react": "^16.14.0",
"react-apollo": "^3.1.5",
"react-app-rewire-define-plugin": "^1.0.0",
"react-app-rewired": "^2.1.8",
"react-dom": "^16.14.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^3.4.1",
"subscriptions-transport-ws": "^0.9.18",
"typescript": "^3.7.3",
The Apollo Client
const uploadLink = createUploadLink({
uri: 'http://localhost:4001/graphql', // Apollo Server is served from port 4000
headers: {
"keep-alive": "true",
'content-type': 'application/json',
...((accessToken !== undefined && accessToken !== null) ? { Authorization: `Bearer ${accessToken}` } : {}),
...((accessToken !== undefined && accessToken !== null) ? { 'x-token': accessToken } : {}),
...((refreshToken !== undefined && refreshToken !== null) ? { 'x-refresh-token': refreshToken } : {}),
}
})
const clientUpload = new ApolloClient({
cache: cache,
link: uploadLink
})
The Uploader Component
const mutUploadFile = gql`
mutation singleUpload($file: Upload!) {
singleUpload(file: $file) {
name
#filename
#mimetype
#encoding
#url
}
}`
export function FileUploadSimpleA() {
const onChange = ((e: any) => {
console.log( 'onChange-------------------------')
const file = e.target.files[0]
console.log('file:', file)
if (file)
clientUpload.mutate({
mutation: mutUploadFile,
variables: {
file: file
}
})
.then(result => {
console.log( 'then-------------------------')
console.log(result)
clientUpload.resetStore();
})
.catch(error => {
console.log( 'catch-------------------------')
console.log(error)
}
);
});
return <input type="file" required onChange={onChange} />;
}
export default FileUploadSimpleA
Console of the browser
fileUploadSimple.tsx:50 onChange-------------------------
fileUploadSimple.tsx:52 file:: FileΒ {name:
"322abcbafe59a52481e6a0b6b84ffbc4.jpg", lastModified: 1614644398495, lastModifiedDate: Mon Mar 01 2021 18:19:58 GMT-0600 (Central Standard Time), webkitRelativePath: "", size: 68817,Β β¦}
fileUploadSimple.tsx:66 catch-------------------------
fileUploadSimple.tsx:67 Error: createReadStream is not a function
I'm having trouble getting redirects to work after accepting a post request from Axios. I do know that the request is being sent and that it at least gets some response from the '/' route, because my console logs "index", "user verified" which is what should happen when someone makes a get request to '/'. The problem is that the page doesn't load. I've even seen in the networking tab on google chrome that index.js is loaded but the page will not change no matter what I've tried! Is there any reason for this?
Other redirects that I've made do seem to work. For example, the index page will reroute to /login if the user is not logged in. This seems to only be an issue with the post request and I have tested it with and without passport authentication (obviously changed that you would need to be logged in to redirect) and it's the same result. So I do not think passport is causing the issue.
You can refer to package.json below to see what I am using
axios code:
axios.post('/login', {username: username, password: password})
/*.then(response => res.redirect('/'))*/
.then(function (response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
})
express side: I have console logs to alert myself during testing
server.get('/', (req,res) =>{
console.log("Index");
if (req.user){
console.log("user verified");
res.redirect('/');
app.render(req,res, '/',req.query);
} else {
console.log("user not logged in");
res.redirect('/login');
}
})
server.post('/login', passport.authenticate('local'), (req, res, next) => {
if (req.user) {
console.log("Logging in");
res.redirect('/');
} else {
console.log("Passwrod Incorrect");
return res.redirect('/login');
}
})
package.json
{
"name": "layout-component",
"version": "1.0.0",
"scripts": {
"dev": "node ./server.js",
"build": "next build",
"start": "NODE_ENV=production node ./server.js"
},
"dependencies": {
"#zeit/next-css": "^0.1.5",
"axios": "^0.18.0",
"bcryptjs": "^2.4.3",
"body-parser": "^1.18.2",
"connect-flash": "^0.1.1",
"connect-mongo": "^2.0.1",
"cookie-parser": "^1.4.3",
"express": "^4.16.3",
"express-session": "^1.15.6",
"express-validator": "^5.1.0",
"file-loader": "^1.1.11",
"hoist-non-react-statics": "^2.5.0",
"jsonwebtoken": "^8.2.0",
"mongodb": "^3.0.5",
"mongoose": "^5.0.12",
"next": "^5.1.0",
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"prop-types": "^15.6.1",
"react": "^16.3.0",
"react-dom": "^16.3.0",
"semantic-ui-css": "^2.3.1",
"semantic-ui-react": "^0.79.0",
"url-loader": "^1.0.1"
},
"license": "ISC"
}
I figured this out after. Apparently, you cannot do a redirect from the server when you make an Axios post request. At least not the way that I was doing it (with the default Axios config.) You need to do the page change on the client side. Here's how I did it.
This really stumped me because I was receiving data from my redirect route using the other method but the page wasn't loading.
Also, for some reason using Next.js, the passport.js "successRedirect" and "failureRedirect" JSON doesn't seem to work. That's why I've written the routing the way I have and did not include those in the passport.authenticate() function. I hope this helps somebody!
My Axios submit function:
onSubmit = (e) => {
e.preventDefault()
const {username, password} = this.state;
axios.post('/login', {username: username, password: password})
.then(function (response) {
if (response.data.redirect == '/') {
window.location = "/index"
} else if (response.data.redirect == '/login'){
window.location = "/login"
}
})
.catch(function(error) {
window.location = "/login"
})
}
The post request in my Express server
server.post('/login', passport.authenticate('local'), (req, res, next) => {
if (req.user) {
var redir = { redirect: "/" };
return res.json(redir);
} else {
var redir = { redirect: '/login'};
return res.json(redir);
}
})
Although it won't be a server-side redirect, this can be accomplished cleanly on the client-side with Axios' "interceptor" which can intercept requests or responses before they get passed to then/catch:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
https://github.com/axios/axios#interceptors