Skip to content

Commit

Permalink
Merge pull request #48 from DuendeSoftware/features/persisted_grant_d…
Browse files Browse the repository at this point in the history
…ata_container

Enable data protection on persisted grants
  • Loading branch information
brockallen authored Dec 21, 2020
2 parents 5cf5bf2 + de00fee commit 43eac65
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 40 deletions.
4 changes: 3 additions & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="All" />

<!--microsoft extensions -->
<PackageReference Update="System.Text.Json" Version="5.0.0" />
<PackageReference Update="Microsoft.Extensions.Caching.Memory" Version="$(ExtensionsVersion)" />
<PackageReference Update="Microsoft.Extensions.Http" Version="$(ExtensionsVersion)" />
<PackageReference Update="Microsoft.Extensions.Http.Polly" Version="$(ExtensionsVersion)" />
<PackageReference Update="Microsoft.Extensions.Logging" Version="$(ExtensionsVersion)" />
<PackageReference Update="Microsoft.Extensions.Logging.Console" Version="$(ExtensionsVersion)" />
<PackageReference Update="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(ExtensionsVersion)" />

<!--misc -->
<PackageReference Update="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Update="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="$(WilsonVersion)" />
Expand All @@ -46,6 +47,7 @@
<PackageReference Update="Serilog.AspNetCore" Version="3.4.0" />

<!--microsoft asp.net core -->
<PackageReference Update="Microsoft.AspNetCore.DataProtection.Abstractions" Version="$(FrameworkVersion)" />
<PackageReference Update="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="$(FrameworkVersion)" />
<PackageReference Update="Microsoft.AspNetCore.TestHost" Version="$(FrameworkVersion)" />
<PackageReference Update="Microsoft.AspNetCore.Identity" Version="$(FrameworkVersion)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public static IIdentityServerBuilder AddRequiredPlatformServices(this IIdentityS
builder.Services.AddOptions();
builder.Services.AddSingleton(
resolver => resolver.GetRequiredService<IOptions<IdentityServerOptions>>().Value);
builder.Services.AddSingleton(
resolver => resolver.GetRequiredService<IOptions<IdentityServerOptions>>().Value.PersistentGrants);
builder.Services.AddHttpClient();

return builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// See LICENSE in the project root for license information.


using Duende.IdentityServer.Stores.Serialization;

namespace Duende.IdentityServer.Configuration
{
/// <summary>
Expand Down Expand Up @@ -141,6 +143,11 @@ public class IdentityServerOptions
/// </summary>
public KeyManagementOptions KeyManagement { get; set; } = new KeyManagementOptions();

/// <summary>
/// Options for persisted grants.
/// </summary>
public PersistentGrantOptions PersistentGrants { get; set; } = new PersistentGrantOptions();

/// <summary>
/// Gets or sets the license key.
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion src/Storage/Duende.IdentityServer.Storage.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
Expand All @@ -10,6 +10,8 @@

<ItemGroup>
<PackageReference Include="IdentityModel" />
<PackageReference Include="System.Text.Json" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Abstractions" />
</ItemGroup>

</Project>
28 changes: 11 additions & 17 deletions src/Storage/Stores/Serialization/ClaimConverter.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,35 @@
// Copyright (c) Duende Software. All rights reserved.
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.


using Newtonsoft.Json;
using System;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Serialization;

#pragma warning disable 1591

namespace Duende.IdentityServer.Stores.Serialization
{
public class ClaimConverter : JsonConverter
public class ClaimConverter : JsonConverter<Claim>
{
public override bool CanConvert(Type objectType)
public override Claim Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return typeof(Claim) == objectType;
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var source = serializer.Deserialize<ClaimLite>(reader);
var source = JsonSerializer.Deserialize<ClaimLite>(ref reader, options);
var target = new Claim(source.Type, source.Value, source.ValueType);
return target;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, Claim value, JsonSerializerOptions options)
{
var source = (Claim)value;

var target = new ClaimLite
{
Type = source.Type,
Value = source.Value,
ValueType = source.ValueType
Type = value.Type,
Value = value.Value,
ValueType = value.ValueType
};

serializer.Serialize(writer, target);
JsonSerializer.Serialize(writer, target, options);
}
}
}
24 changes: 9 additions & 15 deletions src/Storage/Stores/Serialization/ClaimsPrincipalConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,21 @@


using IdentityModel;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Serialization;

#pragma warning disable 1591

