Следующим шагом моего приложения является то, что я получаю токен доступа из внешнего интерфейса, а затем использую этот токен, мой бэкэнд вызывает поставщика внешнего входа, чтобы согласовать идентификатор пользователя и получить дополнительную информацию от поставщика входа.
Как и в этом методе, мне нужно установить и настроить поставщика входа в систему для каждого клиента (Интернет, Android). Я пытаюсь использовать Microsoft.AspNetCore.Authentication.Facebook
в моем бэкэнде для обработки входа в facebook, используя утверждения, которые я получаю из facebook. Я хочу использовать это для создания токена Openidic JWT. Все работает нормально, но заявление
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
выбрасывает исключение
An authorization or token response cannot be returned from this endpoint.
вот вопрос с той же ошибкой, но контекст отличается ASP.NET Core Openiddict выдает Ответ OpenID Connect не может быть возвращен с этой конечной точки
вот еще один вопрос с аналогичным подходом, но с другим Как сгенерировать AccessToken для пользователя, который вошел в систему с помощью внешних поставщиков
Вот мой startup.cs
код
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration["ConnectionStrings:ApplicationDbContext"];
services.AddEntityFrameworkNpgsql();
services.AddDbContext<ApplicationDbContext>(
opts =>
{
opts.UseNpgsql(connectionString, b => b.MigrationsAssembly("Data.Repository"));
opts.UseOpenIddict();
}
);
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication().AddFacebook(facebookOptions =>
{
facebookOptions.AppId = "APP_ID";
facebookOptions.AppSecret = "APP_SECRET";
facebookOptions.SaveTokens = true;
});
var validIssuer = Configuration["Token:Issuer"];
services.AddAuthentication()
.AddJwtBearer(cfg =>
{
cfg.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = validIssuer,
IssuerSigningKey = securityKey,
ValidateIssuer = !String.IsNullOrEmpty(validIssuer),
ValidateAudience = false,
ValidateLifetime = true,
ValidateActor = false,
ValidateIssuerSigningKey = true
};
});
services.AddOpenIddict(options =>
{
// Register the Entity Framework stores.
options.AddEntityFrameworkCoreStores<ApplicationDbContext>();
options.AddMvcBinders();
options.EnableTokenEndpoint("/api/account/token");
options.UseJsonWebTokens();
options.AllowPasswordFlow();
options.AllowCustomFlow("urn:ietf:params:oauth:grant-type:facebook_access_token");
options.AllowCustomFlow("urn:ietf:params:oauth:grant-type:google_access_token");
options.AllowCustomFlow("urn:ietf:params:oauth:grant-type:microsoft_access_token");
options.DisableHttpsRequirement();
options.AddSigningKey(securityKey);
});
services.Configure<IdentityOptions>(options =>
{
//OpenId
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
// User settings
options.User.RequireUniqueEmail = true;
});
services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
// Add framework services.
services.AddMvc(options =>
{
options.Filters.Add(new GlobalExceptionFilter());
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, RoleManager<IdentityRole> roleManager)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseAuthentication();
app.UseCors("CorsPolicy");
app.UseMvc();
}
Это контроллер, который обрабатывает внешний вход
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalLogin(string provider = "Facebook", string returnUrl = null)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Auth", new { returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
return BadRequest("Error from external provider");
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return BadRequest();
}
try
{
var ticket = await _accountService.TokenExchangeAsync(info);
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
catch (Exception ex)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = ex.Message
});
}
}
Здесь код, который обрабатывает создание пользователя и другие связанные вещи, и он вызывается ExternalLoginCallback
public async Task<AuthenticationTicket> TokenExchangeAsync (ExternalLoginInfo info)
{
var claims = info.Principal.Claims;
var profile = new Profile
{
email = claims.FirstOrDefault(x => x.Type == ClaimTypes.Email).Value,
id = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value,
name = claims.FirstOrDefault(x => x.Type == ClaimTypes.Name).Value
};
// Find the user
var user = await _userManager.FindByLoginAsync(info.LoginProvider, profile.id);
if (user == null)
{
if (string.IsNullOrEmpty(profile.email))
throw new Exception("Email is not specified in the user profile for the provider.");
await RegisterExtenalUserAsync(profile, info.LoginProvider);
/// Try to find the user
user = await _userManager.FindByLoginAsync(info.LoginProvider, profile.id);
if (user == null)
{
// Return bad request if the user doesn't exist
throw new Exception("Invalid profile or provider.");
}
}
// Create the principal
var principal = await _signInManager.CreateUserPrincipalAsync(user);
// Claims will not be associated with specific destinations by default, so we must indicate whether they should
// be included or not in access and identity tokens.
foreach (var claim in principal.Claims)
{
// For this sample, just include all claims in all token types.
// In reality, claims' destinations would probably differ by token type and depending on the scopes requested.
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken);
}
// Create a new authentication ticket for the user's principal
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
// Include resources and scopes, as appropriate
var scope = new[]
{
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,
OpenIddictConstants.Scopes.Roles
};
ticket.SetScopes(scope);
// Sign in the user
return ticket;
}
Вот файл журнала
Project> info: Microsoft.AspNetCore.Mvc.SignInResult[1]
Project> Executing SignInResult with authentication scheme (ASOS) and the following principal: System.Security.Claims.ClaimsPrincipal.
Project> info: Microsoft.AspNetCore.Mvc.SignInResult[1]
Project> Executing SignInResult with authentication scheme (ASOS) and the following principal: System.Security.Claims.ClaimsPrincipal.
Project> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Project> Executed action Project.Controllers.AuthController.ExternalLoginCallback (Project) in 9068.4071ms
Project> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Project> Executed action Project.Controllers.AuthController.ExternalLoginCallback (Project) in 9068.4071ms
Project> fail: Microsoft.AspNetCore.Server.Kestrel[13]
Project> Connection id "0HL86DQMN0Q30", Request id "0HL86DQMN0Q30:00000003": An unhandled exception was thrown by the application.
Project> System.InvalidOperationException: An authorization or token response cannot be returned from this endpoint.
Project> at AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerHandler.<SignInAsync>d__6.MoveNext()
Project> --- End of stack trace from previous location where exception was thrown ---
Любая помощь будет оценена по достоинству!
SignIn
- это методControllerBase
и одна из точек входа в стек проверки подлинности ASP.NET Core в MVC Core (другие -SignOut
,Challenge
иForbid
). Здесь OP реализует сервер OIDC с использованием OpenIddict, и реализация логики в контроллере авторизации действительно является рекомендуемым вариантом. - person Kévin Chalet   schedule 29.09.2017