Code Monkey home page Code Monkey logo

Comments (65)

kevinchalet avatar kevinchalet commented on June 5, 2024 1

My pleasure. Don't hesitate if you need additional details or have any other question ๐Ÿ˜ƒ

from openiddict-core.

meywd avatar meywd commented on June 5, 2024 1

I did, but I see now the values are not correct , thanks

from openiddict-core.

meywd avatar meywd commented on June 5, 2024 1

nevermind, I see thats the case for the server above

from openiddict-core.

meywd avatar meywd commented on June 5, 2024 1

Yeah I found about ASPNETCORE_FORWARDEDHEADERS_ENABLED = true the hard way

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024 1

No, OpenIddict uses a unique state/correlation cookie name per authentication demand, so even if you start concurrent operations, all of them should be able to complete successfully (unless you started hundreds of demands at the same time and are hitting cookie limits, of course, but it's not the case here).

from openiddict-core.

meywd avatar meywd commented on June 5, 2024 1

Thanks for your help, will do a final compare with your samples and see if I can find anything

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024 1

This thread is getting a bit long and I believe the initial question (making the Dantooine sample work) has been addressed.

Feel free to open a separate thread if you want to discuss more complex scenarios ๐Ÿ‘๐Ÿป

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

Hey @meywd,

Thanks for sponsoring the project!

Your Dantooine Sample has the Auth Server and the BFF Server each connecting to a different Context and Database, why is that?

The idea behind the decoupling is that the WebAssembly client and its associated BFF server are independent from the authorization server, that may even be managed by a completely different organization (note: the BFF server uses the OpenIddict client, which is stateful and requires an EF Core context for token persistence).

Are there any issues in the code above?
Also any idea why the Token above in BFF Proxy call is null.

Your snippet uses the MSFT OIDC handler instead of the OpenIddict client that is leveraged by the Dantooine sample. Unlike OpenIddict, the MSFT OIDC handler doesn't support storing both the frontchannel access token (returned as part of the authorization response) and the backchannel access token (returned as part of the token response). Instead, it just prefers the backchannel access token when available and stores it using the generic access_token name.

That's why your call to GetTokenAsync(CookieAuthenticationDefaults.AuthenticationScheme, Tokens.BackchannelAccessToken) doesn't return anything. Either migrate to the OpenIddict client or if you prefer using the MSFT OIDC handler, use GetTokenAsync(CookieAuthenticationDefaults.AuthenticationScheme, "access_token") instead.

Hope it'll help.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Will try that, thanks for the quick answer.

I guess I understand the sample more, like a provider and a local user with permissions, being a BFF made it just a gateway in my mind,I didn't consider it to be the main app, In my case I want the API to have the user management, and the BFF simply for handling cookies and refresh tokens and consolidate API calls.

So in my scenario, switching to the openiddict client should be enough? And is there a problem with having the API manage the users?

Also when I want to connect the SPA, should it be a separate client? Or if it depends on the BFF to do auth and save the cookies be enough if all calls go through it?

And I need to be able to do API to API calls with app auth, what should I do to support that?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

So in my scenario, switching to the openiddict client should be enough? And is there a problem with having the API manage the users?

Yep, it should be enough. No problem implementing user management as an API, it's a common approach.

Also when I want to connect the SPA, should it be a separate client? Or if it depends on the BFF to do auth and save the cookies be enough if all calls go through it?

The whole point of having a BFF - backend-for-frontend - is to act as a proxy for your SPA, that will exclusively use cookie authentication instead of token authentication (and isn't considered as an OIDC client: the real client is the BFF server itself).

If you implement OIDC support at the SPA level (e.g using the oidc-client-ts library) and add a client registration for it, then a BFF is completely useless here. Both options are possible and it's up to you to decide what approach you prefer (see #1918 and openiddict/openiddict-samples#180 for related discussions)

And I need to be able to do API to API calls with app auth, what should I do to support that?

You need the client credentials grant for that. See https://github.com/openiddict/openiddict-samples/tree/dev/samples/Aridka.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Thank you Kevin.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

for now I went for BFF with EFCore, had to copy from the sample to make it work, so its only using EF for the tokens? its not doing anything with the dbcontext like users and so on, anyway it now gives:
The introspection request was rejected because the access token was issued to a different client or for another resource server.
the scope seems to be set right so not sure whats the issue.

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

The introspection request was rejected because the access token was issued to a different client or for another resource server.
the scope seems to be set right so not sure whats the issue.

You're getting this error because your API is not allowed to introspect the access token. You likely didn't add a resource to your scope that will be used by OpenIddict to attach the correct audience(s) to the access tokens.

See:

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

I am trying to add Introspection and client credentials to the APIs, I see Introspection is done with the AddValidation call, how to add the AllowClientCredentialsFlow is it the AddServer or AddClient ?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

how to add the AllowClientCredentialsFlow is it the AddServer or AddClient ?

Both:

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

now that I think about it, yeah, thanks

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

but then there will be 3 calls?

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

And the adventure continues

Calling from SPA to BFF, it redirects to Auth server were I login, then back to BFF and I get this error:
No correlation cookie associated with the specified state can be found. Please try logging in again. If the error persists, please contact the website owner.

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

This error happens when OpenIddict's ASP.NET Core client integration cannot find the antiforgery cookie created during the challenge operation while handling the callback/redirection request (most likely because it's not sent by the browser).

Can you share a Fiddler trace?

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

2024-03-08.zip

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

I don't see neither the challenge response nor the faulted redirection/callback request in these traces.

That said, I see your authorization server is accessed without using a HTTPS address, which may be related. Try to fix that to see if it helps.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Yeah I am having a problem making https work in docker, which is almost the same in Azure Container Apps which acts as a proxy and terminates https

2024-03-08.zip

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

The correlation cookie is correctly returned by the OpenIddict client during the challenge phase...

HTTP/2 302
date: Fri, 08 Mar 2024 15:32:01 GMT
server: Kestrel
access-control-allow-origin: *
location: http://host.docker.internal:7774/connect/authorize?client_id=64176522-c3a3-441d-beb8-689b2524b7c3&redirect_uri=https%3A%2F%2Flocalhost%3A7773%2Fcallback%2Flogin%2Flocal&response_type=code&scope=openid%20profile%20api%20workflow&nonce=NJ4dpWLs3jadTF-6hL42GjvHkNJ-VfLExd0IWePuqOo&code_challenge=grI3bBW4_4il88ZheGjVLEnn1WaY9PggIy-KRkeqFjk&code_challenge_method=S256&state=wclCFyCFjm7HFvus9cmgNYdhZyuZc3QA5AnC0fyBrtw
set-cookie: OpenIddict.Client.State.u2gnDRQ75jUY6HD65-yQo0IadiJRxf81279HPFcNn1M=AQAAACtrR1BDMWg0SHctQ3pxdjhma1R0NFFzSWRCaUNYM0tHVXJrNC0wUDdjN2tN; expires=Fri, 08 Mar 2024 15:47:01 GMT; path=/; secure; samesite=none; httponly
content-length: 0
request-context: appId=cid-v1:919cbad0-6934-4760-a4d8-67dfb6823a80

... but it's not sent back by the browser during the callback phase:

GET https://localhost:7773/callback/login/local?code=y0wpQo766Gk_iJb88uMg9BMacaZy7qVOwdFJXnVfIT4&state=PrRdWemY5syo7pSdob5v7cSzduTAiKFLNllMyJGgcTc&iss=http%3A%2F%2Fhost.docker.internal%3A7774%2F HTTP/2
cache-control: max-age=0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
sec-fetch-site: cross-site
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
sec-ch-ua: "Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
referer: http://host.docker.internal:7774/
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
Host: localhost:7773

... hence the error you're seeing.

SSL/TLS termination is not an issue, but you need to configure your ASP.NET Core app correctly to ensure it's HTTPS-aware. See https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-8.0 for more information.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Yes, I figured its related to the callback and redirect to the browser, how to configure that on the BFF? should it be configured as a server as well?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

The problem seems to only affect your authorization server app, not the BFF server that uses HTTPS (https://localhost:7773/).

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

so the flow should be Browser -> BFF -> Auth (Login) -> Auth -> BFF -> Browser or Browser -> BFF -> Auth (Login) -> Auth -> Browser

from openiddict-core.

meywd avatar meywd commented on June 5, 2024
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.All;
});

