Skip to content

Commit 8993881

Browse files
authored
migrate js sample from adal to msal (#68)
1 parent 49f055d commit 8993881

File tree

10 files changed

+417
-139
lines changed

10 files changed

+417
-139
lines changed

JavascriptWebAppSample/README.md

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,65 @@
11
# Javascript Web App Sample
22

3-
For javascript web applications that want access to resources like Azure DevOps REST API's, they will have to support an authentication flow for their users. The [Azure Active Directory Authentication Library (ADAL) for JS](https://github.com/AzureAD/azure-activedirectory-library-for-js) enables javascript application developers to setup inerative authentication flows and obtain access tokens for API usage.
3+
For javascript web applications that want access to resources like Azure DevOps REST API, they will have to support an authentication flow for their users. The [Microsoft Authentication Library for JavaScript](https://github.com/AzureAD/microsoft-authentication-library-for-js) (MSAL.js) enables javascript application developers to setup interactive authentication flows and obtain access tokens for API usage.
44

55
## Sample Application
66

7-
This buildable sample will walk you through the steps to create a single page javascript application which uses ADAL to authenticate a user via an interactive prompt and display all projects contained in a Azure DevOps account/TFS collection.
7+
This sample will walk you through the steps to create a single-page javascript application which uses **MSAL.js** to authenticate a user via an interactive prompt and display all projects contained in an Azure DevOps account/TFS collection.
88

99
To run this sample you will need:
10-
* Http-server. You can download [NPM http server](https://www.npmjs.com/package/http-server) if you need one.
11-
* An Azure Active Directory (AAD) tenant. If you do not have one, follow these [steps to set up an AAD](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-howto-tenant)
12-
* A user account in your AAD tenant
13-
* A Azure DevOps account backed by your AAD tenant where your user account has access. If you have an existing Azure DevOps account not connected to your AAD tenant follow these [steps to connect your AAD tenant to your Azure DevOps account](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/manage-azure-active-directory-groups-vsts?view=vsts&tabs=new-nav)
1410

15-
## Step 1: Clone or download vsts-auth-samples repository
11+
- [Node.js](https://nodejs.org/en/download/) must be installed to run this sample.
12+
- A modern web browser. This sample uses **ES6** conventions and will not run on **Internet Explorer**.
13+
- [Visual Studio Code](https://code.visualstudio.com/download) is recommended for running and editing this sample.
14+
- An Azure Active Directory (Azure AD) tenant. For more information on how to get an Azure AD tenant, see: [How to get an Azure AD tenant](https://azure.microsoft.com/documentation/articles/active-directory-howto-tenant/)
15+
- A user account in your Azure AD tenant.
16+
- An Azure DevOps account backed by your AAD tenant where your user account has access. If you have an existing Azure DevOps account not connected to your AAD tenant follow these [steps to connect your AAD tenant to your Azure DevOps account](https://docs.microsoft.com/azure/devops/organizations/accounts/manage-azure-active-directory-groups-vsts?view=vsts&tabs=new-nav)
1617

17-
From a shell or command line:
18-
```no-highlight
19-
git clone https://github.com/Microsoft/vsts-auth-samples.git
18+
## Step 1: Clone or download azure-devops-auth-samples repository
19+
20+
From a shell or command line:
21+
22+
```console
23+
git clone https://github.com/microsoft/azure-devops-auth-samples.git
24+
```
25+
26+
Then locate the sample folder, where `package.json` file resides. Once you do, type:
27+
28+
```console
29+
npm install
2030
```
2131

22-
## Step 2: Register the sample application with you Azure Active Directory tenant
32+
## Step 2: Register the app
33+
34+
1. Navigate to the Microsoft identity platform for developers [App registrations](https://go.microsoft.com/fwlink/?linkid=2083908) page.
35+
1. Select **New registration**.
36+
1. In the **Register an application page** that appears, enter your application's registration information:
37+
- In the **Name** section, enter a meaningful application name that will be displayed to users of the app, for example `devops-js-app`.
38+
- Under **Supported account types**, select **Accounts in your organizational directory only**.
39+
- In the **Redirect URI (optional)** section, select **Single-Page Application** in the combo-box and enter the following redirect URI: `http://localhost:8081/`.
40+
1. Select **Register** to create the application.
41+
1. In the app's registration screen, find and note the **Application (client) ID**. You use this value in your app's configuration file(s) later in your code.
42+
1. Select **Save** to save your changes.
2343

24-
1. Sign in to the [Azure Portal](https://portal.azure.com).
25-
2. On the top bar, click on your account and under the Directory list, choose the Active Directory tenant where you wish to register your application.
26-
3. On the left hand navigation menu, select `Azure Active Directory`.
27-
4. Click on `App registrations` and select `New application registration` from the top bar.
28-
5. Enter a `name` for you application, ex. "Adal JS sample", choose `Web app/API` for `application type`, and enter `http://localhost:8081` for the `Sign-on URL`. Finally click `create` at the bottom of the screen.
29-
6. Save the `Application ID` from your new application registration. You will need it later in this sample.
30-
7. Grant permissions for Azure DevOps. Click `Settings` -> `Required permissions` -> `add` -> `1 Select an API` -> type in and select `Azure DevOps (Microsoft Visual Studio Team Services)` -> check the box for `Delegated Permissions` -> click `Select` -> click `Done` -> click `Grant Permissions` -> click `Yes`.
31-
8. Click on `Manifest` -> set the value for `oauth2AllowImplicitFlow` to `true` -> click `Save`.
44+
## Step 3: Configure the app to use your app registration
3245

33-
## Step 3: Run the sample
46+
Open the project in your IDE (like Visual Studio or Visual Studio Code) to configure the code.
3447

35-
1. Open `index.html` in [Visual Studio Code](https://code.visualstudio.com/download) or another text editor or IDE.
36-
2. Inside `index.html` there is a section called `Input Vars` you will need to update to run the sample:
37-
* `clientId` - (Required) update this with the `application id` you saved from step 2.6 above
38-
* `replyUri` - (optional) In single page apps this should be the app URL itself. We have set this to `http://localhost:8081`(where we will host our app), but if you are hosting your app at another URL please change this value and the reply URI in `portal.azure.com`
39-
* `logoutredirectUri` - (optional) update if you are hosting your app at a location other than `http://localhost:8081`
40-
* `azureDevOpsApi` - (Required) update this with your Azure DevOps/TFS collection, e.g. http://dev.azure.com/organization/_apis/projects?api-version=4.0 for Azure DevOps or http://myserver:8080/tfs/DefaultCollection/_apis/projects?api-version=4.0 for TFS. If you would like the sample to run a different Azure DevOps API please change the entire string. [Learn more about other Azure DevOps REST API's](https://docs.microsoft.com/en-us/rest/api/vsts/?view=vsts-rest-4.1&viewFallbackFrom=vsts)
41-
* `azureDevOpsResourceId` - Do not change this value. It is used to receive Azure DevOps ADAL authentication tokens
42-
3. Navigate to the ADAL JS sample in cloned repo `vsts-auth-samples/JavascriptWebAppSample/` and start your http-server and set it to serve pages at `localhost:8081` which will by default serve `index.html` at `http://localhost:8081`.
43-
4. Navigate to `http://localhost:8081`. Sign in with a user account from your AAD tenant which has access to the VSTS account specified in the `azureDevOpsApi`. All projects contained in the account should be displayed.
48+
> In the steps below, "ClientID" is the same as "Application ID" or "AppId".
4449
50+
1. Open the `config.js` file
51+
1. Find the key `Enter_the_Application_Id_Here` and replace the existing value with the application ID (clientId) of the `devops-js-app` application copied from the Azure portal.
52+
1. Find the key `https://login.microsoftonline.com/Enter_the_Tenant_Info_Here` and replace the existing value with `https://login.microsoftonline.com/<your-tenant-id>`.
53+
1. Find the key `Enter_the_Redirect_Uri_Here` and replace the existing value with the base address of the `devops-js-app` project (by default `http://localhost:8081`).
4554

55+
## Running the sample
56+
57+
```console
58+
npm start
59+
```
4660

61+
## Explore the sample
4762

63+
1. Open your browser and navigate to `http://localhost:8081`.
64+
1. Click the **sign-in** button on the top right corner.
65+
1. Once signed-in, select the **Call DevOps Rest API** button at the center.

JavascriptWebAppSample/app.js

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Create the main myMSALObj instance
2+
// configuration parameters are located at config.js
3+
const myMSALObj = new msal.PublicClientApplication(msalConfig);
4+
5+
let username = "";
6+
7+
/**
8+
* A promise handler needs to be registered for handling the
9+
* response returned from redirect flow. For more information, visit:
10+
*
11+
*/
12+
myMSALObj.handleRedirectPromise()
13+
.then(handleResponse)
14+
.catch((error) => {
15+
console.error(error);
16+
});
17+
18+
function selectAccount() {
19+
20+
/**
21+
* See here for more info on account retrieval:
22+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
23+
*/
24+
25+
const currentAccounts = myMSALObj.getAllAccounts();
26+
27+
if (!currentAccounts || currentAccounts.length < 1) {
28+
return;
29+
} else if (currentAccounts.length > 1) {
30+
// Add your account choosing logic here
31+
console.warn("Multiple accounts detected.");
32+
} else if (currentAccounts.length === 1) {
33+
username = currentAccounts[0].username;
34+
welcomeUser(username);
35+
}
36+
}
37+
38+
function handleResponse(response) {
39+
40+
/**
41+
* To see the full list of response object properties, visit:
42+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#response
43+
*/
44+
45+
if (response !== null) {
46+
username = response.account.username;
47+
welcomeUser(username);
48+
} else {
49+
50+
selectAccount();
51+
52+
/**
53+
* If you already have a session that exists with the authentication server, you can use the ssoSilent() API
54+
* to make request for tokens without interaction, by providing a "login_hint" property. To try this, comment the
55+
* line above and uncomment the section below.
56+
*/
57+
58+
// myMSALObj.ssoSilent(silentRequest).
59+
// then(() => {
60+
// const currentAccounts = myMSALObj.getAllAccounts();
61+
// username = currentAccounts[0].username;
62+
// welcomeUser(username);
63+
// updateTable();
64+
// }).catch(error => {
65+
// console.error("Silent Error: " + error);
66+
// if (error instanceof msal.InteractionRequiredAuthError) {
67+
// signIn();
68+
// }
69+
// });
70+
}
71+
}
72+
73+
function signIn() {
74+
75+
/**
76+
* You can pass a custom request object below. This will override the initial configuration. For more information, visit:
77+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
78+
*/
79+
80+
myMSALObj.loginRedirect(loginRequest);
81+
}
82+
83+
function signOut() {
84+
85+
/**
86+
* You can pass a custom request object below. This will override the initial configuration. For more information, visit:
87+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
88+
*/
89+
90+
// Choose which account to logout from by passing a username.
91+
const logoutRequest = {
92+
account: myMSALObj.getAccountByUsername(username)
93+
};
94+
95+
myMSALObj.logout(logoutRequest);
96+
}
97+
98+
function getTokenRedirect(request) {
99+
/**
100+
* See here for more info on account retrieval:
101+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
102+
*/
103+
request.account = myMSALObj.getAccountByUsername(username);
104+
105+
return myMSALObj.acquireTokenSilent(request)
106+
.catch(error => {
107+
console.warn("silent token acquisition fails. acquiring token using redirect");
108+
if (error instanceof msal.InteractionRequiredAuthError) {
109+
// fallback to interaction when silent call fails
110+
return myMSALObj.acquireTokenRedirect(request);
111+
} else {
112+
console.warn(error);
113+
}
114+
});
115+
}
116+
117+
// Acquires and access token and then passes it to the API call
118+
function passTokenToApi() {
119+
getTokenRedirect(tokenRequest)
120+
.then(response => {
121+
callApiWithToken(apiConfig.endpoint, response.accessToken);
122+
}).catch(error => {
123+
console.error(error);
124+
});
125+
}
126+
127+
selectAccount();

JavascriptWebAppSample/config.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
2+
/**
3+
* Configuration object to be passed to MSAL instance on creation.
4+
* For a full list of MSAL.js configuration parameters, visit:
5+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md
6+
*/
7+
8+
const msalConfig = {
9+
auth: {
10+
clientId: "Enter_the_Application_Id_Here", // This is the ONLY mandatory field that you need to supply.
11+
authority: "https://login.microsoftonline.com/Enter_the_Tenant_Info_Here", // Defaults to "https://login.microsoftonline.com/common"
12+
redirectUri: "http://localhost:8081", // You must register this URI on Azure Portal/App Registration. Defaults to window.location.href
13+
postLogoutRedirectUri: "http://localhost:8081", // Simply remove this line if you would like navigate to index page after logout.
14+
navigateToLoginRequestUrl: false, // If "true", will navigate back to the original request location before processing the auth code response.
15+
},
16+
cache: {
17+
cacheLocation: "sessionStorage", // Configures cache location. "sessionStorage" is more secure, but "localStorage" gives you SSO btw tabs.
18+
storeAuthStateInCookie: false, // If you wish to store cache items in cookies as well as browser cache, set this to "true".
19+
},
20+
system: {
21+
loggerOptions: {
22+
loggerCallback: (level, message, containsPii) => {
23+
if (containsPii) {
24+
return;
25+
}
26+
switch (level) {
27+
case msal.LogLevel.Error:
28+
console.error(message);
29+
return;
30+
case msal.LogLevel.Info:
31+
console.info(message);
32+
return;
33+
case msal.LogLevel.Verbose:
34+
console.debug(message);
35+
return;
36+
case msal.LogLevel.Warning:
37+
console.warn(message);
38+
return;
39+
}
40+
}
41+
}
42+
}
43+
};
44+
45+
// Add here the endpoints for services you would like to use.
46+
const apiConfig = {
47+
endpoint: "https://dev.azure.com/msaltestingjs/_apis/projects?api-version=4.0",
48+
scopes: ["499b84ac-1321-427f-aa17-267ca6975798/.default"] // do not change this value
49+
};
50+
51+
/**
52+
* Scopes you add here will be prompted for user consent during sign-in.
53+
* By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
54+
* For more information about OIDC scopes, visit:
55+
* https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
56+
*/
57+
const loginRequest = {
58+
scopes: ["openid", "profile"],
59+
};
60+
61+
/**
62+
* Add here the scopes to request when obtaining an access token for a web API. For more information, see:
63+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
64+
*/
65+
const tokenRequest = {
66+
scopes: apiConfig.scopes,
67+
forceRefresh: false // Set this to "true" to skip a cached token and go to the server to get a new token
68+
};
69+
70+
/**
71+
* An optional silentRequest object can be used to achieve silent SSO
72+
* between applications by providing a "login_hint" property.
73+
*/
74+
75+
// const silentRequest = {
76+
// scopes: ["openid", "profile"],
77+
// loginHint: "[email protected]"
78+
// };

JavascriptWebAppSample/favicon.svg

Lines changed: 23 additions & 0 deletions
Loading

JavascriptWebAppSample/fetch.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Helper function to call web API endpoint
3+
* using the authorization bearer token scheme
4+
*/
5+
function callApiWithToken(endpoint, token, callback) {
6+
const headers = new Headers();
7+
const bearer = `Bearer ${token}`;
8+
9+
headers.append("Authorization", bearer);
10+
11+
const options = {
12+
method: "GET",
13+
headers: headers
14+
};
15+
16+
logMessage('Calling web API...');
17+
18+
fetch(endpoint, options)
19+
.then(response => response.json())
20+
.then(response => {
21+
logMessage('Web API responds:');
22+
logMessage(JSON.stringify(response.value[0], null, 4));
23+
}).catch(error => {
24+
console.error(error);
25+
});
26+
}

0 commit comments

Comments
 (0)