How could i pass cookies in Axios - axios

I am in a next-js app and my auth token is stored in cookies.
For some raisons i use Swr and Api route to fetch my secured api backend.
i am trying to find a way to put my auth token in all api request.
During login cookie is set
res.setHeader(
'Set-Cookie',
cookie.serialize('token', data.access_token, {
httpOnly: true,
secure: process.env.NODE_ENV !== 'development',
maxAge: data.expires_in, // 1 week
sameSite: 'strict',
path: '/',
}),
);
This is an example of a page using swr fetch
//page/test.ts - example of my test route
const { data, error } = useFetchContent(id);
if (error) {
showError('error');
replace('/');
}
return <DisplayContent content={data} />
This is a swrFetchHook
// fetchContentHook
function useFetchContent(id: string): ContentDetail {
return useSWR<any>(`/api/content/${id}`, fetcherApiRoute);
}
const fetcherApiRoute = (url: string): Promise<any> => {
return axios(url)
.then((r) => r.data)
.catch((err) => {
console.info('error is ', err)
throw err
});
};
export default useFetchContent;
inside api route
export default async (req, res): Promise<ContentDetail> => {
const { id } = req.query;
if (req.method === 'GET') {
const fetchRealApi = await apiAxios(url);
if(fetchRealApi) {
// here depending on result of fetchRealApi i add some other fetch ...
return res.status(200).json({ ...fetchRealApi, complement: comp1 });
}
return res.status(500)
}
return res.status(500).json({ message: 'Unsupported method only GET is allowed' });
};
and finally api axios configuration
const apiAxios = axios.create({
baseURL: '/myBase',
});
apiAxios.interceptors.request.use(
async (req) => {
// HERE i am trying to get token from cookies
// and also HERE if token is expired i am trying to refresh token
config.headers.Authorization = token;
req.headers['Content-type'] = 'application/x-www-form-urlencoded';
return req;
},
(error) => {
return Promise.reject(error);
},
);
export default apiAxios;
I am stuck here because i cant find token during apiAxios.interceptors.request.use...
Did you know what i am doing wrong, and am i on a correct way to handle this behavior ?

To allow sending server cookie to every subsequent request, you need to set withCredentials to true. here is the code.
const apiAxios = axios.create({
baseURL: '/myBase',
withCredentials: true,
});

Nilesh's answer is right if your API is able to authorize requests based on cookies. Also it needs the API to be in the same domain as your frontend app. If you need to send tokens to the API (the one which is in the cookie), then you will need a small backend component often called BFF or Token Handler. It can extract the token from the cookie and put in an Authorization header.
At Curity we've created a sample implementation of such a Token Handler, of which you can inspire: https://github.com/curityio/kong-bff-plugin/ You can also have a look at an overview article of the Token Handler pattern.

Related

How can I attach my JWT token to every axios call?

