When saving data to Firebase database with a Firebase cloud function, I'd like to also write the IP address where the request comes from.
However, req.connection.remoteAddress always returns ::ffff:0.0.0.0. Is there a way to get the actual IP address of the client that makes the request?
The clients IP is in request.ip.
Example:
export const pay = functions.https.onRequest((request, response) => {
console.log(`My IP is ${request.ip}`);
});
If you are looking for the client ip thru firebase hosting you should use the header fastly-client-ip there will be the real client ip.
The IP address seems to be available in req.headers["x-forwarded-for"].
See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
Note that if there are proxies in between the interim ip addresses are concatenated towards the end:
X-Forwarded-For: <client_ip>, <proxy_1 : actual-ip-as-seen-by-google> ...
This worked for me:
const express = require('express');
const logIP = async (req : any, res : any, next : any) => {
const clientIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.headers['fastly-client-ip'];
}
const logIPApp = express();
logIPApp.use(logIP);
exports.reportIP = functions.https.onRequest(logIPApp);
If you are looking for ip address or headers in cloud callable function, then you can get information inside context object.
for ex:
exports.testUser = async (data, context) => {
console.log('------------------------::context::------------------');
if(context.rawRequest && context.rawRequest.headers){
console.log(context.rawRequest.headers);
}
}
In the headers you can get ip : header { 'x-appengine-user-ip' : 'xxip' } etc.
This is what worked for me using Firebase Cloud Functions:
const clientIP = req.headers['x-appengine-user-ip'] || req.header['x-forwarded-for']
Note, that this doesn't work locally!
Related
I am seeking the best manner in which this should be done.
I have a https based GCF Function such as:
// google function
exports.someFunction = async (req, res) => {
try {
...
// some logic and access
res.status(200).send(data)
}
catch(error) {
res.status(400).send(error.message)
}
}
The API serverless function in Next.js is using axios. Is that the recommended method?
// next.js pages/api/call-google-func.js
async function handler(req, res) {
try {
const url = '....' //https://gcp-zone-project-xx834.cloudfunctions.net/someFunc
const res = await axios.get(url)
const resdata = res.data
res.status(200).send(resdata)
}
catch(error) {
res.status(400).send(error)
}
}
The problem with this method is that the GCF must have public access. How can we set up to access the GCF from Next.js by passing credentials as environment variables. Thanks
I think for this situation where a Vercel Serverless Function must communicate with the outside world, a Google Cloud Function, you'd want to create a JWT token on Vercel's side to pass to Google's side which you would then need to verify. I think Exchanging a self-signed JWT for a Google-signed ID token would be what you need.
Since either side doesn't know about the other, Google's IAM normal cloud privileges for allowing GCG<>GCF communication wouldn't apply here.
I have to send a user IP into the logging service on page load. I use static mode in my next.js app.
I have an idea to use an edge function to get visitor IP, pass it as header and then read this value on the frontend. How can I read it? Is there any other reasonable option to pass information like IP or geo into the frontend?
Thanks!
As I know you can get IP by using getServerSideProps but this approach change page from static to server render more info. You use it inside the page file like this:
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
context in this case contains user ip you can get it like this:
const ip = context.req.headers["x-forwarded-for"] || context.req.socket.remoteAddress;
and than you can pass it to the frontend or send another request with this ip:
export async function getServerSideProps(context) {
const ip = context.req.headers["x-forwarded-for"] || context.req.socket.remoteAddress;
return {
props: { ip }, // will be passed to the page component as props
}
}
and then catch it and use it in your component:
export default function MyPage({ ip }) {...}
Sending ip from getServerSideProps to the server
you can get ip also on you backend if you using Node.js and express inside req:
const myFn = (req, res, next) => {
const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
}
but if you send request from getServerSideProps there will be your Next.js server ip not user ip. So you need manually add header with ip when you sending request from the getServerSideProps for example with axios (it is better to use interceptors for convenience):
axios.post("/some/route", {...},{
headers: { "client-ip": ctx.req.headers["x-forwarded-for"] || ctx.req.socket.remoteAddress },
});
and than on the Node.js backend:
const myFn = (req, res, next) => {
const ip = req.headers["client-ip"] || req.headers["x-forwarded-for"] || req.socket.remoteAddress;
}
and with all this you can get your user ip in all scenarios.
Note
When you working with localhost you will get ::1 as ip but in production it works as expected.
I'm making a weather app, and I get the client IP with IPIFY, but this loses SSR, or I use SSR and I get the server IP. Someone told
me that I could use the header x-forwarded-for and then, with this value, make the weather API call with SSR.
The problem is I'm using only nextjs, no backend here, and second, I don't know how to call or use x-forwarded-for in the front to get the client IP.
Is this possible?
How I can implement that?
I'm using vercel to deploy the app.
Updated answer as request.connection is deprecated since Node.js v13.0.0. So we should now use request.socket instead.
export const getServerSideProps = async ({ req }) => {
const forwarded = req.headers['x-forwarded-for'];
const ip = typeof forwarded === 'string' ? forwarded.split(/, /)[0] : req.socket.remoteAddress;
console.log(ip);
return {
props: { ip },
};
};
Here you go:
export async function getServerSideProps({ req }) {
const forwarded = req.headers["x-forwarded-for"]
const ip = forwarded ? forwarded.split(/, /)[0] : req.connection.remoteAddress
return {
props: {
ip,
},
}
}
I think you can get them through getServerSideProps.
export async function getServerSideProps({ req }) {
console.log(req.headers) //see if you have those headers
return {
props: {
headers
},
}
}
function Page({ headers }) {
// Render data...
}
I've deployed a small HTTP endpoint via Google Cloud Run. It is working fine when I turn off the authentication.
I now want to turn it on so that it is only callable by my Firebase Cloud Function. If I understand it right, I just have to add the correct service account mail address in the IAM settings of the Cloud Run as "Cloud Run invoker".
But which address is the correct one?
I've tried all addresses that I have found in Firebase Console -> Project Settings -> Service Accounts.
I think you can check the specific firebase function. In the UI, the service account used should be listed.
By default, GCF functions all use <project_id>#appspot.gserviceaccount.com
Thanks to #AhmetB - Google and #whlee's answer I got it working. Basically it is enough adding an Authorization Bearer token to the request, which you can get from a special endpoint: https://cloud.google.com/run/docs/authenticating/service-to-service#nodejs
Then you just have to add the service account of the function to the IAM list of the Cloud Run container: <project_id>#appspot.gserviceaccount.com
The nodejs example is using the deprecated request library, so here is my version using axios:
const getOAuthToken = async (receivingServiceURL: string): Promise<string> => {
// Set up metadata server request
const metadataServerTokenURL = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
const uri = metadataServerTokenURL + receivingServiceURL;
const options = {
headers: {
'Metadata-Flavor': 'Google'
}
};
return axios.get(uri, options)
.then((res) => res.data)
.catch((error) => Promise.reject(error));
}
Then you can just use the token in the actual request:
const url = `...`;
const token = await getOAuthToken(url);
axios.post(url, formData, {
headers: {
Authorization: `Bearer ${token}`,
}
}).then(...).catch(...);
#luhu 's answer was really helpful. I'd like to add just one note for those whose are willing to test with the emulators locally first. The metadata server (which is actually http://metadata.google.internal now) as they state
does not work outside of Google Cloud, including from your local machine.
As a workarund, you can use the google-auth-library and then get the token directly if you prefer sticking with axios. Remember to set the GOOGLE_APPLICATION_CREDENTIALS env variable pointing to a service account secret first as it's the only way to make it work (I've tested setting the credential field during admin.initializeApp() but didn't seem to like it).
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
const url_origin = '....'
const client = await auth.getIdTokenClient(url_origin);
const token = (await client.getRequestHeaders()).Authorization;
const url = '....'
const response = await axios.get(
url,
{
headers: {
Authorization: token,
},
}
);
I am working on a simple blog application using Meteor. However, users will be allowed to post anonymously. I want to record IP addresses of the senders along with their blog posts.
I could not find any way to access IP address. How can I achieve that?
if (Meteor.isServer) {
var app = __meteor_bootstrap__.app,
Fiber = Npm.require('fibers'),
headers = {};
app.use(function(req, res, next) {Fiber(function () {
headers.remoteAddress = req.connection.remoteAddress;
console.info(req.connection.remoteAddress);
console.info(res.socket._peername);
next();
}).run();
});
}