or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

common-utilities.mddjango-integration.mdflask-integration.mdhttp-clients.mdindex.mdjose.mdoauth1.mdoauth2.mdoidc.md

flask-integration.mddocs/

0

# Flask Integration

1

2

Complete Flask integration providing OAuth client registry and OAuth 2.0 server implementations. Supports automatic token management, session integration, Flask-specific request/response handling, and seamless integration with Flask's application context and blueprints.

3

4

## Capabilities

5

6

### Flask OAuth Client

7

8

Flask-specific OAuth client registry with automatic token management and session integration.

9

10

```python { .api }

11

class OAuth:

12

"""Flask OAuth client registry."""

13

14

def __init__(self, app: Flask = None) -> None:

15

"""

16

Initialize Flask OAuth registry.

17

18

Args:

19

app: Flask application instance

20

"""

21

22

def init_app(self, app: Flask) -> None:

23

"""

24

Initialize OAuth registry with Flask app.

25

26

Args:

27

app: Flask application instance

28

"""

29

30

def register(self, name: str, client_id: str = None, client_secret: str = None, client_kwargs: dict = None, **kwargs) -> 'FlaskOAuth2App':

31

"""

32

Register an OAuth provider.

33

34

Args:

35

name: Provider name for reference

36

client_id: OAuth client ID

37

client_secret: OAuth client secret

38

client_kwargs: Additional client configuration

39

**kwargs: Provider configuration (server_metadata_url, etc.)

40

41

Returns:

42

FlaskOAuth2App instance

43

"""

44

45

def create_client(self, name: str = None) -> 'FlaskOAuth2App':

46

"""

47

Create OAuth client by name.

48

49

Args:

50

name: Registered provider name

51

52

Returns:

53

FlaskOAuth2App instance

54

"""

55

56

class FlaskOAuth2App:

57

"""Flask OAuth 2.0 application integration."""

58

59

def __init__(self, name: str, client_id: str, client_secret: str = None, client_kwargs: dict = None, **kwargs) -> None:

60

"""

61

Initialize Flask OAuth 2.0 app.

62

63

Args:

64

name: Application name

65

client_id: OAuth client ID

66

client_secret: OAuth client secret

67

client_kwargs: Additional client configuration

68

**kwargs: Provider configuration

69

"""

70

71

def authorize_redirect(self, redirect_uri: str = None, **kwargs) -> Response:

72

"""

73

Create authorization redirect response.

74

75

Args:

76

redirect_uri: Callback URI

77

**kwargs: Additional authorization parameters

78

79

Returns:

80

Flask redirect response

81

"""

82

83

def authorize_access_token(self, **kwargs) -> dict:

84

"""

85

Handle authorization callback and fetch access token.

86

87

Args:

88

**kwargs: Additional token parameters

89

90

Returns:

91

Token dictionary

92

"""

93

94

def parse_id_token(self, token: dict, nonce: str = None, claims_options: dict = None) -> dict:

95

"""

96

Parse and validate OpenID Connect ID token.

97

98

Args:

99

token: Token dictionary containing id_token

100

nonce: Expected nonce value

101

claims_options: Claims validation options

102

103

Returns:

104

ID token claims dictionary

105

"""

106

107

def get(self, url: str, token: dict = None, **kwargs) -> Response:

108

"""

109

Make authenticated GET request.

110

111

Args:

112

url: Request URL

113

token: Access token dictionary

114

**kwargs: Additional request parameters

115

116

Returns:

117

HTTP response

118

"""

119

120

def post(self, url: str, token: dict = None, **kwargs) -> Response:

121

"""

122

Make authenticated POST request.

123

124

Args:

125

url: Request URL

126

token: Access token dictionary

127

**kwargs: Additional request parameters

128

129

Returns:

130

HTTP response

131

"""

132

133

def put(self, url: str, token: dict = None, **kwargs) -> Response:

134

"""

135

Make authenticated PUT request.

136

137

Args:

138

url: Request URL

139

token: Access token dictionary

140

**kwargs: Additional request parameters

141

142

Returns:

143

HTTP response

144

"""

145

146

def delete(self, url: str, token: dict = None, **kwargs) -> Response:

147

"""

148

Make authenticated DELETE request.

149

150

Args:

151

url: Request URL

152

token: Access token dictionary

153

**kwargs: Additional request parameters

154

155

Returns:

156

HTTP response

157

"""

158

159

class FlaskOAuth1App:

160

"""Flask OAuth 1.0 application integration."""

161

162

def __init__(self, name: str, client_id: str, client_secret: str = None, client_kwargs: dict = None, **kwargs) -> None:

163

"""

164

Initialize Flask OAuth 1.0 app.

165

166

Args:

167

name: Application name

168

client_id: OAuth client ID

169

client_secret: OAuth client secret

170

client_kwargs: Additional client configuration

171

**kwargs: Provider configuration

172

"""

173

174

def authorize_redirect(self, callback: str = None, **kwargs) -> Response:

175

"""

176

Create authorization redirect response.

177

178

Args:

179

callback: Callback URI

180

**kwargs: Additional authorization parameters

181

182

Returns:

183

Flask redirect response

184

"""

185

186

def authorize_access_token(self) -> dict:

187

"""

188

Handle authorization callback and fetch access token.

189

190

Returns:

191

Token dictionary

192

"""

193

194

def get(self, url: str, token: dict = None, **kwargs) -> Response:

195

"""Make authenticated GET request."""

196

197

def post(self, url: str, token: dict = None, **kwargs) -> Response:

198

"""Make authenticated POST request."""

199

```

