or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

activity-types-enums.mdattachments-media.mdauthentication-oauth.mdchannel-conversation.mdcore-activity.mdentities-semantic.mdindex.mdrich-cards.mdteams-integration.md

authentication-oauth.mddocs/

0

# Authentication and OAuth

1

2

OAuth token management, authentication flows, and signin processes including token requests, responses, and exchange mechanisms for secure bot authentication in Bot Framework applications.

3

4

## Core Authentication Models

5

6

### Token Response

7

8

Response model containing OAuth tokens returned from authentication providers.

9

10

```python { .api }

11

class TokenResponse(Model):

12

def __init__(self, *, connection_name: str = None, token: str = None,

13

expiration: str = None, channel_id: str = None, **kwargs): ...

14

```

15

16

```python

17

from botbuilder.schema import TokenResponse

18

19

# Token response from OAuth provider

20

token_response = TokenResponse(

21

connection_name="MyOAuthConnection",

22

token="eyJhbGciOiJSUzI1NiIsImtpZCI6...",

23

expiration="2023-12-31T23:59:59Z",

24

channel_id="webchat"

25

)

26

```

27

28

### Token Request

29

30

Request model for initiating OAuth token requests.

31

32

```python { .api }

33

class TokenRequest(Model):

34

def __init__(self, *, provider: str = None, settings: dict = None, **kwargs): ...

35

```

36

37

```python

38

from botbuilder.schema import TokenRequest

39

40

token_request = TokenRequest(

41

provider="AzureAD",

42

settings={

43

"scopes": ["User.Read", "Mail.Read"],

44

"tenant": "common"

45

}

46

)

47

```

48

49

## Token Exchange Models

50

51

### Token Exchange Invoke Request

52

53

Model for token exchange operations in Bot Framework applications.

54

55

```python { .api }

56

class TokenExchangeInvokeRequest(Model):

57

def __init__(self, *, id: str = None, connection_name: str = None,

58

token: str = None, properties: dict = None, **kwargs): ...

59

```

60

61

```python

62

from botbuilder.schema import TokenExchangeInvokeRequest

63

64

exchange_request = TokenExchangeInvokeRequest(

65

id="unique-exchange-id",

66

connection_name="MyOAuthConnection",

67

token="original-token-to-exchange",

68

properties={"scope": "additional-permissions"}

69

)

70

```

71

72

### Token Exchange Invoke Response

73

74

Response model for token exchange operations.

75

76

```python { .api }

77

class TokenExchangeInvokeResponse(Model):

78

def __init__(self, *, id: str = None, connection_name: str = None,

79

failure_detail: str = None, **kwargs): ...

80

```

81

82

```python

83

from botbuilder.schema import TokenExchangeInvokeResponse

84

85

# Successful exchange response

86

success_response = TokenExchangeInvokeResponse(

87

id="unique-exchange-id",

88

connection_name="MyOAuthConnection"

89

)

90

91

# Failed exchange response

92

failure_response = TokenExchangeInvokeResponse(

93

id="unique-exchange-id",

94

connection_name="MyOAuthConnection",

95

failure_detail="Token expired or invalid"

96

)

97

```

98

99

### Token Exchange State

100

101

State information for token exchange flows.

102

103

```python { .api }

104

class TokenExchangeState(Model):

105

def __init__(self, *, connection_name: str = None, conversation = None,

106

relates_to = None, bot_url: str = None, **kwargs): ...

107

```

108

109

```python

110

from botbuilder.schema import TokenExchangeState, ConversationReference

111

112

exchange_state = TokenExchangeState(

113

connection_name="MyOAuthConnection",

114

conversation=conversation_reference,

115

bot_url="https://mybot.azurewebsites.net"

116

)

117

```

118

119

## Authentication Cards

120

121

### OAuth Card

122

123

Card specifically designed for OAuth authentication flows.

124

125

```python { .api }

126

class OAuthCard(Model):

127

def __init__(self, *, text: str = None, connection_name: str = None,

128

buttons: List[CardAction] = None, **kwargs): ...

129

```

130

131

```python

132

from botbuilder.schema import OAuthCard, CardAction

133

134

oauth_card = OAuthCard(

135

text="Please sign in to access your account",

136

connection_name="MyOAuthConnection",

137

buttons=[

138

CardAction(

139

type="signin",

140

title="Sign In",

141

value="https://oauth.provider.com/signin"

142

)

143

]

144

)

145

```

