Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to load Spotify authorisation login page in a popup?

I'm trying to open Spotify's login page in a new popup window so that user can give my application permission to access their spotify data. I'm making a server side request to the Spotify and then sending the receiving HTML from the server to the client as per the auth flow.

The issue arises when I try to open the popup and I see nothing but three loading dots. The code I received from spotify is working as I have tested it in isolation and my popup code is also working but I can't get both of them to work together.

HTML

<html>
  <head>
    <title> Dashboard </title>
  </head>
  <body>
    <button id='myBtn'>Click</button>
  </body>
</html>

JS

const btn = document.getElementById('myBtn');



btn.addEventListener('click', function(){
  
  const popupWindow = window.open('',  'popup', "width=600,height=450,scrollbars=no");

  const codeData = `<!DOCTYPE html>
<html id="app" lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <title>Login - Spotify</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <base href="/">
  <link rel="icon" href="https://accounts.scdn.co/sso/images/favicon.ace4d8543bbb017893402a1e9d1ac1fa.ico">
  <script defer src="https://accounts.scdn.co/sso/js/indexReact.80b656d1587f8f6dc120.js" sp-bootstrap></script>
  <meta id="bootstrap-data" sp-bootstrap-data='{"phoneFeatureEnabled":true,"previewEnabled":false,"user":false,"tpaState":"AQAUyYkSh59ZWwsaoxktt0QP0peVoqudQySwwaRggIRzjIDvJXSPb1XkA+h2SMWxsB4S7lHzObldMs9cX00OyB2SBsLzsgtTmYjqR1Ep6pTiDLfTa8LwlUu7mfJr2i2ZilhhGkRzXztEVTtBeyl1JvTF2OS7QqA/bOYdFtz6yQPiJ7dA5LKaOdJQi6gIqWzfGqIXnTyxxx9Ok/uRa2YSzaPtUPGrjRAx1Z4pkUTc6QmEPJbl+fn+QiJT9LTUrIWw/0rmCj61J0EIY30wX5lFftxu7GWLorgOcG+iGluxKqGMP0dG41j8nKRZzFqV40MlgAoJOk8KYSYZ8Q4Xeeejvug+hVAQylQ1f3o98E+/KC19RPhfrENshoyNm9Xhz+aV+n3qXrTkSE/2wh0JDSVc+uK+vT0sk44eBji9FSnl2C+N+Pmn16uCOBvDaUFm+MLEdTgIwncGT6EmFamF7+JwHkf6j7aeySj6rr7NSkZBWrUivkmRi2Lsfw9B2Z5LGRtXxtAC9k/h2uhDNq9RW/WMxt6PBQEIH5AzbN8P35Ns8thrBSIr/ajViJXQ8q9rckEYMOCfsP3wABSWarVkPd8L2H5EitFYK6e5usTlEW3w3/kALZ9T+1I9LLrGIOt8XQGtvR+F1daROn83bt/oKkDuHvHYx4bX7+NnWHJHKoX2Dahta1q16auFoppSolY/XBVdCGO1J9RePaoOMja7Pj2L0LfKrePy7zse0n+0xiMQ9oZtXQ==","geoLocationCountryCode":"IN","state":"","flowCtx":"dac5b50d-741f-4095-b401-fa87bfd499f4:1705629847","BON":["0","0",-1672731924]}' sp-component="login" sp-translations-data='eyJlcnJvclRpdGxlIjoiRXJyb3IiLCJsb2dpblRpdGxlIjoiTG9naW4iLCJmb3Jnb3RZb3VyUGFzc3dvcmRVc2VybmFtZSI6IkZvcmdvdCB5b3VyIHBhc3N3b3JkPyIsImRvbnRIYXZlQW5BY2NvdW50IjoiRG9uJ3QgaGF2ZSBhbiBhY2NvdW50PyIsImlucHV0VXNlcm5hbWUiOiJFbWFpbCBhZGRyZXNzIG9yIHVzZXJuYW1lIiwiaW5wdXRFbWFpbE9yVXNlcm5hbWUiOiJFbWFpbCBvciB1c2VybmFtZSIsImlucHV0UGFzc3dvcmQiOiJQYXNzd29yZCIsImNoZWNrYm94UmVtZW1iZXJNZSI6IlJlbWVtYmVyIG1lIiwiZXJyb3JGb3JtRGVmYXVsdCI6Ik9vcHMhIFNvbWV0aGluZyB3ZW50IHdyb25nLCBwbGVhc2UgdHJ5IGFnYWluIG9yIGNoZWNrIG91dCBvdXIgPGhlbHBMaW5rPmhlbHAgYXJlYTwvaGVscExpbms+IiwiZXJyb3JJbnZhbGlkQ3JlZGVudGlhbHMiOiJJbmNvcnJlY3QgdXNlcm5hbWUgb3IgcGFzc3dvcmQuIiwiZXJyb3JJbnZhbGlkQ3JlZGVudGlhbHNJbXByb3ZlZCI6IkluY29ycmVjdCBlbWFpbCBhZGRyZXNzLCB1c2VybmFtZSBvciBwYXNzd29yZC4iLCJlcnJvclVua25vd24iOiJPb3BzISBTb21ldGhpbmcgd2VudCB3cm9uZywgcGxlYXNlIHRyeSBhZ2FpbiBvciBjaGVjayBvdXQgb3VyIDxoZWxwTGluaz5oZWxwIGFyZWE8L2hlbHBMaW5rPiIsImVycm9yVHJhbnNpZW50IjoiQW4gZXJyb3IgaGFzIG9jY3VycmVkIHByb2Nlc3NpbmcgeW91ciBsb2dpbi4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCJlcnJvckZhY2Vib29rQWNjb3VudCI6IllvdSBkbyBub3QgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCBjb25uZWN0ZWQgdG8geW91ciBGYWNlYm9vayBhY2NvdW50LiBJZiB5b3UgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIGxvZyBpbiB3aXRoIHlvdXIgU3BvdGlmeSBjcmVkZW50aWFscy4gSWYgeW91IGRvIG5vdCBoYXZlIGEgU3BvdGlmeSBhY2NvdW50LCA8bGlua1dpdGhIcmVmPnNpZ24gdXA8L2xpbmtXaXRoSHJlZj4uIiwiZXJyb3JTZXJ2ZXJFcnJvciI6IkFuIGVycm9yIGhhcyBvY2N1cnJlZCBwcm9jZXNzaW5nIHlvdXIgcmVxdWVzdC4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCJlcnJvclVzZXJuYW1lUmVxdWlyZWQiOiJQbGVhc2UgZW50ZXIgeW91ciBTcG90aWZ5IHVzZXJuYW1lIG9yIGVtYWlsIGFkZHJlc3MuIiwiZXJyb3JVc2VybmFtZUludmFsaWRDaGFyYWN0ZXJzIjoiRm9yYmlkZGVuIGNoYXJhY3RlcihzKSB7Zm9yYmlkZGVuQ2hhcnN9IGluIHVzZXJuYW1lLiIsImVycm9yUGFzc3dvcmRSZXF1aXJlZCI6IlBsZWFzZSBlbnRlciB5b3VyIHBhc3N3b3JkLiIsImVycm9yTm9JbnRlcm5ldENvbm5lY3Rpdml0eSI6IlByb2JsZW0gY29ubmVjdGluZy4gQ2hlY2sgeW91ciBpbnRlcm5ldCBjb25uZWN0aW9uIGFuZCB7dHJ5QWdhaW5MaW5rfS4iLCJlcnJvclRyeUFnYWluIjoidHJ5IGFnYWluIiwibG9nSW4iOiJMb2cgSW4iLCJsb2dJblRvU3BvdGlmeSI6IkxvZyBpbiB0byBTcG90aWZ5Iiwic2lnblVwRm9yU3BvdGlmeSI6IlNpZ24gdXAgZm9yIFNwb3RpZnkiLCJvciI6Im9yIiwibG9naW5Ub0NvbnRpbnVlIjoiVG8gY29udGludWUsIGxvZyBpbiB0byBTcG90aWZ5LiIsImVycm9yVmFsaWRhdGlvbkludmFsaWRDb2RlIjoiVGhpcyBjb2RlIGlzIGludmFsaWQuIENoZWNrIHRoZSBTTVMgYW5kIHRyeSBhZ2Fpbi4iLCJlcnJvclN1Ym1pdFRvb2tUb29Mb25nVG9DcmVhdGUiOiJJdCB0b29rIHRvbyBsb25nIHRvIGNvbXBsZXRlIHlvdXIgcmVxdWVzdC4gVHJ5IGFnYWluLiIsImVycm9yQXBwbGVBY2NvdW50IjoiWW91IGRvIG5vdCBoYXZlIGEgU3BvdGlmeSBhY2NvdW50IGNvbm5lY3RlZCB0byB5b3VyIEFwcGxlIElELiBJZiB5b3UgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIHRyeSBsb2cgaW4gd2l0aCB5b3VyIFNwb3RpZnkgZW1haWwgb3IgdXNlcm5hbWUuIElmIHlvdSBkbyBub3QgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIHNpZ24gdXAuIiwiY29udGludWVXaXRoQXBwbGUiOiJDb250aW51ZSB3aXRoIEFwcGxlIiwiY29udGludWVXaXRoRmFjZWJvb2siOiJDb250aW51ZSB3aXRoIEZhY2Vib29rIiwiY29udGludWVXaXRoUGhvbmVOdW1iZXIiOiJDb250aW51ZSB3aXRoIHBob25lIG51bWJlciIsImNvbnRpbnVlV2l0aEdvb2dsZSI6IkNvbnRpbnVlIHdpdGggR29vZ2xlIiwiZXJyb3JHb29nbGVBY2NvdW50IjoiWW91IGRvIG5vdCBoYXZlIGEgU3BvdGlmeSBhY2NvdW50IGNvbm5lY3RlZCB0byB5b3VyIEdvb2dsZSBBY2NvdW50LiBJZiB5b3UgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIHRyeSBsb2cgaW4gd2l0aCB5b3VyIFNwb3RpZnkgZW1haWwgb3IgdXNlcm5hbWUuIElmIHlvdSBkbyBub3QgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIHNpZ24gdXAuIiwicmVjYXB0Y2hhTGVnYWxOb3RpY2UiOiJUaGlzIHNpdGUgaXMgcHJvdGVjdGVkIGJ5IHJlQ0FQVENIQSBhbmQgdGhlIEdvb2dsZSA8Z29vZ2xlUHJpdmFjeVBvbGljeUxpbms+UHJpdmFjeSBQb2xpY3k8L2dvb2dsZVByaXZhY3lQb2xpY3lMaW5rPiBhbmQgPGdvb2dsZVRlcm1zTGluaz5UZXJtcyBvZiBTZXJ2aWNlPC9nb29nbGVUZXJtc0xpbms+IGFwcGx5LiIsImxvZ2luV2l0aG91dFBhc3N3b3JkIjoiTG9nIGluIHdpdGhvdXQgcGFzc3dvcmQiLCJtYWdpY19saW5rX3BvcHVwX2hlYWRlciI6IkhhdmluZyB0cm91YmxlIGxvZ2dpbmcgaW4/IiwibWFnaWNfbGlua19wb3B1cF9ib2R5IjoiV2Ugc2VudCBhbiBlbWFpbCB3aXRoIGEgbGluayB0aGF0IHdpbGwgbG9nIHlvdSBpbiB3aXRob3V0IGEgcGFzc3dvcmQuIiwiZGlhbG9nX19hY3Rpb25fcmV0cnlfbG9naW4iOiJUcnkgYW5vdGhlciBwYXNzd29yZCJ9'>
  <style>
    body {
      background-color: #121212;
      margin: 0;
    }
    #root {
      min-height: 100vh;
    }
    .loading-indicator-container {
      height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .loading-indicator-container * {
      box-sizing: border-box;
    }
    .loading-indicator {
      content: "";
      width: 56px;
      inline-size: 56px;
      block-size: 12.4px;
      height: 12.4px;
      overflow-clip-margin: content-box;
      overflow: hidden;
    }
    .loading-indicator circle {
      fill: white;
      animation: loading-indicator-kf 1.32s linear infinite;
      transform-origin: center center;
      opacity: 0.5;
    }
    .loading-indicator circle:nth-of-type(2) {
      animation-delay: 0.1s;
    }
    .loading-indicator circle:nth-of-type(3) {
      animation-delay: 0.2s;
    }
    @keyframes loading-indicator-kf {
      0% {
        animation-timing-function: cubic-bezier(1, 0, 0.7, 1);
        opacity: 0.5;
        transform: scale(1);
      }
      40% {
        animation-timing-function: cubic-bezier(0.3, 0, 0, 1);
        opacity: 0.75;
        transform: scale(1.3);
      }
      72.5% {
        animation-timing-function: linear;
        opacity: 0.5;
        transform: scale(1);
      }
      100% {
        opacity: 0.5;
        transform: scale(1);
      }
    }
  </style>
</head>
<body>
<div id="root">
  <div class="loading-indicator-container">
    <svg class="loading-indicator" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 1 100" xml:space="preserve" role="progressbar" aria-valuetext="Loading">
      <circle stroke="none" cx="-140" cy="50" r="32"></circle>
      <circle stroke="none" cx="0" cy="50" r="32"></circle>
      <circle stroke="none" cx="140" cy="50" r="32"></circle>
    </svg>
  </div>
</div>
</body>
</html>
`;
  const codeData2 = `<html><body><h1>MissingOut</h1></body></html>`
  popupWindow.document.write(codeData);  
});

