10.10 Example code listing
The following is the full listing of the final example in this chapter.
To use this on your own system, you must edit the following:
Copy
// Address of the MyID server
const server = "https://myserver";
// The URL for this page - this must match a value for RedirectUris
// in the web.oauth2 appsettings file.
const redirect_uri = "http://127.0.0.1:5500/";
-
Set the server option to the URL of your MyID server.
-
Set the redirect_uri to the URL of this sample web page on your local web server; if you are using the Live Server extension for Visual Studio Code, this is:
http://127.0.0.1:5500/
Copy
<html>
<head>
<title>Single-page PKCE client authentication through Javascript</title>
</head>
<body>
<div id="intro">
<p>This single-page example uses client authentication to obtain an
access token and call the API.</p>
</div>
<div id="login">
<p>Click <b>Login</b> to authenticate to the MyID server using
your client credentials.</p>
</div>
<script>
// Address of the MyID server
const server = "https://myserver";
// Name of the client ID you have set up in the web.oauth2 appsettings file
const client_id = "myid.mywebsite";
// Scope configured for the client
const scope = "myid.rest.basic";
// The URL for this page - this must match a value for RedirectUris
// in the web.oauth2 appsettings file.
const redirect_uri = "http://127.0.0.1:5500/";
// MyID oauth2 authorization URL
const authorize_url = "/web.oauth2/connect/authorize";
// MyID oauth2 token URL
const token_url = "/web.oauth2/connect/token";
// Set up the HTTP requests
// The request object is used to obtain an access token
var request = new XMLHttpRequest();
// The call_api object is used to call the API
var call_api = new XMLHttpRequest();
// The display_person object is used to get info about a person
var display_person = new XMLHttpRequest();
// Get the authorization code from the URL parameters.
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const code = urlParams.get('code');
// If the code is available, use it to obtain an access token
if (code) {
getAccessToken();
}
else {
// Create the form
createForm();
}
function getAccessToken() {
// Use the authorization code to obtain an access code
// This retrieves the code verifier from the browser's session storage
request.open("POST", server + token_url, true);
request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
request.send('client_id=' + client_id +
// Obtain the code_verifier from the session storage
'&code_verifier=' + window.sessionStorage.getItem("code_verifier") +
'&grant_type=authorization_code&redirect_uri=' + redirect_uri +
'&code=' + code);
document.getElementById("login").innerHTML="Working...";
}
// When the access token is returned, obtain it from the response.
request.onload = function() {
let response = JSON.parse(request.responseText);
let access_token = response.access_token;
window.sessionStorage.setItem("access_token", access_token);
getPeople();
}
function createForm() {
// Create the form
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("enctype", "application/x-www-form-urlencoded");
form.setAttribute("action", server + authorize_url);
// Create the hidden fields
var client_id_element = createFormField("hidden", "client_id", client_id);
var scope_element = createFormField("hidden", "scope", scope);
var redirect_uri_element = createFormField("hidden", "redirect_uri", redirect_uri);
var response_type_element = createFormField("hidden", "response_type", "code");
var code_challenge_element = createChallengeField();
var code_challenge_method_element = createFormField("hidden", "code_challenge_method", "S256");
// Create a Login button
var submit_button = document.createElement("input");
submit_button.setAttribute("type", "submit");
submit_button.setAttribute("value", "Login");
// Append the fields to the form
form.appendChild(client_id_element);
form.appendChild(scope_element);
form.appendChild(redirect_uri_element);
form.appendChild(response_type_element);
form.appendChild(code_challenge_element);
form.appendChild(code_challenge_method_element);
// Append the Login button to the form
form.appendChild(submit_button);
// Add the form to the login div
document.getElementById("login").appendChild(form);
}
function createFormField(type, name, value) {
var formFieldElement = document.createElement("input");
formFieldElement.setAttribute("type", type);
formFieldElement.setAttribute("name", name);
formFieldElement.setAttribute("value", value);
return formFieldElement;
}
// Create a function to generate a random code verifier string
function generateRandomString(length) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
// Create an asynchronous function to generate a code challenge from the code verifier
async function generateCodeChallenge(code_verifier) {
var digest = await crypto.subtle.digest("SHA-256",
new TextEncoder().encode(code_verifier));
return btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
}
function createChallengeField() {
// Create the code challenge element for the form
var code_challenge_element = document.createElement("input");
code_challenge_element.setAttribute("type", "hidden");
code_challenge_element.setAttribute("name", "code_challenge");
// Create a random string to use as the code verifier
var code_verifier = generateRandomString(128);
// Store the code verifier to use later
window.sessionStorage.setItem("code_verifier", code_verifier);
// Generate the code challenge from the asyncronous function
(async () => {
const code_challenge = await generateCodeChallenge(code_verifier);
code_challenge_element.setAttribute("value", code_challenge);
})();
// Return the code challenge form element
return code_challenge_element;
}
function getPeople() {
call_api.open("GET", server + "/rest.core/api/People?q=*", true);
call_api.setRequestHeader('Authorization', "Bearer " + window.sessionStorage.getItem("access_token"));
call_api.send();
}
call_api.onload = function() {
let response = JSON.parse(call_api.responseText);
message = "<p>List of people:</p><ul>";
for (key in response.results) {
message += "<li style=\"cursor:pointer;\" onclick=\"displayPerson(\'" +
response.results[key].id + "\')\">" +
response.results[key].firstName + " " +
response.results[key].surname + "</li>";
}
message += "</ul>";
message += "<p style=\"cursor:pointer;\" onclick=\'logout()\'\"><b>Logout</b></p>"
document.getElementById("login").innerHTML = message;
}
function displayPerson(personID) {
display_person.open("GET", server + "/rest.core/api/People/" + personID, true);
display_person.setRequestHeader('Authorization', "Bearer " + window.sessionStorage.getItem("access_token"));
display_person.send();
}
display_person.onload = function() {
let response = JSON.parse(display_person.responseText);
message = "<p><b>Name:</b> " + response.name.fullName + "</p>";
message += "<p><b>Group:</b> " + response.group.name + "</p>";
message += "<p><b>Logon Name:</b> " + response.logonName + "</p>";
message += "<p><b>Roles:</b></p><ul>";
for (key in response.roles) {
message += "<li>" + response.roles[key].name + "</li>"
}
message += "</ul><p style=\"cursor:pointer;\" onclick=\'getPeople()\'\"><b> < Back</b></p>"
document.getElementById("login").innerHTML = message;
}
logout = function() {
window.sessionStorage.setItem("access_token", "");
window.sessionStorage.setItem("code_verifier", "");
window.location.href="/";
}
</script>
</body>
</html>