200

201

### Flask OAuth 2.0 Server

202

203

Flask-specific OAuth 2.0 authorization server implementation with Flask integration features.

204

205

```python { .api }

206

class AuthorizationServer:

207

"""Flask OAuth 2.0 authorization server."""

208

209

def __init__(self, app: Flask = None, query_client: callable = None, save_token: callable = None) -> None:

210

"""

211

Initialize Flask authorization server.

212

213

Args:

214

app: Flask application instance

215

query_client: Function to query client by client_id

216

save_token: Function to save issued tokens

217

"""

218

219

def init_app(self, app: Flask, query_client: callable = None, save_token: callable = None) -> None:

220

"""

221

Initialize authorization server with Flask app.

222

223

Args:

224

app: Flask application instance

225

query_client: Function to query client by client_id

226

save_token: Function to save issued tokens

227

"""

228

229

def register_grant(self, grant_cls: type, extensions: list = None) -> None:

230

"""

231

Register a grant type.

232

233

Args:

234

grant_cls: Grant class to register

235

extensions: List of grant extensions

236

"""

237

238

def register_endpoint(self, endpoint: object) -> None:

239

"""

240

Register an endpoint.

241

242

Args:

243

endpoint: Endpoint instance

244

"""

245

246

def create_authorization_response(self, request: Request = None, grant_user: callable = None) -> Response:

247

"""

248

Create authorization response.

249

250

Args:

251

request: Flask request object (uses current request if None)

252

grant_user: Function to grant authorization to user

253

254

Returns:

255

Flask response object

256

"""

257

258

def create_token_response(self, request: Request = None) -> Response:

259

"""

260

Create token response.

261

262

Args:

263

request: Flask request object (uses current request if None)

264

265

Returns:

266

Flask response object

267

"""

268

269

def create_revocation_response(self, request: Request = None) -> Response:

270

"""

271

Create revocation response.

272

273

Args:

274

request: Flask request object (uses current request if None)

275

276

Returns:

277

Flask response object

278

"""

279

280

def create_introspection_response(self, request: Request = None) -> Response:

281

"""

282

Create introspection response.

283

284

Args:

285

request: Flask request object (uses current request if None)

286

287

Returns:

288

Flask response object

289

"""

290

291

class ResourceProtector:

292

"""Flask OAuth 2.0 resource protector."""

293

294

def __init__(self) -> None:

295

"""Initialize Flask resource protector."""

296

297

def register_token_validator(self, validator: 'BearerTokenValidator') -> None:

298

"""

299

Register bearer token validator.

300

301

Args:

302

validator: Token validator instance

303

"""

304

305

def __call__(self, scopes: list = None, optional: bool = False) -> callable:

306

"""

307

Decorator for protecting Flask routes.

308

309

Args:

310

scopes: Required scopes

311

optional: Whether protection is optional

312

313

Returns:

314

Route decorator function

315

"""

316

317

def acquire_token(self, scopes: list = None, request: Request = None) -> 'OAuth2Token':

318

"""

319

Acquire token from Flask request.

320

321

Args:

322

scopes: Required scopes

323

request: Flask request object

324

325

Returns:

326

OAuth2Token object

327

"""

328

329

# Current token proxy for accessing token in protected routes

330

current_token: LocalProxy

331

```

332

333

### Flask Integration Base Classes

334

335

Base classes for Flask-specific OAuth integrations.

336

337

```python { .api }

338

class FlaskIntegration:

339

"""Flask framework integration base."""

340

341

def __init__(self, oauth: OAuth, name: str, fetch_token: callable = None, update_token: callable = None) -> None:

342

"""

343

Initialize Flask integration.

344

345

Args:

346

oauth: OAuth registry instance

347

name: Provider name

348

fetch_token: Function to fetch stored token

349

update_token: Function to update stored token

350

"""

351

352

def load_server_metadata(self) -> dict:

353

"""

354

Load OAuth server metadata.

355

356

Returns:

357

Server metadata dictionary

358

"""

359

```

360

