RMRK is retiring.
Registration is disabled. The site will remain online, but eventually become a read-only archive. More information.

RMRK.net has nothing to do with Blockchains, Cryptocurrency or NFTs. We have been around since the early 2000s, but there is a new group using the RMRK name that deals with those things. We have nothing to do with them.
NFTs are a scam, and if somebody is trying to persuade you to buy or invest in crypto/blockchain/NFT content, please turn them down and save your money. See this video for more information.
OAuth Module

0 Members and 1 Guest are viewing this topic.

**
Rep:
Level 33
Waifuist Anon
This module allows you to send OAuth-signed HTTP requests to any API you like. With this module, your games will be able to do stuff like signing in to twitter as apps and post status updates or retrieve the user's timeline.

I still haven't fully tested it with post/get params and everything, so feel free to post any issue in this thread.
EDIT: just did some more in-depth testing and managed to retrieve my twitter timeline. still haven't tested it with POST though
EDIT: tested GET and POST params and they seem to be working after this last bugfix :D

Why would you need something like that on RPGMaker?
I'm a programming enthusiast, and I usually have no use for pre-made engines like RPGMaker because for me most of the fun is making the engine from scratch in low-ish level languages such as C and C++.
About two days ago I had a great idea which proved to be a ton of fun: messing around with RPGMaker's scripting system to see if I could make it do things it wasn't exactly designed for in pure RGSS3 (without using custom dlls) and made a script that successfully gets an OAuth token from twitter.
This is the OAuth module I wrote for this purpose.

Author: Franc[e]sco aka lolisamurai
Required scripts: TextUtils, Base64, Digest
Download: http://hnng.moe/f/iC
Source code (also on pastie):
Spoiler for:
Code: [Select]
=begin
  OAuth r2
  by Franc[e]sco
 
  Requires: TextUtils, Base64, Digest
 
  01/09/2015
  r0
    [+] Initial revision
   
  r1
    [*] Fixed the call to build_signed_parameters which was missing one argument
        which caused the parameters to incorrectly build when using a token or
        a pin
       
  01/10/2015
  r2
    [*] Fixed WinHttpSendRequest not taking a pointer as the post param data
        and causing errors when using post params.
    [*] Modified some logic that flawed due to the wrong assumption that
        WinHttpCrackUrl behaved the same when not dynamically allocating
        the results.
    [+] Added debug toggle and some debugging printfs.
=end

DEBUG = false

# WinAPI
GetLastError = Win32API.new("Kernel32", "GetLastError", "", "I")
WinHttpOpen = Win32API.new("winhttp", "WinHttpOpen", "PIPPI", "I")
WinHttpConnect = Win32API.new("winhttp", "WinHttpConnect", "PPII", "I")

WinHttpAddRequestHeaders = Win32API.new("winhttp",
  "WinHttpAddRequestHeaders", "PPII", "I")

WinHttpOpenRequest = Win32API.new("winhttp",
  "WinHttpOpenRequest", "PPPPPPI", "I")

WinHttpSendRequest = Win32API.new("winhttp",
  "WinHttpSendRequest", "PIIPIII", "I")

WinHttpReceiveResponse = Win32API.new("winhttp",
  "WinHttpReceiveResponse", "PP", "I")

WinHttpQueryDataAvailable = Win32API.new("winhttp",
  "WinHttpQueryDataAvailable", "PP", "I")

WinHttpReadData = Win32API.new("winhttp", "WinHttpReadData", "PPIP", "I")
WinHttpCloseHandle = Win32API.new("winhttp", "WinHttpCloseHandle", "P", "I")

WINHTTP_FLAG_SECURE = 0x00800000

ALPHANUMERIC = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

