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

[ACS][Common][Alpha] Added a new auth credential for Teams Phone Extentions #33356

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion common/config/rush/pnpm-lock.yaml

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

Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
"--timeout",
"999999",
"--colors",
"${workspaceFolder}/test/*.spec.ts",
"${workspaceFolder}/test/node/*.spec.ts"
"${workspaceFolder}/test/**/*.spec.ts",
],
"env": { "TS_NODE_COMPILER_OPTIONS": "{\"module\": \"commonjs\"}" },
"console": "integratedTerminal",
Expand Down
1 change: 1 addition & 0 deletions sdk/communication/communication-common/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 2.3.2 (Unreleased)

### Features Added
Introduced support for `TokenCredential` with `EntraCommunicationTokenCredentialOptions`, enabling Entra users to authorize Communication Services and allowing an Entra user with a Teams license to use Teams Phone Extensibility features through the Azure Communication Services resource.

### Breaking Changes

Expand Down
48 changes: 48 additions & 0 deletions sdk/communication/communication-common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ This package contains common code for Azure Communication Service libraries.

- An [Azure subscription][azure_sub].
- An existing Communication Services resource. If you need to create the resource, you can use the [Azure Portal][azure_portal], the [Azure PowerShell][azure_powershell], or the [Azure CLI][azure_cli].
- Having the @azure/identity package installed.

### Installing

```bash
npm install @azure/communication-common
```

```bash
npm install @azure/identity
```

### Browser support

#### JavaScript Bundle
Expand All @@ -33,6 +38,7 @@ Depending on your scenario, you may want to initialize the `AzureCommunicationTo

- a static token (suitable for short-lived clients used to e.g. send one-off Chat messages) or
- a callback function that ensures a continuous authentication state during communications (ideal e.g. for long Calling sessions).
- a token credential capable of obtaining an Entra user token. You can provide any implementation of [TokenCredential interface](https://learn.microsoft.com/es-mx/javascript/api/@azure/core-auth/tokencredential?view=azure-node-latest). It is suitable for scenarios where Entra user access tokens are needed to authenticate with Communication Services.

The tokens supplied to the `AzureCommunicationTokenCredential` either through the constructor or via the token refresher callback can be obtained using the Azure Communication Identity library.

Expand Down Expand Up @@ -105,6 +111,48 @@ const tokenCredential = new AzureCommunicationTokenCredential({
});
```

### Create a credential with a token credential capable of obtaining an Entra user token

