I currently have a pair of OWIN-based services that each use OAuth authentication against the same set of users. I intend to isolate the authorisation server (i.e. The token endpoint) and somehow configure both of my services to accept this token. I assume this would involve some configuration of all my services to allow this token to be decrypted across all relevant services. Is this possible?
4 Answers
After talking with Brock Allen in the comments to the original post, I can't really guarantee this is a good/safe solution, but this is the code I ended up using. (Note: a version of this code is available as a nuget package.)
I created a IDataProtector implementation that uses AES:
internal class AesDataProtectorProvider : IDataProtector
{
// Fields
private byte[] key;
// Constructors
public AesDataProtectorProvider(string key)
{
using (var sha1 = new SHA256Managed())
{
this.key = sha1.ComputeHash(Encoding.UTF8.GetBytes(key));
}
}
// IDataProtector Methods
public byte[] Protect(byte[] data)
{
byte[] dataHash;
using (var sha = new SHA256Managed())
{
dataHash = sha.ComputeHash(data);
}
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = this.key;
aesAlg.GenerateIV();
using (var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV))
using (var msEncrypt = new MemoryStream())
{
msEncrypt.Write(aesAlg.IV, 0, 16);
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var bwEncrypt = new BinaryWriter(csEncrypt))
{
bwEncrypt.Write(dataHash);
bwEncrypt.Write(data.Length);
bwEncrypt.Write(data);
}
var protectedData = msEncrypt.ToArray();
return protectedData;
}
}
}
public byte[] Unprotect(byte[] protectedData)
{
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = this.key;
using (var msDecrypt = new MemoryStream(protectedData))
{
byte[] iv = new byte[16];
msDecrypt.Read(iv, 0, 16);
aesAlg.IV = iv;
using (var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV))
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
using (var brDecrypt = new BinaryReader(csDecrypt))
{
var signature = brDecrypt.ReadBytes(32);
var len = brDecrypt.ReadInt32();
var data = brDecrypt.ReadBytes(len);
byte[] dataHash;
using (var sha = new SHA256Managed())
{
dataHash = sha.ComputeHash(data);
}
if (!dataHash.SequenceEqual(signature))
throw new SecurityException("Signature does not match the computed hash");
return data;
}
}
}
}
}
And then used this in an ISecureDataFormat implementation like so:
public class SecureTokenFormatter : ISecureDataFormat<AuthenticationTicket>
{
// Fields
private TicketSerializer serializer;
private IDataProtector protector;
private ITextEncoder encoder;
// Constructors
public SecureTokenFormatter(string key)
{
this.serializer = new TicketSerializer();
this.protector = new AesDataProtectorProvider(key);
this.encoder = TextEncodings.Base64Url;
}
// ISecureDataFormat<AuthenticationTicket> Members
public string Protect(AuthenticationTicket ticket)
{
var ticketData = this.serializer.Serialize(ticket);
var protectedData = this.protector.Protect(ticketData);
var protectedString = this.encoder.Encode(protectedData);
return protectedString;
}
public AuthenticationTicket Unprotect(string text)
{
var protectedData = this.encoder.Decode(text);
var ticketData = this.protector.Unprotect(protectedData);
var ticket = this.serializer.Deserialize(ticketData);
return ticket;
}
}
The 'key' parameter on the constructor can then set to the same value on a number of services and they will all be able to decrypt ('unprotect') and use the ticket.
-
1Here you can find more 'ready' implementation of AesDataProtector: https://github.com/i4004/Owin.Security.AesDataProtectorProvider/blob/master/Owin.Security.AesDataProtectorProvider/AesDataProtector.cs – piotrwest Sep 14 '15 at 17:00
-
@piotrwest Could you help me understand what makes the linked-to implementation more 'ready'? I did a quick overview and the only difference I see is the ability to inject crypto factories. Is there anything else? In what cases would one want to inject those factories rather than just use the concrete classes coded in this answer? – pettys Mar 17 '16 at 19:28
-
1@pettys, I should've linked to https://github.com/i4004/Owin.Security.AesDataProtectorProvider which clearly shows my intent, instead of the class. By ready I didn't meant better implemented/modeled/designed but put as a NuGet package. I guess I was just too lazy to copy and paste your code... – piotrwest Mar 17 '16 at 23:15
-
@piotrwest Ah, that *is* better! :) Thanks for the explanation - upvoting your comment. – pettys Mar 18 '16 at 19:54
-
There seem to be problems with this implementation (echoed by Brock above). The default DPAPI protector, I believe, adds a MAC to the blob which is verified its 'Unprotect' method. This implementation does not. The data length probably shouldn't be written to the CryptoStream as this potentially provides known plain text. – Ananke Mar 21 '16 at 15:54
-
@Barguast Please let me know how to generate 'Key'? what should be the format? is there any specific class used to generate this 'Key' ? – Vikram Apr 19 '16 at 08:01
-
1@vikram - It just needs to be any secret value. It's a string here for convenience, but you can see in the constructor that I just convert it to bytes to feed into the algorithm as the key. – Barguast Apr 19 '16 at 08:25
The Katana OAuth2 Authorization Server middleware wasn't really designed for this scenario (mainly because its reliance upon the machinekey for token verification).
If you're looking to centralize the token generation then you should look into an OAuth2 authorization server that's designed for this. Thinktecture AuthorizationServer is an open source server that does this: http://thinktecture.github.io/Thinktecture.AuthorizationServer/