# module OAuth contains utilities to send signed HTTP requests with OAuth
module OAuth
 
  # OAuth::request sends a OAuth-signed web request
  #
  # Parameters:
  # url: full url
  # method: GET/POST
  # post_params: Hash that contains post parameters mapped by name
  #   (e.g. Hash["my_value" => 10, "other_value" => "hello"])
  # oauth_consumer_key, oauth_consumer_secret: these must always be provided
  #   and they identify your application. the service on which you're trying
  #   to authenticate should have provided you with these when registering
  #   your application.
  # oauth_token, oauth_token_secret (optional): must be provided on every call
  #   except for the very first request where you actually get these tokens by
  #   requesting them.
  # pin (optional): used during authorization. after obtaining oauth_token and
  #   oauth_token_secret, the user will be prompted to authorize your
  #   application and will be given a pin to enter into your software.
  #
  # Returns: the contents of the response as plain text
  def self.request(url, method, post_params, oauth_consumer_key,
    oauth_consumer_secret, oauth_token="", oauth_token_secret="", pin="")
   
    gle = 0
    signed_params = ""
   
    loop do
      query, gle = TextUtils::url_get_query(url)
      break unless gle == 0
     
      get_params = TextUtils::parse_query(query)
      signed_params, gle = build_signed_parameters(get_params, url, method,
        post_params, oauth_consumer_key, oauth_consumer_secret, oauth_token,
        oauth_token_secret, pin)
      break
    end
   
    unless gle == 0
      return sprintf("%s, GLE=%.8X (%d)", signed_params, gle, gle)
    end
   
    return signed_request(signed_params, url, method, post_params)
  end
 
  # ----------------------------------------------------------------------------
  # [ Internal module functions ]
  # you most likely don't need to use the stuff below here or understand it
  # unless you know what you're doing
  # ----------------------------------------------------------------------------
 
  # generates the OAuth signature from the signature base and the consumer
  # secret. a request token secret can optionally be provided
  def self.create_signature(signature_base,
    consumer_secret, request_token_secret = "")
   
    escaped_consumer_secret = TextUtils::url_encode(consumer_secret)
    escaped_token_secret = TextUtils::url_encode(request_token_secret)
   
    key = escaped_consumer_secret + "&" + escaped_token_secret
    hash = ""
    loop do 
      hash, gle = Digest::hmacsha1(key, signature_base)
      if gle != 0
        return [hash, gle]
      end
      # sometimes hash will be an empty string for no apparent reason
      # but retrying eventually fixes it
      # this is probabilly because of my super ghetto hashing code
      break unless hash.empty?
    end
     
    signature = Base64.encode(hash)[0..-2]
    signature = TextUtils::url_encode(signature)
    return [signature, 0]
  end
  private_class_method :create_signature
 
  # returns the normalized sorted oauth parameters as a string
  def self.normalize_parameters(params)
    res = ""
    first = true
   
    params.sort.map do |key, value|
      res += "&" unless first
      res += sprintf("%s=%s", key, value)
      first = false
    end
    return res
  end
  private_class_method :normalize_parameters
 
  # normalizes the given url
  def self.normalize_url(url)
    normalized = ""
    gle = 0
   
    loop do
      host, path, extrainfo, port, gle = TextUtils::url_crack(url)
      unless gle == 0
        break
      end
   
      # specify non-standard port
      port_str = ""
      if (url.start_with?("http://") and port != 80) or
         (url.start_with?("https://") and port != 443)
         
        port_str = sprintf(":%u", port)
      end
     
      # TODO: edit url_crack to also return the scheme
      # instead of parsing it like this
      scheme = "invalid://"
      s = url.index("://")
      scheme = url[0, s] unless s == nil
     
      normalized = scheme + "://" + host + port_str + path
      break
    end
 
    return normalized
  end
  private_class_method :normalize_url
 
  # creates a oauth-signed parameter list for the desired request
  def self.build_signed_parameters(get_params, url, method, post_params,
    oauth_consumer_key, oauth_consumer_secret, oauth_token="",
    oauth_token_secret="", pin="")
 
    # prepare oauth params   
    oauth_params = Hash[
      "oauth_consumer_key" => oauth_consumer_key,
      "oauth_nonce" => TextUtils::random_string(ALPHANUMERIC, 32),
      "oauth_signature_method" => "HMAC-SHA1",
      "oauth_timestamp" => Time.now.to_i,
      "oauth_version" => "1.0"
    ]
   
    # optional params
    oauth_params["oauth_token"] = oauth_token unless oauth_token.empty?
    oauth_params["oauth_verifier"] = pin unless pin.empty?
   
    # create a full param list with the GET/POST stuff and the oauth params
    all_params = get_params
    all_params.merge!(post_params) if method == "POST" and post_params.length > 0
    all_params.merge!(oauth_params)
   
    # prepare signature base
    normalized_url = normalize_url(url)
    normalized_params = normalize_parameters(all_params)
    signature_base = sprintf("GET&%s&%s",
      TextUtils::url_encode(normalized_url),
      TextUtils::url_encode(normalized_params))
    printf("signature_base = %s\n", signature_base) if DEBUG
   
    oauth_signature, gle = create_signature(signature_base,
      oauth_consumer_secret, oauth_token_secret)
    if gle != 0
      return [oauth_signature, gle]
    end
   
    oauth_params["oauth_signature"] = oauth_signature
    return [oauth_params, 0]
  end
  private_class_method :build_signed_parameters
   
  # build oauth header from the signed oauth params
  def self.build_header(params)
    oauth_header = "Authorization: OAuth "
    firstparam = true
    params.sort.map do |key, value|
      oauth_header += ", " unless firstparam
      oauth_header += sprintf('%s="%s"', key, value)
      firstparam = false
    end
    return oauth_header
  end
  private_class_method :build_header
 
  # sends a signed http request with the given signed params
  def self.signed_request(signed_params, url, method, post_params)
    session = 0
    connect = 0
    request = 0
    html = ""
    host, path, extrainfo, port, gle = TextUtils::url_crack(url)
   
    loop do
      session = WinHttpOpen.call("RPG Maker VX Ace/1.0", 0, '', '', 0)
      unless session
        html = sprintf("Failed to open session, GLE=0x%.8X", GetLastError.call)
        break
      end
     
      connect = WinHttpConnect.call(session, TextUtils::to_ws(host), port, 0)
      unless connect
        html = sprintf("Failed to connect to %s:%d, GLE=0x%.8X",
          host, port, GetLastError.call)
        break
      end
     
      printf("%s %s\n", method, path + extrainfo) if DEBUG
      request = WinHttpOpenRequest.call(connect, TextUtils::to_ws(method),
        TextUtils::to_ws(path + extrainfo), TextUtils::to_ws("HTTP/1.1"),
        "", nil, WINHTTP_FLAG_SECURE)
      unless request
        html = sprintf("Failed to open request %s %s, GLE=0x%.8X",
          method, path, GetLastError.call)
        break
      end
     
      # add oauth header
      oauth_header = build_header(signed_params)
      printf("oauth_header = %s\n", oauth_header) if DEBUG
      headerok = WinHttpAddRequestHeaders.call(request,
        TextUtils::to_ws(oauth_header), oauth_header.length, 0)
      unless headerok
        html = sprintf("Failed to add oauth header \"%s\", GLE=0x%.8X",
          oauth_header, GetLastError.call)
        break
      end
     
      # add post headers if needed
      postdata = ""
      if method == "POST" and post_params.length > 0
        content_header = "Content-Type: application/x-www-form-urlencoded\r\n"
        content_header_ok = WinHttpAddRequestHeaders.call(request,
          TextUtils::to_ws(content_header), content_header.length, 0)
        unless headerok
          html = sprintf("Failed to add content header \"%s\", GLE=0x%.8X",
            content_header, GetLastError.call)
          break
        end
        postdata = TextUtils::build_query(post_params)
      end
     
      # send request
      results = WinHttpSendRequest.call(request, 0, 0,
        postdata.length > 0 ? postdata : "", postdata.length,
        postdata.length, 0)
      unless results
        html = sprintf(
          "Failed to send request with postdata \"%s\", GLE=0x%.8X",
          postdata, GetLastError.call)
        break
      end
     
      # get and read response
      results = WinHttpReceiveResponse.call(request, nil)
      unless results
        html = sprintf("Failed to get response, GLE=0x%.X", GetLastError.call)
        break
      end
     
      loop do
        size = 0
       
        # check how much data is available and dynamically allocate a buffer
        unless WinHttpQueryDataAvailable.call(request, o=[size].pack('i!'))
          html = sprintf("Failed to query for available data, GLE=0x%.X",
            GetLastError.call)
          break
        end
       
        size = o.unpack('i!')[0]
        buffer = ' ' * size
        downloaded = 0
       
        # read data into the buffer
        unless WinHttpReadData.call(request, buffer,
          size, o=[downloaded].pack('i!'))
          html = sprintf(
            "Failed to read %d bytes of available data, GLE=0x%.8X",
            size, GetLastError.call)
          break
        end
       
        downloaded = o.unpack('i!')[0]
       
        # append data to html
        html << buffer
       
        # keep reading data as long as there is some
        break unless size > 0
      end
     
      # clean up
      WinHttpCloseHandle.call(request) if request
      WinHttpCloseHandle.call(connect) if connect
      WinHttpCloseHandle.call(session) if session
      break
    end
   
    return html
  end
  private_class_method :signed_request
 
