or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

http-client.mdindex.mdning.mdoauth.mdopenid.mdssl.mdtesting.md

oauth.mddocs/

0

# OAuth Authentication

1

2

OAuth 1.0a authentication support for secure API access with consumer keys and request tokens. Play WS provides comprehensive OAuth support for accessing protected resources on behalf of users, with both Scala and Java APIs.

3

4

## Capabilities

5

6

### OAuth Client - Scala API

7

8

Main OAuth 1.0a client for handling the OAuth flow.

9

10

```scala { .api }

11

/**

12

* OAuth 1.0a client implementation

13

* @param info Service information including URLs and consumer key

14

* @param use10a Whether to use OAuth 1.0a (default: true)

15

*/

16

case class OAuth(info: ServiceInfo, use10a: Boolean = true) {

17

/** Retrieve a request token for the OAuth flow */

18

def retrieveRequestToken(callbackURL: String): Either[OAuthException, RequestToken]

19

20

/** Exchange request token and verifier for access token */

21

def retrieveAccessToken(token: RequestToken, verifier: String): Either[OAuthException, RequestToken]

22

23

/** Generate authorization redirect URL */

24

def redirectUrl(token: String): String

25

}

26

```

27

28

**OAuth Flow Example:**

29

30

```scala

31

import play.api.libs.oauth._

32

33

// Step 1: Set up OAuth service info

34

val serviceInfo = ServiceInfo(

35

requestTokenURL = "https://api.example.com/oauth/request_token",

36

accessTokenURL = "https://api.example.com/oauth/access_token",

37

authorizationURL = "https://api.example.com/oauth/authorize",

38

key = ConsumerKey("your_consumer_key", "your_consumer_secret")

39

)

40

41

val oauth = OAuth(serviceInfo)

42

43

// Step 2: Get request token

44

val callbackURL = "https://yourapp.com/oauth/callback"

45

oauth.retrieveRequestToken(callbackURL) match {

46

case Right(requestToken) =>

47

// Step 3: Redirect user to authorization URL

48

val authUrl = oauth.redirectUrl(requestToken.token)

49

// Redirect user to authUrl

50

51

case Left(exception) =>

52

// Handle error

53

println(s"Failed to get request token: ${exception.getMessage}")

54

}

55

56

// Step 4: After user authorization, exchange for access token

57

// (This happens in your callback handler)

58

def handleCallback(requestToken: RequestToken, verifier: String) = {

59

oauth.retrieveAccessToken(requestToken, verifier) match {

60

case Right(accessToken) =>

61

// Store access token and use for API calls

62

println(s"Access token: ${accessToken.token}")

63

64

case Left(exception) =>

65

// Handle error

66

println(s"Failed to get access token: ${exception.getMessage}")

67

}

68

}

69

```

70

71

### OAuth Data Types

72

73

Core data types for OAuth authentication.

74

75

```scala { .api }

76

/**

77

* Consumer key and secret pair

78

*/

79

case class ConsumerKey(key: String, secret: String)

80

81

/**

82

* OAuth token (request token or access token)

83

*/

84

case class RequestToken(token: String, secret: String)

85

86

/**

87

* OAuth service configuration

88

*/

89

case class ServiceInfo(

90

requestTokenURL: String,

91

accessTokenURL: String,

92

authorizationURL: String,

93

key: ConsumerKey

94

)

95

```

96

97

### OAuth Request Signing

98

99

Sign HTTP requests using OAuth credentials.

100

101

```scala { .api }

102

/**

103

* OAuth signature calculator for signing requests

104

*/

105

class OAuthCalculator(consumerKey: ConsumerKey, requestToken: RequestToken)

106

extends WSSignatureCalculator with SignatureCalculator

107

108

/**

109

* Factory for creating OAuth calculators

110

*/

111

object OAuthCalculator {

112

def apply(consumerKey: ConsumerKey, token: RequestToken): WSSignatureCalculator

113

}

114

```

115

116

**Request Signing Example:**

117

118

```scala

119

import play.api.libs.ws._

120

import play.api.libs.oauth._

121

122

// After obtaining access token through OAuth flow

123

val consumerKey = ConsumerKey("your_consumer_key", "your_consumer_secret")

124

val accessToken = RequestToken("access_token", "access_token_secret")

125

126

// Create OAuth calculator

127

val calculator = OAuthCalculator(consumerKey, accessToken)

128

129

// Sign and execute requests

130

val response = WS.url("https://api.example.com/protected/resource")

131

.sign(calculator)

132

.get()

133

134

// POST with OAuth signing

135

val postData = Json.obj("message" -> "Hello from OAuth!")

136

val postResponse = WS.url("https://api.example.com/protected/post")

137

.sign(calculator)

138

.post(postData)

139

```

140

141

### OAuth Client - Java API

142

143

Main OAuth 1.0a client for handling the OAuth flow in Java.

144

145

