or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdconfiguration-utilities.mdcore-application.mddatabase-models.mdindex.mdmonitoring-metrics.mdrbac-permissions.mdrest-api.mdservices-oauth.mdsingleuser-integration.mdspawners.md

singleuser-integration.mddocs/

0

# Single-user Server Integration

1

2

JupyterHub provides comprehensive tools and mixins for creating Hub-authenticated single-user servers and Jupyter server extensions. This system enables notebook servers to authenticate with the Hub and integrate seamlessly with the multi-user environment.

3

4

## Capabilities

5

6

### Single-user Application Factory

7

8

Core function for creating Hub-authenticated single-user server applications.

9

10

```python { .api }

11

def make_singleuser_app(cls):

12

"""

13

Create a single-user application class with Hub authentication.

14

15

Decorates a Jupyter server application class to add JupyterHub

16

authentication and integration capabilities.

17

18

Args:

19

cls: Jupyter server application class to enhance

20

21

Returns:

22

Enhanced application class with Hub authentication

23

"""

24

25

# Example usage

26

from jupyter_server.serverapp import ServerApp

27

from jupyterhub.singleuser import make_singleuser_app

28

29

@make_singleuser_app

30

class SingleUserNotebookApp(ServerApp):

31

"""Hub-authenticated notebook server"""

32

pass

33

```

34

35

### Hub Authentication Handlers

36

37

Base classes for implementing Hub-authenticated request handlers in single-user servers.

38

39

```python { .api }

40

class HubAuthenticatedHandler(BaseHandler):

41

"""

42

Base handler for Hub-authenticated requests in single-user servers.

43

44

Provides authentication integration with JupyterHub and handles

45

user verification and permission checking.

46

"""

47

48

@property

49

def hub_auth(self):

50

"""Hub authentication helper instance"""

51

52

@property

53

def hub_user(self):

54

"""Current Hub user information"""

55

56

@property

57

def current_user(self):

58

"""Current authenticated user (compatible with Jupyter handlers)"""

59

60

def get_current_user(self):

61

"""

62

Get current user from Hub authentication.

63

64

Returns:

65

User information dictionary or None if not authenticated

66

"""

67

68

def hub_user_from_cookie(self, cookie_name, cookie_value):

69

"""

70

Get user information from Hub cookie.

71

72

Args:

73

cookie_name: Name of the authentication cookie

74

cookie_value: Cookie value

75

76

Returns:

77

User information or None if invalid

78

"""

79

80

def check_hub_user(self, model):

81

"""

82

Check if current user matches the Hub user for this server.

83

84

Args:

85

model: User model to verify

86

87

Returns:

88

True if user is authorized

89

"""

90

91

class HubOAuthHandler(HubAuthenticatedHandler):

92

"""

93

OAuth-based Hub authentication handler for single-user servers.

94

95

Handles OAuth token validation and user authorization.

96

"""

97

98

def get_current_user_oauth(self, handler):

99

"""

100

Get current user via OAuth token validation.

101

102

Args:

103

handler: Request handler instance

104

105

Returns:

106

User information from OAuth validation

107

"""

108

```

109

110

### Jupyter Server Extension

111

112

Extension system for integrating JupyterHub with Jupyter server.

113

114

```python { .api }

115

def _jupyter_server_extension_points() -> List[Dict[str, str]]:

116

"""

117

Jupyter server extension discovery function.

118

119

Makes the JupyterHub single-user extension discoverable

120

by the Jupyter server extension system.

121

122

Returns:

123

List of extension metadata dictionaries with module and app info

124

"""

125

126

class JupyterHubSingleUser(ExtensionApp):

127

"""

128

Jupyter server extension for JupyterHub single-user integration.

129

130

Provides the extension implementation for Hub-authenticated

131

single-user servers.

132

"""

133

134

name: str = 'jupyterhub-singleuser'

135

description: str = 'JupyterHub single-user server extension'

136

137

def initialize_settings(self):

138

"""Initialize extension settings"""

139

140

def initialize_handlers(self):

141

"""Initialize request handlers"""

142

143

def initialize_templates(self):

144

"""Initialize Jinja2 templates"""

145

```

146

147

### Single-user Server Configuration

148

149

Configuration and startup utilities for single-user servers.

150

151

