Share Your Code

OAuth Library

Posted by muxukeikei, Posted on November 3, 2010, Last updated March 21, 2011

This is OAuth Library for Corona.
download link is http://bit.ly/corona-oauth

**Update**
version 2.0 Release note
・async http request support(network library)
・test using twitter oauth system
・fix some bugs

(only sync http request version for android is preparing now.)

main.lua for test tweet
http://bit.ly/corona-oauth-main
(this main.lua has some bug. (e.g. error handling) )
(can not use simulator. xcode simulator or device is OK)

next version road map
・Authorization header support
・OAuth 2.0 support
・more other oauth1.0a system support
(maybe this version support only Twitter oauth system now)
・retrieve access token support
・more network error handling

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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
---
-- Using oauth in lua. ( for Corona version)
--
--              (original)
--     Copyright 2009-2010 by DracoBlue (JanS@DracoBlue.de)
--                 http://dracoblue.net 
--              
--              (for Corona version)
--              developed by Keisuke Kimura(@muxukeikei)
--              
-- @version 2.0
-- @license MIT License
--
-- version2.0
-- async http request support
-- fix some bug
--
--
 
module(...,package.seeall)
 
local crypto = require("crypto")
local mime = require("mime")
local http = require("socket.http")
local ltn12 = require("ltn12")
 
local function encode_parameter(str)
        return str:gsub('[^-%._~a-zA-Z0-9]',function(c)
                return string.format("%%%02x",c:byte()):upper()
        end)
end
 
local function sha1(str,key,binary)
        binary = binary or false
        return crypto.hmac(crypto.sha1,str,key,binary)
end
 
local function get_nonce()
        return mime.b64(crypto.hmac(crypto.sha1,tostring(math.random()) .. "random" .. tostring(os.time()),"keyyyy"))
end
 
local function get_timestamp()
        return tostring(os.time() + 1)
end
 
local function rawGetRequest(url,callback)
   
    if callback then
        
                network.request(url,"GET",callback,nil)
         
    else
   
                local r,c,h
                local response = {}
           
                r,c,h = http.request{
                        url = url,
                        sink = ltn12.sink.table(response)
                }
                
                return table.concat(response,""),r,c,h
  
    end
   
end
 
local function rawPostRequest(url, rawdata, callback)
    
        if callback then
                
                local params = {}
  
                params.header =  {
                        ["Content-Type"] = "application/x-www-form-urlencoded",
                        ["Content-Length"] = string.len(rawdata),
                }
                params.body = rawdata
   
                network.request(url,"POST",callback,params)
                
        else
        
                local r,c,h
                local response = {}
                
                r,c,h = http.request{
                        url = url,
                        method = "POST",
                        headers = {
                                ["Content-Type"] = "application/x-www-form-urlencoded",
                                ["Content-Length"] = string.len(rawdata),
                        },
                        source = ltn12.source.string(rawdata),
                        sink = ltn12.sink.table(response)
                }
                
                return table.concat(response,""),r,c,h
        
        end
    
end
 
local function oauth_sign(url, method, args, consumer_secret)
 
    assert(method == "GET" or method == "POST","method must be either POST or GET")
    assert(consumer_secret,"consumer_secret required")
    assert(url,"url required")
    
    local token_secret = args.oauth_token_secret or ""
 
    --
    -- Remove the token_secret from the args, 'cause we neither send nor sign it.
    -- (we use it for signing which is why we need it in the first place)
    --
    args.oauth_token_secret = nil
 
        local keys_and_values = {}
 
        for key, val in pairs(args) do
        table.insert(keys_and_values, {
          key = encode_parameter(key),
          val = encode_parameter(val)
        })
    end
 
    table.sort(keys_and_values, function(a,b)
        if a.key < b.key then
            return true
        elseif a.key > b.key then
            return false
        else
            return a.val < b.val
        end
    end)
    
    local key_value_pairs = {}
        for _, rec in pairs(keys_and_values) do
        table.insert(key_value_pairs, rec.key .. "=" .. rec.val)
        end
    
        local query_string_except_signature = table.concat(key_value_pairs, "&")
   
        local SignatureBaseString = method .. '&' .. encode_parameter(url) .. '&' .. encode_parameter(query_string_except_signature)
        local key = encode_parameter(consumer_secret) .. '&' .. encode_parameter(token_secret)
    
        local hmac_binary = sha1(SignatureBaseString, key, true)
 
        local hmac_b64 = mime.b64(hmac_binary)
   
        local query_string = query_string_except_signature .. '&oauth_signature=' .. encode_parameter(hmac_b64)
 
        if method == "GET" then
           return url .. "?" .. query_string
        else
                return query_string
        end
