Skip to content

Commit d42e13f

Browse files
authored
update aad samples for javascript (#837)
1 parent 24ccd86 commit d42e13f

File tree

5 files changed

+197
-65
lines changed

5 files changed

+197
-65
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
WEB_PUBSUB_ENDPOINT=https://<name>.webpubsub.azure.com
2+
HUB_NAME=sample_chat
3+
TENANT_ID=<your-tenant-id>
4+
APP_CLIENT_ID=<your-client-id>
5+
CLIENT_SECRET=<your-client-secret>
6+
CERT_PATH=<path-to-your-cert>
7+
MSI_CLIENT_ID=<your-msi-client-id>

samples/javascript/chatapp-microsoft-entra-id/package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
"description": "",
55
"main": "server.js",
66
"scripts": {
7-
"test": "echo TODO",
8-
"start": "node server.js"
7+
"start": "tsc && node dist/server.js",
8+
"build": "tsc",
9+
"test": "echo TEST"
910
},
1011
"keywords": [],
1112
"author": "",
@@ -14,6 +15,12 @@
1415
"@azure/identity": "^2.0.1",
1516
"@azure/web-pubsub": "^1.0.0",
1617
"@azure/web-pubsub-express": "^1.0.0",
18+
"dotenv": "^16.4.7",
1719
"express": "^4.17.1"
20+
},
21+
"devDependencies": {
22+
"@types/express": "^4.17.17",
23+
"@types/node": "^22.13.10",
24+
"typescript": "^5.0.0"
1825
}
1926
}

samples/javascript/chatapp-microsoft-entra-id/server.js

