alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Play Framework/Scala example source code file (OpenIDSpec.scala)

This example Play Framework source code file (OpenIDSpec.scala) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Play Framework (and Scala) source code examples by using tags.

All credit for the original source code belongs to Play Framework; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Play Framework tags/keywords

and, api, concurrent, identifier, library, ok, openid, openidclient, openidspec, play, play framework, post, seq, some, time, wsmock

The OpenIDSpec.scala Play Framework example source code

/*
 * Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
 */
package play.api.libs.openid

import org.specs2.mutable.Specification
import scala.Predef._
import org.specs2.mock.Mockito
import org.mockito._
import play.api.mvc.Request
import play.api.http._
import play.api.http.Status._
import play.api.libs.openid.Errors.{BAD_RESPONSE, AUTH_ERROR}
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import java.util.concurrent.TimeUnit

object OpenIDSpec extends Specification with Mockito {

  val claimedId = "http://example.com/openid?id=C123"
  val identity = "http://example.com/openid?id=C123&id"
  val defaultSigned = "op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle"
  val dur = Duration(10, TimeUnit.SECONDS)

  // 9.1 Request parameters - http://openid.net/specs/openid-authentication-2_0.html#anchor27
  def isValidOpenIDRequest(query:Params) = {
    query.get("openid.mode") must_== Some(Seq("checkid_setup"))
    query.get("openid.ns") must_== Some(Seq("http://specs.openid.net/auth/2.0"))
  }

