Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,65 @@
# js-macaroon
> Better than cookies! :cookie:

A JavaScript implementation of [macaroons](http://theory.stanford.edu/~ataly/Papers/macaroons.pdf) compatible with the [Go](http://github.com/go-macaroon/macaroon), [Python, and C ](https://github.com/rescrv/libmacaroons) implementations. Including functionality to interact with third party caveat dischargers implemented by the [Go macaroon bakery](http://github.com/go-macaroon-bakery/macaroon-bakery).
[Macaroons](http://theory.stanford.edu/~ataly/Papers/macaroons.pdf) are "Cookies with Contextual Caveats for Decentralized Authorization in the Cloud". Basically, that means bearer tokens that can be given limited scopes or expirations, and which can be limited further by users or 3rd party services themselves.

This JavaScript implementation is compatible with the [Go](http://github.com/go-macaroon/macaroon), [Python, and C ](https://github.com/rescrv/libmacaroons) implementations. It also includes functionality to interact with third party caveat dischargers implemented by the [Go macaroon bakery](http://github.com/go-macaroon-bakery/macaroon-bakery).

## Installation

`npm install --save macaroon`

## Usage

Try out the code in the [examples](./examples)!

```js
const crypto = require('crypto')
const Macaroon = require('..')

// The server creates a macaroon to give to their user
const rootKey = crypto.randomBytes(32)
const original = Macaroon.newMacaroon({
rootKey,
identifier: 'user12345',
location: 'https://some-website.example'
})
const givenToUser = original.exportBinary()


// The user can add additional caveats that will be enforced by the server
// (The server can support whatever type of specific caveats you want)
const userMacaroon = Macaroon.importMacaroon(givenToUser)
userMacaroon.addFirstPartyCaveat('time < ' + new Date(Date.now() + 1000).toISOString())
const givenToThirdParty = userMacaroon.exportBinary()



// The user can give out the more limited caveat to some 3rd party service
// and the server will enforce the specified conditions when the 3rd party goes to authenticate

// This is an example of how the server might verify the macaroon's caveats
function verifier (caveat) {
if (caveat.startsWith('time <')) {
const expiry = Date.parse(caveat.replace('time < ', ''))
if (expiry <= Date.now()) {
throw new Error('macaroon is expired')
}
} else {
// It's generally a good idea to reject macaroons with caveats you don't understand
throw new Error('unsupported caveat: ' + caveat)
}
}

const fromThirdParty = Macaroon.importMacaroon(givenToThirdParty)
fromThirdParty.verify(rootKey, verifier)

// In this example, the macaroon will expire after 1 second
setTimeout(function () {
try {
fromThirdParty.verify(rootKey, verifier)
} catch (err) {
console.log('...but if we wait for too long the server won\'t accept it anymore')
}
}, 1001)
```
57 changes: 57 additions & 0 deletions examples/first-party.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict'

const crypto = require('crypto')
const Macaroon = require('..')

// The server creates a macaroon to give to their user
const rootKey = crypto.randomBytes(32)
const original = Macaroon.newMacaroon({
rootKey,
identifier: 'user12345',
location: 'https://some-website.example'
})
const givenToUser = original.exportBinary()

console.log('original macaroon binary:', Buffer.from(original.exportBinary()).toString('base64'))
console.log('original macaroon json:', original.exportJSON())

// The user can add additional caveats that will be enforced by the server
// (The server can support whatever type of specific caveats you want)
const userMacaroon = Macaroon.importMacaroon(givenToUser)
userMacaroon.addFirstPartyCaveat('time < ' + new Date(Date.now() + 1000).toISOString())
const givenToThirdParty = userMacaroon.exportBinary()

console.log('caveated macaroon binary:', Buffer.from(original.exportBinary()).toString('base64'))
console.log('caveated macaroon json:', original.exportJSON())

// The user can give out the more limited caveat to some 3rd party service
// and the server will enforce the specified conditions when the 3rd party goes to authenticate

// This is an example of how the server might verify the macaroon's caveats
function verifier (caveat) {
if (caveat.startsWith('time <')) {
const expiry = Date.parse(caveat.replace('time < ', ''))
if (expiry <= Date.now()) {
throw new Error('macaroon is expired')
}
} else {
// It's generally a good idea to reject macaroons with caveats you don't understand
throw new Error('unsupported caveat: ' + caveat)
}
}

console.log('verifying macaroon')
const fromThirdParty = Macaroon.importMacaroon(givenToThirdParty)
fromThirdParty.verify(rootKey, verifier)
console.log('yay! the macaroon was valid')

// In this example, the macaroon will expire after 1 second
setTimeout(function () {
try {
fromThirdParty.verify(rootKey, verifier)
} catch (err) {
console.log('...but if we wait for too long the server won\'t accept it anymore')
}
}, 1001)


5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"url": "http://github.com/go-macaroon/js-macaroon/issues"
},
"contributors": [
"Jeff Pihach (https://fromanegg.com)"
"Jeff Pihach (https://fromanegg.com)",
"Evan Schwartz <[email protected]>"
],
"dependencies": {
"sjcl": "^1.0.6",
Expand Down Expand Up @@ -35,5 +36,5 @@
"lint": "eslint .",
"test": "node test/test.js"
},
"version": "3.0.0"
"version": "3.0.1"
}