从Web API

时间:2016-04-26 12:00:18

标签: c# asp.net asp.net-identity .net-4.5

关注@Taiseer Joudeh我能够创建简单的Web API POC。我可以创建新帐户,然后登录并在向标头添加JWT令牌时调用安全Web API。

我想修改负责创建帐户的方法 现在我正在使用新的用户对象返回Create(201)代码,但我想返回访问令牌。

我找到similar question但需要创建HttpClient并向OAuthAuthorizatioServer TokenEndpointPath发出请求。

Second question我发现需要生成返回到前端的临时令牌,但是前端必须向服务器发出额外请求才能获得“真实”令牌。

我想要做的是在创建用户帐户时返回登录响应(access_token,token_type和expires_in)。 我希望用户在创建帐户时进行身份验证。

我只使用Web API和JWT而没有任何cookie。

编辑:我的临时解决方案:
创建用户后我正在这样做:

var validTime = new TimeSpan(0, 0, 0, 10);
var identity = await UserManager.CreateIdentityAsync(user, "JWT");
var jwtFormat = new CustomJwtFormat(ApplicationConfiguration.Issuer);
var authenticationProperties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow, ExpiresUtc = DateTimeOffset.UtcNow.Add(validTime) };
var authenticationTicket = new AuthenticationTicket(identity, authenticationProperties);
var token = jwtFormat.Protect(authenticationTicket);

var response = new
{
    access_token = token,
    token_type = "bearer",
    expires_in = validTime.TotalSeconds.ToInt()
};

return Ok(response);

其中CustomJwtFormat来自this awesome article

5 个答案:

答案 0 :(得分:3)

下面是一些类似于我在我的应用程序中使用Asp.Net Core 1.0的代码。如果您不使用Core 1.0,您的登录和用户注册会有所不同。

Group

基本上,用户已创建,然后自动登录。然后我构建一个JWT(添加你想要的任何声明)并将其返回到响应正文中。在客户端(MVC和Angular JS),我从响应主体中获取JWT并存储它。然后在每个后续请求的Authorization标头中将其传递回服务器。所有服务器操作的授权策略均基于JWT提供的声明集。没有cookie,服务器上没有状态。

编辑:我想你甚至不需要在这个过程中调用signIn方法,因为你可以创建用户,创建一个JWT,然后返回JWT。当用户登录将来的请求时,您将使用登录,创建令牌,注销方法。

答案 1 :(得分:2)

创建用户时使用access_token,token_type和expires_in发送响应的想法是个好主意。但是,我会坚持用HttpClient调用“/ token”终点来完成这个任务。在生成令牌之前,需要执行相当多的安全检查。由于这是安全,我不会冒任何风险。我很乐意使用行业专家提供的库/代码。

也就是说,我试图想出一个类,你可以在创建用户后调用它来创建令牌。此代码取自Microsoft在Katana项目中的OAuth授权服务器实现。您可以访问来源here。正如您所看到的,在创建令牌时会发生很多事情。

以下是用于生成令牌的中间件类的修改版本。您必须提供正确的OAuthAuthorizationServerOptions,Context,username,password,Scopes和clientid才能获取访问令牌。请注意,这是一个示例实现,可指导您朝着正确的方向前进。如果你想使用它,请彻底测试。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AspNetIdentity.WebApi.Providers;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Microsoft.Owin.Security.OAuth;

