ApplicationInsights-node.js icon indicating copy to clipboard operation
ApplicationInsights-node.js copied to clipboard

Support `setAuthenticatedUserContext`

Open gregberns opened this issue 6 years ago • 10 comments

The ApplicationInsights-JS SDK supports a method called setAuthenticatedUserContext which sends a custom auth token with each event.

The client code is basically just doing some validation then updating the ai.user.authUserId tag.

On the server, it looks like the same thing can be done, but is not documented:

let client = appInsights.defaultClient;
let key = client.context.keys.userAuthUserId;
client.context.tags[key] = <authToken>;

Providing this same method on the server side would be helpful for several reasons:

  • A users client and server activity can be tied together by the custom auth id
  • SDK users don't have to figure this out themselves
  • SDK consistency

gregberns avatar Dec 01 '17 17:12 gregberns

Great catch!

The JS SDK can also provide this userId to the server-side using the "ai_authUser" cookie. It's my understanding that our .NET SDK will read this cookie and automatically set the authUserId that it received from the JS SDK. It would be nice to implement that here as well.

OsvaldoRosado avatar Dec 01 '17 17:12 OsvaldoRosado

This would be really useful to be able to do on a per-request basis.

At the moment the only way I can see to add a username field on the server is to fake the user cookie

glenjamin avatar Jul 03 '18 13:07 glenjamin

There's a few workarounds for doing this per-request today (though perhaps faking the user cookie is actually easiest!):

  • If you're manually collecting requests, you can use the tagOverrides parameter at track time
  • If you're automatically collecting requests, you can use appInsights.getCorrelationContext() to set a field that you can access within the context object of a telemetry processor. The telemetry processor can then fill in this field on the envelope.
    • The upside to this approach vs any others is that you can get the user id set on the related telemetry as well (dependencies, exceptions, etc). This issue covers the approach in general: https://github.com/Microsoft/ApplicationInsights-node.js/issues/392

OsvaldoRosado avatar Jul 03 '18 17:07 OsvaldoRosado

Aha, telemetry processor looks like just the ticket.

Are there any pointers to docs on this other than in these issues?

glenjamin avatar Jul 03 '18 18:07 glenjamin

Oh right, I did see it in the README. Perhaps adding an example of logging auth would be good to add. I’ll have a poke around and maybe send in a PR.

glenjamin avatar Jul 03 '18 18:07 glenjamin

Glad you found it! Adding an example to the README would definitely be a welcome addition.

For your own implementation, I would make sure to look at the discussion within #392 over the data privacy implications of getCorrelationContext().customProperties and the alternative presented if the implications are not desirable.

OsvaldoRosado avatar Jul 03 '18 18:07 OsvaldoRosado

This is what I ended up going with

appInsights.defaultClient.addTelemetryProcessor(attachUserId);
function attachUserId(envelope, context) {
  const res = context['http.ServerResponse'];
  if (res && res.locals && res.locals.user) {
    envelope.tags['ai.user.authUserId'] = res.locals.user.username;
  }
}

glenjamin avatar Jul 03 '18 18:07 glenjamin

Is using context['http.ServerResponse'] to grab per-request information a safe thing to do here or should I use the getCorrelationContext().customProperties to temporarily save some information that will help me determine whether I should create a log for this request or not?

appInsights.defaultClient.addTelemetryProcessor(
  (envelope, context) => {
    const originalUrl = context["http.ServerRequest"]?.originalUrl;
    if (originalUrl === "/some-route-i-dont-care-about") return false;
  }
)

Or should I do the following?

appInsights.defaultClient.addTelemetryProcessor(
  (envelope, context) => {
    const requestContext = appInsights.getCorrelationContext().customProperties;
    if (requestContext.originalUrl === "/some-route-i-dont-care-about") return false;
  }
)

Any tips would be greatly appreciated here, thanks!

goto1 avatar Dec 07 '20 21:12 goto1

I ended up doing this to add a username to a request's properties. I'm also curious if it's better to use customProperties with an express middleware where I'd populate username to customProperties, rather than this approach.

  appInsights.defaultClient.addTelemetryProcessor((envelope, context) => {
    if (context) {
      const req = context['http.ServerRequest'];

      if (req && req.user) {
        envelope.tags['ai.user.authUserId'] = req.user.username;
        envelope.tags['ai.user.userId'] = req.user._id;
      }
    }

    return true;
  });

imduchy avatar Jan 04 '23 14:01 imduchy

I ended up doing this to add a username to a request's properties. I'm also curious if it's better to use customProperties with an express middleware where I'd populate username to customProperties, rather than this approach.

  appInsights.defaultClient.addTelemetryProcessor((envelope, context) => {
    if (context) {
      const req = context['http.ServerRequest'];

      if (req && req.user) {
        envelope.tags['ai.user.authUserId'] = req.user.username;
        envelope.tags['ai.user.userId'] = req.user._id;
      }
    }

    return true;
  });

This does not set User Id for me. Only Auth Id.

PieterWillemen avatar Feb 06 '24 14:02 PieterWillemen