drone29a / clj-oauth Goto Github PK
View Code? Open in Web Editor NEWOAuth Consumer support for Clojure
License: BSD 2-Clause "Simplified" License
OAuth Consumer support for Clojure
License: BSD 2-Clause "Simplified" License
I want to use this library to do what Yahoo! calls two-legged OAuth requests (or actually just signing my requests with my consumer key and secret). Does this library provide a way to do this?
Hi There,
ran into some snags using this module to access twitter api.
I'm able to create a consumer and fetch an access token.
Also can successfully generate an approval uri and callback.
When I try to exchange for an access token e.g:
(def access-token-response (oauth/access-token consumer token verifier))
I get a 401
every time.
I'm sure my api key and secret are correct, my app is enabled for sign in and I do get both token and verifier in callback.
Any thoughts on this? Has anyone experienced partial access problems with twitter api?
Thanks!
A few people have put up recent snapshots (http://clojars.org/search?q=clj-oauth), but there doesnt seem to be an official one.
If you could document in the README the right package to include in project.clj, that would also rock!
I used clj-oauth to successfully login to twitter and fetch access-token. However when I try to invoke twitter api for "https://api.twitter.com/1/account/verify_credentials.json", I am getting a 401.
I suspect the (http/get ... ) is not setting the credentials correctly.
(defn handle-twitter-token-credentials [oauth_token oauth_verifier]
{:status 200
:headers {"Content-Type" "text/html"}
:body (let [
access-token-response (oauth/access-token consumer request-token oauth_verifier)
_ (println " access-token-response: " access-token-response)
req-credentials (oauth/credentials consumer
(:oauth_token access-token-response)
(:oauth_token_secret access-token-response)
:GET
"https://api.twitter.com/1/account/verify_credentials.json"
)
; this is ok so far
_ (println "credentials: " req-credentials)
; this breaks at http/get with 401 . The req-credentials looks ok
; I think http/get is not setting the authorization headers correctly
;
info (http/get "https://api.twitter.com/1/account/verify_credentials.json"
{:query-params req-credentials})
_ (println "got info ")
_ (println "class " (class info))
_ (println (str into))
]
(with-out-str "Ok")
)} )
Hi.
I'm trying to use clj-oauth to integrate the Shapeways API (https://developers.shapeways.com/, uses Oauth 1.0) into my Clojure app, and am running into an issue with the request-token response. It looks like they return the oauth_token value in the authentication_url (not as a separate key/value pair), and it looks like they screwed up the URL encoding such that there is an unescaped = sign in the authentication_url value. E.g.
authentication_url=http%3A%2F%2Fapi.shapeways.com%2Flogin%3Foauth_token=SECRET&oauth_token_secret=SECRET2&oauth_callback_confirmed=true
When this is decoded, and the map returned to me, I get:
{:authentication_url http://api.shapeways.com/login?oauth_token, :oauth_token_secret SECRET2, :oauth_callback_confirmed true}
The reason this is happening is because of how the string is parsed in oauth.client/form-decode. It splits each parameter on "=" and then takes the first 2 values in that sequence, which throws away SECRET after "oauth_token=..." in this case. A solution to this problem could be to limit that split to only 2 splits, which will always split on the first = found (I've tested this in my local copy and it works for my situation). Another solution to this problem could be to let me bind my own form-decode function (thus letting me hack around an error in the API in my own code). I'm happy to implement and issue a pull request, but I'd love some guidance on which solution you'd prefer for the library.
As a secondary question, I'm pretty new to Oauth but this pattern (of using an authentication url from the request-token, instead of defined in the Consumer) doesn't seem well supported by clj-oauth. Am I understanding that correctly, and is there "right" way to support this pattern?
Thanks!
I'm trying to upload a file using the Dropbox API. This is what I've written
(defn upload-file
"Upload file to Dropbox using PUT.
root can be either `dropbox` or `sandbox`"
[consumer access-token-response root remote-path local-path]
(http/post (str "https://api-content.dropbox.com/1/files_put/" root "/" remote-path)
{:query-params (make-credentials consumer
access-token-response
:POST
"https://api-content.dropbox.com/1/files/"
nil}
{:body (clojure.java.io/file local-path)}))
(The full file is at https://bitbucket.org/samrat/jopbox/src/620d223b33b85dbe16b787e653c20d25864f2ca6/src/jopbox/client.clj?at=default#cl-66 )
But that doesn't work(I get a 400 error). Could someone please point me in the right direction, please.
If no, what is omniauth like alternative in Clojure.
Hi
This problem seems to be related to one that someone else had posted.
When compiling the latest jar, 1.5.4.
Caused` by java.lang.SecurityException
class "org.bouncycastle.asn1.DEREncodable"'s signer information
does not match signer information of other classes in the same
package
I get this unfortunately and I can't use it.
Is there a fix for this? Probably just need an updated dependency?
Kind regards, Jason.
I need to use clj-http
and found that clj-oauth
starts failing when [clj-http "0.5.3"]
is added as a dependency. Could someone please update clj-oauth to work with clj-http 0.5.3
.
The 1.0 spec allows for 3 methods, the preferred being use of the Authorization header that the clj-oauth supports. The other two embed parameters in form params or a GET header, and clj-oauth doesn't support these two modes.
Hey. After upgrading from 1.5.1 to 1.5.2 I noticed our logs were filing up with lines like:
Private key: #object[org.bouncycastle.jce.provider.JCERSAPrivateCrtKey 0x41e6c9ea RSA Private CRT Key
...
I eventually tracked it down to line 84 in signature.clj in version 1.5.2 that is uploaded to clojars.
(defmethod sign :rsa-sha1
[c ^String base-string & [token-secret]]
(java.security.Security/addProvider
(org.bouncycastle.jce.provider.BouncyCastleProvider.))
(let [private-key (-> (:secret c)
java.io.StringReader.
org.bouncycastle.openssl.PEMReader.
.readObject
.getPrivate)
signer (doto (java.security.Signature/getInstance "SHA1withRSA" "BC")
(.initSign private-key (java.security.SecureRandom.))
(.update (.getBytes base-string)))
raw-sig (.sign signer)]
-->>> (println "Private key: " private-key)
(String. (Base64/encodeBase64 raw-sig))))
In the Readme code example the delimiter doesn't match, the right one should be as below:
(def credentials (oauth/credentials consumer
(:oauth_token access-token-response)
(:oauth_token_secret access-token-response)
:POST
"http://api.twitter.com/1.1/statuses/update.json"
{:status "posting from #clojure with #oauth"})) <-- The right delimiter
org.bouncycastle/bcprov-jdk15on dependency mismatch.
When will the current master branch be posted to clojars? :)
Hi mattrepl,
Is it possible to use clj-oauth for 2-legged oauth? If so, is there a demo or sample code I can take a look at?
All the best,
Aaron
I'm using clj-oauth 1.2.10. When requiring oauth.client I get an exception:
Can't dynamically bind non-dynamic var: clojure.contrib.pprint/format-str
I guess I'm doing something stupid, but I need help, and hope it's ok I file an issue for some feedback :|
parse-form-encoded
is not defined in oauth.server
.
This causes tests in server_test.clj to fail.
Hello,
I've been using this library in order to interact with the ODesk API (http://developers.odesk.com/w/page/12364003/FrontPage), and it has been very helpful. Thank you! However, I did have some trouble with some post requests to match the signature created with OAuth. One of the major problems I encountered was with spaces in the parameter values:
(def params {:recipients "my_recipient"
:subject "Spaces Cause Problems"
:body "Spaces in text cause strange signature mismatches."})
(def credentials (oauth/credentials consumer
(:oauth_token access-token-response)
(:oauth_token_secret access-token-response)
:POST
"https://www.odesk.com/api/mc/v1/threads/my_username.json"
params))
;using clj-http
(http/post "https://www.odesk.com/api/mc/v1/threads/my_username.json"
{:form-params (merge params credentials)})
This post returns a signature mismatch error (401). After some sleuthing, it seemed that the form encoding was slightly different for the OAuth than http/post such that replacing spaces with pluses before creating the signature fixes the problem.
(defn encode-params [params]
(into {} (map #(vector (first %) (.replace (second %) " " "+")) params)))
;;eg. {:key "Hello World"} becomes {:key "Hello+World"}
Using "encoded" parameters before creating the signature and then using the original "unencoded" parameters for the http POST generates a successful request:
(def params {:recipients "my_recipient"
:subject "Spaces Cause Problems"
:body "Spaces in text cause strange signature mismatches."})
(def credentials (oauth/credentials consumer
(:oauth_token access-token-response)
(:oauth_token_secret access-token-response)
:POST
"https://www.odesk.com/api/mc/v1/threads/my_username.json"
(encode-params params)))
;using clj-http
(http/post "https://www.odesk.com/api/mc/v1/threads/my_username.json"
{:form-params (merge params credentials)})
In the case above, encode-params translates "Spaces Cause Problems" into "Spaces+Cause+Problems" for signature creation, but leaves it as "Spaces Cause Problems" for the http/post. This work around was successful.
It is very possible that there is an easier way to send a post request with parameters that contain spaces that I am missing. Did I miss something or is this translation necessary?
Thank you in advance for your input!
The following parts of the example in the main readme file seem to be wrong - looks like the code has changed since it was written:
oauth/user-approval-uri takes only two parameters, doesn't take a parameter
oauth/request-token can take a third parameter, if you need a custom callback-uri
oauth/user-approval-uri doesn't take the request token as a second parameter, instead it takes the raw oauth token, which you have to extract from the request-token yourself:
(oauth/user-approval-uri consumer
(:oauth_token request-token))
I'm not sure if there's a better end-to-end example somewhere? But it'd be great if the readme had these things (and anything I've missed!) fixed - it's quite confusing at the moment.
in the README there is a talk of a "verifier" and in the tests
#_(deftest
#^{:doc "Considered to pass if no exception is thrown."}
access-token
(let [request-token (oc/request-token consumer)]
(oc/access-token consumer request-token ...verifier...)))
I'm new to clojure but I can't see how that could ever compile.
Where does one get the verifier from? e.g. when accessing the twitter API? I've been trying to read through your clojure-twitter client to see how you do it but I cannot work it out.
Actually it seems as though you don't need to sign the body for Quickbooks Online. Weird.
I'm getting a 401 when calling to Twitter for the request token. I've tried with http://twitter.com URLs and https://api.twitter.com URLs, and have triple checked my token and secret. oauth.client/request-token
kicks back 401 every time.
Any ideas?
; SLIME 2010-09-03
user> (require 'twitter)
nil
user> (require ['oauth.client :as 'oauth])
nil
user>
(def consumer (oauth/make-consumer "lSXXXXXXXXyRPXXXXXXXiQ"
"z9XXXXXXXXZwacXXXXXXXXXXXXuQRXXXXXXXXXCa6k"
"http://twitter.com/oauth/request_token"
"http://twitter.com/oauth/access_token"
"http://twitter.com/oauth/authorize"
:hmac-sha1))
#'user/consumer
user> (def request-token (:oauth_token (oauth/request-token consumer)))
; Evaluation aborted.
user>
Got non-success code: 401. Reason: Unauthorized
[Thrown class java.lang.Exception]
Restarts:
0: [QUIT] Quit to the SLIME top level
Backtrace:
0: oauth.client$check_success_response.invoke(client.clj:129)
1: oauth.client$success_content.invoke(client.clj:133)
2: oauth.client$request_token.invoke(client.clj:61)
3: clojure.lang.AFn.applyToHelper(AFn.java:163)
4: clojure.lang.AFn.applyTo(AFn.java:151)
5: clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:2901)
6: clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:2900)
7: clojure.lang.Compiler$DefExpr.eval(Compiler.java:361)
8: clojure.lang.Compiler.eval(Compiler.java:5424)
9: clojure.lang.Compiler.eval(Compiler.java:5386)
10: clojure.core$eval.invoke(core.clj:2382)
[No Locals]
11: swank.commands.basic$eval_region.invoke(basic.clj:47)
12: swank.commands.basic$eval_region.invoke(basic.clj:37)
13: swank.commands.basic$eval807$listener_eval__808.invoke(basic.clj:71)
14: clojure.lang.Var.invoke(Var.java:365)
15: user$eval4909.invoke(NO_SOURCE_FILE)
16: clojure.lang.Compiler.eval(Compiler.java:5419)
17: clojure.lang.Compiler.eval(Compiler.java:5386)
18: clojure.core$eval.invoke(core.clj:2382)
19: swank.core$eval_in_emacs_package.invoke(core.clj:94)
20: swank.core$eval_for_emacs.invoke(core.clj:241)
21: clojure.lang.Var.invoke(Var.java:373)
22: clojure.lang.AFn.applyToHelper(AFn.java:169)
23: clojure.lang.Var.applyTo(Var.java:482)
24: clojure.core$apply.invoke(core.clj:540)
25: swank.core$eval_from_control.invoke(core.clj:101)
26: swank.core$eval_loop.invoke(core.clj:106)
27: swank.core$spawn_repl_thread$fn__489$fn__490.invoke(core.clj:311)
28: clojure.lang.AFn.applyToHelper(AFn.java:159)
29: clojure.lang.AFn.applyTo(AFn.java:151)
30: clojure.core$apply.invoke(core.clj:540)
31: swank.core$spawn_repl_thread$fn__489.doInvoke(core.clj:308)
32: clojure.lang.RestFn.invoke(RestFn.java:398)
33: clojure.lang.AFn.run(AFn.java:24)
34: java.lang.Thread.run(Thread.java:637)
Sep 15, 2010 12:10:37 AM org.apache.http.client.protocol.ResponseProcessCookies processCookies
WARNING: Invalid cookie header: "Set-Cookie: guest_id=128452743722297827; path=/; expires=Fri, 15 Oct 2010 05:10:37 GMT". Unable to parse expires attribute: Fri, 15 Oct 2010 05:10:37 GMT
Sep 15, 2010 12:10:37 AM org.apache.http.impl.client.DefaultRequestDirector handleResponse
WARNING: Authentication error: Unable to respond to any of these challenges: {}
Hi, Could you update the bouncycastle version ? Currently it is set at 1.5.0 , however, some libraries like Buddy (for authentication) require the 1.5.4 version, and the 1.5.0 version here is breaking those.
Add support for OAuth 2.0 to work with Google's APIs:
https://developers.google.com/accounts/docs/OAuth2UserAgent
Is this how you're supposed to setup a get request?
(def credentials (oauth/credentials consumer
(:oauth_token access-token-response)
(:oauth_token_secret access-token-response)
:GET
"https://www.khanacademy.org/api/v1/[email protected]"))
(http/get "https://www.khanacademy.org/api/v1/[email protected]"
:query-params credentials)
=>
clojure.lang.Keyword cannot be cast to clojure.lang.IPersistentCollection
Hi,
I've used clj-oauth succesfully with Twitter.
I'm now trying to use it against Etsy.
https://www.etsy.com/developers/documentation/getting_started/oauth
When I issue the request-token call, I get signature invalid. I'm not sure what's going on. Will continue to test, but can somebody confirm that the library will work with etsy's OAuth implementation (advertised as OAuth 1.0), or is this new territory?
Out of the blue, I started seeing ClassNotFoundExceptions
for org.bouncycastle.jce.provider.BouncyCastleProvider.
I read that it is not part of the standard Java distribution, which left me very confused because I don't remember ever installing BouncyCastleProvider separately.
Could a recent upgrade of the JRE on my system (Mac OS X 10.7.5, Sun Java version "1.7.0_45") have anything to do with this?
Anyway, I've downloaded the jar from the BouncyCastle website, put it in jre/lib/ext and added a line to $JAVA_HOME/jre/lib/security/java.security. Now I get a java.lang.ClassNotFoundException: org.bouncycastle.openssl.PEMReader.
I'm utterly confused.
Hi,
Would it be possible to cut a release with the HMAC-SHA256 signing capability?
Cheers,
Chris
This is my implementation of the Khan Academy Api. Acording to https://github.com/Khan/khan-api/tree/master/examples/php_test_client the signature needs to have no authorization url because Khan Academy skips that step.
(ns khan.api
(:require [oauth.client :as oauth]
[oauth.signature :as sig]
[clj-http.client :as http]))
(def consumer-key "B9nyR6w6NbLRzbQr")
(def consumer-secret "pUQFGKGebZVhEuCA")
(def consumer (oauth/make-consumer consumer-key
consumer-secret
"https://www.khanacademy.org/api/auth/request_token"
"https://www.khanacademy.org/api/auth/access_token"
"https://www.khanacademy.org/api/auth/authorization"
:hmac-sha1))
(def noauth (oauth/make-consumer consumer-key
consumer-secret
"https://www.khanacademy.org/api/auth/request_token"
""
"https://www.khanacademy.org/api/auth/authorization"
:hmac-sha1))
(defn build-oauth-token-request
"Used to build actual OAuth request."
([consumer uri unsigned-oauth-params]
(let [signature (sig/sign consumer
(sig/base-string "GET" uri unsigned-oauth-params))
oauth-params (assoc unsigned-oauth-params :oauth_signature signature)]
(oauth/build-request oauth-params))))
(defn request-token
"Fetch request token for the consumer."
([consumer noauth callback-uri]
(let [unsigned-params (-> (sig/oauth-params consumer
(sig/rand-str 30)
(sig/msecs->secs (System/currentTimeMillis)))
(assoc :oauth_callback callback-uri))]
{:status 200
:headers {"Content-Type" "text/html"}
:body (:body (http/get (:request-uri consumer)
(build-oauth-token-request noauth
(:request-uri consumer)
unsigned-params)))})))
And this is the server that hosts it. I'm having trouble with redirecting to the login callback, I have no idea why khan academy sends me to the default callback. My (probably incorrect) understanding is that upon successful authentication and having provided a callback, the khan academy server sends a GET or POST request to the callback server. I'm using compojure destructuring on "/login/:params" and I was expecting the webpage to print the query params of the GET request. I'm clueless right now.
(ns khan.web
(:require [compojure.core :refer [defroutes GET PUT POST DELETE ANY]]
[compojure.handler :refer [site]]
[compojure.route :as route]
[clojure.java.io :as io]
[ring.adapter.jetty :as jetty]
[environ.core :refer [env]]
[khan.api :as khan]))
(defn khan-api []
(khan/request-token khan/consumer khan/noauth "http://localhost:5000/login/"))
(defroutes app
(GET "/" []
(khan-api))
(POST "/login/:params" [params]
(str params))
(ANY "*" []
(route/not-found (slurp (io/resource "404.html")))))
(defn -main [& [port]]
(let [port (Integer. (or port (env :port) 5000))]
(jetty/run-jetty (site #'app) {:port port :join? false})))
I might have missunderstood your intention here, but I believe it should be always encoded, right now if one of the signature values contains a "=" character it will cause the authorization to fail.
see http://oauth.net/core/1.0a/#rfc.section.5.4.1 (edit updated link to 1.0a )
something like that would do i think (untested):
http://github.com/mattrepl/clj-oauth/blob/master/src/oauth/client.clj#L143
(defn authorization-header
"OAuth credentials formatted for the Authorization HTTP header."
([oauth-params](str "OAuth " %28join ", " %28map %28fn [[k v]]
%28str %28-> k as-str sig/url-encode) "="" (sig/url-encode v) """))
oauth-params))))
I create a config.clj
containing my details, with this structure
(def ^:const +twitter_consumer_key+ "...")
(def ^:const +twitter_consumer_secret+ "...")
(def ^:const +twitter_access_token+ "")
(def ^:const +twitter_access_token_secret+ "")
and then run this code
(ns fommil.teflon.core
(:require
[oauth.client :as oauth]
[clj-http.client :as http]))
(load-file "config.clj")
(def consumer (oauth/make-consumer
+twitter_consumer_key+
+twitter_consumer_secret+
"https://api.twitter.com/oauth/request_token"
"https://api.twitter.com/oauth/access_token"
"https://api.twitter.com/oauth/authorize"
:hmac-sha1))
(def credentials (oauth/credentials
consumer
+twitter_access_token+
+twitter_access_token_secret+
:get
"https://api.twitter.com/1.1/statuses/user_timeline.json"
{:screen_name "fommil"}))
(http/get "https://api.twitter.com/1.1/statuses/user_timeline.json"
{:query-params credentials})
but I get
clj-http: status 401 {:status 401, :headers {"server" "tsa_f", "content-type" "application/json; charset=utf-8", "content-length" "89", "x-connection-hash" "<obfuscated>", "strict-transport-security" "max-age=631138519", "connection" "close", "x-response-time" "106", "set-cookie" "guest_id=<obfuscated>; Domain=.twitter.com; Path=/; Expires=Mon, 19-Mar-2018 17:06:28 UTC", "date" "Sat, 19 Mar 2016 17:06:28 GMT"}, :body "{\"errors\":[{\"code\":32,\"message\":\"Could not authenticate you.\"}]}", :request-time 452, :trace-redirects ["https://api.twitter.com/1.1/statuses/update.json"], :orig-content-encoding "gzip"}
i.e. code 32 "Could not authenticate you".
I can run a similar command, as per the twitter OAuth tool's curl
example, using the same tokens, and everything works as expected.
Is this a bug or am I doing it wrong?
I've seen that similar errors are reported in other OAuth libraries for other languages https://twittercommunity.com/t/errors-message-could-not-authenticate-you-code-32/1223
(request-token consumer "http://google.com")
results in a 401 exception, the response message from twitter says it's an encoding
problem, I guess it has something to do with the encoding of callback url.
Re: #51, consider adding support for custom decode functions.
What if you let developers pass a custom decode function as an argument to make-consumer or request-token?
Your LICENSE is Simplified BSD, but your project file is Eclipse Public License.
Can you please clarify which licence you are releasing this software under? I will assume dual licence unless corrected ๐ (and I choose Simplified BSD).
I'm using 1.4.0-SNAPSHOT
and callback-uri doesn't seem to get added to the authorization URI automatically. I don't know if this is unique to the latest version, though.
Which is the official Clojars package for clj-oauth?
https://clojars.org/search?q=clj-oauth
They all seem to be outdated.
Could the official one be updated and dependency info provided in README?
I am getting Unauthorized 401 from LinkedIn. The base string which LinkedIn expects to have been signed includes the oauth_nonce and oauth_timestamp parameters. I believe that sig/base-string does not include these. Is that right?
Robert
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.