diff --git a/source/Core/Configuration/Hosting/AutoFacConfig.cs b/source/Core/Configuration/Hosting/AutoFacConfig.cs index 6485a9b63..69f672627 100644 --- a/source/Core/Configuration/Hosting/AutoFacConfig.cs +++ b/source/Core/Configuration/Hosting/AutoFacConfig.cs @@ -67,6 +67,7 @@ public static IContainer Configure(IdentityServerOptions options) builder.RegisterDefaultType(fact.CustomRequestValidator); builder.RegisterDefaultType(fact.ExternalClaimsFilter); builder.RegisterDefaultType(fact.CustomTokenValidator); + builder.RegisterDefaultType(fact.CustomTokenResponseGenerator); builder.RegisterDefaultType(fact.ConsentService); builder.RegisterDefaultType(fact.AuthenticationSessionValidator); diff --git a/source/Core/Configuration/IdentityServerServiceFactory.cs b/source/Core/Configuration/IdentityServerServiceFactory.cs index c94a92a7b..90d8e9178 100644 --- a/source/Core/Configuration/IdentityServerServiceFactory.cs +++ b/source/Core/Configuration/IdentityServerServiceFactory.cs @@ -307,6 +307,13 @@ public void Register(Registration registration) /// public Registration CorsPolicyService { get; set; } + /// Gets or sets the custom token response generator + /// + /// + /// The custom token response generator + /// + public Registration CustomTokenResponseGenerator { get; set; } + /// /// Gets or sets the authentication session validator. /// diff --git a/source/Core/Core.csproj b/source/Core/Core.csproj index fc906217b..a9dd9ddb9 100644 --- a/source/Core/Core.csproj +++ b/source/Core/Core.csproj @@ -237,9 +237,11 @@ + + diff --git a/source/Core/Models/TokenResponse.cs b/source/Core/Models/TokenResponse.cs index 5041a1deb..09da04f7c 100644 --- a/source/Core/Models/TokenResponse.cs +++ b/source/Core/Models/TokenResponse.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using System.Collections.Generic; using System.ComponentModel; #pragma warning disable 1591 @@ -27,5 +28,12 @@ public class TokenResponse public string AccessToken { get; set; } public int AccessTokenLifetime { get; set; } public string RefreshToken { get; set; } + + public Dictionary Custom { get; set; } + + public TokenResponse() + { + Custom = new Dictionary(); + } } -} +} \ No newline at end of file diff --git a/source/Core/ResponseHandling/TokenResponseGenerator.cs b/source/Core/ResponseHandling/TokenResponseGenerator.cs index 9d2816a5d..418969eaf 100644 --- a/source/Core/ResponseHandling/TokenResponseGenerator.cs +++ b/source/Core/ResponseHandling/TokenResponseGenerator.cs @@ -36,29 +36,36 @@ public class TokenResponseGenerator private readonly ITokenService _tokenService; private readonly IRefreshTokenService _refreshTokenService; private readonly IScopeStore _scopes; - - public TokenResponseGenerator(ITokenService tokenService, IRefreshTokenService refreshTokenService, IScopeStore scopes) + private readonly ICustomTokenResponseGenerator _customResponseGenerator; + + public TokenResponseGenerator(ITokenService tokenService, IRefreshTokenService refreshTokenService, IScopeStore scopes, ICustomTokenResponseGenerator customResponseGenerator) { _tokenService = tokenService; _refreshTokenService = refreshTokenService; _scopes = scopes; + _customResponseGenerator = customResponseGenerator; } public async Task ProcessAsync(ValidatedTokenRequest request) { Logger.Info("Creating token response"); + TokenResponse response; + if (request.GrantType == Constants.GrantTypes.AuthorizationCode) { - return await ProcessAuthorizationCodeRequestAsync(request); + response = await ProcessAuthorizationCodeRequestAsync(request); } - - if (request.GrantType == Constants.GrantTypes.RefreshToken) + else if (request.GrantType == Constants.GrantTypes.RefreshToken) + { + response = await ProcessRefreshTokenRequestAsync(request); + } + else { - return await ProcessRefreshTokenRequestAsync(request); + response = await ProcessTokenRequestAsync(request); } - return await ProcessTokenRequestAsync(request); + return await _customResponseGenerator.GenerateAsync(request, response); } private async Task ProcessAuthorizationCodeRequestAsync(ValidatedTokenRequest request) diff --git a/source/Core/Results/TokenResult.cs b/source/Core/Results/TokenResult.cs index 42e40e5c6..b920ae0b5 100644 --- a/source/Core/Results/TokenResult.cs +++ b/source/Core/Results/TokenResult.cs @@ -17,9 +17,13 @@ using IdentityServer3.Core.Logging; using IdentityServer3.Core.Models; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; +using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Http; @@ -29,11 +33,12 @@ namespace IdentityServer3.Core.Results internal class TokenResult : IHttpActionResult { private readonly static ILog Logger = LogProvider.GetCurrentClassLogger(); - private readonly static JsonMediaTypeFormatter Formatter = new JsonMediaTypeFormatter + private readonly static JsonSerializer Serializer = new JsonSerializer { - SerializerSettings = { DefaultValueHandling = DefaultValueHandling.Ignore } + DefaultValueHandling = DefaultValueHandling.Ignore, + NullValueHandling = NullValueHandling.Ignore }; - + private readonly TokenResponse _response; public TokenResult(TokenResponse response) @@ -57,9 +62,27 @@ private HttpResponseMessage Execute() token_type = Constants.TokenTypes.Bearer }; + var jobject = JObject.FromObject(dto, Serializer); + + // custom entries + if (_response.Custom != null && _response.Custom.Any()) + { + foreach (var item in _response.Custom) + { + JToken token; + if (jobject.TryGetValue(item.Key, out token)) + { + throw new Exception("Item does already exist - cannot add it via a custom entry: " + item.Key); + } + + jobject.Add(new JProperty(item.Key, item.Value)); + } + } + var response = new HttpResponseMessage(HttpStatusCode.OK) { - Content = new ObjectContent(dto, Formatter) + //Content = new ObjectContent(jobject, new JsonMediaTypeFormatter()) + Content = new StringContent(jobject.ToString(), Encoding.UTF8, "application/json") }; Logger.Info("Returning token response."); diff --git a/source/Core/Services/Default/DefaultCustomTokenResponseGenerator.cs b/source/Core/Services/Default/DefaultCustomTokenResponseGenerator.cs new file mode 100644 index 000000000..a4672aac5 --- /dev/null +++ b/source/Core/Services/Default/DefaultCustomTokenResponseGenerator.cs @@ -0,0 +1,23 @@ +using IdentityServer3.Core.Models; +using IdentityServer3.Core.Validation; +using System.Threading.Tasks; + +namespace IdentityServer3.Core.Services.Default +{ + /// + /// nop custom token response generator + /// + public class DefaultCustomTokenResponseGenerator : ICustomTokenResponseGenerator + { + /// + /// Custom response generation + /// + /// The validated request. + /// The standard token response. + /// The custom token response. + public Task GenerateAsync(ValidatedTokenRequest request, TokenResponse response) + { + return Task.FromResult(response); + } + } +} \ No newline at end of file diff --git a/source/Core/Services/ICustomTokenResponseGenerator.cs b/source/Core/Services/ICustomTokenResponseGenerator.cs new file mode 100644 index 000000000..c748ad6f0 --- /dev/null +++ b/source/Core/Services/ICustomTokenResponseGenerator.cs @@ -0,0 +1,36 @@ +/* + * Copyright 2014, 2015 Dominick Baier, Brock Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using IdentityServer3.Core.Models; +using IdentityServer3.Core.Validation; +using System.Threading.Tasks; + +namespace IdentityServer3.Core.Services +{ + /// + /// Allows adding custom data to a token response + /// + public interface ICustomTokenResponseGenerator + { + /// + /// Custom response generation + /// + /// The validated request. + /// The standard token response. + /// The custom token response. + Task GenerateAsync(ValidatedTokenRequest request, TokenResponse response); + } +} \ No newline at end of file diff --git a/source/Host.Configuration/Config/AnotherCustomGrantValidator.cs b/source/Host.Configuration/Extensions/AnotherCustomGrantValidator.cs similarity index 97% rename from source/Host.Configuration/Config/AnotherCustomGrantValidator.cs rename to source/Host.Configuration/Extensions/AnotherCustomGrantValidator.cs index a9e8f7e06..a8c063e8b 100644 --- a/source/Host.Configuration/Config/AnotherCustomGrantValidator.cs +++ b/source/Host.Configuration/Extensions/AnotherCustomGrantValidator.cs @@ -18,7 +18,7 @@ using IdentityServer3.Core.Validation; using System.Threading.Tasks; -namespace IdentityServer3.Host.Config +namespace Host.Configuration.Extensions { public class AnotherCustomGrantValidator : ICustomGrantValidator { diff --git a/source/Host.Configuration/Config/CustomGrantValidator.cs b/source/Host.Configuration/Extensions/CustomGrantValidator.cs similarity index 97% rename from source/Host.Configuration/Config/CustomGrantValidator.cs rename to source/Host.Configuration/Extensions/CustomGrantValidator.cs index 207040f5f..eedb96a0a 100644 --- a/source/Host.Configuration/Config/CustomGrantValidator.cs +++ b/source/Host.Configuration/Extensions/CustomGrantValidator.cs @@ -18,7 +18,7 @@ using IdentityServer3.Core.Validation; using System.Threading.Tasks; -namespace IdentityServer3.Host.Config +namespace Host.Configuration.Extensions { public class CustomGrantValidator : ICustomGrantValidator { diff --git a/source/Host.Configuration/Extensions/CustomTokenResponseGenerator.cs b/source/Host.Configuration/Extensions/CustomTokenResponseGenerator.cs new file mode 100644 index 000000000..d141df4d1 --- /dev/null +++ b/source/Host.Configuration/Extensions/CustomTokenResponseGenerator.cs @@ -0,0 +1,17 @@ +using IdentityServer3.Core.Models; +using IdentityServer3.Core.Services; +using IdentityServer3.Core.Validation; +using System.Threading.Tasks; + +namespace Host.Configuration.Extensions +{ + class CustomTokenResponseGenerator : ICustomTokenResponseGenerator + { + public Task GenerateAsync(ValidatedTokenRequest request, TokenResponse response) + { + response.Custom.Add("custom_field", "custom data"); + + return Task.FromResult(response); + } + } +} \ No newline at end of file diff --git a/source/Host.Configuration/FactoryExtensions.cs b/source/Host.Configuration/FactoryExtensions.cs index 95fae2536..d7c80858c 100644 --- a/source/Host.Configuration/FactoryExtensions.cs +++ b/source/Host.Configuration/FactoryExtensions.cs @@ -1,16 +1,25 @@ -using IdentityServer3.Core.Services; -using IdentityServer3.Host.Config; +using Host.Configuration.Extensions; +using IdentityServer3.Core.Configuration; +using IdentityServer3.Core.Services; -namespace IdentityServer3.Core.Configuration +namespace Host.Configuration { static class FactoryExtensions { public static IdentityServerServiceFactory AddCustomGrantValidators(this IdentityServerServiceFactory factory) { factory.CustomGrantValidators.Add( - new Registration(typeof(CustomGrantValidator))); + new Registration()); factory.CustomGrantValidators.Add( - new Registration(typeof(AnotherCustomGrantValidator))); + new Registration()); + + return factory; + } + + public static IdentityServerServiceFactory AddCustomTokenResponseGenerator(this IdentityServerServiceFactory factory) + { + factory.CustomTokenResponseGenerator = + new Registration(); return factory; } diff --git a/source/Host.Configuration/Host.Configuration.csproj b/source/Host.Configuration/Host.Configuration.csproj index b2ba6f636..9d04afc88 100644 --- a/source/Host.Configuration/Host.Configuration.csproj +++ b/source/Host.Configuration/Host.Configuration.csproj @@ -88,12 +88,13 @@ - + - + + diff --git a/source/Host.Configuration/IdentityServerExtension.cs b/source/Host.Configuration/IdentityServerExtension.cs index ddc9b3c7b..c5c268360 100644 --- a/source/Host.Configuration/IdentityServerExtension.cs +++ b/source/Host.Configuration/IdentityServerExtension.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using Host.Configuration; using IdentityServer3.Core.Configuration; using IdentityServer3.Host.Config; using Microsoft.Owin; @@ -41,6 +42,7 @@ public static IAppBuilder UseIdentityServer(this IAppBuilder app) .UseInMemoryScopes(Scopes.Get()); factory.AddCustomGrantValidators(); + factory.AddCustomTokenResponseGenerator(); factory.ConfigureClientStoreCache(); factory.ConfigureScopeStoreCache();