and

app.UseForwardedHeaders();

Are used on both BFF and Auth Server, do the APIs need it as well?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

so the flow should be Browser -> BFF -> Auth (Login) -> Auth -> BFF -> Browser or Browser -> BFF -> Auth (Login) -> Auth -> Browser

The first one.

Are used on both BFF and Auth Server, do the APIs need it as well?

You might also need to configure the KnownProxies/KnownNetworks as shown in the docs (see also https://stackoverflow.com/questions/51143761/asp-net-core-docker-https-on-azure-app-service-containers).

If your APIs are also deployed as containerized apps, they may need the same treatment. That said, it should have no impact on the OIDC user flow, that doesn't involve the APIs at this stage.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Trying to fix SSL made it worse, it stopped working, then adding a local CA, and making a certificate from it almost worked, but the BFF failed with PartialChain, after adding HttpClientHandler.DangerousAcceptAnyServerCertificateValidator in the BFF and the API servers, it went back to the No correlation cookie... error, but I can confirm its coming out of the BFF and not the Auth server.

.AuthServer |   | 2024-03-10T05:43:57.553542586Z info: .AuthServer.Areas.Identity.Pages.Account.LoginModel[0]
.AuthServer |   | 2024-03-10T05:43:57.553589787Z       User logged in.
.AuthServer |   | 2024-03-10T05:43:57.567226927Z info: OpenIddict.Server.OpenIddictServerDispatcher[0]
.AuthServer |   | 2024-03-10T05:43:57.567278727Z       The request URI matched a server endpoint: Authorization.
.AuthServer |   | 2024-03-10T05:43:57.570072136Z info: OpenIddict.Server.OpenIddictServerDispatcher[0]
.AuthServer |   | 2024-03-10T05:43:57.570136936Z       The authorization request was successfully extracted: {
.AuthServer |   | 2024-03-10T05:43:57.570147736Z         "client_id": "64176522-c3a3-441d-beb8-689b2524b7c3",
.AuthServer |   | 2024-03-10T05:43:57.570151036Z         "redirect_uri": "https://localhost:8773/callback/login/local",
.AuthServer |   | 2024-03-10T05:43:57.570153536Z         "response_type": "code",
.AuthServer |   | 2024-03-10T05:43:57.570156036Z         "scope": "openid profile api workflow",
.AuthServer |   | 2024-03-10T05:43:57.570158036Z         "nonce": "uVbRppjsVoY8mg2Z2oTPF4UHYz36T9GV-L0h8MZsDzk",
.AuthServer |   | 2024-03-10T05:43:57.570160036Z         "code_challenge": "0IVJtP03xBNM-lNbfkh5TDol69yhx881ejiDfBTjE30",
.AuthServer |   | 2024-03-10T05:43:57.570162236Z         "code_challenge_method": "S256",
.AuthServer |   | 2024-03-10T05:43:57.570164036Z         "state": "VF9Z09IOQ1VDDCHCoi6gHXvWz-vPJM6v7jJMGJCNPF0"
.AuthServer |   | 2024-03-10T05:43:57.570166336Z       }.
.AuthServer |   | 2024-03-10T05:43:58.457324083Z       The authorization response was successfully returned to 'https://localhost:8773/callback/login/local' using the query response mode: {
.AuthServer |   | 2024-03-10T05:43:58.457331183Z         "code": "[redacted]",
.AuthServer |   | 2024-03-10T05:43:58.457333683Z         "state": "VF9Z09IOQ1VDDCHCoi6gHXvWz-vPJM6v7jJMGJCNPF0",
.AuthServer |   | 2024-03-10T05:43:58.457335483Z         "iss": "https://host.docker.internal:8775/"
.AuthServer |   | 2024-03-10T05:43:58.457434084Z       }.
.MainBFF |      | 2024-03-10T05:43:58.477966441Z info: OpenIddict.Client.OpenIddictClientDispatcher[0]
.MainBFF |      | 2024-03-10T05:43:58.478020841Z       The redirection request was successfully extracted: {
.MainBFF |      | 2024-03-10T05:43:58.478027341Z         "code": "[redacted]",
.MainBFF |      | 2024-03-10T05:43:58.478029641Z         "state": "VF9Z09IOQ1VDDCHCoi6gHXvWz-vPJM6v7jJMGJCNPF0",
.MainBFF |      | 2024-03-10T05:43:58.486287666Z         "iss": "https://host.docker.internal:8775/"
.MainBFF |      | 2024-03-10T05:43:58.486358466Z       }.
.MainBFF |      | 2024-03-10T05:43:59.093858179Z info: OpenIddict.Core.OpenIddictTokenManager[0]
.MainBFF |      | 2024-03-10T05:43:59.093923479Z       The token 'dd50cf65-9079-47c2-f3ab-08dc40c509ff' was successfully marked as redeemed.
.MainBFF |      | 2024-03-10T05:43:59.107679420Z info: OpenIddict.Client.OpenIddictClientDispatcher[0]
.MainBFF |      | 2024-03-10T05:43:59.107743520Z       The response was successfully returned as a plain-text document: {
.MainBFF |      | 2024-03-10T05:43:59.107750520Z         "error": "invalid_request",
.MainBFF |      | 2024-03-10T05:43:59.107753720Z         "error_description": "No correlation cookie associated with the specified state can be found. Please try logging in again. If the error persists, please contact the website owner.",
.MainBFF |      | 2024-03-10T05:43:59.107756720Z         "error_uri": "https://documentation.openiddict.com/errors/ID2129"
.MainBFF |      | 2024-03-10T05:43:59.107761120Z       }.

from openiddict-core.

damienbod avatar damienbod commented on June 5, 2024

Hi @meywd Why deploy to Azure Container Apps? Why not just deploy to Azure App Services (Linux Plan) and you can avoid all the certificate setup problems

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

This is local on docker

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

@meywd can you please share a Fiddler trace with the new config in place?

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

2024-03-10.zip

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

Thanks. Looks like the cookie is still not correctly sent back. Two things:

  • Check whether the cookie is correctly persisted using the browser tools.
  • Try re-executing the login flow with the developer console of your browser open to see if there's anything relevant that would explain why it's not persisted or sent.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

could the double request be the reason?

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Screenshot 2024-03-10 070802
could this be the problem Expires: Thu, 01 Jan 1970 00:00:00 GMT

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

No, it's the classical "delete cookies" stuff done by Identity when starting a new login process. It's done at the authorization server level and your issue is at the BFF level: it's unrelated.

Did you see anything in the console logs?

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Screenshot 2024-03-10 071209

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

Check whether the cookie is correctly persisted using the browser tools.

Please check that too.

Have you tried with a different browser?

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

yeah chrome and firefox

and no cookies

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

and no cookies

Try starting a new authentication flow and see if there's a message logged when the OpenIddict correlation cookie is returned.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

just in case, here is the BFF Program.cs


var builder = WebApplication.CreateBuilder(args);

var config = builder.AddAzureAppConfig("MainBFF");

var certificateClient = builder.AddKeyVaultWithClient(config);

builder.AddOpenTelemetry(config);

builder.AddStorage(config);

builder.AddDataProtection(config);

var sqlConnectionString = config.GetConnectionString("SqlServer")!;

builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
    // Configure the context to use sqlite.
    options.UseSqlServer(sqlConnectionString);

    // Register the entity sets needed by OpenIddict.
    // Note: use the generic overload if you need
    // to replace the default OpenIddict entities.
    options.UseOpenIddict<Guid>();
});