```java { .api }

146

/**

147

* OAuth 1.0a client implementation for Java

148

*/

149

public class OAuth {

150

/** Create OAuth client with service info */

151

public OAuth(ServiceInfo info)

152

public OAuth(ServiceInfo info, boolean use10a)

153

154

/** Retrieve a request token for the OAuth flow */

155

public F.Either<OAuthException, RequestToken> retrieveRequestToken(String callbackURL)

156

157

/** Exchange request token and verifier for access token */

158

public F.Either<OAuthException, RequestToken> retrieveAccessToken(RequestToken token, String verifier)

159

160

/** Generate authorization redirect URL */

161

public String redirectUrl(String token)

162

}

163

164

/**

165

* OAuth service configuration for Java

166

*/

167

public class ServiceInfo {

168

public ServiceInfo(String requestTokenURL, String accessTokenURL,

169

String authorizationURL, ConsumerKey key)

170

171

public String getRequestTokenURL()

172

public String getAccessTokenURL()

173

public String getAuthorizationURL()

174

public ConsumerKey getKey()

175

}

176

177

/**

178

* OAuth consumer key for Java

179

*/

180

public class ConsumerKey {

181

public ConsumerKey(String key, String secret)

182

public String getKey()

183

public String getSecret()

184

}

185

186

/**

187

* OAuth token for Java

188

*/

189

public class RequestToken {

190

public RequestToken(String token, String secret)

191

public String getToken()

192

public String getSecret()

193

}

194

```

195

196

**Java OAuth Flow Example:**

197

198

```java

199

import play.libs.oauth.*;

200

import play.libs.F;

201

202

// Step 1: Set up OAuth service info

203

ServiceInfo serviceInfo = new ServiceInfo(

204

"https://api.example.com/oauth/request_token",

205

"https://api.example.com/oauth/access_token",

206

"https://api.example.com/oauth/authorize",

207

new ConsumerKey("your_consumer_key", "your_consumer_secret")

208

);

209

210

OAuth oauth = new OAuth(serviceInfo);

211

212

// Step 2: Get request token

213

String callbackURL = "https://yourapp.com/oauth/callback";

214

F.Either<OAuthException, RequestToken> requestTokenResult = oauth.retrieveRequestToken(callbackURL);

215

216

if (requestTokenResult.right.isDefined()) {

217

RequestToken requestToken = requestTokenResult.right.get();

218

219

// Step 3: Redirect user to authorization URL

220

String authUrl = oauth.redirectUrl(requestToken.getToken());

221

// Redirect user to authUrl

222

} else {

223

// Handle error

224

OAuthException exception = requestTokenResult.left.get();

225

System.out.println("Failed to get request token: " + exception.getMessage());

226

}

227

228

// Step 4: After user authorization, exchange for access token

229

public void handleCallback(RequestToken requestToken, String verifier) {

230

F.Either<OAuthException, RequestToken> accessTokenResult =

231

oauth.retrieveAccessToken(requestToken, verifier);

232

233

if (accessTokenResult.right.isDefined()) {

234

RequestToken accessToken = accessTokenResult.right.get();

235

// Use access token to make authenticated requests

236

} else {

237

// Handle error

238

OAuthException exception = accessTokenResult.left.get();

239

System.out.println("Failed to get access token: " + exception.getMessage());

240

}

241

}

242

```

243

244

### OAuth Request Signing - Java API

245

246

Sign HTTP requests using OAuth credentials in Java.

247

248

```java { .api }

249

/**

250

* OAuth signature calculator for signing requests in Java

251

*/

252

public class OAuthCalculator implements WSSignatureCalculator {

253

public OAuthCalculator(ConsumerKey consumerKey, RequestToken requestToken)

254

255

public static WSSignatureCalculator apply(ConsumerKey consumerKey, RequestToken token)

256

}

257

```

258

259

**Java Request Signing Example:**

260

261

```java

262

import play.libs.ws.*;

263

import play.libs.oauth.*;

264

265

// After obtaining access token through OAuth flow

266

ConsumerKey consumerKey = new ConsumerKey("your_consumer_key", "your_consumer_secret");

267

RequestToken accessToken = new RequestToken("access_token", "access_token_secret");

268

269

// Create OAuth calculator

270

WSSignatureCalculator calculator = OAuthCalculator.apply(consumerKey, accessToken);

271

272

// Make authenticated GET request

273

Promise<WSResponse> getResponse = WS.url("https://api.example.com/protected/resource")

274

.sign(calculator)

275

.get();

276

277

// POST with OAuth signing

278

ObjectNode postData = Json.newObject();

279

postData.put("message", "Hello from OAuth!");

280

Promise<WSResponse> postResponse = WS.url("https://api.example.com/protected/post")

281

.sign(calculator)

282

.post(postData);

283

```

284

285

### Service Configuration Examples

286

287

Common OAuth service configurations for popular APIs.

288

289

**Twitter API Configuration:**

290

291

