AspNetCore.Identity.DocumentDb
AspNetCore.Identity.DocumentDb copied to clipboard
Claims cannot be deserialized
If I create a user without claims it can log in without any errors. If I add some claims to the user upon registration then the user cannot log in.
The error is thrown when the DocumentDbUserStore
FindByNameAsync
or FindByEmailAsync
actions try to read the user data.
The problem is that the Document DB SDK does not respect the JsonConver.DefaultSettings
. You wrote in a comment that this is a workaround but it is not the case.
// TODO: Until DocumentDB SDK exposes it's JSON.NET settings, we need to hijack the global settings to serialize claims
JsonConvert.DefaultSettings = () =>
{
return new JsonSerializerSettings()
{
Converters = new List<JsonConverter>() { new JsonClaimConverter(), new JsonClaimsPrincipalConverter(), new JsonClaimsIdentityConverter() }
};
};
I forked the repository and I get the following error message:
{Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type System.Security.Claims.Claim. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'claims[0].Type', line 1, position 503.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.Linq.JToken.ToObject(Type objectType, JsonSerializer jsonSerializer)
at Microsoft.Azure.Documents.QueryResult.Convert(Type type)
at Microsoft.Azure.Documents.QueryResult.AsType[T]()
at Microsoft.Azure.Documents.Client.FeedResponseBinder.Convert[T](FeedResponse`1 dynamicFeed)
at Microsoft.Azure.Documents.Linq.DocumentQuery`1.<GetEnumerator>d__31.MoveNext()
at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found)
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
at AspNetCore.Identity.DocumentDb.Stores.DocumentDbUserStore`2.FindByNameAsync(String normalizedUserName, CancellationToken cancellationToken)
I have the same problem. Is there any work around how to resolve that issue?
@vindemi I have not found any solutions for this issue. If I am not mistaken, Microsoft is working to support JsonConvert.DefaultSettings
but I do not have any idea when will they finish it.
Our solution was to not use Claims but Roles instead. The serialization of Roles is not a problem.
But soon we will move to Identity Server with SQL Server because this library is not maintained and it has this issue, also it does not support partitioning so I do not think that it is production ready.
I'm also using IdentityServer4 and have it running fine against the AspNetCore.Identity.DocumentDb . Partitioning is definitely an issue and I'll probably have to go with a Mongo implementation without it. I used this to fix the convert issue.
`public class JsonClaimConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(Claim)); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
IList<Claim> claims = new List<Claim>();
JToken jt = JObject.ReadFrom(reader);
for (int i = 0; i < jt.Count(); i++)
{
string type = jt.Values<string>("Type").ElementAtOrDefault(i);
string value = jt.Values<string>("Value").ElementAtOrDefault(i);
string valueType = jt.Values<string>("ValueType").ElementAtOrDefault(i);
string issuer = jt.Values<string>("Issuer").ElementAtOrDefault(i);
string originalIssuer = jt.Values<string>("OriginalIssuer").ElementAtOrDefault(i);
claims.Add(new Claim(type, value, valueType, issuer, originalIssuer));
}
return claims;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var claim = (Claim)value;
JObject jo = new JObject
{
{ "Type", claim.Type },
{ "Value", claim.Value },
{ "ValueType", claim.ValueType },
{ "Issuer", claim.Issuer },
{ "OriginalIssuer", claim.OriginalIssuer }
};
jo.WriteTo(writer);
}
}`
I also added this to the DocumentDBUserStore for the FindBy... issues.
IQueryable<TUser> IQueryableUserStore<TUser>.Users => documentClient.CreateDocumentQuery<TUser>(collectionUri) .Where(u => u.DocumentType == typeof(TUser).Name) .AsQueryable();