I'm creating custom strategy for Nimble.com API. As they're using OAuth, it's pretty simple.
require 'omniauth-oauth2'
module OmniAuth
module Strategies
class Nimble < OmniAuth::Strategies::OAuth2
option :name, "nimble"
option :client_options, {
:site => "https://api.nimble.com",
:authorize_url => '/oauth/authorize',
:token_url => '/oauth/token'
}
# option :access_token_options, {
# :mode => :query,
# :param_name => :access_token
# }
option :provider_ignores_state, true
uid { raw_info['email'] }
info do
{
'uid' => raw_info['email'],
'name' => raw_info['name'],
'email' => raw_info['email']
}
end
extra do
{ 'raw_info' => raw_info }
end
def raw_info
access_token.options[:mode] = :query
access_token.options[:param_name] = :access_token
#raw_info ||= access_token.get('/api/users/myself/', {:parse => :json}).parsed
end
end
end
end
For passing tokens, they need to use access_token parameter in URL. When I specify options in raw_info function directly, as in sample — it's OK.
When I'm trying to specify this options in access_token_options hash (like in commented section) — parameters aren't passing to token. I'm not very good in Ruby, so I didn't figure out from libraries sources — how correctly pass parameters to access_token in OmniAuth OAuth2 descendants.
I'd like to make it "right way", so access_token initialised with correct options, plese someone point me the right way.
Thank you!
I've explored several existing strategies (GitHub, 4SQ), and looks like it's normal practice to directly modify access token options.
So I'll stay with it :)
Related
I am trying to implement/use Fiware Keyrock for authentization. Is there any tutorial/webinar on how to do it. Anyone does this ? It requires callbackurl and url of application of oauth, how to implement this in my rails application that it would communicate with keyrock (IDM). Any help is greatly appreciated, Thank you.
I have done this using the omniauth gem and using oauth2. There are several tutorials for using omniauth. You can first try another provider, like twitter.
I have created a "strategy" like this and placed it into /lib/omni_auth/strategies/filab_strategy.rb
require 'omniauth-oauth2'
module OmniAuth
module Strategies
class FilabStrategy < OmniAuth::Strategies::OAuth2
option :name, "filab"
option :client_options, {
:site => 'https://account.lab.fiware.org',
:authorize_url => 'https://account.lab.fiware.org/oauth2/authorize',
:token_url => 'https://account.lab.fiware.org/oauth2/token'
}
uid { raw_info['id'].to_s }
info do
{
'nickname' => raw_info['nickName'],
'displayName' => raw_info['displayName'],
'email' => raw_info['email'],
'name' => raw_info['displayName'],
}
end
extra do
{:raw_info => raw_info}
end
def raw_info
access_token.options[:mode] = :query
#raw_info ||= access_token.get('user.json', params: { access_token: access_token.token }).parsed
end
end
end
end
Then I configured and it the same way other more mainstream strategies are configured.
I've implemented a custom Puppet function that queries a Keystone server for information. The module that defines this function includes some helper methods that perform the actual work of querying keystone. Broadly, the structure looks like this:
def authenticate(auth_url, username, password)
...
end
def list_tenants(auth_url, token)
...
end
module Puppet::Parser::Functions
newfunction(:lookup_tenant, :type => :rvalue) do |args|
...
end
end
I would like to mock out the authenticate and list_tenants methods
during testing so that I can test the rest of the Puppet module in the
absence of an actual Keystone server.
I haven't previously worked with either Ruby or Rpsec before, and I'm
having a hard time finding examples of how to provide stubs for these
internal methods.
So far I have a stub rspec file that simply verified the existence of
the function:
require 'spec_helper'
describe 'lookup_tenant' do
it "should exist" do
Puppet::Parser::Functions.function("lookup_tenant").should == "function_lookup_tenant"
end
# This will fail because there is no keystone server.
it "should fail" do
should run.with_params(
'http://127.0.0.1:35357/v2.0',
'admin_user',
'admin_password',
'admin_tenant_name',
'target_tenant_name'
).and_raise_error(KeystoneError)
end
end
I would like to be able to provide custom returns from the
authenticate and list_tenants methods (or even raise exceptions
from inside these methods) so that I can test the behavior of the
lookup_tenant function in different failure scenarios.
WebMock could be used for simulating the http requests as stubs. Here is the link to the github repo: https://github.com/bblimke/webmock
For folks who haven't seen webmock before, I wanted to leave some information here about why it's particularly awesome.
So, I have in my module some code that makes an http request:
url = URI.parse("#{auth_url}/tokens")
req = Net::HTTP::Post.new url.path
req['content-type'] = 'application/json'
req.body = JSON.generate(post_args)
begin
res = Net::HTTP.start(url.host, url.port) {|http|
http.request(req)
}
if res.code != '200'
raise KeystoneError, "Failed to authenticate to Keystone server at #{auth_url} as user #{username}."
end
rescue Errno::ECONNREFUSED
raise KeystoneError, "Failed to connect to Keystone server at #{auth_url}."
end
By simply adding a require to the start of the spec file:
require `webmock`
Attempts to open a connection will result in:
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: POST http://127.0.0.1:35357/v2.0/tokens with body '{"auth":{"passwordCredentials":{"username":"admin_user","password":"admin_password"},"tenantName":"admin_tenant"}}' with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
You can stub this request with the following snippet:
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").
with(:body => "{\"auth\":{\"passwordCredentials\":{\"username\":\"admin_user\",\"password\":\"admin_password\"},\"tenantName\":\"admin_tenant\"}}",
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})
And that's just about all the information you need to stub out the
call. You can make the stubs as granular as necessary; I ended up
using something like:
good_auth_request = {
'auth' => {
'passwordCredentials' => {
'username' => 'admin_user',
'password' => 'admin_password',
},
'tenantName' => 'admin_tenant',
}
}
auth_response = {
'access' => {
'token' => {
'id' => 'TOKEN',
}
}
}
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").
with(:body => good_auth_request.to_json).
to_return(:status => 200, :body => auth_response.to_json, :headers => {})
And now I can test my module when there is no Keystone server
available.
I'm setting up an application which can make LastFM API Requests.
These are simple get requests and I'm using the HTTParty gem.
My function is as follows:
def get_albums
self.class.base_uri "http://ws.audioscrobbler.com/2.0/"
options = {
:user => "Gerard1992",
:method => "user.gettopalbums",
:api_key => Constants::LASTFM_API_KEY,
:format => "json"
}
puts options.to_query
self.class.get "/?#{options.to_query}", {} #options don't work
end
This piece of code that's shown above works. The get request returns a set of JSON. My problem is that this /?#{options.to_query} doesn't look that neat. And neither does the actual (now empty {}) options parameter. How do I get the HTTParty options parameter to work like it should?
This is what I've tried, but both cases failed:
self.class.get "/", options
self.class.get "/", options => options
I appreciate the help.
The correct option for query parameters in HTTParty is :query, so what you want is:
self.class.get "/", query: options
You can see all the available parameters in the docs.
Send :verify => false in options hash
I am working on creating an API for my ruby application that authenticates users based on HTTP Digest Authentication. I decided to use the Grape API library because it makes creating an API cleaner in ruby. The Grape documentation states that you can use Digest Authentication like:
http_digest({ :realm => 'Test Api', :opaque => 'app secret' }) do |username|
# lookup the user's password here
{ 'user1' => 'password1' }[username]
end
The Grape implementation above is a wrapper for Rack::Auth::Digest::MD5
Now also for security i read that as of RFC 2617 you don't need to store the password as plain text in the database you store an MD5 digest of the username:realm:password and authticate against that so i created a DataMapper model:
class Key
include DataMapper::Resource
property :id, Serial
property :username, String
property :password, String
property :active, Boolean, :default => true
property :created_at, DateTime, :default => DateTime.now
property :updated_at, DateTime
end
Now with what I provided, I am lost as to how to connect these two and make it work.
Unfortunately, Rack::Auth::Digest::MD5 expects a plaintext password on the server side.
The Grape example code shows a hard-coded lookup of password.
You could replace { 'user1' => 'password1' }[username] with
Key.first( :username => username ).password
provided you stored plaintext passwords in the Key class. You could store these reversibly-encrypted I suppose, although that doesn't add much security unless you construct relatively complex/costly schemes for key management.
Not sure if there is a way around this that would let you store hashed passwords. MD5 isn't the most secure hashing choice (although better than nothing!). If security is an important concern for your API, you will want to look beyond digest auth - using https would help, for example.
Edit: Following a bit of to-and-fro in discussions, the following variation of Grape's example does allow you to store the MD5'd password:
auth :http_digest, { :realm => { :realm => 'Llama', :passwords_hashed => true, :opaque => "7302c32d39bbacb5ed0ace096723fd" } } do |username|
Digest::MD5.hexdigest( 'fred:Llama:654321' )
end
The example gives a hard-coded username:'fred', password:'654321' response. So I think your target code is something like:
auth :http_digest, { :realm => { :realm => 'Llama', :passwords_hashed => true, :opaque => "7302c32d39bbacb5ed0ace096723fd" } } do |username|
k = Key.first( :username => username )
k ? k.password : nil
end
And you store the result of Digest::MD5.hexdigest( "#{username}:#{realm}:#{password}" ) in each user's password property.
Note the double-level hash with :realm twice. This is a bit hacky, but at least you don't have to write your own middleware, Grape is still dealing with it. This is not a documented feature of Grape or covered with tests, so may not work in future versions.
I am a beginner with Ruby and Rails but I managed to use OmniAuth for authentication via Facebook. Everything works fine, I am able to create users and they are able to login with Facebook.
The problem is, I would like to take some of the user data (such as email, profile photo, etc.) and save it.
Going through the README (https://github.com/mkdynamic/omniauth-facebook), I managed to find:
Here's an example Authentication Hash available in request.env['omniauth.auth']:
{
:provider => 'facebook',
:uid => '1234567',
:info => {
:nickname => 'jbloggs',
:email => 'joe#bloggs.com',
:name => 'Joe Bloggs',
:first_name => 'Joe',
:last_name => 'Bloggs',
:image => 'http://graph.facebook.com/1234567/picture?type=square',
:urls => { :Facebook => 'http://www.facebook.com/jbloggs' },
:location => 'Palo Alto, California',
:verified => true
}
}
I tried to do more searching on the Authentication Hash and got this which lists some of the information that can be fetched: https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema
The thing is, I asked for certain permissions. The question is, how do I know what sort of information Facebook is sending? Unfortunately, saying the info is in request.env['omniauth.auth'] does me not much good. How do I fetch the information from here?
I am a real beginner going through the Rails tutorial (http://ruby.railstutorial.org/) but trying to create my own app by trial and error.
request.env['omniauth.auth'] will give you a hash. Check the Ruby docs for what you can do with a hash.
http://ruby-doc.org/core-1.9.3/Hash.html
For any elements that are not always going to be there you can just check for blank? (a Rails convenience method), e.g.
omniauth = request.env['omniauth.auth']
unless omniauth['info']['email'].blank?
send_spam omniauth['info']['email'] # ;)
end
You would probably find the following screencast useful if you haven't seen it already:
http://railscasts.com/episodes/235-omniauth-part-1