```scala

292

val twitterInfo = ServiceInfo(

293

requestTokenURL = "https://api.twitter.com/oauth/request_token",

294

accessTokenURL = "https://api.twitter.com/oauth/access_token",

295

authorizationURL = "https://api.twitter.com/oauth/authorize",

296

key = ConsumerKey("twitter_consumer_key", "twitter_consumer_secret")

297

)

298

299

val twitterOAuth = OAuth(twitterInfo)

300

```

301

302

**Generic API Configuration:**

303

304

```scala

305

val apiInfo = ServiceInfo(

306

requestTokenURL = "https://api.service.com/oauth/request_token",

307

accessTokenURL = "https://api.service.com/oauth/access_token",

308

authorizationURL = "https://api.service.com/oauth/authorize",

309

key = ConsumerKey("consumer_key", "consumer_secret")

310

)

311

312

val apiOAuth = OAuth(apiInfo, use10a = true)

313

```

314

315

### Error Handling

316

317

OAuth operations return `Either[OAuthException, T]` for error handling.

318

319

```scala

320

import play.api.libs.oauth.OAuthException

321

322

oauth.retrieveRequestToken(callbackURL) match {

323

case Right(requestToken) =>

324

// Success - proceed with OAuth flow

325

val authUrl = oauth.redirectUrl(requestToken.token)

326

// Store requestToken temporarily and redirect user

327

328

case Left(oauthException) =>

329

// Handle OAuth-specific errors

330

oauthException match {

331

case ex if ex.getMessage.contains("Unauthorized") =>

332

// Invalid consumer key/secret

333

println("Check your OAuth consumer credentials")

334

335

case ex if ex.getMessage.contains("Invalid callback") =>

336

// Callback URL not authorized

337

println("Verify callback URL in OAuth app settings")

338

339

case ex =>

340

// Other OAuth errors

341

println(s"OAuth error: ${ex.getMessage}")

342

}

343

}

344

```

345

346

### Complete OAuth Integration Example

347

348

```scala

349

import play.api.libs.oauth._

350

import play.api.libs.ws._

351

import play.api.mvc._

352

import scala.concurrent.Future

353

import scala.concurrent.ExecutionContext.Implicits.global

354

355

class OAuthController @Inject()(ws: WSClient, cc: ControllerComponents)

356

extends AbstractController(cc) {

357

358

val oauth = OAuth(ServiceInfo(

359

requestTokenURL = "https://api.example.com/oauth/request_token",

360

accessTokenURL = "https://api.example.com/oauth/access_token",

361

authorizationURL = "https://api.example.com/oauth/authorize",

362

key = ConsumerKey("consumer_key", "consumer_secret")

363

))

364

365

def startOAuth = Action { implicit request =>

366

val callbackURL = routes.OAuthController.callback().absoluteURL()

367

368

oauth.retrieveRequestToken(callbackURL) match {

369

case Right(requestToken) =>

370

// Store request token in session

371

val authUrl = oauth.redirectUrl(requestToken.token)

372

Redirect(authUrl).withSession(

373

"oauth_token" -> requestToken.token,

374

"oauth_secret" -> requestToken.secret

375

)

376

377

case Left(exception) =>

378

BadRequest(s"OAuth error: ${exception.getMessage}")

379

}

380

}

381

382

def callback = Action { implicit request =>

383

val verifier = request.getQueryString("oauth_verifier")

384

val tokenFromCallback = request.getQueryString("oauth_token")

385

386

(for {

387

oauthVerifier <- verifier

388

sessionToken <- request.session.get("oauth_token")

389

sessionSecret <- request.session.get("oauth_secret")

390

if sessionToken == tokenFromCallback

391

} yield {

392

val requestToken = RequestToken(sessionToken, sessionSecret)

393

394

oauth.retrieveAccessToken(requestToken, oauthVerifier) match {

395

case Right(accessToken) =>

396

// Store access token (in database, session, etc.)

397

Ok("OAuth authentication successful!")

398

.withSession(

399

"access_token" -> accessToken.token,

400

"access_secret" -> accessToken.secret

401

)

402

403

case Left(exception) =>

404

BadRequest(s"Failed to get access token: ${exception.getMessage}")

405

}

406

}).getOrElse {

407

BadRequest("Invalid OAuth callback")

408

}

409

}

410

411

def makeAuthenticatedRequest = Action.async { implicit request =>

412

(for {

413

accessToken <- request.session.get("access_token")

414

accessSecret <- request.session.get("access_secret")

415

} yield {

416

val consumerKey = ConsumerKey("consumer_key", "consumer_secret")

417

val token = RequestToken(accessToken, accessSecret)

418

val calculator = OAuthCalculator(consumerKey, token)

419

420

ws.url("https://api.example.com/protected/profile")

421

.sign(calculator)

422

.get()

423

.map(response => Ok(response.json))

424

425

}).getOrElse {

426

Future.successful(Unauthorized("Not authenticated"))

427

}

428

}

429

}

430

```