namespace WebApi.AccessToken
{
    public class TokenGenerator
    {
        public string ClientId { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public IList<string> Scope { get; set; }

        private OAuthAuthorizationServerOptions Options { get; } =
            new OAuthAuthorizationServerOptions()
            {
                //For Dev enviroment only (on production should be AllowInsecureHttp = false)
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/oauth/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new CustomOAuthProvider(),
                AccessTokenFormat = new CustomJwtFormat("http://localhost:59822")
            };

        public async Task<IList<KeyValuePair<string, string>>>  InvokeTokenEndpointAsync(IOwinContext owinContext)
        {
            var result = new List<KeyValuePair<string, string>>();

            DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
            // remove milliseconds in case they don't round-trip
            currentUtc = currentUtc.Subtract(TimeSpan.FromMilliseconds(currentUtc.Millisecond));

            AuthenticationTicket ticket = await InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(owinContext, Options, currentUtc);

            if (ticket == null)
            {
                result.Add(new KeyValuePair<string, string>("ERROR", "Failed to create acess_token"));
                return result;
            }

            ticket.Properties.IssuedUtc = currentUtc;
            ticket.Properties.ExpiresUtc = currentUtc.Add(Options.AccessTokenExpireTimeSpan);
            ticket = new AuthenticationTicket(ticket.Identity, ticket.Properties);

            var accessTokenContext = new AuthenticationTokenCreateContext(
                owinContext,
                Options.AccessTokenFormat,
                ticket);

            await Options.AccessTokenProvider.CreateAsync(accessTokenContext);
            string accessToken = accessTokenContext.Token;

            if (string.IsNullOrEmpty(accessToken))
            {
                accessToken = accessTokenContext.SerializeTicket();
            }

            DateTimeOffset? accessTokenExpiresUtc = ticket.Properties.ExpiresUtc;

            result.Add(new KeyValuePair<string, string>("access_token", accessToken));
            result.Add(new KeyValuePair<string, string>("token_type", "bearer"));
            TimeSpan? expiresTimeSpan = accessTokenExpiresUtc - currentUtc;
            var expiresIn = (long)expiresTimeSpan.Value.TotalSeconds;
            if (expiresIn > 0)
            {
                result.Add(new KeyValuePair<string, string>("expires_in", "bearer"));
            }
            return result;
        }

        private async Task<AuthenticationTicket> InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(IOwinContext owinContext, OAuthAuthorizationServerOptions options, DateTimeOffset currentUtc)
        {
            var grantContext = new OAuthGrantResourceOwnerCredentialsContext(
                owinContext, 
                options, 
                ClientId, 
                UserName, 
                Password, 
                Scope);
            await options.Provider.GrantResourceOwnerCredentials(grantContext);
            return grantContext.Ticket;
        }
    }
}

如果您有任何疑问,请与我们联系。

谢谢你, 索马。

答案 2 :(得分:1)

我假设您指的是以下文章:http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity-2/

在这种情况下验证用户的一般方法是

  1. 对令牌端点进行HTTP调用
  2. 用户在呈现的UI中输入凭据
  3. IdP验证凭据并发出令牌
  4. 用户再次向授权端点发出呼叫,OWIN将验证此(JWT)令牌(在ConfigureOAuthTokenConsumption方法中配置),如果成功,则将设置与令牌到期时相同的到期用户会话。会话使用会话cookie设置。
  5. 现在尝试了解一般情况下,需要整个身份验证过程,因为您的服务器不信任登录的用户是该用户声称的用户。但是,在您的情况下,您(或您的服务器代码)只是创建了一个用户,并且您确定访问您网站的人是您刚刚创建的用户。在这种情况下,您不需要验证令牌来为该用户创建会话。只需创建一个适合您的用例的会话。

    下次用户必须使用令牌登录并向服务器证明自己,但这次用户无需证明他/她自己。

    注意:如果您绝对坚持要求令牌登录您自己刚刚使用其凭据创建的用户,则存在以下几个问题。

    • 您承担了存储(有权访问)用户凭据的责任,这些凭据在应用程序的生命周期内可能与您无关(在大多数情况下,您可能希望充当依赖方而非IdP)
    • 即使你想这样做也不是微不足道的。您必须代表用户在代码(服务器或客户端)中调用令牌端点,输入他们的凭据,检索令牌,调用站点上经过身份验证的端点并检索会话cookie从用户那里隐藏所有这些,如果你讨厌自己,这可能就是你要做的事情:)但也不是非常安全的做事方式,尤其是当你在第一时间实施OAuth时遇到麻烦。

    另外,看一下支持隐式授权的Windows Server 2016(技术预览版本5),如果您可以等待RTM,可能需要将所有这些自定义代码写下来。

答案 3 :(得分:0)

在OAuth解决方案中您作为开发人员不需要自行处理Cookie设置。 cookie处理由框架自动完成。

设置会话的唯一方法是a。使用会话cookie或b。使用无cookie(在url中)方法。请查看http://www.cloudidentity.com/blog/2015/02/19/introducing-adal-js-v1/以获取有关令牌验证和会话建立的更多详细信息(还可以搜索术语cookie,您将知道它所用的内容)。

如果您开始考虑根本不使用cookie,您不仅需要弄清楚如何维护会话并在没有cookie的情况下安全地执行,还必须重新编写代码检查并刷新令牌的令牌刷新代码基于会话cookie的存在。 (即不是一个聪明的主意)

答案 4 :(得分:0)

我正在使用确切的技术堆栈,并且最近成功实施了基于令牌的授权。我参考的链接已经非常巧妙地定义了Web API中基于令牌的身份验证。我必须说的必须书签页面。以下是链接:TOKEN BASED AUTHENTICATION IN WEB APIs