.NET Core Shared Cookies - .net-core

I have .NET Core app with .NET Core Identity.
I've setup shared cookie into Startup.cs:
services.AddIdentity<User, Role>()
.AddEntityFrameworkStores<DataContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = ".AspNet.SharedCookie";
});
services.AddAuthentication()
...
app.UseAuthentication();
app.UseAuthorization();
Also I have 2nd .NET Core app where I don't have authentication at all but want to use just that SharedCookie and I did the following in the Startup.cs:
services.AddAuthentication("Identity.Application")
.AddCookie("Identity.Application", options =>
{
options.Cookie.Name = ".AspNet.SharedCookie";
});
...
app.UseAuthentication();
app.UseAuthorization();
and on controller actions I set attribute [Authorize].
I logged in into 1st app and go to 2nd app and see error /Account/Login... page doesn't exist.
Yes I don't have that page but why do I see this issue? Did I forget anything to add in my code?
And one more question: what's SharedCookie string? Is it random string or it's encoded some user data? can I extract any info from that SharedCookie, for example Id of User?

So my solution was to add DataProtection step to both apps:
if (!Env.IsDevelopment())
{
services.AddDataProtection()
.PersistKeysToFileSystem("{PATH TO COMMON KEY RING FOLDER}")
.SetApplicationName("SharedCookieApp");
}
And one more question: what's SharedCookie string? Is it random string or it's encoded some user data? can I extract any info from that SharedCookie, for example Id of User?
Yes, I can extract Id, Email of user in the following way:
var id = HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
var email = HttpContext.User.FindFirstValue(ClaimTypes.Email);

Related

Auth setup of B2C Web API accessing confidential client (multitenant) Web API

I have a multi-tenant Web API of tenant A. It has permissions exposed and accepted by a B2C Web API of tenant B. (The API App Services live in the same tenant, but their AD instances are separate due to the one being a B2C tenant).
I have the following code in my B2C Web API authenticating with tenant B to access the multi-tenant Web API of tenant A.
I'm using Microsoft.Identity.Web (v1.25.5) and .NET Core (6), and so I don't have to handle making unnecessary calls to get an access token, I'm using the IDownstreamWebApi helper classes (though I have tried without according to the documentation, but land up with the same error.)
My code:
appsettings.json
program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
builder.Configuration.Bind("AzureAdB2C", options);
},
options => {
builder.Configuration.Bind("AzureAdB2C", options);
})
.EnableTokenAcquisitionToCallDownstreamApi(options =>
{
builder.Configuration.Bind("AzureAdB2C", options);
})
.AddDownstreamWebApi("TenantAApi", options =>
{
builder.Configuration.Bind("TenantAApi", options);
})
.AddInMemoryTokenCaches();
Calling code:
var response = await _downstreamWebApi.CallWebApiForAppAsync(
"TenantAApi",
options =>
{
options.HttpMethod = httpMethod;
options.RelativePath = url;
}, content);
var responseContent = await response.Content.ReadAsStringAsync();
The error I receive:
MSAL.NetCore.4.48.0.0.MsalClientException:
ErrorCode: tenant_override_non_aad
Microsoft.Identity.Client.MsalClientException: WithTenantId can only be used when an AAD authority is specified at the application level.
at Microsoft.Identity.Client.AbstractAcquireTokenParameterBuilder`1.WithTenantId(String tenantId)
at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForAppAsync(String scope, String authenticationScheme, String tenant, TokenAcquisitionOptions tokenAcquisitionOptions)
at Microsoft.Identity.Web.DownstreamWebApi.CallWebApiForAppAsync(String serviceName, String authenticationScheme, Action`1 downstreamWebApiOptionsOverride, StringContent content)
What doesn't make sense is that I'm calling this from a B2C Web API, from what I can see in the existing AbstractAcquireTokenParameterBuilder code (see line 292), B2C authorities are not AAD specific, and even so, adding an Authority or AadAuthorityAudience to my AzureAdB2C config object has no effect.
Am I missing a configuration property somewhere?
It seems that this isn't possible according to the following wiki post -
https://github.com/AzureAD/microsoft-identity-web/wiki/b2c-limitations#azure-ad-b2c-protected-web-apis-cannot-call-downstream-apis
For now I'm going to try a different approach and get an access token with a ConfidentialClientApplication object, and if that doesn't work, create a separate app registration in the other tenant and authenticate with that instead.

.Net Core 6 Can't get Claims after authenticated by ADFS