I'm working on my very first nextjs application. I'm using an axios instance to handle my calls to backend
export const httpClient = axios.create({
baseURL: `${process.env.BASE_URL}`,
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
I'm also using next-auth to handle my authentication and authorization
const callbacks = {
async jwt(token, user) {
if (user) {
token.accessToken = user.access_token;
}
return token;
},
async session(session, token) {
session.accessToken = token.accessToken;
return session;
},
};
The call to the authentication endpoint is working correctly. If I console.log() the api response I can see the returned JWT token.
I'm now trying to attach that JWT token to every axios request but the call await getSession() is always null. I wrote following request interceptor
httpClient.interceptors.request.use(
async (config) => {
const session = await getSession();
if (session) { // this never evaluates to true. Session is always `null`
console.log(session);
config.headers.Authorization = `Bearer ${session?.accessToken}`;
}
return config;
},
(error) => {
return Promise.reject(error);
},
);
Following the docs, I wrapped my _app.js <Component> with
<Provider session={pageProps.session}>
<Component {...pageProps} />
</Provider>
Any ideas what I could try next?
Edit:
I call const [session, loading] = useSession(); on another part of the code (in the nextjs frontend) and there everything seems to be working
The problem is that if the axios call is made on the server side, then the interceptor will also be executed on the server side.
If that is the case, when calling getSession() on the server side, you also have to pass it the request or the context (see the note at the bottom of the manual entry for getSession)

Use Firebase Cloud Function for Create User(Remove Automatically Login When register user)

When user register from client side(mobile app) , user automatically login app i dont want auto login so, I did some research, I had to use a firebase cloud function to solve this.
But I get a few errors when calling the function , how can i fix these errors
First error :
Access to XMLHttpRequest at 'https://***.cloudfunctions.net/createUser' from origin 'http://localhost:8100' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
second error :
zone-evergreen.js:2845 POST https://****.cloudfunctions.net/createUser net::ERR_FAILED
Third Error
core.js:4197 ERROR HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: "https://***.cloudfunctions.net/createUser", ok: false, …}
Firebase Console log
2:02:23.363 ÖS
createUser
Function execution started
2:02:23.390 ÖS
createUser
Function execution took 28 ms, finished with status: 'crash'
cloud function :
const functions = require('firebase-functions');
const cors = require('cors')({ origin: true });
const admin = require('firebase-admin')
admin.initializeApp();
exports.createUser = functions.https.onRequest((request, response) => {
return cors(req, res, () => {
if (request.method !== "POST") {
response.status(405).send("Method Not Allowed");
} else {
let body = request.body;
const email = body.email;
const password = body.password;
admin.auth().createUser({
email: email,
emailVerified: false,
password: password,
})
.then((userRecord) => {
return response.status(200).send("Successfully created new user: " +userRecord.uid);
})
.catch((error) => {
return response.status(400).send("Failed to create user: " + error);
});
}
})
});
client side :
signUp(email,password){
let body = {
email : email,
password : password
}
this.http.post(
'https://****.cloudfunctions.net/createUser',
body
).subscribe(a=>{console.log("Work")});
}
EDIT (new cloud function code) :
1st and 2nd bug fixed 3 still continues but I can create users.
exports.createUser = functions.https.onRequest((req, res) => {
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Methods', ' POST, OPTIONS');
res.set('Access-Control-Allow-Headers', '*');
if (req.method === 'OPTIONS') {
res.end();
}
else {
let body = req.body;
console.log(body.email)
console.log("body.password")
const email = body.email;
const password = body.password;
admin.auth().createUser({
email: email,
emailVerified: false,
password: password,
})
.then((userRecord) => {
return res.status(200).send("Successfully created new user: " +userRecord.uid);
})
.catch((error) => {
return res.status(400).send("Failed to create user: " + error);
});
}
});
Cross origin error has many reason, first problem is your current url that probably like a ads url, please change the url pattern and clear browser cache. If you are using a VPN turn off it. The Chrome blocks url that contain ads url. This problem is not happen on production environment.
If your problem not fixed with top solution, use chrome in security disable mode.
Open start menu and type chrome.exe --disable-web-security and
make sure that this headers set in your backend
header('Access-Control-Allow-Origin: *');
header('Access-Control-Request-Method:*');
header('Access-Control-Allow-Headers: Origin,token, Authorization, X-Requested-With, Content-Type, Accept');
header('Access-Control-Allow-Credentials: true');

Not getting refresh_token when authenticating with paypal API

Node.js
Using paypal sandbox env
const PAYPAL_BASE_URL = "https://api.sandbox.paypal.com";
const PAYPAL_TOKEN_URL = `${PAYPAL_BASE_URL}/v1/oauth2/token`;
const tokenOptions = {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Access-Control-Allow-Credentials": true,
},
data: qs.stringify({ grant_type: "client_credentials" }),
auth: {
username: `${CLIENT_ID}`,
password: `${CLIENT_SECRET}`,
},
url: `${PAYPAL_TOKEN_URL}`,
};
async function getToken() {
return await axios(tokenOptions)
.then((res) => {
console.log(res.data.access_token);
console.log(res.data.expires_in);
console.log(res.data);
return res.data;
})
.catch((err) => {
console.log(err);
});
}
Response
"scope": "https://uri.paypal.com/services/invoicing https://uri.paypal.com/services/disputes/read-buyer https://uri.paypal.com/services/payments/realtimepayment https://uri.paypal.com/services/disputes/update-seller https://uri.paypal.com/services/payments/payment/authcapture openid https://uri.paypal.com/services/disputes/read-seller https://uri.paypal.com/services/payments/refund https://api.paypal.com/v1/vault/credit-card https://api.paypal.com/v1/payments/.* https://uri.paypal.com/payments/payouts https://api.paypal.com/v1/vault/credit-card/.* https://uri.paypal.com/services/subscriptions https://uri.paypal.com/services/applications/webhooks",
"access_token": "xxxxxxxx",
"token_type": "Bearer",
"app_id": "xxxx",
"expires_in": 32346,
"nonce": "2020-11-07T20:09:09Zmc3xM34owS0WsAI5rHVx2eOJb80xJ06Z6tFQx6LT_i0"
As you can see I am not getting the refresh_token. Reading the documentation I wanted to use the refresh token to get a new access_token when the expire time is getting close.
Should I not be getting a refresh_token here?
A client credentials request using a clientid/secret does not give a refresh_token.
Just repeat the same request when needed.
#Preston PHX
Adding a comment here as the comments does not allow that many characters.
Can I do something to get a refresh_token?
Problems I have is that I am using node as a proxy with http-proxy-middleware.
app.use("/", PROXY_TO_PAYPAL);
On app startup I authenticate and store the token in a variable that will expire.
console.log(`Starting proxy at ${HOST}:${PORT}`);
console.log(`PAYPAL_TOKEN_URL: ${PAYPAL_TOKEN_URL}`);
// set the token on startup
getToken().then((data) => {
token = data.access_token;
console.log(`expires_in ${parseInt(data.expires_in)} seconds`);
const timeoutDate = getTimeoutDate(parseInt(data.expires_in) * 1000);
tokenTimeOut = timeoutDate.getUTCMilliseconds();
console.log(`Token times out at ${timeoutDate.toUTCString()}`);
});
});
I need to update the token BEFORE it expires, otherwise I risk that a proxy request will fail as it could potentially use an expired token.
I created a scheduler that will watch the when the token is getting close to expire and will renew it (call the authentication again), like say 15 min before.
async function getToken() {
return await axios(tokenOptions)
.then((res) => {
console.log(res.data.access_token);
console.log(res.data.expires_in);
console.log(res.data);
return res.data;
})
.catch((err) => {
console.log(err);
});
}
cron.schedule("* * * * *", function () {
console.log("running a task every minute");
// refresh token
if (tokenTimeOut - new Date().getUTCMilliseconds() < 900*1000) {
getToken().then((data) => {
token = data.access_token;
const timeoutDate = getTimeoutDate(parseInt(data.expires_in) * 1000);
tokenTimeOut = timeoutDate.getUTCMilliseconds();
console.log(`Token times out at ${timeoutDate.toUTCString()}`);
});
}
});
I am adding the Bearer header on each request bore it is proxied
const middlewareOptions = {
target: `${PAYPAL_BASE_URL}`, // target host
changeOrigin: true,
onProxyReq(proxyReq, req, res) {
if (!token) {
getToken().then((data) => {
token = data.access_token;
tokenTimeOut = data.expires_in;
});
}
proxyReq.setHeader("Authorization", `Bearer ${token}`);
},
};
I might get the same token again until I am closer to the expire date (I think) - which could work. Eventually I would get a new token and a new expire time.
I am confused about the documentation. Here they talk about the refresh_token https://developer.paypal.com/docs/connect-with-paypal/integrate/#6-get-access-token
But here there are not refresh_token
https://developer.paypal.com/docs/api/get-an-access-token-curl/
What I wanted to do is when I am close to the expire time I would use the refresh_token to get a new token.

