Reading "Authorization" headers

1,034 views
Skip to first unread message

Alex Lambert

unread,
Jul 7, 2012, 8:37:27 PM7/7/12
to spray...@googlegroups.com
Hi folks,


I am trying to implement support for OAuth 2.0 bearer tokens in my Spray 0.9.0 application. One acceptable method of transmitting the OAuth access token is to use the "Authorization" header with the scheme "Bearer". However, the current bearer token specification (http://self-issued.info/docs/draft-ietf-oauth-v2-bearer.html#authz-header) uses a different grammar than defined in RFC 2617:

The Authorization header field uses the framework defined by HTTP/1.1, Part 7 [I‑D.ietf‑httpbis‑p7‑auth] as follows:
credentials = "Bearer" 1*SP b64token
The b64token syntax was chosen over the alternative #auth-param syntax also defined by HTTP/1.1, Part 7 [I‑D.ietf‑httpbis‑p7‑auth] both for simplicity and for compatibility with existing implementations. If additional parameters are needed in the future, a different scheme would need to be defined.

So, for example, a client may send "Authorization: Bearer mF_9.B5f-4.1JqM" or "Authorization: Bearer /wz0rcb257M+f8i7tTWfRw==".

In AuthorizationHeader.scala, spray expects that the authorization scheme is followed by zero or more auth-param values (which it treats as key-value pairs). This behavior is correct per section 1.2 of RFC 2617, which defines auth-param as token "=" ( token | quoted-string ). Unfortunately, this prevents me from accessing the OAuth token; when I run curl -v -H 'Authorization: Bearer mF_9.B5f-4.1JqM' http://localhost:8080 and print the list of headers, I receive "List(Host: localhost:8080, Accept: */*, Authorization: Bearer , User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5)".

Does anyone have any suggestions for how I can read the access token from my application? I tried turning on relaxed header parsing the result did not change (I do not receive warnings about the header being invalid.)



Thanks,
Alex

Mathias

unread,
Jul 9, 2012, 4:40:03 AM7/9/12
to spray...@googlegroups.com
Alex,

ideally you should be able to turn on relaxed header parsing and have spray parse the "invalid" Authorization header as a CustomHeader.
However, spray 0.9.0 has a bug in [this line]https://github.com/spray/spray/blob/master/spray-base/src/main/scala/cc/spray/http/parser/AuthorizationHeader.scala#L28 , which is

CredentialDef ~~> HttpHeaders.`Authorization`

but should be

CredentialDef ~ EOI ~~> HttpHeaders.`Authorization`

This bug prevents the triggering of a syntax error and the correct parsing into a CustomHeader.

The only way I can see you getting to the bearer token with an unchanged spray 0.9.0 is to intercept the HttpRequest _before_ the headers are being parsed by sprays HttpParser. The way you do this depends on whether you run spray 0.9.0 on spray-can or inside of a servlet container.
Whats your setup?

Cheers,
Mathias

---
mat...@spray.cc
http://www.spray.cc

Alex Lambert

unread,
Jul 9, 2012, 9:35:55 AM7/9/12
to spray...@googlegroups.com
Hi Mathias,

Thank you for investigating this so quickly!

We have embedded Jetty 8 into our application (following http://wiki.eclipse.org/Jetty/Tutorial/Embedding_Jetty), and we are running Spray using the Servlet 3.0 connector; is this amenable to your suggestion of intercepting the request first? If not, in the worst case we could fork 0.9.0 and apply this change.


Thanks again - it's a pleasure using Spray,
Alex

Mathias

unread,
Jul 9, 2012, 9:48:06 AM7/9/12
to spray...@googlegroups.com
Alex,

you should subclass the Servlet30ConnectorServlet and override the following method
somewhere along these lines:

override def httpRequest(hsr: HttpServletRequest): cc.spray.http.HttpRequest = {
val request = super.httpRequest(hsr)
request.withHeaders {
request.headers.map {
case Authorization(...) =>
// transform header using the original header value from the hsr
case header => header
}
}
}

If you then tie in you custom ConnectorServlet you should be all set.

Cheers,
Mathias

---
mat...@spray.cc
http://www.spray.cc

Alex Lambert

unread,
Jul 9, 2012, 9:55:01 AM7/9/12
to spray...@googlegroups.com
Excellent, thank you Mathias! I will give this a try.


Best,
Alex

Alex Lambert

unread,
Jul 10, 2012, 7:58:26 PM7/10/12
to spray...@googlegroups.com
Hi Mathias,

Just wanted to follow up -- your solution worked perfectly.

If anyone else tries this, please be aware that your Servlet30ConnectorServlet subclass must be in the cc.spray.connectors package or you will encounter a confusing compiler error:

[error] {file:/home/alex/biff/grok/}redline/compile:compile: java.lang.Error: Unexpected tree in genLoad:Servlet30ConnectorServletWithAuthorizationFix.super/class scala.reflect.generic.Trees$Super at:source-/home/alex/biff/grok/redline/src/main/scala/com/bifflabs/grok/redline/app/spray/Servlet30ConnectorServletWithAuthorizationFix.scala,line-14,offset=513

It looks like this is tracked in the Scala compiler project at https://issues.scala-lang.org/browse/SI-4626


Thanks again,
Alex
Reply all
Reply to author
Forward
0 new messages