361

### Token Management

362

363

Flask-specific token management utilities.

364

365

```python { .api }

366

def token_update(token: dict, refresh_token: str = None, access_token: str = None) -> None:

367

"""

368

Update token in Flask session.

369

370

Args:

371

token: Token dictionary to update

372

refresh_token: New refresh token

373

access_token: New access token

374

"""

375

```

376

377

### Flask Signals

378

379

Flask signals for OAuth events.

380

381

```python { .api }

382

# OAuth 2.0 server signals

383

client_authenticated: NamedSignal # Emitted when client is authenticated

384

token_authenticated: NamedSignal # Emitted when token is authenticated

385

token_revoked: NamedSignal # Emitted when token is revoked

386

```

387

388

## Usage Examples

389

390

### OAuth Client Setup

391

392

```python

393

from flask import Flask, session, url_for, redirect, jsonify

394

from authlib.integrations.flask_client import OAuth

395

396

app = Flask(__name__)

397

app.secret_key = 'your-secret-key'

398

399

# Initialize OAuth registry

400

oauth = OAuth(app)

401

402

# Register Google OAuth provider

403

google = oauth.register(

404

name='google',

405

client_id='your-google-client-id',

406

client_secret='your-google-client-secret',

407

server_metadata_url='https://accounts.google.com/.well-known/openid_configuration',

408

client_kwargs={

409

'scope': 'openid email profile'

410

}

411

)

412

413

# Register GitHub OAuth provider

414

github = oauth.register(

415

name='github',

416

client_id='your-github-client-id',

417

client_secret='your-github-client-secret',

418

access_token_url='https://github.com/login/oauth/access_token',

419

access_token_params=None,

420

authorize_url='https://github.com/login/oauth/authorize',

421

authorize_params=None,

422

api_base_url='https://api.github.com/',

423

client_kwargs={'scope': 'user:email'},

424

)

425

426

@app.route('/login/google')

427

def google_login():

428

redirect_uri = url_for('google_callback', _external=True)

429

return google.authorize_redirect(redirect_uri)

430

431

@app.route('/callback/google')

432

def google_callback():

433

token = google.authorize_access_token()

434

435

# Parse ID token for OpenID Connect

436

user_info = google.parse_id_token(token)

437

session['user'] = user_info

438

439

return redirect(url_for('profile'))

440

441

@app.route('/login/github')

442

def github_login():

443

redirect_uri = url_for('github_callback', _external=True)

444

return github.authorize_redirect(redirect_uri)

445

446

@app.route('/callback/github')

447

def github_callback():

448

token = github.authorize_access_token()

449

450

# Get user info from API

451

response = github.get('user', token=token)

452

user_info = response.json()

453

session['user'] = user_info

454

455

return redirect(url_for('profile'))

456

457

@app.route('/profile')

458

def profile():

459

user = session.get('user')

460

if not user:

461

return redirect(url_for('login'))

462

return jsonify(user)

463

```

464

465

### OAuth 2.0 Authorization Server

466

467

```python

468

from flask import Flask, request, session, render_template

469

from authlib.integrations.flask_oauth2 import AuthorizationServer, ResourceProtector

470

from authlib.oauth2.rfc6749.grants import AuthorizationCodeGrant, RefreshTokenGrant

471

from authlib.oauth2.rfc6749.models import ClientMixin, AuthorizationCodeMixin, TokenMixin

472

473

app = Flask(__name__)

474

app.secret_key = 'your-secret-key'

475

476

# Database models

477

class Client(db.Model, ClientMixin):

478

id = db.Column(db.Integer, primary_key=True)

479

client_id = db.Column(db.String(40), unique=True)

480

client_secret = db.Column(db.String(55))

481

482

def get_client_id(self):

483

return self.client_id

484

485

def check_client_secret(self, client_secret):

486

return self.client_secret == client_secret

487

488

class AuthorizationCode(db.Model, AuthorizationCodeMixin):

489

id = db.Column(db.Integer, primary_key=True)

490

user_id = db.Column(db.Integer, nullable=False)

491

code = db.Column(db.String(120), unique=True, nullable=False)

492

client_id = db.Column(db.String(40), nullable=False)

493

494

def get_user_id(self):

495

return self.user_id

496

497

class Token(db.Model, TokenMixin):

498

id = db.Column(db.Integer, primary_key=True)

499

user_id = db.Column(db.Integer, nullable=False)

500

client_id = db.Column(db.String(40), nullable=False)

501

access_token = db.Column(db.String(255), unique=True)

502

refresh_token = db.Column(db.String(255), unique=True)

503

504

def get_user_id(self):

505

return self.user_id

506

507

# Query functions

508

def query_client(client_id):

509

return Client.query.filter_by(client_id=client_id).first()

510

511

def save_token(token, request, *args, **kwargs):

512

if request.grant_type == 'authorization_code':

513

code = request.credential

514

item = Token(

515

client_id=request.client.client_id,

516

user_id=code.get_user_id(),

517

**token

518

)

519

else:

520

item = Token(

521

client_id=request.client.client_id,

522

**token

523

)

524

db.session.add(item)

525

db.session.commit()

526

527

# Initialize authorization server

528

authorization = AuthorizationServer(app, query_client=query_client, save_token=save_token)

529

530

# Register grants

531

authorization.register_grant(AuthorizationCodeGrant)

532

authorization.register_grant(RefreshTokenGrant)

533

534

@app.route('/authorize', methods=['GET', 'POST'])

535

def authorize():

536

user = get_current_user() # Your user authentication

537

538

if request.method == 'GET':

539

try:

540

grant = authorization.validate_consent_request(end_user=user)

541

return render_template('authorize.html', grant=grant, user=user)

542

except OAuth2Error as error:

543

return {'error': error.error}, 400

544

545

if not user:

546

return redirect('/login')

547

548

if request.form.get('confirm'):

549

grant_user = user

550

else:

551

grant_user = None

552

553

return authorization.create_authorization_response(grant_user=grant_user)

554

555

@app.route('/token', methods=['POST'])

556

def issue_token():

557

return authorization.create_token_response()

558

559

@app.route('/revoke', methods=['POST'])

560

def revoke_token():

561

return authorization.create_revocation_response()

562

```

