From 5354a034b2563e6a050a205c3f07a93c36583ee3 Mon Sep 17 00:00:00 2001 From: Buck Perley Date: Mon, 21 Feb 2022 21:36:18 -0600 Subject: [PATCH] support base64 identifier in macaroon (#15) --- src/identifier.ts | 19 +++++++++++++++++- src/lsat.ts | 21 ++++---------------- tests/identifier.spec.ts | 9 +++++++++ tests/lsat.spec.ts | 42 ++++++++++++++++++++++++++++++---------- 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/identifier.ts b/src/identifier.ts index 693e82e..ba6f716 100644 --- a/src/identifier.ts +++ b/src/identifier.ts @@ -2,6 +2,7 @@ const assert = require('assert') const bufio = require('bufio') import crypto from 'crypto' import uuidv4 from 'uuid/v4' +import * as Macaroon from 'macaroon' import { IdentifierOptions } from './types' @@ -88,7 +89,11 @@ export class Identifier extends bufio.Struct { } static fromString(str: string): Identifier { - return new this().fromHex(str) + try { + return new this().fromHex(str) + } catch (e) { + return new this().fromBase64(str) + } } /** @@ -137,3 +142,15 @@ export class Identifier extends bufio.Struct { } } } + +export const decodeIdentifierFromMacaroon = (raw: string): string => { + const macaroon = Macaroon.importMacaroon(raw) + let identifier = macaroon._exportAsJSONObjectV2().i + if (identifier == undefined) { + identifier = macaroon._exportAsJSONObjectV2().i64 + if (identifier == undefined) { + throw new Error(`Problem parsing macaroon identifier`) + } + } + return identifier +} diff --git a/src/lsat.ts b/src/lsat.ts index 9a8f818..353578b 100644 --- a/src/lsat.ts +++ b/src/lsat.ts @@ -4,7 +4,7 @@ const bufio = require('bufio') import crypto from 'crypto' import * as Macaroon from 'macaroon' -import { Caveat, Identifier } from '.' +import { Caveat, decodeIdentifierFromMacaroon, Identifier } from '.' import { LsatOptions } from './types' import { isHex, getIdFromRequest, decode } from './helpers' @@ -306,14 +306,9 @@ export class Lsat extends bufio.Struct { */ static fromMacaroon(macaroon: string, invoice?: string): Lsat { assert(typeof macaroon === 'string', 'Requires a raw macaroon string for macaroon to generate LSAT') - const identifier = Macaroon.importMacaroon(macaroon)._exportAsJSONObjectV2().i - let id: Identifier + let id: Identifier, identifier: string try { - if (identifier == undefined) { - throw new Error( - `macaroon identifier undefined` - ) - } + identifier = decodeIdentifierFromMacaroon(macaroon) id = Identifier.fromString(identifier) } catch (e:any) { throw new Error( @@ -410,15 +405,7 @@ export class Lsat extends bufio.Struct { ) const paymentHash = getIdFromRequest(invoice) - let identifier - const mac = Macaroon.importMacaroon(macaroon) - identifier = mac._exportAsJSONObjectV2().i - if (identifier == undefined){ - identifier = mac._exportAsJSONObjectV2().i64 - if (identifier == undefined){ - throw new Error(`Problem parsing macaroon identifier`) - } - } + const identifier = decodeIdentifierFromMacaroon(macaroon) return new this({ id: identifier, diff --git a/tests/identifier.spec.ts b/tests/identifier.spec.ts index 2e87d92..33d31b7 100644 --- a/tests/identifier.spec.ts +++ b/tests/identifier.spec.ts @@ -6,7 +6,9 @@ import { LATEST_VERSION, TOKEN_ID_SIZE, ErrUnknownVersion, + decodeIdentifierFromMacaroon, } from '../src' +import { testChallenges } from './data' describe('Macaroon Identifier', () => { it('should properly serialize identifier of known version', () => { @@ -33,4 +35,11 @@ describe('Macaroon Identifier', () => { const encodeId = (): Identifier => new Identifier(options) expect(encodeId).to.throw(ErrUnknownVersion, options.version.toString()) }) + + it('can decode from different macaroon types', () => { + for (const { macaroon } of testChallenges) { + const id = decodeIdentifierFromMacaroon(macaroon) + Identifier.fromString(id) + } + }) }) diff --git a/tests/lsat.spec.ts b/tests/lsat.spec.ts index 0bdee2f..92b0c1d 100644 --- a/tests/lsat.spec.ts +++ b/tests/lsat.spec.ts @@ -1,8 +1,18 @@ import { expect } from 'chai' -import * as Macaroon from "macaroon"; -import { Caveat, Lsat, parseChallengePart } from '../src' -import { testChallengeParts, invoice, testChallenges, testChallengeErrors } from './data' +import * as Macaroon from 'macaroon' +import { + Caveat, + decodeIdentifierFromMacaroon, + Lsat, + parseChallengePart, +} from '../src' +import { + testChallengeParts, + invoice, + testChallenges, + testChallengeErrors, +} from './data' import { getTestBuilder } from './utilities' import { decode } from '../src/helpers' @@ -41,9 +51,15 @@ describe('LSAT Token', () => { }) it('should be able to decode lsat challenges', () => { - for (const {name, challenge, macaroon, paymentHash, expiration} of testChallenges) { + for (const { + name, + challenge, + macaroon, + paymentHash, + expiration, + } of testChallenges) { const fromChallenge = (): Lsat => Lsat.fromChallenge(challenge) - + expect(fromChallenge, `${name} should not have thrown`).to.not.throw() const lsat = fromChallenge() expect(lsat.baseMacaroon).to.equal( @@ -59,13 +75,12 @@ describe('LSAT Token', () => { expiration, `expiration from ${name} LSAT did not match` ) - else - expect(lsat.validUntil).to.equal(0) + else expect(lsat.validUntil).to.equal(0) } }) it('should be able to decode header challenges', () => { - for (const {name, challenge} of testChallenges) { + for (const { name, challenge } of testChallenges) { const header = `LSAT ${challenge}` const fromHeader = (): Lsat => Lsat.fromHeader(header) expect(fromHeader, `${name} should not have thrown`).to.not.throw() @@ -73,12 +88,12 @@ describe('LSAT Token', () => { }) it('should fail on incorrectly encoded challenges', () => { - for (const {name, challenge, error} of testChallengeErrors) { + for (const { name, challenge, error } of testChallengeErrors) { const fromChallenge = (): Lsat => Lsat.fromChallenge(challenge) expect(fromChallenge, `${name} should not have thrown`).to.throw(error) } }) - + it('should be able to check expiration to see if expired', () => { const lsat = Lsat.fromChallenge(challenge) expect(lsat.isExpired()).to.be.false @@ -228,4 +243,11 @@ describe('LSAT Token', () => { const addInvalidInv = (): void => lsat.addInvoice('12345') expect(addInvalidInv).to.throw() }) + + it('test macaroon versions', () => { + for (const { macaroon, name } of testChallenges) { + const test = () => Lsat.fromMacaroon(macaroon) + expect(test, `${name} should not have thrown`).to.not.throw() + } + }) })