146

147

### Signin Card

148

149

Generic signin card for authentication prompts.

150

151

```python { .api }

152

class SigninCard(Model):

153

def __init__(self, *, text: str = None, buttons: List[CardAction] = None, **kwargs): ...

154

```

155

156

```python

157

from botbuilder.schema import SigninCard, CardAction

158

159

signin_card = SigninCard(

160

text="Authentication required to continue",

161

buttons=[

162

CardAction(type="signin", title="Sign In", value="signin_url"),

163

CardAction(type="imBack", title="Cancel", value="cancel")

164

]

165

)

166

```

167

168

## Authentication Constants

169

170

### Signin Constants

171

172

Constants for signin operations and event names.

173

174

```python { .api }

175

class SignInConstants:

176

verify_state_operation_name: str = "signin/verifyState"

177

token_exchange_operation_name: str = "signin/tokenExchange"

178

token_response_event_name: str = "tokens/response"

179

```

180

181

```python

182

from botbuilder.schema import SignInConstants

183

184

# Use constants for consistent event handling

185

if activity.name == SignInConstants.token_response_event_name:

186

# Handle token response

187

pass

188

elif activity.name == SignInConstants.verify_state_operation_name:

189

# Handle state verification

190

pass

191

```

192

193

### Caller ID Constants

194

195

Constants for identifying Bot Framework channels and authentication contexts.

196

197

```python { .api }

198

class CallerIdConstants:

199

public_azure_channel: str = "urn:botframework:azure"

200

us_gov_channel: str = "urn:botframework:azureusgov"

201

bot_to_bot_prefix: str = "urn:botframework:aadappid:"

202

```

203

204

```python

205

from botbuilder.schema import CallerIdConstants

206

207

def is_azure_channel(caller_id: str) -> bool:

208

return caller_id == CallerIdConstants.public_azure_channel

209

210

def is_bot_to_bot(caller_id: str) -> bool:

211

return caller_id and caller_id.startswith(CallerIdConstants.bot_to_bot_prefix)

212

```

213

214

## Authentication Flow Patterns

215

216

### Basic OAuth Flow

217

218

```python

219

from botbuilder.schema import (

220

Activity, ActivityTypes, Attachment, OAuthCard,

221

CardAction, TokenResponse

222

)

223

224

async def start_oauth_flow(connection_name: str) -> Activity:

225

"""Initiate OAuth authentication flow"""

226

oauth_card = OAuthCard(

227

text="Please sign in to continue using the bot",

228

connection_name=connection_name,

229

buttons=[

230

CardAction(type="signin", title="Sign In")

231

]

232

)

233

234

attachment = Attachment(

235

content_type="application/vnd.microsoft.card.oauth",

236

content=oauth_card

237

)

238

239

return Activity(

240

type=ActivityTypes.message,

241

text="Authentication required",

242

attachments=[attachment]

243

)

244

245

async def handle_token_response(activity: Activity) -> str:

246

"""Handle token response from OAuth provider"""

247

if activity.name == "tokens/response":

248

token_response = TokenResponse(**activity.value)

249

return token_response.token

250

return None

251

```

252

253

### Token Exchange Flow

254

255

```python

256

from botbuilder.schema import (

257

TokenExchangeInvokeRequest, TokenExchangeInvokeResponse,

258

InvokeResponse

259

)

260

261

async def handle_token_exchange(request: TokenExchangeInvokeRequest) -> InvokeResponse:

262

"""Handle token exchange invoke"""

263

try:

264

# Attempt to exchange token

265

exchanged_token = await exchange_token(

266

request.token,

267

request.connection_name

268

)

269

270

response = TokenExchangeInvokeResponse(

271

id=request.id,

272

connection_name=request.connection_name

273

)

274

275

return InvokeResponse(

276

status=200,

277

body=response

278

)

279

280

except Exception as e:

281

error_response = TokenExchangeInvokeResponse(

282

id=request.id,

283

connection_name=request.connection_name,

284

failure_detail=str(e)

285

)

286

287

return InvokeResponse(

288

status=412, # Precondition Failed

289

body=error_response

290

)

291

```

292

293

### State Verification

294

295

```python

296

from botbuilder.schema import SignInConstants

297

298

async def verify_signin_state(activity: Activity) -> bool:

299

"""Verify signin state for security"""

300

if activity.name == SignInConstants.verify_state_operation_name:

301

state = activity.value.get('state')

302

# Verify state matches expected value

303

return await validate_state(state)

304

return False

305

```

