CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-typesafe-play--play-ws-2-10

Asynchronous HTTP client for Play Framework with OAuth, OpenID, and SSL/TLS support

Pending
Overview
Eval results
Files

oauth.mddocs/

OAuth Authentication

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.

Capabilities

OAuth Client - Scala API

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}")
  }
}

OAuth Data Types

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
)

OAuth Request Signing

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)

OAuth Client - Java API

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());
    }
}

OAuth Request Signing - Java API

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);

Service Configuration Examples

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)

Error Handling

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}")
    }
}

Complete OAuth Integration Example

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

docs

http-client.md

index.md

ning.md

oauth.md

openid.md

ssl.md

testing.md

tile.json