2 openid connect in asp.net core application - asp.net-core

I've been trying to add second identity provider to my web app, but have a problem with the configuration.
The app has the folowing configuration
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "cookie";
options.DefaultSignInScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
options.DefaultSignOutScheme = "oidc";
})
.AddCookie("cookie")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = AppConfig.AuthorizationServerAdress;
options.ClientId = AppConfig.OpenidApp;
options.ClientSecret = AppConfig.OpenidAppSecret;
options.ResponseType = OpenIdConnectResponseType.Code;
options.ResponseMode = OpenIdConnectResponseMode.Query;
options.UsePkce = true;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
})
.AddCookie("cookie2")
.AddOpenIdConnect("oidc2", options =>
{
options.Authority = AppConfig.AuthorizationExternalServerAdress;
options.ClientId = AppConfig.OpenidExternalApp;
options.ClientSecret = AppConfig.OpenidExternalAppSecret;
options.ResponseType = OpenIdConnectResponseType.Code;
options.ResponseMode = OpenIdConnectResponseMode.Query;
options.UsePkce = true;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
});
It works by default with the first oidc provider, but if I use oidc2 to log in and then navigate to my app, I'll go to my default oidc provider. It means that the second provider will be ignored.
Can somebody help me with the configuration, please?

The problem is that both handlers will listen for the callback request from your identityprovider on URL /signin-oidc
So, to solve it, you need to make sure they are different, like:
.AddOpenIdConnect("oidc", options =>
{
//other options
options.CallbackPath = new PathString("/oidc/handler1");
}
.AddOpenIdConnect("oidc2", options =>
{
//other options
options.CallbackPath = new PathString("/oidc/handler2");
}
also, see OpenIdConnect: Manually handle Callback
But, in general I advice that your clients and apps only should trust one provider (token issuer) and let users choose how to authenticate through your primary provider, like in this picture:

Related

Response type is not valid

I am using openidict and oidc-client authentication,
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Identity/Account/Login";
options.LogoutPath = "/Identity/Account/Logout";
})
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.ForwardSignIn = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = baseUrl;
options.CallbackPath = new PathString("/authentication/login-callback");
options.SignedOutRedirectUri = baseUrl;
options.ClientId = AuthenticationClient.WebClientId;
options.RequireHttpsMetadata = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.UsePkce = true;
/// Use the authorization code flow.
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
options.Scope.Add(Scopes.OpenId);
options.Scope.Add(Scopes.Profile);
options.Scope.Add(AuthenticationClient.WebClientApiScope);
}
Here when the response type is set to "Code id/Code id_token/code token" I'm getting
Open ID connect hybrid flow is not supported error.
When it is "code" , I get the below error.
error:unauthorized_client
error_description:The specified 'response_type' is not valid for this client application.
error_uri:https://documentation.openiddict.com/errors/ID2043
Can someone pls help me on this?
I had the same issue when I tried to configure OpenIddic server and authorize it through the OIDC protocol.
I was configuring postman public client, that allowed AuthorizationCode GrandType, and I forgot to add explicitly allowed ResponseType Code:
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "postman",
DisplayName = "Postman",
RedirectUris = { new Uri("https://www.getpostman.com/oath2/callback") },
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Device,
OpenIddictConstants.Permissions.Endpoints.Token,
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
OpenIddictConstants.Permissions.GrantTypes.DeviceCode,
OpenIddictConstants.Permissions.GrantTypes.Password,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles,
OpenIddictConstants.Permissions.ResponseTypes.Code <-- this was my issue
}
};
And this how OpenIddic stored this in DB
[
"ept:authorization",
"ept:device",
"ept:token",
"gt:authorization_code",
"gt:urn:ietf:params:oauth:grant-type:device_code",
"gt:password",
"gt:refresh_token",
"scp:email",
"scp:profile",
"scp:roles",
"rst:code"
]
As OpenIddict is a library for creating authorization we need set everything explicitly.

ABP Framework API Scope

I cloned MicroserviceDemo solution, before run project PublicWebsite.Host, I commented below code in PublicWebSiteHostModule.cs, but why PublicWebsite.Host still can access PublicWebSiteGateway, ProductService and BloggingService? In my view, I think app only access to required scopes. (Other hand, comment or don't comment below code, PublicWebsite.Host still run correctly. Sorry for my English).
Thanks you!
context.Services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(365);
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.ClientId = configuration["AuthServer:ClientId"];
options.ClientSecret = configuration["AuthServer:ClientSecret"];
options.RequireHttpsMetadata = false;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("role");
options.Scope.Add("email");
options.Scope.Add("phone");
//options.Scope.Add("PublicWebSiteGateway");
//options.Scope.Add("ProductService");
//options.Scope.Add("BloggingService");
options.ClaimActions.MapAbpClaimTypes();
});

Identityserve 4 in production env : "IDX10501: Signature validation failed. Unable to match key"

