ASP.Net Identity 2 – 来自OAuthAuthorizationServerProvider的自定义响应

这个问题是我之前的问题的延续: 使用SMS密码的ASP.Net Identity 2登录 – 不使用双因素身份validation

我已经构建了自定义OAuthAuthorizationServerProvider以支持自定义grant_type。
我的想法是创建一个sms grant_type,允许用户生成一次性访问代码,当用grant_type密码发送请求时,该代码将发送到他的手机,然后用户作为密码。

现在生成,存储和通过短信发送密码我想要返回自定义响应,而不是来自我的GrantCustomExtension的令牌。

 public override async Task GrantCustomExtension(OAuthGrantCustomExtensionContext context) { const string allowedOrigin = "*"; context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] {allowedOrigin}); if (context.GrantType != "sms") { context.SetError("invalid_grant", "unsupported grant_type"); return; } var userName = context.Parameters.Get("username"); if (userName == null) { context.SetError("invalid_grant", "username is required"); return; } var userManager = context.OwinContext.GetUserManager(); ApplicationUser user = await userManager.FindByNameAsync(userName); if (user == null) { context.SetError("invalid_grant", "user not found"); return; } var generator = new TotpSecurityStampBasedTokenProvider(); await userManager.UpdateSecurityStampAsync(user.Id); var accessCode = await generator.GenerateAsync("SMS", userManager, user); var accessCodeExpirationTime = TimeSpan.FromMinutes(5); var result = await userManager.AddAccessCode(user, accessCode, accessCodeExpirationTime); if(result.Succeeded) { Debug.WriteLine("Login code:"+accessCode); //here I'll send login code to user phone via SMS } //return 200 (OK) //with content type="application/json; charset=utf-8" //and custom json content {"message":"code send","expires_in":300} //skip part below ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "SMS"); var ticket = new AuthenticationTicket(oAuthIdentity, null); context.Validated(ticket); } 

如何停止生成令牌并从OAuthAuthorizationServerProvider返回自定义响应?

我知道两种方法: TokenEndpointTokenEndpointResponse ,但我想覆盖整个响应,而不仅仅是令牌。

编辑:
现在我正在使用以下代码在GrantCustomExtension创建临时的ClaimsIdentity:

 var ci = new ClaimsIdentity(); ci.AddClaim(new Claim("message","send")); ci.AddClaim(new Claim("expires_in", accessCodeExpirationTime.TotalSeconds.ToString(CultureInfo.InvariantCulture))); context.Validated(ci); 

而我正在重写TokenEndpointResponse

 public override Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context) { if (context.TokenEndpointRequest.GrantType != "sms") return base.TokenEndpointResponse(context); //clear response containing temporary token. HttpContext.Current.Response.SuppressContent = true; return Task.FromResult(null); } 

这有两个问题:调用context.Validated(ci); 我说这是一个有效的用户,但我想回复我通过短信发送访问代码的信息。 HttpContext.Current.Response.SuppressContent = true; 清除响应,但我想返回一些东西而不是空响应。

这是一种解决方法,然后是最终解决方案,但我相信这是解决您的问题的最可靠方法,而无需重写默认OAuthAuthorizationServerProvider实现中的大量代码。

方法很简单:使用Owin中间件来捕获令牌请求,并在发送SMS时覆盖响应。

[在评论后编辑]修复了允许响应正文根据此答案进行缓冲和更改的代码https://stackoverflow.com/a/36414238/965722

在您的Startup.cs文件中:

 public void Configuration(IAppBuilder app) { var tokenPath = new PathString("/Token"); //the same path defined in OAuthOptions.TokenEndpointPath app.Use(async (c, n) => { //check if the request was for the token endpoint if (c.Request.Path == tokenPath) { var buffer = new MemoryStream(); var body = c.Response.Body; c.Response.Body = buffer; // we'll buffer the response, so we may change it if needed await n.Invoke(); //invoke next middleware (auth) //check if we sent a SMS if (c.Get("sms_grant:sent")) { var json = JsonConvert.SerializeObject( new { message = "code send", expires_in = 300 }); var bytes = Encoding.UTF8.GetBytes(json); buffer.SetLength(0); //change the buffer buffer.Write(bytes, 0, bytes.Length); //override the response headers c.Response.StatusCode = 200; c.Response.ContentType = "application/json"; c.Response.ContentLength = bytes.Length; } buffer.Position = 0; //reset position await buffer.CopyToAsync(body); //copy to real response stream c.Response.Body = body; //set again real stream to response body } else { await n.Invoke(); //normal behavior } }); //other owin middlewares in the pipeline //ConfigureAuth(app); //app.UseWebApi( .. ); } 

在您的自定义授权方法中:

 // ... var result = await userManager.AddAccessCode(user, accessCode, accessCodeExpirationTime); if(result.Succeeded) { Debug.WriteLine("Login code:"+accessCode); //here I'll send login code to user phone via SMS } context.OwinContext.Set("sms_grant:sent", true); //you may validate the user or set an error, doesn't matter anymore //it will be overwritten //... 

我建议你看看这个答案:

https://stackoverflow.com/a/24090287/2508268

 public override Task TokenEndpoint(OAuthTokenEndpointContext context) { foreach (KeyValuePair property in context.Properties.Dictionary) { context.AdditionalResponseParameters.Add(property.Key, property.Value); } return Task.FromResult(null); }