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
```