namespace Duende.IdentityServer.Stores.Serialization
{
public class ClaimsPrincipalConverter : JsonConverter
public class ClaimsPrincipalConverter : JsonConverter<ClaimsPrincipal>
{
public override bool CanConvert(Type objectType)
public override ClaimsPrincipal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return typeof(ClaimsPrincipal) == objectType;
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var source = serializer.Deserialize<ClaimsPrincipalLite>(reader);
var source = JsonSerializer.Deserialize<ClaimsPrincipalLite>(ref reader, options);
if (source == null) return null;

var claims = source.Claims.Select(x => new Claim(x.Type, x.Value, x.ValueType));
Expand All @@ -30,16 +26,14 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
return target;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, ClaimsPrincipal value, JsonSerializerOptions options)
{
var source = (ClaimsPrincipal)value;

var target = new ClaimsPrincipalLite
{
AuthenticationType = source.Identity.AuthenticationType,
Claims = source.Claims.Select(x => new ClaimLite { Type = x.Type, Value = x.Value, ValueType = x.ValueType }).ToArray()
AuthenticationType = value.Identity.AuthenticationType,
Claims = value.Claims.Select(x => new ClaimLite { Type = x.Type, Value = x.Value, ValueType = x.ValueType }).ToArray()
};
serializer.Serialize(writer, target);
JsonSerializer.Serialize(writer, target, options);
}
}
}
88 changes: 82 additions & 6 deletions src/Storage/Stores/Serialization/PersistentGrantSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,59 @@
// See LICENSE in the project root for license information.


using Newtonsoft.Json;
using Microsoft.AspNetCore.DataProtection;
using System;
using System.Text.Json;

namespace Duende.IdentityServer.Stores.Serialization
{
/// <summary>
/// Options for how persisted grants are persisted.
/// </summary>
public class PersistentGrantOptions
{
/// <summary>
/// Data protect the persisted grants "data" column.
/// </summary>
public bool DataProtectData { get; set; } = true;
}

/// <summary>
/// JSON-based persisted grant serializer
/// </summary>
/// <seealso cref="IPersistentGrantSerializer" />
public class PersistentGrantSerializer : IPersistentGrantSerializer
{
private static readonly JsonSerializerSettings _settings;
private static readonly JsonSerializerOptions _settings;

private readonly PersistentGrantOptions _options;
private readonly IDataProtector _provider;

static PersistentGrantSerializer()
{
_settings = new JsonSerializerSettings
_settings = new JsonSerializerOptions
{
ContractResolver = new CustomContractResolver()
IgnoreReadOnlyFields = true,
IgnoreReadOnlyProperties = true,

};
_settings.Converters.Add(new ClaimConverter());
_settings.Converters.Add(new ClaimsPrincipalConverter());
}

/// <summary>
/// Ctor.
/// </summary>
/// <param name="options"></param>
/// <param name="dataProtectionProvider"></param>
public PersistentGrantSerializer(PersistentGrantOptions options = null, IDataProtectionProvider dataProtectionProvider = null)
{
_options = options;
_provider = dataProtectionProvider?.CreateProtector(nameof(PersistentGrantSerializer));
}

bool ShouldDataProtect => _options?.DataProtectData == true && _provider != null;

/// <summary>
/// Serializes the specified value.
/// </summary>
Expand All @@ -32,7 +63,21 @@ static PersistentGrantSerializer()
/// <returns></returns>
public string Serialize<T>(T value)
{
return JsonConvert.SerializeObject(value, _settings);
var payload = JsonSerializer.Serialize(value, _settings);

if (ShouldDataProtect)
{
payload = _provider.Protect(payload);
}

var data = new PersistentGrantDataContainer
{
PersistentGrantDataContainerVersion = 1,
DataProtected = ShouldDataProtect,
Payload = payload,
};

return JsonSerializer.Serialize(data, _settings);
}

/// <summary>
Expand All @@ -43,7 +88,38 @@ public string Serialize<T>(T value)
/// <returns></returns>
public T Deserialize<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json, _settings);
var container = JsonSerializer.Deserialize<PersistentGrantDataContainer>(json, _settings);

if (container.PersistentGrantDataContainerVersion == 0)
{
return JsonSerializer.Deserialize<T>(json, _settings);
}

if (container.PersistentGrantDataContainerVersion == 1)
{
var payload = container.Payload;

if (container.DataProtected)
{
if (_provider == null)
{
throw new Exception("No IDataProtectionProvider configured.");
}

payload = _provider.Unprotect(container.Payload);
}

return JsonSerializer.Deserialize<T>(payload, _settings);
}

throw new Exception($"Invalid version in persisted grant data: '{container.PersistentGrantDataContainerVersion}'.");
}
}

class PersistentGrantDataContainer
{
public int PersistentGrantDataContainerVersion { get; set; }
public bool DataProtected { get; set; }
public string Payload { get; set; }
}
}

0 comments on commit 43eac65

Please sign in to comment.