end
 
local function getRequestToken(options,method,callback)
 
    assert(options.authorize_url,"authorize_url option must be set") 
    assert(options.request_token_url,"request_token_url option must be set") 
    assert(options.consumer_key,"consumer_key option must be set")
    assert(options.consumer_secret,"consumer_secret option must be set")
    assert(options.token_ready_url,"token_ready_url option must be set")
        assert(method == "GET" or method == "POST","method must be either POST or GET")
        
        if callback then
                assert(type(callback) == "function", "callback must be function")
        end
        
        local authorize_url = options.authorize_url
 
        local post_data = {
         oauth_consumer_key = options.consumer_key,
         oauth_timestamp    = get_timestamp(),
         oauth_version      = '1.0',
         oauth_nonce        = get_nonce(),
         oauth_callback         = options.token_ready_url,
         oauth_signature_method = "HMAC-SHA1"
    }
    
    local result = oauth_sign(options.request_token_url,
                                                method,
                                                post_data,
                                                options.consumer_secret)
   
    if method == "GET" then
        return rawGetRequest(result,callback)
    else
                return rawPostRequest(options.request_token_url,result,callback)
    end
    
end
 
-- @return oauth_Access
local function getAccessByTokenAndSecret(token, token_secret, options)
    assert(options.consumer_key,"consumer_key option must be set")
    local consumer_key = options.consumer_key
    local consumer_secret = options.consumer_secret
    
    local function local_call(url, method, params,callback)
        local post_data = {
           oauth_consumer_key = consumer_key,
           oauth_timestamp    = get_timestamp(),
           oauth_version      = '1.0',
           oauth_nonce        = get_nonce(),
           oauth_token           = token,
           oauth_token_secret = token_secret,
           oauth_signature_method = "HMAC-SHA1"
        }
        for k,v in pairs(params) do
            post_data[k] = v
        end
        if (method == "POST") then
            post_data = oauth_sign(
                url,
                "POST",
                post_data,
                consumer_secret
            )
            return rawPostRequest(url, post_data,callback)
           
        elseif (method == "GET") then
            url = oauth_sign(
                url,
                "GET",
                post_data,
                consumer_secret
            )
            
            return rawGetRequest(url,callback)
            
        end
       
    end
    
    return {
        call = local_call
    }
    
end
 
 
local function parse_request_token_response(options, response)
    
    local token = response:match('oauth_token=([^&]+)')
    local secret = response:match('oauth_token_secret=([^&]+)')
    
        return {
                getAuthorizeUrl = function()    
                if token and secret then
                    return options.authorize_url .. "?" .. "oauth_token=" .. token .. "&" .. "oauth_callback=" .. options.token_ready_url;
            end
            return nil
        end,
        getToken = function()
                if token then
                return token
                        end             
            return nil
        end,
        getTokenSecret = function()
                if secret then
                return secret
                end   
            return nil
        end,
    } 
    
end
 
local function parse_access_token_response(options, response)
 
 local token = response:match('oauth_token=([^&]+)')
 local secret = response:match('oauth_token_secret=([^&]+)')
 local consumer_key = options.consumer_key
    
        return {
                getAccess = function()
                if token and secret then
                    return getAccessByTokenAndSecret(
                    token,
                        secret,
                    {
                       consumer_key = consumer_key
                        }
                )
            end
            return nil
        end,
        getToken = function()
                if token then
                return token
                        end             
            return nil
        end,
        getTokenSecret = function()
                if secret then
                return secret
                end   
            return nil
        end,
        getUserId = function()
            if response then
                return response:match(           'user_id=([^&]+)')
            end
            return nil  
        end,
        getScreenName = function()
            if response then
                return response:match(       'screen_name=([^&]+)')
            end
            return nil
        end
    } 
    
end
 
local function getAccessToken(token, verifier, token_secret, options, method, callback)
    assert(options.consumer_key,"consumer_key option must be set")
    assert(options.consumer_secret,"consumer_secret option must be set")
    assert(token,"oauth_token option must be set")
    assert(token_secret,"oauth_token_secret must be set")
    assert(options.access_token_url,"access_token_url option must be set")
    assert(method == "GET" or method == "POST","method must be either POST or GET")
    
    if callback then
                assert(type(callback) == "function", "callback must be function")
        end
        
    local consumer_key = options.consumer_key
    
    local post_data = {
       oauth_consumer_key = consumer_key,
       oauth_timestamp    = get_timestamp(),
       oauth_version      = '1.0',
       oauth_nonce        = get_nonce(),
       oauth_token        = options.oauth_token,
       oauth_token_secret = options.oauth_token_secret,
       oauth_verifier     = verifier,
       oauth_signature_method = "HMAC-SHA1", 
    }
    local post_data = oauth_sign(options.access_token_url,
                          method,
                          post_data,
                          options.consumer_secret)
    
    if method == "GET" then
        return rawGetRequest(result,callback)
    else
                return rawPostRequest(options.request_token_url,result,callback)
    end
        
