ASP.Net OAuth授权服务器:添加一个数组作为附加响应参数

我已经实现了一个自定义的OAuthAuthorizationServerProvider ,我希望在我的客户端请求访问令牌时在响应中添加一些其他元素。

为此,我重写了OAuthAuthorizationServerProvider.TokenEndpoint方法,并成功设法添加了一些单个元素(通过将它们添加到context.AdditionalResponseParameters字典中)。 现在我有这样的回应:

 { "access_token": "wxoCtLSdPXNW9KK09PVhSqYho...", "token_type": "bearer", "expires_in": 1199, "refresh_token": "uk0kFyj4Q2OufWKt4IzWQHlj...", "toto": "bloblo", "tata": "blabla" } 

这很棒,但我的目标是添加一个数组以获得这种响应:

 { "access_token": "wxoCtLSdPXNW9KK09PVhSqYho...", "token_type": "bearer", "expires_in": 1199, "refresh_token": "uk0kFyj4Q2OufWKt4IzWQHlj...", "scopes": ["read", "write"] } 

我试图添加一个json解析的列表或数组而不是一个简单的字符串,但它给了我

 "scopes": "[\"read\",\"write\"]" 

这是解析为Json的字符串,而不是Json数组:/

如何在TokenEndpoint响应中添加Json数组?

问题

当我们使用app.OAuthBearerAuthenticationExtensions ,会调用下一个链:

 public static class OAuthBearerAuthenticationExtensions { public static IAppBuilder UseOAuthBearerAuthentication(this IAppBuilder app, OAuthBearerAuthenticationOptions options) { if (app == null) throw new ArgumentNullException(nameof (app)); app.Use((object) typeof (OAuthBearerAuthenticationMiddleware), (object) app, (object) options); app.UseStageMarker(PipelineStage.Authenticate); return app; } } 

然后, OAuthAuthorizationServerMiddleware类型的对象使用内部类OAuthAuthorizationServerHandler ,其中使用JsonTextWriter

 using (var jsonTextWriter = new JsonTextWriter((TextWriter) new StreamWriter((Stream) memory))) { jsonTextWriter.WriteStartObject(); jsonTextWriter.WritePropertyName("access_token"); jsonTextWriter.WriteValue(accessToken); jsonTextWriter.WritePropertyName("token_type"); jsonTextWriter.WriteValue("bearer"); // and so on this.Response.ContentLength = new long?((long) body.Length); await this.Response.WriteAsync(body, this.Request.CallCancelled); } 

这里有两个限制:
*) JsonTextWriter是一个无法配置的纯类,它只是将字符串写为StringBuilder,因此无法应用Json.Settings = new MySettings()JsontTextWriter也不支持复杂对象。 数组只能写为jsonTextWriter.WriteStartArray()jsonTextWriter.WriteEndArray() ,但在OAuthAuthorizationServerHandler被忽略。
*)有些类是内部的,不能被覆盖或inheritance。

似乎Microsoft开发人员没有预见到这个问题,只是将自定义属性限制为IDictionary

解决方案1

而不是app.UseOAuthBearerAuthentication(...)应用您自己的代码

 app.Use(options); app.UseStageMarker(PipelineStage.Authenticate); 

您可以从OAuthBearerAuthenticationMiddleware派生一个类,并将其用于您自己的目的。

解决方案2

覆盖令牌端点响应。 这是一个棘手的事情。

1)创建一个自定义中间件,它将包装其他调用并覆盖Body响应流。

 class AuthenticationPermissionsMiddleware : OwinMiddleware { public AuthenticationPermissionsMiddleware(OwinMiddleware next) : base(next) { } public override async Task Invoke(IOwinContext context) { if (!context.Request.Path.Equals("/Token") { await Next.Invoke(context); return; } using (var tokenBodyStream = new MemoryStream()) { // save initial OWIN stream var initialOwinBodyStream = context.Response.Body; // create new memory stream context.Response.Body = tokenBodyStream; // other middlewares will will update our tokenBodyStream await Next.Invoke(context); var tokenResponseBody = GetBodyFromStream(context.Response); var obj = JsonConvert.DeserializeObject(tokenResponseBody); var jObject = JObject.FromObject(obj); // add your custom array or any other object var scopes = new Scope[]; jObject.Add("scopes", JToken.FromObject(scopes)); var bytes = Encoding.UTF8.GetBytes(jObject.ToString()); context.Response.Body.Seek(0, SeekOrigin.Begin); await tokenBodyStream.WriteAsync(bytes, 0, bytes.Length); context.Response.ContentLength = data.LongLength; tokenBodyStream.Seek(0, SeekOrigin.Begin); // get back result to the OWIN stream await context.Response.Body.CopyToAsync(initialOwinBodyStream); } } } private string GetBodyFromStream(IOwinResponse response) { using (var memoryStream = new MemoryStream()) { response.Body.Seek(0, SeekOrigin.Begin); response.Body.CopyTo(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); using (var reader = new StreamReader(memoryStream)) { return reader.ReadToEnd(); } } } } 

2)在认证启动方法中使用UseOAuthBearerTokens之前的新中间件。

 app.Use(); app.UseOAuthBearerTokens(options);