Checkout this codepen to see the issue for yourself. I have tested the code in isolation, checked the consoles and the network tab but I can't figure out why the Spotify HTML code is being changed to loading dots code in the popup.

UPDATE 1

The HTML code in the new window is actually not different from the Spotify HTML code. The issue is most probably due to the fact that the elements which are not being loaded are supposed to come from the external scripts that are being loaded in the . The question now becomes why these scripts are having trouble being loaded in a new window or a new popup window.

like image 462
Anant Avatar asked Dec 21 '25 06:12

Anant


1 Answers

Per the MDN docs on document.write():

Users agents are explicitly allowed to avoid executing script elements inserted via this method. And to make matters even worse, the exact behavior of this method can in some cases be dependent on network latency, which can lead to failures that are very hard to debug.

Along with other inconsistent and unintuitive behavior surrounding document.write(), its use is generally discouraged.

You can read more about potential alternatives on this SO question, but you can accomplish what you're looking for using more traditional DOM manipulation techniques.

The example JS below successfully loads your script, using regular DOM manipulation

const btn = document.getElementById("myBtn");

btn.addEventListener("click", function () {
  const popupWindow = window.open("", "_blank");

  const codeData = `
    
<div id="root">
  <div class="loading-indicator-container">
    <svg class="loading-indicator" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 1 100" xml:space="preserve" role="progressbar" aria-valuetext="Loading">
      <circle stroke="none" cx="-140" cy="50" r="32"></circle>
      <circle stroke="none" cx="0" cy="50" r="32"></circle>
      <circle stroke="none" cx="140" cy="50" r="32"></circle>
    </svg>
  </div>
</div>
<style>
    body {
      background-color: #121212;
      margin: 0;
    }
    #root {
      min-height: 100vh;
    }
    .loading-indicator-container {
      height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .loading-indicator-container * {
      box-sizing: border-box;
    }
    .loading-indicator {
      content: "";
      width: 56px;
      inline-size: 56px;
      block-size: 12.4px;
      height: 12.4px;
      overflow-clip-margin: content-box;
      overflow: hidden;
    }
    .loading-indicator circle {
      fill: white;
      animation: loading-indicator-kf 1.32s linear infinite;
      transform-origin: center center;
      opacity: 0.5;
    }
    .loading-indicator circle:nth-of-type(2) {
      animation-delay: 0.1s;
    }
    .loading-indicator circle:nth-of-type(3) {
      animation-delay: 0.2s;
    }
    @keyframes loading-indicator-kf {
      0% {
        animation-timing-function: cubic-bezier(1, 0, 0.7, 1);
        opacity: 0.5;
        transform: scale(1);
      }
      40% {
        animation-timing-function: cubic-bezier(0.3, 0, 0, 1);
        opacity: 0.75;
        transform: scale(1.3);
      }
      72.5% {
        animation-timing-function: linear;
        opacity: 0.5;
        transform: scale(1);
      }
      100% {
        opacity: 0.5;
        transform: scale(1);
      }
    }
  </style>
</body>
</html>
`;
  const codeData2 = `<html><body><h1>MissingOut</h1></body></html>`;
  popupWindow.document.body.innerHTML = codeData;
  const metaElement = popupWindow.document.createElement("meta");
  popupWindow.document.head.appendChild(metaElement);
  metaElement.outerHTML = `<meta id="bootstrap-data" sp-bootstrap-data='...'>`;

  popupWindow.document.getElementsByTagName("html")[0].id = "app";
  popupWindow.document.getElementsByTagName("html")[0].lang = "en";
  popupWindow.document.getElementsByTagName("html")[0].dir = "ltr";
  const scriptElement = popupWindow.document.createElement("script");
  scriptElement.setAttribute(
    "src",
    "https://accounts.scdn.co/sso/js/indexReact.80b656d1587f8f6dc120.js"
  );

  popupWindow.document.body.appendChild(scriptElement);
});

like image 129
smallpepperz Avatar answered Dec 23 '25 18:12

smallpepperz