end
 
local function getAccessTokenByPin(pin,method,options,callback)
    assert(options.consumer_key,"consumer_key option must be set")
    assert(options.consumer_secret,"consumer_secret option must be set")
    assert(options.oauth_token,"oauth_token option must be set")
    assert(options.oauth_token_secret,"oauth_token_secret must be set")
    assert(options.access_token_url,"access_token_url option must be set")
    assert(method == "GET" or method == "POST","method must be either POST or GET")
    assert(pin,"pin must be given")
    
    if callback then
                assert(type(callback) == "function", "callback must be function")
        end
        
    local consumer_key = options.consumer_key
   
        local post_data = {
       oauth_consumer_key = consumer_key,
       oauth_timestamp    = get_timestamp(),
       oauth_version      = '1.0',
       oauth_nonce        = get_nonce(),
       oauth_token        = options.oauth_token,
       oauth_token_secret = options.oauth_token_secret,
       oauth_verifier     = tostring(pin),
       oauth_signature_method = "HMAC-SHA1",
        }
    
    local post_data = oauth_sign(options.access_token_url,
                          "POST",
                          post_data,
                          options.consumer_secret)
                          
    rawPostRequest(options.access_token_url, post_data,callback)
  
end
 
consumer = {}
function newConsumer(options)
    assert(options.request_token_url,"request_token_url option must be set") 
    assert(options.authorize_url,"authorize_url option must be set") 
    assert(options.access_token_url,"access_token_url option must be set")
    assert(options.consumer_key,"consumer_key option must be set")
    assert(options.consumer_secret,"consumer_secret option must be set")
    assert(options.token_ready_url,"token_ready_url option must be set")
    
    local consumer_key = options.consumer_key
    local consumer_secret = options.consumer_secret
    local authorize_url = options.authorize_url
    local request_token_url = options.request_token_url
    local access_token_url = options.access_token_url
    local token_ready_url = options.token_ready_url
    
    function local_getRequestToken(method,callback)
        local auth_opts = {
            consumer_key = consumer_key,
            consumer_secret = consumer_secret,
            request_token_url = request_token_url,
            authorize_url = authorize_url,
            token_ready_url = token_ready_url,
        }
        return getRequestToken(auth_opts,method,callback)
    end
    
    function local_getAccessToken(token, verifier, token_secret,method,callback)
        local access_opts = {
            consumer_key = consumer_key,
            consumer_secret = consumer_secret,
            access_token_url = access_token_url
        }
        return getAccessToken(token, verifier, token_secret, access_opts,method,callback)
    end
    
    function local_parse_request_token_response(response)
        return parse_request_token_response(options,response)
    end
    
     
    function local_parse_access_token_response(response)
        return parse_access_token_response(options,response)
    end
    
    function local_getAccessTokenByPin(pin,method,options,callback)
        local access_opts = {
            consumer_key = options.consumer_key,
            consumer_secret = options.consumer_secret,
            access_token_url = options.access_token_url,
            oauth_token = options.oauth_token,
            oauth_token_secret = options.oauth_token_secret,
        }
        return getAccessTokenByPin(pin,method,access_opts,callback)
    end
    
    function local_getAccess(token, token_secret)
        return getAccessByTokenAndSecret(token, token_secret, { consumer_key = consumer_key,consumer_secret = consumer_secret })
    end
    
    return {
        getRequestToken = local_getRequestToken,
        getAccessToken = local_getAccessToken,
        getAccessTokenByPin = local_getAccessTokenByPin,
        getAccess = local_getAccess,
        parseRequestTokenResponse = local_parse_request_token_response, 
        parseAccessTokenResponse = local_parse_access_token_response,
    }
end

Compatibility: 
Corona 2.0

Replies

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

I'm unable to get this to work, I've tried it with Readability's API as well as Twitter, with no luck :(

any suggestions?

muxukeikei
User offline. Last seen 2 years 31 weeks ago. Offline
Joined: 4 Feb 2010

