or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-extensions.mdhttp-client.mdindex.mdoauth-core.mdserver-verification.md

server-verification.mddocs/

0

# Server Verification

1

2

Server-side OAuth request verification for service providers implementing OAuth 1.0 protected resources. The Server class validates incoming requests, checks signatures, and extracts verified parameters for application use.

3

4

## Capabilities

5

6

### OAuth Request Verification

7

8

The Server class provides comprehensive request verification including signature validation, timestamp checking, version verification, and parameter extraction.

9

10

```python { .api }

11

class Server:

12

def __init__(self, signature_methods: dict = None):

13

"""

14

Initialize OAuth server with supported signature methods.

15

16

Args:

17

signature_methods (dict): Dictionary of signature method name to instance mappings.

18

If None, defaults to empty dict.

19

"""

20

21

def add_signature_method(self, signature_method) -> dict:

22

"""

23

Add signature method to server.

24

25

Args:

26

signature_method: SignatureMethod instance

27

28

Returns:

29

dict: Updated signature methods dictionary

30

"""

31

32

def verify_request(self, request, consumer, token) -> dict:

33

"""

34

Verify OAuth request and return non-OAuth parameters.

35

36

Args:

37

request: Request object to verify

38

consumer: Consumer credentials to verify against

39

token: Token credentials to verify against

40

41

Returns:

42

dict: Non-OAuth parameters from verified request

43

44

Raises:

45

Error: If verification fails (invalid signature, expired timestamp, etc.)

46

MissingSignature: If oauth_signature parameter is missing

47

"""

48

49

def build_authenticate_header(self, realm: str = '') -> dict:

50

"""

51

Build WWW-Authenticate header for 401 responses.

52

53

Args:

54

realm (str): OAuth realm parameter

55

56

Returns:

57

dict: Header dictionary with WWW-Authenticate

58

"""

59

```

60

61

### Server Configuration

62

63

```python { .api }

64

# Server properties

65

Server.timestamp_threshold = 300 # Time threshold in seconds (default: 5 minutes)

66

Server.version = '1.0' # OAuth version

67

```

68

69

## Usage Examples

70

71

### Basic Request Verification

72

73

```python

74

import oauth2

75

76

# Set up server with supported signature methods

77

server = oauth2.Server()

78

server.add_signature_method(oauth2.SignatureMethod_HMAC_SHA1())

79

server.add_signature_method(oauth2.SignatureMethod_PLAINTEXT())

80

81

# Consumer from your database/store

82

consumer = oauth2.Consumer('known_consumer_key', 'known_consumer_secret')

83

84

# Token from your database/store

85

token = oauth2.Token('known_token_key', 'known_token_secret')

86

87

# Create request from incoming HTTP request

88

request = oauth2.Request.from_request(

89

http_method='GET',

90

http_url='https://api.myservice.com/protected_resource',

91

headers={'Authorization': 'OAuth oauth_consumer_key="known_consumer_key"...'},

92

query_string='param1=value1&param2=value2'

93

)

94

95

try:

96

# Verify the request

97

parameters = server.verify_request(request, consumer, token)

98

print(f"Verified parameters: {parameters}")

99

100

# Process the authenticated request

101

# parameters contains non-OAuth query parameters

102

103

except oauth2.Error as e:

104

print(f"OAuth verification failed: {e}")

105

# Return 401 Unauthorized with authenticate header

106

auth_header = server.build_authenticate_header(realm='api.myservice.com')

107

```

108

109

### Web Framework Integration (Django Example)

110

111

```python

112

import oauth2

113

from django.http import HttpResponse, HttpResponseBadRequest

114

from django.views.decorators.csrf import csrf_exempt

115

116

# Initialize server

117

oauth_server = oauth2.Server()

118

oauth_server.add_signature_method(oauth2.SignatureMethod_HMAC_SHA1())

119

120

def get_consumer(consumer_key):

121

"""Look up consumer from database."""

122

# Replace with your consumer lookup logic

123

if consumer_key == 'valid_key':

124

return oauth2.Consumer('valid_key', 'valid_secret')

125

return None

126

127

def get_token(token_key):

128

"""Look up token from database."""

129

# Replace with your token lookup logic

130

if token_key == 'valid_token':

131

return oauth2.Token('valid_token', 'valid_token_secret')

132

return None

133

134

@csrf_exempt

135

def protected_resource(request):

136

"""Protected API endpoint."""

137

138

# Create OAuth request from Django request

139

oauth_request = oauth2.Request.from_request(

140

http_method=request.method,

141

http_url=request.build_absolute_uri(),

142

headers=request.META,

143

query_string=request.META.get('QUERY_STRING', '')

144

)

145

146

if not oauth_request:

147

return HttpResponseBadRequest("Invalid OAuth request")

148

149

# Look up consumer

150

consumer_key = oauth_request.get('oauth_consumer_key')

151

consumer = get_consumer(consumer_key)

152

if not consumer:

153

response = HttpResponse("Unauthorized", status=401)

154

auth_header = oauth_server.build_authenticate_header()

155

response['WWW-Authenticate'] = auth_header['WWW-Authenticate']

156

return response

157

158

# Look up token

159

token_key = oauth_request.get('oauth_token')

160

token = get_token(token_key) if token_key else None

161

162

try:

163

# Verify request

164

parameters = oauth_server.verify_request(oauth_request, consumer, token)

165

166

# Process authenticated request

167

return HttpResponse(f"Success! Parameters: {parameters}")

168

169

except oauth2.Error as e:

170

response = HttpResponse(f"OAuth error: {e}", status=401)

171

auth_header = oauth_server.build_authenticate_header()

172

response['WWW-Authenticate'] = auth_header['WWW-Authenticate']

173

return response

174

```

