Go

The Go version of Biscuit can be found on Github.

Install

In go.mod:

require(
    github.com/biscuit-auth/biscuit-go v1.0.0
)

Create a root key

func CreateKey() (ed25519.PublicKey, ed25519.PrivateKey) {
	rng := rand.Reader
	publicRoot, privateRoot, _ := ed25519.GenerateKey(rng)
	return publicRoot, privateRoot
}

Create a token

func CreateToken(root *ed25519.PrivateKey) (*biscuit.Biscuit, error) {
	builder := biscuit.NewBuilder(*root)

	fact, err := parser.FromStringFact(`user("1234")`)
	if err != nil {
		return nil, fmt.Errorf("failed to parse authority facts: %v", err)
	}
	err = builder.AddAuthorityFact(fact)
	if err != nil {
		return nil, fmt.Errorf("failed to add authority facts: %v", err)
	}

	check, err := parser.FromStringCheck(`check if operation("read")`)
	if err != nil {
		return nil, fmt.Errorf("failed to parse authority checks: %v", err)
	}
	err = builder.AddAuthorityCheck(check)
	if err != nil {
		return nil, fmt.Errorf("failed to add authority checks: %v", err)
	}

	token, err := builder.Build()
	if err != nil {
		return nil, fmt.Errorf("failed to build biscuit: %v", err)
	}

	return token, nil
}

Create an authorizer

func Authorize(token *biscuit.Biscuit, root *ed25519.PublicKey) error {
	authorizer, err := token.Authorizer(*root)
	if err != nil {
		return fmt.Errorf("failed to create authorizer: %v", err)
	}

	fact1, err := parser.FromStringFact(`resource("/a/file1.txt")`)
	if err != nil {
		return fmt.Errorf("failed to parse authorizer fact: %v", err)
	}
	authorizer.AddFact(fact1)

	fact2, err := parser.FromStringFact(`operation("read")`)
	if err != nil {
		return fmt.Errorf("failed to parse authorizer fact: %v", err)
	}
	authorizer.AddFact(fact2)

	policy, err := parser.FromStringPolicy(`allow if resource("/a/file1.txt")`)
	if err != nil {
		return fmt.Errorf("failed to parse authorizer policy: %v", err)
	}
	authorizer.AddPolicy(policy)

	return authorizer.Authorize()
}

Attenuate a token

func Attenuate(serializedToken []byte, root *ed25519.PublicKey) ([]byte, error) {
	token, err := biscuit.Unmarshal(serializedToken)
	if err != nil {
		return nil, fmt.Errorf("failed to deserialize biscuit: %v", err)
	}

	blockBuilder := token.CreateBlock()

	check, err := parser.FromStringCheck(`check if resource($file), operation($permission), ["read"].contains($permission)`)
	if err != nil {
		return nil, fmt.Errorf("failed to parse check: %v", err)
	}
	err = blockBuilder.AddCheck(check)
	if err != nil {
		return nil, fmt.Errorf("failed to add block check: %v", err)
	}

	rng := rand.Reader
	token2, err := token.Append(rng, blockBuilder.Build())
	if err != nil {
		return nil, fmt.Errorf("failed to append: %v", err)
	}

	return token2.Serialize()
}

Seal a token

rng := rand.Reader
return token.Seal(rng)

Reject revoked tokens

The Biscuit::RevocationIds method returns the list of revocation identifiers as byte arrays.

identifiers := token.RevocationIds();

Query data from the authorizer

The Authorizer::Query method takes a rule as argument and extract the data from generated facts as tuples.

func Query(authorizer biscuit.Authorizer) (biscuit.FactSet, error) {
	rule, err := parser.FromStringRule(`data($name, $id) <- user($name, $id`)
	if err != nil {
		return nil, fmt.Errorf("failed to parse check: %v", err)
	}

	return authorizer.Query(rule)
}