I'd definitely consider using something like Json.Net. This will keep all your serialised data nice and cross-platform. Also, since you mentioned you're using Asp.Net Core, BinaryFormatter
isn't available to you if you want to use the cross-platform .Net Standard libraries.
To take your example, you might do something similar to this:
public static string GenerateToken(Guid id, params object[] data)
{
var claims = new List<object>(data);
claims.Add(new
{
id = id
});
string serialised = Newtonsoft.Json.JsonConvert.SerializeObject(claims);
return serialised;
}
So you can call the method with something like:
GenerateToken(Guid.NewGuid(), "hello world!", 25, new { Test = "value" });
Giving you the following:
["hello world!",25,{"Test":"value"},{"id":"bf9e5d38-5ac4-4c6b-b68f-88136fc233cf"}]
You could just encrypt this string and pass it to your API, decrypt it and then deserialise it to an object:
public static object DeserialiseToken(string token)
{
object deserialised = Newtonsoft.Json.JsonConvert.DeserializeObject(token);
return deserialised;
}
That will return you an object with all your original data in.
Notice that because we're using params object[]
for our arguments, we can't create key-value pairs easily. We lose a variable's original name when we pass it into the method, and we haven't really got a good way of knowing what each entry in the data
array should be called. For example, accessing the 'hello world!' string could be tedious.
We might run into difficulties interpreting the data properly when we want to read it later on.
Improving It!
Having said all that, I think we can improve the approach a little bit.
The first thing I'd do is introduce a proper model for your claims. If you can guarantee your tokens will all have the same 'model' for the data they contain, you can have a class such as:
public class Token
{
public Guid Id { get; set; }
public int UserId { get; set; }
public bool Enable { get; set; }
}
And pass that directly into Json.Net (or use some other serialiser):
string output = GenerateToken(new Token
{
Id = Guid.NewGuid(),
Enable = false,
UserId = 2062
});
and
public static string GenerateToken(Token claims)
{
string serialised = Newtonsoft.Json.JsonConvert.SerializeObject(claims);
return serialised;
}
When you get back around to deserialising the json, you can map it straight to an object:
public static Token DeserialiseToken(string token)
{
Token deserialised = Newtonsoft.Json.JsonConvert.DeserializeObject<Token>(token);
return deserialised;
}
You'll have a strongly-typed object with all your claims mapped against them.
You should also think about whether actually need to encrypt your token. One popular approach is the JSON Web Token (JWT) standard, where the set of claims are plaintext, but sent along with a verification hash, where the claims are hashed together with a secret.
In a situation where a user modifies the claims, when the token reaches your API it will rehash the claims with the secret, and the signatures won't match, so you'll know it's been tampered with!
If you're not storing anything particularly sensitive in your token then this is a perfectly good approach.