175

176

### Flask Integration Example

177

178

```python

179

import oauth2

180

from flask import Flask, request, jsonify, make_response

181

182

app = Flask(__name__)

183

184

# OAuth server setup

185

oauth_server = oauth2.Server()

186

oauth_server.add_signature_method(oauth2.SignatureMethod_HMAC_SHA1())

187

188

def oauth_required(f):

189

"""Decorator for OAuth-protected endpoints."""

190

def decorated_function(*args, **kwargs):

191

# Create OAuth request

192

oauth_request = oauth2.Request.from_request(

193

http_method=request.method,

194

http_url=request.url,

195

headers=dict(request.headers),

196

query_string=request.query_string.decode('utf-8')

197

)

198

199

if not oauth_request:

200

return make_response("Invalid OAuth request", 400)

201

202

# Look up consumer and token (implement your lookup logic)

203

consumer = lookup_consumer(oauth_request.get('oauth_consumer_key'))

204

token = lookup_token(oauth_request.get('oauth_token'))

205

206

if not consumer:

207

response = make_response("Unauthorized", 401)

208

auth_header = oauth_server.build_authenticate_header()

209

response.headers['WWW-Authenticate'] = auth_header['WWW-Authenticate']

210

return response

211

212

try:

213

# Verify request

214

parameters = oauth_server.verify_request(oauth_request, consumer, token)

215

216

# Add verified parameters to request context

217

request.oauth_parameters = parameters

218

request.oauth_consumer = consumer

219

request.oauth_token = token

220

221

return f(*args, **kwargs)

222

223

except oauth2.Error as e:

224

response = make_response(f"OAuth error: {e}", 401)

225

auth_header = oauth_server.build_authenticate_header()

226

response.headers['WWW-Authenticate'] = auth_header['WWW-Authenticate']

227

return response

228

229

decorated_function.__name__ = f.__name__

230

return decorated_function

231

232

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

233

@oauth_required

234

def protected_endpoint():

235

"""OAuth-protected API endpoint."""

236

return jsonify({

237

'message': 'Success!',

238

'parameters': request.oauth_parameters,

239

'consumer': request.oauth_consumer.key

240

})

241

```

242

243

### Custom Signature Method

244

245

```python

246

import oauth2

247

import hashlib

248

249

class SignatureMethod_SHA256(oauth2.SignatureMethod):

250

"""Custom SHA256-based signature method."""

251

name = 'HMAC-SHA256'

252

253

def signing_base(self, request, consumer, token):

254

"""Calculate signing base and key."""

255

sig = (

256

oauth2.escape(request.method),

257

oauth2.escape(request.normalized_url),

258

oauth2.escape(request.get_normalized_parameters()),

259

)

260

261

key = f"{oauth2.escape(consumer.secret)}&"

262

if token:

263

key += oauth2.escape(token.secret)

264

265

raw = '&'.join(sig)

266

return key.encode('ascii'), raw.encode('ascii')

267

268

def sign(self, request, consumer, token):

269

"""Generate SHA256 signature."""

270

import hmac

271

import base64

272

273

key, raw = self.signing_base(request, consumer, token)

274

hashed = hmac.new(key, raw, hashlib.sha256)

275

return base64.b64encode(hashed.digest())[:-1]

276

277

# Use custom signature method

278

server = oauth2.Server()

279

server.add_signature_method(SignatureMethod_SHA256())

280

server.add_signature_method(oauth2.SignatureMethod_HMAC_SHA1()) # Fallback

281

```

282

283

### Error Handling and Debugging

284

285

```python

286

import oauth2

287

288

server = oauth2.Server()

289

server.add_signature_method(oauth2.SignatureMethod_HMAC_SHA1())

290

291

try:

292

parameters = server.verify_request(request, consumer, token)

293

294

except oauth2.MissingSignature:

295

# oauth_signature parameter is missing

296

return error_response("Missing signature", 401)

297

298

except oauth2.Error as e:

299

error_msg = str(e)

300

301

if "Invalid signature" in error_msg:

302

# Signature calculation mismatch

303

# Log expected signature base string for debugging

304

print(f"Signature verification failed: {error_msg}")

305

306

elif "Expired timestamp" in error_msg:

307

# Request timestamp is too old

308

print(f"Timestamp expired: {error_msg}")

309

310

elif "OAuth version" in error_msg:

311

# Unsupported OAuth version

312

print(f"Version error: {error_msg}")

313

314

return error_response(f"OAuth error: {error_msg}", 401)

315

316

def error_response(message, status_code):

317

"""Return error response with proper headers."""

318

response = make_response(message, status_code)

319

if status_code == 401:

320

auth_header = server.build_authenticate_header(realm='api.example.com')

321

response.headers.update(auth_header)

322

return response

323

```

324

325

### Timestamp Configuration

326

327

```python

328

import oauth2

329

330

# Create server with custom timestamp threshold

331

server = oauth2.Server()

332

server.timestamp_threshold = 600 # Allow 10 minutes clock skew

333

334

# Or modify global default

335

oauth2.Server.timestamp_threshold = 120 # 2 minutes

336

337

server.add_signature_method(oauth2.SignatureMethod_HMAC_SHA1())

338

```

339

340

## Security Considerations

341

342

1. **Timestamp Validation**: Default 5-minute threshold prevents replay attacks

343

2. **Nonce Tracking**: Implement nonce storage to prevent duplicate requests

344

3. **HTTPS Only**: Always use HTTPS in production for credential protection

345

4. **Consumer/Token Storage**: Securely store consumer secrets and token secrets

346

5. **Signature Method**: HMAC-SHA1 is recommended over PLAINTEXT

347

6. **Error Messages**: Avoid exposing sensitive information in error responses