builder.Services.AddAntiforgery(options =>
{
    options.HeaderName = "X-XSRF-TOKEN";
    options.Cookie.Name = "__Host-X-XSRF-TOKEN";
    options.Cookie.SameSite = SameSiteMode.Strict;
    options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});

var enableDebugging = config.GetValue<bool>("EnableDebugging");

builder.Services.AddHttpClient();
builder.Services.AddOptions();

var openIDConnectSettings = config.GetSection("OpenIDConnectSettings");

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
    options.LoginPath = "/login";
    options.LogoutPath = "/logout";
    options.ExpireTimeSpan = TimeSpan.FromMinutes(50);
    options.SlidingExpiration = false;
});

// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks
// (like pruning orphaned authorizations from the database) at regular intervals.
builder.Services.AddQuartz(options =>
{
    options.UseSimpleTypeLoader();
    options.UseInMemoryStore();
});

// Register the Quartz.NET service and configure it to block shutdown until jobs are complete.
builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);

builder.Services
    .AddOpenIddict()
    .AddCore(options =>
    {
        // Configure OpenIddict to use the Entity Framework Core stores and models.
        // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
        options.UseEntityFrameworkCore()
                .UseDbContext<ApplicationDbContext>()
               .ReplaceDefaultEntities<Guid>();

        // Enable Quartz.NET integration.
        options.UseQuartz();
    })
    .AddClient(options =>
    {
        options
            .AllowAuthorizationCodeFlow()
            .AllowRefreshTokenFlow()
            .AllowClientCredentialsFlow();

        options.AddEncryptionKey(new SymmetricSecurityKey(
           Convert.FromBase64String(builder.Configuration["<key name>"]!)));

        // Register the signing credentials.
        var cert = new X509Certificate2(certificateClient.DownloadCertificate("<cert name>"));
        options.AddSigningCertificate(cert);

        // Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
        options.UseAspNetCore()
               .EnableStatusCodePagesIntegration()
               .EnableRedirectionEndpointPassthrough()
               .EnablePostLogoutRedirectionEndpointPassthrough();

        // Register the System.Net.Http integration and use the identity of the current
        // assembly as a more specific user agent, which can be useful when dealing with
        // providers that use the user agent as a way to throttle requests (e.g Reddit).
        options.UseSystemNetHttp()
               .ConfigureHttpClientHandler(handler =>
               {
                   if(builder.Environment.IsDevelopment())
                       handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
               })
               .SetProductInformation(typeof(Program).Assembly);

        // Add a client registration matching the client application definition in the server project.
        var registration = new OpenIddictClientRegistration
        {
            Issuer = new Uri(openIDConnectSettings["Authority"]!, UriKind.Absolute),

            ClientId = openIDConnectSettings["ClientId"],
            ClientSecret = openIDConnectSettings["ClientSecret"],

            // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint
            // URI per provider, unless all the registered providers support returning a special "iss"
            // parameter containing their URL as part of authorization responses. For more information,
            // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4.
            RedirectUri = new Uri("callback/login/local", UriKind.Relative),
            PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative)
        };
        registration.Scopes.UnionWith((openIDConnectSettings["Scope"] ?? "").Split(" ", StringSplitOptions.RemoveEmptyEntries).Select(s => s)!);
        options.AddRegistration(registration);
    });

