Dual authentication with ASP.NET Core MVC - .net-core

I have an ASP.NET Core MVC web application and I want to allow my users to authenticate using identity server 4 and Azure Active Directory. All my internal users (company users) need to use Azure AD and all external users should use identity server to signing.
I have setup authentication in startup class as follows:
services.AddAuthentication(options =>
{
options.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = Configuration["Identity:WebApiUrl"];
options.ClientId = "xxxxxxx";
options.ClientSecret = "xxxxxxxxx";
options.ResponseType = "id_token";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.MapUniqueJsonKey("email", "email");
options.SaveTokens = true;
})
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
This will only allow internal users to signin using azure AD and external users gets following error.
Exception: Correlation failed.
Unknown location
Exception: An error was encountered while handling the remote login.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()
How can I solve this issue and allow both external and internal users to login ?
Thank you.

Related

Blazor WebAssembly - Prevent Access for Unauthorized Users

I've got a Blazor webassembly app. It's part of an ASP.NET core website which has Cookie Authentication. You can login by visiting /login which is implemented as a .net core razor page.
services.AddRazorPages()
//.AddMvcOptions(options => options.Filters.Add(new AuthorizeFilter()))
.AddRazorPagesOptions(options =>
{
options.Conventions.AllowAnonymousToPage("/Login");
options.Conventions.AuthorizeFolder("/");
options.Conventions.AuthorizePage("/Logout");
options.Conventions.AllowAnonymousToPage("/Login");
});
services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
options.LoginPath = "/Login";
// true by default
options.SlidingExpiration = true;
// 14 days by default
options.ExpireTimeSpan = TimeSpan.FromDays(20);
});
When the user isn't logged in, it's still possible to visit any route in the Blazor WebAssembly app. All the .net core Views are (correctly) inaccessible.
Where do I configure the WebAssembly part so that it's inaccessible to a non-authenticated user?
NOTE, I don't want to implement OIDC authentication in the WebAssembly part of the app (described in the Microsoft documentation) - or to add any of the login/logout logic to the WebAssembly part, just to make all the routes exposed by the WebAssembly app inaccessible for a non-authenticated user.

AWS Cognito Signin returning Bad gateway error

My .Net Core application is published to an elastic beanstalk load balanced environment and I'm using the Cognito hosted UI for authentication but after entering correct login details I get a 502 error.
snippet from startup.cs
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.ResponseType = Configuration["Authentication:Cognito:ResponseType"];
options.MetadataAddress = Configuration["Authentication:Cognito:MetadataAddress"];
options.ClientId = Configuration["Authentication:Cognito:ClientId"];
options.SaveTokens = true;
options.ClientSecret = Configuration["Authentication:Cognito:Secret"];
options.Scope.Add(Configuration["Authentication:Cognito:Scope"]);
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut,
OnRedirectToIdentityProvider = (async context =>
{
context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http:", "https:");
await Task.FromResult(0);
})
};
});
When I inspect the network activity in the browser I'm seeing this...
...which suggests that cognito is redirecting to /signin-oidc but there's no authorisation so it redirects back to cognito which then redirects back, and this repeats until it eventually throws the 502 error.
When I'm testing locally I'm able to login okay which makes me think it's maybe some kind of loadbalancing issue??
I kept getting a Bad Gateway 502 until I discovered that my next.config.js had async rewrites enabled for the source/destination URLs in my clumsy attempt to fix a CORS issue. Removing that fixed my problem to get next-auth working with cognito. Then I had to fix the underlying CORS problem with server side policy settings.

How to configure multiple tenants for .net core 5.0 openidconnect authentication?

I am using asp.net core 5.0 and openidconnect to authenticate users. My application will be used by several organizations.
My database stores the openid Connect options (client id, client secret, authority, etc) for each organization. I authenticated users by getting all the stored openid connect options (for all organizations) in my database and add each as below in the startup.cs
foreach(OrganizationSetting setting in settings)
{
authBuilder.AddOpenIdConnect(settings.AuthenticationScheme, options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = setting.Authority
options.ClientId = setting.ClientId;
options.CallbackPath =setting.CallbackPath;
options.ClientSecret =setting.ClientSecret;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.Scope.Add("openid");
options.SaveTokens = true;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = false,
SaveSigninToken = true
};
})
}
As you see, I must have different values for the autheticationscheme property and for the options.CallbackPath, else the application will throw an exception.
Since I am new to this, is there a better way to achieve my goal? maybe setting the clientid/tenantid at runtime before calling the challenge method ?
Thank you

.Net Core 3.1 as a standalone Identity Server

I would like to use a .Net Core 3.1 web app to allow an app (e.g. iPhone or Web Javascript) to authenticate with a username and password and receive a Jason Web Token (JWT) that contains claims about the user... if it succeeds, then the JWT can be sent as a bearer token to an API that would decode and validate the JWT (the token would be asymmetric and use a public/private key pair) and retrieve any claims that are embedded... perhaps a bonus if the app could decode the JWT as well in order to retrieve any claims.
Any thoughts on if this approach is possible? And, if there are any discussions or examples of how this might be done, that would be terrific.
Take a look at the examples that IdentityServer4 is providing. This sample/quickstart includes the case you are describing. https://github.com/IdentityServer/IdentityServer4/tree/main/samples/Quickstarts/6_AspNetIdentity/src
The API needs to be a scope in the IdentityServer4 configuration. It has a connection with the authority (IdentityServer4):
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:5001";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
The Client, in this example an MVC Client, needs to be a client in IdentityServer4. There are many types of GrantTypes. https://identityserver4.readthedocs.io/en/latest/topics/grant_types.html
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://localhost:5001";
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.Scope.Add("api1");
options.SaveTokens = true;
});
Hope this helps you out

How to get the access_token shared between Asp.Net and Asp.Net Core applications

I am using Identity Server 4 to provide access to multiple applications, some of which are Asp.Net Core, some are classic Asp.Net (MVC & WebForms).
The applications need to share their cookie due to the design of the old WebForms application (basically the API is 'in' the Web App, so it is protected by the Web App's cookie based authentiation). I'm using Data Protection to achieve the cookie sharing but this creates a separate issue.
If I login via the Core application and then navigate to the WebForms application, I don't know how to get the access_token to make calls to other APIs.
In the Core application I can retreive it from the HttpContext:
_httpContextAccessor.HttpContext.GetTokenAsync(AuthConstants.AccessToken).Result
What is the equivalent method to get the access_token in WebForms?
For context this is my configuration for the Core application authentication:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services
.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie = new CookieBuilder
{
Domain = configuration.CookieDomain,
HttpOnly = configuration.CookieHttpOnly,
Name = configuration.CookieName
};
options.ExpireTimeSpan = configuration.AuthTimeout;
options.SlidingExpiration = configuration.AllowSlidingAuthTimeout;
options.DataProtectionProvider = DataProtectionProvider.Create
(
new DirectoryInfo(configuration.DataProtectionKeyDirectory)
);
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = configuration.AuthorizationServerUri;
options.RequireHttpsMetadata = configuration.RequireHttps;
options.ClientId = configuration.Client;
options.ClientSecret = configuration.Secret;
options.ResponseType = configuration.ResponseType;
options.Scope.Clear();
foreach (var resource in configuration.Scope.Split(" "))
options.Scope.Add(resource);
options.SignedOutRedirectUri = configuration.RedirectUri;
options.UseTokenLifetime = configuration.UseAuthServerLifetime;
options.SaveTokens = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role
};
});
Just to close this out I did not find the answer I was looking for and ended up separating the API into it's own service as should have been done originally. That nullified this issue.

Resources