Asynchronous HTTP client for Play Framework with OAuth, OpenID, and SSL/TLS support
—
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.
Main OAuth 1.0a client for handling the OAuth flow.
/**
* OAuth 1.0a client implementation
* @param info Service information including URLs and consumer key
* @param use10a Whether to use OAuth 1.0a (default: true)
*/
case class OAuth(info: ServiceInfo, use10a: Boolean = true) {
/** Retrieve a request token for the OAuth flow */
def retrieveRequestToken(callbackURL: String): Either[OAuthException, RequestToken]
/** Exchange request token and verifier for access token */
def retrieveAccessToken(token: RequestToken, verifier: String): Either[OAuthException, RequestToken]
/** Generate authorization redirect URL */
def redirectUrl(token: String): String
}OAuth Flow Example:
import play.api.libs.oauth._
// Step 1: Set up OAuth service info
val serviceInfo = ServiceInfo(
requestTokenURL = "https://api.example.com/oauth/request_token",
accessTokenURL = "https://api.example.com/oauth/access_token",
authorizationURL = "https://api.example.com/oauth/authorize",
key = ConsumerKey("your_consumer_key", "your_consumer_secret")
)
val oauth = OAuth(serviceInfo)
// Step 2: Get request token
val callbackURL = "https://yourapp.com/oauth/callback"
oauth.retrieveRequestToken(callbackURL) match {
case Right(requestToken) =>
// Step 3: Redirect user to authorization URL
val authUrl = oauth.redirectUrl(requestToken.token)
// Redirect user to authUrl
case Left(exception) =>
// Handle error
println(s"Failed to get request token: ${exception.getMessage}")
}
// Step 4: After user authorization, exchange for access token
// (This happens in your callback handler)
def handleCallback(requestToken: RequestToken, verifier: String) = {
oauth.retrieveAccessToken(requestToken, verifier) match {
case Right(accessToken) =>
// Store access token and use for API calls
println(s"Access token: ${accessToken.token}")
case Left(exception) =>
// Handle error
println(s"Failed to get access token: ${exception.getMessage}")
}
}Core data types for OAuth authentication.
/**
* Consumer key and secret pair
*/
case class ConsumerKey(key: String, secret: String)
/**
* OAuth token (request token or access token)
*/
case class RequestToken(token: String, secret: String)
/**
* OAuth service configuration
*/
case class ServiceInfo(
requestTokenURL: String,
accessTokenURL: String,
authorizationURL: String,
key: ConsumerKey
)Sign HTTP requests using OAuth credentials.
/**
* OAuth signature calculator for signing requests
*/
class OAuthCalculator(consumerKey: ConsumerKey, requestToken: RequestToken)
extends WSSignatureCalculator with SignatureCalculator
/**
* Factory for creating OAuth calculators
*/
object OAuthCalculator {
def apply(consumerKey: ConsumerKey, token: RequestToken): WSSignatureCalculator
}Request Signing Example:
import play.api.libs.ws._
import play.api.libs.oauth._
// After obtaining access token through OAuth flow
val consumerKey = ConsumerKey("your_consumer_key", "your_consumer_secret")
val accessToken = RequestToken("access_token", "access_token_secret")
// Create OAuth calculator
val calculator = OAuthCalculator(consumerKey, accessToken)
// Sign and execute requests
val response = WS.url("https://api.example.com/protected/resource")
.sign(calculator)
.get()
// POST with OAuth signing
val postData = Json.obj("message" -> "Hello from OAuth!")
val postResponse = WS.url("https://api.example.com/protected/post")
.sign(calculator)
.post(postData)Main OAuth 1.0a client for handling the OAuth flow in Java.
/**
* OAuth 1.0a client implementation for Java
*/
public class OAuth {
/** Create OAuth client with service info */
public OAuth(ServiceInfo info)
public OAuth(ServiceInfo info, boolean use10a)
/** Retrieve a request token for the OAuth flow */
public F.Either<OAuthException, RequestToken> retrieveRequestToken(String callbackURL)
/** Exchange request token and verifier for access token */
public F.Either<OAuthException, RequestToken> retrieveAccessToken(RequestToken token, String verifier)
/** Generate authorization redirect URL */
public String redirectUrl(String token)
}
/**
* OAuth service configuration for Java
*/
public class ServiceInfo {
public ServiceInfo(String requestTokenURL, String accessTokenURL,
String authorizationURL, ConsumerKey key)
public String getRequestTokenURL()
public String getAccessTokenURL()
public String getAuthorizationURL()
public ConsumerKey getKey()
}
/**
* OAuth consumer key for Java
*/
public class ConsumerKey {
public ConsumerKey(String key, String secret)
public String getKey()
public String getSecret()
}
/**
* OAuth token for Java
*/
public class RequestToken {
public RequestToken(String token, String secret)
public String getToken()
public String getSecret()
}Java OAuth Flow Example:
import play.libs.oauth.*;
import play.libs.F;
// Step 1: Set up OAuth service info
ServiceInfo serviceInfo = new ServiceInfo(
"https://api.example.com/oauth/request_token",
"https://api.example.com/oauth/access_token",
"https://api.example.com/oauth/authorize",
new ConsumerKey("your_consumer_key", "your_consumer_secret")
);
OAuth oauth = new OAuth(serviceInfo);
// Step 2: Get request token
String callbackURL = "https://yourapp.com/oauth/callback";
F.Either<OAuthException, RequestToken> requestTokenResult = oauth.retrieveRequestToken(callbackURL);
if (requestTokenResult.right.isDefined()) {
RequestToken requestToken = requestTokenResult.right.get();
// Step 3: Redirect user to authorization URL
String authUrl = oauth.redirectUrl(requestToken.getToken());
// Redirect user to authUrl
} else {
// Handle error
OAuthException exception = requestTokenResult.left.get();
System.out.println("Failed to get request token: " + exception.getMessage());
}
// Step 4: After user authorization, exchange for access token
public void handleCallback(RequestToken requestToken, String verifier) {
F.Either<OAuthException, RequestToken> accessTokenResult =
oauth.retrieveAccessToken(requestToken, verifier);
if (accessTokenResult.right.isDefined()) {
RequestToken accessToken = accessTokenResult.right.get();
// Use access token to make authenticated requests
} else {
// Handle error
OAuthException exception = accessTokenResult.left.get();
System.out.println("Failed to get access token: " + exception.getMessage());
}
}Sign HTTP requests using OAuth credentials in Java.
/**
* OAuth signature calculator for signing requests in Java
*/
public class OAuthCalculator implements WSSignatureCalculator {
public OAuthCalculator(ConsumerKey consumerKey, RequestToken requestToken)
public static WSSignatureCalculator apply(ConsumerKey consumerKey, RequestToken token)
}Java Request Signing Example:
import play.libs.ws.*;
import play.libs.oauth.*;
// After obtaining access token through OAuth flow
ConsumerKey consumerKey = new ConsumerKey("your_consumer_key", "your_consumer_secret");
RequestToken accessToken = new RequestToken("access_token", "access_token_secret");
// Create OAuth calculator
WSSignatureCalculator calculator = OAuthCalculator.apply(consumerKey, accessToken);
// Make authenticated GET request
Promise<WSResponse> getResponse = WS.url("https://api.example.com/protected/resource")
.sign(calculator)
.get();
// POST with OAuth signing
ObjectNode postData = Json.newObject();
postData.put("message", "Hello from OAuth!");
Promise<WSResponse> postResponse = WS.url("https://api.example.com/protected/post")
.sign(calculator)
.post(postData);Common OAuth service configurations for popular APIs.
Twitter API Configuration:
val twitterInfo = ServiceInfo(
requestTokenURL = "https://api.twitter.com/oauth/request_token",
accessTokenURL = "https://api.twitter.com/oauth/access_token",
authorizationURL = "https://api.twitter.com/oauth/authorize",
key = ConsumerKey("twitter_consumer_key", "twitter_consumer_secret")
)
val twitterOAuth = OAuth(twitterInfo)Generic API Configuration:
val apiInfo = ServiceInfo(
requestTokenURL = "https://api.service.com/oauth/request_token",
accessTokenURL = "https://api.service.com/oauth/access_token",
authorizationURL = "https://api.service.com/oauth/authorize",
key = ConsumerKey("consumer_key", "consumer_secret")
)
val apiOAuth = OAuth(apiInfo, use10a = true)OAuth operations return Either[OAuthException, T] for error handling.
import play.api.libs.oauth.OAuthException
oauth.retrieveRequestToken(callbackURL) match {
case Right(requestToken) =>
// Success - proceed with OAuth flow
val authUrl = oauth.redirectUrl(requestToken.token)
// Store requestToken temporarily and redirect user
case Left(oauthException) =>
// Handle OAuth-specific errors
oauthException match {
case ex if ex.getMessage.contains("Unauthorized") =>
// Invalid consumer key/secret
println("Check your OAuth consumer credentials")
case ex if ex.getMessage.contains("Invalid callback") =>
// Callback URL not authorized
println("Verify callback URL in OAuth app settings")
case ex =>
// Other OAuth errors
println(s"OAuth error: ${ex.getMessage}")
}
}import play.api.libs.oauth._
import play.api.libs.ws._
import play.api.mvc._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
class OAuthController @Inject()(ws: WSClient, cc: ControllerComponents)
extends AbstractController(cc) {
val oauth = OAuth(ServiceInfo(
requestTokenURL = "https://api.example.com/oauth/request_token",
accessTokenURL = "https://api.example.com/oauth/access_token",
authorizationURL = "https://api.example.com/oauth/authorize",
key = ConsumerKey("consumer_key", "consumer_secret")
))
def startOAuth = Action { implicit request =>
val callbackURL = routes.OAuthController.callback().absoluteURL()
oauth.retrieveRequestToken(callbackURL) match {
case Right(requestToken) =>
// Store request token in session
val authUrl = oauth.redirectUrl(requestToken.token)
Redirect(authUrl).withSession(
"oauth_token" -> requestToken.token,
"oauth_secret" -> requestToken.secret
)
case Left(exception) =>
BadRequest(s"OAuth error: ${exception.getMessage}")
}
}
def callback = Action { implicit request =>
val verifier = request.getQueryString("oauth_verifier")
val tokenFromCallback = request.getQueryString("oauth_token")
(for {
oauthVerifier <- verifier
sessionToken <- request.session.get("oauth_token")
sessionSecret <- request.session.get("oauth_secret")
if sessionToken == tokenFromCallback
} yield {
val requestToken = RequestToken(sessionToken, sessionSecret)
oauth.retrieveAccessToken(requestToken, oauthVerifier) match {
case Right(accessToken) =>
// Store access token (in database, session, etc.)
Ok("OAuth authentication successful!")
.withSession(
"access_token" -> accessToken.token,
"access_secret" -> accessToken.secret
)
case Left(exception) =>
BadRequest(s"Failed to get access token: ${exception.getMessage}")
}
}).getOrElse {
BadRequest("Invalid OAuth callback")
}
}
def makeAuthenticatedRequest = Action.async { implicit request =>
(for {
accessToken <- request.session.get("access_token")
accessSecret <- request.session.get("access_secret")
} yield {
val consumerKey = ConsumerKey("consumer_key", "consumer_secret")
val token = RequestToken(accessToken, accessSecret)
val calculator = OAuthCalculator(consumerKey, token)
ws.url("https://api.example.com/protected/profile")
.sign(calculator)
.get()
.map(response => Ok(response.json))
}).getOrElse {
Future.successful(Unauthorized("Not authenticated"))
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-typesafe-play--play-ws-2-10