Description:
We are implementing a custom policy (B2C_1A_CONTACTSIGNUP) for contact users in Azure AD B2C. Our application supports two separate authentication schemes: one for main account users (AzureAdB2C) and one for contact users (AzureAdB2CContact) using OpenIdConnect and custom policies.
In Program.cs, we configure MetadataAddress with the p= parameter:
Error Message:
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
A email link is click and is using:
ContactController > Invite
return Challenge(props, "AzureAdB2CContact");
Program.cs
options.MetadataAddress = $"https://test.b2clogin.com/mylifestore.onmicrosoft.com/v2.0/.well-known/openid-configuration?p={policy}";
In OnRedirectToIdentityProvider, if we include:
context.ProtocolMessage.SetParameter("p", policy);
The redirect URL ends up with two p= parameters, e.g.:
...?p=B2C_1A_CONTACTSIGNUP&...&p=B2C_1A_CONTACTSIGNUP
When omitting that line, the redirect results in:
...?p=null
which triggers this error:
https://test.b2clogin.com/.../client/perftrace?tx=...&p=null
We also tried removing the p parameter before re-adding it:
context.ProtocolMessage.Parameters.Remove("p");
context.ProtocolMessage.SetParameter("p", policy);
But this still results in a duplicate or conflicting p= in the final redirect.
Full logic:
builder.Services.AddAuthentication()
.AddOpenIdConnect("AzureAdB2CContact", options =>
{
var b2cConfig = builder.Configuration.GetSection("AzureAdB2CContact");
var policy = b2cConfig["SignUpSignInPolicyId"];
// Correct Authority and MetadataAddress for custom policy
options.Authority = "https://test.b2clogin.com/test.onmicrosoft.com/v2.0/";
options.MetadataAddress = $"https://test.b2clogin.com/test.onmicrosoft.com/v2.0/.well-known/openid-configuration?p={policy}";
options.ClientId = b2cConfig["ClientId"];
options.ClientSecret = b2cConfig["ClientSecret"];
options.CallbackPath = b2cConfig["CallbackPath"];
options.ResponseType = OpenIdConnectResponseType.IdToken;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("offline_access");
options.SaveTokens = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
ValidateIssuer = true
};
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
// Remove any existing 'p' parameter to avoid duplicates
context.ProtocolMessage.Parameters.Remove("p");
// Now set the correct policy
context.ProtocolMessage.SetParameter("p", policy);
var inviteToken = context.HttpContext.Request.Query["inviteToken"].FirstOrDefault();
if (!string.IsNullOrEmpty(inviteToken))
{
context.ProtocolMessage.SetParameter("extension_inviteToken", inviteToken);
}
return Task.CompletedTask;
}
};
});
Using:
.NET 8.0
ASP.NET Core Web App
Custom B2C policy for contact sign-up
Please confirm how to reliably pass the p= parameter with a custom policy without causing duplication or null. Is there a recommended configuration or workaround for this dual-scheme setup?
The issue you're facing is because the p
(policy) parameter is being added in both the MetadataAddress
and manually in OnRedirectToIdentityProvider
, which causes a duplicate or conflicting p=
in the redirect URL, resulting in either p=null
or a broken link.
To resolve the issue,
Set the policy
only via Authority
, and remove both MetadataAddress
and any manual setting of p
in event handlers.
options.Authority = $"https://test.b2clogin.com/test.onmicrosoft.com/{policy}/v2.0/";
Now remove,
options.MetadataAddress = ...
context.ProtocolMessage.SetParameter("p", policy);
This ensures the middleware correctly includes the policy once, avoiding any duplication or p=null
errors.
Updated code:
builder.Services.AddAuthentication()
.AddOpenIdConnect("AzureAdB2CContact", options =>
{
var b2cConfig = builder.Configuration.GetSection("AzureAdB2CContact");
var policy = b2cConfig["SignUpSignInPolicyId"];
options.Authority = $"https://test.b2clogin.com/test.onmicrosoft.com/{policy}/v2.0/";
options.ClientId = b2cConfig["ClientId"];
options.ClientSecret = b2cConfig["ClientSecret"];
options.CallbackPath = b2cConfig["CallbackPath"];
options.ResponseType = OpenIdConnectResponseType.IdToken;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("offline_access");
options.SaveTokens = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
ValidateIssuer = true
};
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
var inviteToken = context.HttpContext.Request.Query["inviteToken"].FirstOrDefault();
if (!string.IsNullOrEmpty(inviteToken))
{
context.ProtocolMessage.SetParameter("extension_inviteToken", inviteToken);
}
return Task.CompletedTask;
}
};
});
Make sure the policy (e.g., B2C_1A_CONTACTSIGNUP
) is correctly uploaded and available under Azure AD B2C -> Identity Experience Framework
.
By simplifying the config and letting the OpenID middleware handle the p
parameter via Authority
, you eliminate the risk of duplicate or missing policy values in your requests.