I'm trying to enable login-functionality to multiple AzureAD's in one identity server.
The reason this is necessary, is that multiple tenants need to be able to login to their own AzureAD through 1 identityserver. Setting these up will happen dynamically.
For this example, I have made an extension to AuthenticationBuilder which allows passing multiple AzureAd's and adds them to the AuthenticationBuilder.
//startup.cs
Dictionary<string, string> azure1 = new Dictionary<string, string>();
azure1.Add("name", "azure1");
azure1.Add("clientid", <azure1id>);
azure1.Add("tenantid", <azure1tenant>);
Dictionary<string, string> azure2 = new Dictionary<string, string>();
azure2.Add("name", "azure2");
azure2.Add("clientid", <azure2id>);
azure2.Add("tenantid", <azure2tenant>);
Dictionary<string, string>[] ids = { azure1, azure2 };
services.AddAuthentication()
.AddCookie()
.AddAzureAdAuthentications(ids)
//AuthenticationBuilderExtension
public static AuthenticationBuilder AddAzureAdAuthentications(this AuthenticationBuilder builder, Dictionary<string, string>[] azureAds)
{
string name = "";
string clientid = "";
string tenantid = "";
foreach (Dictionary<string, string> azuread in azureAds)
{
name = azuread.GetValueOrDefault("name");
clientid = azuread.GetValueOrDefault("clientid");
tenantid = azuread.GetValueOrDefault("tenantid");
builder.AddOpenIdConnect(name, name, options =>
{
options.Authority = $"https://login.microsoftonline.com/{tenantid}";
options.ClientId = clientid;
options.ResponseType = OpenIdConnectResponseType.IdToken;
});
}
return builder;
}
I would expect the above code to add 2 azureAd's as a login options, this works fine. I would think the authentication-flow would stay intact.
Instead, 2 problems appear.
Exception: Correlation failed. Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler+d__12.MoveNext()
Removing 1 of the 2 AzureAD's from the dictionary results in everything working fine.
Implementing leastprivileges answer (thanks) results in the following: It "almost" works. The AzureAD's are recognized correctly, leading to the correct flows and accepting only the corresponding accounts. But now the client is not succesfully logged in after the callback to the identityserver.
I feel it's just a small edit regarding the signinscheme, but i can't really figure it out as the documention on this is quite minimal.
foreach (Dictionary<string, string> azuread in ids)
{
string name = azuread.GetValueOrDefault("name");
string clientid = azuread.GetValueOrDefault("clientid");
string tenantid = azuread.GetValueOrDefault("tenantid");
services.AddAuthentication(name).AddCookie($"Cookie{name}").AddOpenIdConnect(name, name, options =>
{
options.SignInScheme = $"Cookie{name}";
options.SignOutScheme = $"Cookie1{name}";
options.CallbackPath = $"/signin-{name}";
options.Authority = $"https://login.microsoftonline.com/{tenantid}";
options.ClientId = clientid;
options.ResponseType = OpenIdConnectResponseType.IdToken;
});
}
Might not be that difficult, but sharing the solution to help others:
services.AddAuthentication().AddCookie($"Cookie{name}")
.AddOpenIdConnect(name, name, options =>
{
options.CallbackPath = $"/signin-{name}";
options.Authority = $"https://login.microsoftonline.com/{tenantid}";
options.ClientId = clientid;
options.ResponseType = OpenIdConnectResponseType.IdToken;
});
Each handler needs at least a unique CallbackPath
- there are other callbacks for signout and post redirect - they must be set as well if you are using those features.
Also the authentication scheme must be unique.