C# ASP.NET Web API用于Web和移动应用程序的社会身份验证

C# ASP.NET Web API用于Web和移动应用程序的社会身份验证,c#,android,asp.net,asp.net-web-api,oauth,C#,Android,Asp.net,Asp.net Web Api,Oauth,我的问题有点复杂,所以请容忍我,因为我试图很好地阐述我正在努力解决的问题 目标 有一个ASP.NET网站,允许用户通过用户名/密码或社交网站(Facebook、Twitter、Google等)注册和登录,该网站也有一个API。需要使用[Authorize]锁定此API。API需要能够被移动客户端(Android、iOS等)访问,这些客户端可以通过用户名/密码或社交(Facebook、Twitter、Google等)登录 背景 所以我做过一些网站,它们可以从我的目标出发做一两件事,但不是全部。有很

我的问题有点复杂,所以请容忍我,因为我试图很好地阐述我正在努力解决的问题

目标

有一个ASP.NET网站,允许用户通过用户名/密码或社交网站(Facebook、Twitter、Google等)注册和登录,该网站也有一个API。需要使用
[Authorize]
锁定此API。API需要能够被移动客户端(Android、iOS等)访问,这些客户端可以通过用户名/密码或社交(Facebook、Twitter、Google等)登录

背景

所以我做过一些网站,它们可以从我的目标出发做一两件事,但不是全部。有很多很好的在线示例和VS项目中内置的示例,展示了如何让用户通过社交应用注册和登录,但这些示例仅适用于网站,不适用于手机。我做过一个网站,Android应用程序使用用户名/密码通过API进行身份验证,但没有使用OAuth或社交凭据

我开始使用它作为参考,但我不知道如何将其用于我的网站登录和我的移动应用程序登录

使它听起来很简单,但没有显示任何代码

问题

是否有教程或GitHub示例可以帮助我实现目标?我基本上想要一个网站,人们可以注册用户名/密码或使用他们的社交帐户,也可以让用户通过移动设备进行注册和登录。移动设备基本上只使用API推/拉数据,但我不确定如何将社交登录与我的API结合起来。我假设我需要使用OAuth并走这条路,但我找不到任何好的例子来说明如何为web和移动设备做到这一点


或者,正确的解决方案是让网页都是cookie auth,API都是单独的“网站”,都是token auth,它们都绑定到同一个数据库?

我已经在自己的ASP.NET MVC应用程序中使用ASP.NET Identity成功地完成了这项任务,但是,接着你提到了一个问题:我也需要它来使用Web API,这样我的移动应用程序就可以进行本机交互

我不熟悉您链接的文章,但读完后,我注意到很多工作和代码都是不必要的,并且使ASP.NET Identity中已经存在的功能变得复杂

以下是我的建议,我假设您使用的是ASP.NET Identity V2,它相当于围绕MVC5的包(而不是新的MVC6 vNext)。这将允许您的网站和移动应用程序通过API通过本地登录(用户名/密码)和外部OAuth提供商(来自您网站上的MVC web视图)以及来自移动应用程序的web API调用进行身份验证:

第一步。创建项目时,请确保包含MVC和Web API所需的软件包。在ASP.NET项目选择对话框中,您可以选择复选框,确保MVC和Web API都被选中。如果您在创建项目时还没有这样做,我建议您创建一个新项目并迁移现有代码,而不是搜索并手动添加依赖项和模板代码

第二步。在Startup.Auth.cs文件中,您需要代码来告诉OWIN使用cookie身份验证、允许外部登录cookie以及支持OAuth承载令牌(这是Web API调用进行身份验证的方式)。以下是我的工作项目代码库的相关摘录:

Startup.Auth.cs

// Enable the application to use a cookie to store information for the signed in user
        // and to use a cookie to temporarily store information about a user logging in with a third party login provider
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/account/login"),
            Provider = new CookieAuthenticationProvider
            {
                // Enables the application to validate the security stamp when the user logs in.
                // This is a security feature which is used when you change a password or add an external login to your account.  
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

// Configure the application for OAuth based flow
        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/account/externallogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            //AllowInsecureHttp = false
        };

        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions);

 app.UseTwitterAuthentication(
            consumerKey: "Twitter API Key",
            consumerSecret: "Twitter API Secret");

        app.UseFacebookAuthentication(
            appId: "Facebook AppId",
            appSecret: "Facebook AppSecret");
// Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OAuth;
using Butler.Models;

using Schloss.AspNet.Identity.Neo4j;

