Device Authentication in RhoConnect

The generated RhoConnect application code includes a file called “application_controller.rb” which contains an implementation for the “login” route. This route is called when a device first connects to the RhoConnect application and provides username/password credentials.

Authentication Workflow

The following diagram shows how this authenticate method is called:

Example

If your back-end service requires authentication, simply add code to the login route and return true if authentication was successful or false to deny access to the application from this device. For example:

post "/login", :rc_handler => :authenticate do
  username = params[:login]
  password = params[:password]
  success = false
  # ... connect to backend using API and authenticate ...
  result = do_custom_authentication(username,password)
  if result.code == 200
    # save the data for later use in the source adapter
    Store.put_value("username:#{username}:token",result.body)
    success = true
  end
  return success
end

Changing Login

If your actual user name is different than the username that is input into the client, you can change the username by returning a string instead of true if authentication was successful.

post "/login", :rc_handler => :authenticate do
  username = params[:login]
  password = params[:password]
  success = false
  # ... connect to backend using API and authenticate ...
  result = do_custom_authentication(username,password)
  if result.code == 200
    new_user = custom_user_mapping(username)
    # save the data for later use in the source adapter
    Store.put_value("username:#{new_user}:token",result.body)
      success = new_user
  end
  return success
end

LDAP Example

The code sample above demonstrates authentication by calling a method you would implement called ‘do_custom_authentication’ that returns an HTTP response object, however the actual implementation is left as an exercise. If your application needs to connect to LDAP, you can use the following sample code to perform simple authentication:

require 'net/ldap'

post "/login", :rc_handler => :authenticate do
  username = params[:login]
  password = params[:password]
  success = false
  ldap = Net::LDAP.new
  ldap.host = "localhost"
  ldap.port = 389
  ldap.auth "cn=#{username},dc=example,dc=com", password

  # we only need to bind to verify successful login
  success = true if ldap.bind

  return success
end

Where the domain component “dc=example,dc=com” is replaced by your domain. For example, the ldap.auth code above using “rhomobile.com” would be:

ldap.auth "cn=#{username},dc=rhomobile,dc=com", password

This example uses the net-ldap gem. To install this gem, run the following:

$ [sudo] gem install net-ldap

For more details about LDAP, please visit http://www.openldap.org/.

X509 Example

The code sample below demonstrates using an X509 certificate for authentication. See X509 certificate generation for more information about generating a cert. You can also view a sample project of the code below.

Inside of your RhoConnect app you can replace the password parameter with a pem file parameter. This parameter is expecting a string formatted for .pem with your certificate followed by your private key.

require 'socket'
require 'openssl'

post "/login", :rc_handler => :authenticate do
  username = params[:login]
  pem = params[:password]
  #assuming pem file has certificate and then private key below in pem format
  pem_arr = pem.split("-----END CERTIFICATE-----")
  pem_arr[0] << "-----END CERTIFICATE-----"

  socket = TCPSocket.new('my.secureserver.com', 4567)

  ssl_context = OpenSSL::SSL::SSLContext.new

  ssl_context.cert = OpenSSL::X509::Certificate.new(pem_arr[0].strip)
  ssl_context.key = OpenSSL::PKey::RSA.new(pem_arr[1].strip)

  ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
  ssl_socket.sync_close = true

  ca_cert = OpenSSL::X509::Certificate.new(File.open("CA.crt"))

  if ssl_socket.connect
    puts "socket connected correctly"
    return true
  else
    puts "socket failed to connect"
    return false
  end

end

A simple Sinatra app listening on port 4567 for connections might look something like this.

require 'rubygems'
require 'socket'
require 'openssl'

socket = TCPServer.new('my.secureserver.com', 4567)

ssl_context = OpenSSL::SSL::SSLContext.new()
ssl_context.cert = OpenSSL::X509::Certificate.new(File.open("server.crt"))
ssl_context.key = OpenSSL::PKey::RSA.new(File.open("server.key"))

ca_cert = OpenSSL::X509::Certificate.new(File.open("CA.crt"))

ssl_socket = OpenSSL::SSL::SSLServer.new(socket, ssl_context)


loop do
  connection = ssl_socket.accept
  Thread.new {
    begin
      #do something
    rescue
      $stderr.puts $!
    end
  }
end
Back to Top