> trollapps

Sorry , When I write this library, I forget about SSL.
I'm rewritten this oauth-library using network library now.

please wait :)

dgaedcke
User offline. Last seen 15 hours 7 min ago. Offline
Joined: 17 Apr 2010

I was just about to ask about async and ssl.....glad to hear you are working on it with the new network support. I look forward to checking it out when you are ready for testers ;-)

spjnowak
User offline. Last seen 3 hours 43 min ago. Offline
Joined: 21 Aug 2009

@muxukeikei Any progress on this? Looks like a lot of people need OAuth access for Twitter, Facebook etc.

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

muxukeikei,

I actually got it working for what I needed. I'll probably rewrite it for async somewhere in the next couple weeks

spjnowak
User offline. Last seen 3 hours 43 min ago. Offline
Joined: 21 Aug 2009

Did you get it working with Twitter (which is what I need)?

maarten.koopmans
User offline. Last seen 3 weeks 11 hours ago. Offline
Joined: 29 Mar 2010

Maybe you could share what you have, and let others add to it/improve it?

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

@spjnowak

Yea, got it working with Twitter. Once I rewrite it using async I'll post code.

maarten.koopmans
User offline. Last seen 3 weeks 11 hours ago. Offline
Joined: 29 Mar 2010

I can live without the async, need it for dropbox integration. Feel free to send it to me privately ;-)))

maarten.koopmans
User offline. Last seen 3 weeks 11 hours ago. Offline
Joined: 29 Mar 2010

I can live without the async, need it for dropbox integration. Feel free to send it to me privately ;-)))

spjnowak
User offline. Last seen 3 hours 43 min ago. Offline
Joined: 21 Aug 2009

I'm losing track of who's writing what code now! And who's going to finish something first ... I just need to post a tweet - do I need async? ssl?

muxukeikei? trollapps?

Puzzle Runner
User offline. Last seen 17 hours 59 min ago. Offline
Joined: 8 Feb 2011

Can we get an example of this working with Twitter?

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

All,

here is what I've come up with using code from muxukeikei. This will work with twitter as well.

oAuth.lua

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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
module(...,package.seeall)
 
local http = require("socket.http")
local ltn12 = require("ltn12")
local crypto = require("crypto")
local mime = require("mime")
--/////////////////////////////////////////////////////////////////////////////////////
--// GET REQUEST TOKEN
--/////////////////////////////////////////////////////////////////////////////////////
function getRequestToken(consumer_key, token_ready_url, request_token_url, consumer_secret)
 
        local post_data = 
        {
                oauth_consumer_key = consumer_key,
                oauth_timestamp    = get_timestamp(),
                oauth_version      = '1.0',
                oauth_nonce        = get_nonce(),
                oauth_callback         = token_ready_url,
                oauth_signature_method = "HMAC-SHA1"
        }
    
    local post_data = oAuthSign(request_token_url, "POST", post_data, consumer_secret)
    
    local result = rawPostRequest(request_token_url, post_data)
    local token = result:match('oauth_token=([^&]+)')
    local token_secret = result:match('oauth_token_secret=([^&]+)')
        
        return 
        {
                token = token,
                token_secret = token_secret
        }
        
end
--/////////////////////////////////////////////////////////////////////////////////////
--// GET ACCESS TOKEN
--/////////////////////////////////////////////////////////////////////////////////////
function getAccessToken(token, verifier, token_secret, consumer_key, consumer_secret, access_token_url)
            
    local post_data = 
        {
                oauth_consumer_key = consumer_key,
                oauth_timestamp    = get_timestamp(),
                oauth_version      = '1.0',
                oauth_nonce        = get_nonce(),
                oauth_token        = token,
                oauth_token_secret = token_secret,
                oauth_verifier     = verifier,
                oauth_signature_method = "HMAC-SHA1"
 
    }
    local post_data = oAuthSign(access_token_url, "POST", post_data, consumer_secret)
    local result = rawPostRequest(access_token_url, post_data)
    return result
end
--/////////////////////////////////////////////////////////////////////////////////////
--// MAKE REQUEST
--/////////////////////////////////////////////////////////////////////////////////////
function makeRequest(url, body, consumer_key, token, consumer_secret, token_secret, method)
    
    local post_data = 
        {
                oauth_consumer_key = consumer_key,
                oauth_nonce        = get_nonce(),
                oauth_signature_method = "HMAC-SHA1",
                oauth_token        = token,
                oauth_timestamp    = get_timestamp(),
                oauth_version      = '1.0',
                oauth_token_secret = token_secret
    }
        for i=1, #body do
                post_data[body[i].key] = body[i].value
        end
    local post_data = oAuthSign(url, method, post_data, consumer_secret)
 
        local result
        
        if method == "POST" then
        result = rawPostRequest(url, post_data)
        else
        result = rawGetRequest(post_data)
        end
        
    return result
