社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Git

使用(内部部署)Gitlab服务器作为OAuth提供程序

marrrschine • 4 年前 • 214 次点击  

我想让.NET核心2.1 MVC应用程序使用我的内部Gitlab服务器作为OAuth身份验证服务提供商。 在Gitlab管理区域中,我添加了一个应用程序:

Application Id: xxx
Secret: xxx
Callback url: http://localhost:5000/Account/ExternalLoginCallback
Trusted: Y
Scopes: - api (Access the authenticated user's API)
        - openid (Authenticate using OpenID Connect)

这个 Startup.ConfigureServices 类似于:

services.AddAuthentication().AddOAuth("GitLab", options => 
        {
            options.ClientId = "xxx";
            options.ClientSecret = "xxx";
            options.CallbackPath = new PathString("/Account/ExternalLoginCallback");

            options.AuthorizationEndpoint = "https://myGitlabServer/oauth/authorize";
            options.TokenEndpoint = "https://myGitlabServer/login/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.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);

                    var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
                    response.EnsureSuccessStatusCode();

                    var user = JObject.Parse(await response.Content.ReadAsStringAsync());

                    context.RunClaimActions(user);
                }
            };
        });

在导航到我的应用程序的登录页面时,我可以选择Gitlab作为登录提供程序,并成功重定向到登录页面。在使用正确的凭证输入之后,我希望调用控制器,但重定向失败。

Exception: OAuth token endpoint failure: Status: NotFound;Headers: Server: nginx

相应的请求是:

GET http://localhost:5000/Account/ExternalLoginCallback?code=xxx&state=xxx HTTP/1.1

这个 AccountController 签名如下:

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    {
       ...
    }

你知道我遗漏了什么或做错了什么吗?

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/38242
 
214 次点击  
文章 [ 1 ]  |  最新文章 4 年前
marrrschine
Reply   •   1 楼
marrrschine    5 年前

有几件事需要改正。

第一个本地主机不适用于部署在您网络中某个位置的应用程序,因此我必须将回调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 我有两种方法来处理外部登录:

    [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>&nbsp; @provider.Name</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>