end

Video demonstration of my module obtaining an OAuth token from twitter: http://hnng.moe/f/ho

Example code - obtaining an OAuth token from twitter:
Code: auto:0 [Select]
    html = OAuth::request(
      "https://api.twitter.com/oauth/request_token",
      "GET", Hash[], "my_consumer_key",
      "my_consumer_secret"
    )
   
    oauth_token = ""
    oauth_token_secret = ""
   
    html.split(/&/).each do |param|
      name, value = param.split(/=/)
     
      if name == "oauth_token"
        oauth_token = value
      elsif name == "oauth_token_secret"
        oauth_token_secret = value
      end
    end
   
    authurl = "api.twitter.com/oauth/authorize?oauth_token="
    authurl += oauth_token
    Thread.new { system("start https://#{authurl}") }

The consumer key and consumer secret are provided by twitter when you create your custom app.
« Last Edit: January 10, 2015, 02:38:31 AM by Franc[e]sco »

**
Rep:
Level 33
Waifuist Anon
Updated!
Code: [Select]
  r1
    [*] Fixed the call to build_signed_parameters which was missing one argument
        which caused the parameters to incorrectly build when using a token or
        a pin

**
Rep:
Level 33
Waifuist Anon
Updated yet again, GET params are now working great! POST should also work fine.
Code: [Select]
  r2
    [*] Fixed WinHttpSendRequest not taking a pointer as the post param data
        and causing errors when using post params.
    [*] Modified some logic that flawed due to the wrong assumption that
        WinHttpCrackUrl behaved the same when not dynamically allocating
        the results.
    [+] Added debug toggle and some debugging printfs.