  "OpenID" should {
    "initiate discovery" in {
      val ws = createMockWithValidOpDiscoveryAndVerification
      val openId = new OpenIDClient(ws)
      openId.redirectURL("http://example.com", "http://foo.bar.com/openid")
      there was one(ws.request).get()
    }

    "generate a valid redirectUrl" in {
      val ws = createMockWithValidOpDiscoveryAndVerification
      val openId = new OpenIDClient(ws)
      val redirectUrl = Await.result(openId.redirectURL("http://example.com", "http://foo.bar.com/returnto"), dur)

      val query = parseQueryString(redirectUrl)

      isValidOpenIDRequest(query)

      query.get("openid.return_to") must_== Some(Seq("http://foo.bar.com/returnto"))
      query.get("openid.realm") must beNone
    }

    "generate a valid redirectUrl with a proper required extended attributes request" in {
      val ws = createMockWithValidOpDiscoveryAndVerification
      val openId = new OpenIDClient(ws)
      val redirectUrl = Await.result(openId.redirectURL("http://example.com", "http://foo.bar.com/returnto",
        axRequired = Seq("email" -> "http://schema.openid.net/contact/email")), dur)

      val query = parseQueryString(redirectUrl)

      isValidOpenIDRequest(query)

      query.get("openid.ax.mode") must_== Some(Seq("fetch_request"))
      query.get("openid.ns.ax") must_== Some(Seq("http://openid.net/srv/ax/1.0"))
      query.get("openid.ax.required") must_== Some(Seq("email"))
      query.get("openid.ax.type.email") must_== Some(Seq("http://schema.openid.net/contact/email"))
    }

    "generate a valid redirectUrl with a proper 'if_available' extended attributes request" in {
      val ws = createMockWithValidOpDiscoveryAndVerification
      val openId = new OpenIDClient(ws)
      val redirectUrl = Await.result(openId.redirectURL("http://example.com", "http://foo.bar.com/returnto",
        axOptional = Seq("email" -> "http://schema.openid.net/contact/email")), dur)

      val query = parseQueryString(redirectUrl)

      isValidOpenIDRequest(query)

      query.get("openid.ax.mode") must_== Some(Seq("fetch_request"))
      query.get("openid.ns.ax") must_== Some(Seq("http://openid.net/srv/ax/1.0"))
      query.get("openid.ax.if_available") must_== Some(Seq("email"))
      query.get("openid.ax.type.email") must_== Some(Seq("http://schema.openid.net/contact/email"))
    }

    "generate a valid redirectUrl with a proper 'if_available' AND required extended attributes request" in {
      val ws = createMockWithValidOpDiscoveryAndVerification
      val openId = new OpenIDClient(ws)
      val redirectUrl = Await.result(openId.redirectURL("http://example.com", "http://foo.bar.com/returnto",
        axRequired = Seq("first" -> "http://axschema.org/namePerson/first"),
        axOptional = Seq("email" -> "http://schema.openid.net/contact/email")), dur)

      val query = parseQueryString(redirectUrl)

      isValidOpenIDRequest(query)

      query.get("openid.ax.mode") must_== Some(Seq("fetch_request"))
      query.get("openid.ns.ax") must_== Some(Seq("http://openid.net/srv/ax/1.0"))
      query.get("openid.ax.required") must_== Some(Seq("first"))
      query.get("openid.ax.type.first") must_== Some(Seq("http://axschema.org/namePerson/first"))
      query.get("openid.ax.if_available") must_== Some(Seq("email"))
      query.get("openid.ax.type.email") must_== Some(Seq("http://schema.openid.net/contact/email"))
    }

    "verify the response" in {
      val ws = createMockWithValidOpDiscoveryAndVerification

      val openId = new OpenIDClient(ws)

      val responseQueryString = openIdResponse
      val userInfo = Await.result(openId.verifiedId(setupMockRequest(responseQueryString)), dur)

      "the claimedId must be present" in {
        userInfo.id must be equalTo claimedId
      }

      val argument = ArgumentCaptor.forClass(classOf[Params])
      "direct verification using a POST request was used" in {
        there was one(ws.request).post(argument.capture())(any[Writeable[Params]], any[ContentTypeOf[Params]])

        val verificationQuery = argument.getValue

        "openid.mode was set to check_authentication" in {
          verificationQuery.get("openid.mode") must_== Some(Seq("check_authentication"))
        }

        "every query parameter apart from openid.mode is used in the verification request" in {
          (verificationQuery - "openid.mode") forall { case (key,value) => responseQueryString.get(key) == Some(value) } must beTrue
        }
      }
    }

    // 11.2 If the Claimed Identifier was not previously discovered by the Relying Party
    // (the "openid.identity" in the request was "http://specs.openid.net/auth/2.0/identifier_select" or a different Identifier,
    // or if the OP is sending an unsolicited positive assertion), the Relying Party MUST perform discovery on the
    // Claimed Identifier in the response to make sure that the OP is authorized to make assertions about the Claimed Identifier.
    "verify the response using discovery on the claimed Identifier" in {
      val ws = createMockWithValidOpDiscoveryAndVerification
      val openId = new OpenIDClient(ws)

      val spoofedEndpoint = "http://evilhackerendpoint.com"
      val responseQueryString = openIdResponse - "openid.op_endpoint" + ("openid.op_endpoint" -> Seq(spoofedEndpoint))

      Await.result(openId.verifiedId(setupMockRequest(responseQueryString)), dur)

      "direct verification does not use the openid.op_endpoint that is part of the query string" in {
        ws.urls contains(spoofedEndpoint) must beFalse
      }
      "the endpoint is resolved using discovery on the claimed Id" in {
        ws.urls(0) must be equalTo claimedId
      }
      "use endpoint discovery and then direct verification" in {
        got {
          // Use discovery to resolve the endpoint
          one(ws.request).get()
          // Verify the response
          one(ws.request).post(any[Params])(any[Writeable[Params]], any[ContentTypeOf[Params]])
        }
      }
      "use direct verification on the discovered endpoint" in {
        ws.urls(1) must be equalTo "https://www.google.com/a/example.com/o8/ud?be=o8" // From the mock XRDS
      }
    }

    "fail response verification if direct verification fails" in {
      val ws = new WSMock

      ws.response.status returns OK thenReturns OK
      ws.response.header(HeaderNames.CONTENT_TYPE) returns Some("application/xrds+xml") thenReturns Some("text/plain")
      ws.response.xml returns scala.xml.XML.loadString(readFixture("discovery/xrds/simple-op.xml"))
      ws.response.body returns "is_valid:false\n"

      val openId = new OpenIDClient(ws)

      Await.result(openId.verifiedId(setupMockRequest()), dur) must throwA[AUTH_ERROR.type]

      there was one(ws.request).post(any[Params])(any[Writeable[Params]], any[ContentTypeOf[Params]])
    }

    "fail response verification if the response indicates an error" in {
      val ws = new WSMock

      ws.response.status returns OK thenReturns OK
      ws.response.header(HeaderNames.CONTENT_TYPE) returns Some("application/xrds+xml") thenReturns Some("text/plain")
      ws.response.xml returns scala.xml.XML.loadString(readFixture("discovery/xrds/simple-op.xml"))
      ws.response.body returns "is_valid:false\n"

      val openId = new OpenIDClient(ws)

      val errorResponse = (openIdResponse - "openid.mode") + ("openid.mode" -> Seq("error"))

      Await.result(openId.verifiedId(setupMockRequest(errorResponse)), dur) must throwA[BAD_RESPONSE.type]
    }

    // OpenID 1.1 compatibility - 14.2.1
    "verify an OpenID 1.1 response that is missing the \"openid.op_endpoint\" parameter" in {
      val ws = createMockWithValidOpDiscoveryAndVerification
      val openId = new OpenIDClient(ws)

      val responseQueryString = (openIdResponse - "openid.op_endpoint")

      val userInfo = Await.result(openId.verifiedId(setupMockRequest(responseQueryString)), dur)

      "the claimedId must be present" in {
        userInfo.id must be equalTo claimedId
      }

      "using discovery and direct verification" in {
        got {
          // Use discovery to resolve the endpoint
          one(ws.request).get()
          // Verify the response
          one(ws.request).post(any[Params])(any[Writeable[Params]], any[ContentTypeOf[Params]])
        }
      }
    }
  }

  def createMockWithValidOpDiscoveryAndVerification = {
    val ws = new WSMock
    ws.response.status returns OK thenReturns OK
    ws.response.header(HeaderNames.CONTENT_TYPE) returns Some("application/xrds+xml") thenReturns Some("text/plain")
    ws.response.xml returns scala.xml.XML.loadString(readFixture("discovery/xrds/simple-op.xml"))
    ws.response.body returns "is_valid:true\n" // http://openid.net/specs/openid-authentication-2_0.html#kvform
    ws
  }

  def setupMockRequest(queryString:Params = openIdResponse) = {
    val request = mock[Request[_]]
    request.queryString returns queryString
    request
  }

  def openIdResponse = createDefaultResponse(claimedId, identity, defaultSigned)

}

Other Play Framework source code examples

Here is a short list of links related to this Play Framework OpenIDSpec.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.