Auth0-spa-js: Polyfills needed for IE 11?

Created on 25 Jul 2019  路  13Comments  路  Source: auth0/auth0-spa-js

Hey 馃憢

I have a Vue CLI app that I've adding this package to via npm. I have it all working in modern browsers, but IE 11 throws a number of errors related to missing features (i.e., String.prototype.startsWith).

I could be totally off here, but it looks like dist/auth0-spa-js.production.js is built to be ES5 compatible, and work in IE 11?

If not, my follow-up question: What are the polyfills this package needs for IE 11?

Vue CLI recommends either explicitly including the polyfills needed (which I've done with other dependencies), or just including all polyfills for your target browsers. I'd rather not include all polyfills if possible.

Most helpful comment

Hi folks. Sorry to keep you all waiting. I'm doing some cleanup in the polyfills to make sure we only load what we need (we'll also get rid of the legacy entrypoint and just have the regular bundle be ie11 compatible).

All 13 comments

I am getting similar issue in IE

Unhandled promise rejection ReferenceError: 'crypto' is undefined

seems to be coming from loginWithRedirect

yep, I mentioned this in another ticket as well, I think a fix for at least Crypto, will be in the next release https://github.com/auth0/auth0-spa-js/pull/98

Does anyone have a suggested "quick fix" for the Unhandled promise rejection ReferenceError: 'crypto' is undefined problem??

We're getting beat up bad right now as a lot of our users on using windows 11 unfortunately.

Thanks.

@izzmeifter This is the polyfill they use in the legacy build: https://github.com/auth0/auth0-spa-js/blob/070505cda234e0a14c7cdac86e92f65a2ecdadb4/src/legacy.ts#L3-L5

if (!window.crypto && (window).msCrypto) {
(window).crypto = (window).msCrypto;
}

This solves the crypto issue but immediately exposes another issue for me.

it appears that crypto.getRandomValues(new Uint8Array(43)) returns an object rather than an array in IE11 and as such the subsequent line which does a foreach fails
randomValues.forEach(v => (random += charset[v % charset.length]));

@ed-sparkes I think this should be solved by https://github.com/auth0/auth0-spa-js/pull/98 (ES5 compatibility) which is in the next release as well. Eagerly awaiting the release now... :)

@PapaMufflon It looks like #98 has already been released in v1.1.1: https://github.com/auth0/auth0-spa-js/blob/master/CHANGELOG.md

@jstayton @ed-sparkes You are correct, my bad. It still does not work on IE11.

Finally got it working. I had to polyfill Uint8Array.slice, Array.includes and windows.crypto with a getRandomValues method returning a new Array (used a modified version of webcrypto-shim for that).

@PapaMufflon can you share your code for this polyfill thanks

Sure, I'm using Angular and am very sure that there are better solutions. In main.ts:

```` typescript
// IE11 fetch polyfill
import 'unfetch/polyfill/index';

auth0Ie11Polyfill();

