Skip to content
This repository was archived by the owner on Mar 8, 2024. It is now read-only.

Commit 6f3a651

Browse files
authored
Merge pull request #4 from payid-org/clear-addresses-after-signing
change the `sign` command to clear unsigned addresses by default
2 parents 1ab899e + 0635ed4 commit 6f3a651

File tree

4 files changed

+111
-14
lines changed

4 files changed

+111
-14
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,12 @@ Before you sign an PayID, you must either load the PayID using the `load` comman
142142

143143
Once a PayID has been initialized or loaded, you can sign it using an [identity key](#identity-keys). You must either generate a new key, or load an existing one. Once your PayID has been loaded or initialized, and your identity key has been generated or loaded,
144144
you can sign the PayID using `sign` command. The `sign` command signs each of your PayID address
145-
mappings using the loaded identity keys, and outputs the resulting PayID with a `verifiedAddress` field. Run the `save`
146-
command to save your PayID, with signed addresses, to file.
145+
mappings using the loaded identity keys, and outputs the resulting PayID with a `verifiedAddress` field.
146+
147+
By default, the sign command clears the unsigned `addresses` from the results. If you wish to
148+
retain unsigned addresses after signing, use `sign --keep-addresses` or `sign -k` instead.
149+
150+
Finally, run the `save` command to save your PayID, with signed addresses, to file.
147151

148152
### Inspect a Verified PayID
149153

src/commands/Command.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ abstract class Command {
3030
this.vorpal = vorpal
3131
}
3232

33-
public setup(): void {
33+
/**
34+
* Sets up and registers the vorpal command.
35+
*
36+
* @returns The registered command.
37+
*/
38+
public setup(): Vorpal.Command {
3439
// Register the concrete command to Vorpal.
3540
// Execute the concrete action inside a try/catch wrapper
36-
this.vorpal.command(this.command(), this.description()).action(
41+
return this.vorpal.command(this.command(), this.description()).action(
3742
async (args: Args): Promise<void> => {
3843
await this.action(args).catch((error) => {
3944
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- error has any type

src/commands/payid-sign.ts

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,35 @@ import {
33
signWithKeys,
44
IdentityKeySigningParams,
55
toKey,
6+
PaymentInformation,
67
} from '@payid-org/utils'
78
import { JWKECKey, JWKOctKey, JWKOKPKey, JWKRSAKey } from 'jose'
9+
import * as Vorpal from 'vorpal'
810

911
import Command from './Command'
1012

1113
/**
1214
* Signs the currently loaded PayID PaymentInformation using the loaded signings keys.
1315
*/
1416
export default class SignPayIdCommand extends Command {
15-
protected async action(): Promise<void> {
17+
/**
18+
* @override
19+
*/
20+
public setup(): Vorpal.Command {
21+
return super
22+
.setup()
23+
.option(
24+
'-k, --keep-addresses',
25+
'Keep the unverified addresses section after signing.',
26+
)
27+
}
28+
29+
/**
30+
* @override
31+
*/
32+
protected async action(args: Vorpal.Args): Promise<void> {
33+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Vorpal.options isn't typed
34+
const isKeepAddresses: boolean = args.options['keep-addresses'] ?? false
1635
const info = this.getPaymentInfo()
1736
const payId = info.payId
1837
if (!payId) {
@@ -28,15 +47,7 @@ export default class SignPayIdCommand extends Command {
2847
return
2948
}
3049

31-
const updatedAddresses = info.addresses.map((address) => {
32-
const jws = signWithKeys(payId, address, signingKeys)
33-
return convertToVerifiedAddress(jws)
34-
})
35-
const updated = {
36-
payId: info.payId,
37-
addresses: info.addresses,
38-
verifiedAddresses: updatedAddresses,
39-
}
50+
const updated = signPayId(info, signingKeys, isKeepAddresses)
4051

4152
this.localStorage.setPaymentInfo(updated)
4253
this.logPaymentInfo(updated)
@@ -70,6 +81,33 @@ export default class SignPayIdCommand extends Command {
7081
}
7182
}
7283

84+
/**
85+
* Signs all the addresses for the given payment information and returns
86+
* with verified address.
87+
*
88+
* @param info - The payment information to sign.
89+
* @param signingKeys - The keys to sign with.
90+
* @param isKeepAddresses - If true, the unverified addresses property will be retained instead of cleared.
91+
* @returns A copy of the PaymentInformation but with verified addresses.
92+
*/
93+
export function signPayId(
94+
info: PaymentInformation,
95+
signingKeys: IdentityKeySigningParams[],
96+
isKeepAddresses: boolean,
97+
): PaymentInformation {
98+
const payId = info.payId
99+
const updatedAddresses = info.addresses.map((address) => {
100+
const jws = signWithKeys(payId, address, signingKeys)
101+
return convertToVerifiedAddress(jws)
102+
})
103+
const updated = {
104+
payId: info.payId,
105+
addresses: isKeepAddresses ? info.addresses : [],
106+
verifiedAddresses: updatedAddresses,
107+
}
108+
return updated
109+
}
110+
73111
/**
74112
* Returns the default algorithm to use to sign with the given jwk.
75113
*

test/unit/signPayId.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'mocha'
2+
import {
3+
AddressDetailsType,
4+
IdentityKeySigningParams,
5+
PaymentInformation,
6+
} from '@payid-org/utils'
7+
import { assert } from 'chai'
8+
import { JWK } from 'jose'
9+
10+
import { signPayId } from '../../src/commands/payid-sign'
11+
12+
const info: PaymentInformation = {
13+
payId: 'boaty$mcboatface.com',
14+
addresses: [
15+
{
16+
paymentNetwork: 'boatcoin',
17+
environment: 'seanet',
18+
addressDetailsType: AddressDetailsType.CryptoAddress,
19+
addressDetails: {
20+
address: 'xyz12345',
21+
},
22+
},
23+
],
24+
verifiedAddresses: [],
25+
}
26+
27+
describe('when signPayId()', function (): void {
28+
let signingKey: IdentityKeySigningParams
29+
30+
beforeEach('create key', async function (): Promise<void> {
31+
const key = await JWK.generate('EC', 'P-256')
32+
signingKey = new IdentityKeySigningParams(key, 'ES256')
33+
})
34+
35+
it('called with keepAddresses=true, then addresses property is retained', async function (): Promise<
36+
void
37+
> {
38+
const result = signPayId(info, [signingKey], true)
39+
assert.equal(result.addresses, info.addresses)
40+
assert.lengthOf(result.verifiedAddresses, 1)
41+
})
42+
43+
it('called with keepAddresses=false, then addresses property is cleared', async function (): Promise<
44+
void
45+
> {
46+
const result = signPayId(info, [signingKey], false)
47+
assert.isEmpty(result.addresses)
48+
assert.lengthOf(result.verifiedAddresses, 1)
49+
})
50+
})

0 commit comments

Comments
 (0)