Lines changed: 0 additions & 63 deletions
This file was deleted.
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import express, { Request, Response } from "express";
2+
3+
import dotenv from "dotenv";
4+
5+
import {
6+
AzurePowerShellCredential,
7+
VisualStudioCodeCredential,
8+
ClientSecretCredential,
9+
ClientSecretCredentialOptions,
10+
ClientCertificateCredential,
11+
ClientCertificateCredentialOptions,
12+
ClientAssertionCredential,
13+
ManagedIdentityCredential,
14+
TokenCredential,
15+
TokenCredentialOptions,
16+
AzureAuthorityHosts,
17+
} from "@azure/identity";
18+
19+
import { WebPubSubServiceClient } from "@azure/web-pubsub";
20+
import { WebPubSubEventHandler, ConnectRequest, UserEventRequest } from "@azure/web-pubsub-express";
21+
22+
dotenv.config();
23+
24+
const endpoint = process.env.WEB_PUBSUB_ENDPOINT || ""; // Default to an empty string if not set
25+
const hubName = process.env.HUB_NAME || "default_hub";
26+
const tenantId = process.env.TENANT_ID || "";
27+
const appClientId = process.env.APP_CLIENT_ID || "";
28+
const clientSecret = process.env.CLIENT_SECRET || "";
29+
const certPath = process.env.CERT_PATH || "";
30+
const msiClientId = process.env.MSI_CLIENT_ID || "";
31+
32+
const app = express();
33+
34+
enum AuthType {
35+
AzurePowershell,
36+
VisualStudioCode,
37+
ApplicationWithClientSecret,
38+
ApplicationWithCertification,
39+
ApplicationWithFederatedIdentity,
40+
SystemAssignedMSI,
41+
UserAssignedMSI,
42+
}
43+
44+
let authType: AuthType = AuthType.AzurePowershell;
45+
46+
function GetTokenCredential(authType: AuthType): TokenCredential {
47+
switch (authType) {
48+
case AuthType.AzurePowershell:
49+
return GetAzurePowershellCredential();
50+
case AuthType.VisualStudioCode:
51+
return GetVisualStudioCodeCredential();
52+
case AuthType.ApplicationWithClientSecret:
53+
return GetApplicationWithClientSecretCredential();
54+
case AuthType.ApplicationWithCertification:
55+
return GetApplicationWithCertificationCredential();
56+
case AuthType.ApplicationWithFederatedIdentity:
57+
return GetApplicationWithFederatedIdentityCredential();
58+
case AuthType.SystemAssignedMSI:
59+
return GetSystemAssignedMSICredential();
60+
case AuthType.UserAssignedMSI:
61+
return GetUserAssignedMSICredential();
62+
default:
63+
throw new Error("Invalid auth type");
64+
}
65+
}
66+
67+
function GetAzurePowershellCredential(): AzurePowerShellCredential {
68+
return new AzurePowerShellCredential();
69+
}
70+
71+
function GetVisualStudioCodeCredential(): VisualStudioCodeCredential {
72+
return new VisualStudioCodeCredential();
73+
}
74+
75+
function GetApplicationWithClientSecretCredential(): ClientSecretCredential {
76+
const options: ClientSecretCredentialOptions = {
77+
// China: AzureAuthorityHosts.AzureChina
78+
// US Government: AzureAuthorityHosts.AzureGovernment
79+
authorityHost: AzureAuthorityHosts.AzurePublicCloud,
80+
};
81+
return new ClientSecretCredential(tenantId, appClientId, clientSecret, options);
82+
}
83+
84+
function GetApplicationWithCertificationCredential(): ClientCertificateCredential {
85+
const options: ClientCertificateCredentialOptions = {
86+
// China: AzureAuthorityHosts.AzureChina
87+
// US Government: AzureAuthorityHosts.AzureGovernment
88+
authorityHost: AzureAuthorityHosts.AzurePublicCloud,
89+
};
90+
return new ClientCertificateCredential(tenantId, appClientId, certPath, options);
91+
}
92+
93+
function GetApplicationWithFederatedIdentityCredential(): ClientAssertionCredential {
94+
const options: TokenCredentialOptions = {
95+
// China: AzureAuthorityHosts.AzureChina
96+
// US Government: AzureAuthorityHosts.AzureGovernment
97+
authorityHost: AzureAuthorityHosts.AzurePublicCloud,
98+
};
99+
const msiCredential = new ManagedIdentityCredential(msiClientId, options);
100+
return new ClientAssertionCredential(
101+
tenantId,
102+
appClientId,
103+
async () => {
104+
// Entra ID US Government: api://AzureADTokenExchangeUSGov
105+
// Entra ID China operated by 21Vianet: api://AzureADTokenExchangeChina
106+
const scope = "api://AzureADTokenExchange/.default";
107+
const token = await msiCredential.getToken(scope);
108+
109+
if (!token?.token) {
110+
throw new Error("Failed to get token from MSI");
111+
}
112+
return token.token;
113+
},
114+
options,
115+
);
116+
}
117+
118+
function GetSystemAssignedMSICredential(): ManagedIdentityCredential {
119+
const options: TokenCredentialOptions = {
120+
authorityHost: AzureAuthorityHosts.AzurePublicCloud,
121+
};
122+
return new ManagedIdentityCredential(options);
123+
}
124+
125+
function GetUserAssignedMSICredential(): ManagedIdentityCredential {
126+
const options: TokenCredentialOptions = {
127+
authorityHost: AzureAuthorityHosts.AzurePublicCloud,
128+
};
129+
return new ManagedIdentityCredential(msiClientId, options);
130+
}
131+
132+
const credential: TokenCredential = GetTokenCredential(authType);
133+
const serviceClient = new WebPubSubServiceClient(endpoint, credential, hubName);
134+
135+
const handler = new WebPubSubEventHandler(hubName, {
136+
path: "/eventhandler",
137+
handleConnect: async (req: ConnectRequest, res) => {
138+
console.log(req);
139+
await serviceClient.sendToAll({
140+
type: "system",
141+
message: `${req.context.userId} joined`,
142+
});
143+
res.success();
144+
},
145+
handleUserEvent: async (req: UserEventRequest, res) => {
146+
if (req.context.eventName === "message") {
147+
await serviceClient.sendToAll({
148+
from: req.context.userId,
149+
message: req.data,
150+
});
151+
}
152+
res.success();
153+
},
154+
allowedEndpoints: [endpoint],
155+
});
156+
157+
app.use(handler.getMiddleware());
158+
app.get("/negotiate", async (req: Request, res: Response) => {
159+
const id = req.query.id as string;
160+
if (!id) {
161+
res.status(400).send("missing user id");
162+
return;
163+
}
164+
const token = await serviceClient.getClientAccessToken({ userId: id });
165+
res.json({
166+
url: token.url,
167+
});
168+
});
169+
170+
app.use(express.static("public"));
171+
app.listen(8080, () => console.log("server started"));
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"outDir": "./dist",
4+
"rootDir": "./",
5+
"module": "commonjs",
6+
"target": "es6",
7+
"strict": true,
8+
"esModuleInterop": true
9+
}
10+
}

0 commit comments

Comments
 (0)