builder.Services.AddControllersWithViews(options =>
        options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));
builder.Services.AddRazorPages();

// Create an authorization policy used by YARP when forwarding requests
// from the WASM application to the Dantooine.Api1 resource server.
builder.Services.AddAuthorization(options => options.AddPolicy("CookieAuthenticationPolicy", builder =>
{
    builder.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
    builder.RequireAuthenticatedUser();
}));

var cert = new X509Certificate2(await certificateClient.DownloadCertificateAsync("<cert name>"));
builder.Services.AddReverseProxy()
    .LoadFromConfig(config.GetSection("ReverseProxy"))
    .ConfigureHttpClient((context, handler) =>
    {
        handler.SslOptions = new SslClientAuthenticationOptions
        {
            RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
            {
                return true;
            },
            ClientCertificates = new X509CertificateCollection
            {
                cert
            }
        };
    })
    .AddTransforms(builder => builder.AddRequestTransform(async context =>
    {
        // Attach the access token retrieved from the authentication cookie.
        //
        // Note: in a real world application, the expiration date of the access token
        // should be checked before sending a request to avoid getting a 401 response.
        // Once expired, a new access token could be retrieved using the OAuth 2.0
        // refresh token grant (which could be done transparently).
        var token = await context.HttpContext.GetTokenAsync(
            scheme: CookieAuthenticationDefaults.AuthenticationScheme,
            tokenName: Tokens.BackchannelAccessToken);

        context.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue(Schemes.Bearer, token);
    }));

