Skip to content

Commit ee82564

Browse files
authored
Merge pull request #56 from Azure-Samples/migrate-sample
Migrate B2C user management sample
2 parents 1f1c52a + 11b7ca8 commit ee82564

File tree

22 files changed

+3016
-4952
lines changed

22 files changed

+3016
-4952
lines changed

.github/workflows/node.js.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ jobs:
4343
npm audit --production
4444
npm run test
4545
46+
- run: |
47+
cd 2-Authorization-I/2-call-graph-b2c/
48+
npm ci
49+
npm audit --production
50+
npm run test
51+
4652
- run: |
4753
cd 3-Authorization-II/1-call-api/API
4854
npm ci
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Configuration object to be passed to MSAL instance on creation.
3+
* For a full list of MSAL.js configuration parameters, visit:
4+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md
5+
*/
6+
const msalConfig = {
7+
auth: {
8+
clientId: "Enter_the_Client_Id_Here", // This is the ONLY mandatory field that you need to supply.
9+
authority: "https://login.microsoftonline.com/Enter_the_Tenant_Id_Here",
10+
redirectUri: "http://localhost:3000", // You must register this URI on Azure Portal/App Registration.
11+
},
12+
cache: {
13+
cacheLocation: "localStorage", // This configures where your cache will be stored
14+
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
15+
},
16+
};
17+
18+
/**
19+
* Scopes you add here will be prompted for user consent during sign-in.
20+
* By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
21+
* For more information about OIDC scopes, visit:
22+
* https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
23+
*/
24+
const loginRequest = {
25+
scopes: [ "openid", "offline_access" ]
26+
};
27+
28+
/**
29+
* Add here the scopes to request when obtaining an access token for MS Graph API. For more information, see:
30+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
31+
*/
32+
const tokenRequest = {
33+
scopes: [ "User.ReadWrite.All" ]
34+
};
35+
36+
// Add here the endpoints for MS Graph API services you would like to use.
37+
const graphConfig = {
38+
graphUsersEndpoint: "https://graph.microsoft.com/v1.0/users"
39+
};
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Create the main MSAL instance
2+
// configuration parameters are located at authConfig.js
3+
const pca = new msal.PublicClientApplication(msalConfig);
4+
5+
let username = "";
6+
let accountId = "";
7+
8+
function selectAccount() {
9+
10+
/**
11+
* See here for more info on account retrieval:
12+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
13+
*/
14+
15+
const currentAccounts = pca.getAllAccounts();
16+
if (currentAccounts === null) {
17+
return;
18+
} else if (currentAccounts.length > 1) {
19+
// Add choose account code here
20+
console.warn("Multiple accounts detected.");
21+
} else if (currentAccounts.length === 1) {
22+
accountId = currentAccounts[0].homeAccountId;
23+
username = currentAccounts[0].username;
24+
showWelcomeMessage(username);
25+
}
26+
}
27+
28+
function handleResponse(response) {
29+
30+
/**
31+
* To see the full list of response object properties, visit:
32+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#response
33+
*/
34+
35+
if (response !== null) {
36+
username = response.account.username;
37+
accountId = response.account.homeAccountId;
38+
showWelcomeMessage(username);
39+
} else {
40+
selectAccount();
41+
}
42+
}
43+
44+
function signIn() {
45+
46+
/**
47+
* You can pass a custom request object below. This will override the initial configuration. For more information, visit:
48+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
49+
*/
50+
51+
pca.loginPopup(loginRequest)
52+
.then(handleResponse)
53+
.catch(error => {
54+
console.error(error);
55+
});
56+
}
57+
58+
function signOut() {
59+
60+
/**
61+
* You can pass a custom request object below. This will override the initial configuration. For more information, visit:
62+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
63+
*/
64+
65+
// Choose which account to logout from by passing a username.
66+
67+
const logoutRequest = {
68+
account: pca.getAccountByHomeId(accountId),
69+
postLogoutRedirectUri: msalConfig.auth.redirectUri,
70+
mainWindowRedirectUri: msalConfig.auth.redirectUri
71+
};
72+
73+
pca.logoutPopup(logoutRequest);
74+
}
75+
76+
async function getTokenPopup(request) {
77+
78+
/**
79+
* See here for more info on account retrieval:
80+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
81+
*/
82+
request.account = pca.getAccountByHomeId(accountId);
83+
84+
return pca.acquireTokenSilent(request)
85+
.then((response) => {
86+
// In case the response from B2C server has an empty accessToken field
87+
// throw an error to initiate token acquisition
88+
if (!response.accessToken || response.accessToken === "") {
89+
throw new msal.InteractionRequiredAuthError;
90+
}
91+
return response;
92+
})
93+
.catch(error => {
94+
console.log(error);
95+
console.log("silent token acquisition fails. acquiring token using popup");
96+
if (error instanceof msal.InteractionRequiredAuthError) {
97+
// fallback to interaction when silent call fails
98+
return pca.acquireTokenPopup(request)
99+
.then(response => {
100+
console.log(response);
101+
return response;
102+
}).catch(error => {
103+
console.log(error);
104+
});
105+
} else {
106+
console.log(error);
107+
}
108+
});
109+
}
110+
111+
selectAccount();
112+
113+
class MyAuthenticationProvider {
114+
115+
/**
116+
* This method will get called before every request to the msgraph server
117+
* This should return a Promise that resolves to an accessToken (in case of success) or rejects with error (in case of failure)
118+
* Basically this method will contain the implementation for getting and refreshing accessTokens
119+
*/
120+
121+
async getAccessToken() {
122+
return new Promise(async (resolve, reject) => {
123+
const authResponse = await getTokenPopup(tokenRequest);
124+
125+
if (authResponse.accessToken && authResponse.accessToken.length !== 0) {
126+
resolve(authResponse.accessToken);
127+
} else {
128+
reject(Error("Error: cannot obtain access token."));
129+
}
130+
});
131+
}
132+
}
Lines changed: 23 additions & 0 deletions
Loading
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const clientOptions = {
2+
authProvider: new MyAuthenticationProvider(),
3+
};
4+
5+
const client = MicrosoftGraph.Client.initWithMiddleware(clientOptions);
6+
7+
async function getUsers() {
8+
try {
9+
console.log('Graph API called at: ' + new Date().toString());
10+
return await client.api('/users').get();
11+
} catch (error) {
12+
console.log(error);
13+
return error;
14+
}
15+
}
16+
17+
async function deleteUser(id) {
18+
try {
19+
console.log('Graph API called at: ' + new Date().toString());
20+
return await client.api(`/users/${id}`).delete();
21+
} catch (error) {
22+
console.log(error);
23+
return error;
24+
}
25+
}
26+
27+
async function createUser(user) {
28+
try {
29+
console.log('Graph API called at: ' + new Date().toString());
30+
return await client.api('/users').post(user);
31+
} catch (error) {
32+
console.log(error);
33+
return error;
34+
}
35+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
7+
<title>Microsoft identity platform</title>
8+
<link rel="SHORTCUT ICON" href="./favicon.svg" type="image/x-icon">
9+
10+
<!-- msal.min.js can be used in the place of msal.js; included msal.js to make debug easy -->
11+
<script src="https://alcdn.msauth.net/browser/2.13.1/js/msal-browser.js"
12+
integrity="sha384-7hwr87O1w6buPsX92CwuRaz/wQzachgOEq+iLHv0ESavynv6rbYwKImSl7wUW3wV"
13+
crossorigin="anonymous"></script>
14+
15+
<!-- To help ensure reliability, Microsoft provides a second CDN -->
16+
<script type="text/javascript">
17+
if (typeof Msal === 'undefined') document.write(unescape("%3Cscript src='https://alcdn.msftauth.net/browser/2.13.1/js/msal-browser.js' type='text/javascript' crossorigin='anonymous' %3E%3C/script%3E"));
18+
</script>
19+
20+
<!-- import Graph SDK for JavaScript -->
21+
<script type="text/javascript"
22+
src="https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-js-sdk.js"></script>
23+
24+
<!-- adding Bootstrap 4 for UI components -->
25+
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
26+
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
27+
<link rel="SHORTCUT ICON" href="https://c.s-microsoft.com/favicon.ico?v2" type="image/x-icon">
28+
</head>
29+
30+
<body>
31+
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
32+
<a class="navbar-brand" href="/">Microsoft identity platform</a>
33+
<div class="btn-group ml-auto dropleft">
34+
<button type="button" id="signIn" class="btn btn-secondary" onclick="signIn()">
35+
Sign in
36+
</button>
37+
</div>
38+
</nav>
39+
<br>
40+
<h5 class="card-header text-center">Vanilla JavaScript SPA calling MS Graph API with MSAL.js</h5>
41+
<br>
42+
<div class="row" style="margin:auto">
43+
<div id="card-div" class="col-md-3" style="display:none">
44+
<div class="card text-center">
45+
<div class="card-body">
46+
<h5 class="card-title" id="WelcomeMessage">Please sign-in to manage your users</h5>
47+
<div id="profile-div"></div>
48+
<br>
49+
<br>
50+
<button class="btn btn-primary" id="getUsers">Get Users</button>
51+
<button class="btn btn-primary" id="addUsers">Add Users</button>
52+
</div>
53+
</div>
54+
</div>
55+
<br>
56+
<br>
57+
<div class="list-group col-md-4" id="list-tab" role="tablist">
58+
</div>
59+
<div class="tab-content col-md-5" id="nav-tabContent">
60+
</div>
61+
<div class="col-md-6" id="form-div" style="display:none">
62+
<div class="form-group">
63+
<input class="form-control" id="displayName" placeholder="Display Name">
64+
<input class="form-control" id="userPrincipalName" placeholder="User Principal Name">
65+
<input class="form-control" id="password" placeholder="Password">
66+
</div>
67+
<button type="submit" class="btn btn-primary" id="submitButton">Submit</button>
68+
</div>
69+
<br>
70+
<br>
71+
72+
<p id="survey" style="display:none; position: fixed; left: 0; bottom: 0; width: 100%; text-align: center;">How did
73+
we do?
74+
<a href="https://forms.office.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR73pcsbpbxNJuZCMKN0lURpUOUlINE03TVo4TEM4MVRGUUQ2TlBRUUFBSCQlQCN0PWcu"
75+
target="_blank">Share your experience with us!</a>
76+
</p>
77+
78+
<!-- importing bootstrap.js and supporting js libraries -->
79+
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
80+
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
81+
crossorigin="anonymous"></script>
82+
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
83+
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
84+
crossorigin="anonymous"></script>
85+
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
86+
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
87+
crossorigin="anonymous"></script>
88+
89+
<!-- importing app scripts (load order is important) -->
90+
<script type="text/javascript" src="./authConfig.js"></script>
91+
<script type="text/javascript" src="./ui.js"></script>
92+
<script type="text/javascript" src="./authProvider.js"></script>
93+
<script type="text/javascript" src="./graph.js"></script>
94+
</body>
95+
96+
</html>

0 commit comments

Comments
 (0)