Microsoft Entra ID + Nginx cannot authorize

William A Wang 251 Reputation points
2026-04-16T02:11:36.09+00:00

The app works good in Windows Server but after migrated to Ubuntu 24 + Kernel + Nginx, the redirected url https://xxx.org/signin-oidc shows Nginx 502 Bad Gateway, and kernel shows:

info: Microsoft.IdentityModel.LoggingExtensions.IdentityLoggerAdapter[0]

IDX10239: Lifetime of the token is valid.

info: Microsoft.IdentityModel.LoggingExtensions.IdentityLoggerAdapter[0]

IDX10234: Audience Validated.Audience: 'ffd077e8-605b-444f-9137-6e761158c305'

info: Microsoft.IdentityModel.LoggingExtensions.IdentityLoggerAdapter[0]

IDX10245: Creating claims identity from the validated token: '[PII of type 'Microsoft.IdentityModel.JsonWebTokens.JsonWebToken' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.

info: Microsoft.IdentityModel.LoggingExtensions.IdentityLoggerAdapter[0]

IDX21305: OpenIdConnectProtocolValidationContext.ProtocolMessage.Code is null, there is no 'code' in the OpenIdConnect Response to validate.

info: Microsoft.IdentityModel.LoggingExtensions.IdentityLoggerAdapter[0]

IDX21310: OpenIdConnectProtocolValidationContext.ProtocolMessage.AccessToken is null, there is no 'token' in the OpenIdConnect Response to validate.

here is nginx config:
server {

listen 80;

listen [::]:80;

server_name xxx.org;

add_header X-Frame-Options "SAMEORIGIN" always;

add_header X-Content-Type-Options "nosniff" always;

add_header Referrer-Policy "strict-origin-when-cross-origin" always;

return 301 https://$host$request_uri;

}

server {

listen 443 ssl http2;

listen [::]:443 ssl http2;

server_name xxx.org;

ssl_certificate /apps/cert/xxx.org+3.pem;

ssl_certificate_key /apps/cert/xxx.org+3-key.pem;

ssl_protocols TLSv1.2 TLSv1.3;

ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

ssl_prefer_server_ciphers on;

ssl_session_cache shared:SSL:10m;

ssl_session_timeout 10m;

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

add_header X-Frame-Options "SAMEORIGIN" always;

add_header X-Content-Type-Options "nosniff" always;

location / {

proxy_pass http://127.0.0.1:5000;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection "upgrade";

proxy_set_header Host $host;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header X-Forwarded-Proto $scheme;

proxy_set_header X-Forwarded-Host $host;

proxy_set_header X-Forwarded-Port $server_port;

proxy_read_timeout 86400s;

proxy_send_timeout 86400s;

proxy_buffering off;

}

location /_blazor {

proxy_pass http://127.0.0.1:5000;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection "upgrade";

proxy_set_header Host $host;

proxy_cache_bypass $http_upgrade;

proxy_read_timeout 86400s;

}

}

Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<ForwardedHeadersOptions>(options =>

{

options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;

options.KnownNetworks.Clear();

options.KnownProxies.Clear();

});

// Add services to the container.

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)

.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddAuthorization(options =>

{

// By default, all incoming requests will be authorized according to the default policy.

options.FallbackPolicy = options.DefaultPolicy;

});

builder.Services.AddRazorPages()

.AddMicrosoftIdentityUI()

.AddNewtonsoftJson(options =>

{

options.SerializerSettings.ContractResolver = new DefaultContractResolver();

options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";

options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

options.SerializerSettings.TypeNameAssemblyFormatHandling = Newtonsoft.Json.TypeNameAssemblyFormatHandling.Simple;

});

builder.Services.AddSingleton<INetwork, Network>();

builder.Services.AddSingleton<IViewService, ViewService>();

builder.Services.AddSingleton<IUserService, UserService>();

builder.Services.AddSingleton<IIndustryService, IndustryService>();

builder.Services.AddDbContextFactory<SqlContext>(options => options.UseSqlServer(builder.Configuration["DefaultConnectionString"]));

builder.Services.AddDistributedMemoryCache();

builder.Services.AddSession(options =>

{

options.IdleTimeout = TimeSpan.FromMinutes(10);

options.Cookie.HttpOnly = true;

options.Cookie.IsEssential = true;

});

var app = builder.Build();

app.UseForwardedHeaders();

app.UseSession();

// Configure the HTTP request pipeline.

if (!app.Environment.IsDevelopment())

{

app.UseExceptionHandler("/Error");

// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.

app.UseHsts();

}

app.Use((context, next) =>

{

context.Response.Headers.AltSvc = "h3=\":443\"";

return next(context);

});

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();

app.UseAuthorization();

app.MapRazorPages();

app.MapControllers();

app.Run();

Microsoft Security | Microsoft Entra | Microsoft Entra ID

1 answer

Sort by: Most helpful
  1. AI answer

    2026-04-16T02:11:53.1066667+00:00

    The 502 Bad Gateway indicates that Nginx can’t successfully proxy the /signin-oidc callback to the ASP.NET Core app on http://127.0.0.1:5000, even though Microsoft Entra ID has issued and validated the token.

    From the provided configuration and logs, the relevant points are:

    1. Nginx is correctly configured as a reverse proxy to http://127.0.0.1:5000 on port 443, including forwarding Host, X-Forwarded-Proto, and other headers, which matches the documented pattern for ASP.NET Core + Nginx reverse proxy.
    2. The ASP.NET Core app is configured to use forwarded headers via UseForwardedHeaders() and ForwardedHeadersOptions, which is required when running behind a reverse proxy.
    3. The Microsoft Identity logs show that the token lifetime and audience are valid, and a claims identity is created, which means the OpenID Connect response is being processed inside the app.
    4. The 502 is a proxy error, which (per the Nginx + ASP.NET Core guidance) occurs when Nginx can’t reach or get a valid response from the backend application when forwarding the request.

    Given these facts, the issue is in the communication between Nginx and the ASP.NET Core app on Linux, not in Microsoft Entra ID itself. The documented guidance for this setup highlights these checks:

    1. Confirm that the ASP.NET Core app is actually running and listening on http://127.0.0.1:5000 on Ubuntu (for example by running dotnet <app_assembly.dll> directly and testing curl http://127.0.0.1:5000 from the server).
    2. Verify that Nginx is using the correct upstream address and port (proxy_pass http://127.0.0.1:5000;) and that there is no firewall or SELinux rule blocking local traffic.
    3. After any change to Nginx configuration, run sudo nginx -t to validate the configuration and then sudo nginx -s reload to apply it.
    4. If the app runs locally but doesn’t respond through Nginx, check Nginx error logs for 502-related details and adjust proxy-related settings (for example, header and buffer limits) only if needed. The documentation notes that default proxy buffer limits can cause issues with long headers (such as those used by Microsoft Entra ID), and that increasing proxy_buffer_size, proxy_buffers, proxy_busy_buffers_size, or large_client_header_buffers may be required in such scenarios, but only when necessary due to DoS risk.

    In summary, the Microsoft Entra ID token is valid and processed, and the 502 is due to Nginx not being able to successfully forward or receive the response from the ASP.NET Core app on 127.0.0.1:5000. The next steps are to verify the app is listening on that endpoint on Ubuntu, confirm Nginx can reach it, validate and reload the Nginx configuration, and only then consider adjusting proxy buffer limits if long headers are involved.


    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.