XClaim does not work the way I hoped. see: http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed . Also added Clearing of cashes on authentication code, just to make sure that browser caches don't get in the way. webid
authorHenry Story <henry.story@bblfish.net>
Thu, 05 Jan 2012 00:48:40 +0100
branchwebid
changeset 166 fc3c5c54f72b
parent 164 b920b4d63786
child 167 a38d404047ce
XClaim does not work the way I hoped. see: http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed . Also added Clearing of cashes on authentication code, just to make sure that browser caches don't get in the way.
src/main/scala/auth/WebIDSrvc.scala
src/main/scala/auth/X509Cert.scala
src/main/scala/auth/X509Claim.scala
src/main/scala/auth/X509view.scala
--- a/src/main/scala/auth/WebIDSrvc.scala	Sat Dec 24 17:54:05 2011 +0100
+++ b/src/main/scala/auth/WebIDSrvc.scala	Thu Jan 05 00:48:40 2012 +0100
@@ -33,7 +33,6 @@
 import com.hp.hpl.jena.rdf.model.ModelFactory
 import sommer.{CertAgent, Extractors}
 import org.w3.readwriteweb.util._
-import org.w3.readwriteweb.auth._
 import unfiltered.request._
 import org.fusesource.scalate.scuery.{Transform, Transformer}
 import org.w3.readwriteweb.netty.ReadWriteWebNetty.StaticFiles
@@ -41,6 +40,7 @@
 import xml._
 import unfiltered.response._
 import java.security.interfaces.RSAPublicKey
+import org.w3.readwriteweb.auth._
 
 object WebIDSrvc {
   val dateFormat: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
@@ -86,16 +86,23 @@
       if (next!=Nil && next.size==1) srvStaticFiles(next.head)
       else req match {
         case Params(RelyingParty(rp)) => req match {
-          case Params(DoIt(_)) & XClaim(claim: XClaim) => {
-            val answer = if (claim == NoClaim) "error=nocert&"
-            else if (claim.claims.size == 0) "error=noWebID&"
-            else if (claim.verified.size == 0) "error=noVerifiedWebID&" +
-              claim.claims.map(claim => claim.verify.failMap(e => "cause="+e.getMessage)).mkString("&")+"&"
-            else claim.verified.slice(0, 3).foldRight("") {
-              (wid, str) => "webid=" + URLEncoder.encode(wid.url.toExternalForm, "UTF-8") + "&"
+          //&doit => we redirect to the relying party with the identity answer
+          //TODO: here I would have liked to go with what the user decided without forcing him to login,
+          //ie by using XClaim(claim) in the match,
+          //but http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed
+          case Params(DoIt(_))  => {
+            val answer = X509Claim.unapply(req) match {
+              case None => "error=nocert&"
+              case Some(claim) =>
+                if (claim.claims.size == 0) "error=noWebID&"
+                else if (claim.verified.size == 0) "error=noVerifiedWebID&" +
+                  claim.claims.map(claim => claim.verify.failMap(e => "cause=" + e.getMessage)).mkString("&") + "&"
+                else claim.verified.slice(0, 3).foldRight("") {
+                  (wid, str) => "webid=" + URLEncoder.encode(wid.url.toExternalForm, "UTF-8") + "&"
+                }
             }
             val signedAnswer = sign(rp.toExternalForm + "?" + answer).toExternalForm
-            Redirect(signedAnswer)
+            Redirect(signedAnswer) ~> Expires("0") ~> CacheControl("no-cache")
           }
           //GET=>The user just arrived on the page. We recuperated the X509 claim in case he has authenticated already
           case GET(_) & XClaim(claim: XClaim) => {
@@ -103,7 +110,7 @@
               case NoClaim => profilePg
               case claim: X509Claim => if (claim.verified.size > 0) authenticatedPg else errorPg
             }
-            Ok ~> Html5(new ServiceTrans(rp, claim).apply(pg))
+            Ok ~> Html5(new ServiceTrans(rp, claim).apply(pg)) ~> Expires("0") ~> CacheControl("no-cache")
           }
           //POST=> we authenticate the user because he has agreed to be authenticated on the page, which we know if the
           // request is a POST
--- a/src/main/scala/auth/X509Cert.scala	Sat Dec 24 17:54:05 2011 +0100
+++ b/src/main/scala/auth/X509Cert.scala	Thu Jan 05 00:48:40 2012 +0100
@@ -217,9 +217,11 @@
     val sslh = r.underlying.context.getPipeline.get(classOf[SslHandler])
     
     trySome(sslh.getEngine.getSession.getPeerCertificates.toIndexedSeq) orElse {
+      //it seems that the jvm does not keep a very good cache of remote certificates in a session. But
+      //see http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed
       if (!fetch) None
       else {
-        sslh.setEnableRenegotiation(true)
+        sslh.setEnableRenegotiation(true) // todo: does this have to be done on every request?
         r match {
           case UserAgent(agent) if needAuth(agent) => sslh.getEngine.setNeedClientAuth(true)
           case _ => sslh.getEngine.setWantClientAuth(true)
--- a/src/main/scala/auth/X509Claim.scala	Sat Dec 24 17:54:05 2011 +0100
+++ b/src/main/scala/auth/X509Claim.scala	Thu Jan 05 00:48:40 2012 +0100
@@ -92,6 +92,8 @@
 }
 
 object XClaim {
+  //warning: it turns out that one nearly never recuperates the client certificate
+  //see http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed
   implicit val fetch = false
   def unapply[T](r: HttpRequest[T])(implicit m: Manifest[T]): Option[XClaim] = r match {
     case Certs(c1: X509Certificate, _*) => trySome(X509Claim.idCache.get(c1))
--- a/src/main/scala/auth/X509view.scala	Sat Dec 24 17:54:05 2011 +0100
+++ b/src/main/scala/auth/X509view.scala	Thu Jan 05 00:48:40 2012 +0100
@@ -23,17 +23,14 @@
 
 package org.w3.readwriteweb.auth
 
-import java.io.File
-import unfiltered.response.{Ok, Html}
 import unfiltered.Cycle
-import org.fusesource.scalate.{Binding, TemplateEngine}
 import xml.{Elem, XML}
 import unfiltered.request.Path
 import org.fusesource.scalate.scuery.{Transform, Transformer}
-import org.w3.readwriteweb.WebCache
 import unfiltered.scalate.Scalate
 import java.text.DateFormat
 import java.util.Date
+import unfiltered.response.{CacheControl, Expires, Ok, Html}
 
 /**
  * This plan just described the X509 WebID authentication information.
@@ -56,8 +53,8 @@
   
   def intent : Cycle.Intent[Req,Res] = {
     case req @ Path("/test/WebId")  => req match {
-      case X509Claim(claim) => Ok ~> Html( new X509Filler(claim).apply(webidTst) )
-      case _ => Ok ~> Html (new NoX509().apply(noX509))
+      case X509Claim(claim) => Ok ~> Html( new X509Filler(claim).apply(webidTst) ) ~> Expires("0") ~> CacheControl("no-cache")
+      case _ => Ok ~> Html (new NoX509().apply(noX509)) ~> Expires("0") ~> CacheControl("no-cache")
     }
     case req @ Path("/test/WebIdAuth2") => Ok ~> Scalate(req, "hello.ssp")
   }