builder.WebHost.UseWebRoot("wwwroot");
builder.WebHost.UseStaticWebAssets();

// Add builder.Services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.All;
});

// Configure CORS to allow designer app hosted on a different origin to invoke the APIs.
builder.Services.AddCors(cors => cors
    .AddDefaultPolicy(policy => policy
        .AllowAnyOrigin() // For demo purposes only. Use a specific origin instead.
        .AllowAnyHeader()
        .AllowAnyMethod()));

// Register the worker responsible for creating the database used to store tokens.
// Note: in a real world application, this step should be part of a setup script.
builder.Services.AddHostedService<Worker>();

var app = builder.Build();
app.UseForwardedHeaders();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseWebAssemblyDebugging();
}
else
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseCors();

//app.UseSecurityHeaders(
//    SecurityHeadersDefinitions.GetHeaderPolicyCollection(app.Environment.IsDevelopment(),
//        config["OpenIDConnectSettings:Authority"]!));

app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();
app.MapControllers();
app.MapReverseProxy();
app.MapFallbackToPage("/_Host");

app.Run();

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

and no cookies

Try starting a new authentication flow and see if there's a message logged when the OpenIddict correlation cookie is returned.

not seeing any messages, could the use of host.docker.internal instead of localhost cause an issue?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

no seeing any messages, could the use of host.docker.internal instead of localhost cause an issue?

