OpenIddict 密码授予返回状态代码 415

时间:2021-05-23 20:46:04

标签: c# asp.net-core openiddict

下午好,

所以我正在努力将 OpenIddict 集成到现有项目中。由于无法调试问题,我陷入了无法发行令牌的困境。所以我从控制器得到了两个完全不同的响应。我已经通过 swagger 和 postman 尝试了这个并得到了相同的状态:415 不支持的媒体类型。现在我很确定我读到密码授予令牌的信息必须是“application/x-www-form-urlencoded”所以​​这就是我传递给控制器​​的信息,但仍然收到 415。另一方面,如果我查看项目的调试日志,我会看到以下内容:

OpenIddict.Server.OpenIddictServerDispatcher: Information: The request address matched a server endpoint: Token.
OpenIddict.Server.OpenIddictServerDispatcher: Information: The token request was successfully extracted: {
  "grant_type": "password",
  "username": "Administrator@MRM2Inc.com",
  "password": "[redacted]"
}.

OpenIddict.Server.OpenIddictServerDispatcher: Information: The token request was successfully validated.

我看起来也是这样。它从未进入令牌端点,因为我立即设置了断点。 这是我的设置:

Startup.cs

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddDbContext<IdentDbContext>(options =>
            {
                //options.UseSqlServer(
                //    Configuration.GetConnectionString("IdentityDB"));

                options.UseOpenIddict<Guid>();
            });
                

            
            // Add the Identity Services we are going to be using the Application Users and the Application Roles
            services.AddIdentity<ApplicationUsers, ApplicationRoles>(config =>
            {
                config.SignIn.RequireConfirmedEmail = true;
                config.SignIn.RequireConfirmedAccount = true;
                config.User.RequireUniqueEmail = true;
                config.Lockout.MaxFailedAccessAttempts = 3;
            }).AddEntityFrameworkStores<IdentDbContext>()
            .AddUserStore<ApplicationUserStore>()
            .AddRoleStore<ApplicationRoleStore>()
            .AddRoleManager<ApplicationRoleManager>()
            .AddUserManager<ApplicationUserManager>()
            .AddErrorDescriber<ApplicationIdentityErrorDescriber>()
            .AddDefaultTokenProviders()
            .AddDefaultUI();

            services.AddDataLibrary();    
            
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
            });                

            //services.AddSingleton<IGenerateTokens, GenerateTokens>();

            // Add in the email
            var emailConfig = Configuration.GetSection("EmailConfiguration").Get<EmailConfiguration>();
            services.AddSingleton(emailConfig);
            services.AddEmailLibrary();

            services.AddOpenIddict()
                // Register the OpenIddict core components.
                .AddCore(options =>
                {
                    // Configure OpenIddict to use the Entity Framework Core stores and models.
                    // Note: call ReplaceDefaultEntities() to replace the default entities.
                    options.UseEntityFrameworkCore()
                    .UseDbContext<IdentDbContext>()
                    .ReplaceDefaultEntities<Guid>();
                })
                // Register the OpenIddict server components.
                .AddServer(options =>
                {
                    // Enable the token endpoint.  What other endpoints?
                    options.SetTokenEndpointUris("/Token");

                    // Enable the client credentials flow.  Which flow do I need?
                    options.AllowPasswordFlow();

                    options.AcceptAnonymousClients();

                    // Register the signing and encryption credentials.
                    options.AddDevelopmentEncryptionCertificate()
                          .AddDevelopmentSigningCertificate();

                    // Register the ASP.NET Core host and configure the ASP.NET Core options.
                    options.UseAspNetCore()                               
                           .EnableTokenEndpointPassthrough();
                })
                // Register the OpenIddict validation components.
                .AddValidation(options =>
                {
                    // Import the configuration from the local OpenIddict server instance.
                    options.UseLocalServer();

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

            // Register the Swagger generator, defining 1 or more Swagger documents
            services.AddSwaggerGen(swagger => 
            {
                swagger.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
                {
                    Name = "Authorization",
                    Type = SecuritySchemeType.Http,
                    Scheme = "Bearer",
                    BearerFormat = "JWT",
                    In = ParameterLocation.Header,
                    Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer'[space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\""
                });
                swagger.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer"
                            }
                        },
                        new string[] {}
                    }
                });
                swagger.OperationFilter<SwaggerDefaultValues>();
                swagger.OperationFilter<AuthenticationRequirementOperationFilter>();

                // Set the comments path for the Swagger JSON and UI.
                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                swagger.IncludeXmlComments(xmlPath);
            });
            services.AddApiVersioning();
            services.AddVersionedApiExplorer(options =>
            {
                options.GroupNameFormat = "'v'VVVV";
                options.DefaultApiVersion = ApiVersion.Parse("0.6.alpha");
                options.AssumeDefaultVersionWhenUnspecified = true;
            });
            services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // 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.UseHttpsRedirection();
            app.UseStaticFiles();

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
            // specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {               
                c.DisplayOperationId();
                var versionDescription = provider.ApiVersionDescriptions;
                foreach (var description in provider.ApiVersionDescriptions.OrderByDescending(_ => _.ApiVersion))
                {
                    c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", $"MRM2 Identity API {description.GroupName}");
                }
            });

            app.UseRouting();

            app.UseAuthentication();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

