scalaj.http.OAuth.scala Maven / Gradle / Ivy
The newest version!
package scalaj.http
/** scalaj.http
Copyright 2010 Jonathan Hoffman
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import java.net.URL
case class Token(key: String, secret: String)
/** utility methods used by [[scalaj.http.HttpRequest]] */
object OAuth {
import java.net.URI
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
val MAC = "HmacSHA1"
def sign(req: HttpRequest, consumer: Token, token: Option[Token], verifier: Option[String]): HttpRequest = {
req.option(conn => {
val baseParams: Seq[(String,String)] = Seq(
("oauth_timestamp", (System.currentTimeMillis / 1000).toString),
("oauth_nonce", System.currentTimeMillis.toString)
)
var (oauthParams, signature) = getSig(baseParams, req, consumer, token, verifier)
oauthParams +:= (("oauth_signature", signature))
conn.setRequestProperty("Authorization", "OAuth " + oauthParams.map(p => p._1 + "=\"" + percentEncode(p._2) +"\"").mkString(","))
})
}
def getSig(baseParams: Seq[(String,String)], req: HttpRequest, consumer: Token, token: Option[Token], verifier: Option[String]): (Seq[(String,String)], String) = {
var oauthParams = ("oauth_version", "1.0") +: ("oauth_consumer_key", consumer.key) +: ("oauth_signature_method", "HMAC-SHA1") +: baseParams
token.foreach{t =>
oauthParams +:= (("oauth_token", t.key))
}
verifier.foreach{v =>
oauthParams +:= (("oauth_verifier", v))
}
// oauth1.0 specifies that only querystring and x-www-form-urlencoded body parameters should be included in signature
// req.params from multi-part requests are included in the multi-part request body and should NOT be included
val allTheParams = if (req.connectFunc.isInstanceOf[MultiPartConnectFunc]) {
oauthParams
} else {
req.params ++ oauthParams
}
val baseString = Seq(req.method.toUpperCase,normalizeUrl(new URL(req.url)),normalizeParams(allTheParams)).map(percentEncode).mkString("&")
val keyString = percentEncode(consumer.secret) + "&" + token.map(t => percentEncode(t.secret)).getOrElse("")
val key = new SecretKeySpec(keyString.getBytes(HttpConstants.utf8), MAC)
val mac = Mac.getInstance(MAC)
mac.init(key)
val text = baseString.getBytes(HttpConstants.utf8)
(oauthParams, HttpConstants.base64(mac.doFinal(text)))
}
private def normalizeParams(params: Seq[(String,String)]) = {
percentEncode(params).sortWith(_ < _).mkString("&")
}
private def normalizeUrl(url: URL) = {
val uri = new URI(url.toString)
val scheme = uri.getScheme().toLowerCase()
var authority = uri.getAuthority().toLowerCase()
val dropPort = (scheme.equals("http") && uri.getPort() == 80) || (scheme.equals("https") && uri.getPort() == 443)
if (dropPort) {
// find the last : in the authority
val index = authority.lastIndexOf(":")
if (index >= 0) {
authority = authority.substring(0, index)
}
}
var path = uri.getRawPath()
if (path == null || path.length() <= 0) {
path = "/" // conforms to RFC 2616 section 3.2.2
}
// we know that there is no query and no fragment here.
scheme + "://" + authority + path
}
def percentEncode(params: Seq[(String,String)]): Seq[String] = {
params.map(p => percentEncode(p._1) + "=" + percentEncode(p._2))
}
def percentEncode(s: String): String = {
if (s == null) "" else {
HttpConstants.urlEncode(s, HttpConstants.utf8).replace("+", "%20").replace("*", "%2A").replace("%7E", "~")
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy