title: How to enable CORS in an Azure App Registration when used in an OAuth Authorization Flow with PKCE?
body: <p>I have a pure Javascript app which attempts to get an access token from Azure using OAuth Authorization Flow with PKCE.</p>
  
  <p><strong>The app is not hosted in Azure. I only use Azure as an OAuth Authorization Server.</strong></p>
  
  <pre><code>    //Based on: https://developer.okta.com/blog/2019/05/01/is-the-oauth-implicit-flow-dead
  
      var config = {
          client_id: "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx",
          redirect_uri: "http://localhost:8080/",
          authorization_endpoint: "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize",
          token_endpoint: "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token",
          requested_scopes: "openid api://{tenant-id}/user_impersonation"
      };
  
      // PKCE HELPER FUNCTIONS
  
      // Generate a secure random string using the browser crypto functions
      function generateRandomString() {
          var array = new Uint32Array(28);
          window.crypto.getRandomValues(array);
          return Array.from(array, dec =&gt; ('0' + dec.toString(16)).substr(-2)).join('');
      }
  
      // Calculate the SHA256 hash of the input text. 
      // Returns a promise that resolves to an ArrayBuffer
      function sha256(plain) {
          const encoder = new TextEncoder();
          const data = encoder.encode(plain);
          return window.crypto.subtle.digest('SHA-256', data);
      }
  
      // Base64-urlencodes the input string
      function base64urlencode(str) {
          // Convert the ArrayBuffer to string using Uint8 array to convert to what btoa accepts.
          // btoa accepts chars only within ascii 0-255 and base64 encodes them.
          // Then convert the base64 encoded to base64url encoded
          //   (replace + with -, replace / with _, trim trailing =)
          return btoa(String.fromCharCode.apply(null, new Uint8Array(str)))
      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
      }
  
      // Return the base64-urlencoded sha256 hash for the PKCE challenge
      async function pkceChallengeFromVerifier(v) {
          const hashed = await sha256(v);
          return base64urlencode(hashed);
      }
  
      // Parse a query string into an object
      function parseQueryString(string) {
          if (string == "") { return {}; }
          var segments = string.split("&amp;").map(s =&gt; s.split("="));
          var queryString = {};
          segments.forEach(s =&gt; queryString[s[0]] = s[1]);
          return queryString;
      }
  
      // Make a POST request and parse the response as JSON
      function sendPostRequest(url, params, success, error) {
          var request = new XMLHttpRequest();
          request.open('POST', url, true);
          request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
          request.onload = function () {
              var body = {};
              try {
                body = JSON.parse(request.response);
              } catch (e) { }
  
              if (request.status == 200) {
                success(request, body);
              } else {
                error(request, body);
              }
          }
  
          request.onerror = function () {
              error(request, {});
          }
          var body = Object.keys(params).map(key =&gt; key + '=' + params[key]).join('&amp;');
          request.send(body);
      }
  
      function component() {
          const element = document.createElement('div');
          const btn = document.createElement('button');
          element.innerHTML = 'Hello'+ 'webpack';
          element.classList.add('hello');
          return element;
      }
  
  
      (async function () {
          document.body.appendChild(component());
  
          const isAuthenticating = JSON.parse(window.localStorage.getItem('IsAuthenticating'));
          console.log('init -&gt; isAuthenticating', isAuthenticating);
          if (!isAuthenticating) {
          window.localStorage.setItem('IsAuthenticating', JSON.stringify(true));
  
          // Create and store a random "state" value
          var state = generateRandomString();
          localStorage.setItem("pkce_state", state);
  
          // Create and store a new PKCE code_verifier (the plaintext random secret)
          var code_verifier = generateRandomString();
          localStorage.setItem("pkce_code_verifier", code_verifier);
  
          // Hash and base64-urlencode the secret to use as the challenge
          var code_challenge = await pkceChallengeFromVerifier(code_verifier);
  
          // Build the authorization URL
          var url = config.authorization_endpoint
        + "?response_type=code"
        + "&amp;client_id=" + encodeURIComponent(config.client_id)
        + "&amp;state=" + encodeURIComponent(state)
        + "&amp;scope=" + encodeURIComponent(config.requested_scopes)
        + "&amp;redirect_uri=" + encodeURIComponent(config.redirect_uri)
        + "&amp;code_challenge=" + encodeURIComponent(code_challenge)
        + "&amp;code_challenge_method=S256"
        ;
  
          // Redirect to the authorization server
          window.location = url;
      } else {
  
          // Handle the redirect back from the authorization server and
          // get an access token from the token endpoint
  
          var q = parseQueryString(window.location.search.substring(1));
  
          console.log('queryString', q);
  
          // Check if the server returned an error string
          if (q.error) {
            alert("Error returned from authorization server: " + q.error);
            document.getElementById("error_details").innerText = q.error + "\n\n" + q.error_description;
            document.getElementById("error").classList = "";
          }
  
          // If the server returned an authorization code, attempt to exchange it for an access token
          if (q.code) {
  
            // Verify state matches what we set at the beginning
            if (localStorage.getItem("pkce_state") != q.state) {
              alert("Invalid state");
            } else {
  
              // Exchange the authorization code for an access token
              // !!!!!!! This POST fails because of CORS policy.
              sendPostRequest(config.token_endpoint, {
                grant_type: "authorization_code",
                code: q.code,
                client_id: config.client_id,
                redirect_uri: config.redirect_uri,
                code_verifier: localStorage.getItem("pkce_code_verifier")
              }, function (request, body) {
  
                // Initialize your application now that you have an access token.
                // Here we just display it in the browser.
                document.getElementById("access_token").innerText = body.access_token;
                document.getElementById("start").classList = "hidden";
                document.getElementById("token").classList = "";
  
                // Replace the history entry to remove the auth code from the browser address bar
                window.history.replaceState({}, null, "/");
  
              }, function (request, error) {
                // This could be an error response from the OAuth server, or an error because the 
                // request failed such as if the OAuth server doesn't allow CORS requests
                document.getElementById("error_details").innerText = error.error + "\n\n" + error.error_description;
                document.getElementById("error").classList = "";
              });
            }
  
            // Clean these up since we don't need them anymore
            localStorage.removeItem("pkce_state");
            localStorage.removeItem("pkce_code_verifier");
          }
      }
  
      }());
  </code></pre>
  
  <p>In Azure I only have an App registration (not an app service).</p>
  
  <p><a href="https://i.stack.imgur.com/lHtLv.png" rel="noreferrer">Azure App Registration</a></p>
  
  <p>The first step to get the authorization code works.</p>
  
  <p>But the POST to get the access token fails. (picture from <a href="https://developer.okta.com/blog/2019/08/22/okta-authjs-pkce/?utm_campaign=text_website_all_multiple_dev_dev_oauth-pkce_null&amp;utm_source=oauthio&amp;utm_medium=cpc" rel="noreferrer">here</a>)</p>
  
  <p><a href="https://i.stack.imgur.com/znuZI.png" rel="noreferrer">OAuth Authorization Code Flow with PKCE</a></p>
  
  <blockquote>
    <p>Access to XMLHttpRequest at
    '<a href="https://login.microsoftonline.com/" rel="noreferrer">https://login.microsoftonline.com/</a>{tenant-id}/oauth2/v2.0/token' from
    origin '<a href="http://localhost:8080" rel="noreferrer">http://localhost:8080</a>' has been blocked by CORS policy: No
    'Access-Control-Allow-Origin' header is present on the requested
    resource.</p>
  </blockquote>
  
  <p><strong>Where in Azure do I configure the CORS policy for an App Registration?</strong></p>