練習 - 新增單一登錄
在此練習中,您會將單一登錄新增至訊息延伸模組,以驗證用戶查詢。
設定後端 API 應用程式註冊
首先,建立後端 API 的 Microsoft Entra 應用程式註冊。 基於此練習的目的,您會建立新的應用程式,不過,在生產環境中,您會使用現有的應用程式註冊。
在瀏覽器視窗中:
- 流覽至 Azure 入口網站
- 開啟入口網站功能表,然後選 取 [Microsoft標識符]
- 選取 [應用程式註冊] ,然後選取 [ 新增註冊]
- 在 [註冊應用程式] 表單中,指定下列值:
- 名稱:產品 API
- 支持帳戶類型:任何組織目錄中的帳戶 (任何Microsoft內部標識符租使用者 - 多租使用者)
- 選取 [註冊 ] 以建立應用程式註冊
- 在應用程式註冊左側功能表中,選取 [ 公開 API]
- 選 取 [新增 並 儲存 ] 以建立新的應用程式識別碼 URI
- 在 [此 API 定義的範圍] 區段中,選取 [ 新增範圍]
- 在 [新增範圍] 表單中,指定下列值:
- 範圍名稱: Product.Read
- 誰可以同意?:系統管理員和使用者
- 管理員同意顯示名稱:讀取產品
- 管理員同意描述:允許應用程式讀取產品數據
- 使用者同意顯示名稱:讀取產品
- 使用者同意描述:允許應用程式讀取產品數據
- 狀態: 已啟用
- 選 取 [新增範圍 ] 以建立範圍
接下來,記下應用程式註冊標識碼和範圍標識符。 您需要這些值來設定用來取得後端 API 存取權杖的應用程式註冊。
- 在應用程式註冊左側功能表中,選取 [ 指令清單]
- 複製 appId 屬性值並加以儲存以供稍後使用
- 複製 api.oauth2PermissionScopes[0].id 屬性值,並儲存以供稍後使用
由於我們需要專案中的這些值,請將它們新增至環境檔案。
在 Visual Studio 和 TeamsApp 專案中:
在 env 資料夾中,開啟 .env.local
在檔案中,建立下列環境變數,並將值設定為應用程式註冊標識碼和範圍標識碼:
BACKEND_API_ENTRA_APP_ID=<app-registration-id> BACKEND_API_ENTRA_APP_SCOPE_ID=<scope-id>
Save your changes
建立應用程式註冊指令清單檔案以向後端 API 進行驗證
若要向後端 API 進行驗證,您需要應用程式註冊才能取得用來呼叫 API 的存取令牌。
接下來,建立應用程式註冊指令清單檔案。 指令清單會定義應用程式註冊的 API 許可權範圍和重新導向 URI。
在 Visual Studio 和 TeamsApp 專案中:
在 infra\entra 資料夾中,建立名為 entra.products.api.manifest.json
在 檔案中,新增下列程序代碼:
{ "id": "${{PRODUCTS_API_ENTRA_APP_OBJECT_ID}}", "appId": "${{PRODUCTS_API_ENTRA_APP_ID}}", "name": "${{APP_INTERNAL_NAME}}-product-api-${{TEAMSFX_ENV}}", "accessTokenAcceptedVersion": 2, "signInAudience": "AzureADMultipleOrgs", "optionalClaims": { "idToken": [], "accessToken": [ { "name": "idtyp", "source": null, "essential": false, "additionalProperties": [] } ], "saml2Token": [] }, "requiredResourceAccess": [ { "resourceAppId": "${{BACKEND_API_ENTRA_APP_ID}}", "resourceAccess": [ { "id": "${{BACKEND_API_ENTRA_APP_SCOPE_ID}}", "type": "Scope" } ] } ], "oauth2Permissions": [], "preAuthorizedApplications": [], "identifierUris": [], "replyUrlsWithType": [ { "url": "https://token.botframework.com/.auth/web/redirect", "type": "Web" } ] }
Save your changes
requiredResourceAccess 屬性會指定應用程式註冊標識碼和後端 API 的範圍標識碼。
replyUrlsWithType 屬性會指定 Bot Framework 令牌服務在使用者驗證之後,用來將存取令牌傳回令牌服務的重新導向 URI。
接下來,更新自動化工作流程以建立和更新應用程式註冊。
在 TeamsApp 專案中:
開 啟teamsapp.local.yml
在 檔案中,尋找使用 addApp/update 動作的步驟
在動作之後,新增 aadApp/create 和 aadApp/update 動作,以建立和更新應用程式註冊:
- uses: aadApp/create with: name: ${{APP_INTERNAL_NAME}}-products-api-${{TEAMSFX_ENV}} generateClientSecret: true signInAudience: AzureADMultipleOrgs writeToEnvironmentFile: clientId: PRODUCTS_API_ENTRA_APP_ID clientSecret: SECRET_PRODUCTS_API_ENTRA_APP_CLIENT_SECRET objectId: PRODUCTS_API_ENTRA_APP_OBJECT_ID tenantId: PRODUCTS_API_ENTRA_APP_TENANT_ID authority: PRODUCTS_API_ENTRA_APP_OAUTH_AUTHORITY authorityHost: PRODUCTS_API_ENTRA_APP_OAUTH_AUTHORITY_HOST - uses: aadApp/update with: manifestPath: "./infra/entra/entra.products.api.manifest.json" outputFilePath : "./infra/entra/build/entra.products.api.${{TEAMSFX_ENV}}.json"
Save your changes
aadApp/create 動作會建立具有指定名稱、物件的新應用程式註冊,併產生客戶端密碼。 writeToEnvironmentFile 屬性會將應用程式註冊標識碼、用戶端密碼、對象標識碼、租使用者標識碼、授權單位和授權單位主機寫入環境檔案。 客戶端密碼會加密並安全地儲存在 env.local.user 檔案中。 用戶端密碼的環境變數名稱前面會加上 SECRET_,它會告知Teams工具組不要在記錄中寫入值。
aadApp/update 動作會以指定的指令清單檔案更新應用程式註冊。
集中連線設定名稱
首先,將環境檔案中的連線設定名稱集中化,並更新應用程式組態,以在運行時間存取環境變數值。
在 Visual Studio 和 TeamsApp 項目中繼續:
在 env 資料夾中,開啟 .env.local
在 檔案中,新增下列程序代碼:
CONNECTION_NAME=ProductsAPI
開 啟teamsapp.local.yml
在 檔案中,尋找使用以 ./appsettings 為目標 之 file/createOrUpdateJsonFile 動作 的步驟。Development.json 檔案。 更新內容陣列以包含 CONNECTION_NAME 環境變數,並將值寫入 appsettings。Development.json 檔案:
- uses: file/createOrUpdateJsonFile with: target: ../ProductsPlugin/appsettings.Development.json content: BOT_ID: ${{BOT_ID}} BOT_PASSWORD: ${{SECRET_BOT_PASSWORD}} CONNECTION_NAME: ${{CONNECTION_NAME}}
Save your changes
接下來,更新應用程式組態以存取 CONNECTION_NAME 環境變數。
在 ProductsPlugin 專案中:
開 啟Config.cs
在 ConfigOptions 類別中,新增名為 CONNECTION_NAME
public class ConfigOptions { public string BOT_ID { get; set; } public string BOT_PASSWORD { get; set; } public string CONNECTION_NAME { get; set; } }
Save your changes
開 啟Program.cs
在檔案中,更新讀取應用程式組態的程序代碼,以包含 CONNECTION_NAME 屬性
var config = builder.Configuration.Get<ConfigOptions>(); builder.Configuration["MicrosoftAppType"] = "MultiTenant"; builder.Configuration["MicrosoftAppId"] = config.BOT_ID; builder.Configuration["MicrosoftAppPassword"] = config.BOT_PASSWORD; builder.Configuration["ConnectionName"] = config.CONNECTION_NAME;
Save your changes
接下來,更新 Bot 程式代碼,以在運行時間使用連線設定名稱。
在 [搜尋] 資料夾中,開 啟 [SearchApp.cs
在 SearchApp 類別中,建立可接受 IConfiguration 物件的建構函式,並將CONNECTION_NAME屬性的值指派給名為 connectionName 的私人字段
public class SearchApp : TeamsActivityHandler { private readonly string connectionName; public SearchApp(IConfiguration configuration) { connectionName = configuration["CONNECTION_NAME"]; } }
Save your changes
設定產品 API 連線設定
若要使用後端 API 進行驗證,您必須在 Azure Bot 資源中設定連線設定。
繼續使用 Visual Studio 和 TeamsApp 專案:
在 infra 資料夾中,開 啟 azure.parameters.local.json
在 檔案中,新增 backendApiEntraAppClientId、 productsApiEntraAppClientId、 productsApiEntraAppClientSecret 和 connectionName 參數
{ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "resourceBaseName": { "value": "bot-${{RESOURCE_SUFFIX}}-${{TEAMSFX_ENV}}" }, "botEntraAppClientId": { "value": "${{BOT_ID}}" }, "botDisplayName": { "value": "${{APP_DISPLAY_NAME}}" }, "botAppDomain": { "value": "${{BOT_DOMAIN}}" }, "backendApiEntraAppClientId": { "value": "${{BACKEND_API_ENTRA_APP_ID}}" }, "productsApiEntraAppClientId": { "value": "${{PRODUCTS_API_ENTRA_APP_ID}}" }, "productsApiEntraAppClientSecret": { "value": "${{SECRET_PRODUCTS_API_ENTRA_APP_CLIENT_SECRET}}" }, "connectionName": { "value": "${{CONNECTION_NAME}}" } } }
Save your changes
接下來,更新 Bicep 檔案以包含新的參數,並將其傳遞至 Azure Bot 資源。
在 infra 資料夾中,開啟名為 azure.local.bicep 的檔案
在檔案的 botAppDomain 參數宣告之後,新增 backendApiEntraAppClientId、 productsApiEntraAppClientId、 productsApiEntraAppClientSecret 和 connectionName 參數宣告
param backendApiEntraAppClientId string param productsApiEntraAppClientId string @secure() param productsApiEntraAppClientSecret string param connectionName string
在 azureBotRegistration 模組宣告中,新增參數
module azureBotRegistration './botRegistration/azurebot.bicep' = { name: 'Azure-Bot-registration' params: { resourceBaseName: resourceBaseName botEntraAppClientId: botEntraAppClientId botAppDomain: botAppDomain botDisplayName: botDisplayName backendApiEntraAppClientId: backendApiEntraAppClientId productsApiEntraAppClientId: productsApiEntraAppClientId productsApiEntraAppClientSecret: productsApiEntraAppClientSecret connectionName: connectionName } }
儲存變更。
最後,更新 Bot 註冊 Bicep 檔案以包含新的連線設定。
在 infra/botRegistration 資料夾中,開 啟 azurebot.bicep
在檔案的 botAppDomain 參數宣告之後,新增 backendApiEntraAppClientId、 productsApiEntraAppClientId、 productsApiEntraAppClientSecret 和 connectionName 參數宣告
param backendApiEntraAppClientId string param productsApiEntraAppClientId string @secure() param productsApiEntraAppClientSecret string param connectionName string
在 檔案中,建立名為 botServicesProductsApiConnection 的新資源
resource botServicesProductsApiConnection 'Microsoft.BotService/botServices/connections@2022-09-15' = { parent: botService name: connectionName location: 'global' properties: { serviceProviderDisplayName: 'Azure Active Directory v2' serviceProviderId: '30dd229c-58e3-4a48-bdfd-91ec48eb906c' clientId: productsApiEntraAppClientId clientSecret: productsApiEntraAppClientSecret scopes: 'api://${backendApiEntraAppClientId}/Product.Read' parameters: [ { key: 'tenantID' value: 'common' } { key: 'tokenExchangeUrl' value: 'api://${botAppDomain}/botid-${botEntraAppClientId}' } ] } }
Save your changes
在訊息擴充功能中設定驗證
若要驗證訊息延伸模組中的用戶查詢,您可以使用 Bot Framework SDK 從 Bot Framework 令牌服務取得使用者的存取令牌。 存取令牌接著可用來存取外部服務的數據。
若要簡化程式代碼,請建立可處理使用者驗證的協助程序類別。
繼續使用 Visual Studio 和 ProductsPlugin 專案:
建立名為 Helpers 的新資料夾
在 Helpers 資料夾中,建立名為 AuthHelpers.cs 的新類別檔案
在 檔案中,新增下列程序代碼:
using Microsoft.Bot.Connector.Authentication; using Microsoft.Bot.Schema; using Microsoft.Bot.Schema.Teams; internal static class AuthHelpers { internal static async Task<MessagingExtensionResponse> CreateAuthResponse(UserTokenClient userTokenClient, string connectionName, Activity activity, CancellationToken cancellationToken) { var resource = await userTokenClient.GetSignInResourceAsync(connectionName, activity, null, cancellationToken); return new MessagingExtensionResponse { ComposeExtension = new MessagingExtensionResult { Type = "auth", SuggestedActions = new MessagingExtensionSuggestedAction { Actions = [ new() { Type = ActionTypes.OpenUrl, Value = resource.SignInLink, Title = "Sign In", }, ], }, }, }; } internal static async Task<TokenResponse> GetToken(UserTokenClient userTokenClient, string state, string userId, string channelId, string connectionName, CancellationToken cancellationToken) { var magicCode = string.Empty; if (!string.IsNullOrEmpty(state)) { if (int.TryParse(state, out var parsed)) { magicCode = parsed.ToString(); } } return await userTokenClient.GetUserTokenAsync(userId, connectionName, channelId, magicCode, cancellationToken); } internal static bool HasToken(TokenResponse tokenResponse) => tokenResponse != null && !string.IsNullOrEmpty(tokenResponse.Token); }
Save your changes
AuthHelpers 類別中的三個協助程式方法會處理訊息延伸模組中的用戶驗證。
- CreateAuthResponse 方法會建構回應,以在使用者介面中轉譯登入連結。 登入連結是使用 GetSignInResourceAsync 方法從令牌服務擷取。
- GetToken 方法會使用令牌服務用戶端來取得目前使用者的存取令牌。 方法會使用魔術碼來驗證要求的真實性。
- HasToken 方法會檢查來自令牌服務的回應是否包含存取令牌。 如果令牌不是 Null 或空白,則方法會傳回 true。
接下來,更新訊息延伸模組程序代碼,以使用協助程式方法來驗證用戶查詢。
在 [搜尋] 資料夾中,開 啟 [SearchApp.cs
在檔案頂端,新增下列using語句:
using Microsoft.Bot.Connector.Authentication;
在 OnTeamsMessagingExtensionQueryAsync 方法中,於 方法的開頭新增下列程序代碼:
var userTokenClient = turnContext.TurnState.Get<UserTokenClient>(); var tokenResponse = await AuthHelpers.GetToken(userTokenClient, query.State, turnContext.Activity.From.Id, turnContext.Activity.ChannelId, connectionName, cancellationToken); if (!AuthHelpers.HasToken(tokenResponse)) { return await AuthHelpers.CreateAuthResponse(userTokenClient, connectionName, (Activity)turnContext.Activity, cancellationToken); }
Save your changes
接下來,將令牌服務網域新增至應用程式指令清單檔案,以確保用戶端可以在起始單一登錄流程時信任網域。
在 TeamsApp 專案中:
在 appPackage 資料夾中,開 啟 manifest.json
在 檔案中,更新 validDomains 陣 列,新增令牌服務的網域:
"validDomains": [ "token.botframework.com", "${{BOT_DOMAIN}}" ]
Save your changes
建立和更新資源
一切就緒之後,請執行 準備 Teams 應用程式相依性 程式,以建立新的資源並更新現有的資源。
在 Visual Studio 中繼續:
- 在 [方案總管] 中,以滑鼠右鍵按兩下 TeamsApp 專案
- 展開 [Teams 工具組] 功能表,選取 [準備 Teams 應用程式相依性]
- 在 [ Microsoft 365 帳戶 ] 對話框中,選取 [ 繼續]
- 在 [ 布建] 對話框中,選取 [ 布建]
- 在 [Teams 工具組] 警告 對話框中,選取 [ 布建]
- 在 [Teams 工具組資訊 ] 對話框中,選取交叉圖示以關閉對話方塊
執行和偵錯
布建資源之後,請啟動偵錯會話來測試訊息擴充功能。
若要開始新的偵錯會話,請按 F5 或從工具列選取 [開始 ]
等候瀏覽器視窗開啟,且應用程式安裝對話框出現在 Microsoft Teams Web 用戶端中。 如果出現提示,請輸入您的Microsoft 365 帳戶認證。
在應用程式安裝對話框中,選取 [ 新增]
開啟新的或現有的Microsoft Teams 聊天
在訊息撰寫區域中,選 + 取以開啟應用程式選擇器
在應用程式清單中,選取 [Contoso 產品 ] 以開啟訊息擴充功能
在文字框中,輸入 你好
顯示訊息:您必須登入才能使用此應用程式
遵循 登入 連結來啟動驗證流程
同意要求的許可權,並返回Microsoft Teams
等候搜尋完成並顯示結果
在結果清單中,選 取 [你好 ],將卡片內嵌到撰寫消息框中
返回 Visual Studio,然後從工具列選取 [ 停止 ],或按 Shift + F5 停止偵錯會話。