Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Facebook sample app #6220

Merged
merged 11 commits into from
Jul 21, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# MSAL.js Sample - Authorization Code Flow for Non-Microsoft Identity Providers

## About this sample
This developer sample is used to demonstrate how to configure an app for use with non-Microsoft identity providers by using Facebook as an example.

## How to run the sample:
- Replace client ID with the app ID from the basic settings page of the app registration on the [Meta for Developers page](https://developers.facebook.com/).

## Important changes when using non-Microsoft identity providers
- You must change the authority in the config to an authority supported by the identity provider. Additionally, for non-Microsoft authorities, you must add the authority (without the https://) to the knownAuthorities parameter in the config.
- You must set the protocol mode to OIDC.
- You have the option of configuring different authentication options when using OIDC protocol mode. These are set in the OIDCOptions parameter.
- The `serverResponseType` parameter sets the format supported by MSAL for requests and server responses. Supported options are:
- `ServerResponseType.FRAGMENT` (for a hash fragment). If not set, MSAL defaults to `ServerResponseType.FRAGMENT`. If supported by the identity provider, we highly recommend using `ServerResponseType.FRAGMENT`.
- `ServerResponseType.QUERY` (for a query parameter). We recommend using `ServerResponseType.QUERY` only if the identity provider does not support sending server responses in a hash fragment.
- The `defaultScopes` parameter provides the option to override the default scopes sent by MSAL. If not set, it defaults to ["openid", "profile", "offline_access"]. If `defaultScopes` does not include "openid", MSAL will automatically add it for OIDC compliance.
- You have the option to manually configure endpoints used by the identity provider. If not set, MSAL will attempt to discover the endpoints. Manual endpoint configuration should be passed in a stringified JSON object and include issuer, authorization_endpoint, token_endpoint, jwks_uri, and (if available) end_session_endpoint.
- You must set allowNativeBroker to false when using a non-Microsoft identity provider.
shylasummers marked this conversation as resolved.
Show resolved Hide resolved
- If the identity provider does not have an end_session_endpoint, MSAL will not automatically redirect the page upon logout.
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
let signInType;
let accountId = "";

// Create the main myMSALObj instance
// configuration parameters are located at authConfig.js
const myMSALObj = new msal.PublicClientApplication(msalConfig);

myMSALObj.initialize().then(() => {
// Redirect: once login is successful and redirects with tokens, call Graph API
myMSALObj.handleRedirectPromise().then(handleResponse).catch(err => {
console.error(err);
});
})

function handleResponse(resp) {
if (resp !== null) {
accountId = resp.account.homeAccountId;
myMSALObj.setActiveAccount(resp.account);
showWelcomeMessage(resp.account);
} else {
// need to call getAccount here?
const currentAccounts = myMSALObj.getAllAccounts();
if (!currentAccounts || currentAccounts.length < 1) {
return;
} else if (currentAccounts.length > 1) {
// Add choose account code here
} else if (currentAccounts.length === 1) {
const activeAccount = currentAccounts[0];
myMSALObj.setActiveAccount(activeAccount);
accountId = activeAccount.homeAccountId;
showWelcomeMessage(activeAccount);
}
}
}

async function signIn(method) {
signInType = method;
if (signInType === "popup") {
return myMSALObj.loginPopup({
...loginRequest,
redirectUri: "/redirect"
}).then(handleResponse).catch(function (error) {
console.log(error);
});
} else if (signInType === "redirect") {
return myMSALObj.loginRedirect(loginRequest)
}
}

function signOut(interactionType) {
const logoutRequest = {
account: myMSALObj.getAccountByHomeId(accountId)
};

if (interactionType === "popup") {
myMSALObj.logoutPopup(logoutRequest).then(() => {
window.location.reload();
});
} else {
myMSALObj.logoutRedirect(logoutRequest);
window.location.reload();
}
}

async function getTokenPopup(request, account) {
request.redirectUri = "/redirect"
return await myMSALObj
.acquireTokenSilent(request)
.catch(async (error) => {
console.log("silent token acquisition fails.");
if (error instanceof msal.InteractionRequiredAuthError) {
console.log("acquiring token using popup");
return myMSALObj.acquireTokenPopup(request).catch((error) => {
console.error(error);
});
} else {
console.error(error);
}
});
}

async function getTokenRedirect(request, account) {
return await myMSALObj.acquireTokenSilent(request).catch(async (error) => {
console.log("silent token acquisition fails.");
if (error instanceof msal.InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
console.log("acquiring token using redirect");
myMSALObj.acquireTokenRedirect(request);
} else {
console.error(error);
}
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Config object to be passed to Msal on creation
const msalConfig = {
auth: {
clientId: "ENTER_CLIENT_ID_HERE",
authority: "https://facebook.com",
knownAuthorities: ["facebook.com"],
protocolMode: msal.ProtocolMode.OIDC,
OIDCOptions: { "serverResponseType": msal.ServerResponseType.QUERY, "defaultScopes": ["openid"] },
authorityMetadata: '{ "issuer": "https://www.facebook.com", "authorization_endpoint": "https://facebook.com/dialog/oauth/", "token_endpoint": "https://graph.facebook.com/oauth/access_token", "jwks_uri": "https://www.facebook.com/.well-known/oauth/openid/jwks/" }',
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false,
},
system: {
allowNativeBroker: false,
shylasummers marked this conversation as resolved.
Show resolved Hide resolved
loggerOptions: {
logLevel: msal.LogLevel.Trace,
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case msal.LogLevel.Error:
console.error(message);
return;
case msal.LogLevel.Info:
console.info(message);
return;
case msal.LogLevel.Verbose:
console.debug(message);
return;
case msal.LogLevel.Warning:
console.warn(message);
return;
default:
console.log(message);
return;
}
},
},
},
telemetry: {
application: {
appName: "MSAL Browser V2 Default Sample",
appVersion: "1.0.0",
},
},
}
};

// Add here scopes for id token to be used at MS Identity Platform endpoints.
const loginRequest = {
scopes: ['openid']
};

// Add here the endpoints for FB Graph API services you would like to use.
const graphConfig = {
graphMeEndpoint: "https://graph.facebook.com/v17.0/me?fields=id,name,email" //you have to add the fields at the end of the url
};

// Add here scopes for access token to be used at MS Graph API endpoints.
const tokenRequest = {
scopes: ['openid'],
forceRefresh: false // Set this to "true" to skip a cached token and go to the server to get a new token
};

const silentRequest = {
scopes: ['openid']
};

const logoutRequest = {}
shylasummers marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Helper function to call MS Graph API endpoint
// using authorization bearer token scheme
function callMSGraph(endpoint, accessToken, callback) {
const headers = new Headers();
const bearer = `Bearer ${accessToken}`;

headers.append("Authorization", bearer);

const options = {
method: "GET",
headers: headers
};

console.log('request made to Graph API at: ' + new Date().toString());

fetch(endpoint, options)
.then(response => response.json())
.then(response => callback(response, endpoint))
.catch(error => console.log(error));
}

async function seeProfile() {
const currentAcc = myMSALObj.getAccountByHomeId(accountId);
if (currentAcc) {
const response = await getTokenPopup(loginRequest, currentAcc).catch(error => {
console.log(error);
});
callMSGraph(graphConfig.graphMeEndpoint, response.accessToken, updateUI);
profileButton.style.display = 'none';
}
}

async function seeProfileRedirect() {
const currentAcc = myMSALObj.getAccountByHomeId(accountId);
if (currentAcc) {
const response = await getTokenRedirect(loginRequest, currentAcc).catch(error => {
console.log(error);
});
callMSGraph(graphConfig.graphMeEndpoint, response.accessToken, updateUI);
profileButton.style.display = 'none';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Quickstart | MSAL.JS Vanilla JavaScript SPA</title>

<script src="../../lib/msal-browser.js" type="text/javascript"></script>

<!-- adding Bootstrap 4 for UI components -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="SHORTCUT ICON" href="https://c.s-microsoft.com/favicon.ico?v2" type="image/x-icon">
</head>

<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand" href="/">MS Identity Platform</a>
<div class="btn-group ml-auto dropleft">
<button type="button" id="SignIn" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
Sign In
</button>
<div class="dropdown-menu">
<button class="dropdown-item" id="popup" onclick="signIn(this.id)">Sign in using Popup</button>
<button class="dropdown-item" id="redirect" onclick="signIn(this.id)">Sign in using Redirect</button>
</div>
</div>
</nav>
<br>
<h5 class="card-header text-center">Vanilla JavaScript SPA calling MS Graph API with MSAL.JS</h5>
<br>
<div class="row" style="margin:auto">
<div id="card-div" class="col-md-3" style="display:none">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title" id="WelcomeMessage">Please sign-in to see your profile</h5>
<div id="profile-div"></div>
<br>
<br>
<button class="btn btn-primary" id="seeProfile" onclick="seeProfile()">See Profile</button>
</div>
</div>
</div>
<br>
<br>
<div class="col-md-4">
<div class="list-group" id="list-tab" role="tablist">
</div>
</div>
<div class="col-md-5">
<div class="tab-content" id="nav-tabContent">
</div>
</div>
</div>
<br>
<br>

<!-- importing bootstrap.js and supporting js libraries -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
crossorigin="anonymous"></script>

<!-- importing app scripts | load order is important -->
<script type="text/javascript" src="./authConfig.js"></script>
<script type="text/javascript" src="./ui.js"></script>
<script type="text/javascript" src="./auth.js"></script>
<script type="text/javascript" src="./graph.js"></script>
</body>

</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!--
Blank page for redirect purposes. When using popup and silent APIs,
we recommend setting the redirectUri to a blank page or a page that does not implement MSAL.
For more information, please follow this link:
https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/login-user.md#redirecturi-considerations
-->
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Select DOM elements to work with
const welcomeDiv = document.getElementById("WelcomeMessage");
const signInButton = document.getElementById("SignIn");
const popupButton = document.getElementById("popup");
const redirectButton = document.getElementById("redirect");
const cardDiv = document.getElementById("card-div");
const profileButton = document.getElementById("seeProfile");
const profileDiv = document.getElementById("profile-div");

function showWelcomeMessage(account) {
// Reconfiguring DOM elements
cardDiv.style.display = 'initial';
welcomeDiv.innerHTML = `Welcome ${account.name}`;
signInButton.setAttribute('class', "btn btn-success dropdown-toggle");
signInButton.innerHTML = "Sign Out";
popupButton.setAttribute('onClick', "signOut(this.id)");
popupButton.innerHTML = "Sign Out with Popup";
redirectButton.setAttribute('onClick', "signOut(this.id)");
redirectButton.innerHTML = "Sign Out with Redirect";
}

function updateUI(data, endpoint) {
console.log('Graph API responded at: ' + new Date().toString());

if (endpoint === graphConfig.graphMeEndpoint) {
const title = document.createElement('p');
title.innerHTML = "<strong>Name: </strong>" + data.name;
const email = document.createElement('p');
email.innerHTML = "<strong>Mail: </strong>" + data.email;
profileDiv.appendChild(title);
profileDiv.appendChild(email);
}
}
Loading