Skip to content

Commit

Permalink
Converted Certificate Sample to TypeScript (#7368)
Browse files Browse the repository at this point in the history
This is the first time we'll have an E2E test running on a TypeScript
sample. They all currently run on JS samples.
  • Loading branch information
Robbie-Microsoft authored Nov 18, 2024
1 parent 2b750fb commit 1a82bfb
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 153 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 41 additions & 0 deletions samples/msal-node-samples/cliArgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import yargs from "yargs";

interface Arguments {
c: string;
p: number;
r: string | undefined;
s: string;
$0: string;
}

const argv: Arguments = yargs(process.argv.slice(2))
.usage("Usage: $0 -p [PORT]")
.options({
c: {
type: "string",
alias: "cache location",
default: "data/cache.json",
description:
"(Optional) Cache location - default is data/cache.json",
},
p: {
type: "number",
alias: "port",
default: 3000,
description: "(Optional) Port Number - default is 3000",
},
r: {
alias: "region",
default: undefined,
description: "(Optional) Region - default is undefined",
},
s: {
type: "string",
alias: "scenario",
default: "AAD",
description: "(Optional) Scenario name - default is AAD",
},
})
.parseSync();

export default argv;
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ This sample demonstrates how to implement an MSAL Node [confidential client appl

The **Client Credentials** flow is most commonly used for a daemon or a command-line app that calls web APIs and does not have any user interaction.

MSAL Node also supports specifying a **regional authority** for acquiring tokens when using the client credentials flow. For more information on this, please refer to: [Regional Authorities](../../../lib/msal-node/docs/regional-authorities.md).
This sample requires an [Azure Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts). Key Vault and related topics are discussed in [Securing MSAL Node with Azure Key Vault and Azure Managed Identity](../../../lib/msal-node/docs/key-vault-managed-identity.md).

> :information_source: While you may run this sample locally, you are expected to deploy and run it on **Azure App Service** following the [guide here](../../../lib/msal-node/docs/key-vault-managed-identity.md#using-azure-managed-identity).
## Setup

Expand All @@ -24,10 +26,9 @@ Locate the folder where `package.json` resides in your terminal. Then type:
1. Select **Register** to create the application.
1. In the app's registration screen, find and note the **Application (client) ID** and **Directory (Tenant) ID**. You use these values in your app's configuration file(s) later.
1. In the app's registration screen, select the **Certificates & secrets** blade in the left.
- In the **Client secrets** section, select **New client secret**.
- Type a key description (for instance `app secret`),
- Select one of the available key durations (6 months, 12 months or Custom) as per your security posture.
- The generated key value will be displayed when you select the **Add** button. Copy and save the generated value for use in later steps.
- Click on **Upload** certificate and select the certificate file to upload.
- Click **Add**. Once the certificate is uploaded, the _thumbprint_, _start date_, and _expiration_ values are displayed.
- Upload the certificate to your key vault as well
1. In the app's registration screen, select the API permissions blade in the left to open the page where we add access to the APIs that your application needs.
- Select the **Add a permission** button and then,
- Ensure that the **Microsoft APIs** tab is selected.
Expand All @@ -38,16 +39,16 @@ Locate the folder where `package.json` resides in your terminal. Then type:

Before running the sample, you will need to replace the values in retrieve-cert-from-key-vault code as well as the configuration object:

```javascript
```typescript
const keyVaultSecretClient = await getKeyVaultSecretClient(
"ENTER_KEY_VAULT_URL"
"ENTER_KEY_VAULT_URL" // optional, the "KEY_VAULT_URL" environment variable can be set instead
);
[thumbprint, privateKey, x5c] = await getCertificateInfo(
keyVaultSecretClient,
"ENTER_CERT_NAME"
);

const config = {
config = {
auth: {
clientId: "ENTER_CLIENT_ID",
authority: "https://login.microsoftonline.com/ENTER_TENANT_INFO",
Expand All @@ -62,14 +63,24 @@ const config = {

## Run the app

In the same folder, type:
Before running the sample (and everytime changes are made to the sample), the TypeScript will need to be compiled. In the same folder, type:

```console
npm start
npx tsc
```

This will compile the TypeScript into JavaScript, and put the compiled files in the `/dist` folder.

The sample can now be run by typing:

```console
node dist/client-credentials-with-cert-from-key-vault/app.js
```

After that, you should see the response from Microsoft Entra ID in your terminal.
An npm script, which will run the above npx and node command, has been configured in package.json. To compile and start the sample, type:

## More information
```console
npm start
```

- [Tutorial: Call the Microsoft Graph API in a Node.js console app](https://docs.microsoft.com/azure/active-directory/develop/tutorial-v2-nodejs-console)
The token returned from Microsoft Entra ID should be immediately displayed in the terminal.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import {
AuthenticationResult,
ConfidentialClientApplication,
Configuration,
LogLevel,
} from "@azure/msal-node";
import argv from "../cliArgs"; // command line arguments - see samples/msal-node-samples/cliArgs.ts

const getClientCredentialsToken = async (
cca: ConfidentialClientApplication,
clientCredentialRequestScopes: Array<string>,
region?: string
): Promise<AuthenticationResult | null> => {
// With client credentials flows permissions need to be granted in the portal by a tenant administrator.
// The scope is always in the format "<resource>/.default"
const clientCredentialRequest = {
scopes: clientCredentialRequestScopes,
azureRegion: region, // (optional) specify the region you will deploy your application to here (e.g. "westus2")
skipCache: true, // (optional) this skips the cache and forces MSAL to get a new token from Azure AD
};

try {
const response: AuthenticationResult | null =
await cca.acquireTokenByClientCredential(clientCredentialRequest);
console.log("Response: ", response);
return response;
} catch (error) {
console.log(JSON.stringify(error));
throw error;
}
};

/**
* The code below checks if the script is being executed manually or in automation.
* If the script was executed manually, it will initialize a ConfidentialClientApplication object
* and execute the sample client credentials application.
*/
if (argv.$0 === "dist/client-credentials-with-cert-from-key-vault/app.js") {
(async () => {
const clientConfig: Configuration = {
auth: {
clientId: "<ENTER_CLIENT_ID>",
authority:
"https://login.microsoftonline.com/<ENTER_TENANT_ID>",
clientCertificate: {
thumbprintSha256:
"<ENTER_CLIENT_CERTIFICATE_THUMBPRINT_SHA_256>",
privateKey: "ENTER_CLIENT_CERTIFICATE_PRIVATE_KEY",
x5c: "ENTER_CLIENT_CERTIFICATE_X5C",
},
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: LogLevel.Verbose,
},
},
};

const confidentialClientApplication: ConfidentialClientApplication =
new ConfidentialClientApplication(clientConfig);

await getClientCredentialsToken(
confidentialClientApplication,
["https://graph.microsoft.com/.default"],
argv.r
);
})();
}

export default getClientCredentialsToken;
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
"AccessToken": {},
"RefreshToken": {},
"AppMetadata": {}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
module.exports = {
displayName: "Client Credentials with Certificate from Key Vault",
preset: "../../e2eTestUtils/jest-puppeteer-utils/jest-preset-no-setup.js",
extensionsToTreatAsEsm: [".ts", ".tsx"],
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
"main": "index.js",
"private": true,
"scripts": {
"start": "node index.js",
"test:e2e": "jest",
"build:package": "cd ../../.. && npm run build:all --workspace=lib/msal-node",
"start:build": "npm run build:package && npm start"
"build": "npx tsc",
"start": "npm run build && node dist/client-credentials-with-cert-from-key-vault/app.js",
"build:package": "cd ../../.. && npm run build:all --workspace=lib/msal-node"
},
"type": "module",
"author": "",
"license": "MIT",
"dependencies": {
"@azure/msal-node": "^2.0.0-beta.0"
"@azure/msal-node": "^2.15.0"
},
"devDependencies": {
"@types/jest": "^29.5.0",
Expand Down
Loading

0 comments on commit 1a82bfb

Please sign in to comment.