- 7,385
- 19
- 24
-
I actually had some luck with this after posting. There is an AccessTokenFormat property on both the server configuration and bearer token configuration. I implemented ISecureDataFormat
to serialize the ticket and encrypt with AES. Provided I use the same cipher key for all of my services, the token appears to work on each of them. Do you see any issues with this approach? If not, I'll write this up a proper answer. Thanks for your reply. – Barguast Feb 16 '14 at 23:25 -
The extensibility points are certainly there, so it's possible. It's just unfortunate that they leave this up to the general developer to fill these in. And this is why it's unfortunate -- you don't want encryption (per-se), but you want a signature to verify that it was issued from your server. Successful decryption doesn't provide trust. – Brock Allen Feb 18 '14 at 19:58
-
Thanks, though I'm struggling to see why my method could be harmful. It's just using AES over DPAPI, and there is an element of signature checking in there to ensure an invalid but decryptable token is still rejected. – Barguast Feb 18 '14 at 21:54
-
All I'm saying is that if your app trusts this token, then you need for it to be signed. Encryption doesn't protect you against someone changing the token and then resubmitting it to you. You need a way to ensure it's not been modified and that it originated from your issuer. I didn't really know what your code is doing, so I wasn't sure if was being properly validated. – Brock Allen Feb 19 '14 at 16:32
-
I know this is an old question, but I had a similar use case. According to the docs, OWIN OAuth uses the machine key to protect the data. Since you control all instances, I presume that simply setting the machinekey in the web config would work.

- 156
- 1
- 4
-
In this case I am self hosting in a Windows Service, and unless I'm mistaken you aren't able to set the machineKey in the app.config? – Barguast May 22 '14 at 07:17
-
Sorry I assumed you were using IIS. According to the link I posted, it's using DPAPI by default in that case, which is linked with the user account (and password). In that case I'd say implementing your own data protector might be your best bet, but make sure you use a well known algorithm. I don't know where you found yours, so I won't vouch for it's relative safety, but it appears to be a good start. – jbblanchet May 22 '14 at 11:38
-
Thanks, it's been in use for the past few months and it works wonderfully. – Barguast May 22 '14 at 11:40
You can use this nuget package https://www.nuget.org/packages/Owin.Security.AesDataProtectorProvider/
It contains extension method for IAppBuilder that allows you setup own key
appBuilder.UseAesDataProtectorProvider(key);

- 51
- 6