forked from drone29a/clj-oauth
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.clj
177 lines (163 loc) · 7.14 KB
/
client.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
(ns
#^{:author "Matt Revelle"
:doc "OAuth client library for Clojure."}
oauth.client
(:require [oauth.digest :as digest]
[oauth.signature :as sig]
[com.twinql.clojure.http :as http])
(:use [clojure.string :only [join split upper-case]]))
(declare success-content
authorization-header)
(defstruct #^{:doc "OAuth consumer"} consumer
:key
:secret
:request-uri
:access-uri
:authorize-uri
:signature-method)
(defn check-success-response [m]
(let [code (:code m)]
(if (or (< code 200)
(>= code 300))
(throw (new Exception (str "Got non-success response " code ".")))
m)))
(defn success-content [m]
(:content
(check-success-response m)))
(defn make-consumer
"Make a consumer struct map."
[key secret request-uri access-uri authorize-uri signature-method]
(struct consumer
key
secret
request-uri
access-uri
authorize-uri
signature-method))
;;; Parse form-encoded bodies from OAuth responses.
(defmethod http/entity-as :urldecoded
[entity as status]
(into {}
(if-let [body (http/entity-as entity :string status)]
(map (fn [kv]
(let [[k v] (split kv #"=")
k (or k "")
v (or v "")]
[(keyword (sig/url-decode k)) (sig/url-decode v)]))
(split body #"&"))
nil)))
(defn request-token
"Fetch request token for the consumer."
([consumer]
(let [unsigned-params (sig/oauth-params consumer)
signature (sig/sign consumer
(sig/base-string "POST"
(:request-uri consumer)
unsigned-params))
params (assoc unsigned-params
:oauth_signature signature)]
(success-content
(http/post (:request-uri consumer)
:headers {"Authorization" (authorization-header params)}
:parameters (http/map->params {:use-expect-continue false})
:as :urldecoded))))
([consumer callback-uri]
(let [unsigned-params (assoc (sig/oauth-params consumer)
:oauth_callback callback-uri)
signature (sig/sign consumer
(sig/base-string "POST"
(:request-uri consumer)
unsigned-params))
params (assoc unsigned-params
:oauth_signature signature)]
(success-content
(http/post (:request-uri consumer)
:headers {"Authorization" (authorization-header params)}
:parameters (http/map->params {:use-expect-continue false})
:as :urldecoded)))))
(defn user-approval-uri
"Builds the URI to the Service Provider where the User will be prompted
to approve the Consumer's access to their account."
[consumer token]
(.toString (http/resolve-uri (:authorize-uri consumer)
{:oauth_token token})))
(defn access-token
"Exchange a request token for an access token.
When provided with two arguments, this function operates as per OAuth 1.0.
With three arguments, a verifier is used."
([consumer request-token]
(access-token consumer request-token nil))
([consumer request-token verifier]
(let [unsigned-params (if verifier
(sig/oauth-params consumer
(:oauth_token request-token)
verifier)
(sig/oauth-params consumer
(:oauth_token request-token)))
signature (sig/sign consumer
(sig/base-string "POST"
(:access-uri consumer)
unsigned-params)
(:oauth_token_secret request-token))
params (assoc unsigned-params
:oauth_signature signature)]
(success-content
(http/post (:access-uri consumer)
:headers {"Authorization" (authorization-header params)}
:parameters (http/map->params {:use-expect-continue false})
:as :urldecoded)))))
(defn xauth-access-token
"Request an access token with a username and password with xAuth."
[consumer username password]
(let [oauth-params (sig/oauth-params consumer)
post-params {:x_auth_username username
:x_auth_password password
:x_auth_mode "client_auth"}
signature (sig/sign consumer
(sig/base-string "POST"
(:access-uri consumer)
(merge oauth-params
post-params)))
params (assoc oauth-params
:oauth_signature signature)]
(success-content
(http/post (:access-uri consumer)
:query post-params
:headers {"Authorization" (authorization-header params)}
:parameters (http/map->params {:use-expect-continue false})
:as :urldecoded))))
(defn credentials
"Return authorization credentials needed for access to protected resources.
The key-value pairs returned as a map will need to be added to the
Authorization HTTP header or added as query parameters to the request."
([consumer token token-secret request-method request-uri & [request-params]]
(let [unsigned-oauth-params (sig/oauth-params consumer token)
unsigned-params (merge request-params
unsigned-oauth-params)
signature (sig/sign consumer
(sig/base-string (-> request-method
sig/as-str
upper-case)
request-uri
unsigned-params)
token-secret)]
(assoc unsigned-oauth-params :oauth_signature signature))))
(defn authorization-header
"OAuth credentials formatted for the Authorization HTTP header."
([oauth-params]
(str "OAuth " (join ", " (map (fn [[k v]]
(str (-> k sig/as-str sig/url-encode) "=\"" (-> v sig/as-str sig/url-encode) "\""))
oauth-params))))
([oauth-params realm]
(authorization-header (assoc oauth-params realm))))
(defn check-success-response [m]
(let [code (:code m)
reason (:reason m)]
(if (or (< code 200)
(>= code 300))
(throw (new Exception (str "Got non-success code: " code ". "
"Reason: " reason ", "
"Content: " (:content m))))
m)))
(defn success-content [m]
(:content (check-success-response m)))