563

564

### Resource Protection

565

566

```python

567

from authlib.integrations.flask_oauth2 import ResourceProtector, current_token

568

from authlib.oauth2.rfc6750 import BearerTokenValidator

569

570

# Token validator

571

class MyBearerTokenValidator(BearerTokenValidator):

572

def authenticate_token(self, token_string):

573

return Token.query.filter_by(access_token=token_string).first()

574

575

def request_invalid(self, request):

576

return False

577

578

def token_revoked(self, token):

579

return token.revoked

580

581

# Initialize resource protector

582

require_oauth = ResourceProtector()

583

require_oauth.register_token_validator(MyBearerTokenValidator())

584

585

@app.route('/api/user')

586

@require_oauth('profile')

587

def api_user():

588

user = get_user_by_id(current_token.user_id)

589

return jsonify({

590

'id': user.id,

591

'username': user.username,

592

'email': user.email

593

})

594

595

@app.route('/api/posts')

596

@require_oauth('read')

597

def api_posts():

598

posts = Post.query.filter_by(user_id=current_token.user_id).all()

599

return jsonify([{

600

'id': post.id,

601

'title': post.title,

602

'content': post.content

603

} for post in posts])

604

605

@app.route('/api/posts', methods=['POST'])

606

@require_oauth('write')

607

def api_create_post():

608

data = request.get_json()

609

post = Post(

610

user_id=current_token.user_id,

611

title=data['title'],

612

content=data['content']

613

)

614

db.session.add(post)

615

db.session.commit()

616

return jsonify({'id': post.id}), 201

617

618

# Optional protection

619

@app.route('/api/public')

620

@require_oauth(optional=True)

621

def api_public():

622

if current_token:

623

return jsonify({'message': f'Hello {current_token.user_id}'})

624

else:

625

return jsonify({'message': 'Hello anonymous'})

626

```

627

628

### Using Flask Signals

629

630

```python

631

from authlib.integrations.flask_oauth2 import client_authenticated, token_authenticated

632

633

@client_authenticated.connect

634

def on_client_authenticated(sender, client=None):

635

print(f'Client {client.client_id} authenticated')

636

# Log client authentication, update metrics, etc.

637

638

@token_authenticated.connect

639

def on_token_authenticated(sender, token=None):

640

print(f'Token for user {token.user_id} authenticated')

641

# Update user last activity, log API usage, etc.

642

643

@token_revoked.connect

644

def on_token_revoked(sender, token=None):

645

print(f'Token {token.access_token} revoked')

646

# Clean up related resources, notify user, etc.

647

```

648

649

### Session-based Token Storage

650

651

```python

652

from flask import session

653

654

def fetch_token(name):

655

"""Fetch token from Flask session."""

656

return session.get(f'{name}_token')

657

658

def update_token(name, token, refresh_token=None, access_token=None):

659

"""Update token in Flask session."""

660

if refresh_token:

661

token['refresh_token'] = refresh_token

662

if access_token:

663

token['access_token'] = access_token

664

session[f'{name}_token'] = token

665

666

# Register provider with token callbacks

667

oauth.register(

668

name='provider',

669

client_id='client-id',

670

client_secret='client-secret',

671

fetch_token=lambda: fetch_token('provider'),

672

update_token=lambda token, **kwargs: update_token('provider', token, **kwargs)

673

)

674

```