end
--/////////////////////////////////////////////////////////////////////////////////////
--// OAUTH SIGN
--/////////////////////////////////////////////////////////////////////////////////////
function oAuthSign(url, method, args, consumer_secret)
 
    local token_secret = args.oauth_token_secret or ""
 
    args.oauth_token_secret = nil
 
        local keys_and_values = {}
 
        for key, val in pairs(args) do
                table.insert(keys_and_values, 
                {
                        key = encode_parameter(key),
                        val = encode_parameter(val)
                })
    end
 
    table.sort(keys_and_values, function(a,b)
        if a.key < b.key then
            return true
        elseif a.key > b.key then
            return false
        else
            return a.val < b.val
        end
    end)
    
    local key_value_pairs = {}
 
    for _, rec in pairs(keys_and_values) do
        table.insert(key_value_pairs, rec.key .. "=" .. rec.val)
    end
    
   local query_string_except_signature = table.concat(key_value_pairs, "&")
   
   local sign_base_string = method .. '&' .. encode_parameter(url) .. '&' .. encode_parameter(query_string_except_signature)
 
   local key = encode_parameter(consumer_secret) .. '&' .. encode_parameter(token_secret)
   local hmac_binary = sha1(sign_base_string, key, true)
 
   local hmac_b64 = mime.b64(hmac_binary)
   local query_string = query_string_except_signature .. '&oauth_signature=' .. encode_parameter(hmac_b64)
 
        if method == "GET" then
           return url .. "?" .. query_string
        else
                return query_string
        end
end
--/////////////////////////////////////////////////////////////////////////////////////
--// ENCODE PARAMETER
--/////////////////////////////////////////////////////////////////////////////////////
function encode_parameter(str)
        return str:gsub('[^-%._~a-zA-Z0-9]',function(c)
                return string.format("%%%02x",c:byte()):upper()
        end)
end
--/////////////////////////////////////////////////////////////////////////////////////
--// SHA 1
--/////////////////////////////////////////////////////////////////////////////////////
function sha1(str,key,binary)
        binary = binary or false
        return crypto.hmac(crypto.sha1,str,key,binary)
end
--/////////////////////////////////////////////////////////////////////////////////////
--// GET NONCE
--/////////////////////////////////////////////////////////////////////////////////////
function get_nonce()
        return mime.b64(crypto.hmac(crypto.sha1,tostring(math.random()) .. "random" .. tostring(os.time()),"keyyyy"))
end
--/////////////////////////////////////////////////////////////////////////////////////
--// GET TIMESTAMP
--/////////////////////////////////////////////////////////////////////////////////////
function get_timestamp()
        return tostring(os.time() + 1)
end
--/////////////////////////////////////////////////////////////////////////////////////
--// RAW GET REQUEST
--/////////////////////////////////////////////////////////////////////////////////////
function rawGetRequest(url)
        local r,c,h
        local response = {}
 
        r,c,h = http.request
        {
                url = url,
                sink = ltn12.sink.table(response)
        }
 
        return table.concat(response,"")
end
--/////////////////////////////////////////////////////////////////////////////////////
--// RAW POST REQUEST
--/////////////////////////////////////////////////////////////////////////////////////
function rawPostRequest(url, rawdata)
 
        local r,c,h
        local response = {}
 
        r,c,h = http.request
        {
                url = url,
                method = "POST",
                headers = 
                {
                        ["Content-Type"] = "application/x-www-form-urlencoded", 
                        ["Content-Length"] = string.len(rawdata)
                },
                source = ltn12.source.string(rawdata),
                sink = ltn12.sink.table(response)
        }
 
        return table.concat(response,"")
end

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

and here's an example of utilizing it with twitter. let me know if you run into problems, I had to single out chunks of code from my project and might have missed some stuff. the responseToTable function is what I use to turn url variables into a table, anyone know a better solution?

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
local consumer_key = ''
local consumer_secret = ''
local access_token
local access_token_secret
local user_id
local screen_name
 
 
local twitter_request = (oAuth.getRequestToken(consumer_key, "your website", "http://twitter.com/oauth/request_token", consumer_secret))
local twitter_request_token = twitter_request.token
local twitter_request_token_secret = twitter_request.token_secret
 