AuthorizationController.cs:

[Route("api/[controller]/[action]")]
    [ApiController]
    [ApiVersion("0.8.alpha")]
    [Produces(MediaTypeNames.Application.Json)]
    [Consumes(MediaTypeNames.Application.Json)]
    public class AuthorizationController : ControllerBase
    {
        private readonly IConfiguration _configuration;
        private readonly IdentDbContext _context;
        private readonly ApplicationUserManager _userManager;
        private readonly ApplicationRoleManager _roleManager;
        private readonly IGenerateTokens _tokens;
        private readonly IOpenIddictApplicationManager _applicationManager;
        private readonly IOpenIddictAuthorizationManager _authorizationManager;
        private readonly IOpenIddictScopeManager _scopeManager;
        private readonly SignInManager<ApplicationUsers> _signInManager;
        private HttpClient _client;

        public AuthorizationController(IConfiguration configuration, IdentDbContext context, ApplicationUserManager userManager, 
            ApplicationRoleManager roleManager, IGenerateTokens tokens, IOpenIddictApplicationManager applicationManager, IOpenIddictAuthorizationManager authorizationManager,
            IOpenIddictScopeManager scopeManager, SignInManager<ApplicationUsers> signInManager)
        {            
            _configuration = configuration;
            _context = context;
            _userManager = userManager;
            _roleManager = roleManager;
            _tokens = tokens;
            _applicationManager = applicationManager;
            _authorizationManager = authorizationManager;
            _scopeManager = scopeManager;
            _signInManager = signInManager;
        }
        



        [HttpPost("/token"), Produces("application/json")]
        public async Task<IActionResult> Exchange()
        {
            var request = HttpContext.GetOpenIddictServerRequest() ??
                throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");  //does not even hit this breakpoint.

            ClaimsPrincipal claimsPrincipal;
            

            if (request.IsPasswordGrantType())
            {
                var user = await _userManager.FindByNameAsync(request.Username);                
                
                var roleList = await _userManager.GetRolesListAsync(user);
                var databaseList = await _userManager.GetDatabasesAsync(user);
                string symKey = _configuration["Jwt:Symmetrical:Key"];
                string jwtSub = _configuration["Jwt:Subject"];
                string issuer = _configuration["Jwt:Issuer"];
                string audience = _configuration["Jwt:Audience"];

                var claims = new List<Claim>
                {
                    new Claim(JwtRegisteredClaimNames.Sub, jwtSub, issuer),
                    new Claim(ClaimTypes.NameIdentifier, user.Id.ToString(), issuer),
                    new Claim(ClaimTypes.Name, user.UserName, issuer)
                };

                foreach (var role in roleList)
                {
                    claims.Add(new Claim(ClaimTypes.Role, role.Name));
                }

                foreach (var database in databaseList)
                {
                    claims.Add(new Claim(type: "DatabaseName", database));
                }
                var identity = new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
                identity.AddClaim(OpenIddictConstants.Claims.Name, user.UserName, OpenIddictConstants.Destinations.AccessToken);
                identity.AddClaim(OpenIddictConstants.Claims.Subject, jwtSub, OpenIddictConstants.Destinations.AccessToken);
                identity.AddClaim(OpenIddictConstants.Claims.Audience, audience, OpenIddictConstants.Destinations.AccessToken);
                foreach (var cl in claims)
                {
                    identity.AddClaim(cl.Type, cl.Value, OpenIddictConstants.Destinations.AccessToken);                    
                }

                claimsPrincipal = new ClaimsPrincipal(identity);
                
            }

            if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType())
            {
                // Retrieve the claims principal stored in the authorization code/device code/refresh token.
                var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;

                // Retrieve the user profile corresponding to the authorization code/refresh token.
                // Note: if you want to automatically invalidate the authorization code/refresh token
                // when the user password/roles change, use the following line instead:
                // var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
                var user = await _userManager.GetUserAsync(principal);
                if (user == null)
                {
                    return Forbid(
                        authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
                        properties: new AuthenticationProperties(new Dictionary<string, string>
                        {
                            [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                            [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid."
                        }));
                }

                // Ensure the user is still allowed to sign in.
                if (!await _signInManager.CanSignInAsync(user))
                {
                    return Forbid(
                        authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
                        properties: new AuthenticationProperties(new Dictionary<string, string>
                        {
                            [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                            [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in."
                        }));
                }

                foreach (var claim in principal.Claims)
                {
                    claim.SetDestinations(GetDestinations(claim, principal));
                }

                // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
                return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
            }

            throw new InvalidOperationException("The specified grant type is not supported.");
        }

        private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal)
        {
            // Note: by default, claims are NOT automatically included in the access and identity tokens.
            // To allow OpenIddict to serialize them, you must attach them a destination, that specifies
            // whether they should be included in access tokens, in identity tokens or in both.

            switch (claim.Type)
            {
                case Claims.Name:
                    yield return Destinations.AccessToken;

                    if (principal.HasScope(Scopes.Profile))
                        yield return Destinations.IdentityToken;

                    yield break;

                case Claims.Email:
                    yield return Destinations.AccessToken;

                    if (principal.HasScope(Scopes.Email))
                        yield return Destinations.IdentityToken;

                    yield break;

                case Claims.Role:
                    yield return Destinations.AccessToken;

                    if (principal.HasScope(Scopes.Roles))
                        yield return Destinations.IdentityToken;

                    yield break;

                // Never include the security stamp in the access and identity tokens, as it's a secret value.
                case "AspNet.Identity.SecurityStamp": yield break;

                default:
                    yield return Destinations.AccessToken;
                    yield break;
            }
        } 
    }

这是我为此设置邮递员的方式,您可以看到 415 返回enter image description here

不确定我在设置中遗漏了什么,但我收到了相互矛盾的信息,而且我想对控制器进行故障排除以确定我还需要什么来完成控制器并确保我获得了我需要的信息程序的其余部分。 415 不允许我解决问题,但有人说信息是正确的?

2 个答案:

答案 0 :(得分:1)

<块引用>
   [HttpPost("/token"), Produces("application/json")]
    public async Task<IActionResult> Exchange()

根据上面的代码,由于您将Produces属性设置为application/json,所以当您使用PostMan发送请求时,请尝试将Content-Type设置为application/json (在请求的 Body 面板中,选择 Raw 并选择 JSON 格式)。

参考以下示例截图:

enter image description here

此外,我还尝试将 Produces 属性更改为 application/x-www-form-urlencoded,但它会显示 http 406 错误或 415 错误。因此,尝试使用上述方法并将 Content-Type 设置为 application/json

答案 1 :(得分:0)

原来问题出在控制器的头上。上一个答案帮助我看到,虽然他们看到了产品(“应用程序/json”),但事实并非如此。在控制器的顶部,我有一个 Consumes("application/json")。去掉这个就进入方法了。

相关问题