Konfigurera agenten så att den använder OAuth

En agent använder OAuth för att logga in användare och få tokens för nedströmsresurser (såsom Microsoft Graph) utan att hantera inloggningsuppgifterna själv. Azure Bot Service hanterar tokenutbytet och agenten hämtar den resulterande användartoken under en tur.

Översikt

Att använda OAuth i en agent involverar tre aktiviteter:

  1. Konfigurera OAuth på Azure Bot och appregistrering: Skapa en eller flera OAuth-anslutningar på din Azure Bot-resurs, var och en backad av en Microsoft Entra ID-appregistrering. Att lägga till användarbehörighet med autentiseringsuppgifter med federerad identitet täcker det vanligaste tillvägagångssättet. Andra typer av autentiseringsuppgifter, såsom klienthemligheter eller certifikat, stöds också. För hela uppsättningen av alternativ, se grundläggande autentisering för Bot Service.
  2. Konfigurera matchande inställningar i agenten: Varje OAuth-anslutning på Azure Bot mappas till en OAuth-hanterare i agentens konfiguration. Mer information finns i Inställningar. För allmän agentkonfiguration, se Vad är SDK för Microsoft 365-agenter.
  3. Använd tokens i kod: Under en runda, hämta användartoken – eller utför ett On-Behalf-Of (OBO)-utbyte – via agentens användarauktorisations-API. Se Använd token i kod (icke-OBO) och Använd token i kod (OBO).

Ha följande begrepp i åtanke när du läser resten av den här artikeln:

  • En Azure Bot kan innehålla flera OAuth-anslutningar. Till exempel en anslutning för Microsoft Graph och en annan för GitHub. Varje anslutning konfigureras oberoende på Azure Bot.
  • Det finns en 1:1-relation mellan en OAuth-anslutning på Azure Bot och en OAuth-hanterare i agenten. En hanterarws AzureBotOAuthConnectionName-inställning namnger Azure Bot-anslutningen den använder. För att använda två anslutningar, definiera två hanterare.
  • Agentens användarbehörighets-API är den yta du anropar i koden. I .NET är detta AgentApplication.UserAuthorization – till exempel GetTurnTokenAsync för att läsa en token och ExchangeTurnTokenAsync för att utföra ett OBO-utbyte. De motsvarande ytorna är authorization i JavaScript och auth i Python.

För arbetsexempel, se automatisk inloggning och OBO-exempel för:

Språkstöd för OAuth

Agents SDK stöder OAuth för .NET, JavaScript och Python. Kärnkoncepten är desamma i alla språk: OAuth-hanterare, att koppla hanterare till rutter och OBO-utbyte. Endast konfigurationsformatet och hanterarens API-namn skiljer sig åt:

Language Var du konfigurerar API-yta
.NET appsettings.json (eller kod i Program.cs) AgentApplication.UserAuthorization
JavaScript .env miljövariabler AgentApplication.authorization
Python .env miljövariabler AgentApplication.auth

För JavaScript och Python använder nycklarna .env samma hierarkiska namn som .NET-strukturen appsettings.json , där varje nivå är separerad av ett dubbelt understreck (__). JavaScript-nycklar behåller lövnamnen med kamelnotation som visas i tabellerna (till exempel azureBotOAuthConnectionName). Python-nycklar är versaler (till exempel AZUREBOTOAUTHCONNECTIONNAME).

Viktigt

Global automatisk inloggning (AutoSignIn) och DefaultHandlerName stöds endast i .NET. I JavaScript och Python kopplar du OAuth-hanterare till specifika rutter, som visas i Per rutt-konfigurationen.

Inställningar

Ett användarauktoriseringsobjekt i AgentApplication styr hur agenten hämtar användartoken. Minst namnger varje hanterare den Azure Bot OAuth-anslutning den använder. Följande exempel visar den minimala strukturen i varje språk. Tabellerna nedan beskriver resten av de tillgängliga egenskaperna, och OBO-sektionerna täcker inställningarna OBOConnectionName och OBOScopes.

I .NET, konfigurera användarauktorisering under AgentApplication i appsettings.json:

  "AgentApplication": {
    "UserAuthorization": {
      "DefaultHandlerName": "{{handler-name}}",
      "AutoSignIn": true | false,
      "Handlers": {
        "{{handler-name}}": {
          "Settings": {
            "AzureBotOAuthConnectionName": "{{azure-bot-connection-name}}"
          }
        }
      }
    }
  }