local function listener(event)
        
        local remain_open = true
        local url = event.url
        
        if url:find("oauth_token") then
                
                url = url:sub(url:find("?") + 1, url:len())
                
                local authorize_response = responseToTable(url, {"=", "&"})
                remain_open = false
                
                local access_response = responseToTable(oAuth.getAccessToken(authorize_response.oauth_token, authorize_response.oauth_verifier, twitter_request_token_secret, consumer_key, consumer_secret, "https://api.twitter.com/oauth/access_token"), {"=", "&"})
                
                access_token = access_response.oauth_token
                access_token_secret = access_response.oauth_token_secret
                user_id = access_response.user_id
                screen_name = access_response.screen_name
                -- API CALL:
                local request_response = oAuth.makeRequest("http://api.twitter.com/1/" .. user_id .. "/lists.json", "", consumer_key, access_token, consumer_secret, access_token_secret, "GET")                
        end
 
        return remain_open
end
native.showWebPopup(184, 162, 400, 700, "http://api.twitter.com/oauth/authorize?oauth_token=" .. twitter_request_token, {urlRequest = listener})
 
 
--/////////////////////////////////////////////////////////////////////////////////////
--// RESPONSE TO TABLE
--/////////////////////////////////////////////////////////////////////////////////////
function responseToTable(str, delimeters)
        local obj = {}
        
        while str:find(delimeters[1]) ~= nil do
                if #delimeters > 1 then
                        local key_index = 1
                        local val_index = str:find(delimeters[1])
                        local key = str:sub(key_index, val_index - 1)
 
                        str = str:sub((val_index + delimeters[1]:len()))
 
                        local end_index
                        local value
                
                        if str:find(delimeters[2]) == nil then
                                end_index = str:len()
                                value = str
                        else
                                end_index = str:find(delimeters[2])
                                value = str:sub(1, (end_index - 1))
                                str = str:sub((end_index + delimeters[2]:len()), str:len())
                        end
                        obj[key] = value
                        --print(key .. ":" .. value)
                else
                        
                        local val_index = str:find(delimeters[1])
                        str = str:sub((val_index + delimeters[1]:len()))
                        
                        local end_index
                        local value
                
                        if str:find(delimeters[1]) == nil then
                                end_index = str:len()
                                value = str
                        else
                                end_index = str:find(delimeters[1])
                                value = str:sub(1, (end_index - 1))
                                str = str:sub(end_index, str:len())
                        end
                        obj[#obj + 1] = value
                        --print(value)
                end
        end
        return obj
end

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

also, here's an example of making an API call with parameters:

1
2
3
4
5
6
7
8
9
10
11
12
local params = {}
params[1] = 
{
        key = 'include_entities', 
        value = 'true'
}
params[2] =
{
        key = 'count',
        value = '100'
}
request_response = oAuth.makeRequest("http://api.twitter.com/1/statuses/home_timeline.json", params, consumer_key, access_token, consumer_secret, access_token_secret, "GET")

Puzzle Runner
User offline. Last seen 17 hours 59 min ago. Offline
Joined: 8 Feb 2011

I'm beginning to work with this, I'll post again with progress or lack thereof :)

Puzzle Runner
User offline. Last seen 17 hours 59 min ago. Offline
Joined: 8 Feb 2011

Unfortunately, I can't get this to work, and it may be a configuration issue as much as it is a coding issue. My goal is to simply post a tweet as this is all the functionality I really need.

The first thing I notice is that in your

1
local twitter_request = (oAuth.getRequestToken(consumer_key, "your website", "http://twitter.com/oauth/request_token", consumer_secret))

You have a string of "your website". I don't have a website, my game is simply an iPhone app and on my Twitter Dev account, my game is registered as a client, not a website (is that something I need to change?). I am not sure if there is some kind of App Id I need to be passing in (like Facebook?) or just pass in a blank? As of now I am using a blank string.

I am able to get as far as launching a web pop-up, having the user log in, having the user give my application permissions, and then being sent to a page with a "validation code" they are supposed to enter inside of my game. Which of course leaves me bewildered...

Code wise, the access_token and access_token_secret are not being given to me, which leaves me unable to post anything.

One more caveat: On the Twitter Dev account, there is an oauth 1.0a access_token and access_token_secret I can use... I am not sure if I could or should be using these for something.

