Там, где нужно исправить несколько вещей.
Первый localhost не применяется к приложению, развернутому где-то в вашей сети, поэтому мне пришлось изменить URL обратного вызова с
http://localhost:5000/Account/ExternalLoginCallback
на
Callback url: http://{IP}:5000/signin-gitlab
Обратите внимание на переименование /Account/ExternalLoginCallback
в /signin-gitlab
.
Startup.ConfigureServices
(соответствующие части):
...
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOAuth("Gitlab", options =>
{
options.SignInScheme = IdentityConstants.ExternalScheme;
// App creds
options.ClientId = "xxx";
options.ClientSecret = "xxx";
options.CallbackPath = new PathString("/signin-gitlab");
options.AuthorizationEndpoint = "https://myGitlabServer/oauth/authorize";
options.TokenEndpoint = "https://myGitlabServer/oauth/token";
options.UserInformationEndpoint = "https://myGitlabServer/api/v4/user";
options.SaveTokens = true;
options.Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted);
response.EnsureSuccessStatusCode();
var user = JObject.Parse(await response.Content.ReadAsStringAsync());
// Add claims
var userId = user.Value<string>("username");
if (!string.IsNullOrEmpty(userId))
{
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId, ClaimValueTypes.String, context.Options.ClaimsIssuer));
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, userId);
}
var name = user.Value<string>("name");
if (!string.IsNullOrEmpty(name))
{
context.Identity.AddClaim(new Claim(ClaimTypes.GivenName, name, ClaimValueTypes.String, context.Options.ClaimsIssuer));
options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, name);
}
var email = user.Value<string>("email");
if (!string.IsNullOrEmpty(email))
{
context.Identity.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.Email, context.Options.ClaimsIssuer));
options.ClaimActions.MapJsonKey(ClaimTypes.Email, email);
}
var avatar = user.Value<string>("avatar_url");
if (!string.IsNullOrEmpty(avatar))
{
context.Identity.AddClaim(new Claim(ClaimTypes.Uri, avatar, ClaimValueTypes.String, context.Options.ClaimsIssuer));
options.ClaimActions.MapJsonKey(ClaimTypes.Uri, avatar);
}
}
};
})
.AddCookie();
...
Внутри моего AccountController
У меня есть 2 метода для обработки внешних входов в систему:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", 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)
{
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToAction(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// Sign in
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
var user = await _userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
var claimsPrincipal = await this._signInManager.CreateUserPrincipalAsync(user);
var identity = claimsPrincipal.Identity as ClaimsIdentity;
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
var updateResult = await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
return RedirectToAction(nameof(Lockout));
}
else
{
// If the user doesn't have an account, then ask to create one
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLogin", new ExternalLoginViewModel { Email = email });
}
}
Наконец часть представления в моем представлении входа в систему:
<section>
<h4>Use another service to log in.</h4>
<hr />
@{
var loginProviders = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (loginProviders.Count == 0)
{
<div>
<p>
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
for details on setting up this ASP.NET application to support logging in via external services.
</p>
</div>
}
else
{
<form asp-action="ExternalLogin" asp-route-returnurl="@Context.Request.Query["returnUrl"]" method="post" class="form-horizontal">
<div>
<p>
@foreach (var provider in loginProviders)
{
<button type="submit" class="btn btn-gitlab" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account"><i class="fab fa-@provider.Name.ToLower()"></i> @provider.Name</button>
}
</p>
</div>
</form>
}
}
</section>