Using Oauth tickets across several services?

前端 未结 4 1140
执笔经年
执笔经年 2020-12-02 21:27

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

相关标签:
4条回答
  • 2020-12-02 21:56

    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/

    0 讨论(0)
  • 2020-12-02 21:56

    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.

    0 讨论(0)
  • 2020-12-02 21:56

    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.

    Ref: http://msdn.microsoft.com/en-us/library/microsoft.owin.security.oauth.oauthauthorizationserveroptions(v=vs.113).aspx

    0 讨论(0)
  • 2020-12-02 21:57

    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);
    
    0 讨论(0)
提交回复
热议问题