ERR_CERT_AUTHORITY_INVALID with axios in browser - javascript

I set a API https server with self-signed cert.
And I am setting a axios https-api call in a function that will run on client's side only. Then when it called the api, it threw an ERR_CERT_AUTHORITY_INVALID
I noticed many articles have motioned below code on how to by pass this.
// At instance level
const instance = axios.create({
httpsAgent: new https.Agent({
rejectUnauthorized: false
})
});
instance.get('https://something.com/foo');
// At request level
const agent = new https.Agent({
rejectUnauthorized: false
});
axios.get('https://something.com/foo', { httpsAgent: agent });
But this popular code only works on server side. And then I noticed that this error is triggered by chrome. So chrome should be the root cause, although I was writing javascript
So, I noticed people have talked about to configure either below one of these. I tried all and doesnt work at all
1.Insecure content
=> this is about http only. So this is not a solution for http
enable the domain when the site pop up.
=> This is the tricky part. Since I host my code on vercel. So at first, it is identified as secure. The error only prompt when I click submit button in my website in which it calls my API server which is in other domain, with a self-signed cert only.
For example:
const handleSubmit = async (e) => {
e.preventDefault();
const newPost = {
theme: theme,
content: content,
};
try {
const res = await axios.post(
`${process.env.NEXT_PUBLIC_API_URL}/${username}`,
newPost
);
} catch (err) {
console.log(err);
}
Is there a easy way to let the chrome be able to call the api?

Related

Axios doesn't create a cookie even though set-cookie header is there?

Front-End: [Axios]
const formSubmit = async (e) => {
e.preventDefault()
const formData = new FormData(e.target)
const email = formData.get('email')
const password = formData.get('password')
try {
const res = await axios.post('http://172.16.2.19:3001/api/v1/auth/login', {
email,
password,
})
console.log(res.data) // its okay, I can login if email & password are correct.
} catch (error) {
console.log(error)
}
}
Back-End [Nodejs ExpressJs]:
Inside App.js:
const cors = require('cors')
app.use(cors({ credentials: true }))
Inside Login.js (/auth/login endpoint):
// ... code, then... if email & password are correct:
// 3600000ms = 1hour
res.cookie('jwt', token, { httpOnly: true, expires: new Date(Date.now() + 3600000 })
res.status(200).json({
status: 'success'
token,
data: userDoc,
})
Then, when I login in my browser:
I can login successfully, but no cookies will be created, see:
The front-end http service (react app) is running on http://172.16.2.19:3000
The back-end http service (expressjs) is running on http://172.16.2.19:3001
The axios requests I'm sending from the front-end are requesting: http://172.16.2.19:3001
So what's the problem?
The problem that no cookies are getting created in the browser is preventing me from continuing to design the front-end application, because if I wanted to request anything from my API, I have to be authenticated, all the routes on the API I made are protected, so if I wanted to request anything from the API, I will have to send my jwt token along with the request.
edit **:
here's the response from the /auth/login endpoint after successfully logging in:
I am using brave browser, the latest version.
I tried this on firefox, it has the same behavior.
GUYS GUYS GUYS I found it!!!! after 3 hours of researching, let me save your time:
For anyone having the same problem, all you have to do is
change your backend code from:
const cors = require('cors')
app.use(cors({ credentials: true }))
to
app.use(cors({ credentials: true, origin: true }))
and make sure you're using withCredentials: true on the front-end with every request (the login POST method and all the other requests that requires authentication)
why?
setting origin property to true is going to reflect the request origin, the origin property can be a string if you wanted to specify a particular domain, ex: http://localhost:3000. But if you have more than one client, setting this to true is a wise choise.
and for those of you wondering about mobile devices in case of specifying a string for the origin field with one particular domain. This problem of cors only happens in browsers, any other client doesn't use that CORS policy.
I would check by passing {withCredentials: true} as the third argument to the axios method to allow the browser to set the cookie via the request.
I don't think it is correct to use the backend to save cookies, as cookies is a browser feature separate from the database. I might be wrong though. When the post is successful, res will return a token. You save this token in the browser's local storage.
const formSubmit = async (e) => {
e.preventDefault()
const formData = new FormData(e.target)
const email = formData.get('email')
const password = formData.get('password')
try {
const res = await axios.post('http://172.16.2.19:3001/api/v1/auth/login', {
email,
password,
})
//browsers local storage
localStorage.setItem('access_token',res.data.access);
localStorage.setItem('refresh_token',res.data.refresh);
console.log(res.data) // its okay, I can login if email & password are correct.
}
You will then have to create an authorization header as such
headers:{
Authorization: localStorage.getItem('access_token')
? 'JWT '+localStorage.getItem('access_token')
: null
}

Firebase 'internal' error when using Functions and Admin

I am having trouble with some functions that have been working previously. I have created a function to let the user create another user and it was working fine. Functions that do not use Admin works fine just as before.
However, due to some Firebase updates, the function broke and I am getting an 'internal' error. I had to change import firebase from "firebase/app"; to import firebase from "firebase/compat/app"; as well as import "firebase/compat/functions"; but that did not work, as well as updating firebase to "^9.8.1", but that did not work either.
Here is my web function:
async registerUser(userInfo) {
const functions = getFunctions();
const createUser = httpsCallable(functions, "createUser");
// Used to have this:
// let createUser = firebase.functions().httpsCallable("createUser");
//Call to function where it breaks.
return await createUser({
//User data here
})
.then(
//stuff happens here
).catch((error) => {
//Error happens here
})
}
Here is the function that was already deployed in Firebase:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.createUser = functions.https.onCall(async (userData) => {
return await admin
.auth()
.createUser(userData)
.then(() => {
return { isError: false };
})
.catch((error) => {
return { isError: true, errorMessage: error };
});
});
Here is the response log:
Request Method: OPTIONS
Status Code: 403
Referrer Policy: strict-origin-when-cross-origin
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
content-length: 305
content-type: text/html; charset=UTF-8
date: Tue, 24 May 2022 16:04:14 GMT
server: Google Frontend
You can refer to the Admin SDK error handling page and check the Structure of the error, the refer to policy: strict-origin-when-cross-origin part gives us a good starting point.
But first, we can see the root cause of this issue as in the same page we have a table for Platform error codes where:
INTERNAL | Internal server error. Typically a server bug.
In the Firebase Admin Authentication API Errors we have a similar table where:
auth/internal-error | The Authentication server encountered an unexpected error while trying to process the request. The error message should contain the response from the Authentication server containing additional information. If the error persists, please report the problem to our Bug Report support channel.
Now talking about the cors message, this question suggests:
Consider importing like this, as shown in the samples:
const cors = require('cors')({origin: true});
And the general form of your function will be like this:
exports.fn = functions.https.onRequest((req, res) => {
cors(req, res, () => {
// your function body here - use the provided req and res from cors
})
});
You might need to make some changes to the structure as the question has some time, so I will also leave the reference to the Call functions via HTTP requests and check if it works for you.
I will follow up if you provide updates and consider submitting a bug report as previously mentioned by the documentation if this is necessary.

Cookie not set in request with NodeJS and NextJS

I'm developing a fullstack app with Node + Express backend and NextJS front end (separate servers) and am having trouble requesting the browser to attach the cookie vended down as part of the response header from the node server. Here's the setup:
Node server is running on localhost:3000 and NextJs server is running on localhost:3001.
I have set up alias in etc/hosts to route someAlias.com to 127.0.0.1.
Using the front end UI (port 3001) I was able to vend the cookie with JsHttp's cookie module with the following code from the backend (port 3000):
import { serialize } from 'cookie';
...
const cookie = serialize(TOKEN_NAME, TOKEN_VAL, {
httpOnly: true,
sameSite: 'none',
});
I was able to observe the Set-Cookie header in the response.
However, in the subsequent requests, I did not see the cookie being attached. I have tried fiddling with the above cookie serialization params with no success:
Here are the arguments I've tried:
domain: ['.someAlias.com:3000', '.someAlias.com:3001']
path: '/'
domain: '.someAlias.com'
I have a feeling it might just be due to front end and back end server ports being different, but all requests have been initiated on the client side going to localhost:3000 (backend port). So not sure what I've possibly done wrong here.
====== UPDATE =======
I've run a couple more experiments, and found out that when I'm accessing a URL directly, NextJs renders the page server-side. When I'm transitioning between pages within the app, the page is rendered client-side where it queries the backend port 3000 directly. Unfortunately in neither scenario did I see any cookie being set...
the cookies must be sent directly from the browser to the server , which is not the case when you use nextJs . because when you access to your app next js will server side render your page and then the request will be sent from nextjs server to your nodejs server so the browser will send the cookies to nextjs server not to your nodejs server .
the solution is to send cookies manually from nextjs server to nodejs .
example with fetchApi and getServerSideProps function:
export async function getServerSideProps(context){
try{
const res = await fetch(`your-api-endpoint`, {
method: 'GET',
credentials:'include',
headers: {
'Access-Control-Allow-Credentials': true,
Cookie: context.req.headers.cookie
},
})
const data = await res.json()
if(!res.ok){
throw data
}
}catch(err){
return console.log(err)
}
return {
props: {
data
}
}
}
SAMESITE NONE
If you use SameSite=none you also need to use Secure, meaning SSL must also be used. You are then saying that your 2 servers are from unrelated domains, which is probably not what you want, since browsers will drop cookies aggressively.
LOCAL PC OPTIONS
Use these settings initially on your development computer, and ensure that all URLs used in the browser and Ajax calls use http://somealias.com:3000 and http://somealias.com:3001 rather than localhost. Cookies will then stick on a development computer.
Domain=.somealias.com
Path=/
SameSite=strict
HTTP Only
DEPLOYED OPTIONS
When you deploy to a proper environment, also use SSL and set the cookie option Secure. Most importantly, the two domains must meet hosting prerequisites of sharing the same base domain, eg:
https://www.example.com
https://api.example.com
This ensures that cookies issued are considered first party and in the same site, so that they are not dropped. If preconditions are not met, there is nothing you can do in code to fix the problem.
SIMILAR RESOURCE
This Curity code example uses local development domains and same site cookie settings similar to those I've used above and may be useful to compare against.
You should set serialized cookie with res.set Express method.
Alternatively, you can use res.cookie method without additional cookie package like this:
res.cookie(TOKEN_NAME, TOKEN_VAL, {
httpOnly: true,
sameSite: 'none',
});
Note, you shouldn't worry about different ports on the same domain, since cookies are not isolated by port but domain only. No matter what port you use, cookies should be visible.
Since you said, "I was able to observe the Set-Cookie header in the response", I believe your node.js setting correct.
When you get response from node js, you need to set cookies, which can be done with a npm packages easily. I will demonstrate with js-cookie:
import Cookies from "js-cookie";
you write a reusable function to set the cookies:
// what ever your project returns
setSession(authResult) {
//converting everything to miliseconds
const expiresAt =
JSON.stringify(authResult.expiresIn * 1000) + new Date().getTime();
// I just put properties. I dont know how project sets
Cookies.set("user", authResult.idTokenPayload);
Cookies.set("jwt", authResult.idToken);
Cookies.set("expiresAt", expiresAt);
}
Everytime you make request you have to set headers. You have to retrieve cookies based on if you are on browser or on server. So you have to write a function if you are on server. Since I demonstrated how to set cookies with js-cookies, you can get the cookies easily on the browser. This reusable function to retrieve the cookie if you are on the server:
// cookieKey: I set three "user", "jwt","expiresAt"
export const getCookieFromReq = (req, cookieKey) => {
console.log("req.headers", req.headers);
// cookies are attached to the req.header.cookie.
const cookie = req.headers.cookie
.split(";")
.find((c) => c.trim().startsWith(`${cookieKey}=`));
if (!cookie) return undefined;
return cookie.split("=")[1];
};
Now you have to write a function to set the headers:
import Cookies from "js-cookie";
import { getCookieFromReq } from "./directoryOf";
export const setAuthHeader = (req) => {
const token = req ? getCookieFromReq(req, "jwt") : Cookies.getJSON("jwt");
if (token) {
return {
headers: { authorization: `Bearer ${token}` },
};
}
return undefined;
};
Now when you make request, you have to use this setAuthHeader. For example:
await axiosInstance
.post("/blogs", blogData, setAuthHeader())
.then((response) => response.data)
.catch((error) => rejectPromise(error));

Modify POST request body in service worker

I am trying to add a parameter to the body of a POST request in a service worker but the original body is send. I use the following code
let token = '';
self.addEventListener('message', function (event) {
if (event.data && event.data.type === 'SET_TOKEN') {
token = event.data.token;
}
});
self.addEventListener('fetch', function (event) {
const destURL = new URL(event.request.url);
const headers = new Headers(event.request.headers);
if (token) headers.append('Authorization', token);
if (destURL.pathname === '/logout/') {
const promiseChain = event.request.json().then((originalBody) => {
return fetch(event.request.url, {
method: event.request.method,
headers,
// this body is not send to the server but only the original body
body: JSON.stringify(Object.assign(originalBody, { token })),
});
});
event.respondWith(promiseChain);
return;
}
const authReq = new Request(event.request, {
headers,
mode: 'cors',
});
event.respondWith(fetch(authReq));
});
Generally speaking, that should work. Here's a very similar live example that you can run and confirm:
https://glitch.com/edit/#!/materialistic-meadow-rowboat?path=sw.js%3A18%3A7
It will just POST to https://httpbin.org/#/Anything/post_anything, which will in turn echo back the request body and headers.
If your code isn't working, I would suggest using that basic sample as a starting point and slowing customizing it with your own logic. Additionally, it would be a good idea to confirm that your service worker is properly in control of the client page when its makes that request. Using Chrome DevTool's debugger interface, you should be able to put breakpoints in your service worker's fetch event handler and confirm that everything is running as expected.
Taking a step back, you should make sure that your web app isn't coded in such a way that it requires the service worker to be in control in order to go things like expire auth tokens. It's fine to have special logic in the service worker to account for auth, but make sure your code paths work similarly when the service worker doesn't intercept requests, as might be the case when a user force-reloads a web page by holding down the Shift key.

How to use axios with a proxy server to make an https call?

It's basically the same question here.
I'm using a browser. The following code is compiled by webpack.
I tried this:
const axios = require('axios');
var res = await axios.get('https://api.ipify.org?format=json', {
proxy: {
host: 'proxy-url',
port: 80,
auth: {username: 'my-user', password: 'my-password'}
}
});
console.log(res.data); // gives my ip and not the proxy's one.
I also tried this with the same code, but it did not work:
const axios = require('axios-https-proxy-fix');
Then, I tried with httpsAgent:
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent')
var agent = new HttpsProxyAgent('http://my-user:my-pass#proxy-url:port');
var res = await axios.get('https://api.ipify.org?format=json', {
httpsAgent: agent,
});
console.log(res.data); // gives my ip and not the proxy's one.
Is this a bug? Am I cursed or maybe do I have a problem reading the documentation?
if you want to use axios and work-around the issue then consider using https-proxy-agent for proxy agent as mentioned in link
const HttpsProxyAgent = require("https-proxy-agent"),
axios = require("axios");
const httpsAgent = new HttpsProxyAgent({host: "proxyhost", port: "proxyport", auth: "username:password"})
//use axios as you normally would, but specify httpsAgent in the config
axios = axios.create({httpsAgent});
There is an open issue on axios's github page.
The issue is labeled as bug from 31 Mar and is not resolved yet.
So it seems you are not cursed, just a bug in axios.
You may add your details to that thread in order to dev team prioritize this issue.
In case you cannot wait for this issue to be resolved, you may consider using fetch API like #Sumi Straessle proposed in the comments.
axios's config.proxy is Node.js only. I think it is meaningless in browsers.
You can check lib/adapters/xhr.js and will find nothing related to proxy there.
If you are trying to access an HTTPS URL by an HTTP proxy, you need to create an HTTPS-HTTP tunnel.
I found the solution for this issue in this post: https://janmolak.com/node-js-axios-behind-corporate-proxies-8b17a6f31f9d. Now it works perfectly!

Categories

Resources