306

307

## Integration with Bot Framework

308

309

### Authentication Middleware

310

311

```python

312

from botbuilder.schema import Activity, ActivityTypes

313

314

class AuthenticationMiddleware:

315

def __init__(self, connection_name: str):

316

self.connection_name = connection_name

317

318

async def on_message_activity(self, turn_context, next_handler):

319

"""Check authentication before processing messages"""

320

token = await self.get_user_token(turn_context)

321

322

if not token:

323

# Send OAuth card

324

oauth_activity = await self.create_oauth_prompt()

325

await turn_context.send_activity(oauth_activity)

326

return

327

328

# User is authenticated, continue processing

329

await next_handler()

330

331

async def get_user_token(self, turn_context) -> str:

332

"""Get user token from token store"""

333

# Implementation depends on token store

334

pass

335

336

async def create_oauth_prompt(self) -> Activity:

337

"""Create OAuth authentication prompt"""

338

return await start_oauth_flow(self.connection_name)

339

```

340

341

### Secure API Calls

342

343

```python

344

import aiohttp

345

from botbuilder.schema import TokenResponse

346

347

async def make_authenticated_api_call(token: str, api_url: str):

348

"""Make API call with OAuth token"""

349

headers = {

350

'Authorization': f'Bearer {token}',

351

'Content-Type': 'application/json'

352

}

353

354

async with aiohttp.ClientSession() as session:

355

async with session.get(api_url, headers=headers) as response:

356

if response.status == 401:

357

raise AuthenticationError("Token expired or invalid")

358

return await response.json()

359

360

class AuthenticationError(Exception):

361

"""Custom exception for authentication errors"""

362

pass

363

```

364

365

## Security Best Practices

366

367

### Token Management

368

369

1. **Store tokens securely** - Never log or persist tokens in plain text

370

2. **Validate token expiration** - Check expiration before using tokens

371

3. **Handle token refresh** - Implement refresh token flows where supported

372

4. **Scope validation** - Verify tokens have required scopes

373

374

```python

375

from datetime import datetime

376

from botbuilder.schema import TokenResponse

377

378

def is_token_valid(token_response: TokenResponse) -> bool:

379

"""Validate token expiration"""

380

if not token_response.expiration:

381

return True # No expiration set

382

383

expiration = datetime.fromisoformat(token_response.expiration.replace('Z', '+00:00'))

384

return datetime.now(expiration.tzinfo) < expiration

385

386

def validate_token_scopes(token: str, required_scopes: list) -> bool:

387

"""Validate token has required scopes"""

388

# Implementation depends on token format (JWT, etc.)

389

pass

390

```

391

392

### Connection Security

393

394

1. **Use HTTPS** - All OAuth flows must use HTTPS

395

2. **Validate state parameters** - Prevent CSRF attacks

396

3. **Secure redirect URIs** - Use exact match for redirect URIs

397

4. **Rate limiting** - Implement rate limiting for auth requests

398

399

```python

400

import hashlib

401

import secrets

402

403

def generate_state() -> str:

404

"""Generate secure state parameter"""

405

return secrets.token_urlsafe(32)

406

407

def validate_state(received_state: str, expected_state: str) -> bool:

408

"""Validate state parameter to prevent CSRF"""

409

return secrets.compare_digest(received_state, expected_state)

410

```

411

412

## Error Handling

413

414

```python

415

from botbuilder.schema import Activity, ActivityTypes

416

417

def create_auth_error_response(error_message: str) -> Activity:

418

"""Create user-friendly authentication error response"""

419

return Activity(

420

type=ActivityTypes.message,

421

text=f"Authentication failed: {error_message}. Please try signing in again."

422

)

423

424

def handle_oauth_errors(error_type: str) -> Activity:

425

"""Handle different OAuth error scenarios"""

426

error_messages = {

427

'access_denied': "Access was denied. Please try again or contact support.",

428

'invalid_request': "Invalid authentication request. Please try again.",

429

'server_error': "Authentication server error. Please try again later.",

430

'temporarily_unavailable': "Authentication service temporarily unavailable."

431

}

432

433

message = error_messages.get(error_type, "Unknown authentication error occurred.")

434

return create_auth_error_response(message)

435

```