2019-10-23 07:44:55 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var A = module.exports;
|
|
|
|
var U = require('./utils.js');
|
|
|
|
|
|
|
|
var Keypairs = require('@root/keypairs');
|
|
|
|
var Enc = require('@root/encoding/bytes');
|
2019-10-28 08:26:27 +00:00
|
|
|
var agreers = {};
|
2019-10-23 07:44:55 +00:00
|
|
|
|
2020-07-28 21:53:50 +00:00
|
|
|
A._getAccountKid = function (me, options) {
|
2019-10-23 07:44:55 +00:00
|
|
|
// It's just fine if there's no account, we'll go get the key id we need via the existing key
|
2019-10-25 00:49:42 +00:00
|
|
|
var kid =
|
|
|
|
options.kid ||
|
2020-07-28 21:53:50 +00:00
|
|
|
(options.account && options.account.key && options.account.key.kid);
|
2019-10-23 07:44:55 +00:00
|
|
|
|
2019-10-25 00:49:42 +00:00
|
|
|
if (kid) {
|
|
|
|
return Promise.resolve(kid);
|
2019-10-23 07:44:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//return Promise.reject(new Error("must include KeyID"));
|
|
|
|
// This is an idempotent request. It'll return the same account for the same public key.
|
2020-07-28 21:53:50 +00:00
|
|
|
return A._registerAccount(me, options).then(function (account) {
|
2019-10-25 00:49:42 +00:00
|
|
|
return account.key.kid;
|
2019-10-23 07:44:55 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
// ACME RFC Section 7.3 Account Creation
|
|
|
|
/*
|
|
|
|
{
|
|
|
|
"protected": base64url({
|
|
|
|
"alg": "ES256",
|
|
|
|
"jwk": {...},
|
|
|
|
"nonce": "6S8IqOGY7eL2lsGoTZYifg",
|
|
|
|
"url": "https://example.com/acme/new-account"
|
|
|
|
}),
|
|
|
|
"payload": base64url({
|
|
|
|
"termsOfServiceAgreed": true,
|
|
|
|
"onlyReturnExisting": false,
|
|
|
|
"contact": [
|
|
|
|
"mailto:cert-admin@example.com",
|
|
|
|
"mailto:admin@example.com"
|
|
|
|
]
|
|
|
|
}),
|
|
|
|
"signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
|
|
|
|
}
|
|
|
|
*/
|
2020-07-28 21:53:50 +00:00
|
|
|
A._registerAccount = function (me, options) {
|
2019-10-23 07:44:55 +00:00
|
|
|
//#console.debug('[ACME.js] accounts.create');
|
|
|
|
|
2019-10-26 06:03:43 +00:00
|
|
|
function agree(agreed) {
|
2019-10-23 07:44:55 +00:00
|
|
|
var err;
|
2019-10-26 06:03:43 +00:00
|
|
|
if (!agreed) {
|
|
|
|
err = new Error("must agree to '" + me._tos + "'");
|
2019-10-23 07:44:55 +00:00
|
|
|
err.code = 'E_AGREE_TOS';
|
|
|
|
throw err;
|
|
|
|
}
|
2019-10-25 00:49:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2019-10-23 07:44:55 +00:00
|
|
|
|
2019-10-25 00:49:42 +00:00
|
|
|
function getAccount() {
|
2020-07-28 21:53:50 +00:00
|
|
|
return U._importKeypair(options.accountKey).then(function (pair) {
|
2019-10-23 07:44:55 +00:00
|
|
|
var contact;
|
|
|
|
if (options.contact) {
|
|
|
|
contact = options.contact.slice(0);
|
2019-10-25 00:49:42 +00:00
|
|
|
} else if (options.subscriberEmail) {
|
|
|
|
contact = ['mailto:' + options.subscriberEmail];
|
2019-10-23 07:44:55 +00:00
|
|
|
}
|
2019-10-25 00:49:42 +00:00
|
|
|
|
2019-10-23 07:44:55 +00:00
|
|
|
var accountRequest = {
|
2019-10-25 00:49:42 +00:00
|
|
|
termsOfServiceAgreed: true,
|
2019-10-23 07:44:55 +00:00
|
|
|
onlyReturnExisting: false,
|
|
|
|
contact: contact
|
|
|
|
};
|
2019-10-25 00:49:42 +00:00
|
|
|
|
|
|
|
var pub = pair.public;
|
2020-07-28 21:53:50 +00:00
|
|
|
return attachExtAcc(pub, accountRequest).then(function (accReq) {
|
2019-10-25 00:49:42 +00:00
|
|
|
var payload = JSON.stringify(accReq);
|
2019-10-23 07:44:55 +00:00
|
|
|
return U._jwsRequest(me, {
|
2019-10-25 00:49:42 +00:00
|
|
|
accountKey: options.accountKey,
|
2019-10-23 07:44:55 +00:00
|
|
|
url: me._directoryUrls.newAccount,
|
|
|
|
protected: { kid: false, jwk: pair.public },
|
|
|
|
payload: Enc.strToBuf(payload)
|
2020-07-28 21:53:50 +00:00
|
|
|
}).then(function (resp) {
|
2019-10-23 07:44:55 +00:00
|
|
|
var account = resp.body;
|
|
|
|
|
|
|
|
if (resp.statusCode < 200 || resp.statusCode >= 300) {
|
|
|
|
if ('string' !== typeof account) {
|
|
|
|
account = JSON.stringify(account);
|
|
|
|
}
|
|
|
|
throw new Error(
|
|
|
|
'account error: ' +
|
|
|
|
resp.statusCode +
|
|
|
|
' ' +
|
|
|
|
account +
|
|
|
|
'\n' +
|
|
|
|
payload
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-10-25 00:49:42 +00:00
|
|
|
// the account id url is the "kid"
|
|
|
|
var kid = resp.headers.location;
|
2019-10-23 07:44:55 +00:00
|
|
|
if (!account) {
|
|
|
|
account = { _emptyResponse: true };
|
|
|
|
}
|
|
|
|
if (!account.key) {
|
|
|
|
account.key = {};
|
|
|
|
}
|
2019-10-25 00:49:42 +00:00
|
|
|
account.key.kid = kid;
|
2019-10-23 07:44:55 +00:00
|
|
|
return account;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-10-25 00:49:42 +00:00
|
|
|
// for external accounts (probably useless, but spec'd)
|
|
|
|
function attachExtAcc(pubkey, accountRequest) {
|
|
|
|
if (!options.externalAccount) {
|
|
|
|
return Promise.resolve(accountRequest);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Keypairs.signJws({
|
|
|
|
// TODO is HMAC the standard, or is this arbitrary?
|
|
|
|
secret: options.externalAccount.secret,
|
|
|
|
protected: {
|
|
|
|
alg: options.externalAccount.alg || 'HS256',
|
|
|
|
kid: options.externalAccount.id,
|
|
|
|
url: me._directoryUrls.newAccount
|
|
|
|
},
|
|
|
|
payload: Enc.strToBuf(JSON.stringify(pubkey))
|
2020-07-28 21:53:50 +00:00
|
|
|
}).then(function (jws) {
|
2019-10-25 00:49:42 +00:00
|
|
|
accountRequest.externalAccountBinding = jws;
|
|
|
|
return accountRequest;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-10-23 07:44:55 +00:00
|
|
|
return Promise.resolve()
|
2020-07-28 21:53:50 +00:00
|
|
|
.then(function () {
|
2019-10-23 07:44:55 +00:00
|
|
|
//#console.debug('[ACME.js] agreeToTerms');
|
|
|
|
var agreeToTerms = options.agreeToTerms;
|
2019-10-26 06:03:43 +00:00
|
|
|
if (!agreeToTerms) {
|
2020-07-28 21:53:50 +00:00
|
|
|
agreeToTerms = function (terms) {
|
2019-10-28 08:26:27 +00:00
|
|
|
if (agreers[options.subscriberEmail]) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
agreers[options.subscriberEmail] = true;
|
|
|
|
console.info();
|
|
|
|
console.info(
|
|
|
|
'By using this software you (' +
|
|
|
|
options.subscriberEmail +
|
|
|
|
') are agreeing to the following:'
|
2019-10-26 06:03:43 +00:00
|
|
|
);
|
|
|
|
console.info(
|
|
|
|
'ACME Subscriber Agreement:',
|
|
|
|
terms.acmeSubscriberTermsUrl
|
|
|
|
);
|
|
|
|
console.info(
|
|
|
|
'Greenlock/ACME.js Terms of Use:',
|
2019-10-27 09:07:19 +00:00
|
|
|
terms.acmeJsTermsUrl
|
2019-10-26 06:03:43 +00:00
|
|
|
);
|
2019-10-28 08:26:27 +00:00
|
|
|
console.info();
|
2019-10-26 06:03:43 +00:00
|
|
|
return true;
|
|
|
|
};
|
|
|
|
} else if (true === agreeToTerms) {
|
2020-07-28 21:53:50 +00:00
|
|
|
agreeToTerms = function (terms) {
|
2019-10-26 06:03:43 +00:00
|
|
|
return terms && true;
|
2019-10-23 07:44:55 +00:00
|
|
|
};
|
|
|
|
}
|
2019-10-26 06:03:43 +00:00
|
|
|
return agreeToTerms({
|
2019-10-27 09:58:22 +00:00
|
|
|
acmeSubscriberTermsUrl: me._tos,
|
|
|
|
acmeJsTermsUrl: 'https://rootprojects.org/legal/#terms'
|
2019-10-26 06:03:43 +00:00
|
|
|
});
|
2019-10-23 07:44:55 +00:00
|
|
|
})
|
2019-10-25 00:49:42 +00:00
|
|
|
.then(agree)
|
|
|
|
.then(getAccount);
|
2019-10-23 07:44:55 +00:00
|
|
|
};
|