I JavaScript, konfigurera användarauktorisering med miljövariabler i filen .env:

# Connection used to authenticate the agent itself
connections__serviceConnection__settings__clientId=
connections__serviceConnection__settings__clientSecret=
connections__serviceConnection__settings__tenantId=
connectionsMap__0__connection=serviceConnection
connectionsMap__0__serviceUrl=*

# OAuth handler named "{{handler-name}}"
AgentApplication__UserAuthorization__Handlers__{{handler-name}}__Settings__azureBotOAuthConnectionName={{azure-bot-connection-name}}

I Python, konfigurera användarauktorisering med miljövariabler i filen .env:

# Connection used to authenticate the agent itself
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=

# OAuth handler named "{{handler-name}}"
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__{{handler-name}}__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME={{azure-bot-connection-name}}

Egenskaper för UserAuthorization

Följande tabell listar toppnivåegenskaperna för UserAuthorization som avgör hur handlers väljs och hur tokens hämtas för varje inkommande aktivitet.

Egenskap Obligatoriskt Type beskrivning
DefaultHandlerName Nej (rekommenderas) sträng Endast .NET. Namnet på hanteraren som används när AutoSignIn utvärderas till sant och ingen åsidosättning per väg har angetts.
AutoSignIn Nej bool eller ombud Endast .NET. När värdet är sant (standard) försöker agenten hämta en token för varje inkommande aktivitet. Åsidosätt vid körning med Options.AutoSignIn för att filtrera aktivitetstyper.
Handlers Ja (minst en) objekt (ordlista) Mappning av handlernamn till dess konfiguration. Varje nyckel måste vara unik.

I .NET, för att begränsa vilka aktiviteter som automatisk inloggning gäller för, sätt ett predikat liknande: Options.AutoSignIn = (context, ct) => Task.FromResult(context.Activity.IsType(ActivityTypes.Message));. JavaScript och Python stödjer inte global automatisk inloggning. Logga istället in avgränsat genom att koppla specifika hanterarnamn till enskilda rutter, som visas i exemplen per rutt.

Egenskaper för inställningar

Följande tabell beskriver det inbäddade Settings-objektet som används för en enskild OAuth-hanterare och styr visningen av inloggningskort, återförsöksbeteende, timeouts samt valfri OBO-utbyteskonfiguration.

Egenskap Obligatoriskt Type beskrivning
AzureBotOAuthConnectionName Ja sträng OAuth-anslutningsnamn definierat på Azure robot-resursen.
OBOConnectionName Nej (endast OBO) sträng Namnet på en Agents SDK-anslutning som används för att utföra ett On-Behalf-Of-tokenutbyte.
OBOScopes Nej (endast OBO) sträng[] Omfång som begärdes under OBO-utbytet. Om det utelämnas med OBOConnectionName, kan du manuellt anropa ExchangeTurnTokenAsync.
Title Nej sträng Anvpassad rubrik för inloggning. Standardvärdet är Logga in.
Text Nej sträng Knapptext för inloggningskort. Standardvärdet är Logga in.
InvalidSignInRetryMax Nej heltal Det maximala antalet försök som tillåts när användaren anger en ogiltig kod Standard är 2.
InvalidSignInRetryMessage Nej sträng Meddelande som visas efter att användaren har angett en ogiltig kod. Standardmeddelandet är Ogiltig inloggningskod. Ange den sexsiffriga koden.
Timeout Nej int (ms) Antal millisekunder innan ett pågående inloggningsförsök går ut. Standardvärdet är 900 000 (15 minuter).

Kommentar

AzureBotOAuthConnectionName, OBOConnectionName, OBOScopes, , Titleoch Text gäller för alla tre språk (med hjälp av nyckelfallet per språk som beskrivs i Språkstöd för OAuth). InvalidSignInRetryMax, InvalidSignInRetryMessage och Timeout är .NET-inställningar.

Vilken typ ska du använda?

Använd följande tabell för att avgöra vilket angreppssätt som passar ditt scenario.

Alternativ Använd när
Automatisk inloggning (endast .NET) Du vill att varje inkommande aktivitet automatiskt ska hämta en token, eller så vill du ha en filtrerad delmängd (till exempel endast meddelanden eller allt utom händelser) genom att ange ett predikat till UserAuthorizationOptions.AutoSignIn. Stöds endast i .NET.
Per väg Endast specifika rutt-hanterare behöver tokens eller så måste olika rutter använda olika OAuth-anslutningar (och därmed olika tokens). Detta alternativ är det enda alternativet i JavaScript och Python. I .NET är det additivt med global automatisk inloggning. Om båda är aktiverade i .NET har turen tillgång till tokens från båda.

