I'm trying to create a master slave type configuration for authentication with IdentityServer4 as below
MyMasterIdentityServer0 (Master) - receives id_token and gives access_token
|---> MySlaveIdentityServer1 (Basic Auth)
|---> MySlaveIdentityServer2 (Windows Auth)
|---> MySlaveIdentityServer3 (SmartCard Certificate Auth)
|---> MySlaveIdentityServer4 (SAML SSO Auth)
|---> Cloud Demo IdentityServer
|---> Google Auth
|---> Facebook Auth
|---> Microsoft Auth
|---> Twitter Auth
All my applications and api will point to and authenticate with the MyMasterIdentityServer0
Users can have a choice of authentication using any of the above providers. They can choose username/password in which case they should be redirected to the MySlaveIdentityServer1 (Basic Auth), or they can choose using the Windows Auth using their AD account in which case they will be redirected to MySlaveIdentityServer2 (Windows Auth), or choose any other provider.
Once the user has been authenticated, he receives an id_token from the provider server and gets redirected back to the MyMasterIdentityServer0 where the external user is looked up using the Provider and ProviderUserId and then given an access_token to access the applications/api based on his permissions.
They problem I'm facing is that the IdentityServer Master Slave configuration is not working for me and is giving me an error Unable to unprotect the message.State when the user is redirected back to the master server after authentication. I tried looking up the issue and AuthO also faced this same bug which they recently fixed.
Error Received
Exception: Unable to unprotect the message.State
IdentityServer-Master Configuration
// WORKING
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "ID4DemoCloud",
DisplayName = "Login with ID4DemoCloud",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
SignOutScheme = IdentityServerConstants.SignoutScheme,
Authority = "https://demo.identityserver.io/",
ClientId = "implicit",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
},
//Events = new OpenIdConnectEvents() { }
});
// NOT WORKING
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "MySlaveIdentityServer1BasicAuth",
DisplayName = "Login with MySlaveIdentityServer1 Basic Auth",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
SignOutScheme = IdentityServerConstants.SignoutScheme,
Authority = "http://localhost:5100/",
ClientId = "MyMasterIdentityServer0",
ClientSecret = "secret",
RequireHttpsMetadata = false,
//TokenValidationParameters = new TokenValidationParameters
//{
// NameClaimType = "name",
// RoleClaimType = "role"
//},
});
Basic Auth Server Client Configuration
public static class Clients
{
public static IEnumerable<Client> GetClients()
{
return new[]
{
new Client
{
ClientId = "MyMasterIdentityServer0",
ClientName = "My Master IdentityServer 0",
ClientSecrets = new List<Secret> { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Implicit,
AllowedScopes = new List<string>
{
StandardScopes.OpenId,
StandardScopes.Profile
},
RequireConsent = false,
AllowOfflineAccess = false,
RedirectUris = new [] { "http://localhost:5000/signin-oidc" }
}
};
}
}
All the auth providers are working fine except the internally deployed MySlaveIdentityServers 1, 2, 3 and 4 ... even the Cloud Demo Identity server is working fine. Can anyone give me any advice or suggestions ?
I believe you are getting the Unable to unprotect the message.State
error because one of your OIDC providers is trying to decrypt/unprotect the message state of the other one. (The message state is just a random string to help with security.)
I suggest that you name the AuthenticationSchemes for each OIDC provider like oidc-demo
and oidc-master
. Then the external providers should send you back to the corresponding signin-oidc-demo
and signin-oidc-master
endpoints.
--
Turns out this answer was basically, correct. When using multiple OIDC providers you need different AuthenticationSchemes AND CallbackPath
values:
.AddOpenIdConnect("oidc-google", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.CallbackPath = "/signin-oidc-google";
...
}
.AddOpenIdConnect("oidc-microsoft", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.CallbackPath = "/signin-oidc-microsoft";
...
}
Note that the authentication middleware will magically handle any CallbackPath
that's configured, so it doesn't need to be handled explicitly.
If you don't differentiate OIDC providers and use separate callback paths, they may try to sign in with the same scheme and the cryptography won't match and only the first OIDC provider registered in your code will work.