It shouldn't.

Unfortunately, I'm out of ideas at this point. I'd need to debug that myself: consider sharing your entire solution (Docker stuff included) and I'll try to repro the issue locally.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Does the use of AddDataProtection cause issues?

            builder.Services
                .AddDataProtection()
                .PersistKeysToAzureBlobStorage(new Uri(config.GetSection("DataProtection").GetValue<string>("Url")!))
                .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
                {
                    EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
                    ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
                });

if the keys for each server are different, do they need to share the same key ring?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

No, DP configuration would only be a concern if you had multiple instances running.

Here, it's a server-browser (bad) cookie interaction. A lot harder to debug...

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

checking the token data, anything odd here?
Screenshot 2024-03-10 082145

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

No. If the state token was invalid, you'd get a different issue. It's really a cookie issue here.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

on a side note, how does the BFF know where to redirect?

from what I see, the return URL is the API URL, so UI app requests API through BFF, BFF does login then returns to the API URL.

ui.domain.com/admin/company -> bff.domain.com/admin/company -> auth.domain.com/authorize -> auth.domain.com/login -> bff.domain.com/callback/login -> then what happens?

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Currently the BFF is simply a client, does it need to be a client and a server so it can redirect back to the UI, and the UI is registered as a client to the BFF?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

on a side note, how does the BFF know where to redirect?

image

That ReturnUrl is eventually preserved in the state token and can be restored from your AuthenticationController.

ui.domain.com/admin/company -> bff.domain.com/admin/company -> auth.domain.com/authorize -> auth.domain.com/login -> bff.domain.com/callback/login -> then what happens?

You're redirected back to the initial page.
Note: the SPA (what you call UI?) and the BFF must be on the same domain for things to work correctly, otherwise cookies won't be shared between the SPA and the BFF.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

they are two separate apps hosted on different subdomains, so it seem I need a gateway to redirect calls.

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

The standard approach is to have a BFF per SPA. Thatโ€™s the whole point.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

If it's for API optimization yes, but for auth purposes why do you need one per one, couldn't one BFF serve multiple SPAs?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

The unique raison dโ€™รชtre of a BFF is to act as a proxy between the SPA and the OIDC server. In practice, itโ€™s almost always the app that serves the SPA that also acts as the BFF.