Here is my code. The callTwitter function is attached to a button.

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
--Twitter Functions
local postTwitter = function(message)
        local params = { {"status", message} }
        if access_token ~= nil then
                oauth.makeRequest("http://api.twitter.com/1/statuses/update.json", params, consumer_key, access_token, consumer_secret, access_token_secret, "POST")
        end
end
        
local callTwitter = function(message)
        local twitter_request = oauth.getRequestToken(consumer_key, "", "https://twitter.com/oauth/request_token", consumer_secret)
        local twitter_request_token = twitter_request.token
        local twitter_request_token_secret = twitter_request.token_secret
        print("TOKEN CallTwitter:")
        print(twitter_request_token)
        local twitterListener = function(event)
                local remain_open = true
                local url = event.url
                if url:find("oauth_token") then         
                        url = url:sub(url:find("?") + 1, url:len())
                        
                        local authorize_response = oauth.responseToTable(url, {"=", "&"})
                        remain_open = false
                        
                        local access_response = oauth.responseToTable(oauth.getAccessToken(authorize_response.oauth_token, authorize_response.oauth_verifier, twitter_request_token_secret, consumer_key, consumer_secret, "https://api.twitter.com/oauth/access_token"), {"=", "&"})
                        access_token = access_response.oauth_token
                        access_token_secret = access_response.oauth_token_secret
                        user_id = access_response.user_id
                        screen_name = access_response.screen_name
                        postTwitter(message)
                end
                return remain_open
        end
        native.showWebPopup(10, 10, display.contentWidth - 30, display.contentHeight -10, "https://api.twitter.com/oauth/authorize?oauth_token=" .. twitter_request_token, {urlRequest = twitterListener})
end

Massively appreciate the help.

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

the access_token is given to you in a URL that's passed back to your website url.

the access_token given to you on the twitter website is for logging in as yourself without authentication for testing purposes.

you need to have a website in order to skip the copy and pasting of the code that the user is given. for example if you were to put google.com as your website, it would pass back

www.google.com?token=whatever&token_secret=whatever

Puzzle Runner
User offline. Last seen 17 hours 59 min ago. Offline
Joined: 8 Feb 2011

Ok. I am guessing that means I would have to have a DB on that website to save the token and token secret, and then have my app reach back to that website and grab it. Given that at this point in time, that's a lot of infrastructure just to display "I just beat level 5 of this cool game!" without having the user enter a quick twitter code...

What would I have to change in my code to get the twitter code thing working?

(As mean as it may sound, I can advertise twitter functionality in my app the same way regardless of whether the user has to enter a code or not)

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

no, it doesn't require any functionality from a web server. the webPopup is forwarded to your url, with the URL variables included, that's what this is for:

1
if url:find("oauth_token") then

it searches the URL of the webPopup for the URL variables, once it gets them, they are stored and the webPopup is closed

Puzzle Runner
User offline. Last seen 17 hours 59 min ago. Offline
Joined: 8 Feb 2011

So what you're telling me is that I just need to set up my application as a website (on Twitter Dev) and have a blank webpage somewhere and to just literally put that in place of the "your website" string with nothing else needed?

Puzzle Runner
User offline. Last seen 17 hours 59 min ago. Offline
Joined: 8 Feb 2011

Ohhh I think I see what you're talking about ...you are saying to:

1. Change to a Website on Twitter Dev
2. Create a Page that just lists out the tokens in the HTML
3. The Code will just search that url's html for the tokens and grab them.
4. Pop-up closes and you can annoy the world with stupid tweets :P

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

basically, yes. the webpage i forwarded it to for my app is actually a blank webpage. just set it as "http://google.com" for now if you want, i just tested, and it works fine

trollapps
User offline. Last seen 2 years 33 weeks ago. Offline
Joined: 25 Jan 2011

no, the html page isn't what the code is searching, it's the url.

Puzzle Runner
User offline. Last seen 17 hours 59 min ago. Offline
Joined: 8 Feb 2011

lol I'm so close! I am able to approve the app now that it is a "website" without the code and the approval shows up under the list of installed apps. Better yet, the google.com thing works, and as you said the pop-up automatically closes because it found the url with the oauth token. It is also able to pull in the token and the verifier into my app.

However, I still can't tweet and here's why:

1
        local access_response = oauth.responseToTable(oauth.getAccessToken(authorize_response.oauth_token, authorize_response.oauth_verifier, twitter_request_token_secret, consumer_key, consumer_secret, "https://api.twitter.com/oauth/access_token"), {"=", "&"})