For scenarios where an Entra user can be used with Communication Services, you need to initialize any implementation of [TokenCredential interface](https://learn.microsoft.com/es-mx/javascript/api/@azure/core-auth/tokencredential?view=azure-node-latest) and provide it to the ``EntraCommunicationTokenCredentialOptions``.
Along with this, you must provide the URI of the Azure Communication Services resource and the scopes required for the Entra user token. These scopes determine the permissions granted to the token.
If the scopes are not provided, by default, it sets the scopes to `https://communication.azure.com/clients/.default`.

```typescript
const options: InteractiveBrowserCredentialInBrowserOptions = {
tenantId: "<your-tenant-id>",
clientId: "<your-client-id>",
redirectUri: "<your-redirect-uri>",
};
const entraTokenCredential = new InteractiveBrowserCredential(options);

const entraTokenCredentialOptions: EntraCommunicationTokenCredentialOptions = {
resourceEndpoint: "https://<your-resource>.communication.azure.com",
tokenCredential: entraTokenCredential,
scopes: ["https://communication.azure.com/clients/VoIP"]
};

const credential = new AzureCommunicationTokenCredential(entraTokenCredentialOptions);
```

The same approach can be used for authorizing an Entra user with a Teams license to use Teams Phone Extensibility features through your Azure Communication Services resource.
This requires providing the `https://auth.msft.communication.azure.com/TeamsExtension.ManageCalls` scope.

```typescript
const options: InteractiveBrowserCredentialInBrowserOptions = {
tenantId: "<your-tenant-id>",
clientId: "<your-client-id>",
redirectUri: "<your-redirect-uri>",
};
const entraTokenCredential = new InteractiveBrowserCredential(options);

const entraTokenCredentialOptions: EntraCommunicationTokenCredentialOptions = {
resourceEndpoint: "https://<your-resource>.communication.azure.com",
tokenCredential: entraTokenCredential,
scopes: ["https://auth.msft.communication.azure.com/TeamsExtension.ManageCalls"]
};

const credential = new AzureCommunicationTokenCredential(entraTokenCredentialOptions);
```
## Troubleshooting

- **Invalid token specified**: Make sure the token you are passing to the `AzureCommunicationTokenCredential` constructor or to the `tokenRefresher` callback is a bare JWT token string. E.g. if you're using the [Azure Communication Identity library][invalid_token_sdk] or [REST API][invalid_token_rest] to obtain the token, make sure you're passing just the `token` part of the response object.
Expand Down
5 changes: 4 additions & 1 deletion sdk/communication/communication-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
"dependencies": {
"@azure/abort-controller": "^2.1.2",
"@azure/core-auth": "^1.9.0",
"@azure/core-rest-pipeline": "^1.18.2",
"@azure-rest/core-client": "^2.3.3",
"@azure/core-rest-pipeline": "^1.17.0",
"@azure/core-tracing": "^1.2.0",
"@azure/core-util": "^1.11.0",
"events": "^3.3.0",
Expand All @@ -69,13 +70,15 @@
"devDependencies": {
"@azure-tools/test-utils-vitest": "^1.0.0",
"@azure/dev-tool": "^1.0.0",
"@azure/identity": "~4.3.0",
"@azure/eslint-plugin-azure-sdk": "^3.0.0",
"@azure/logger": "^1.1.4",
"@types/node": "^18.0.0",
"@vitest/browser": "^3.0.3",
"@vitest/coverage-istanbul": "^3.0.3",
"eslint": "^9.9.0",
"mockdate": "^3.0.5",
"nock": "14.0.1",
"playwright": "^1.48.0",
"typescript": "~5.7.2",
"vitest": "^3.0.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { TokenCredential } from '@azure/core-auth';
export class AzureCommunicationTokenCredential implements CommunicationTokenCredential {
constructor(token: string);
constructor(refreshOptions: CommunicationTokenRefreshOptions);
constructor(entraOptions: EntraCommunicationTokenCredentialOptions);
dispose(): void;
getToken(options?: CommunicationGetTokenOptions): Promise<AccessToken>;
}
Expand Down Expand Up @@ -70,6 +71,13 @@ export interface EndpointCredential {
endpoint: string;
}

// @public
export interface EntraCommunicationTokenCredentialOptions {
resourceEndpoint: string;
scopes?: string[];
tokenCredential: TokenCredential;
}

// @public
export const getIdentifierKind: (identifier: CommunicationIdentifier) => CommunicationIdentifierKind;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import type {
import type { AccessToken } from "@azure/core-auth";
import { StaticTokenCredential } from "./staticTokenCredential.js";
import { parseToken } from "./tokenParser.js";
import {
type EntraCommunicationTokenCredentialOptions,
EntraTokenCredential,
} from "./entraTokenCredential.js";

/**
* The CommunicationTokenCredential implementation with support for proactive token refresh.
Expand All @@ -33,11 +37,25 @@ export class AzureCommunicationTokenCredential implements CommunicationTokenCred
* @param refreshOptions - Options to configure refresh and opt-in to proactive refreshing.
*/
constructor(refreshOptions: CommunicationTokenRefreshOptions);
constructor(tokenOrRefreshOptions: string | CommunicationTokenRefreshOptions) {
if (typeof tokenOrRefreshOptions === "string") {
this.tokenCredential = new StaticTokenCredential(parseToken(tokenOrRefreshOptions));
/**
* Creates an instance of CommunicationTokenCredential with an Entra ID token credential. In most cases, you might want to use InteractiveBrowserCredential to sign in your user.
* @param entraOptions - Options to configure the Entra ID token credential.
*/
constructor(entraOptions: EntraCommunicationTokenCredentialOptions);
constructor(
tokenOrRefreshOptionsOrEntraOptions:
| string
| CommunicationTokenRefreshOptions
| EntraCommunicationTokenCredentialOptions,
) {
if (typeof tokenOrRefreshOptionsOrEntraOptions === "string") {
this.tokenCredential = new StaticTokenCredential(
parseToken(tokenOrRefreshOptionsOrEntraOptions),
);
} else if ("tokenRefresher" in tokenOrRefreshOptionsOrEntraOptions) {
this.tokenCredential = new AutoRefreshTokenCredential(tokenOrRefreshOptionsOrEntraOptions);
} else {
this.tokenCredential = new AutoRefreshTokenCredential(tokenOrRefreshOptions);
this.tokenCredential = new EntraTokenCredential(tokenOrRefreshOptionsOrEntraOptions);
}
}

Expand Down
Loading