openiddict / openiddict-core

Flexible and versatile OAuth 2.0/OpenID Connect stack for .NET
https://openiddict.com/
Apache License 2.0
4.43k stars 520 forks source link

Multiple Project Introspections #2159

Closed chamavv closed 2 months ago

chamavv commented 2 months ago

Personal contribution

Version

5.7.0

Provider name

OAuth 2.0/OpenID

Describe the bug

Hello, good morning, I hope you are well. I would like to see if you can support me. I have been working on a project with openiddict to protect all the applications I develop. I was looking at some documentation and the projects they have for the following structure: The server, Client, Api. The first project I set up works fine with introspection, the client communicates correctly with the API, but the second client does not arrive authenticated to the API, that is, the client in Angular is authenticated correctly, but when I want to consume I do not have any response or log, to save the error. Can you help me if I am missing any settings? Use similar settings to avoid differences, you will not be able to detect the problem. Or Some different way to establish that communication. I would appreciate your support. I share my configurations.

Resource_server_1 is the one that is working

To reproduce

builder.Services.AddDbContext<IntranetContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("IntranetConnection"));
    options.UseOpenIddict();

});

builder.Services.AddScoped<IPasswordHasher<IdentityUser>, PasswordHasher<IdentityUser>>();

builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Logging.AddEventLog();

builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<IntranetContext>()
    .AddDefaultTokenProviders();

builder.Services.Configure<IdentityOptions>(options =>
{
    options.ClaimsIdentity.UserNameClaimType = Claims.Name;
    options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
    options.ClaimsIdentity.RoleClaimType = Claims.Role;
    options.SignIn.RequireConfirmedAccount = false;
});

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
          .AddCookie(options =>
          {
              options.AccessDeniedPath = "/connect/signin";
              options.LoginPath = "/connect/signin";
              options.LogoutPath = "/connect/signout";
              //options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
              //options.Cookie.SameSite = SameSiteMode.None;
          });

//builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
GlobalBoostraper.GlobalRegisterServices(builder.Services, builder.Configuration);
builder.Services.AddScoped<IPasswordHasher<IdentityUser>, PasswordHasher<IdentityUser>>();
builder.Services.AddOpenIddict()

    .AddCore(options =>
    {
        options.UseEntityFrameworkCore()
               .UseDbContext<IntranetContext>();
        options.UseQuartz();
    })
    .AddServer(options =>
    {
        options.SetAuthorizationEndpointUris(builder.Configuration["OpenIddict:Endpoints:Authorization"]!)
               .SetTokenEndpointUris(builder.Configuration["OpenIddict:Endpoints:Token"]!)
               .SetIntrospectionEndpointUris(builder.Configuration["OpenIddict:Endpoints:Introspection"]!)
               .SetLogoutEndpointUris(builder.Configuration["OpenIddict:Endpoints:Logout"]!)
               .SetUserinfoEndpointUris(builder.Configuration["OpenIddict:Endpoints:Userinfo"]!);

        options.AllowAuthorizationCodeFlow()
               .RequireProofKeyForCodeExchange()
               .AllowClientCredentialsFlow()
               .AllowPasswordFlow()
               .AllowHybridFlow()
               .AllowRefreshTokenFlow();

        options.SetAccessTokenLifetime(TimeSpan.FromMinutes(30));
        options.SetIdentityTokenLifetime(TimeSpan.FromMinutes(30));
        options.SetRefreshTokenLifetime(TimeSpan.FromDays(14));

        options.RegisterScopes(
           OpenIddictConstants.Scopes.OpenId,
           OpenIddictConstants.Scopes.Profile,
           OpenIddictConstants.Scopes.Email,
           OpenIddictConstants.Scopes.OfflineAccess,
           "api_IPD",
           "api_Eficiencia");

        options.AddDevelopmentEncryptionCertificate()
               .AddDevelopmentSigningCertificate();

        options.UseAspNetCore()
             //.EnableTokenEndpointPassthrough()
             .EnableAuthorizationEndpointPassthrough()
             .EnableLogoutEndpointPassthrough()
             .EnableVerificationEndpointPassthrough()
              .EnableStatusCodePagesIntegration();
        //.EnableUserinfoEndpointPassthrough();
        options.AddEventHandler<OpenIddictServerEvents.ProcessSignInContext>(builder =>
        {
            builder.UseInlineHandler(context =>
            {
                var scopes = context.Principal.GetScopes();
                Console.WriteLine($"Token issued with scopes: {string.Join(", ", scopes)}");
                return default;
            });
        });

        options.AddEventHandler<OpenIddictServerEvents.HandleTokenRequestContext>
        (builder => builder.UseScopedHandler<CustomValidationHandler>());
    })
    .AddValidation(options =>
    {
        options.UseLocalServer();
        options.UseAspNetCore();
        options.EnableAuthorizationEntryValidation();
    });

builder.Services.AddQuartz(options =>
{
    options.UseMicrosoftDependencyInjectionJobFactory();
    options.UseSimpleTypeLoader();
    options.UseInMemoryStore();
});

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("DefaultPolicy", policy =>
    {
        policy.RequireAuthenticatedUser();
    });
});

builder.Services.AddRazorPages();

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "AllowAll",
        policy =>
        {
            policy.AllowAnyHeader();
            policy.AllowAnyMethod();
            policy.AllowAnyOrigin();
        });
});

builder.Services.AddTransient<ApplicationSeeder>();
var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("AllowAll");