Använda token i koden (icke-OBO)

Det här avsnittet visar hur du hämtar och använder användartoken som direkt returneras av din Azure robot OAuth-anslutning utan att utföra ett On-Behalf-Of-utbyte. I .NET kan du använda global automatisk inloggning eller per-rutt-hanterare. JavaScript och Python använder endast hanterare per rutt. Inuti din aktivitetshanterare, hämta token (GetTurnTokenAsync i .NET, authorization.getToken i JavaScript, auth.get_token i Python) så sent som möjligt så att SDK:n kan uppdatera token om den är nära utgång. Följande exempel illustrerar båda mönstren.

Automatisk inloggning (endast .NET)

Kommentar

Global automatisk inloggning och DefaultHandlerName är endast tillgängliga i .NET. För JavaScript och Python, använd Per-rutt-konfiguration.

Använd denna konfiguration när global automatisk inloggning ska hämta en token för varje inkommande aktivitet utan att behöva ange per-rutt-hanterare.

  "AgentApplication": {
    "UserAuthorization": {
      "DefaultHandlerName": "auto",
      "Handlers": {
        "auto": {
          "Settings": {
            "AzureBotOAuthConnectionName": "teams_sso",
          }
        }
      }
    }
  },

Din agentkod skulle se ut ungefär så här:

public class MyAgent : AgentApplication
{
    [MessageRoute]
    public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
    {
        var token = await UserAuthorization.GetTurnTokenAsync(turnContext);

        // use the token 
    }
}

Per-ruttkonfiguration

Använd en per-route-konfiguration när du vill ha granulär kontroll: endast de rutter som du uttryckligen markerar hämtar tokens. Per rutt-konfiguration har följande fördelar:

  • Det minskar onödig token-hämtning.
  • Det tillåter olika rutter att rikta in sig på distinkta OAuth-anslutningar (och därmed olika resurser eller omfattningar).
  • Den låter dig blanda autentiserade och oautentiserade rutter inom samma agent.

I följande exempel är en enda graph hanterare endast kopplad till meddelanderutten.

I .NET inaktiveras global automatisk inloggning och hanteraren graph kopplas till rutten med hjälp av autoSignInHandlers.

  "AgentApplication": {
    "UserAuthorization": {
      "AutoSignIn": false,
      "Handlers": {
        "graph": {
          "Settings": {
            "AzureBotOAuthConnectionName": "teams_sso",
          }
        }
      }
    }
  },

Din agentkod skulle se ut ungefär så här:

public class MyAgent : AgentApplication
{
    [MessageRoute(autoSignInHandlers: "graph")]
    public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
    {
        var token = await UserAuthorization.GetTurnTokenAsync(turnContext, "graph");

        // use the token
    }
}

I JavaScript, skicka en array av handlernamn som sista argument till ruttregistreringen. Endast den vägen utlöser inloggning för hanteraren graph .

AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=teams_sso
AgentApplication__UserAuthorization__Handlers__graph__Settings__title=Graph Sign In
AgentApplication__UserAuthorization__Handlers__graph__Settings__text=Sign in with Microsoft Graph

Din agentkod skulle se ut ungefär så här:

class MyAgent extends AgentApplication {
  constructor () {
    super({ storage: new MemoryStorage() })
    // the `graph` handler runs only for this route
    this.onMessage('-me', this._profileRequest, ['graph'])
  }

  private _profileRequest = async (context, state) => {
    const tokenResponse = await this.authorization.getToken(context, 'graph')

    // use tokenResponse.token
  }
}

I Python, skicka till auth_handlers ruttdekoratören. Endast den vägen utlöser inloggning för hanteraren GRAPH .

AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=teams_sso

Din agentkod skulle se ut ungefär så här:

@AGENT_APP.message(re.compile(r"^/(me|profile)$", re.IGNORECASE), auth_handlers=["GRAPH"])
async def profile_request(context: TurnContext, state: TurnState) -> None:
    token_response = await AGENT_APP.auth.get_token(context, "GRAPH")

    # use token_response.token

Hämta token under en tur

Hämta användartokenen när du behöver den under en tur. Du kan anropa den flera gånger. Anropa metoden direkt före användning så att uppdateringslogiken (vid behov) hanteras automatiskt.

