Esercizio - Aggiungere l'accesso Single Sign-On
In questo esercizio si aggiunge l'accesso Single Sign-On all'estensione del messaggio per autenticare le query utente.
Configurare la registrazione dell'app per le API back-end
Creare prima di tutto una registrazione dell'app Microsoft Entra per l'API back-end. Ai fini di questo esercizio ne si crea uno nuovo, tuttavia, in un ambiente di produzione, si userà una registrazione dell'app esistente.
In una finestra del browser:
- Passare al portale di Azure
- Aprire il menu del portale e selezionare Microsoft Entra ID
- Selezionare Registrazioni app e quindi Nuova registrazione
- Nel modulo Registra un'applicazione specificare i valori seguenti:
- Nome: API Products
- Tipi di account di supporto: account in qualsiasi directory organizzativa (qualsiasi tenant di Microsoft Entra ID - Multitenant)
- Selezionare Registra per creare la registrazione dell'app
- Nel menu a sinistra della registrazione dell'app selezionare Esporre un'API
- Selezionare Aggiungi e salva per creare un nuovo URI ID applicazione
- Nella sezione Ambiti definiti da questa API selezionare Aggiungi un ambito
- Nel modulo Aggiungi un ambito specificare i valori seguenti:
- Nome ambito: Product.Read
- Chi può fornire il consenso?: Amministratori e utenti
- Nome visualizzato del consenso amministratore: Leggere i prodotti
- Descrizione del consenso amministratore: consente all'app di leggere i dati del prodotto
- Nome visualizzato consenso utente: Leggere i prodotti
- Descrizione del consenso utente: consente all'app di leggere i dati del prodotto
- Stato: abilitato
- Selezionare Aggiungi ambito per creare l'ambito
Prendere quindi nota dell'ID di registrazione dell'app e dell'ID ambito. Questi valori sono necessari per configurare la registrazione dell'app usata per ottenere un token di accesso per l'API back-end.
- Nel menu a sinistra della registrazione dell'app selezionare Manifesto
- Copiare il valore della proprietà appId e salvarlo per un uso successivo
- Copiare il valore della proprietà api.oauth2PermissionScopes[0].id e salvarlo per un uso successivo
Poiché sono necessari questi valori nel progetto, aggiungerli al file di ambiente.
In Visual Studio e nel progetto TeamsApp:
Nella cartella env aprire .env.local
Nel file creare le variabili di ambiente seguenti e impostare i valori sull'ID di registrazione dell'app e sull'ID ambito:
BACKEND_API_ENTRA_APP_ID=<app-registration-id> BACKEND_API_ENTRA_APP_SCOPE_ID=<scope-id>
Save your changes
Creare un file manifesto di registrazione dell'app per l'autenticazione con l'API back-end
Per eseguire l'autenticazione con l'API back-end, è necessaria una registrazione dell'app per ottenere un token di accesso con cui chiamare l'API.
Creare quindi un file manifesto di registrazione dell'app. Il manifesto definisce gli ambiti di autorizzazione dell'API e l'URI di reindirizzamento nella registrazione dell'app.
In Visual Studio e nel progetto TeamsApp:
Nella cartella infra\entra creare un file denominato entra.products.api.manifest.json
Nel file aggiungere il codice seguente:
{ "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
La proprietà requiredResourceAccess specifica l'ID di registrazione dell'app e l'ID ambito dell'API back-end.
La proprietà replyUrlsWithType specifica l'URI di reindirizzamento utilizzato dal servizio token bot framework per restituire il token di accesso al servizio token dopo l'autenticazione dell'utente.
Aggiornare quindi il flusso di lavoro automatizzato per creare e aggiornare la registrazione dell'app.
Nel progetto TeamsApp:
Apri teamsapp.local.yml
Nel file trovare il passaggio che usa l'azione addApp/update
Dopo l'azione, aggiungere le azioni aadApp/create e aadApp/update per creare e aggiornare la registrazione dell'app:
- 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
L'azione aadApp/create crea una nuova registrazione dell'app con il nome, il gruppo di destinatari e genera un segreto client specificato. La proprietà writeToEnvironmentFile scrive l'ID di registrazione dell'app, il segreto client, l'ID oggetto, l'ID tenant, l'autorità e l'host dell'autorità nei file di ambiente. Il segreto client viene crittografato e archiviato in modo sicuro nel file env.local.user . Il nome della variabile di ambiente per il segreto client è preceduto da SECRET_ e indica a Teams Toolkit di non scrivere il valore nei log.
L'azione aadApp/update aggiorna la registrazione dell'app con il file manifesto specificato.
Centralizzare il nome dell'impostazione di connessione
Innanzitutto, centralizzare il nome dell'impostazione di connessione nel file di ambiente e aggiornare la configurazione dell'app per accedere al valore della variabile di ambiente in fase di esecuzione.
Continuare in Visual Studio e nel progetto TeamsApp:
Nella cartella env aprire .env.local
Nel file aggiungere il codice seguente:
CONNECTION_NAME=ProductsAPI
Apri teamsapp.local.yml
Nel file trovare il passaggio che usa l'azione file/createOrUpdateJsonFile destinata a ./appsettings. Development.json file. Aggiornare la matrice di contenuto per includere la variabile di ambiente CONNECTION_NAME e scrivere il valore in appsettings. Development.json file:
- 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
Aggiornare quindi la configurazione dell'app per accedere alla variabile di ambiente CONNECTION_NAME .
Nel progetto ProductsPlugin:
Apri Config.cs
Nella classe ConfigOptions aggiungere una nuova proprietà denominata 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
Apri Program.cs
Nel file aggiornare il codice che legge la configurazione dell'app per includere la proprietà 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
Aggiornare quindi il codice del bot per usare il nome dell'impostazione di connessione in fase di esecuzione.
Nella cartella Cerca aprire SearchApp.cs
Nella classe SearchApp creare un costruttore che accetta un oggetto IConfiguration e assegna il valore della proprietà CONNECTION_NAME a un campo privato denominato connectionName
public class SearchApp : TeamsActivityHandler { private readonly string connectionName; public SearchApp(IConfiguration configuration) { connectionName = configuration["CONNECTION_NAME"]; } }
Save your changes
Configurare l'impostazione di connessione dell'API Products
Per eseguire l'autenticazione con l'API back-end, è necessario configurare un'impostazione di connessione nella risorsa di Azure Bot.
Continuare in Visual Studio e nel progetto TeamsApp:
Nella cartella infra aprire azure.parameters.local.json
Nel file aggiungere i parametri backendApiEntraAppClientId, productsApiEntraAppClientId, productsApiEntraAppClientSecret e 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
Aggiornare quindi il file Bicep per includere i nuovi parametri e passarli alla risorsa di Azure Bot.
Nella cartella infra aprire il file denominato azure.local.bicep
Nel file, dopo la dichiarazione del parametro botAppDomain, aggiungere le dichiarazioni di parametro backendApiEntraAppClientId, productsApiEntraAppClientId, productsApiEntraAppClientSecret e connectionName
param backendApiEntraAppClientId string param productsApiEntraAppClientId string @secure() param productsApiEntraAppClientSecret string param connectionName string
Nella dichiarazione del modulo azureBotRegistration aggiungere i nuovi parametri
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 } }
Salvare le modifiche.
Aggiornare infine il file Bicep di registrazione del bot per includere la nuova impostazione di connessione.
Nella cartella infra/botRegistration aprire azurebot.bicep
Nel file, dopo la dichiarazione del parametro botAppDomain, aggiungere le dichiarazioni di parametro backendApiEntraAppClientId, productsApiEntraAppClientId, productsApiEntraAppClientSecret e connectionName
param backendApiEntraAppClientId string param productsApiEntraAppClientId string @secure() param productsApiEntraAppClientSecret string param connectionName string
Nel file creare una nuova risorsa denominata 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
Configurare l'autenticazione nell'estensione del messaggio
Per autenticare le query utente nell'estensione del messaggio, usare Bot Framework SDK per ottenere un token di accesso per l'utente dal servizio token bot framework. Il token di accesso può quindi essere usato per accedere ai dati da un servizio esterno.
Per semplificare il codice, creare una classe helper che gestisce l'autenticazione utente.
Continuare in Visual Studio e nel progetto ProductsPlugin:
Creare una nuova cartella denominata Helpers
Nella cartella Helpers creare un nuovo file di classe denominato AuthHelpers.cs
Nel file aggiungere il codice seguente:
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
I tre metodi helper nella classe AuthHelpers gestiscono l'autenticazione utente nell'estensione del messaggio.
- Il metodo CreateAuthResponse costruisce una risposta che esegue il rendering di un collegamento di accesso nell'interfaccia utente. Il collegamento di accesso viene recuperato dal servizio token usando il metodo GetSignInResourceAsync .
- Il metodo GetToken usa il client del servizio token per ottenere un token di accesso per l'utente corrente. Il metodo usa un codice magic per verificare l'autenticità della richiesta.
- Il metodo HasToken controlla se la risposta del servizio token contiene un token di accesso. Se il token non è Null o vuoto, il metodo restituisce true.
Aggiornare quindi il codice dell'estensione del messaggio per usare i metodi helper per autenticare le query utente.
Nella cartella Cerca aprire SearchApp.cs
Nella parte superiore del file aggiungere l'istruzione using seguente:
using Microsoft.Bot.Connector.Authentication;
Nel metodo OnTeamsMessagingExtensionQueryAsync aggiungere il codice seguente all'inizio del metodo:
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
Aggiungere quindi il dominio del servizio token al file manifesto dell'app per assicurarsi che il client possa considerare attendibile il dominio durante l'avvio di un flusso di Single Sign-On.
Nel progetto TeamsApp:
Nella cartella appPackage aprire manifest.json
Nel file aggiornare la matrice validDomains , aggiungere il dominio del servizio token:
"validDomains": [ "token.botframework.com", "${{BOT_DOMAIN}}" ]
Save your changes
Creare e aggiornare le risorse
Con tutto ciò che è ora disponibile, eseguire il processo Preparare le dipendenze delle app di Teams per creare nuove risorse e aggiornarne di quelle esistenti.
Continuare in Visual Studio:
- In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto TeamsApp
- Espandere il menu Teams Toolkit, selezionare Prepare Teams App Dependencies (Preparare le dipendenze delle app di Teams)
- Nella finestra di dialogo Account Microsoft 365 selezionare Continua
- Nella finestra di dialogo Provision (Provision ) selezionare Provisioning (Provisioning)
- Nella finestra di dialogo di avviso di Teams Toolkit selezionare Provisioning
- Nella finestra di dialogo Informazioni di Teams Toolkit selezionare l'icona a croce per chiudere la finestra di dialogo
Eseguire ed eseguire il debug
Con il provisioning delle risorse, avviare una sessione di debug per testare l'estensione del messaggio.
Per avviare una nuova sessione di debug, premere F5 o selezionare Avvia dalla barra degli strumenti
Attendere l'apertura di una finestra del browser e la finestra di dialogo di installazione dell'app viene visualizzata nel client Web di Microsoft Teams. Se richiesto, immettere le credenziali dell'account Microsoft 365.
Nella finestra di dialogo di installazione dell'app selezionare Aggiungi
Aprire una chat di Microsoft Teams nuova o esistente
Nell'area di composizione dei messaggi selezionare + per aprire la selezione app
Nell'elenco delle app selezionare Prodotti Contoso per aprire l'estensione del messaggio
Nella casella di testo immettere hello
Viene visualizzato un messaggio che indica che è necessario accedere per usare questa app
Seguire il collegamento di accesso per avviare il flusso di autenticazione
Consenso alle autorizzazioni richieste e ritorno a Microsoft Teams
Attendere il completamento della ricerca e visualizzare i risultati
Nell'elenco dei risultati selezionare hello per incorporare una scheda nella finestra di messaggio compose
Tornare a Visual Studio e selezionare Arresta dalla barra degli strumenti o premere MAIUSC + F5 per arrestare la sessione di debug.