```python { .api }

152

class SingleUserNotebookApp(JupyterApp):

153

"""

154

Single-user notebook server application.

155

156

Main application class for JupyterHub single-user servers

157

with Hub authentication and integration.

158

"""

159

160

# Hub connection configuration

161

hub_host: str # JupyterHub host

162

hub_prefix: str # Hub URL prefix

163

hub_api_url: str # Hub API URL

164

165

# Authentication configuration

166

user: str # Username for this server

167

group: str # User group

168

cookie_name: str # Hub authentication cookie name

169

170

# Server configuration

171

base_url: str # Server base URL

172

default_url: str # Default redirect URL

173

174

# OAuth configuration (optional)

175

oauth_client_id: str # OAuth client ID

176

oauth_redirect_uri: str # OAuth redirect URI

177

178

def initialize(self, argv=None):

179

"""

180

Initialize single-user server.

181

182

Args:

183

argv: Command line arguments

184

"""

185

186

def start(self):

187

"""Start the single-user server"""

188

189

def make_singleuser_app(self):

190

"""Create single-user application with Hub integration"""

191

192

def main(argv=None) -> int:

193

"""

194

Main entry point for jupyterhub-singleuser command.

195

196

Args:

197

argv: Command line arguments

198

199

Returns:

200

Exit code (0 for success)

201

"""

202

```

203

204

### Hub API Integration

205

206

Tools for single-user servers to communicate with the JupyterHub API.

207

208

```python { .api }

209

class HubAPIClient:

210

"""

211

API client for single-user servers to communicate with Hub.

212

213

Provides methods for servers to report status and interact

214

with the central Hub.

215

"""

216

217

def __init__(self, hub_api_url: str, api_token: str):

218

"""

219

Initialize Hub API client.

220

221

Args:

222

hub_api_url: JupyterHub API base URL

223

api_token: API token for authentication

224

"""

225

226

async def get_user(self, username: str) -> Dict[str, Any]:

227

"""

228

Get user information from Hub.

229

230

Args:

231

username: Username to look up

232

233

Returns:

234

User information dictionary

235

"""

236

237

async def update_server_activity(self):

238

"""Update server activity timestamp in Hub"""

239

240

async def report_server_status(self, status: str):

241

"""

242

Report server status to Hub.

243

244

Args:

245

status: Server status ('running', 'ready', 'error', etc.)

246

"""

247

248

async def get_server_info(self, username: str, server_name: str = '') -> Dict[str, Any]:

249

"""

250

Get server information from Hub.

251

252

Args:

253

username: Server owner username

254

server_name: Named server (empty for default)

255

256

Returns:

257

Server information dictionary

258

"""

259

```

260

261

## Usage Examples

262

263

### Basic Single-user Server Setup

264

265

```python

266

# Create Hub-authenticated server application

267

from jupyter_server.serverapp import ServerApp

268

from jupyterhub.singleuser import make_singleuser_app

269

270

@make_singleuser_app

271

class MyHubServer(ServerApp):

272

"""Custom Hub-authenticated server"""

273

274

# Custom configuration

275

custom_setting = Unicode(

276

'default_value',

277

config=True,

278

help="Custom server setting"

279

)

280

281

def initialize_settings(self):

282

"""Initialize custom settings"""

283

super().initialize_settings()

284

285

# Add custom settings to web app

286

self.web_app.settings.update({

287

'custom_setting': self.custom_setting

288

})

289

290

# Start the server

291

if __name__ == '__main__':

292

MyHubServer.launch_instance()

293

```

294

295

### Custom Authentication Handler

296

297

```python

298

from jupyterhub.singleuser.mixins import HubAuthenticatedHandler

299

from tornado import web

300

301

class CustomAPIHandler(HubAuthenticatedHandler):

302

"""Custom API endpoint with Hub authentication"""

303

304

@web.authenticated

305

async def get(self):

306

"""GET endpoint with user authentication"""

307

user = self.current_user

308

if not user:

309

raise web.HTTPError(401, "Authentication required")

310

311

# Verify user matches server owner

312

if user['name'] != self.hub_user['name']:

313

raise web.HTTPError(403, "Access denied")

314

315

# Custom API logic

316

data = await self.get_user_data(user)

317

self.write({'data': data, 'user': user['name']})

318

319

async def get_user_data(self, user):

320

"""Get data specific to authenticated user"""

321

# Your custom data retrieval logic

322

return {'files': [], 'settings': {}}

323

324

# Register handler in server application

325

class CustomHubServer(SingleUserNotebookApp):

326

"""Server with custom API endpoints"""

327

328

def initialize_handlers(self):

329

"""Add custom handlers"""

330

super().initialize_handlers()

331

332

# Add custom API endpoint

333

self.web_app.add_handlers('.*', [

334

(r'/api/custom', CustomAPIHandler),

335

])

336

```

