- ABP Framework version: v8.2.3
- UI Type: Angular / MVC / Blazor WASM / Blazor Server
- Database System: EF Core (PostgreSQL)
- Tiered (for MVC) or Auth Server Separated (for Angular): yes
- Exception message and full stack trace:
- Steps to reproduce the issue:
Hi Team,
My client is looking to set CORS from the value saved in DB instead of appsettings.json, I have extended ConfigureApplication class as below:
ConfigureOpenIddict(openIddict = {
openIddict. ConfigureApplication(spp =>
spp. AddOrUpdateProperty<string>(
"AppCORS",
property =>
{
property Attributes. Add(new RequiredAttributeD);
}
);
});
}):
However, I don't know How to move the below code to AppServices.cs
context.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder
.WithOrigins(
configuration["App:CorsOrigins"]?
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.Trim().RemovePostFix("/"))
.ToArray() ?? Array.Empty<string>()
)
.WithAbpExposedHeaders()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
Could you please help here? Thanks, Navneet
15 Answer(s)
-
0
hi
context.Services.AddCors(options =
will only execute once.You can use
MyCorsPolicyProvider
to dynamic get doamins from database/cache.// context.Services.AddCors(options => // { // options.AddDefaultPolicy(builder => // { // builder // .WithOrigins( // configuration["App:CorsOrigins"]? // .Split(",", StringSplitOptions.RemoveEmptyEntries) // .Select(o => o.Trim().RemovePostFix("/")) // .ToArray() ?? Array.Empty<string>() // ) // .WithAbpExposedHeaders() // .SetIsOriginAllowedToAllowWildcardSubdomains() // .AllowAnyHeader() // .AllowAnyMethod() // .AllowCredentials(); // }); // }); context.Services.RemoveAll(typeof(ICorsPolicyProvider)); context.Services.Add(ServiceDescriptor.Transient<ICorsPolicyProvider, MyCorsPolicyProvider>());
public class MyCorsPolicyProvider : ICorsPolicyProvider { private readonly static Task<CorsPolicy?> NullResult = Task.FromResult<CorsPolicy?>(null); private readonly CorsOptions _options; public MyCorsPolicyProvider(IOptions<CorsOptions> options) { _options = options.Value; } public async Task<CorsPolicy?> GetPolicyAsync(HttpContext context, string? policyName) { //get domains from database, remember to cache it var domains = new List<string> { "https://localhost:44300", "https://localhost:44301" }; var builder = new CorsPolicyBuilder(Array.Empty<string>()); builder .WithOrigins( domains.Select(o => o.Trim().RemovePostFix("/")) .ToArray() ?? Array.Empty<string>() ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); var result = builder.Build(); return result; } }
-
0
Hi maliming,
Thanks for your code help :-)
Regarding your comments "get domains from database, remember to cache it" I understand that I need to inject Irepository first as below:
private readonly IRepository<OpenIddictApplication, Guid> _OpenIddictApplicationRepository;
How do you use cache in the code that is written in TechDB.Application that supports in web Module?
if I cache it in ApplicationServices as per https://abp.io/docs/latest/framework/infrastructure/entity-cache, will it be an issue if the tenant is switched?
Thanks, Navneet
-
0
hi
You can use
IDistributedCache<YourDomainsCacheItem>
. It supports multiple tenants.ABP uses
IDistributedCache<T>
everywhere. : )https://abp.io/docs/latest/framework/fundamentals/caching
-
0
hi
context.Services.AddCors(options =
will only execute once.You can use
MyCorsPolicyProvider
to dynamic get doamins from database/cache.// context.Services.AddCors(options => // { // options.AddDefaultPolicy(builder => // { // builder // .WithOrigins( // configuration["App:CorsOrigins"]? // .Split(",", StringSplitOptions.RemoveEmptyEntries) // .Select(o => o.Trim().RemovePostFix("/")) // .ToArray() ?? Array.Empty<string>() // ) // .WithAbpExposedHeaders() // .SetIsOriginAllowedToAllowWildcardSubdomains() // .AllowAnyHeader() // .AllowAnyMethod() // .AllowCredentials(); // }); // }); context.Services.RemoveAll(typeof(ICorsPolicyProvider)); context.Services.Add(ServiceDescriptor.Transient<ICorsPolicyProvider, MyCorsPolicyProvider>());
public class MyCorsPolicyProvider : ICorsPolicyProvider { private readonly static Task<CorsPolicy?> NullResult = Task.FromResult<CorsPolicy?>(null); private readonly CorsOptions _options; public MyCorsPolicyProvider(IOptions<CorsOptions> options) { _options = options.Value; } public async Task<CorsPolicy?> GetPolicyAsync(HttpContext context, string? policyName) { //get domains from database, remember to cache it var domains = new List<string> { "https://localhost:44300", "https://localhost:44301" }; ........... } }
Hi maliming,
Do I put the above code in the Application or EntityFrameworkCore as the AuthServer does not have direct reference to the Application?
Also, the Client has three webapplications deployed in three regions pointing to one Authserver (seeded all three web applications), how can I find which application the user is coming to use API host
{ private readonly static Task<CorsPolicy?> NullResult = Task.FromResult<CorsPolicy?>(null); private readonly CorsOptions _options; private readonly IRepository<OpenIddictApplication, Guid> _OpenIddictApplicationRepository; public MyCorsPolicyProvider( IOptions<CorsOptions> options, IRepository<OpenIddictApplication, Guid> OpenIddictApplicationRepository) { _options = options.Value; _OpenIddictApplicationRepository = OpenIddictApplicationRepository; } public async Task<CorsPolicy?> GetPolicyAsync(HttpContext context, string? policyName) { //get domains from database, remember to cache it var selectedApplication = _OpenIddictApplicationRepository.GetAsync(????);
_OpenIddictApplicationRepository.GetAsync(?) - how to identify which ClientID of application the user is getting authenticated and allowed Cors
Thanks, Navneet
-
0
hi
Add
MyCorsPolicyProvider
to your AuthServer(web
) project.You can inject the
ICurrentClient,
which has anId
property.
-
0
hi
Add
MyCorsPolicyProvider
to your AuthServer(web
) project.You can inject the
ICurrentClient,
which has anId
property.
that make sense, however, If I add MyCorsPolicyProvider to AuthServer('web'), then how can I search as the authserver does not have direct to repository like:- private readonly IRepository<OpenIddictApplication, Guid> _OpenIddictApplicationRepository;"
to query like:- var selectedApplication = _OpenIddictApplicationRepository.GetAsync(id)
-
0
hi
I think your authserver indirectly dependent on openiddict domain module.
so you can inject the openiddict's repository.
authserver
=>EF core project
=>OpenIddict EF Core
=>OpenIddict Domain
-
0
hi
I think your authserver indirectly dependent on openiddict domain module.
so you can inject the openiddict's repository.
authserver
=>EF core project
=>OpenIddict EF Core
=>OpenIddict Domain
Thanks Maliming,
I have injected "private readonly ICurrentClient _currentClient;" and _currentClient.Id is always null? I am not sure If I am missing anything.
-
0
hi
What is the process of your authentication?
After signing in to the website, you can get the
client id
.Are you using the code flow to get token?
-
0
hi
What is the process of your authentication?
After signing in to the website, you can get the
client id
.Are you using the code flow to get token?
I have created an Application to use Allow Authorization Code Flow, below is the screenshot:
public class MyCorsPolicyProvider : ICorsPolicyProvider { private readonly static Task<CorsPolicy?> NullResult = Task.FromResult<CorsPolicy?>(null); private readonly CorsOptions _options; protected IOpenIddictApplicationManager _ApplicationManager { get; } private readonly ICurrentClient _currentClient; public MyCorsPolicyProvider( IOptions<CorsOptions> options, IOpenIddictApplicationManager applicationManager, ICurrentClient currentClient) { _options = options.Value; _ApplicationManager = applicationManager; _currentClient = currentClient; } public async Task<CorsPolicy?> GetPolicyAsync(HttpContext context, string? policyName) { //get domains from database, remember to cache it var id = _currentClient.Id; // value is coming null var app = await _ApplicationManager.FindByIdAsync(id);
-
0
hi
For anonymous requests, there is no way to get the
client ID
.The
client id
has to exist in a query string or form post.You can try to inject the
IHttpContextAccessor
and call theGetOpenIddictServerRequest
extension method.var request = HttpContext.GetOpenIddictServerRequest()
but the
OpenIddictServerRequest
only exists in the openiddict request.eg:
connect/token
orconnect/authorize
-
0
hi
For anonymous requests, there is no way to get the
client ID
.The
client id
has to exist in a query string or form post.You can try to inject the
IHttpContextAccessor
and call theGetOpenIddictServerRequest
extension method.var request = HttpContext.GetOpenIddictServerRequest()
but the
OpenIddictServerRequest
only exists in the openiddict request.eg:
connect/token
orconnect/authorize
Hi maliming,
you are correct, without authentication, client id cannot be accessed, however, I managed to get the client id by HttpContext.
- In your code, you have injected the below and didn't use it, do I need to get anything to enforce CORS by below:-
private readonly static Task<CorsPolicy?> NullResult = Task.FromResult<CorsPolicy?>(null); private readonly CorsOptions _options;
Also, when I browse the endpoint via Postman
connect/token
orconnect/authorize
I don't reach to MyCorsPolicyProvider method.Someresone get and set from cache is not working for me, could you please see if you can find any issue with my below code:
public class MyCorsPolicyProvider : ICorsPolicyProvider { private readonly static Task<CorsPolicy?> NullResult = Task.FromResult<CorsPolicy?>(null); private readonly CorsOptions _options; private readonly IOpenIddictApplicationRepository _openIddictApplicationrepo; private readonly IDistributedCache<string> _cache; public MyCorsPolicyProvider( IOptions<CorsOptions> options, IDistributedCache<string> cache, IOpenIddictApplicationRepository openIddictApplicationrepo) { _options = options.Value; _cache = cache; _openIddictApplicationrepo = openIddictApplicationrepo; } public async Task<CorsPolicy?> GetPolicyAsync(HttpContext context, string? policyName) { //get domains from database, remember to cache it var httpContext = context; // Get the ReturnUrl from the query string var returnUrl = httpContext.Request.Query["ReturnUrl"].ToString(); string clientIdFromUrl = ""; if (!string.IsNullOrEmpty(returnUrl)) { // Decode the ReturnUrl var decodedReturnUrl = HttpUtility.UrlDecode(returnUrl); // Check if the decoded URL contains a query string var uriParts = decodedReturnUrl.Split('?'); if (uriParts.Length > 1) { var innerQueryString = uriParts[1]; // Extract the query string part // Parse the inner query string var queryParameters = HttpUtility.ParseQueryString(innerQueryString); // Extract the client_id clientIdFromUrl = queryParameters["client_id"]; } } var CorsDomain = GetAsync(clientIdFromUrl); var domains = new List<string> { CorsDomain.ToString() }; var builder = new CorsPolicyBuilder(Array.Empty<string>()); builder .WithOrigins( domains.Select(o => o.Trim().RemovePostFix("/")) .ToArray() ?? Array.Empty<string>() ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); var result = builder.Build(); return result; } private async Task<string?> GetAsync(string clientId) { var CorsFromCache = await _cache.GetAsync(clientId); if (CorsFromCache == null) { var CorsFromDb = GetFromDBAsync(clientId); await _cache.SetAsync(clientId,CorsFromDb.ToString()); return CorsFromDb.ToString(); } return null; } private async Task<string?> GetFromDBAsync(string clientId) { var appinfo = await _openIddictApplicationrepo.FindByClientIdAsync(clientId); return appinfo.AppCORS.ToString(); } }
-
0
hi
see 3.2
Cors will check if the request header contains
Origin
etc..
Check the logic of
CorsMiddleware
https://github.com/dotnet/aspnetcore/blob/release/9.0/src/Middleware/CORS/src/Infrastructure/CorsMiddleware.cs#L107-L110 https://github.com/dotnet/aspnetcore/blob/release/9.0/src/Middleware/CORS/src/Infrastructure/CorsMiddleware.cs#L112-L157 https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-9.0
3.1 You can call
_cache.GetOrAddAsync
.3.2 If there is no domains, you can return
Task.FromResult<CorsPolicy?>(null);
https://github.com/dotnet/aspnetcore/blob/release/9.0/src/Middleware/CORS/src/Infrastructure/DefaultCorsPolicyProvider.cs#L12
-
0
Hi maliming,
Thanks for sharing the links, it is now crystal clear how CORS works in the backend with the request header.
PS:- I have also read your article on extending grant type, it was impressive - Good work mate!!
-
0
Great : )