app.UseAuthentication();
app.UseAuthorization();
//app.UseEndpoints(endpoints => endpoints.MapRazorPages());
app.MapRazorPages();

using (var scope = app.Services.CreateScope())
{
    var serviceProvider = scope.ServiceProvider;
    var seeder = serviceProvider.GetRequiredService<ApplicationSeeder>();
    await seeder.SeedAsync();
}

app.Run();

builder.Services.AddOpenIddict() .AddValidation(options => { // Note: the validation handler uses OpenID Connect discovery // to retrieve the issuer signing keys used to validate tokens. options.SetIssuer("https://localhost:7180/"); options.AddAudiences("resource_server_2");

       options.UseIntrospection()
          .SetClientId("resource_server_2")
          .SetClientSecret("EficienciaTalleres2025#4");

       // Register the System.Net.Http integration.
       options.UseSystemNetHttp();

       // Register the ASP.NET Core host.
       options.UseAspNetCore();
   });

``

clients

  if (!await context.OpenIddictApplications.AnyAsync(s => s.ClientId == "eficienciaFront"))
  {

      var applicationDescriptor = new OpenIddictApplicationDescriptor
      {
          ClientId = "eficienciaFront",
          ClientSecret = "eficiencia2024$1",
          DisplayName = "Eficiencia Talleres",
          RedirectUris = { new Uri("http://localhost:4200/signin-oidc") },
          PostLogoutRedirectUris = { new Uri("http://localhost:4200/signout-callback-oidc") },

          Permissions =
      {
          OpenIddictConstants.Permissions.Endpoints.Authorization,
          OpenIddictConstants.Permissions.Endpoints.Token,

          OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
          OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
          OpenIddictConstants.Permissions.GrantTypes.ClientCredentials,
          OpenIddictConstants.Permissions.GrantTypes.Password,

         OpenIddictConstants.Permissions.Scopes.Email,
          OpenIddictConstants.Permissions.Scopes.Profile,
          OpenIddictConstants.Permissions.Scopes.Address,
          OpenIddictConstants.Permissions.Scopes.Phone,
          OpenIddictConstants.Permissions.Prefixes.Scope + "api_Eficiencia",

          OpenIddictConstants.Permissions.ResponseTypes.Code,
          OpenIddictConstants.Permissions.ResponseTypes.IdToken,
          OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken,
          OpenIddictConstants.Permissions.ResponseTypes.Token
      },
          Requirements =
      {
          Requirements.Features.ProofKeyForCodeExchange,
      },
      };

      await manager.CreateAsync(applicationDescriptor);

      if (await manager.FindByClientIdAsync("resource_server_2") is null)
      {
          await manager.CreateAsync(new OpenIddictApplicationDescriptor
          {
              ClientId = "resource_server_2",
              ClientSecret = "EficienciaTalleres2025#4",
              Permissions =
      {
          Permissions.Endpoints.Introspection
      }
          });
      }

      if (await scopeManager.FindByNameAsync("api_Eficiencia") == null)
      {
          await scopeManager.CreateAsync(new OpenIddictScopeDescriptor
          {
              Name = "api_Eficiencia",
              DisplayName = "API Eficiencia",
              Resources =
          {
              "resource_server_2"
          }
          });
      }
  }

  if (!await context.OpenIddictApplications.AnyAsync(s => s.ClientId == "ipdFront"))
  {
      var applicationDescriptor = new OpenIddictApplicationDescriptor
      {
          ClientId = "ipdFront",
          ClientSecret = "IpdSecrent2024",
          DisplayName = "Aplicación IPD Angular",
          RedirectUris = { new Uri("http://localhost:4200/signin-oidc") },
          PostLogoutRedirectUris = { new Uri("http://localhost:4200/signout-callback-oidc") },

          Permissions =
      {
          OpenIddictConstants.Permissions.Endpoints.Authorization,
          OpenIddictConstants.Permissions.Endpoints.Token,

          OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
          OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
          OpenIddictConstants.Permissions.GrantTypes.ClientCredentials,
          OpenIddictConstants.Permissions.GrantTypes.Password,

         OpenIddictConstants.Permissions.Scopes.Email,
          OpenIddictConstants.Permissions.Scopes.Profile,
          OpenIddictConstants.Permissions.Scopes.Address,
          OpenIddictConstants.Permissions.Scopes.Phone,
          OpenIddictConstants.Permissions.Prefixes.Scope + "api_IPD",

          OpenIddictConstants.Permissions.ResponseTypes.Code,
          OpenIddictConstants.Permissions.ResponseTypes.IdToken,
          OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken,
          OpenIddictConstants.Permissions.ResponseTypes.Token
      },
          Requirements =
      {
          Requirements.Features.ProofKeyForCodeExchange,
      },
      };

      await manager.CreateAsync(applicationDescriptor);

      if (await manager.FindByClientIdAsync("resource_server_1") is null)
      {
          await manager.CreateAsync(new OpenIddictApplicationDescriptor
          {
              ClientId = "resource_server_1",
              ClientSecret = "846B62D0-DEF9-4215-A99D-86E6B8DAB342",
              Permissions =
      {
          Permissions.Endpoints.Introspection
      }
          });
      }

      if (await scopeManager.FindByNameAsync("api_IPD") == null)
      {
          await scopeManager.CreateAsync(new OpenIddictScopeDescriptor
          {
              Name = "api_IPD",
              DisplayName = "API Front",
              Resources =
          {
              "resource_server_1"
          }
          });
      }

Exceptions (if any)

Without exception