Automatic request signing with API Gateway REST API and Amplify

This https://aws-amplify.github.io/docs/js/api#signing-request-with-iam says AWS Amplify provides the ability to sign requests automatically ..is this the same with API gateway REST requests that are restricted by Cognito?
auth.currentSession().then(token => {
console.log('>>>>', token.getIdToken().getJwtToken());
authToken = token.getIdToken().getJwtToken();
const myInit = { // OPTIONAL
headers: {
Authorization: authToken
},
response: true,
};
api.get(apiName, path, myInit).then(response => {
// Add your code here
console.log(response);
}).catch(error => {
console.log(error.response);
});
}
);
but I get Authorization header requires 'Credential' parameter. Authorization header requires 'Signature'
But in angular this does not work as Auth.currentSession() does not compile
endpoints: [
{
name: 'test',
endpoint: 'https://xyz.execute-api.us-west-2.amazonaws.com/test',
custom_header: async () => {
// Alternatively, with Cognito User Pools use this:
return {Authorization: (await Auth.currentSession()).idToken.jwtToken};
}
}
]
}
Resolved had typo with the request url it had to be /items/:test where test was the partition name in dynamo, also the
headers: {
Authorization: token
},
is not required:
https://github.com/aws-amplify/amplify-js/issues/2810#issuecomment-470213459

how to integrate passport-facebook and jwt without session?

I want to use passport-github or facebook login with jwt token, without using saving sessions on the server.
But we have two requests from frontend:
app.get('/auth/facebook',
passport.authenticate('facebook'));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
how to handle the frontend codes?
In normal case, we only have one request
axios.post(`${API_URL}/auth/login`, { email, password })
.then(response => {
cookie.save('token', response.data.token, { path: '/' });
dispatch({ type: AUTH_USER });
window.location.href = CLIENT_ROOT_URL + '/dashboard';
})
.catch((error) => {
errorHandler(dispatch, error.response, AUTH_ERROR)
});
}
so we can save the token locally. but for passport-facebook, we have two requests('/auth/facebook' and '/auth/facebook/callback'). So how to save the token locally?
First of all, I think the GET request will not work. You need to use an a link to trigger the /auth/login.
<a href="http://localhost:5150/auth/facebook">
To send the token to the client, you should redirect to a client page with the jwt stored in cookie.
const token = user.generateJwt();
res.cookie("auth", token);
return res.redirect(`http://localhost:3000/socialauthredirect`);
and at the client landing page extract the jwt and save it to local storage.
class SocialAuthRedirect extends Component {
componentWillMount() {
this.props.dispatch(
fbAuthUser(getCookie("auth"), () => {
document.cookie =
"auth=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
this.props.history.push("/profile");
})
);
}
render() {
return <div />;
}
}