I recently got a problem about authenticated by ADFS
In dot net core 6, below is my scenario.
I got one Web-Api site host on IIS in server 2019, and an ADFS server
Web -Api domain like https://xxx.domain.com
ADFS one like https://ooo.domain.com
I use WS-federation in my program
both browsers can't get value, edge and chrome
setting is
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
}).AddWsFederation(options =>
{ options.MetadataAddress = "my adfs url/FederationMetadata/2007-06/FederationMetadata.xml";
options.Wreply = "https://my webpai url/checkstate";
options.CallbackPath = "/checkstate";
options.BackchannelTimeout = TimeSpan.FromMinutes(1);
options.RemoteAuthenticationTimeout = TimeSpan.FromMinutes(15);
})
.AddCookie();
web-api
signin action for auth and will redirect to adfs server login page
and auth back to the checkstate action this part are work very well.
But I can't Get the value what I want.
In my understanding use the ws-federation(Microsoft.AspNetCore.Authentication.WsFederation6.0.3)
don't need to fetch other service for parse the value.
whole workflow should like this
Users fetch the Api => auth => adfs login => success and get adfs shared value in cookies
=> back to the callback action => get value in action and do something.
When I opened the Dev tools, I can see the real flow like
Signin 302 =>adfs 200 this with a lot cookies
prefix and key is "MSISAUTH"
=> checkstate 200 but, no cookies
I already contact with the ADFS server cruise member and got response said
"We done every setting and it's look fine. "
My question is Did I miss some key-part ?
and is any misunderstanding on workflow?
[Authorize]
[HttpGet("signin")]
public IActionResult Signin()
{
return new EmptyResult();
}
[HttpPost("checkstate")]
public IActionResult CheckState()
{
var name = User.Claims.FirstOrDefault(x=>x.Type == ClaimTypes.Name)?.Value;
return Ok($"Name:{name}");
}
My question is Did I miss some key-part ?
and is any misunderstanding on workflow?
How can I get the Claims value ?

Azure AD B2C code example does not provide token validation option

I understand that Microsoft emphasizes on a proper token validation.
The following code example (link includes the exact line of code) does not include token validation:
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/blob/master/4-WebApp-your-API/4-2-B2C/Client/Startup.cs#L44
services.AddMicrosoftIdentityWebAppAuthentication(Configuration, Constants.AzureAdB2C)
.EnableTokenAcquisitionToCallDownstreamApi(new string[] { Configuration["TodoList:TodoListScope"] })
.AddInMemoryTokenCaches();
How can I improve above line of code so that it can validate tenant ID claim?
• To validate the token received from Azure AD B2C in Asp.Net, you will have to include ‘TokenValidationParameters’ value and define the validation of token claims received accordingly in the ‘Startup.cs’ file of the Web API. Please find the below sample code to be included in the ‘Startup.cs’ file for token validation which protects the Web API with Microsoft Identity platform: -
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
Configuration.Bind("AzureAdB2C", options);
options.TokenValidationParameters.ValidIssuers = new[] { /* list of valid issuers */ };
options.TokenValidationParameters.ValidAudiences = new[] { /* list of valid audiences */};
},
options => { Configuration.Bind("AzureAdB2C", options); });
Once the above has been done, add the method app.UseAuthentication() before app.UseMvc() in the Configure method as below: -
‘ app.UseAuthentication();
app.UseMvc(); ‘
Thus, you can add token validation parameters in your Asp.Net Web API for verifying tenant ID claims. For more detailed information regarding this, please refer to the documentation links below: -
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/4-WebApp-your-API/4-2-B2C
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-configuration#token-validation

.NET Core Identity - How generate token from another place than path/connect/token

in my web api application I get the acess token from http:applicationpath/connect/token with some parameters (this endpoint is from Identity I think, since we dont create it neither can see it).
But now I need to generate the token from a specific controller but cant see how to do this.
Someone knows how this can be made? Or even if it's possible?
Thanks
Some more info:
My application is an integrator (is this the word?) between an android app(app1) and other web application(app2).
1- The app1 user will send the login and password to my application .
2- Then my application will send then to the app2 who will, if everything goes well, return the app2 token .
3- Then I have to save this token in my db.
4- Then verify if the user exists in my db, and if not, save it.
5- And finally generate an token for my application and return it to the user.
Based on your comment:
But can I, instead of change de default endpoint, make another
endpoint that do the same (generate the token)?
it seems that you are rather looking for Extending discovery. This is quite easy actually.
Add a custom entry in the configuration of startup:
services.AddIdentityServer(options =>
{
options.Discovery.CustomEntries.Add("custom_token", "~/customtoken");
});
And add a controller that handles the request:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
// In case a token is required for login, like the UserInfo endpoint:
//[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
public class CustomTokenController : ControllerBase
{
[Route("customtoken")]
public IActionResult CustomTokenEndpoint()
{
return Ok();
}
}
Update
You can 'replace' the endpoint by disabling the default authorization endpoint and adding a custom endpoint as described above.
Disable the endpoint:
services
.AddIdentityServer(options =>
{
options.Endpoints.EnableAuthorizeEndpoint = false;
})
You may want to use the Authorize path constant.
public const string Authorize = ConnectPathPrefix + "/authorize";
Add the new endpoint:
services.AddIdentityServer(options =>
{
options.Discovery.CustomEntries.Add("authorization_endpoint", $"~/{Authorize}");
});
Please note, I didn't test it, but I think this should work.

ASP Identity Core GeneratePasswordResetTokenAsync expired

I have set up an Identity Server 4 project in .NET Core 2.1, I have everything working but when I use the user manager to generate the reset password token, the token expires after 24 hours, can I change this so it's 48 hours?
My code to send the reset token looks like this:
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = url.EmailConfirmationLink(user.Id, code, scheme);
My ConfigureServices looks like this:
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Thanks :)
Adding the following code to ConfigureServices() method in Startup.cs class should help you.
services.Configure<DataProtectionTokenProviderOptions>(options =>
options.TokenLifespan = TimeSpan.FromDays(2));
Default Token Lifespan is 24 hours (1 day). Please refer github and TokenOptions

Resources