关注@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。
答案 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)
在这种情况下验证用户的一般方法是
现在尝试了解一般情况下,需要整个身份验证过程,因为您的服务器不信任登录的用户是该用户声称的用户。但是,在您的情况下,您(或您的服务器代码)只是创建了一个用户,并且您确定访问您网站的人是您刚刚创建的用户。在这种情况下,您不需要验证令牌来为该用户创建会话。只需创建一个适合您的用例的会话。
下次用户必须使用令牌登录并向服务器证明自己,但这次用户无需证明他/她自己。
注意:如果您绝对坚持要求令牌登录您自己刚刚使用其凭据创建的用户,则存在以下几个问题。
另外,看一下支持隐式授权的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。