Where to set Sentry's setUser in Next.js app?

Viewed 853

I have been trying to set user data into Sentry's scope globally, so every time there's an error or event, user info is passed to it.

My app is built in Next.js, so naturally I added the config as it is in Sentry's documentation for Next.js.

I haven't got the idea on where to add the Sentry.setUser({id: user.Id}) method in order for it to set the user globally.

So far I have added it to the Sentry's _error.js file, inside the getInitialProps method:

import NextErrorComponent from 'next/error';

import * as Sentry from '@sentry/nextjs';
import { getUser } from '../lib/session';

const MyError = ({ statusCode, hasGetInitialPropsRun, err }) => {
    if (!hasGetInitialPropsRun && err) {
        Sentry.captureException(err);
    }

    return <NextErrorComponent statusCode={statusCode} />;
};

MyError.getInitialProps = async (context) => {
    const errorInitialProps = await NextErrorComponent.getInitialProps(context);

    const { req, res, err, asPath } = context;

    errorInitialProps.hasGetInitialPropsRun = true;

    const user = await getUser(req, res);

    // Set user information
    if (user) {
        console.log('Setting user');
        Sentry.setUser({ id: user.Id });
    }
    else {
        console.log('Removing user');
        Sentry.configureScope(scope => scope.setUser(null));
    }

    if (res?.statusCode === 404) {
        return errorInitialProps;
    }


    if (err) {
        Sentry.captureException(err);

        await Sentry.flush(2000);

        return errorInitialProps;
    }

    Sentry.captureException(
        new Error(`_error.js getInitialProps missing data at path: ${asPath}`),
    );
    await Sentry.flush(2000);

    return errorInitialProps;
};

export default MyError;

But when trying to log errors, the user info doesn't show in Sentry, only the default user ip:

enter image description here

I have also tried setting the user after successful login, and still nothing..

Help is appreciated!!

3 Answers

I would suggest using the callback handler to set your Sentry user context.

import { handleAuth, handleLogin, handleCallback } from "@auth0/nextjs-auth0";
import * as Sentry from "@sentry/nextjs";
import { NextApiHandler } from "next";

const afterCallback = (_req, _res, session, _state) => {
  Sentry.setUser({
    id: session.user.sub,
    email: session.user.email,
    username: session.user.nickname,
    name: session.user.name,
    avatar: session.user.picture,
  });
  return session;
};

const handler: NextApiHandler = handleAuth({
  async login(req, res) {
    await handleLogin(req, res, {
      returnTo: "/dashboard",
    });
  },
  async callback(req, res) {
    try {
      await handleCallback(req, res, { afterCallback });
    } catch (error) {
      res.status(error.status || 500).end(error.message);
    }
  },
});

export default Sentry.withSentry(handler);

You can set the user in Sentry right after successful login

const handleLogin = {
  try {
    const res = await axios.post("/login", {"john@example.com", "password"})
    if (res && res?.data) {
      // Do other stuff
      Sentry.setUser({ email: "john@example.com" });
    }
  }
}

Additionaly you can clear the user while logging out

const handleLogout = {
  // Do othe stuff
  Sentry.configureScope(scope => scope.setUser(null));
}

Not sure if this is the right way, but the above solutions didn't work for me. So I tried calling setUser inside _app.tsx.

import { useEffect } from "react";
import { setUser } from "@sentry/nextjs";
import { UserProvider, useUser } from "@auth0/nextjs-auth0";
import type { AppProps } from "next/app";

function SentryUserManager() {
  const { user } = useUser();

  useEffect(() => {
    if (user) {
      setUser({
        email: user.email ?? undefined,
        username: user.name ?? undefined,
      });
    } else {
      setUser(null);
    }
  }, [user]);
  return null;
}

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <UserProvider>
      <Component {...pageProps} />
      <SentryUserManager />
    </UserProvider>
  );
}

Still not sure why this worked for me and the other solutions didn't, but figured it was worth sharing.

Related