I am trying to implement simple authentication with react-router. I think there is an issue with replace and callback. Consider following code:
1) Routing configuration
function getRoutes() {
return {
path: "/",
indexRoute: require("./Home"),
component: require("./Shared/Layout"),
onEnter: handleEnter,
childRoutes: [
require("./Login"),
require("./Secured"),
require("./Any")
]
}
}
function handleEnter(nextState, replace, callback) {
let state = store.getState()
if (!state.hasIn(["shared", "user"])) {
store.dispatch(fetchUser())
.then(callback)
}
}
2) ./Secured route configuration
export = {
path: "/secured",
component: require("./Secured"),
onEnter(nextState, replace) {
let state = store.getState()
if (!state.getIn(["shared", "user"])) {
replace("/login")
}
}
}
It should work like this:
Fetch user when entering root route (async operation, we need callback)
Go to /secured and check whether user is authenticated when entering the route
If user is not authenticated go to /login
The problem is that the /login page will not be rendered. The URL is changed to /login, but nothing is displayed and there are no error messages in console. When I remove callback parameter from the root route configuration, it starts working as expected.
Am I doing something wrong?
Well, it was really stupid :) I've forgot to call callback when user is already authenticated.
Related
The Problem:
I'm new to Next.js (1 month) and Vercel (1 day), and between them something appears to be inserting .json in my urls on the search route, causing them to fail with error:
[GET] /_next/data/9MJcw6afNEM1L-eax6OWi/search/hand.json?term=hand
10:21:52:87
Function Status:None
Edge Status:500
Duration:292.66 ms
Init Duration: 448.12 ms
Memory Used:88 MB
ID:fra1:fra1::ldzhz-1644484912454-0a30b71b6c90
User Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0
TypeError: Only absolute URLs are supported
at getNodeRequestOptions (/var/task/node_modules/next/dist/compiled/node-fetch/index.js:1:61917)
at /var/task/node_modules/next/dist/compiled/node-fetch/index.js:1:63448
at new Promise (<anonymous>)
at Function.fetch [as default] (/var/task/node_modules/next/dist/compiled/node-fetch/index.js:1:63382)
at fetchWithAgent (/var/task/node_modules/next/dist/server/node-polyfill-fetch.js:38:39)
at getServerSideProps (/var/task/.next/server/chunks/730.js:238:28)
at Object.renderToHTML (/var/task/node_modules/next/dist/server/render.js:566:26)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async doRender (/var/task/node_modules/next/dist/server/base-server.js:855:38)
at async /var/task/node_modules/next/dist/server/base-
server.js:950:28
2022-02-10T09:21:53.788Z 994c9544-0bbe-4a68-af83-f0e4c322151e ERROR
Error: Your `getServerSideProps` function did not return an object. Did you forget to add a `return`?
at Object.renderToHTML (/var/task/node_modules/next/dist/server/render.js:592:19)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async doRender (/var/task/node_modules/next/dist/server/base-server.js:855:38)
at async /var/task/node_modules/next/dist/server/base-server.js:950:28
at async /var/task/node_modules/next/dist/server/response-cache.js:63:36 { page: '/search/[term]'}
RequestId: 994c9544-0bbe-4a68-af83-f0e4c322151e Error: Runtime exited with error: exit status 1
Runtime.ExitError
Though the browser says https://.../search/hand as it should.
No such thing is happening on my local server build though, and it works perfectly well.
Background/Code Snippets:
The search route is the only route that uses SSR, and is also the only route with this issue. It is a dynamic route, so it seems either next in production or vercel expects some kind of json for it -presumably pre-rendered content-, and is replacing the route URL with json.
Also I have had to use the VERCEL_URL environment variable to prepare a URL for fetch requests, so this may also be messing up the URL, but the .json in the error message makes me think otherwise, since search should not be pre-rendered.
The Page Structure For the Search Route (Index imports the component in [term] and defines its own getServerSide props to accommodate a search route without a param):
|-Search
|- [term].js
|- Index.js
The Code For [term].js:
...
export default function Search({results, currentSearch}){
...
}
export async function getServerSideProps(req) {
const { criteria, page } = req.query;
const { term } = req.params || { term: '' };
try {
const data = await fetch(`${process.env.VERCEL_URL}/api/search/${term}?criteria=${criteria || 'name'}&page=${page}`);
const searchRes = await data.json();
return {
props: {
results: searchRes.data,
currentSearch: searchRes.query
}
}
} catch (e) {
console.log(e)
}
}
Index.js is similar:
import Search from "./[term]";
export default Search;
export async function getServerSideProps(req) {
const { criteria, page } = req.query;
const { term } = req.params || { term: '' };
if(!term){
return {
props: {
results: [],
currentSearch: {}
}
}
}
try {
const data = await fetch(`${process.env.VERCEL_URL}/api/search/${term}?criteria=${criteria || 'name'}&page=${page}`);
const searchRes = await data.json();
return {
props: {
results: searchRes.data,
currentSearch: searchRes.query
}
}
} catch (e) {
console.log(e)
}
}
The API I'm trying to fetch from is confirmed to be working, so this problem is strictly regarding pages, or .json being provided to the fetch method from router params.
It would turn out that VERCEL_URL is actually an absolute URL (It does not include a protocol). I had to deploy console.log statements to find this. A little embarrassed that I missed it in the docs.
The .json was not actually in the query or params, and therefore not in the fetch request. The fetch failed because the url had no protocol.
The .json in the page url must be from Next's internal operations, and does not mean the page is being built ahead of time. Yes it is being rendered using some json, but my thinking that the json indicates a pre-rendered page(SSG/ISR) was wrong. This must mean Server Side Rendering will also make use of such json, but only at runtime, when the request is made.
The use of .json after the params slug in the GET requests for a page has no bearing on the internal flow of your app, provided it has worked correctly. If you see it in error messages, know that it is from Next and examine other parts of the code at the point of failure.
The page structure I attempted ([param].js + index.js in the same
directory) is fine, which is why my local build could work properly.
I want to delete this question because the solution is essentially one that a thorough look in the docs would have revealed, but at the same time I think the mistake itself is an easily made one and that some of the conclusions listed above(particularly the one about json being used in all next routes) could save time spent debugging for some new users of Next/Vercel.
I have 2 functions(authenticate, restrictAccess) in the before hook (get) and i want to chain them together.But the restrictAccess is being executed twice(by the second round it has lost the context)
When i remove the authenticate , the restrictAccess works as expected.
Here is my hook
module.exports = {
before: {
all: [],
get: [authenticate('jwt'), restrictAccess()],
....
But when i remove authenticate as this
module.exports = {
before: {
all: [],
get: [ restrictAccess()],
....
restrictAccess works as expected
The only reason I could think of is that you maybe using the hooks on the users-service.
If this is the case the authenticate-hook probably internally calls the user service to set params.user to the requesting user, which would cause the two calls for the restrictAccess-hook.
A possible fix would be to ignore all internal calls in your restrictAccess-hook:
module.exports = context => {
if (context.params && context.params.provider) {
// put restriction logik here....
}
return context;
}
I'm trying to authenticate using github.
I configured properly the callback, successRedirect and failureRedirect.
The successRedirect page is called. In this page I try to call the authenticate function.
client.authenticate({
strategy: 'github'
})
The promise resolve with a token but when I try to access a secured service, it returns an error. Then If I try to retry to access a second time to the service, it works.
Can someone explain me or provide me a working example.
My code:
const hello = client.service('hello');
function getVal(iter) {
console.log("Iter " + iter)
hello.get(1, {}).then((data) => {
console.log('User is logged');
console.dir(data)
}, (error) => {
console.dir(error)
getVal(iter + 1)
})
}
client.authenticate({
strategy: 'github'
}).then((token) => {
console.dir(token)
getVal(0)
}, (error) => console.dir(error));
In the logs I see that the first call to the service fails with an authentication error but not the second while I'm supposed to be logged (because it's in the configured success redirection)
My logs:
Object { accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6ImFjY2…" }
Iter 0
{
className: "not-authenticated"
code: 401
……
}
Iter 1
User is logged
{…}
The oAuth2 client usage API shows that instead of calling authenticate you just have to link the user to /auth/github to kick of the oAuth flow:
Login With GitHub
It seems if I change path in root onEnter or onChange hook, the url will change infinite(?_k=u9huwr will always change). But if I change path in child routes, it will work. Actually I want to handle the authentication in one place, otherwise every child route should handle the same logic.
{
path: '/',
onChange: function(prevState, nextState, replace, callback) {
if(!logined) {
replace('login');
}
},
childRoutes: [
....
]
}
I am trying to serve some static content for a protractor test using gulp in conjunction with gulp-webserver.
For all routes that start with '/mocks' I want to serve from base path test/mocks (this is the way that the build server is set up - I can't change that).
After much trial and error I got to a configuration that looks like this:
gulp.task('serve-e2e', ['build-dev'], function() {
gulp.src('app').pipe(webserver({
port: 9001,
middleware: [
{route: '/mocks', handle: serveStatic('test/mocks')}
]
}));
});
However, that still gives me 404 errors (I've verified the folder location - nothing wrong there) for everything starting with /mocks.
I've even tried this - looking at the code in gulp-webserver, middleware only accepts functions - still to no avail:
gulp.task('serve-e2e', ['build-dev'], function() {
gulp.src('app').pipe(webserver({
port: 9001,
middleware: [
getStatic({route: /^\/mocks/, handle: serveStatic('test/mocks')})
]
})
);
function getStatic(opts) {
return function(req, res, next) {
if (parseurl(req).pathname.match(opts.route)) {
return opts.handle(req, res, next);
} else {
return next();
}
}
}
I'm sure there's a better way to do this but I'm stumped. Can anybody help me?
So it seems that the original route is included in the path which under the above code would result in test/mocks/mocks.
While it is still necessary to add the getStatic method (gulp-webserver doesn't forward an object to connect - which would be an option if the object contained keys 'route' and 'handle'), the problem was solved for me when I changed it to
getStatic({route: /^\/mocks/, handle: serveStatic('test')})