@ -1,45 +1,54 @@
( function ( ) {
( function ( ) {
'use strict' ;
"use strict" ;
/*global URLSearchParams,Headers*/
/*global URLSearchParams,Headers*/
var PromiseA = window . Promise ;
var PromiseA = window . Promise ;
var VERSION = '2' ;
var VERSION = "2" ;
// ACME recommends ECDSA P-256, but RSA 2048 is still required by some old servers (like what replicated.io uses )
// ACME recommends ECDSA P-256, but RSA 2048 is still required by some old servers (like what replicated.io uses )
// ECDSA P-384, P-521, and RSA 3072, 4096 are NOT recommend standards (and not properly supported)
// ECDSA P-384, P-521, and RSA 3072, 4096 are NOT recommend standards (and not properly supported)
var BROWSER_SUPPORTS_RSA = false ;
var BROWSER_SUPPORTS_RSA = false ;
var ECDSA_OPTS = { kty : 'EC' , namedCurve : 'P-256' } ;
var ECDSA_OPTS = { kty : "EC" , namedCurve : "P-256" } ;
var RSA_OPTS = { kty : 'RSA' , modulusLength : 2048 } ;
var RSA_OPTS = { kty : "RSA" , modulusLength : 2048 } ;
var Promise = window . Promise ;
var Promise = window . Promise ;
var Keypairs = window . Keypairs ;
var Keypairs = window . Keypairs ;
var ACME = window . ACME ;
var ACME = window . ACME ;
var CSR = window . CSR ;
var CSR = window . CSR ;
var $qs = function ( s ) { return window . document . querySelector ( s ) ; } ;
var $qs = function ( s ) {
var $qsa = function ( s ) { return window . document . querySelectorAll ( s ) ; } ;
return window . document . querySelector ( s ) ;
} ;
var $qsa = function ( s ) {
return window . document . querySelectorAll ( s ) ;
} ;
var acme ;
var acme ;
var info = { } ;
var info = { } ;
var steps = { } ;
var steps = { } ;
var i = 1 ;
var i = 1 ;
var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory' ;
var apiUrl = "https://acme-{{env}}.api.letsencrypt.org/directory" ;
// fix previous browsers
// fix previous browsers
var isCurrent = ( localStorage . getItem ( 'version' ) === VERSION ) ;
var isCurrent = localStorage . getItem ( "version" ) === VERSION ;
if ( ! isCurrent ) {
if ( ! isCurrent ) {
localStorage . clear ( ) ;
localStorage . clear ( ) ;
localStorage . setItem ( 'version' , VERSION ) ;
localStorage . setItem ( "version" , VERSION ) ;
}
}
localStorage . setItem ( 'version' , VERSION ) ;
localStorage . setItem ( "version" , VERSION ) ;
function updateApiType ( ) {
function updateApiType ( ) {
/*jshint validthis: true */
/*jshint validthis: true */
var input = this || Array . prototype . filter . call (
var input =
$qsa ( '.js-acme-api-type' ) , function ( $el ) { return $el . checked ; }
this ||
) [ 0 ] ;
Array . prototype . filter . call ( $qsa ( ".js-acme-api-type" ) , function ( $el ) {
return $el . checked ;
} ) [ 0 ] ;
//#console.log('ACME api type radio:', input.value);
//#console.log('ACME api type radio:', input.value);
$qs ( '.js-acme-directory-url' ) . value = apiUrl . replace ( /{{env}}/g , input . value ) ;
$qs ( ".js-acme-directory-url" ) . value = apiUrl . replace (
/{{env}}/g ,
input . value
) ;
}
}
function hideForms ( ) {
function hideForms ( ) {
$qsa ( '.js-acme-form' ) . forEach ( function ( el ) {
$qsa ( ".js-acme-form" ) . forEach ( function ( el ) {
el . hidden = true ;
el . hidden = true ;
} ) ;
} ) ;
}
}
@ -66,7 +75,10 @@
setTimeout ( function ( ) {
setTimeout ( function ( ) {
window . alert ( str ) ;
window . alert ( str ) ;
if ( window . confirm ( "Start over?" ) ) {
if ( window . confirm ( "Start over?" ) ) {
document . location . href = document . location . href . replace ( /\/app.*/ , '/' ) ;
document . location . href = document . location . href . replace (
/\/app.*/ ,
"/"
) ;
}
}
} , 10 ) ;
} , 10 ) ;
} ) ;
} ) ;
@ -76,48 +88,66 @@
var j = i ;
var j = i ;
i += 1 ;
i += 1 ;
return PromiseA . resolve ( ) . then ( function ( ) {
return PromiseA . resolve ( )
. then ( function ( ) {
return steps [ j ] . submit ( ev ) ;
return steps [ j ] . submit ( ev ) ;
} ) . catch ( function ( err ) {
} )
. catch ( function ( err ) {
var ourfault = true ;
var ourfault = true ;
console . error ( err ) ;
console . error ( err ) ;
if ( /failed to fetch/i . test ( err . message ) ) {
if ( /failed to fetch/i . test ( err . message ) ) {
return newAlert ( "Network connection failure." ) ;
return newAlert ( "Network connection failure." ) ;
}
}
if ( 'E_ACME_CHALLENGE' === err . code ) {
if ( "E_ACME_CHALLENGE" === err . code ) {
if ( 'dns-01' === err . type ) {
if ( "dns-01" === err . type ) {
ourfault = false ;
ourfault = false ;
return newAlert ( "It looks like the DNS record you set for "
return newAlert (
+ err . altname + " was incorrect or did not propagate. "
"It looks like the DNS record you set for " +
+ "The error message was '" + err . message + "'" ) ;
err . altname +
} else if ( 'http-01' === err . type ) {
" was incorrect or did not propagate. " +
"The error message was '" +
err . message +
"'"
) ;
} else if ( "http-01" === err . type ) {
ourfault = false ;
ourfault = false ;
return newAlert ( "It looks like the file you uploaded for "
return newAlert (
+ err . altname + " was incorrect or could not be downloaded. "
"It looks like the file you uploaded for " +
+ "The error message was '" + err . message + "'" ) ;
err . altname +
" was incorrect or could not be downloaded. " +
"The error message was '" +
err . message +
"'"
) ;
}
}
}
}
if ( ourfault ) {
if ( ourfault ) {
err . auth = undefined ;
err . auth = undefined ;
window . alert ( "Something went wrong. It's probably our fault, not yours."
window . alert (
+ " Please email aj@rootprojects.org to let him know. The error message is: \n"
"Something went wrong. It's probably our fault, not yours." +
+ JSON . stringify ( err , null , 2 ) ) ;
" Please email aj@rootprojects.org to let him know. The error message is: \n" +
JSON . stringify ( err , null , 2 )
) ;
return new Promise ( function ( ) { } ) ;
return new Promise ( function ( ) { } ) ;
}
}
} ) ;
} ) ;
}
}
function testKeypairSupport ( ) {
function testKeypairSupport ( ) {
return Keypairs . generate ( RSA_OPTS ) . then ( function ( ) {
return Keypairs . generate ( RSA_OPTS )
. then ( function ( ) {
console . info ( "[crypto] RSA is supported" ) ;
console . info ( "[crypto] RSA is supported" ) ;
BROWSER_SUPPORTS_RSA = true ;
BROWSER_SUPPORTS_RSA = true ;
} ) . catch ( function ( ) {
} )
. catch ( function ( ) {
console . warn ( "[crypto] RSA is NOT supported" ) ;
console . warn ( "[crypto] RSA is NOT supported" ) ;
return Keypairs . generate ( ECDSA_OPTS ) . then ( function ( ) {
return Keypairs . generate ( ECDSA_OPTS )
console . info ( '[crypto] ECDSA is supported' ) ;
. then ( function ( ) {
} ) . catch ( function ( e ) {
console . info ( "[crypto] ECDSA is supported" ) ;
} )
. catch ( function ( e ) {
console . warn ( "[crypto] EC is NOT supported" ) ;
console . warn ( "[crypto] EC is NOT supported" ) ;
throw e ;
throw e ;
} ) ;
} ) ;
@ -125,8 +155,15 @@
}
}
function getServerKeypair ( ) {
function getServerKeypair ( ) {
var sortedAltnames = info . identifiers . map ( function ( ident ) { return ident . value ; } ) . sort ( ) . join ( ',' ) ;
var sortedAltnames = info . identifiers
var serverJwk = JSON . parse ( localStorage . getItem ( 'server:' + sortedAltnames ) || 'null' ) ;
. map ( function ( ident ) {
return ident . value ;
} )
. sort ( )
. join ( "," ) ;
var serverJwk = JSON . parse (
localStorage . getItem ( "server:" + sortedAltnames ) || "null"
) ;
if ( serverJwk ) {
if ( serverJwk ) {
return PromiseA . resolve ( serverJwk ) ;
return PromiseA . resolve ( serverJwk ) ;
}
}
@ -139,62 +176,85 @@
keypairOpts = ECDSA_OPTS ;
keypairOpts = ECDSA_OPTS ;
}
}
return Keypairs . generate ( RSA_OPTS ) . catch ( function ( err ) {
return Keypairs . generate ( RSA_OPTS )
console . error ( "[Error] Keypairs.generate(" + JSON . stringify ( RSA_OPTS ) + "):" ) ;
. catch ( function ( err ) {
console . error (
"[Error] Keypairs.generate(" + JSON . stringify ( RSA_OPTS ) + "):"
) ;
throw err ;
throw err ;
} ) . then ( function ( pair ) {
} )
localStorage . setItem ( 'server:' + sortedAltnames , JSON . stringify ( pair . private ) ) ;
. then ( function ( pair ) {
localStorage . setItem (
"server:" + sortedAltnames ,
JSON . stringify ( pair . private )
) ;
return pair . private ;
return pair . private ;
} ) ;
} ) ;
}
}
function getAccountKeypair ( email ) {
function getAccountKeypair ( email ) {
var json = localStorage . getItem ( 'account:' + email ) ;
var json = localStorage . getItem ( "account:" + email ) ;
if ( json ) {
if ( json ) {
return Promise . resolve ( JSON . parse ( json ) ) ;
return Promise . resolve ( JSON . parse ( json ) ) ;
}
}
return Keypairs . generate ( ECDSA_OPTS ) . catch ( function ( err ) {
return Keypairs . generate ( ECDSA_OPTS )
console . warn ( "[Error] Keypairs.generate(" + JSON . stringify ( ECDSA_OPTS ) + "):\n" , err ) ;
. catch ( function ( err ) {
console . warn (
"[Error] Keypairs.generate(" + JSON . stringify ( ECDSA_OPTS ) + "):\n" ,
err
) ;
return Keypairs . generate ( RSA_OPTS ) . catch ( function ( err ) {
return Keypairs . generate ( RSA_OPTS ) . catch ( function ( err ) {
console . error ( "[Error] Keypairs.generate(" + JSON . stringify ( RSA_OPTS ) + "):" ) ;
console . error (
"[Error] Keypairs.generate(" + JSON . stringify ( RSA_OPTS ) + "):"
) ;
throw err ;
throw err ;
} ) ;
} ) ;
} ) . then ( function ( pair ) {
} )
localStorage . setItem ( 'account:' + email , JSON . stringify ( pair . private ) ) ;
. then ( function ( pair ) {
localStorage . setItem ( "account:" + email , JSON . stringify ( pair . private ) ) ;
return pair . private ;
return pair . private ;
} ) ;
} ) ;
}
}
function updateChallengeType ( ) {
function updateChallengeType ( ) {
/*jshint validthis: true*/
/*jshint validthis: true*/
var input = this || Array . prototype . filter . call (
var input =
$qsa ( '.js-acme-challenge-type' ) , function ( $el ) { return $el . checked ; }
this ||
) [ 0 ] ;
Array . prototype . filter . call ( $qsa ( ".js-acme-challenge-type" ) , function (
$qs ( '.js-acme-verification-wildcard' ) . hidden = true ;
$el
$qs ( '.js-acme-verification-http-01' ) . hidden = true ;
) {
$qs ( '.js-acme-verification-dns-01' ) . hidden = true ;
return $el . checked ;
} ) [ 0 ] ;
$qs ( ".js-acme-verification-wildcard" ) . hidden = true ;
$qs ( ".js-acme-verification-http-01" ) . hidden = true ;
$qs ( ".js-acme-verification-dns-01" ) . hidden = true ;
if ( info . challenges . wildcard ) {
if ( info . challenges . wildcard ) {
$qs ( '.js-acme-verification-wildcard' ) . hidden = false ;
$qs ( ".js-acme-verification-wildcard" ) . hidden = false ;
}
}
if ( info . challenges [ input . value ] ) {
if ( info . challenges [ input . value ] ) {
$qs ( '.js-acme-verification-' + input . value ) . hidden = false ;
$qs ( ".js-acme-verification-" + input . value ) . hidden = false ;
}
}
}
}
function saveContact ( email , domains ) {
function saveContact ( email , domains ) {
// to be used for good, not evil
// to be used for good, not evil
return window . fetch ( 'https://api.rootprojects.org/api/rootprojects.org/public/community' , {
return window
method : 'POST'
. fetch (
, cors : true
"https://api.rootprojects.org/api/rootprojects.org/public/community" ,
, headers : new Headers ( { 'Content-Type' : 'application/json' } )
{
, body : JSON . stringify ( {
method : "POST" ,
address : email
cors : true ,
, project : 'greenlock-domains@rootprojects.org'
headers : new Headers ( { "Content-Type" : "application/json" } ) ,
, timezone : new Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone
body : JSON . stringify ( {
, domain : domains . join ( ',' )
address : email ,
project : "greenlock-domains@rootprojects.org" ,
timezone : new Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone ,
domain : domains . join ( "," )
} )
} )
} ) . catch ( function ( err ) {
}
)
. catch ( function ( err ) {
console . error ( err ) ;
console . error ( err ) ;
} ) ;
} ) ;
}
}
@ -203,27 +263,39 @@
console . info ( "\n1. Show domains form" ) ;
console . info ( "\n1. Show domains form" ) ;
updateProgress ( 0 ) ;
updateProgress ( 0 ) ;
hideForms ( ) ;
hideForms ( ) ;
$qs ( '.js-acme-form-domains' ) . hidden = false ;
$qs ( ".js-acme-form-domains" ) . hidden = false ;
} ;
} ;
steps [ 1 ] . submit = function ( ) {
steps [ 1 ] . submit = function ( ) {
console . info ( "[submit] 1. Process domains, create ACME client" , info . domains ) ;
console . info (
info . domains = $qs ( '.js-acme-domains' ) . value
"[submit] 1. Process domains, create ACME client" ,
. replace ( /https?:\/\//g , ' ' ) . replace ( /[,+]/g , ' ' ) . trim ( ) . split ( /\s+/g ) ;
info . domains
console . info ( "[domains]" , info . domains . join ( ' ' ) ) ;
) ;
info . domains = $qs ( ".js-acme-domains" )
. value . replace ( /https?:\/\//g , " " )
. replace ( /[,+]/g , " " )
. trim ( )
. split ( /\s+/g ) ;
console . info ( "[domains]" , info . domains . join ( " " ) ) ;
info . identifiers = info . domains . map ( function ( hostname ) {
info . identifiers = info . domains . map ( function ( hostname ) {
return { type : 'dns' , value : hostname . toLowerCase ( ) . trim ( ) } ;
return { type : "dns" , value : hostname . toLowerCase ( ) . trim ( ) } ;
} ) ;
} ) ;
info . identifiers . sort ( function ( a , b ) {
info . identifiers . sort ( function ( a , b ) {
if ( a === b ) { return 0 ; }
if ( a === b ) {
if ( a < b ) { return 1 ; }
return 0 ;
if ( a > b ) { return - 1 ; }
}
if ( a < b ) {
return 1 ;
}
if ( a > b ) {
return - 1 ;
}
} ) ;
} ) ;
var acmeDirectoryUrl = $qs ( '.js-acme-directory-url' ) . value ;
var acmeDirectoryUrl = $qs ( ".js-acme-directory-url" ) . value ;
acme = ACME . create ( { Keypairs : Keypairs , CSR : CSR } ) ;
acme = ACME . create ( { Keypairs : Keypairs , CSR : CSR } ) ;
return acme . init ( acmeDirectoryUrl ) . then ( function ( directory ) {
return acme . init ( acmeDirectoryUrl ) . then ( function ( directory ) {
$qs ( '.js-acme-tos-url' ) . href = directory . meta . termsOfService ;
$qs ( ".js-acme-tos-url" ) . href = directory . meta . termsOfService ;
return steps [ i ] ( ) ;
return steps [ i ] ( ) ;
} ) ;
} ) ;
} ;
} ;
@ -233,68 +305,77 @@
updateProgress ( 0 ) ;
updateProgress ( 0 ) ;
hideForms ( ) ;
hideForms ( ) ;
$qs ( '.js-acme-form-account' ) . hidden = false ;
$qs ( ".js-acme-form-account" ) . hidden = false ;
} ;
} ;
steps [ 2 ] . submit = function ( ) {
steps [ 2 ] . submit = function ( ) {
console . info ( "[submit] 2. Create ACME account (get Key ID)" ) ;
console . info ( "[submit] 2. Create ACME account (get Key ID)" ) ;
var email = $qs ( '.js-acme-account-email' ) . value . toLowerCase ( ) . trim ( ) ;
var email = $qs ( ".js-acme-account-email" )
. value . toLowerCase ( )
. trim ( ) ;
info . email = email ;
info . email = email ;
info . contact = [ 'mailto:' + email ] ;
info . contact = [ "mailto:" + email ] ;
info . agree = $qs ( '.js-acme-account-tos' ) . checked ;
info . agree = $qs ( ".js-acme-account-tos" ) . checked ;
//info.greenlockAgree = $qs('.js-gl-tos').checked;
//info.greenlockAgree = $qs('.js-gl-tos').checked;
// TODO ping with version and account creation
// TODO ping with version and account creation
setTimeout ( saveContact , 100 , email , info . domains ) ;
setTimeout ( saveContact , 100 , email , info . domains ) ;
$qs ( '.js-account-next' ) . disabled = true ;
$qs ( ".js-account-next" ) . disabled = true ;
return info . cryptoCheck . then ( function ( ) {
return info . cryptoCheck
. then ( function ( ) {
return getAccountKeypair ( email ) . then ( function ( jwk ) {
return getAccountKeypair ( email ) . then ( function ( jwk ) {
// TODO save account id rather than always retrieving it?
// TODO save account id rather than always retrieving it?
console . info ( "[accounts] upsert for" , email ) ;
console . info ( "[accounts] upsert for" , email ) ;
return acme . accounts . create ( {
return acme . accounts
email : email
. create ( {
, agreeToTerms : info . agree && true
email : email ,
, accountKeypair : { privateKeyJwk : jwk }
agreeToTerms : info . agree && true ,
} ) . then ( function ( account ) {
accountKeypair : { privateKeyJwk : jwk }
} )
. then ( function ( account ) {
console . info ( "[accounts] result:" , account ) ;
console . info ( "[accounts] result:" , account ) ;
info . account = account ;
info . account = account ;
info . privateJwk = jwk ;
info . privateJwk = jwk ;
info . email = email ;
info . email = email ;
} ) . catch ( function ( err ) {
} )
. catch ( function ( err ) {
console . error ( "[accounts] failed to upsert account:" ) ;
console . error ( "[accounts] failed to upsert account:" ) ;
console . error ( err ) ;
console . error ( err ) ;
return newAlert ( err . message || JSON . stringify ( err , null , 2 ) ) ;
return newAlert ( err . message || JSON . stringify ( err , null , 2 ) ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) . then ( function ( ) {
} )
. then ( function ( ) {
var jwk = info . privateJwk ;
var jwk = info . privateJwk ;
var account = info . account ;
var account = info . account ;
console . info ( "[orders] requesting" ) ;
console . info ( "[orders] requesting" ) ;
return acme . orders . request ( {
return acme . orders
account : account
. request ( {
, accountKeypair : { privateKeyJwk : jwk }
account : account ,
, domains : info . domains
accountKeypair : { privateKeyJwk : jwk } ,
} ) . then ( function ( order ) {
domains : info . domains
} )
. then ( function ( order ) {
info . order = order ;
info . order = order ;
console . info ( "[orders] created " , order ) ;
console . info ( "[orders] created " , order ) ;
var claims = order . claims ;
var claims = order . claims ;
var obj = { 'dns-01' : [ ] , 'http-01' : [ ] , 'wildcard' : [ ] } ;
var obj = { "dns-01" : [ ] , "http-01" : [ ] , wildcard : [ ] } ;
info . challenges = obj ;
info . challenges = obj ;
var $httpList = $qs ( '.js-acme-http' ) ;
var $httpList = $qs ( ".js-acme-http" ) ;
var $dnsList = $qs ( '.js-acme-dns' ) ;
var $dnsList = $qs ( ".js-acme-dns" ) ;
var $wildList = $qs ( '.js-acme-wildcard' ) ;
var $wildList = $qs ( ".js-acme-wildcard" ) ;
var httpTpl = $httpList . innerHTML ;
var httpTpl = $httpList . innerHTML ;
var dnsTpl = $dnsList . innerHTML ;
var dnsTpl = $dnsList . innerHTML ;
var wildTpl = $wildList . innerHTML ;
var wildTpl = $wildList . innerHTML ;
$httpList . innerHTML = '' ;
$httpList . innerHTML = "" ;
$dnsList . innerHTML = '' ;
$dnsList . innerHTML = "" ;
$wildList . innerHTML = '' ;
$wildList . innerHTML = "" ;
claims . forEach ( function ( claim ) {
claims . forEach ( function ( claim ) {
//#console.log("claims[i]", claim);
//#console.log("claims[i]", claim);
@ -302,15 +383,15 @@
claim . challenges . forEach ( function ( c ) {
claim . challenges . forEach ( function ( c ) {
var auth = c ;
var auth = c ;
var data = {
var data = {
type : c . type
type : c . type ,
, hostname : hostname
hostname : hostname ,
, url : c . url
url : c . url ,
, token : c . token
token : c . token ,
, httpPath : auth . challengeUrl
httpPath : auth . challengeUrl ,
, httpAuth : auth . keyAuthorization
httpAuth : auth . keyAuthorization ,
, dnsType : 'TXT'
dnsType : "TXT" ,
, dnsHost : auth . dnsHost
dnsHost : auth . dnsHost ,
, dnsAnswer : auth . keyAuthorizationDigest
dnsAnswer : auth . keyAuthorizationDigest
} ;
} ;
//#console.log("claims[i].challenge", data);
//#console.log("claims[i].challenge", data);
@ -318,26 +399,36 @@
if ( claim . wildcard ) {
if ( claim . wildcard ) {
obj . wildcard . push ( data ) ;
obj . wildcard . push ( data ) ;
$tpl . innerHTML = wildTpl ;
$tpl . innerHTML = wildTpl ;
$tpl . querySelector ( ".js-acme-ver-txt-host" ) . innerHTML = data . dnsHost ;
$tpl . querySelector ( ".js-acme-ver-txt-host" ) . innerHTML =
$tpl . querySelector ( ".js-acme-ver-txt-value" ) . innerHTML = data . dnsAnswer ;
data . dnsHost ;
$tpl . querySelector ( ".js-acme-ver-txt-value" ) . innerHTML =
data . dnsAnswer ;
$wildList . appendChild ( $tpl ) ;
$wildList . appendChild ( $tpl ) ;
} else if ( obj [ data . type ] ) {
} else if ( obj [ data . type ] ) {
obj [ data . type ] . push ( data ) ;
obj [ data . type ] . push ( data ) ;
if ( 'dns-01' === data . type ) {
if ( "dns-01" === data . type ) {
$tpl . innerHTML = dnsTpl ;
$tpl . innerHTML = dnsTpl ;
$tpl . querySelector ( ".js-acme-ver-txt-host" ) . innerHTML = data . dnsHost ;
$tpl . querySelector ( ".js-acme-ver-txt-host" ) . innerHTML =
$tpl . querySelector ( ".js-acme-ver-txt-value" ) . innerHTML = data . dnsAnswer ;
data . dnsHost ;
$tpl . querySelector ( ".js-acme-ver-txt-value" ) . innerHTML =
data . dnsAnswer ;
$dnsList . appendChild ( $tpl ) ;
$dnsList . appendChild ( $tpl ) ;
} else if ( 'http-01' === data . type ) {
} else if ( "http-01" === data . type ) {
$tpl . innerHTML = httpTpl ;
$tpl . innerHTML = httpTpl ;
$tpl . querySelector ( ".js-acme-ver-file-location" ) . innerHTML = data . httpPath . split ( "/" ) . slice ( - 1 ) ;
$tpl . querySelector (
$tpl . querySelector ( ".js-acme-ver-content" ) . innerHTML = data . httpAuth ;
".js-acme-ver-file-location"
$tpl . querySelector ( ".js-acme-ver-uri" ) . innerHTML = data . httpPath ;
) . innerHTML = data . httpPath . split ( "/" ) . slice ( - 1 ) ;
$tpl . querySelector ( ".js-acme-ver-content" ) . innerHTML =
data . httpAuth ;
$tpl . querySelector ( ".js-acme-ver-uri" ) . innerHTML =
data . httpPath ;
$tpl . querySelector ( ".js-download-verify-link" ) . href =
$tpl . querySelector ( ".js-download-verify-link" ) . href =
"data:text/octet-stream;base64," + window . btoa ( data . httpAuth ) ;
"data:text/octet-stream;base64," +
$tpl . querySelector ( ".js-download-verify-link" ) . download = data . httpPath . split ( "/" ) . slice ( - 1 ) ;
window . btoa ( data . httpAuth ) ;
$tpl . querySelector (
".js-download-verify-link"
) . download = data . httpPath . split ( "/" ) . slice ( - 1 ) ;
$httpList . appendChild ( $tpl ) ;
$httpList . appendChild ( $tpl ) ;
}
}
}
}
@ -347,29 +438,36 @@
// hide wildcard if no wildcard
// hide wildcard if no wildcard
// hide http-01 and dns-01 if only wildcard
// hide http-01 and dns-01 if only wildcard
if ( ! obj . wildcard . length ) {
if ( ! obj . wildcard . length ) {
$qs ( '.js-acme-wildcard-challenges' ) . hidden = true ;
$qs ( ".js-acme-wildcard-challenges" ) . hidden = true ;
}
}
if ( ! obj [ 'http-01' ] . length ) {
if ( ! obj [ "http-01" ] . length ) {
$qs ( '.js-acme-challenges' ) . hidden = true ;
$qs ( ".js-acme-challenges" ) . hidden = true ;
}
}
console . info ( "[housekeeping] challenges" , info . challenges ) ;
console . info ( "[housekeeping] challenges" , info . challenges ) ;
updateChallengeType ( ) ;
updateChallengeType ( ) ;
return steps [ i ] ( ) ;
return steps [ i ] ( ) ;
} ) . catch ( function ( err ) {
} )
. catch ( function ( err ) {
if ( err . detail || err . urn ) {
if ( err . detail || err . urn ) {
console . error ( "(Probably) User Error:" ) ;
console . error ( "(Probably) User Error:" ) ;
console . error ( err ) ;
console . error ( err ) ;
return newAlert ( "There was an error, probably with your email or domain:\n" + err . message ) ;
return newAlert (
"There was an error, probably with your email or domain:\n" +
err . message
) ;
}
}
throw err ;
throw err ;
} ) ;
} ) ;
} ) . catch ( function ( err ) {
} )
console . error ( 'Step \'\' Error:' ) ;
. catch ( function ( err ) {
console . error ( "Step '' Error:" ) ;
console . error ( err , err . stack ) ;
console . error ( err , err . stack ) ;
return newAlert ( "An error happened (but it's not your fault)."
return newAlert (
+ " Email aj@rootprojects.org to let him know that 'order and get challenges' failed." ) ;
"An error happened (but it's not your fault)." +
" Email aj@rootprojects.org to let him know that 'order and get challenges' failed."
) ;
} ) ;
} ) ;
} ;
} ;
@ -377,14 +475,14 @@
console . info ( "\n3. Present challenge options" ) ;
console . info ( "\n3. Present challenge options" ) ;
updateProgress ( 1 ) ;
updateProgress ( 1 ) ;
hideForms ( ) ;
hideForms ( ) ;
$qs ( '.js-acme-form-challenges' ) . hidden = false ;
$qs ( ".js-acme-form-challenges" ) . hidden = false ;
} ;
} ;
steps [ 3 ] . submit = function ( ) {
steps [ 3 ] . submit = function ( ) {
console . info ( "[submit] 3. Fulfill challenges, fetch certificate" ) ;
console . info ( "[submit] 3. Fulfill challenges, fetch certificate" ) ;
var challengePriority = [ 'dns-01' ] ;
var challengePriority = [ "dns-01" ] ;
if ( 'http-01' === $qs ( '.js-acme-challenge-type:checked' ) . value ) {
if ( "http-01" === $qs ( ".js-acme-challenge-type:checked" ) . value ) {
challengePriority . unshift ( 'http-01' ) ;
challengePriority . unshift ( "http-01" ) ;
}
}
console . info ( "[challenge] selected " , challengePriority [ 0 ] ) ;
console . info ( "[challenge] selected " , challengePriority [ 0 ] ) ;
@ -396,32 +494,34 @@
// info.order.claims.push(...)
// info.order.claims.push(...)
// TODO warn about wait-time if DNS
// TODO warn about wait-time if DNS
return getServerKeypair ( ) . then ( function ( serverJwk ) {
return getServerKeypair ( ) . then ( function ( serverJwk ) {
return acme . orders . complete ( {
return acme . orders
account : info . account
. complete ( {
, accountKeypair : { privateKeyJwk : jwk }
account : info . account ,
, order : info . order
accountKeypair : { privateKeyJwk : jwk } ,
, domains : info . domains
order : info . order ,
, domainKeypair : { privateKeyJwk : serverJwk }
domains : info . domains ,
, challengePriority : challengePriority
domainKeypair : { privateKeyJwk : serverJwk } ,
, challenges : false
challengePriority : challengePriority ,
, onChallengeStatus : function ( details ) {
challenges : false ,
$qs ( '.js-challenge-responses' ) . hidden = false ;
onChallengeStatus : function ( details ) {
$qs ( '.js-challenge-response-type' ) . innerText = details . type ;
$qs ( ".js-challenge-responses" ) . hidden = false ;
$qs ( '.js-challenge-response-status' ) . innerText = details . status ;
$qs ( ".js-challenge-response-type" ) . innerText = details . type ;
$qs ( '.js-challenge-response-altname' ) . innerText = details . altname ;
$qs ( ".js-challenge-response-status" ) . innerText = details . status ;
}
$qs ( ".js-challenge-response-altname" ) . innerText = details . altname ;
} ) . then ( function ( certs ) {
}
} )
. then ( function ( certs ) {
return Keypairs . export ( { jwk : serverJwk } ) . then ( function ( keyPem ) {
return Keypairs . export ( { jwk : serverJwk } ) . then ( function ( keyPem ) {
console . info ( 'WINNING!' ) ;
console . info ( "WINNING!" ) ;
console . info ( certs ) ;
console . info ( certs ) ;
$qs ( '#js-fullchain' ) . innerHTML = [
$qs ( "#js-fullchain" ) . innerHTML = [
certs . cert . trim ( ) + "\n"
certs . cert . trim ( ) + "\n" ,
, certs . chain + "\n"
certs . chain + "\n"
] . join ( "\n" ) ;
] . join ( "\n" ) ;
$qs ( "#js-download-fullchain-link" ) . href =
$qs ( "#js-download-fullchain-link" ) . href =
"data:text/octet-stream;base64," + window . btoa ( certs ) ;
"data:text/octet-stream;base64," + window . btoa ( certs ) ;
$qs ( '#js-privkey' ) . innerHTML = keyPem ;
$qs ( "#js-privkey" ) . innerHTML = keyPem ;
$qs ( "#js-download-privkey-link" ) . href =
$qs ( "#js-download-privkey-link" ) . href =
"data:text/octet-stream;base64," + window . btoa ( keyPem ) ;
"data:text/octet-stream;base64," + window . btoa ( keyPem ) ;
return submitForm ( ) ;
return submitForm ( ) ;
@ -433,48 +533,47 @@
// spinner
// spinner
steps [ 4 ] = function ( ) {
steps [ 4 ] = function ( ) {
console . info ( '\n4. Show loading spinner' ) ;
console . info ( "\n4. Show loading spinner" ) ;
updateProgress ( 1 ) ;
updateProgress ( 1 ) ;
hideForms ( ) ;
hideForms ( ) ;
$qs ( '.js-acme-form-poll' ) . hidden = false ;
$qs ( ".js-acme-form-poll" ) . hidden = false ;
} ;
} ;
steps [ 4 ] . submit = function ( ) {
steps [ 4 ] . submit = function ( ) {
console . info ( '[submit] 4. Order complete' ) ;
console . info ( "[submit] 4. Order complete" ) ;
return steps [ i ] ( ) ;
return steps [ i ] ( ) ;
} ;
} ;
steps [ 5 ] = function ( ) {
steps [ 5 ] = function ( ) {
console . info ( '\n5. Present certificates (yay!)' ) ;
console . info ( "\n5. Present certificates (yay!)" ) ;
updateProgress ( 2 ) ;
updateProgress ( 2 ) ;
hideForms ( ) ;
hideForms ( ) ;
$qs ( '.js-acme-form-download' ) . hidden = false ;
$qs ( ".js-acme-form-download" ) . hidden = false ;
} ;
} ;
function init ( ) {
function init ( ) {
$qsa ( '.js-acme-api-type' ) . forEach ( function ( $el ) {
$qsa ( ".js-acme-api-type" ) . forEach ( function ( $el ) {
$el . addEventListener ( 'change' , updateApiType ) ;
$el . addEventListener ( "change" , updateApiType ) ;
} ) ;
} ) ;
updateApiType ( ) ;
updateApiType ( ) ;
$qsa ( '.js-acme-form' ) . forEach ( function ( $el ) {
$qsa ( ".js-acme-form" ) . forEach ( function ( $el ) {
$el . addEventListener ( 'submit' , function ( ev ) {
$el . addEventListener ( "submit" , function ( ev ) {
ev . preventDefault ( ) ;
ev . preventDefault ( ) ;
return submitForm ( ev ) ;
return submitForm ( ev ) ;
} ) ;
} ) ;
} ) ;
} ) ;
$qsa ( '.js-acme-challenge-type' ) . forEach ( function ( $el ) {
$qsa ( ".js-acme-challenge-type" ) . forEach ( function ( $el ) {
$el . addEventListener ( 'change' , updateChallengeType ) ;
$el . addEventListener ( "change" , updateChallengeType ) ;
} ) ;
} ) ;
var params = new URLSearchParams ( window . location . search ) ;
var params = new URLSearchParams ( window . location . search ) ;
var apiType = params . get ( 'acme-api-type' ) || "staging-v02" ;
var apiType = params . get ( "acme-api-type" ) || "staging-v02" ;
if ( params . has ( 'acme-domains' ) ) {
if ( params . has ( "acme-domains" ) ) {
$qs ( '.js-acme-domains' ) . value = params . get ( 'acme-domains' ) ;
$qs ( ".js-acme-domains" ) . value = params . get ( "acme-domains" ) ;
$qsa ( '.js-acme-api-type' ) . forEach ( function ( ele ) {
$qsa ( ".js-acme-api-type" ) . forEach ( function ( ele ) {
if ( ele . value === apiType ) {
if ( ele . value === apiType ) {
ele . checked = true ;
ele . checked = true ;
}
}
@ -489,16 +588,20 @@
}
}
init ( ) ;
init ( ) ;
$qs ( 'body' ) . hidden = false ;
$qs ( "body" ) . hidden = false ;
// in the background
// in the background
info . cryptoCheck = testKeypairSupport ( ) . then ( function ( ) {
info . cryptoCheck = testKeypairSupport ( )
. then ( function ( ) {
console . info ( "[crypto] self-check: passed" ) ;
console . info ( "[crypto] self-check: passed" ) ;
} ) . catch ( function ( err ) {
} )
console . error ( '[crypto] could not use either RSA nor EC.' ) ;
. catch ( function ( err ) {
console . error ( "[crypto] could not use either RSA nor EC." ) ;
console . error ( err ) ;
console . error ( err ) ;
window . alert ( "Generating secure certificates requires a browser with cryptography support."
window . alert (
+ "Please consider a recent version of Chrome, Firefox, or Safari." ) ;
"Generating secure certificates requires a browser with cryptography support." +
"Please consider a recent version of Chrome, Firefox, or Safari."
) ;
throw err ;
throw err ;
} ) ;
} ) ;
} ( ) ) ;
} ) ( ) ;