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
});