(BTW, itโ€™s called backend-for-frontend, not backend-for-frontends ๐Ÿ˜„)

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Could this be related to correlation cookie error?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

Possibly.

As I said, I need to test the whole thing locally to be able to help you more. Otherwise, we're just going to spend a lot of time trying to guess what may or may not cause this issue.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

I understand, I appreciate your help, I need to clean it before I can send it.

I noticed that the auth server requests login, although it says I am logged in and it displays my user name.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

Spent all yesterday stripping the code down to the basics but couldn't get it to work somehow, then now I got the idea to try the same pattern on your Dantooine sample, so I add allow all CORS call

        // Configure CORS to allow designer app hosted on a different origin to invoke the APIs.
        services.AddCors(cors => cors
                .AddDefaultPolicy(policy => policy
                .AllowAnyOrigin() // For demo purposes only. Use a specific origin instead.
                .AllowAnyHeader()
                .AllowAnyMethod()));

to both Auth and BFF servers and called the BFF from the UI, the same error showed.
No correlation cookie associated with the specified state can be found.

so the issue here is that the original call is not coming from the BFF...

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

so the issue here is that the original call is not coming from the BFF...

What do you mean exactly?

FWIW, I tried to reproduce it locally and wasn't able to (even when hosting the BFF and the WASM app on a separate domain).
That said, you don't need CORS at all in a BFF scenario, since everything shares the same origin/domain (that's even the whole point, otherwise cookies couldn't be shared between the SPA and its BFF), so if it's causing issues, just remove it?

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

I am testing locally, my SPA is not served from the BFF, its hosted on its own service and port, so it can't access the BFF, and when i added allow all to the BFF it complained about the auth server rejecting the request, so I added allow all there.

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

you mentioned sharing cookies across domain, in production all will be hosted in separate sub-domains of the same domain, so it should work correct?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

you mentioned sharing cookies across domain, in production all will be hosted in separate sub-domains of the same domain, so it should work correct?

By default, cookies are not shared with subdomains. See https://stackoverflow.com/questions/18492576/share-cookies-between-subdomain-and-domain for more information.

I am testing locally, my SPA is not served from the BFF, its hosted on its own service and port, so it can't access the BFF, and when i added allow all to the BFF it complained about the auth server rejecting the request, so I added allow all there.

Most likely the root cause of this issue...

I really feel like you're trying to use the BFF pattern incorrectly... ๐Ÿ˜†

from openiddict-core.

meywd avatar meywd commented on June 5, 2024

I really feel like you're trying to use the BFF pattern incorrectly... ๐Ÿ˜†

that is mostly correct as its my first time doing it, my idea is that I don't like mixing back-end and front-end, I thought we are done with AJAX\MVC style projects, seems not, its just cleaner to split them for development.

The whole idea is doing SSO, multiple APIs, multiple SPAs(some end users, some internal\admin users) with one or more BFFs to secure them, plus potential 3rd party websites connecting to the APIs using App to App auth, 3rd party and 1st party mobile apps.

the reason why I think it should work separately, is because BFFs for mobile can't actually serve the mobile apps them selves, they are deployed through stores, but my understanding could be wrong.

By default, cookies are not shared with subdomains. See https://stackoverflow.com/questions/18492576/share-cookies-between-subdomain-and-domain for more information.

So can we do this?

from openiddict-core.

kevinchalet avatar kevinchalet commented on June 5, 2024

the reason why I think it should work separately, is because BFFs for mobile can't actually serve the mobile apps them selves, they are deployed through stores, but my understanding could be wrong.

"BFFs for mobile apps" don't exist: mobile apps can directly use OIDC/token authentication without requiring a cookie/token translation mechanism (which is what a BFF is, essentially). A BFF only makes sense for a SPA.

So can we do this?

I don't think I'd recommend it, but you should technically be able to do it. You'll need to tweak the cookie options of the Identity cookie handlers and of the OpenIddict client (https://github.com/openiddict/openiddict-core/blob/dev/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreOptions.cs#L77) to specify an explicit domain, tho'.

from openiddict-core.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.