Skip to content

Commit d6d6213

Browse files
tracyboehreraxelsrz
authored andcommitted
Added 18.bot-authentication (#419)
1 parent 5c256fa commit d6d6213

File tree

13 files changed

+656
-0
lines changed

13 files changed

+656
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Bot Authentication
2+
3+
Bot Framework v4 bot authentication sample
4+
5+
This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to use authentication in your bot using OAuth.
6+
7+
The sample uses the bot authentication capabilities in [Azure Bot Service](https://docs.botframework.com), providing features to make it easier to develop a bot that authenticates users to various identity providers such as Azure AD (Azure Active Directory), GitHub, Uber, etc.
8+
9+
NOTE: Microsoft Teams currently differs slightly in the way auth is integrated with the bot. Refer to sample 46.teams-auth.
10+
11+
## Running the sample
12+
- Clone the repository
13+
```bash
14+
git clone https://github.com/Microsoft/botbuilder-python.git
15+
```
16+
- Activate your desired virtual environment
17+
- Bring up a terminal, navigate to `botbuilder-python\samples\18.bot-authentication` folder
18+
- In the terminal, type `pip install -r requirements.txt`
19+
- Deploy your bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment)
20+
- [Add Authentication to your bot via Azure Bot Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-authentication?view=azure-bot-service-4.0&tabs=csharp)
21+
- Modify `APP_ID`, `APP_PASSWORD`, and `CONNECTION_NAME` in `config.py`
22+
23+
After Authentication has been configured via Azure Bot Service, you can test the bot.
24+
25+
- In the terminal, type `python app.py`
26+
27+
## Testing the bot using Bot Framework Emulator
28+
29+
[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.
30+
31+
- Install the Bot Framework Emulator version 4.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
32+
33+
### Connect to the bot using Bot Framework Emulator
34+
35+
- Launch Bot Framework Emulator
36+
- File -> Open Bot
37+
- Enter a Bot URL of `http://localhost:3978/api/messages`
38+
- Enter the app id and password
39+
40+
## Authentication
41+
42+
This sample uses bot authentication capabilities in Azure Bot Service, providing features to make it easier to develop a bot that authenticates users to various identity providers such as Azure AD (Azure Active Directory), GitHub, Uber, etc. These updates also take steps towards an improved user experience by eliminating the magic code verification for some clients.
43+
44+
## Deploy the bot to Azure
45+
46+
To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions.
47+
48+
## Further reading
49+
50+
- [Bot Framework Documentation](https://docs.botframework.com)
51+
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
52+
- [Azure Portal](https://portal.azure.com)
53+
- [Add Authentication to Your Bot Via Azure Bot Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-authentication?view=azure-bot-service-4.0&tabs=csharp)
54+
- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
55+
- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
56+
- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)

samples/18.bot-authentication/app.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import asyncio
5+
import sys
6+
from datetime import datetime
7+
from types import MethodType
8+
9+
from flask import Flask, request, Response
10+
from botbuilder.core import (
11+
BotFrameworkAdapter,
12+
BotFrameworkAdapterSettings,
13+
ConversationState,
14+
MemoryStorage,
15+
TurnContext,
16+
UserState,
17+
)
18+
from botbuilder.schema import Activity, ActivityTypes
19+
20+
from bots import AuthBot
21+
22+
# Create the loop and Flask app
23+
from dialogs import MainDialog
24+
25+
LOOP = asyncio.get_event_loop()
26+
app = Flask(__name__, instance_relative_config=True)
27+
app.config.from_object("config.DefaultConfig")
28+
29+
# Create adapter.
30+
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
31+
SETTINGS = BotFrameworkAdapterSettings(app.config["APP_ID"], app.config["APP_PASSWORD"])
32+
ADAPTER = BotFrameworkAdapter(SETTINGS)
33+
34+
35+
# Catch-all for errors.
36+
async def on_error(self, context: TurnContext, error: Exception):
37+
# This check writes out errors to console log .vs. app insights.
38+
# NOTE: In production environment, you should consider logging this to Azure
39+
# application insights.
40+
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
41+
42+
# Send a message to the user
43+
await context.send_activity("The bot encountered an error or bug.")
44+
await context.send_activity("To continue to run this bot, please fix the bot source code.")
45+
46+
# Send a trace activity if we're talking to the Bot Framework Emulator
47+
if context.activity.channel_id == 'emulator':
48+
# Create a trace activity that contains the error object
49+
trace_activity = Activity(
50+
label="TurnError",
51+
name="on_turn_error Trace",
52+
timestamp=datetime.utcnow(),
53+
type=ActivityTypes.trace,
54+
value=f"{error}",
55+
value_type="https://www.botframework.com/schemas/error"
56+
)
57+
# Send a trace activity, which will be displayed in Bot Framework Emulator
58+
await context.send_activity(trace_activity)
59+
60+
ADAPTER.on_turn_error = MethodType(on_error, ADAPTER)
61+
62+
# Create MemoryStorage and state
63+
MEMORY = MemoryStorage()
64+
USER_STATE = UserState(MEMORY)
65+
CONVERSATION_STATE = ConversationState(MEMORY)
66+
67+
# Create dialog
68+
DIALOG = MainDialog(app.config["CONNECTION_NAME"])
69+
70+
# Create Bot
71+
BOT = AuthBot(CONVERSATION_STATE, USER_STATE, DIALOG)
72+
73+
74+
# Listen for incoming requests on /api/messages.
75+
@app.route("/api/messages", methods=["POST"])
76+
def messages():
77+
# Main bot message handler.
78+
if "application/json" in request.headers["Content-Type"]:
79+
body = request.json
80+
else:
81+
return Response(status=415)
82+
83+
activity = Activity().deserialize(body)
84+
auth_header = (
85+
request.headers["Authorization"] if "Authorization" in request.headers else ""
86+
)
87+
88+
try:
89+
task = LOOP.create_task(
90+
ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
91+
)
92+
LOOP.run_until_complete(task)
93+
return Response(status=201)
94+
except Exception as exception:
95+
raise exception
96+
97+
98+
if __name__ == "__main__":
99+
try:
100+
app.run(debug=False, port=app.config["PORT"]) # nosec debug
101+
except Exception as exception:
102+
raise exception
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from .dialog_bot import DialogBot
5+
from .auth_bot import AuthBot
6+
7+
__all__ = ["DialogBot", "AuthBot"]
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from typing import List
5+
from botbuilder.core import (
6+
ConversationState,
7+
UserState,
8+
TurnContext,
9+
)
10+
from botbuilder.dialogs import Dialog
11+
from botbuilder.schema import ChannelAccount
12+
13+
from helpers.dialog_helper import DialogHelper
14+
from .dialog_bot import DialogBot
15+
16+
17+
class AuthBot(DialogBot):
18+
def __init__(
19+
self,
20+
conversation_state: ConversationState,
21+
user_state: UserState,
22+
dialog: Dialog,
23+
):
24+
super(AuthBot, self).__init__(
25+
conversation_state, user_state, dialog
26+
)
27+
28+
async def on_members_added_activity(
29+
self, members_added: List[ChannelAccount], turn_context: TurnContext
30+
):
31+
for member in members_added:
32+
# Greet anyone that was not the target (recipient) of this message.
33+
# To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
34+
if member.id != turn_context.activity.recipient.id:
35+
await turn_context.send_activity("Welcome to AuthenticationBot. Type anything to get logged in. Type "
36+
"'logout' to sign-out.")
37+
38+
async def on_token_response_event(
39+
self, turn_context: TurnContext
40+
):
41+
# Run the Dialog with the new Token Response Event Activity.
42+
await DialogHelper.run_dialog(
43+
self.dialog,
44+
turn_context,
45+
self.conversation_state.create_property("DialogState"),
46+
)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import asyncio
5+
6+
from botbuilder.core import ActivityHandler, ConversationState, UserState, TurnContext
7+
from botbuilder.dialogs import Dialog
8+
from helpers.dialog_helper import DialogHelper
9+
10+
11+
class DialogBot(ActivityHandler):
12+
def __init__(
13+
self,
14+
conversation_state: ConversationState,
15+
user_state: UserState,
16+
dialog: Dialog,
17+
):
18+
if conversation_state is None:
19+
raise Exception(
20+
"[DialogBot]: Missing parameter. conversation_state is required"
21+
)
22+
if user_state is None:
23+
raise Exception("[DialogBot]: Missing parameter. user_state is required")
24+
if dialog is None:
25+
raise Exception("[DialogBot]: Missing parameter. dialog is required")
26+
27+
self.conversation_state = conversation_state
28+
self.user_state = user_state
29+
self.dialog = dialog
30+
31+
async def on_turn(self, turn_context: TurnContext):
32+
await super().on_turn(turn_context)
33+
34+
# Save any state changes that might have occurred during the turn.
35+
await self.conversation_state.save_changes(turn_context, False)
36+
await self.user_state.save_changes(turn_context, False)
37+
38+
async def on_message_activity(self, turn_context: TurnContext):
39+
await DialogHelper.run_dialog(
40+
self.dialog,
41+
turn_context,
42+
self.conversation_state.create_property("DialogState"),
43+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License.
4+
5+
import os
6+
7+
""" Bot Configuration """
8+
9+
10+
class DefaultConfig:
11+
""" Bot Configuration """
12+
13+
PORT = 3978
14+
APP_ID = os.environ.get("MicrosoftAppId", "")
15+
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
16+
CONNECTION_NAME = os.environ.get("ConnectionName", "")

0 commit comments

Comments
 (0)