namespace Butler.Providers
{
    public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        private readonly string _publicClientId;

        public ApplicationOAuthProvider(string publicClientId)
        {
            if (publicClientId == null)
            {
                throw new ArgumentNullException("publicClientId");
            }

            _publicClientId = publicClientId;
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

            ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);

            if (user == null)
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }

            ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
               OAuthDefaults.AuthenticationType);
            ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
                CookieAuthenticationDefaults.AuthenticationType);

            AuthenticationProperties properties = CreateProperties(user.UserName);
            AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesIdentity);
        }

        public override Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            {
                context.AdditionalResponseParameters.Add(property.Key, property.Value);
            }

            return Task.FromResult<object>(null);
        }

        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            // Resource owner password credentials does not provide a client ID.
            if (context.ClientId == null)
            {
                context.Validated();
            }

            return Task.FromResult<object>(null);
        }

        public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            if (context.ClientId == _publicClientId)
            {
                //Uri expectedRootUri = new Uri(context.Request.Uri, "/");

                //if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                //{
                    context.Validated();
                //}
            }

            return Task.FromResult<object>(null);
        }

        public static AuthenticationProperties CreateProperties(string userName)
        {
            IDictionary<string, string> data = new Dictionary<string, string>
            {
                { "userName", userName }
            };
            return new AuthenticationProperties(data);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;

namespace Butler.Results
{
    public class ChallengeResult : IHttpActionResult
    {
        public ChallengeResult(string loginProvider, ApiController controller)
        {
            LoginProvider = loginProvider;
            Request = controller.Request;
        }

        public string LoginProvider { get; set; }
        public HttpRequestMessage Request { get; set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            Request.GetOwinContext().Authentication.Challenge(LoginProvider);

            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
            response.RequestMessage = Request;
            return Task.FromResult(response);
        }
    }
}
第四步。MVC和Web API都需要AccountController(或等效的控制器)。在我的项目中,我有两个AccountController文件,一个MVC控制器继承自基本控制器类,另一个AccountController继承自位于Controllers.API命名空间中的ApiController,以保持整洁。我正在使用Web API和MVC项目中的标准模板AccountController代码。以下是帐户控制器的API版本:

AccountController.cs(Controllers.API命名空间)

使用系统;
使用System.Collections.Generic;
使用System.Net.Http;
使用System.Security.Claims;
使用System.Security.Cryptography;
使用System.Threading.Tasks;
使用System.Web;
使用System.Web.Http;
使用System.Web.Http.ModelBinding;
使用Microsoft.AspNet.Identity;
使用Microsoft.AspNet.Identity.Owin;
使用Microsoft.Owin.Security;
使用Microsoft.Owin.Security.Cookies;
使用Microsoft.Owin.Security.OAuth;
使用Disco.Models.API;
使用迪斯科舞厅供应商;
使用Disco.Results;
使用Schloss.AspNet.Identity.Neo4j;
使用Disco.Results.API;
命名空间Disco.Controllers.API
{
[授权]
[RoutePrefix(“api/账户”)]
公共类AccountController:ApicController
{
私有常量字符串LocalLoginProvider=“Local”;
私有应用程序用户管理器\u用户管理器;
公共账户控制员()
{            
}
公共帐户控制器(ApplicationUserManager用户管理器,
ISecureDataFormat访问令牌格式)
{
UserManager=UserManager;
AccessTokenFormat=AccessTokenFormat;
}
公共应用程序管理员用户管理器
{
得到
{
返回_userManager??Request.GetOwinContext().GetUserManager();
}
专用设备
{
_userManager=value;
}
}
公共ISecureDataFormat AccessTokenFormat{get;private set;}
//获取帐户/用户信息
[主机身份验证(DefaultAuthenticationTypes.ExternalBearer)]
[路线(“用户信息”)]
public UserInfoViewModel GetUserInfo()
{
ExternalLoginData externalLogin=ExternalLoginData.FromIdentity(User.Identity作为索赔实体);
返回
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;

namespace Butler.Results
{
    public class ChallengeResult : IHttpActionResult
    {
        public ChallengeResult(string loginProvider, ApiController controller)
        {
            LoginProvider = loginProvider;
            Request = controller.Request;
        }

        public string LoginProvider { get; set; }
        public HttpRequestMessage Request { get; set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            Request.GetOwinContext().Authentication.Challenge(LoginProvider);

            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
            response.RequestMessage = Request;
            return Task.FromResult(response);
        }
    }
}