337

338

### Hub API Integration

339

340

```python

341

import os

342

from jupyterhub.singleuser import HubAPIClient

343

344

class IntegratedServer(SingleUserNotebookApp):

345

"""Server with Hub API integration"""

346

347

def __init__(self, **kwargs):

348

super().__init__(**kwargs)

349

350

# Initialize Hub API client

351

self.hub_client = HubAPIClient(

352

hub_api_url=os.environ['JUPYTERHUB_API_URL'],

353

api_token=os.environ['JUPYTERHUB_API_TOKEN']

354

)

355

356

async def initialize(self):

357

"""Initialize with Hub registration"""

358

await super().initialize()

359

360

# Report server startup to Hub

361

await self.hub_client.report_server_status('starting')

362

363

# Get user information from Hub

364

user_info = await self.hub_client.get_user(self.user)

365

self.log.info(f"Starting server for user: {user_info['name']}")

366

367

def start(self):

368

"""Start server and report to Hub"""

369

super().start()

370

371

# Report ready status

372

asyncio.create_task(self.hub_client.report_server_status('ready'))

373

374

async def periodic_status_update(self):

375

"""Periodic status updates to Hub"""

376

while True:

377

try:

378

await self.hub_client.update_server_activity()

379

await asyncio.sleep(300) # Update every 5 minutes

380

except Exception as e:

381

self.log.error(f"Failed to update Hub status: {e}")

382

await asyncio.sleep(60) # Retry after 1 minute

383

```

384

385

### Extension Integration

386

387

```python

388

from jupyterhub.singleuser.extension import JupyterHubSingleUser

389

from jupyter_server.extension.application import ExtensionApp

390

391

class CustomExtension(ExtensionApp):

392

"""Custom extension with Hub integration"""

393

394

name = 'my-hub-extension'

395

description = 'Custom JupyterHub extension'

396

397

def initialize_settings(self):

398

"""Initialize extension settings"""

399

# Get Hub authentication info

400

hub_user = self.serverapp.web_app.settings.get('hub_user')

401

if hub_user:

402

self.log.info(f"Extension initialized for user: {hub_user['name']}")

403

404

# Add custom settings

405

self.serverapp.web_app.settings.update({

406

'custom_extension_enabled': True

407

})

408

409

def initialize_handlers(self):

410

"""Initialize extension handlers"""

411

from .handlers import CustomHandler

412

413

self.handlers.extend([

414

(r'/my-extension/api/.*', CustomHandler),

415

])

416

417

# Register extension

418

def _jupyter_server_extension_paths():

419

"""Extension discovery function"""

420

return [{

421

'module': 'my_extension',

422

'app': CustomExtension

423

}]

424

```

425

426

### OAuth Integration

427

428

```python

429

class OAuthIntegratedServer(SingleUserNotebookApp):

430

"""Single-user server with OAuth integration"""

431

432

oauth_client_id = Unicode(

433

config=True,

434

help="OAuth client ID for Hub integration"

435

)

436

437

def initialize_settings(self):

438

"""Initialize OAuth settings"""

439

super().initialize_settings()

440

441

# Configure OAuth settings

442

if self.oauth_client_id:

443

self.web_app.settings.update({

444

'oauth_client_id': self.oauth_client_id,

445

'oauth_redirect_uri': f'{self.base_url}oauth-callback'

446

})

447

448

def initialize_handlers(self):

449

"""Add OAuth handlers"""

450

super().initialize_handlers()

451

452

# Add OAuth callback handler

453

self.web_app.add_handlers('.*', [

454

(r'/oauth-callback', OAuthCallbackHandler),

455

(r'/oauth-login', OAuthLoginHandler),

456

])

457

458

class OAuthCallbackHandler(HubAuthenticatedHandler):

459

"""Handle OAuth callback from Hub"""

460

461

async def get(self):

462

"""Process OAuth authorization callback"""

463

code = self.get_argument('code', None)

464

state = self.get_argument('state', None)

465

466

if not code:

467

raise web.HTTPError(400, "Missing authorization code")

468

469

# Exchange code for token with Hub

470

token_info = await self.exchange_oauth_code(code)

471

472

# Validate user and redirect

473

user = await self.validate_oauth_token(token_info['access_token'])

474

self.redirect(self.get_argument('next', '/'))

475

476

async def exchange_oauth_code(self, code):

477

"""Exchange OAuth code for access token"""

478

# Implementation depends on Hub OAuth configuration

479

pass

480

```

