Next.js - how to pass a visitor ip into the frontend - next.js

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.

Related

oak on Deno - How do I build a URL to a route?

I come from a land of ASP.NET Core. Having fun learning a completely new stack.
I'm used to being able to:
name a route "orders"
give it a path like /customer-orders/{id}
register it
use the routing system to build a URL for my named route
An example of (4) might be to pass a routeName and then routeValues which is an object like { id = 193, x = "y" } and the routing system can figure out the URL /customer-orders/193?x=y - notice how it just appends extraneous key-vals as params.
Can I do something like this in oak on Deno?? Thanks.
Update: I am looking into some functions on the underlying regexp tool the routing system uses. It doesn't seem right that this often used feature should be so hard/undiscoverable/inaccessible.
https://github.com/pillarjs/path-to-regexp#compile-reverse-path-to-regexp
I'm not exactly sure what you mean by "building" a URL, but the URL associated to the incoming request is defined by the requesting client, and is available in each middleware callback function's context parameter at context.request.url as an instance of the URL class.
The documentation provides some examples of using a router and the middleware callback functions that are associated to routes in Oak.
Here's an example module which demonstrates accessing the URL-related data in a request:
so-74635313.ts:
import { Application, Router } from "https://deno.land/x/oak#v11.1.0/mod.ts";
const router = new Router({ prefix: "/customer-orders" });
router.get("/:id", async (ctx, next) => {
// An instance of the URL class:
const { url } = ctx.request;
// An instance of the URLSearchParams class:
const { searchParams } = url;
// A string:
const { id } = ctx.params;
const serializableObject = {
id,
// Iterate all the [key, value] entries and collect into an array:
searchParams: [...searchParams.entries()],
// A string representation of the full request URL:
url: url.href,
};
// Respond with the object as JSON data:
ctx.response.body = serializableObject;
ctx.response.type = "application/json";
// Log the object to the console:
console.log(serializableObject);
await next();
});
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
function printStartupMessage({ hostname, port, secure }: {
hostname: string;
port: number;
secure?: boolean;
}): void {
if (!hostname || hostname === "0.0.0.0") hostname = "localhost";
const address =
new URL(`http${secure ? "s" : ""}://${hostname}:${port}/`).href;
console.log(`Listening at ${address}`);
console.log("Use ctrl+c to stop");
}
app.addEventListener("listen", printStartupMessage);
await app.listen({ port: 8000 });
In a terminal shell (I'll call it shell A), the program is started:
% deno run --allow-net so-74635313.ts
Listening at http://localhost:8000/
Use ctrl+c to stop
Then, in another shell (I'll call it shell B), a network request is sent to the server at the route described in your question — and the response body (JSON text) is printed below the command:
% curl 'http://localhost:8000/customer-orders/193?x=y'
{"id":"193","searchParams":[["x","y"]],"url":"http://localhost:8000/customer-orders/193?x=y"}
Back in shell A, the output of the console.log statement can be seen:
{
id: "193",
searchParams: [ [ "x", "y" ] ],
url: "http://localhost:8000/customer-orders/193?x=y"
}
ctrl + c is used to send an interrupt signal (SIGINT) to the deno process and stop the server.
I am fortunately working with a React developer today!
Between us, we've found the .url(routeName, ...) method on the Router instance and that does exactly what I need!
Here's the help for it:
/** Generate a URL pathname for a named route, interpolating the optional
* params provided. Also accepts an optional set of options. */
Here's it in use in context:
export const routes = new Router()
.get(
"get-test",
"/test",
handleGetTest,
);
function handleGetTest(context: Context) {
console.log(`The URL for the test route is: ${routes.url("get-test")}`);
}
// The URL for the test route is: /test

Get the client IP on NextJS and use SSR

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...
}

How to get client IP address in a Firebase cloud function?

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!

Iron Route - Server Side, access this.params from onBeforeAction()

I need to serve some REST API Endpoints from my meteor application.
Endpoints must be accessible on the server side, so I'm using Iron router for server side routing.
All works great, but now I need access to the this.params for permission checking.
My current route:
Router.route('myServerRoute', {
where: "server",
path: '/api/v1/doit/:partner',
onBeforeAction: function(req, res, next) {
API.beforeAction(req, res, next, ['admin','API']);
}
})
The API.beforeAction is a function I'm using to validate the user token (This token is in one of the headers)
This function check if the token is valid and if that user have one of the roles from the 4th parameter.
The :partner is the name of the partner that use the API.
Let say that :partner is 'store1' (/api/v1/doit/store1)
I want to verify that only users that have the store1 role will be able to access the /api/v1/doit/store1 URL
So I want to pass the value of the :partner parameter to the API.beforeAction function
On the onBeforeAction function, I don't have access to the this.params (it is empty)
Some suggested to access the params using Router.current()
But this is a client call, and it is not available server side.
I can use req.url, parse it and get the partner name. but I don't like to do the parsing myself when I know that Iron Route already parsed this URL
Any suggestions how to get the URL parameters inside the onBeforeAction?
You don't need to do permission checking in your onBeforeAction. I implemented my API with Iron Router.
In the example bellow I handle a get request with an API key and return informations or error code.
Router.route('/api/thing/:apikey', { where: 'server' })
.get(function getThing () {
if (typeof this.params.apikey === 'undefined' || this.params.apikey.length != 16 || !Userprofile.findOne({ apiKey: this.params.apikey })) {
this.response.statusCode = 403;
return this.response.end('Not authorized');
}
const things = Thing.find({ owner: Userprofile.findOne({ apiKey: this.params.apikey }).owner }).fetch();
if (things.length > 0) {
this.response.statusCode = 200;
return this.response.end(JSON.stringify(things));
} else {
this.response.statusCode = 200;
return this.response.end('No things found');
}
});

How to record user IP address to database?

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();
});
}

Resources