Auth0 in Chrome Extension Content/Background script MV3

Hi I got it working with manifest v3 & pure js:

I have a loginpopuphtml that references a login.js script. Here is the content of the login.js script. Heavily influnced by: Robert Tolton | Implementing Auth0 Authentication into a Chrome… and Call Your API Using the Authorization Code Flow with PKCE.

var btnLogin = document.getElementById("btnLogin");
var btnLogout = document.getElementById("btnLogout");

const fetchAuthConfig = () => fetch("../../auth_config.json");

function getRandomBytes(){
  const rndArray = new Uint8Array(44);
  window.crypto.getRandomValues(rndArray);
  return rndArray;
}

function buf2Base64(buffer) {
  return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)))
          .replace(/\+/g, '-')
          .replace(/\//g, '_')
          .replace(/=/g, '');
}

function getParameterByName(name, url = window.location.href) {
  name = name.replace(/[\[\]]/g, '\\$&');
  var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
      results = regex.exec(url);
  if (!results) return null;
  if (!results[2]) return '';
  return decodeURIComponent(results[2].replace(/\+/g, ' '));
}

async function getConfig(){
  const response = await fetchAuthConfig();
  const config = await response.json();
  return config
}
async function windowSha256(buffer) {
  let bytes = new TextEncoder().encode(buffer)
  return await window.crypto.subtle.digest('SHA-256', bytes);
}

async function callOurApi(accessToken){
  const config = await getConfig();
  const headers = await getAuthHeaders(accessToken);
  const res = await axios.get(`${config.audience}/YOUR_API_ENDPOINT_TO_TEST`, headers);
  console.log('callOurApi', res);
}

async function getAuthHeaders(accessToken) {
  return {
    headers: {
      Authorization: `Bearer ${accessToken}`
    }
  };
}

const login = async () => {
  // First lets get the redirectUrl and config. The redirect url must be added to your allowed callbacks urls and Allowed Origins (CORS) in Auth0 application
  // looks like: https://EXTENSION_ID.chromiumapp.org/
  const redirectUrl = chrome.identity.getRedirectURL()
  console.log('redirectUrl', redirectUrl)
  const config = await getConfig();

  // next we are going to generate a codeChallenge Sha256 code challenge and verifier
  // https://auth0.com/docs/get-started/authentication-and-authorization-flow/call-your-api-using-the-authorization-code-flow-with-pkce#create-code-verifier
  const inputBytes = getRandomBytes();
  const verifier = buf2Base64(inputBytes) 

  const shaHash = await windowSha256(verifier)
  const codeChallenge = buf2Base64(shaHash);
  console.log('codeChallenge', codeChallenge)

  // Now we make a request to authorise the user using chrome's identity framework. We get a code back
  let options = {
    client_id: config.clientId,
    redirect_uri: redirectUrl,
    response_type: 'code',
    audience: config.audience,
    scope: 'openid',
    code_challenge: codeChallenge,
    code_challenge_method: 'S256'
  };

  let resultUrl = await new Promise((resolve, reject) => {
    let queryString = new URLSearchParams(options).toString();
    let url = `https://${config.domain}/authorize?${queryString}`;
    console.log(url);
    chrome.identity.launchWebAuthFlow({
      url,
      interactive: true
    }, callbackUrl => {
      console.log(callbackUrl);
      resolve(callbackUrl);
    });
  });

  // We are now going to use that code to generate a token
  if (resultUrl) {
    const code = getParameterByName('code', resultUrl);
    console.log('code', code);

    const body = JSON.stringify({
      redirect_uri: redirectUrl,
      grant_type: 'authorization_code',
      client_id: config.clientId,
      code_verifier: verifier,
      code: code
    })
  
    // I couldn't get this working with fetch, so Axios it is.
    const result = await axios.post(`https://${config.domain}/oauth/token`, body, {
        headers: { 'Content-Type': 'application/json' }
    });
  
    console.log(result);

    if (
        result &&
        result.data &&
        result.data.access_token &&
        result.data.expires_in
    ) {

        console.log(result.data.access_token)
        console.log(result.data.expires_in)

        // keep the access_token somewhere can access from anywhere in the app. and reuse this token until it expires.
        // Can now make a network request
        console.log("Calling our api (Demo)")
        callOurApi(result.data.access_token)

    } else {
      console.log('Auth0 Authentication Data was invalid')
    }
  } else {
    console.log('Auth0 Cancelled or error. resultUrl', resultUrl)
  }
};
btnLogin.addEventListener("click", async function() {
    login();
});

btnLogout.addEventListener("click", async function() {
  await chrome.identity.clearAllCachedAuthTokens();
  // wipe access token from wherever you've kept it
});

3 Likes