Just a quick question/thought and I have a basic understanding of Open Telemetry.
Aare there a standard/recommendation for passing the traceId/context across page redirects in the browser? (like does the browser support passing the context across a 30x redirect?).
What I wanted to do was to visualize the authentication flow when using OpenID-Connect and I thought it would be very cool if I could pass the traceId across redirects to get a complete picture of all the page loads.
Assuming that I could get the auth service to support it).
I don't see what language/platform/framework you are using. So, I'll assume it's .NET.
We do not have standard/recommendation for redirects. I had the same problem with redirects and this is what I came up with:
Create new Propagator to check if request has trace in cookies
public class CookiePropagator : TextMapPropagator
{
public const string IdpTraceid = "IdP-TraceId";
public const string IdpTraceState = "Idp-TraceState";
public static readonly int TraceparentLengthV0 = "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-00".Length;
public override void Inject<T>(PropagationContext context, T carrier, Action<T, string, string> setter)
{
// We're ignoring setter because it is used for Headers in HTTP requests only.
}
public override PropagationContext Extract<T>(PropagationContext context, T carrier,
Func<T, string, IEnumerable<string>> getter)
{
// We're ignoring getter because it is used for Headers only. It's the way OpenTelemetry works for now.
if (carrier == null)
{
return context;
}
// Let's check if we have it in the cookie
if (carrier is HttpRequest
{
Path.Value: "/login1"
or "/login2"
or "/authorize"
or "/challenge" // all workflows you want to track with 302 cookie
} request
&& request.Cookies[IdpTraceid] != null)
{
// You can use TryExtractTraceParent from
// https://github.com/open-telemetry/opentelemetry-dotnet/blob/ba8a0e4c131054217555aae5c8a210da3a4b7c39/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs
var parsed = TryExtractTraceParent(request.Cookies[IdpTraceid], out var traceId, out var spanId, out var traceoptions);
if (!parsed)
{
return context;
}
string tracestate = null;
var traceStateFromCookie = request.Cookies[IdpTraceState];
if (!string.IsNullOrWhiteSpace(traceStateFromCookie))
{
// You can use TryExtractTraceState from
// https://github.com/open-telemetry/opentelemetry-dotnet/blob/ba8a0e4c131054217555aae5c8a210da3a4b7c39/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs
TryExtractTraceState([traceStateFromCookie], out tracestate);
}
return new PropagationContext(
new ActivityContext(traceId, spanId, traceoptions, tracestate, isRemote: true),
context.Baggage);
}
return context;
}
}
You will have to put traceId in cookie, so it could be done in middleware:
app.Use(async (ctx, next) =>
{
await next.Invoke(ctx);
if (ctx.Request.Path == "first/path/to/start/tracking" && Activity.Current != null)
{
var cookieOptions = new CookieOptions
{
// Two minutes should suffice for most login use cases
Expires = DateTimeOffset.Now.AddMinutes(2)
};
// To make your WriteTraceParentIntoSpan you can check:
// https://github.com/open-telemetry/opentelemetry-dotnet/blob/ba8a0e4c131054217555aae5c8a210da3a4b7c39/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs
var traceParent = string.Create(CookiePropagator.TraceparentLengthV0, Activity.Current, CookiePropagator.WriteTraceParentIntoSpan);
ctx.Response.Cookies.Append(CookiePropagator.IdpTraceid, traceParent, cookieOptions);
ctx.Response.Cookies.Append(CookiePropagator.IdpTraceState, Activity.Current.TraceStateString ?? string.Empty, cookieOptions);
}
// Removing cookies after last redirect
if (ctx.Request.Path == "/last/call/to/stop/tracking")
{
ctx.Response.Cookies.Delete(CookiePropagator.IdpTraceid);
ctx.Response.Cookies.Delete(CookiePropagator.IdpTraceState);
}
});
Don't forget to add new propagator to SDK!!!
Sdk.SetDefaultTextMapPropagator(new CompositeTextMapPropagator(new TextMapPropagator[]
{
new TraceContextPropagator(),
new CookiePropagator()
}));