481

482

## Advanced Integration Patterns

483

484

### Multi-server User Environment

485

486

```python

487

class MultiServerManager:

488

"""Manage multiple servers for a single user"""

489

490

def __init__(self, hub_client, user_name):

491

self.hub_client = hub_client

492

self.user_name = user_name

493

self.servers = {}

494

495

async def get_user_servers(self):

496

"""Get all servers for user from Hub"""

497

user_info = await self.hub_client.get_user(self.user_name)

498

return user_info.get('servers', {})

499

500

async def start_named_server(self, server_name, server_options=None):

501

"""Start a named server for user"""

502

response = await self.hub_client.start_server(

503

username=self.user_name,

504

server_name=server_name,

505

options=server_options or {}

506

)

507

return response

508

509

async def stop_named_server(self, server_name):

510

"""Stop a named server"""

511

await self.hub_client.stop_server(

512

username=self.user_name,

513

server_name=server_name

514

)

515

```

516

517

### Server Health Monitoring

518

519

```python

520

class HealthMonitoredServer(SingleUserNotebookApp):

521

"""Server with health monitoring and auto-restart"""

522

523

health_check_interval = Integer(

524

60,

525

config=True,

526

help="Health check interval in seconds"

527

)

528

529

def start(self):

530

"""Start server with health monitoring"""

531

super().start()

532

533

# Start health monitoring task

534

asyncio.create_task(self.health_monitor())

535

536

async def health_monitor(self):

537

"""Monitor server health and report to Hub"""

538

while True:

539

try:

540

# Check server health

541

health_status = await self.check_health()

542

543

# Report to Hub

544

status = 'healthy' if health_status else 'unhealthy'

545

await self.hub_client.report_server_status(status)

546

547

# Auto-restart if unhealthy

548

if not health_status:

549

self.log.warning("Server unhealthy, requesting restart")

550

await self.request_restart()

551

552

await asyncio.sleep(self.health_check_interval)

553

except Exception as e:

554

self.log.error(f"Health monitoring error: {e}")

555

await asyncio.sleep(60)

556

557

async def check_health(self):

558

"""Check server health status"""

559

try:

560

# Custom health check logic

561

return True

562

except Exception:

563

return False

564

565

async def request_restart(self):

566

"""Request server restart from Hub"""

567

await self.hub_client.report_server_status('restart_requested')

568

```

569

570

### Shared Resource Access

571

572

```python

573

class SharedResourceServer(SingleUserNotebookApp):

574

"""Server with shared resource access controls"""

575

576

def initialize_settings(self):

577

"""Initialize with resource access controls"""

578

super().initialize_settings()

579

580

# Get user's resource permissions from Hub

581

user_scopes = self.hub_user.get('scopes', [])

582

583

# Configure resource access based on scopes

584

self.web_app.settings.update({

585

'shared_storage_access': 'shared-storage' in user_scopes,

586

'gpu_access': 'gpu-resources' in user_scopes,

587

'admin_access': 'admin' in user_scopes

588

})

589

590

def initialize_handlers(self):

591

"""Add resource access handlers"""

592

super().initialize_handlers()

593

594

# Add shared resource handlers

595

self.web_app.add_handlers('.*', [

596

(r'/shared-storage/.*', SharedStorageHandler),

597

(r'/gpu-status', GPUStatusHandler),

598

])

599

600

class SharedStorageHandler(HubAuthenticatedHandler):

601

"""Handler for shared storage access"""

602

603

@web.authenticated

604

async def get(self, path):

605

"""Access shared storage with permission check"""

606

if not self.settings.get('shared_storage_access'):

607

raise web.HTTPError(403, "Shared storage access denied")

608

609

# Shared storage access logic

610

pass

611

```