I have a 3 tier .NET core application with:
identityserver 4
an API
a blazer app
On my local/dev computer, everything works fine. But, I have installed everything
into a real server and then I have an issue that appears.
I log into the application, play with, then I wait some time (don't know how much) and then when I try to use the app, the Blazor app crash because of this code :
bool isAuthenticated = await _authenticationVerifier.IsAuthenticatedAsync();
if (isAuthenticated)
User = await _userAppService.GetCurrentUserAsync();
The 'isAuthenticated' is true, in fact my cookies look good, but the Blazor app is no more authorized to connect to the API server.
I got the following error message on the API server:
Bearer was not authenticated. Failure message: IDX10501: Signature validation failed. Unable to match key:
kid: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
Exceptions caught: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
token: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
What is strange is that it work for some time, then after some time (1/2 days), I got this crash.
I don't know what to check and/or how to debug this issue. I'm looking for a solution since weeks :-(
I join some code:
On the API Server:
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = true;
options.ApiName = "MyAppName";
});
}
On the Blazor (Server side Blazor):
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(ApplicationConstants.LoginCookieExpirationDelay);
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = true;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.ClientId = configuration["AuthServer:ClientId"];
options.ClientSecret = configuration["AuthServer:ClientSecret"];
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("role");
options.Scope.Add("email");
options.Scope.Add("phone");
options.Scope.Add("SoCloze");
options.ClaimActions.MapAbpClaimTypes();
});
context.Services.AddSingleton<BlazorServerAuthStateCache>();
context.Services.AddScoped<AuthenticationStateProvider, BlazorServerAuthState>();
context.Services.AddScoped<AuthenticationVerifier>();
}
On the IdentityServer 4 side:
// Identity cookie expiration
context.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = ".AspNetCore.Identity.Application";
options.ExpireTimeSpan = TimeSpan.FromDays(ApplicationConstants.LoginCookieExpirationDelay);
});
var clientConfig = context.Services.GetConfiguration().GetSection("IdentityServer:Clients");
context.Services
.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
options.RequireAuthenticatedSignIn = true;
})
.AddFacebook("Facebook", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.AppId = clientConfig["Facebook:ClientId"];
options.AppSecret = clientConfig["Facebook:ClientSecret"];
options.Fields.Add("picture");
})
.AddIdentityCookies();
}

Multiple Azure AD Authentication Identity Server 4

I would like to know if it's possible to setup a .NET Core Application using IdentityServer 4 that can Authenticate to more than one AzureAd configuration.
Currently you can add 1 AzureAD configuration like this:
services.AddAuthentication()
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
But I wanted to be able to Authenticate users from multiple Organisations using AzureAd. So different TenantId...etc
This will have to be done on the fly depending on the organisation chosen in the UI.
How can I accomplish that ?
You can use AddOpenIdConnect middleware :
services.AddAuthentication()
.AddOpenIdConnect("AADTenant1", "AADTenant1", options =>
{
options.ClientId = "<app1>";
options.Authority = "https://login.microsoftonline.com/<tenant1>/";
options.CallbackPath = "/signin-oidc-aadtenant1";
options.SaveTokens = true;
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
})
.AddOpenIdConnect("AADTenant2", "AADTenant2", options =>
{
options.ClientId = "<app2>";
options.Authority = "https://login.microsoftonline.com/<tenant2>/";
options.CallbackPath = "/signin-oidc-aadtenant2";
options.SaveTokens = true;
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
})
And trigger the scheme you want to challenge :
var callbackUrl = Url.Action("ExternalLoginCallback");
var props = new AuthenticationProperties
{
RedirectUri = callbackUrl,
Items =
{
{ "scheme", provider },
{ "returnUrl", returnUrl }
}
};
return Challenge(provider, props);

ASP.NET Authorize Filter Denies Access for User in Specified Role

In my ASP.NET Core 2.0 Application, I am stuck with an issue an Admin logged in User cannot access controller I used the Authorize Filter on [Authorize(Policy="AdminAlone")].
I confirmed that the user is in the "Administrators" role and added the policy in startup.cs but it redirects to an AccessDenied view when I try to access the controller.
I saw a similar problem on this link, but the solution didn't help me
Startup Class in MVC Client - ConfigureServices
services.AddMvc();
services.AddSession();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddAuthorization(options =>
{
options.AddPolicy("AdminAlone", policy => policy.RequireRole("Administrators"));
});
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie("Cookies")
.AddOpenIdConnect("Bearer", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvcWeb";
options.ClientSecret = "spring12345";
options.ResponseType = OidcConstants.ResponseTypes.CodeIdToken;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("NuB.HospitalSearch");
options.Scope.Add("offline_access");
});
Web API ConfigureServices
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler
{
InboundClaimTypeMap = new Dictionary<string, string>()
};
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(option =>
{
option.Audience = "NuB.HospitalSearch";
option.Authority = "http://localhost:5000";
option.RequireHttpsMetadata = false;
option.SecurityTokenValidators.Clear();
option.SecurityTokenValidators.Add(jwtSecurityTokenHandler);
option.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateAudience = true,
ValidAudience = "NuB.HospitalSearch",
ValidateIssuer = true
};
});
You may try the following. Inside your AddOpenIdConnect configuration add
options.TokenValidationParameters = new TokenValidationParameters {
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role
};
In fact, this property defines the types and definitions required for validating a token. Please refer to this post from Dominick Baier for a more detailed explanation.