Next.js redirect from an API route - javascript

I am building a back-office app that requires users to sign in.
I have 2 external APIs:
API A : to manage user accounts and sessions
API B : to perform CRUD actions on another database (unrelated to users database)
The problem is that I don't want users to be able to perform calls to API B if their session is not valid. So I added some API endpoints in Next (under pages/api) that do the following actions:
verifying the validity of the session against API A
if session is valid: continue to step 3, if not: redirect to page /login
make the call to API B
Everything works fine if the session is valid but it fails if the session is not valid.
I have tried
res.redirect(307, '/login').end()
and
res.writeHead(307, { Location: '/login' }).end()
but it didn't work. It fails even by specifying the whole path (http://localhost:3000/login). What I don't understand is that I am successfully redirected to my /login page if I make the request directly from the browser (GET http://localhost:3000/api/data). It doesn't work when I make the request with Axios inside a React component.
Any idea how I can fix this?

As #juliomalves and #yqlim explained, I had to make the redirect manually based on the response of the API.

Faced same problem solve using below code:
Api
res.status(200).json({ success: "success" }) //add at last of the api to give response
page
import Router from 'next/router'
let res = await fetch('api', {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
if (res.status == 200) {
Router.push('/location')
}
Answer is correct as #Jules Grenier sayes,but provided an example

You do not need .end(). Have you tried res.redirect(307, '/login')?
In Next.js v12 and v13, the following works for me.
// /api/example.js
const handler = async function (req, res) {
// custom logic
if (failed)
return res.redirect(307, '/login')
}
export default handler;

The API request must be initiated by a <form>.
redirect will not work with <fetch>

Related

SvelteKit Hook Prevents Endpoint Request

Trying out SvelteKit and I'm having a hard time with hooks. The docs don't really seem to explain it all too well. My current understanding of hooks is that they basically allow you to interact with requests made to your server before they get to their destination? (I'm open to a better explanation - specifically the handle hook).
My current issue is I made an endpoint called login. As the name suggests, it allows users to sign into my application by generating a token and storing it as a cookie on their client. This works until I add hooks. After reading the hooks description, I figured the handle hook is perfect for what I want to do - validate the token on each request - if invalid, reroute the user to the login screen, if valid, allow the request to continue uninterrupted.
export const handle: Handle = async ({ event, resolve }) => {
const isLogin = event.url.pathname.startsWith('/login')
const cookies = cookie.parse(event.request.headers.get('cookie') || '');
const token = cookies['token']
if (!token) {
if (!isLogin) {
return Response.redirect(`${event.url.origin}/login`)
}
return await resolve(event)
} else {
try {
await verifyToken(token)
if (isLogin) {
return Response.redirect(`${event.url.origin}/about`)
}
} catch (err) {
return Response.redirect(`${event.url.origin}/login`)
}
}
return await resolve(event)
};
This does not work as expected. When I initiate the request to the api/login endpoint, the request does not seem to make it there. I have console.logs all over the endpoint but no messages were outputted to the terminal & when I check the application storage, no new cookie was added.
What am I missing about hooks?
Why is it not passing the request off to the endpoint?
Any idea how I can fix this?
The handle hook runs for every request—including endpoints.
When you fetch /api/login without a token, your hook will redirect the request to /login since isLogin === false. You need to allow through every route that should be accessible without a login, for example:
const isLogin = /^\/(api\/)?login$/.test(event.url.pathname)

How to acquire token with MSAL acquireTokenRedirect()?

I'm trying to figure out how to correctly use msal to acquire token with the redirect method.
I have the following function:
function acquireTokenRedirect() {
const params = {
redirectUri: 'http://localhost:8080/admin/',
scopes: ['user.read']
};
console.log('Acquire token via redirect');
msal.acquireTokenRedirect(params);
}
And the callback:
msal.handleRedirectCallback((error, response) => {
console.log('Redirect call back is called');
});
When I call the method, this is what I get in Chrome console:
Acquire token via redirect
Navigated to http://localhost:8080/admin/
Redirect call back is called
GET http://localhost:8080/ 404 (Not Found)
Navigated to http://localhost:8080/
Why does it redirect to http://localhost:8080/ when I specifically tell it to redirect to http://localhost:8080/admin/?
MSAL is configured to use redirectURI: http://localhost:8080/admin/, and the same redirect is specified in the Azure portal for the project.
In comparison, msal.loginRedirect() redirects to the correct URI.
I suppose this is related to the solution in this issue :
To avoid the extra redirect, what I needed to do is set the
"navigateToLoginRequestUrl" param in Auth config to false. This solves
my problem.
Reference : msal in React SPA - use access token received from AcquireTokenRedirect
Let me know if that helps

Nuxt asyncdata axios on refresh loses auth token

I am calling a get api that gets an array of mail data. It works fine on postman. When I use asyncdata method to get the array. It only works once if user refreshes the page I get 401 error. I pull token from cookies just fine. Normally on non asyncData I do this to set up the header
this.$axios.setHeader('Authorization','Bearer ' + this.$store.state.token);
this.$axios.$post('upload/avatar',formData,{
headers: {'content-type': 'multipart/form-data'}
}).then(res =>{
}).catch(err => console.error(err));{
}
}
This works fine and has no issues
but my asnycData is like this
asyncData(context){
//Cookie has to be read for async to work for now if user disables cookies breaks this page
let token = Cookie.get('token');
context.app.$axios.setHeader('Authorization',`Bearer ${token}`);
return context.app.$axios.$get('get/all/mail').then(mailData =>{
console.log(context.app.$axios.defaults);
let mailMap = [];
//create array to load mail data in
for(let key in mailData){
mailMap.push({...mailData[key]});
}
return{
mailArray:mailMap
}
}).catch(e =>console.error(e));
}
I am trying to make a simple inbox page that can send , delete , and draft messages.
The problem is probably due to the fact that since asyncData is running from the server, it'll lose any browser cookies.
If you're using axios, the nuxt community has setup a middleware module that can be used to automatically inject browser cookies into server requests.

HTTP authenticating --- some weird way

When I try to login emodal.com (I assume it uses basic authentication because it just requires a user/pass) on the browser it works fine and lets me go to a certain page and lets me through. However when I try to go in the page that I want and authenticate programmatically (using request or request-promise) it gets denied because of "invalid credentials". It seems like I need to login manually through a browser or something by clicking the login button and THEN i would be able to go to the private protected page that I want (why doesn't it work programmatically whereas the browser (google chrome) works? Is it going through a second step in the authentication process that I am not aware of?)
Here I provided 3 screenshots of how the devtools console looks when I log in (it makes a request to ValidateWharfageUser, then Login, then GetStatus as shown, then I guess thats where it denies me programmatically).
import dotenv = require('dotenv');
dotenv.config({ path: '../logins.env' });
import rp = require('request-promise');
const jsonUrl = `http://availability.emodal.com/ImportAvailability/GetContainerInfoList?sgrdModel=%7B%22searchtext%22:%22%22,%22page%22:1,%22pageSize%22:280,%22sortBy%22:%221%22,%22sortDirection%22:%22asc%22,%22sortColumns%22:%22%22%7D`;
const authOpts = {
uri: jsonUrl,
auth: {
user: process.env.EMODAL_id,
pass: process.env.EMODAL_pw,
sendImmediately: false
},
method: 'get'
}
rp(authOpts)
.then(resp => {
console.log(resp);
}).catch(err => { throw Error(err)});
The 3 screenshots:
http://i.imgur.com/hjThLt1.png
http://i.imgur.com/0uPAMMs.png
http://i.imgur.com/xBF2DAV.png

How to connect to Asana's API using Asana's JS/Ruby library

I have a backend API written in Ruby and a client App that uses Angular. I'd like to authenticate the user to authenticate the user via the Angular app.
As such I've created my App on Asana. I'm having a few issues though:
First issue: I'm using the Authorisation Endpoint of Authorisation Code Grant. After reading the docs, I realised that I have to use Implicit Grant instead, which is more suitable for a browser-based app, however when I change it to Implicit Grant, save it and reload the page, it changes back to Authorisation Code Grant.
Then on my Angular App I have the following code:
var client = Asana.Client.create({
clientId: 133,
clientSecret: 'mysecretcode',
redirectUri: 'http://localhost:7699/profile'
});
client.useOauth({
flowType: Asana.auth.PopFlow
});
client.authorize().then(function () {
console.log('Auth completed');
}).catch(function (err) {
console.log(err);
});
client.users.me().then(function (result) {
console.log(result);
});
The above almost works. I do get redirected to Asana for the authorisation part, once I click on "Allow", I'm redirected back to my app, and I do get a code as part of the url. The code is something like:
http://localhost:7699/profile#access_token=very_long_string
If I understood the docs correctly, I could use the above access_token to make my first request. When I tried using Asana's JS library to make a request like so:
client.users.me().then(function (result) {
console.log(result);
});
Please note the client object I'm referring to is the same I've created earlier for authorisation. The above returns a 401, Unauthorised code.
Then I tried the following:
var params = {
grant_type: 'refresh_token',
client_id: 876787,
client_secret: 'some_secret',
redirect_uri: 'http://localhost:7699/profile',
code: my_access_code
};
$http.post('https://app.asana.com/-/oauth_token', params).then(function (result) {
console.log(result);
});
Which also gets me a 401 unauthorised code.
What am I doing wrong here?
I recommend you start by copy-pasting one of the examples from the node-asana examples directory into your app, and seeing if that works.
If you want to keep using the popup flow, the thing I suspect you are missing is the call to Asana.auth.PopupFlow.runReceiver(); in popup_receiver.html. This should be on the page pointed to by your redirect_uri, and tells the page that created the popup the auth data it needs to make subsequent requests. Also note how the page that originates the authentication request (popup.html) includes actions that happen after authentication in the callback passed to then: this ensures that these actions happen only after the user completes authentication through the popup.

Categories