is giving me back a "Invalid / expired Token" as the result. I have confirmed that the token and verifier passed in to the method match what I get in the callback (so it's not a parsing issue). The other values passed in to the getAccessToken method also seem to be correct when I look at them.

Any ideas what may be causing this?

franciscojlm
User offline. Last seen 1 year 41 weeks ago. Offline
Joined: 22 Feb 2011

Hi all,
I'm been using the code of this post. I've got to authenticate in Twitter and publish a twet, and to authenticate in Twitpic using the same code. My problem at this time is this, how can I upload a image using the Twitpic API??? I've tried a lot of things, but I haven't got anything. My last test was:
- load a image.
- include it in a display group.
- save this display group in a file.
- then, I read it using the Lua's io and file API.
- In this point, I get authorization in Twitpic, and when I try to publish the image, twitpic says that the image is not valid. Maybe wrong format.

Others test were: to codify the read file in mime.b64 format, to use http.request after the OAUth dance,... but I always got the same result :-(

any idea??

Mike_Lorenz
User offline. Last seen 1 year 47 weeks ago. Offline
Joined: 1 Oct 2010

Did anyone ever solve any of this? None of the sample code here works.

If you have working code, I'm sure a lot of other Corona programmers would love to see it!

dgaedcke
User offline. Last seen 15 hours 7 min ago. Offline
Joined: 17 Apr 2010

+1 -- is oauth implemented yet? Anyone have a good & stable Twitter library

ckausik
User offline. Last seen 9 weeks 6 days ago. Offline
Joined: 18 Jan 2011

I have been using trollapps' code for a non-Twitter API. It seems to wok fine if they is no parameter specified with the URL (example: http://www.test.com/testpage/..). But the moment I change the URL to something like http://www.test.com/testpage?para=1&para=2.., it fails with invalid signature problem.

Anyone using the above code for any non-Twitter service?

Blue Spruce Games
User offline. Last seen 42 weeks 19 hours ago. Offline
Joined: 14 Feb 2011

I can't seem to get this code to work for me. I get the following error:

Runtime error
/Users/Peter/Desktop/oAuthTest/main.lua:23: attempt to call field 'responseToTable' (a nil value)
over this line:

1
local authorize_response = oAuth.responseToTable(url, {"=", "&"})

in this function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local function listener(event)
        
        local remain_open = true
        local url = event.url
        
        if url:find("oauth_token") then
                
                url = url:sub(url:find("?") + 1, url:len())
                
                local authorize_response = oAuth.responseToTable(url, {"=", "&"})
                remain_open = false
                
                local access_response = oAuth.responseToTable(oAuth.getAccessToken(authorize_response.oauth_token, authorize_response.oauth_verifier, twitter_request_token_secret, consumer_key, consumer_secret, "https://api.twitter.com/oauth/access_token"), {"=", "&"})
                
                access_token = access_response.oauth_token
                access_token_secret = access_response.oauth_token_secret
                user_id = access_response.user_id
                screen_name = access_response.screen_name
                -- API CALL:
                local request_response = oAuth.makeRequest("http://api.twitter.com/1/" .. user_id .. "/lists.json", "", consumer_key, access_token, consumer_secret, access_token_secret, "GET")                
        end
 
        return remain_open
end

Does anyone know how to fix this?

Gigaflop
User offline. Last seen 42 weeks 3 days ago. Offline
Joined: 11 Feb 2011

Wow. I have no idea how to use this thing.

I got as far as getting a consumer key and consumer secret from twitter and using "options.token_ready_url = "http://www.google.com"" , inputting it into the demo main.lua, and then from there it breaks.

When I build on the device I get the event.response "unauthorized."

Unfortunately, I haven't the faintest idea of where to go from here. I'm supposed to add the ability to post scores to twitter to my game... something I had expected to be simple is turning out to waste a lot of my time.

rithy_huon007
User offline. Last seen 2 weeks 2 days ago. Offline
Joined: 8 Aug 2011

Hi all,

Does anyone know how to post a photo to Twitter?
I checked Twitter api and found there is POST statuses/update_with_media which allows to post a photo to twitter.

My problem is that i don't know how to add param to the url in corona. If anyone know or successfully post a photo to Twitter, please share.

Your idea will be much more appreciated. :-)

silverthorax
User offline. Last seen 4 days 22 hours ago. Offline
Joined: 8 Jun 2011

We just released a Twitter library for Corona SDK that does all the dirty work for you.
It can upload pictures, and it's REALLY simple to use.
With our lib, a tweet in your Corona code will look like this :

1
twitterLib.tweet( "Hello world", "local/path/to/image.jpg" )

Check it out: http://silverthorax.com/libs/twitter-facade