We can create a Single Page Apps (SPAs) which uses JavaScript to work with Common Data Service data. To provide this, Cross-Origin Resource Sharing (CORS) is enabled so that your SPAs can bypass browser restrictions that normally prevent requests that cross domain boundaries.
you can use the Azure Active Directory Authentication Library for JavaScript (adal.js) and it will take care of much of the CORS complexity for you. Since Common Data Service are authenticated using Azure Active Directory, ADAL.js is the supported way to authenticate SPA users.
Step – 1 Create new Empty Web Application in Visua Studio.

Step – 2 Add a new HTML page named SimpleSPA.html
to the project and paste in the following code:
<!DOCTYPE html>
<html>
<head>
<title>Simple SPA</title>
<meta charset="utf-8" />
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.17/js/adal.min.js"></script>
<script type="text/javascript">
"use strict";
//Set these variables to match your environment
var organizationURI = "https://infylearninfy.crm.dynamics.com"; //The URL of your Common Data Service organization
var tenant = "f629b38b-d95b-4d46-81d7-192aa10400ae"; //The name of the Azure AD organization you use
var clientId = "2370bdb3-742b-40f4-998e-f0f9fc34a8c5"; //The ClientId you got when you registered the application
var pageUrl = "https://localhost:44396/SimpleSPA.html"; //The URL of this page in your development environment when debugging.
var user, authContext, message, errorMessage, loginButton, logoutButton, getAccountsButton, accountsTable, accountsTableBody;
//Configuration data for AuthenticationContext
var endpoints = {
orgUri: organizationURI
};
window.config = {
tenant: tenant,
clientId: clientId,
postLogoutRedirectUri: pageUrl,
endpoints: endpoints,
cacheLocation: 'localStorage',
};
document.onreadystatechange = function () {
if (document.readyState == "complete") {
//Set DOM elements referenced by scripts
message = document.getElementById("message");
errorMessage = document.getElementById("errorMessage");
loginButton = document.getElementById("login");
logoutButton = document.getElementById("logout");
getAccountsButton = document.getElementById("getAccounts");
accountsTable = document.getElementById("accountsTable");
accountsTableBody = document.getElementById("accountsTableBody");
//Event handlers on DOM elements
loginButton.addEventListener("click", login);
logoutButton.addEventListener("click", logout);
getAccountsButton.addEventListener("click", getAccounts);
//call authentication function
authenticate();
if (user) {
loginButton.style.display = "none";
logoutButton.style.display = "block";
getAccountsButton.style.display = "block";
var helloMessage = document.createElement("p");
helloMessage.textContent = "Hello " + user.profile.name;
message.appendChild(helloMessage)
}
else {
loginButton.style.display = "block";
logoutButton.style.display = "none";
getAccountsButton.style.display = "none";
}
}
}
// Function that manages authentication
function authenticate() {
//OAuth context
authContext = new AuthenticationContext(config);
// Check For & Handle Redirect From AAD After Login
var isCallback = authContext.isCallback(window.location.hash);
if (isCallback) {
authContext.handleWindowCallback();
}
var loginError = authContext.getLoginError();
if (isCallback && !loginError) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
else {
errorMessage.textContent = loginError;
}
user = authContext.getCachedUser();
}
//function that logs in the user
function login() {
authContext.login();
}
//function that logs out the user
function logout() {
authContext.logOut();
accountsTable.style.display = "none";
accountsTableBody.innerHTML = "";
}
//function that initiates retrieval of accounts
function getAccounts() {
getAccountsButton.disabled = true;
var retrievingAccountsMessage = document.createElement("p");
retrievingAccountsMessage.textContent = "Retrieving 10 accounts from " + organizationURI + "/api/data/v9.1/accounts";
message.appendChild(retrievingAccountsMessage)
// Function to perform operation is passed as a parameter to the acquireToken method
authContext.acquireToken(organizationURI, retrieveAccounts)
}
//Function that actually retrieves the accounts
function retrieveAccounts(error, token) {
// Handle ADAL Errors.
if (error || !token) {
errorMessage.textContent = 'ADAL error occurred: ' + error;
return;
}
var req = new XMLHttpRequest()
req.open("GET", encodeURI(organizationURI + "/api/data/v9.1/accounts?$select=name,address1_city&$top=10"), true);
//Set Bearer token
req.setRequestHeader("Authorization", "Bearer " + token);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
var accounts = JSON.parse(this.response).value;
renderAccounts(accounts);
}
else {
var error = JSON.parse(this.response).error;
console.log(error.message);
errorMessage.textContent = error.message;
}
}
};
req.send();
}
//Function that writes account data to the accountsTable
function renderAccounts(accounts) {
accounts.forEach(function (account) {
var name = account.name;
var city = account.address1_city;
var nameCell = document.createElement("td");
nameCell.textContent = name;
var cityCell = document.createElement("td");
cityCell.textContent = city;
var row = document.createElement("tr");
row.appendChild(nameCell);
row.appendChild(cityCell);
accountsTableBody.appendChild(row);
});
accountsTable.style.display = "block";
}
</script>
<style>
body {
font-family: 'Segoe UI';
}
table {
border-collapse: collapse;
}
td, th {
border: 1px solid black;
}
#errorMessage {
color: red;
}
#message {
color: green;
}
</style>
</head>
<body>
<button id="login">Login</button>
<button id="logout" style="display:none;">Logout</button>
<button id="getAccounts" style="display:none;">Get Accounts</button>
<div id="errorMessage"></div>
<div id="message"></div>
<table id="accountsTable" style="display:none;">
<thead><tr><th>Name</th><th>City</th></tr></thead>
<tbody id="accountsTableBody"></tbody>
</table>
</body>
</html>
Step – 3 Register Application in Azure AD. Follow steps defined here.

Redirect URL
This is the URL which the user should be redirected to after they sign in. Select Web from the drop down list. For debugging purposes in Visual Studio it should be https://localhost:####/SimpleSPA.html
where #### represents the port number
Step – 4 Replace Placeholder value with your Application and CRM instance details
//Set these variables to match your environment
var organizationURI = “https://infylearninfy.crm.dynamics.com”; //The URL of your Common Data Service organization
var tenant = “f629b38b-d95b-4d46-81d7-192aa10400ae”; //The name of the Azure AD organization you use
var clientId = “2370bdb3-742b-40f4-998e-f0f9fc34a8c5”; //The ClientId you got when you registered the application
var pageUrl = “https://localhost:44396/SimpleSPA.html”; //The URL of this page in your development environment when debugging.
Step – 5 Enable ID tokens in Azure AD App Registration Authentication

Step – 6 Run Your application and once you login, You will see Top 10 Accounts


Hope this helps!