Home

Awesome

biscuit-net

biscuit-net is an implementation of Biscuit in .NET/C#. It aims to be fully compatible with other existing implementations, so that tokens issued by, for example, the Rust version, could be validated by this library and vice versa.

Documentation and specifications

Biscuit introduction

Taken from biscuit-rust:

Biscuit is an authorization token for microservices architectures with the following properties:

Non goals:

Basic Usage

These usage samples and additinal ones, as well es required usings can be found in BiscuitBuilderTests.cs.

Create a biscuit

var rootKey = Ed25519.NewSigningKey();
var token = Biscuit.New(rootKey)
    .AuthorityBlock("""
        right("/a/file1.txt", "read");
        right("/a/file1.txt", "write");
        right("/a/file2.txt", "read");
        right("/a/file2.txt", "write");
    """)
    .EndBlock()
.Serialize();

// token is now a byte[], ready to be shared

Attenuate a biscuit

var attenuatedToken = Biscuit.Attenuate(token)
    .AddBlock()
        .Add("""check if resource("file4")""")
        .Add("""check if resource("file5")""")
    .EndBlock()
    .Serialize();
// attenuatedToken is now a byte[] attenuation of the original token, and ready to be shared

Verify a biscuit

var verificationKey = new Ed25519.VerificationKey(rootKey.Public);
if(!Biscuit.TryDeserialize(token, verificationKey, out var biscuit, out var formatErr))
{
    throw new Exception($"Couldn't deserialize/validate biscuit: {formatErr}");
}

if(!Parser.Authorizer("""resource("file5"); allow if true;""").TryAuthorize(biscuit, out err))
{
    throw new Exception($"Couldn't authorize biscuit: {err}");
}

Seal a biscuit

Sealing a biscuit means it can no longer be attenuated.

var rootKey = Ed25519.NewSigningKey();        
var token = Biscuit.New(rootKey)
    .AuthorityBlock()
        .Add("resource", "file4")
    .EndBlock()
    .Seal();


//this will throw an exception
Biscuit.Attenuate(token);

Third-party blocks

You can learn more about third-party blocks in the relevant section of the Biscuit specifiction, or in this blog Post.

Adding a third-party block

var rootKey = Ed25519.NewSigningKey();
var thirdPartyKey = Ed25519.NewSigningKey();

var verificationKey = new Ed25519.VerificationKey(rootKey.Public);        
        
var token1 = Biscuit.New(rootKey)
    .AuthorityBlock()
        .Add("resource", "file4")
    .EndBlock()
    .Serialize();

var token2 = Biscuit.Attenuate(token1)
    .AddThirdPartyBlock(request => 
        //the request would usually be send to a third-party over the wire
        //the third party processes the requests, builds a third-party block, signs
        //it, it sends it back.
        //for the sake of the example, everything here happens in-process
        Biscuit.NewThirdParty()
            .Add("""check if resource("file4")""")
            .Add("""check if resource("file5")""")
        .Sign(thirdPartyKey, request)
    )
    .Serialize();
// token2 is now a byte[], containing the third party block

Trusting a third-party block issuer

var rootKey = Ed25519.NewSigningKey();
var thirdPartyKey = Ed25519.NewSigningKey();

var token1 = Biscuit.New(rootKey)
    .AuthorityBlock()
        .Add("resource", "file4")                
    .EndBlock()
    .AddBlock()
        //this block trusts any blocks signed by the thirdPartyKey
        //even if these blocks have been appended only later-on 
        .Trusts(thirdPartyKey.Public)
        .Add("""check if resource("file5");""")
    .EndBlock()
    .AddBlock()                
        .AddCheck(Check.CheckKind.One)
            .AddRule("""resource("file5")""")
                //while the overall block only trusts authority and the authorizer
                //this rule also trusts blocks signed by the thirdPartyKey
                .Trusts(thirdPartyKey.Public)
            .EndRule()
        .EndCheck()
    .EndBlock()
    .Serialize();

Project Structure

The most interesting bits of this implementation are:

Test coverage

The implementation currently passes all the tests of the conformance test suite published as part of the Biscuit specification.

There are also additional unit-like tests, however those could be a more complete. Feel free to chip in.

NuGet Prereleases

NuGet preleases are currently available on Github Packages. To consume them, your project needs to configure an additional NuGet source like follows.

# create a new project-specif nuget.config, if you don't already have one
dotnet new nugetconfig  
# add the GitHub nuget source 
dotnet nuget add source https://nuget.pkg.github.com/dmunch/index.json   
# install biscuit_net.Parser, which pulls in the other packages as dependies and is required for the examples to work 
dotnet add package biscuit_net.Parser

A word of caution: hic sunt dracones

This code should NOT be used in production scenarios before further scrutinizing and reviewing happend. You have been warned.

License

Licensed under Apache License, Version 2.0.