Language Ring
.NET GetTurnTokenAsync(turnContext, handlerName)
JavaScript authorization.getToken(context, handlerName)
Python auth.get_token(context, handler_name)

Använda token i koden (OBO)

On-Behalf-Of (OBO) bygger på att den initiala användarinloggningen returnerar en utbytbar token. Det kräver att OAuth-anslutningens scope inkluderar ett som motsvarar ett scope som tillgängliggörs av nedströms-API:et (till exempel om det tillgängliggjorda scopet är defaultScopes, kan det konfigurerade scopet vara api://botid-{{clientId}}/defaultScopes). Agent-SDK:n genomför därefter ett Microsofts autentiseringsbibliotek (MSAL)-tokenutbyte med en konfigurerad anslutning identifierad av OBOConnectionName och listan över OBOScopes. När både OBOConnectionName och OBOScopes finns i konfigurationen sker utbytet automatiskt, och du får den slutgiltiga tokenen via standardtokenanropet (GetTurnTokenAsync / getToken / get_token). Om någon av dem saknas kan du utföra utbytet explicit vid körning (ExchangeTurnTokenAsync i .NET, authorization.exchangeToken i JavaScript, auth.exchange_token i Python) så att du kan lösa anslutnings- eller omfångslistan dynamiskt.

OBO i konfiguration

Använd detta mönster när du vet vilken nedströmsresurs och vilka scopes du behöver vid konfigurationstillfället. När du anger både OBOConnectionName och OBOScopes utför SDK:et automatiskt OBO-utbytet (på uppdrag av) under inloggningen. Detta innebär att efterföljande anrop till standardtokenhämtaren returnerar OBO-token direkt utan att extra kod under körning behövs.

  "AgentApplication": {
    "UserAuthorization": {
      "AutoSignIn": false,
      "Handlers": {
        "graph": {
          "Settings": {
            "AzureBotOAuthConnectionName": "teams_sso",
            "OBOConnectionName": "ServiceConnection",
            "OBOScopes": [
              "https://graph.microsoft.com/.default"
            ]
          }
        }
      }
    }
  },
  "Connections": {
    "ServiceConnection": {
      "Settings": {
        "AuthType": "FederatedCredentials",
        "AuthorityEndpoint": "https://login.microsoftonline.com/{{TenantId}}",
        "ClientId": "{{ClientId}}",
        "FederatedClientId": "{{ManagedIdentityClientId}}",
        "Scopes": [
          "https://api.botframework.com/.default"
        ]
      }
    }
  },

Din agentkod skulle se ut ungefär så här:

public class MyAgent : AgentApplication
{
    [MessageRoute(autoSignInHandlers: "graph")]
    public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
    {
        // returns the OBO token because OBOConnectionName and OBOScopes are configured
        var token = await UserAuthorization.GetTurnTokenAsync(turnContext, "graph");

        // use the token
    }
}

I JavaScript, definiera en OBO-anslutning i anslutningsavbildningen och referera den från hanteraren med oboConnectionName och oboScopes.

# Agent's own connection
connections__serviceConnection__settings__clientId=
connections__serviceConnection__settings__clientSecret=
connections__serviceConnection__settings__tenantId=

# OBO connection
connections__oboConnection__settings__clientId=
connections__oboConnection__settings__clientSecret=
connections__oboConnection__settings__tenantId=

connectionsMap__0__connection=serviceConnection
connectionsMap__0__serviceUrl=*
connectionsMap__1__connection=oboConnection
connectionsMap__1__serviceUrl=obo

AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=teams_sso
AgentApplication__UserAuthorization__Handlers__graph__Settings__oboConnectionName=oboConnection
AgentApplication__UserAuthorization__Handlers__graph__Settings__oboScopes=https://graph.microsoft.com/.default

Din agentkod skulle se ut ungefär så här:

class MyAgent extends AgentApplication {
  constructor () {
    super({ storage: new MemoryStorage() })
    this.onActivity('message', this._onMessage, ['graph'])
  }

  private _onMessage = async (context, state) => {
    // returns the OBO token because oboConnectionName and oboScopes are configured
    const tokenResponse = await this.authorization.getToken(context, 'graph')

    // use tokenResponse.token
  }
}

I Python, definiera en OBO-anslutning under CONNECTIONS och referera den från hanteraren med OBOCONNECTIONNAME och OBOSCOPES.

# Agent's own connection
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=

# OBO connection
CONNECTIONS__OBO__SETTINGS__CLIENTID=
CONNECTIONS__OBO__SETTINGS__CLIENTSECRET=
CONNECTIONS__OBO__SETTINGS__TENANTID=

AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=teams_sso
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__OBOCONNECTIONNAME=OBO
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__OBOSCOPES=https://graph.microsoft.com/.default

Din agentkod skulle se ut ungefär så här:

@AGENT_APP.message(re.compile(r".*"), auth_handlers=["GRAPH"])
async def on_message(context: TurnContext, state: TurnState) -> None:
    # returns the OBO token because OBOCONNECTIONNAME and OBOSCOPES are configured
    token_response = await AGENT_APP.auth.get_token(context, "GRAPH")

    # use token_response.token

OBO-utbyte under körning

Använd en runtime-exchange när du inte kan fixa nedströmsresursen, scopes eller anslutningen i konfigurationen. Denna situation uppstår till exempel när scopes beror på hyresgäst, användarroll eller en funktionsflagga. I denna modell konfigurerar du valfritt OBO-anslutningen och anropar sedan utbytesmetoden med de scopes du väljer vid turtillfället. Du får en utbytt token som du omedelbart kan använda.

Anropa ExchangeTurnTokenAsync med de scopes du väljer vid turtillfället.

  "AgentApplication": {
    "UserAuthorization": {
      "AutoSignIn": false,
      "Handlers": {
        "graph": {
          "Settings": {
            "AzureBotOAuthConnectionName": "teams_sso",
            "OBOConnectionName": "ServiceConnection"
          }
        }
      }
    }
  },
  "Connections": {
    "ServiceConnection": {
      "Settings": {
        "AuthType": "FederatedCredentials",
        "AuthorityEndpoint": "https://login.microsoftonline.com/{{TenantId}}",
        "ClientId": "{{ClientId}}",
        "FederatedClientId": "{{ManagedIdentityClientId}}",
        "Scopes": [
          "https://api.botframework.com/.default"
        ]
      }
    }
  },

Din agentkod skulle se ut ungefär så här:

public class MyAgent : AgentApplication
{
    [MessageRoute(autoSignInHandlers: "graph")]
    public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
    {
        var scopes = GetScopes();

        var exchangedToken = await UserAuthorization.ExchangeTurnTokenAsync(turnContext, "graph", exchangeScopes: scopes);

        // use the token
    }
}

Anropa authorization.exchangeToken med det hanterarnamn och de omfång du väljer vid turtillfället.

connections__oboConnection__settings__clientId=
connections__oboConnection__settings__clientSecret=
connections__oboConnection__settings__tenantId=

connectionsMap__1__connection=oboConnection
connectionsMap__1__serviceUrl=obo

AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=teams_sso
AgentApplication__UserAuthorization__Handlers__graph__Settings__oboConnectionName=oboConnection

Din agentkod skulle se ut ungefär så här:

class MyAgent extends AgentApplication {
  constructor () {
    super({ storage: new MemoryStorage() })
    this.onActivity('message', this._onMessage, ['graph'])
  }

  private _onMessage = async (context, state) => {
    const scopes = getScopes()
    const exchangedToken = await this.authorization.exchangeToken(context, 'graph', { scopes })

    // use exchangedToken.token
  }
}

Anropa auth.exchange_token med de omfång du väljer vid turtillfället och hanterarnamnet.

CONNECTIONS__MCS__SETTINGS__CLIENTID=
CONNECTIONS__MCS__SETTINGS__CLIENTSECRET=
CONNECTIONS__MCS__SETTINGS__TENANTID=

AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=teams_sso
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__OBOCONNECTIONNAME=MCS

Din agentkod skulle se ut ungefär så här:

@AGENT_APP.message(re.compile(r".*"), auth_handlers=["MCS"])
async def on_message(context: TurnContext, state: TurnState) -> None:
    scopes = get_scopes()
    token_response = await AGENT_APP.auth.exchange_token(context, scopes, "MCS")

    # use token_response.token

Regionala OAuth-inställningar

För icke-USA-regioner, uppdatera slutpunkten för token-tjänsten som din agent använder.

I exemplet nedan visas ett .NET-konfigurationen. Lägg till det i appsettings.json:

"RestChannelServiceClientFactory": {
   "TokenServiceEndpoint": "{{service-endpoint-uri}}"
}

För service-endpoint-url använder du lämpligt värde från följande tabell för publika molnrobotar med dataresidens i den angivna regionen.

URI Region
https://europe.api.botframework.com Europa
https://unitedstates.api.botframework.com USA
https://india.api.botframework.com Indien