// standard angular-stuff
if (environment.production) {
enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

// auth0-IE11 polyfill begins here
function auth0Ie11Polyfill() {
webcryptoShim(window);

if (!Uint8Array.prototype.slice) {
Object.defineProperty(Uint8Array.prototype, 'slice', {
value: function (begin, end)
{
return new Uint8Array(Array.prototype.slice.call(this, begin, end));
}
});
}

if (!Array.prototype.includes) {
Array.prototype.includes = x => Array.prototype.indexOf(x) >= 0;
}
}

class AesGcmEncryptResult {
ciphertext: ArrayBuffer;
tag: ArrayBuffer;
}

// from https://github.com/vibornoff/webcrypto-shim/blob/master/webcrypto-shim.js
function webcryptoShim(global: any) {
if ( typeof Promise !== 'function' )
throw "Promise support required";

var _crypto = global.crypto || global.msCrypto;
if ( !_crypto ) return;

var _subtle = _crypto.subtle || _crypto.webkitSubtle;
if ( !_subtle ) return;

var _Crypto = global.Crypto || _crypto.constructor || Object,
_SubtleCrypto = global.SubtleCrypto || _subtle.constructor || Object,
_CryptoKey = global.CryptoKey || global.Key || Object;

var isEdge = global.navigator.userAgent.indexOf('Edge/') > -1;
var isIE = !!global.msCrypto && !isEdge;
var isWebkit = !_crypto.subtle && !!_crypto.webkitSubtle;
if ( !isIE && !isWebkit ) return;

function s2a ( s ) {
return btoa(s).replace(/\=+$/, '').replace(/+/g, '-').replace(/\//g, '_');
}

function a2s ( s ) {
s += '===', s = s.slice( 0, -s.length % 4 );
return atob( s.replace(/-/g, '+').replace(/_/g, '/') );
}

function s2b ( s ) {
var b = new Uint8Array(s.length);
for ( var i = 0; i < s.length; i++ ) b[i] = s.charCodeAt(i);
return b;
}

function b2s ( b ) {
if ( b instanceof ArrayBuffer ) b = new Uint8Array(b);
return String.fromCharCode.apply( String, b );
}

function alg ( a ) {
var r = { 'name': (a.name || a || '').toUpperCase().replace('V','v') };
switch ( r.name ) {
case 'SHA-1':
case 'SHA-256':
case 'SHA-384':
case 'SHA-512':
break;
case 'AES-CBC':
case 'AES-GCM':
case 'AES-KW':
if ( a.length ) r['length'] = a.length;
break;
case 'HMAC':
if ( a.hash ) r['hash'] = alg(a.hash);
if ( a.length ) r['length'] = a.length;
break;
case 'RSAES-PKCS1-v1_5':
if ( a.publicExponent ) r['publicExponent'] = new Uint8Array(a.publicExponent);
if ( a.modulusLength ) r['modulusLength'] = a.modulusLength;
break;
case 'RSASSA-PKCS1-v1_5':
case 'RSA-OAEP':
if ( a.hash ) r['hash'] = alg(a.hash);
if ( a.publicExponent ) r['publicExponent'] = new Uint8Array(a.publicExponent);
if ( a.modulusLength ) r['modulusLength'] = a.modulusLength;
break;
default:
throw new SyntaxError("Bad algorithm name");
}
return r;
};

function jwkAlg ( a ) {
return {
'HMAC': {
'SHA-1': 'HS1',
'SHA-256': 'HS256',
'SHA-384': 'HS384',
'SHA-512': 'HS512',
},
'RSASSA-PKCS1-v1_5': {
'SHA-1': 'RS1',
'SHA-256': 'RS256',
'SHA-384': 'RS384',
'SHA-512': 'RS512',
},
'RSAES-PKCS1-v1_5': {
'': 'RSA1_5',
},
'RSA-OAEP': {
'SHA-1': 'RSA-OAEP',
'SHA-256': 'RSA-OAEP-256',
},
'AES-KW': {
'128': 'A128KW',
'192': 'A192KW',
'256': 'A256KW',
},
'AES-GCM': {
'128': 'A128GCM',
'192': 'A192GCM',
'256': 'A256GCM',
},
'AES-CBC': {
'128': 'A128CBC',
'192': 'A192CBC',
'256': 'A256CBC',
},
}[a.name][ ( a.hash || {} ).name || a.length || '' ];
}

function b2jwk ( k ) {
if ( k instanceof ArrayBuffer || k instanceof Uint8Array ) k = JSON.parse( decodeURIComponent( escape( b2s(k) ) ) );
var jwk:any = { 'kty': k.kty, 'alg': k.alg, 'ext': k.ext || k.extractable };
switch ( jwk.kty ) {
case 'oct':
jwk.k = k.k;
case 'RSA':
[ 'n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi', 'oth' ].forEach( function ( x ) { if ( x in k ) jwk[x] = k[x] } );
break;
default:
throw new TypeError("Unsupported key type");
}
return jwk;
}

function jwk2b ( k ) {
var jwk = b2jwk(k);
if ( isIE ) jwk['extractable'] = jwk.ext, delete jwk.ext;
return s2b( unescape( encodeURIComponent( JSON.stringify(jwk) ) ) ).buffer;
}

function pkcs2jwk ( k ) {
var info = b2der(k), prv = false;
if ( info.length > 2 ) prv = true, info.shift(); // remove version from PKCS#8 PrivateKeyInfo structure
var jwk = { 'ext': true };
switch ( info[0][0] ) {
case '1.2.840.113549.1.1.1':
var rsaComp = [ 'n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi' ],
rsaKey = b2der( info[1] );
if ( prv ) rsaKey.shift(); // remove version from PKCS#1 RSAPrivateKey structure
for ( var i = 0; i < rsaKey.length; i++ ) {
if ( !rsaKey[i][0] ) rsaKey[i] = rsaKey[i].subarray(1);
jwk[ rsaComp[i] ] = s2a( b2s( rsaKey[i] ) );
}
jwk['kty'] = 'RSA';
break;
default:
throw new TypeError("Unsupported key type");
}
return jwk;
}

function jwk2pkcs ( k ) {
var key, info:any = [ [ '', null ] ], prv = false;
switch ( k.kty ) {
case 'RSA':
var rsaComp = [ 'n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi' ],
rsaKey = [];
for ( var i = 0; i < rsaComp.length; i++ ) {
if ( !( rsaComp[i] in k ) ) break;
var b = rsaKey[i] = s2b( a2s( k[ rsaComp[i] ] ) );
if ( b[0] & 0x80 ) rsaKey[i] = new Uint8Array(b.length + 1), rsaKey[i].set( b, 1 );
}
if ( rsaKey.length > 2 ) prv = true, rsaKey.unshift( new Uint8Array([0]) ); // add version to PKCS#1 RSAPrivateKey structure
info[0][0] = '1.2.840.113549.1.1.1';
key = rsaKey;
break;
default:
throw new TypeError("Unsupported key type");
}
info.push( new Uint8Array( der2b(key) ).buffer );
if ( !prv ) info[1] = { 'tag': 0x03, 'value': info[1] };
else info.unshift( new Uint8Array([0]) ); // add version to PKCS#8 PrivateKeyInfo structure
return new Uint8Array( der2b(info) ).buffer;
}

var oid2str = { 'KoZIhvcNAQEB': '1.2.840.113549.1.1.1' },
str2oid = { '1.2.840.113549.1.1.1': 'KoZIhvcNAQEB' };

function b2der ( buf, ctx? ) {
if ( buf instanceof ArrayBuffer ) buf = new Uint8Array(buf);
if ( !ctx ) ctx = { pos: 0, end: buf.length };

  if ( ctx.end - ctx.pos < 2 || ctx.end > buf.length ) throw new RangeError("Malformed DER");

  var tag = buf[ctx.pos++],
      len = buf[ctx.pos++];

  if ( len >= 0x80 ) {
      len &= 0x7f;
      if ( ctx.end - ctx.pos < len ) throw new RangeError("Malformed DER");
      for ( var xlen = 0; len--; ) xlen <<= 8, xlen |= buf[ctx.pos++];
      len = xlen;
  }

  if ( ctx.end - ctx.pos < len ) throw new RangeError("Malformed DER");

  var rv;

  switch ( tag ) {
      case 0x02: // Universal Primitive INTEGER
          rv = buf.subarray( ctx.pos, ctx.pos += len );
          break;
      case 0x03: // Universal Primitive BIT STRING
          if ( buf[ctx.pos++] ) throw new Error( "Unsupported bit string" );
          len--;
      case 0x04: // Universal Primitive OCTET STRING
          rv = new Uint8Array( buf.subarray( ctx.pos, ctx.pos += len ) ).buffer;
          break;
      case 0x05: // Universal Primitive NULL
          rv = null;
          break;
      case 0x06: // Universal Primitive OBJECT IDENTIFIER
          var oid = btoa( b2s( buf.subarray( ctx.pos, ctx.pos += len ) ) );
          if ( !( oid in oid2str ) ) throw new Error( "Unsupported OBJECT ID " + oid );
          rv = oid2str[oid];
          break;
      case 0x30: // Universal Constructed SEQUENCE
          rv = [];
          for ( var end = ctx.pos + len; ctx.pos < end; ) rv.push( b2der( buf, ctx ) );
          break;
      default:
          throw new Error( "Unsupported DER tag 0x" + tag.toString(16) );
  }

  return rv;

}

function der2b ( val, buf? ) {
if ( !buf ) buf = [];

  var tag = 0, len = 0,
      pos = buf.length + 2;

  buf.push( 0, 0 ); // placeholder

  if ( val instanceof Uint8Array ) {  // Universal Primitive INTEGER
      tag = 0x02, len = val.length;
      for ( var i = 0; i < len; i++ ) buf.push( val[i] );
  }
  else if ( val instanceof ArrayBuffer ) { // Universal Primitive OCTET STRING
      tag = 0x04, len = val.byteLength, val = new Uint8Array(val);
      for ( var i = 0; i < len; i++ ) buf.push( val[i] );
  }
  else if ( val === null ) { // Universal Primitive NULL
      tag = 0x05, len = 0;
  }
  else if ( typeof val === 'string' && val in str2oid ) { // Universal Primitive OBJECT IDENTIFIER
      var oid = s2b( atob( str2oid[val] ) );
      tag = 0x06, len = oid.length;
      for ( var i = 0; i < len; i++ ) buf.push( oid[i] );
  }
  else if ( val instanceof Array ) { // Universal Constructed SEQUENCE
      for ( var i = 0; i < val.length; i++ ) der2b( val[i], buf );
      tag = 0x30, len = buf.length - pos;
  }
  else if ( typeof val === 'object' && val.tag === 0x03 && val.value instanceof ArrayBuffer ) { // Tag hint
      val = new Uint8Array(val.value), tag = 0x03, len = val.byteLength;
      buf.push(0); for ( var i = 0; i < len; i++ ) buf.push( val[i] );
      len++;
  }
  else {
      throw new Error( "Unsupported DER value " + val );
  }

  if ( len >= 0x80 ) {
      var xlen = len, len = 4;
      buf.splice( pos, 0, (xlen >> 24) & 0xff, (xlen >> 16) & 0xff, (xlen >> 8) & 0xff, xlen & 0xff );
      while ( len > 1 && !(xlen >> 24) ) xlen <<= 8, len--;
      if ( len < 4 ) buf.splice( pos, 4 - len );
      len |= 0x80;
  }

  buf.splice( pos - 2, 2, tag, len );

  return buf;

}

function CryptoKey ( key, alg, ext, use ) {
Object.defineProperties( this, {
_key: {
value: key
},
type: {
value: key.type,
enumerable: true,
},
extractable: {
value: (ext === undefined) ? key.extractable : ext,
enumerable: true,
},
algorithm: {
value: (alg === undefined) ? key.algorithm : alg,
enumerable: true,
},
usages: {
value: (use === undefined) ? key.usages : use,
enumerable: true,
},
});
}

function isPubKeyUse ( u ) {
return u === 'verify' || u === 'encrypt' || u === 'wrapKey';
}

function isPrvKeyUse ( u ) {
return u === 'sign' || u === 'decrypt' || u === 'unwrapKey';
}

[ 'generateKey', 'importKey', 'unwrapKey' ]
.forEach( function ( m ) {
var _fn = _subtle[m];

      _subtle[m] = function ( a, b, c ) {
          var args = [].slice.call(arguments),
              ka, kx, ku;

          switch ( m ) {
              case 'generateKey':
                  ka = alg(a), kx = b, ku = c;
                  break;
              case 'importKey':
                  ka = alg(c), kx = args[3], ku = args[4];
                  if ( a === 'jwk' ) {
                      b = b2jwk(b);
                      if ( !b.alg ) b.alg = jwkAlg(ka);
                      if ( !b.key_ops ) b.key_ops = ( b.kty !== 'oct' ) ? ( 'd' in b ) ? ku.filter(isPrvKeyUse) : ku.filter(isPubKeyUse) : ku.slice();
                      args[1] = jwk2b(b);
                  }
                  break;
              case 'unwrapKey':
                  ka = args[4], kx = args[5], ku = args[6];
                  args[2] = c._key;
                  break;
          }

          if ( m === 'generateKey' && ka.name === 'HMAC' && ka.hash ) {
              ka.length = ka.length || { 'SHA-1': 512, 'SHA-256': 512, 'SHA-384': 1024, 'SHA-512': 1024 }[ka.hash.name];
              return _subtle.importKey( 'raw', _crypto.getRandomValues( new Uint8Array( (ka.length+7)>>3 ) ), ka, kx, ku );
          }

          if ( isWebkit && m === 'generateKey' && ka.name === 'RSASSA-PKCS1-v1_5' && ( !ka.modulusLength || ka.modulusLength >= 2048 ) ) {
              a = alg(a), a.name = 'RSAES-PKCS1-v1_5', delete a.hash;
              return _subtle.generateKey( a, true, [ 'encrypt', 'decrypt' ] )
                  .then( function ( k ) {
                      return Promise.all([
                          _subtle.exportKey( 'jwk', k.publicKey ),
                          _subtle.exportKey( 'jwk', k.privateKey ),
                      ]);
                  })
                  .then( function ( keys ) {
                      keys[0].alg = keys[1].alg = jwkAlg(ka);
                      keys[0].key_ops = ku.filter(isPubKeyUse), keys[1].key_ops = ku.filter(isPrvKeyUse);
                      return Promise.all([
                          _subtle.importKey( 'jwk', keys[0], ka, true, keys[0].key_ops ),
                          _subtle.importKey( 'jwk', keys[1], ka, kx, keys[1].key_ops ),
                      ]);
                  })
                  .then( function ( keys ) {
                      return {
                          publicKey: keys[0],
                          privateKey: keys[1],
                      };
                  });
          }

          if ( ( isWebkit || ( isIE && ( ka.hash || {} ).name === 'SHA-1' ) )
                  && m === 'importKey' && a === 'jwk' && ka.name === 'HMAC' && b.kty === 'oct' ) {
              return _subtle.importKey( 'raw', s2b( a2s(b.k) ), c, args[3], args[4] );
          }

          if ( isWebkit && m === 'importKey' && ( a === 'spki' || a === 'pkcs8' ) ) {
              return _subtle.importKey( 'jwk', pkcs2jwk(b), c, args[3], args[4] );
          }

          if ( isIE && m === 'unwrapKey' ) {
              return _subtle.decrypt( args[3], c, b )
                  .then( function ( k ) {
                      return _subtle.importKey( a, k, args[4], args[5], args[6] );
                  });
          }

          var op;
          try {
              op = _fn.apply( _subtle, args );
          }
          catch ( e ) {
              return Promise.reject(e);
          }

          if ( isIE ) {
              op = new Promise( function ( res, rej ) {
                  op.onabort =
                  op.onerror =    function ( e ) { rej(e)               };
                  op.oncomplete = function ( r ) { res(r.target.result) };
              });
          }

          op = op.then( function ( k ) {
              if ( ka.name === 'HMAC' ) {
                  if ( !ka.length ) ka.length = 8 * k.algorithm.length;
              }
              if ( ka.name.search('RSA') == 0 ) {
                  if ( !ka.modulusLength ) ka.modulusLength = (k.publicKey || k).algorithm.modulusLength;
                  if ( !ka.publicExponent ) ka.publicExponent = (k.publicKey || k).algorithm.publicExponent;
              }
              if ( k.publicKey && k.privateKey ) {
                  k = {
                      publicKey: new CryptoKey( k.publicKey, ka, kx, ku.filter(isPubKeyUse) ),
                      privateKey: new CryptoKey( k.privateKey, ka, kx, ku.filter(isPrvKeyUse) ),
                  };
              }
              else {
                  k = new CryptoKey( k, ka, kx, ku );
              }
              return k;
          });

          return op;
      }
  });

[ 'exportKey', 'wrapKey' ]
.forEach( function ( m ) {
var _fn = _subtle[m];

      _subtle[m] = function ( a, b, c ) {
          var args = [].slice.call(arguments);

          switch ( m ) {
              case 'exportKey':
                  args[1] = b._key;
                  break;
              case 'wrapKey':
                  args[1] = b._key, args[2] = c._key;
                  break;
          }

          if ( ( isWebkit || ( isIE && ( b.algorithm.hash || {} ).name === 'SHA-1' ) )
                  && m === 'exportKey' && a === 'jwk' && b.algorithm.name === 'HMAC' ) {
              args[0] = 'raw';
          }

          if ( isWebkit && m === 'exportKey' && ( a === 'spki' || a === 'pkcs8' ) ) {
              args[0] = 'jwk';
          }

          if ( isIE && m === 'wrapKey' ) {
              return _subtle.exportKey( a, b )
                  .then( function ( k ) {
                      if ( a === 'jwk' ) k = s2b( unescape( encodeURIComponent( JSON.stringify( b2jwk(k) ) ) ) );
                      return  _subtle.encrypt( args[3], c, k );
                  });
          }

          var op;
          try {
              op = _fn.apply( _subtle, args );
          }
          catch ( e ) {
              return Promise.reject(e);
          }

          if ( isIE ) {
              op = new Promise( function ( res, rej ) {
                  op.onabort =
                  op.onerror =    function ( e ) { rej(e)               };
                  op.oncomplete = function ( r ) { res(r.target.result) };
              });
          }

          if ( m === 'exportKey' && a === 'jwk' ) {
              op = op.then( function ( k ) {
                  if ( ( isWebkit || ( isIE && ( b.algorithm.hash || {} ).name === 'SHA-1' ) )
                          && b.algorithm.name === 'HMAC') {
                      return { 'kty': 'oct', 'alg': jwkAlg(b.algorithm), 'key_ops': b.usages.slice(), 'ext': true, 'k': s2a( b2s(k) ) };
                  }
                  k = b2jwk(k);
                  if ( !k.alg ) k['alg'] = jwkAlg(b.algorithm);
                  if ( !k.key_ops ) k['key_ops'] = ( b.type === 'public' ) ? b.usages.filter(isPubKeyUse) : ( b.type === 'private' ) ? b.usages.filter(isPrvKeyUse) : b.usages.slice();
                  return k;
              });
          }

          if ( isWebkit && m === 'exportKey' && ( a === 'spki' || a === 'pkcs8' ) ) {
              op = op.then( function ( k ) {
                  k = jwk2pkcs( b2jwk(k) );
                  return k;
              });
          }

          return op;
      }
  });

[ 'encrypt', 'decrypt', 'sign', 'verify' ]
.forEach( function ( m ) {
var _fn = _subtle[m];

      _subtle[m] = function ( a, b, c, d ) {
          if ( isIE && ( !c.byteLength || ( d && !d.byteLength ) ) )
              throw new Error("Empy input is not allowed");

          var args = [].slice.call(arguments),
              ka = alg(a);

          if ( isIE && ( m === 'encrypt' || m === 'decrypt' ) && b.algorithm.hash ) {
              args[0].hash = args[0].hash || b.algorithm.hash;
          }

          if ( isIE && m === 'decrypt' && ka.name === 'AES-GCM' ) {
              var tl = a.tagLength >> 3;
              args[2] = (c.buffer || c).slice( 0, c.byteLength - tl ),
              a.tag = (c.buffer || c).slice( c.byteLength - tl );
          }

          args[1] = b._key;

          var op;
          try {
              op = _fn.apply( _subtle, args );
          }
          catch ( e ) {
              return Promise.reject(e);
          }

          if ( isIE ) {
              op = new Promise( function ( res, rej ) {
                  op.onabort =
                  op.onerror = function ( e ) {
                      rej(e);
                  };

                  op.oncomplete = function ( r ) {
                      var r = r.target.result;

                      if ( m === 'encrypt' && r instanceof AesGcmEncryptResult ) {
                          var c = r.ciphertext, t = r.tag;
                          r = new Uint8Array( c.byteLength + t.byteLength );
                          r.set( new Uint8Array(c), 0 );
                          r.set( new Uint8Array(t), c.byteLength );
                          r = r.buffer;
                      }

                      res(r);
                  };
              });
          }

          return op;
      }
  });

if ( isIE ) {
var _digest = _subtle.digest;

  _subtle['digest'] = function ( a, b ) {
      if ( !b.byteLength )
          throw new Error("Empy input is not allowed");

      var op;
      try {
          op = _digest.call( _subtle, a, b );
      }
      catch ( e ) {
          return Promise.reject(e);
      }

      op = new Promise( function ( res, rej ) {
          op.onabort =
          op.onerror =    function ( e ) { rej(e)               };
          op.oncomplete = function ( r ) { res(r.target.result) };
      });

      return op;
  };

  global.crypto = Object.create( _crypto, {
      getRandomValues: { value: function ( a ) { return new Array(_crypto.getRandomValues(a)) } },
      subtle:          { value: _subtle },
  });

  global.CryptoKey = CryptoKey;

}

if ( isWebkit ) {
_crypto.subtle = _subtle;

  global.Crypto = _Crypto;
  global.SubtleCrypto = _SubtleCrypto;
  global.CryptoKey = CryptoKey;

}
};
````

Hi folks. Sorry to keep you all waiting. I'm doing some cleanup in the polyfills to make sure we only load what we need (we'll also get rid of the legacy entrypoint and just have the regular bundle be ie11 compatible).

Build please!!!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Benjathing picture Benjathing  路  5Comments

yirenlu92 picture yirenlu92  路  4Comments

Hawxy picture Hawxy  路  7Comments

stupidSheep picture stupidSheep  路  4Comments

Hopchenko picture Hopchenko  路  6Comments