Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion samples/experimental/sso/child/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from aiohttp import web
from aiohttp.web import Request, Response
from aiohttp.web_response import json_response
from botbuilder.core import (
BotFrameworkAdapterSettings,
ConversationState,
Expand Down Expand Up @@ -83,7 +84,9 @@ async def messages(req: Request) -> Response:
auth_header = req.headers["Authorization"] if "Authorization" in req.headers else ""

try:
await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
response = await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
if response:
return json_response(data=response.body, status=response.status)
return Response(status=201)
except Exception as exception:
raise exception
Expand Down
2 changes: 1 addition & 1 deletion samples/experimental/sso/child/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
class DefaultConfig:
""" Bot Configuration """

PORT = 3978
PORT = 3979
APP_ID = os.environ.get("MicrosoftAppId", "")
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
CONNECTION_NAME = ""
86 changes: 48 additions & 38 deletions samples/experimental/sso/parent/bots/parent_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
ConversationState,
UserState,
MessageFactory,
TurnContext
TurnContext,
)
from botbuilder.schema import (
Activity,
Expand All @@ -21,9 +21,12 @@
DeliveryModes,
ChannelAccount,
OAuthCard,
TokenExchangeInvokeRequest
TokenExchangeInvokeRequest,
)
from botframework.connector.token_api.models import (
TokenExchangeResource,
TokenExchangeRequest,
)
from botframework.connector.token_api.models import TokenExchangeResource, TokenExchangeRequest

from config import DefaultConfig
from helpers.dialog_helper import DialogHelper
Expand All @@ -44,7 +47,7 @@ def __init__(
self._user_state = user_state
self._dialog = dialog
self._from_bot_id = config.APP_ID
self._to_bot_id = config.SKILL_APP_ID
self._to_bot_id = config.SKILL_MICROSOFT_APP_ID
self._connection_name = config.CONNECTION_NAME

async def on_turn(self, turn_context: TurnContext):
Expand All @@ -57,7 +60,10 @@ async def on_message_activity(self, turn_context: TurnContext):
# for signin, just use an oauth prompt to get the exchangeable token
# also ensure that the channelId is not emulator
if turn_context.activity.type != "emulator":
if turn_context.activity.text == "login" or turn_context.activity.text.isdigit():
if (
turn_context.activity.text == "login"
or turn_context.activity.text.isdigit()
):
await self._conversation_state.load(turn_context, True)
await self._user_state.load(turn_context, True)
await DialogHelper.run_dialog(
Expand All @@ -68,31 +74,35 @@ async def on_message_activity(self, turn_context: TurnContext):
elif turn_context.activity.text == "logout":
bot_adapter = turn_context.adapter
await bot_adapter.sign_out_user(turn_context, self._connection_name)
await turn_context.send_activity(MessageFactory.text("You have been signed out."))
await turn_context.send_activity(
MessageFactory.text("You have been signed out.")
)
elif turn_context.activity.text in ("skill login", "skill logout"):
# incoming activity needs to be cloned for buffered replies
clone_activity = MessageFactory.text(turn_context.activity.text)

TurnContext.apply_conversation_reference(
clone_activity,
TurnContext.get_conversation_reference(turn_context.activity),
True
True,
)

clone_activity.delivery_mode = DeliveryModes.expect_replies

response_1 = await self._client.post_activity(
activities = await self._client.post_buffered_activity(
self._from_bot_id,
self._to_bot_id,
"http://localhost:2303/api/messages",
"http://localhost:3979/api/messages",
"http://tempuri.org/whatever",
turn_context.activity.conversation.id,
clone_activity,
)

if response_1.status == int(HTTPStatus.OK):
if not await self._intercept_oauth_cards(response_1.body, turn_context):
await turn_context.send_activities(response_1.body)
if activities:
if not await self._intercept_oauth_cards(
activities, turn_context
):
await turn_context.send_activities(activities)

return

Expand All @@ -102,22 +112,20 @@ async def on_message_activity(self, turn_context: TurnContext):
TurnContext.apply_conversation_reference(
activity,
TurnContext.get_conversation_reference(turn_context.activity),
True
True,
)
activity.delivery_mode = DeliveryModes.expect_replies

response = await self._client.post_activity(
activities = await self._client.post_buffered_activity(
self._from_bot_id,
self._to_bot_id,
"http://localhost:2303/api/messages",
"http://localhost:3979/api/messages",
"http://tempuri.org/whatever",
str(uuid4()),
activity
activity,
)

if response.status == int(HTTPStatus.OK):
await turn_context.send_activities(response.body)

await turn_context.send_activities(activities)
await turn_context.send_activity(MessageFactory.text("parent: after child"))

async def on_members_added_activity(
Expand All @@ -130,36 +138,39 @@ async def on_members_added_activity(
)

async def _intercept_oauth_cards(
self,
activities: List[Activity],
turn_context: TurnContext,
self, activities: List[Activity], turn_context: TurnContext,
) -> bool:
if not activities:
return False
activity = activities[0]

if activity.attachments:
for attachment in filter(lambda att: att.content_type == CardFactory.content_types.oauth_card,
activity.attachments):
for attachment in filter(
lambda att: att.content_type == CardFactory.content_types.oauth_card,
activity.attachments,
):
oauth_card: OAuthCard = OAuthCard().from_dict(attachment.content)
oauth_card.token_exchange_resource: TokenExchangeResource = TokenExchangeResource().from_dict(
oauth_card.token_exchange_resource)
oauth_card.token_exchange_resource
)
if oauth_card.token_exchange_resource:
token_exchange_provider: BotFrameworkAdapter = turn_context.adapter

result = await token_exchange_provider.exchange_token(
turn_context,
self._connection_name,
turn_context.activity.from_property.id,
TokenExchangeRequest(uri=oauth_card.token_exchange_resource.uri)
TokenExchangeRequest(
uri=oauth_card.token_exchange_resource.uri
),
)

if result.token:
return await self._send_token_exchange_invoke_to_skill(
turn_context,
activity,
oauth_card.token_exchange_resource.id,
result.token
result.token,
)
return False

Expand All @@ -168,33 +179,32 @@ async def _send_token_exchange_invoke_to_skill(
turn_context: TurnContext,
incoming_activity: Activity,
identifier: str,
token: str
token: str,
) -> bool:
activity = self._create_reply(incoming_activity)
activity.type = ActivityTypes.invoke
activity.name = "signin/tokenExchange"
activity.value = TokenExchangeInvokeRequest(
id=identifier,
token=token,
)
activity.value = TokenExchangeInvokeRequest(id=identifier, token=token,)

# route the activity to the skill
response = await self._client.post_activity(
self._from_bot_id,
self._to_bot_id,
"http://localhost:2303/api/messages",
"http://localhost:3979/api/messages",
"http://tempuri.org/whatever",
incoming_activity.conversation.id,
activity
activity,
)

# Check response status: true if success, false if failure
is_success = int(HTTPStatus.OK) <= response.status <= 299
message = "Skill token exchange successful" if is_success else "Skill token exchange failed"
message = (
"Skill token exchange successful"
if is_success
else "Skill token exchange failed"
)

await turn_context.send_activity(MessageFactory.text(
message
))
await turn_context.send_activity(MessageFactory.text(message))

return is_success

Expand Down