正确的WebApi和OAuth登录过程

本文关键字:OAuth 登录 过程 WebApi | 更新日期: 2023-09-27 18:16:26

我最近开始深入研究WebApi,作为开始新的c# web项目的基础。我已经看了一下VS2013 Update 4提供的基本模板,特别是MVC(尝试将方法移植到WebApi)以及基本的WebApi模板。

通过我建立这个框架(它基本上应该提供身份验证和授权规则)的努力,我已经习惯了以下来源作为指导:

  • http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
  • http://bitoftech.net/2015/02/03/asp-net-identity-2-accounts-confirmation-password-user-policy-configuration/
  • http://kaliko.com/blog/aspnet-template-for-data-access-identity/

使用这些源代码,我拼凑了一个基本的WebApi框架,它使用OAuth和JWT作为承载令牌提供者。我还用一个使用Telerik ORM而不是实体框架的库取代了原生的asp.net身份库(因为我们需要映射到使用元数据在结构上定义的自定义表,但现在这并不重要)。

然而,上述来源都没有描述如何使用WebApi进行登录,并且我所做的其他搜索也没有产生结果。

使用使用asp.net Identity的基本MVC模板(带有个人用户帐户和Cookie身份验证),它包含相当强大的登录过程,可以登录用户,可以锁定用户,或者可以检查是否需要帐户验证,这些都是由登录管理器提供的。

基本的WebApi模板(带有个人用户帐户和Cookie身份验证)使用AuthAuthorizationServerOptions,它被设置为检查具有提供的凭据的用户是否存在,生成用户身份以获得ClaimsIdenity,生成身份验证票据,验证票据,然后调用context. request . context. Authentication。登录(参数类型ClaimsIdentity)。这个函数做什么,我不确定,因为它似乎没有应用到它的MVC模板有使用SigninManager相同的规则。

我想知道的是,使用WebApi登录一个人的正确过程是什么?承载令牌授予是否构成登录过程的一部分,或者应该完成登录(使用IAuthenticationManager或SigninManager),并且基于登录的结果,如果成功,则只应该请求令牌?

下面我提供了我的Startup.cs和OAuthProvider的版本。

Startup.cs:

[assembly: OwinStartup(typeof(TelerikAndWebAPI.Startup))]
namespace TelerikAndWebAPI
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
            app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
            ConfigureOAuthTokenGeneration(app);
            ConfigureOAuthTokenConsumption(app);
            WebApiConfig.Register(config);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        }
        //Uses JWT bearer tokens
        public void ConfigureOAuthTokenGeneration(IAppBuilder app)
        {
            bool debug = false;
            if (System.Diagnostics.Debugger.IsAttached) {
                debug = true;
            }
            else{
                debug = false;
            }
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = debug,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new CustomOAuthProvider(),
                AccessTokenFormat = new CustomJwtFormat(HttpContext.Current.Request.Url.AbsoluteUri.Replace(HttpContext.Current.Request.Url.PathAndQuery, "/"))
            };
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
        }
        //Consume the JWT bearer tokens
        private void ConfigureOAuthTokenConsumption(IAppBuilder app)
        {
            var issuer = HttpContext.Current.Request.Url.AbsoluteUri.Replace(HttpContext.Current.Request.Url.PathAndQuery, "/");
            string audienceId = JwtCodeProvider.audienceID;
            byte[] audienceSecret = TextEncodings.Base64Url.Decode(JwtCodeProvider.audienceSecret);
            // Api controllers with an [Authorize] attribute will be validated with JWT
            app.UseJwtBearerAuthentication(
                new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = AuthenticationMode.Active,
                    AllowedAudiences = new[] { audienceId },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                    {
                        new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
                    }
                });
        }
    }
}

OAuthProvder:

    public class CustomOAuthProvider : OAuthAuthorizationServerProvider
    {
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
            return Task.FromResult<object>(null);
        }
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var allowedOrigin = "*";
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
            using (AccountController accounts = new AccountController())
            {
                //Should I plug in a signin manager or authentication manager here and then based on whether the
                //sign in is successful, then do the bearer token generation?
                IdentityUser user = await accounts.FindUser(context.UserName, context.Password);
                if (user == null)
                {
                    context.SetError("invalid_grant", "The user name or password is incorrect.");
                    return;
                }
                ClaimsIdentity oAuthIdentity = await accounts.GenerateUserIdentity(user, "JWT");
                var ticket = new AuthenticationTicket(oAuthIdentity, null);
                context.Validated(ticket);
            }          
        }
    }

为了清晰起见,ClaimsIdentity oAuthIdentity = await accounts.GenerateUserIdentity(user, "JWT");的源代码:

    public async Task<ClaimsIdentity> GenerateUserIdentity(IdentityUser user,string authenticationType)
    {
        ClaimsIdentity oAuthIdentity = await UserManager.CreateIdentityAsync(user, authenticationType);
        return oAuthIdentity;
    }

我已经在我认为我应该插入SignInManager方法的oauthrprovider类中包含了注释,但是,我不确定。如果有人能告诉我最好的做法是什么,我将不胜感激。

作为免责声明,我的类中的许多代码与我提供的源代码中相同。一旦我完成了概念证明,我就会改变这个。

正确的WebApi和OAuth登录过程

VS 2013模板的问题,它们混合了MVC应用程序的身份验证(应用程序在浏览器中呈现并使用传统的身份验证cookie)和依赖于OAuth 2.0承载令牌的Web Api身份验证,而不管令牌格式(JWT或默认格式)。

在REST api世界中,身份验证应该保持简单,因为你有一个广泛的消费者(移动应用程序,JS,服务器端服务,MVC等),它应该是无状态的(对每个请求进行身份验证),所以要实现这一点,你需要在开始时获得一个OAuth访问令牌,通过在服务器上提供用户名/密码验证,然后发出这个令牌并将其返回给消费者。此令牌应该有到期日期/时间(几个小时、几天、几个月)。

一旦您有了访问令牌,您需要使用承载方案将它与授权头中的每个请求一起发送。然后服务器将负责验证它并允许/拒绝调用。JWT访问令牌是自包含的令牌,因此您可以在其中放入声明(关于登录的用户或代表用户的客户端应用程序的信息),并且您可以在每个请求时在服务器上再次读取这些声明,而无需访问DB。

现在SignIn管理器在你的情况下是不需要的,它将是有用的,如果你想创建一个安全的身份验证cookie,将在MVC应用程序中使用。

如果你的应用程序将包含MVC和Web API,那么你需要SignIn管理器

我希望我的回答能澄清你的疑虑。