0
# OAuth & Authentication
1
2
OAuth token management and user authentication functionality. Includes token providers, authentication utilities, and integration with Bot Framework authentication services.
3
4
## Capabilities
5
6
### ExtendedUserTokenProvider
7
8
Extended user token provider interface that provides comprehensive OAuth token management capabilities including token retrieval, sign-out, and token exchange functionality.
9
10
```python { .api }
11
class ExtendedUserTokenProvider:
12
async def get_user_token(self, turn_context: TurnContext, connection_name: str, magic_code: str = None, oauth_app_credentials=None):
13
"""
14
Get OAuth token for user.
15
16
Args:
17
turn_context (TurnContext): Current turn context
18
connection_name (str): OAuth connection name
19
magic_code (str, optional): Magic code from OAuth flow
20
oauth_app_credentials (optional): OAuth app credentials
21
22
Returns:
23
TokenResponse: Token response or None
24
"""
25
26
async def sign_out_user(self, turn_context: TurnContext, connection_name: str, user_id: str = None, oauth_app_credentials=None):
27
"""
28
Sign out user from OAuth provider.
29
30
Args:
31
turn_context (TurnContext): Current turn context
32
connection_name (str): OAuth connection name
33
user_id (str, optional): User ID to sign out
34
oauth_app_credentials (optional): OAuth app credentials
35
"""
36
37
async def get_oauth_sign_in_link(self, turn_context: TurnContext, connection_name: str, oauth_app_credentials=None, final_redirect: str = None):
38
"""
39
Get OAuth sign-in link.
40
41
Args:
42
turn_context (TurnContext): Current turn context
43
connection_name (str): OAuth connection name
44
oauth_app_credentials (optional): OAuth app credentials
45
final_redirect (str, optional): Final redirect URL
46
47
Returns:
48
str: OAuth sign-in URL
49
"""
50
51
async def exchange_token(self, turn_context: TurnContext, connection_name: str, user_id: str, exchange_request):
52
"""
53
Exchange token with OAuth provider.
54
55
Args:
56
turn_context (TurnContext): Current turn context
57
connection_name (str): OAuth connection name
58
user_id (str): User ID
59
exchange_request: Token exchange request
60
61
Returns:
62
TokenResponse: Exchange response
63
"""
64
```
65
66
### UserTokenProvider
67
68
Base user token provider interface that defines the core OAuth functionality for token management in bot applications.
69
70
```python { .api }
71
class UserTokenProvider:
72
async def get_user_token(self, turn_context: TurnContext, connection_name: str, magic_code: str = None):
73
"""Get user token from OAuth provider."""
74
75
async def sign_out_user(self, turn_context: TurnContext, connection_name: str, user_id: str = None):
76
"""Sign out user from OAuth provider."""
77
78
async def get_oauth_sign_in_link(self, turn_context: TurnContext, connection_name: str):
79
"""Get OAuth sign-in link."""
80
```
81
82
### ConnectorClientBuilder
83
84
Abstract base class for building connector clients with authentication support for Bot Framework communication.
85
86
```python { .api }
87
class ConnectorClientBuilder:
88
async def create_connector_client(self, service_url: str, identity=None, audience: str = None):
89
"""
90
Create connector client with authentication.
91
92
Args:
93
service_url (str): Service URL
94
identity (optional): Claims identity
95
audience (str, optional): Target audience
96
97
Returns:
98
ConnectorClient: Authenticated connector client
99
"""
100
```
101
102
## Usage Examples
103
104
### Basic OAuth Flow
105
106
```python
107
from botbuilder.core import ActivityHandler, TurnContext, MessageFactory, CardFactory
108
109
class OAuthBot(ActivityHandler):
110
def __init__(self, connection_name: str):
111
self.connection_name = connection_name
112
113
async def on_message_activity(self, turn_context: TurnContext):
114
text = turn_context.activity.text.lower()
115
116
if text in ["login", "signin"]:
117
await self.send_oauth_card(turn_context)
118
elif text in ["logout", "signout"]:
119
await self.sign_out_user(turn_context)
120
elif text == "profile":
121
await self.show_user_profile(turn_context)
122
else:
123
await turn_context.send_activity(MessageFactory.text("Say 'login' to authenticate"))
124
125
async def on_token_response_event(self, turn_context: TurnContext):
126
# Handle successful OAuth token response
127
token_response = turn_context.activity.value
128
if token_response and token_response.get("token"):
129
await turn_context.send_activity(MessageFactory.text("Authentication successful!"))
130
await self.show_user_profile(turn_context)
131
else:
132
await turn_context.send_activity(MessageFactory.text("Authentication failed"))
133
134
async def send_oauth_card(self, turn_context: TurnContext):
135
# Create OAuth card
136
card = CardFactory.oauth_card(
137
connection_name=self.connection_name,
138
title="Please sign in",
139
text="Click below to sign in with your account"
140
)
141
142
reply = MessageFactory.attachment(card)
143
await turn_context.send_activity(reply)
144
145
async def sign_out_user(self, turn_context: TurnContext):
146
# Get the adapter (assumes it implements ExtendedUserTokenProvider)
147
adapter = turn_context.adapter
148
149
await adapter.sign_out_user(turn_context, self.connection_name)
150
await turn_context.send_activity(MessageFactory.text("You have been signed out"))
151
152
async def show_user_profile(self, turn_context: TurnContext):
153
# Get user token
154
adapter = turn_context.adapter
155
token_response = await adapter.get_user_token(turn_context, self.connection_name)
156
157
if token_response:
158
# Use token to call external API (example with Microsoft Graph)
159
user_info = await self.get_user_info_from_graph(token_response.token)
160
await turn_context.send_activity(
161
MessageFactory.text(f"Hello {user_info.get('displayName', 'User')}!")
162
)
163
else:
164
await self.send_oauth_card(turn_context)
165
166
async def get_user_info_from_graph(self, token: str):
167
# Example: Call Microsoft Graph API
168
import aiohttp
169
170
headers = {"Authorization": f"Bearer {token}"}
171
async with aiohttp.ClientSession() as session:
172
async with session.get("https://graph.microsoft.com/v1.0/me", headers=headers) as response:
173
if response.status == 200:
174
return await response.json()
175
return {}
176
```
177
178
### Magic Code Flow
179
180
```python
181
async def on_message_activity(self, turn_context: TurnContext):
182
text = turn_context.activity.text
183
184
# Check if message contains magic code (6-digit number)
185
if text.isdigit() and len(text) == 6:
186
await self.process_magic_code(turn_context, text)
187
else:
188
await self.send_oauth_card(turn_context)
189
190
async def process_magic_code(self, turn_context: TurnContext, magic_code: str):
191
adapter = turn_context.adapter
192
193
# Try to get token using magic code
194
token_response = await adapter.get_user_token(
195
turn_context,
196
self.connection_name,
197
magic_code=magic_code
198
)
199
200
if token_response:
201
await turn_context.send_activity(MessageFactory.text("Authentication successful with magic code!"))
202
await self.show_user_profile(turn_context)
203
else:
204
await turn_context.send_activity(MessageFactory.text("Invalid magic code. Please try again."))
205
await self.send_oauth_card(turn_context)
206
```
207
208
### Token Exchange
209
210
```python
211
async def handle_token_exchange(self, turn_context: TurnContext, sso_token: str):
212
adapter = turn_context.adapter
213
214
# Create token exchange request
215
exchange_request = {
216
"token": sso_token,
217
"uri": "https://graph.microsoft.com/.default"
218
}
219
220
# Exchange SSO token for access token
221
token_response = await adapter.exchange_token(
222
turn_context,
223
self.connection_name,
224
turn_context.activity.from_property.id,
225
exchange_request
226
)
227
228
if token_response:
229
await turn_context.send_activity(MessageFactory.text("Token exchange successful!"))
230
# Use the exchanged token
231
await self.use_access_token(token_response.token)
232
else:
233
await turn_context.send_activity(MessageFactory.text("Token exchange failed"))
234
await self.send_oauth_card(turn_context)
235
```
236
237
## Types
238
239
```python { .api }
240
class TokenResponse:
241
"""OAuth token response."""
242
def __init__(self):
243
self.channel_id: str = None
244
self.connection_name: str = None
245
self.token: str = None
246
self.expiration: str = None
247
248
class TokenExchangeRequest:
249
"""Token exchange request."""
250
def __init__(self):
251
self.uri: str = None
252
self.token: str = None
253
254
class OAuthCard:
255
"""OAuth card for authentication."""
256
def __init__(self):
257
self.text: str = None
258
self.connection_name: str = None
259
self.buttons: list = None
260
```