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