Skip to content

Commit bb530d0

Browse files
author
Tracy Boehrer
committed
Merge branch 'main' into users/tracyboehrer/remove-repo-docs
# Conflicts: # docs/index.md # samples/dotnet/auto-signin/README.md
2 parents ce106e0 + ea98c7b commit bb530d0

File tree

7 files changed

+275
-1
lines changed

7 files changed

+275
-1
lines changed

samples/python/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
|----|----|----|
55
|Quickstart|Simplest agent|N/A|
66
|Auto Sign In|Simple OAuth agent using Graph and GitHub|[auto-signin](auto-signin/README.md)|
7+
|OBO Authorization|OBO flow to access a Copilot Studio Agent|[obo-authorization](obo-authorization/README.md)|
78
|Semantic Kernel Integration|A weather agent built with Semantic Kernel|[semantic-kernel-multiturn](semantic-kernel-multiturn/README.md)|
89
|Streaming Agent|Streams OpenAI responses|[azure-ai-streaming](azure-ai-streaming/README.md)|
910
|Copilot Studio Client|Console app to consume a Copilot Studio Agent|[copilotstudio-client](copilotstudio-client/README.md)|
1011
|Cards Agent|Agent that uses rich cards to enhance conversation design |[cards](cards/README.md)|
11-
|Copilot Studio Skill|Call the echo bot from a Copilot Studio skill |[copilotstudio-skill](copilotstudio-skill/README.md)|
12+
|Copilot Studio Skill|Call the echo bot from a Copilot Studio skill |[copilotstudio-skill](copilotstudio-skill/README.md)|
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# OBO Auth Sample
2+
3+
This Agent has been created using [Microsoft 365 Agents SDK](https://github.com/microsoft/agents-for-net), it shows how to use authorization in your Agent using OAuth and OBO.
4+
5+
- The sample uses the Agent SDK User Authorization capabilities in [Azure Bot Service](https://docs.botframework.com), providing features to make it easier to develop an Agent that authorizes users with various identity providers such as Azure AD (Azure Active Directory), GitHub, Uber, etc.
6+
- This sample shows how to use an OBO Exchange to communicate with Microsoft Copilot Studio using the [CopilotClient class](https://learn.microsoft.com/python/api/microsoft-agents-copilotstudio-client/microsoft.agents.copilotstudio.client.copilotclient).
7+
8+
## Prerequisites
9+
10+
- [Python](https://www.python.org/) version 3.9 or higher
11+
- [dev tunnel](https://learn.microsoft.com/azure/developer/dev-tunnels/get-started?tabs=windows) (for local development)
12+
- Access to CopilotStudio to [create an Agent](https://learn.microsoft.com/microsoft-copilot-studio/fundamentals-get-started?tabs=web)
13+
14+
## Local Setup
15+
16+
### Configuration
17+
18+
1. Create an Agent in Copilot Studio.
19+
1. Publish your newly created Agent
20+
1. Got to Settings => Advanced => Metadata and copy the following values. You will need them later:
21+
1. Schema name
22+
1. Environment ID
23+
24+
1. [Create an Azure Bot](https://aka.ms/AgentsSDK-CreateBot)
25+
- Record the Application ID, the Tenant ID, and the Client Secret for use below
26+
27+
1. Open the `env.TEMPLATE` file in the root of the sample project, rename it to `.env` and configure the following values:
28+
1. Set the **CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID** to the AppId of the bot identity.
29+
2. Set the **CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET** to the Secret that was created for your identity. *This is the `Secret Value` shown in the AppRegistration*.
30+
3. Set the **CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID** to the Tenant Id where your application is registered.
31+
32+
1. Setting up OAuth for an exchangeable token
33+
1. Create a new App Registration
34+
1. SingleTenant
35+
1. Give it a name and click **Register**
36+
1. **Authentication** tab
37+
1. **Add Platform**, then **Web**, Set `Redirect URI` to `Web` and `https://token.botframework.com/.auth/web/redirect`
38+
1. **Add Platform**, then **Mobile and desktop applications**, and add an additional `http://localhost` Uri.
39+
1. **API Permissions** tab
40+
1. **Dynamics CRM** with **user_impersonation**
41+
1. **Graph** with **User.Read**
42+
1. **Power Platform API** with **CopilotStudio.Copilots.Invoke**
43+
1. Grant Admin Consent for your tenant.
44+
1. **Expose an API** tab
45+
1. Click **Add a Scope**
46+
1. **Application ID URI** should be: `api://botid-{{appid}}`
47+
1. **Scope Name** is "defaultScope"
48+
1. **Who can consent** is **Admins and users**
49+
1. Enter values for the required Consent fields
50+
1. **Certificates & secrets**
51+
1. Create a new secret and record the value. This will be used later.
52+
53+
1. Create Azure Bot **OAuth Connection**
54+
1. On the Azure Bot created in Step #2, Click **Configuration** tab then the **Add OAuth Connection Settings** button.
55+
1. Enter a **Name**. This will be used later.
56+
1. For **Service Provider** select **Azure Active Directory v2**
57+
1. **Client id** and **Client Secret** are the values created in step #4.
58+
1. Enter the **Tenant ID**
59+
1. **Scopes** is `api://botid-{{appid}}/defaultScope`. appid is the **Client id** value from #4.
60+
61+
1. Configure the authorization handlers
62+
1. Open the `.env` file and update:
63+
1. Set the **CONNECTIONS__MCS__SETTINGS__CLIENTID** to the **Client id** from #4
64+
1. Set the **CONNECTIONS__MCS__SETTINGS__CLIENTSECRET** to the **Client Secret** value from #4.
65+
1. Set the **CONNECTIONS__MCS__SETTINGS__TENANTID** to the Tenant Id.
66+
1. Keep **AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__OBOCONNECTIONNAME=MCS**
67+
1. Set **AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME={{Name}}** where **Name** is the name of your OAuth connection created in step 5.
68+
69+
1. Configure the sample to use your CopilotStudio Agent by modifying the following variables in the `.env` file based on the values obtained in step #1:
70+
71+
```bash
72+
COPILOTSTUDIOAGENT__ENVIRONMENTID="" # Environment ID of the Agent from MCS
73+
COPILOTSTUDIOAGENT__SCHEMANAME="" # Schema Name of the Agent from MCS
74+
```
75+
76+
1. Run `dev tunnels`. See [Create and host a dev tunnel](https://learn.microsoft.com/azure/developer/dev-tunnels/get-started?tabs=windows) and host the tunnel with anonymous user access command as shown below:
77+
78+
```bash
79+
devtunnel host -p 3978 --allow-anonymous
80+
```
81+
82+
1. Take note of the url shown after `Connect via browser:`
83+
84+
1. On the Azure Bot, select **Settings**, then **Configuration**, and update the **Messaging endpoint** to `{tunnel-url}/api/messages`
85+
86+
### Running the Agent
87+
88+
1. Open this folder from your IDE or Terminal of preference
89+
1. (Optional but recommended) Set up virtual environment and activate it.
90+
1. Install dependencies
91+
92+
```sh
93+
pip install -r requirements.txt
94+
```
95+
96+
### Run in localhost, anonymous mode
97+
98+
1. Start the application
99+
100+
```sh
101+
python -m src.main
102+
```
103+
104+
At this point you should see the message
105+
106+
```text
107+
======== Running on http://localhost:3978 ========
108+
```
109+
110+
The agent is ready to accept messages.
111+
112+
## Accessing the Agent
113+
114+
### Using the Agent in WebChat
115+
116+
1. Go to your Azure Bot Service resource in the Azure Portal and select **Test in WebChat**
117+
118+
## Further reading
119+
120+
To learn more about building Bots and Agents, see our [Microsoft 365 Agents SDK](https://github.com/microsoft/agents) repo.
121+
122+
For more information on logging configuration, see the logging section in the Quickstart Agent sample README.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=
2+
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=
3+
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=
4+
5+
CONNECTIONS__MCS__SETTINGS__CLIENTID=
6+
CONNECTIONS__MCS__SETTINGS__CLIENTSECRET=
7+
CONNECTIONS__MCS__SETTINGS__TENANTID=
8+
9+
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=
10+
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__OBOCONNECTIONNAME=MCS
11+
12+
COPILOTSTUDIOAGENT__SCHEMANAME=
13+
COPILOTSTUDIOAGENT__ENVIRONMENTID=
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
python-dotenv
2+
aiohttp
3+
microsoft-agents-activity
4+
microsoft-agents-hosting-core
5+
microsoft-agents-hosting-aiohttp
6+
microsoft-agents-authentication-msal
7+
microsoft-agents-copilotstudio-client
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import re
5+
from os import environ
6+
from dotenv import load_dotenv
7+
8+
from microsoft.agents.hosting.core import (
9+
Authorization,
10+
TurnContext,
11+
MemoryStorage,
12+
AgentApplication,
13+
TurnState,
14+
MemoryStorage,
15+
)
16+
from microsoft.agents.activity import load_configuration_from_env, ActivityTypes
17+
from microsoft.agents.hosting.aiohttp import CloudAdapter
18+
from microsoft.agents.authentication.msal import MsalConnectionManager
19+
from microsoft.agents.copilotstudio.client import ConnectionSettings, CopilotClient, PowerPlatformEnvironment, PowerPlatformCloud
20+
21+
# Load configuration from environment
22+
load_dotenv()
23+
agents_sdk_config = load_configuration_from_env(environ)
24+
25+
# Create storage and connection manager
26+
STORAGE = MemoryStorage()
27+
CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config)
28+
ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER)
29+
AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config)
30+
31+
AGENT_APP = AgentApplication[TurnState](
32+
storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config
33+
)
34+
35+
# just for testing
36+
# in practice, use a more robust method to manage app state with conversation state
37+
mcs_convo_id = None
38+
39+
async def get_client(context: TurnContext) -> CopilotClient:
40+
41+
settings = ConnectionSettings(
42+
environment_id=environ.get("COPILOTSTUDIOAGENT__ENVIRONMENTID"),
43+
agent_identifier=environ.get("COPILOTSTUDIOAGENT__SCHEMANAME"),
44+
cloud=PowerPlatformCloud.PROD,
45+
copilot_agent_type=None,
46+
custom_power_platform_cloud=None,
47+
)
48+
49+
scope = PowerPlatformEnvironment.get_token_audience(settings)
50+
51+
# where are exchanging the token every time. You can be smarter about this.
52+
token_response = await AGENT_APP.auth.exchange_token(context, [scope], "MCS")
53+
mcs_client = CopilotClient(settings, token_response.token)
54+
55+
return mcs_client
56+
57+
@AGENT_APP.message("/signout")
58+
async def signout(context: TurnContext, state: TurnState):
59+
# Force a user signout to reset the user state
60+
# This is needed to reset the token in Azure Bot Services if needed.
61+
# Typically this wouldn't be need in a production Agent. Made available to assist it starting from scratch.
62+
await AGENT_APP.auth.sign_out(context, state)
63+
await context.send_activity("You have signed out")
64+
65+
# Since Auto SignIn is enabled, by the time this is called the token is already available via Authorization.get_token or
66+
# Authorization.exchange_token.
67+
# NOTE: This is a slightly unusual way to handle incoming Activities (but perfectly) valid. For this sample,
68+
# we just want to proxy messages to/from a Copilot Studio Agent.
69+
@AGENT_APP.message(re.compile(r".*"), auth_handlers=["MCS"])
70+
async def default_handler(context: TurnContext, _state: TurnState):
71+
global mcs_convo_id
72+
73+
mcs_client = await get_client(context)
74+
75+
if not mcs_convo_id:
76+
async for reply in mcs_client.start_conversation():
77+
if reply.type == ActivityTypes.message:
78+
await context.send_activity(reply.text)
79+
mcs_convo_id = reply.conversation.id
80+
elif context.activity.type == ActivityTypes.message:
81+
async for reply in mcs_client.ask_question(context.activity.text, mcs_convo_id):
82+
if reply.type == ActivityTypes.message:
83+
await context.send_activity(reply.text)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
# enable logging for Microsoft Agents library
5+
# for more information, see README.md for Quickstart Agent
6+
import logging
7+
ms_agents_logger = logging.getLogger("microsoft.agents")
8+
ms_agents_logger.addHandler(logging.StreamHandler())
9+
ms_agents_logger.setLevel(logging.INFO)
10+
11+
from .agent import AGENT_APP, CONNECTION_MANAGER
12+
from .start_server import start_server
13+
14+
start_server(
15+
agent_application=AGENT_APP,
16+
auth_configuration=CONNECTION_MANAGER.get_default_connection_configuration(),
17+
)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from os import environ
2+
from microsoft.agents.hosting.core import AgentApplication, AgentAuthConfiguration
3+
from microsoft.agents.hosting.aiohttp import (
4+
start_agent_process,
5+
jwt_authorization_middleware,
6+
CloudAdapter,
7+
)
8+
from aiohttp.web import Request, Response, Application, run_app
9+
10+
def start_server(
11+
agent_application: AgentApplication, auth_configuration: AgentAuthConfiguration
12+
):
13+
async def entry_point(req: Request) -> Response:
14+
agent: AgentApplication = req.app["agent_app"]
15+
adapter: CloudAdapter = req.app["adapter"]
16+
return await start_agent_process(
17+
req,
18+
agent,
19+
adapter,
20+
)
21+
22+
APP = Application(middlewares=[jwt_authorization_middleware])
23+
APP.router.add_post("/api/messages", entry_point)
24+
APP["agent_configuration"] = auth_configuration
25+
APP["agent_app"] = agent_application
26+
APP["adapter"] = agent_application.adapter
27+
28+
try:
29+
run_app(APP, host="localhost", port=environ.get("PORT", 3978))
30+
except Exception as error:
31+
raise error

0 commit comments

Comments
 (0)