Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Oauth tickets across several services?

Tags:

oauth-2.0

owin

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?

like image 266
Barguast Avatar asked Feb 16 '14 00:02

Barguast


People also ask

Is it possible to use the authentication token for multiple request?

Yes, it is possible to reuse the authentication token for multiple requests. We can achieve it by creating a collection and adding all the requests having the same authentication token to that collection and then assigning the auth token to the same collection.

Why OAuth should not be used for authentication?

Let's start with the biggest reason why OAuth isn't authentication: access tokens are not intended for the client application. When an authorization server issues an access token, the intended audience is the protected resource. After all, this is what the token is providing access to.

Is OAuth overkill?

For server-to-server APIs - APIs designed to be used only by a small number of servers – OAuth is overkill.

What is difference between OAuth and OAuth 2?

OAuth 2.0 promises to simplify things in following ways: Once the token was generated, OAuth 1.0 required that the client send two security tokens on every API call, and use both to generate the signature. OAuth 2.0 has only one security token, and no signature is required.


2 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.

like image 84
Barguast Avatar answered Sep 21 '22 02:09

Barguast


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/

like image 44
Brock Allen Avatar answered Sep 21 '22 02:09

Brock Allen