Migrating U2F to WebAuthn gem in Ruby, where to get the parameters for AuthenticatorAttestationResponse - ruby-on-rails

I have a couple of questions about the WebAuthn gem and the use of U2fMigrator.
I hope someone can point me in the right direction about it.
I am in the step just after converting my old U2F credentials using U2fMigrator.
migrated_credential = WebAuthn::U2fMigrator.new(
app_id: my_domain,
certificate: u2f_registration.certificate,
key_handle: u2f_registration.key_handle,
public_key: u2f_registration.binary_public_key,
counter: u2f_registration.counter
)
The documentation says: “U2fMigrator class quacks like WebAuthn::AuthenticatorAttestationResponse” but without verify implementation.
Does that mean I need to create an instance of this AuthenticatorAttestationResponse for authentication?
If so. Where I should get this data from?
assertion_response = WebAuthn::AuthenticatorAssertionResponse.new(
credential_id: '',
authenticator_data: '',
client_data_json: '',
signature: '',
)
I am guessing that will allow me to authenticate the new migrated credentials like this:
assertion_response.verify(
WebAuthn::Credential.options_for_get(:extensions => { appid: my_domain }).challenge,
allowed_creadentials: migrated_credential.credential,
rp_id: my_domain
)
And also, I am guessing I don't need to re-register these credentials yet.
I am following this documentation:
https://github.com/cedarcode/webauthn-ruby/blob/master/docs/u2f_migration.md
https://github.com/castle/ruby-u2f
https://github.com/cedarcode/webauthn-ruby/blob/master/README.md#authentication
UPDATE 1
I've found this cool explanation in this guide
I will dig into it and I'll post the solution if I can find it.
UPDATE 2
I've spent the whole week trying to get the authenticatorAssertionResponse
from
Unfortunately, I only get a message saying I don't have a key registered:
I'm passing through the extension and appid where the U2F credential was registered originally. I wonder if it stoped working now the deprecation is complete.

U2fMigrator is instantiated with data that's already stored in your database. Instances of it respond to the same methods as AuthenticatorAttestationResponse, except it misses a verify method since the data was already verified in the past. In other words: the migrator behaves nearly the same as a freshly WebAuthn registered authenticator and it is meant to be used as such.
Does that mean I need to create an instance of this
AuthenticatorAttestationResponse for authentication?
Yes. The AuthenticatorAttestationResponse is instantiated with browser data from the WebAuthn navigator.credentials.get call. This in itself is unrelated to the U2F migration question, except for the part where the data comes from for its verify method. This comes either from a migrator instance (in the "real time conversion" approach) or is retrieved from the database.
Hope that makes sense, PRs welcome to improve the docs!

Related

graphql_devise for authentication in Rails/Graphql/Apollo/React. "field requires authentication" error

I have a project where I am setting up a Rails API with Graphql and React/Apollo. I've purpled all the google links looking for the best authentication solution, but it seems I haven't been able to find a clear answer.
I settled on using the graphql_devise gem, which leverages devise & devise_token_auth. I would have preferred to find a JWT solution, but just couldn't. (If you have any obvious suggestions please tell me!)
First I mounted a separate auth route, but ran into problems with multiple endpoints when I was setting up ApolloClient. I couldn't figure out how to direct auth related requests to my auth endpoint, while letting the rest go through to my graphql one. (If figuring this out is the easiest solution, please tell me!) Instead, I mounted the auth routes in my own schema, as instructed by the docs:
class MyApiSchema < GraphQL::Schema
use GraphqlDevise::SchemaPlugin.new(
query: Types::QueryType,
mutation: Types::MutationType,
resource_loaders: [
GraphqlDevise::ResourceLoader.new('User', only: [:login, :logout])
]
)
mutation(Types::MutationType)
query(Types::QueryType)
And edited the execute line in graphql_controller.rb:
result = MyApiSchema.execute(query, variables: variables, context: graphql_context(:user), operation_name: operation_name)
At this point, running a test query in postman is successful. I can access the graphql route with a userLogin mutation without any headers set, and get a successful response with client, uid & token. Authentication of my other queries also works—success with headers, denied without.
But when I try to perform the same queries using useQuery in react, it doesn't work. In the Apollo Client Developer Tools plugin in Chrome, it doesn't work either, returning only [Object object]. By looking at the request in my Network tab, I can see that this is the result of the same error: "photo field requires authentication".
When I pry into the rails server, I can see that the headers are being received. I can even authenticate the user manually in my graphql_controller before the execute method is called, so I don't think that it is a CORS issue. I have set up the rack-cors gem in Rails to expose the required headers.
When I drill into the code, it seems that the graphql_devise method set_current_resource is failing to return my user. This seems to stem from the devise method set_user_by_token, but I have not been able to figure out why it is failing.
If anyone has any experience with implementing this gem & stack, I would very much appreciate your input! If you have a better way of approaching authentication, I'd love to know what your strategy is. If you can help me solve this ... field requires authentication error, I will love you forever.
Apologies if I've provided too much/too little info, or if my question is too vague. Please let me know if there's something specific I should know to ask/show in my code. Hope you can help! Thanks.
I've managed to find the issue, and I thought I'd explain.
Having traced the problem to the set_user_by_token method in the devise_token_auth gem, I bundle open devise_token_auth'd, and put a byebug inside. This revealed that #token.token was not being extracted correctly from the headers.
The problem was that my header access-token was somehow being converted to accessToken, so when devise tried to set the token info from the request headers using this key, it returned nil.
I do not know why or where this conversion takes place. I suspect it originates from Apollo/React rather than Rails, since the headers on my postman query were not altered. In react, when I set the headers they are set with access-token, as below, but it seems that at some point in the life of my request they are changed.
How I have set the headers in React:
const client = new ApolloClient({
cache,
link: new HttpLink({
uri: 'http://localhost:3000/graphql',
headers: {
accessToken: localStorage.getItem('access-token'),
client: localStorage.getItem('client'),
uid: localStorage.getItem('uid')
},
}),
});
The keys that devise_token_auth uses to select the headers from the request can be changed in the initializers/devise_token_auth.rb file.
I edited them as follows:
config.headers_names = {:'access-token' => 'accessToken',
:'client' => 'client',
:'expiry' => 'expiry',
:'uid' => 'uid',
:'token-type' => 'token-type' }
This means that my front-end is now working smoothly, and I can perform queries/mutations with authentication. However, now in my postman queries I have to change the header to accessToken if I want them to work.
It would be great to know exactly at what point my header from React is changed to camelCase, but for now, it's working. Hope this helps if anyone goes through the same frustrations!

Convert Salesforce Lead with Ruby

I am completely new to salesforce and its API(s). I have rails application with an existing users table. I need to start synchronizing data over to our new salesforce account.
Currently, I am trying to convert existing 'Leads' to 'Accounts'. As I understand it, this can only be accomplished with the old SOAP API. I am trying to use the rforce gem.
I am able to authenticate according to the documentation, like this:
binding = RForce::Binding.new \
'https://www.salesforce.com/services/Soap/u/20.0'
binding.login \
'email', 'password' + 'token'
Then I try to call the convert lead action like this:
binding.convertLead "leadId" => lead_id
but I get the error:
:errors=>{:message=>"valid leadId is required", :statusCode=>"INVALID_CROSS_REFERENCE_KEY"}
I know for sure that the leadId I am using is indeed valid, because I have actually retrieved it just prior to this call (using the REST API).
I believe that the problem may be that the rforce gem is not structuring the underlying SOAP call properly -- which I think is being described in this SO post.
I appreciate any additional information anyone can provide.
Thanks very much,
Matt
Try using lead.Id instead of lead_Id.
The input hash needs to have the key 'leadConverts':
binding.convertLead 'leadConverts' => { 'leadId' => lead_id, 'convertedStatus' => 'Status' }
You may need to add other arguments depending on what is required to convert a Lead on your Salesforce platform.

sfGuardAuth across multiple apps

I've got 3 apps: Backend, Frontend and Members.
And I've got 2 credentials: Administrators and Members.
Administrators are imported by default but Members are created in the Backend.
When a Member is created, an event handler also inserts this Member as a sf_guard_user and of course, the proper relations in sf_guard_user_group and sf_guard_user_permission.
That's the boring part, now the fun:
Frontend is not secured, but Members is and using these credentials: [administrator, member].
According to all this, Members created in the Backend that also get inserted (correctly as far as I can tell) should be able to login to the Members secured app, since they get the member group/permission/credential.
This is not happenning, the only ones that can login to the Members app are the administrators, which is not wrong, but either is the fact that correctly created Member users can't login to it.
The error thrown by the guard is the classic: The username and/or password is invalid.
Now that I edit the error, the salt comes to mind: How would one emulate the inserting of the salt as the guard does it? Maybe that's what I'm not inserting correctly (or inserting at all) and thus the password invalid error (and in fact everythine else I've described is ok! omg)
And that's my problem.
Thanks in advance :)
[administrator, member] means both are required, I believe.
I think you want [[administrator, member]] for the credential requirement.
Also, yes, you will want to make sure you use a salt, and set the algo.
parent::_set('password', call_user_func_array($algorithm, array($salt.$password)));
Salt before password, as well.

How to deal with authentication for a Ruby API wrapper?

I'm working on an API wrapper for Viddler, which will eventually be made public, and I'm trying to figure out the best way to deal with authentication/API keys, specifically with usage within Rails applications in mind.
The easiest way to write the wrapper would be to just have the code create a new client each time, and the developer could store the API key in a constant for future use:
#client = Viddler::Client.new(VIDDLER_API_KEY)
The problem with this is, it's kind of clunky to have to keep creating client objects and passing in the API key. This gets even more complicated when you throw user authentication into the mix.
I'm thinking some sort of solution where I all the the API key to be set in the environment file and then the authentication would be done in a before_filter.
Viddler::Client.api_key = 'abc123'
Viddler::Client.authenticate! 'username', 'password'
Viddler::Client would then store this in a class variable, and you could call Viddler::Client.new without any parameters and make authenticated calls. One thing I'd be concerned about is that this means the developer would have to be sure to clear out the authentication before or after each request, since the class variables would persist between requests.
Any thoughts?
Storing the API key globally would for sure be pretty useful and certainly is the way to go for that kind of information. User authentication on the other hand I think shouldn't be stored globally, never ever, especially for a high level API, because telling your users to "ensure to add an after_filter :reset_viddler_auth" might lead to some unexpected security risks.
# in a config/initializer/*.rb file or something
Viddler::Client.api_key = "abc123"
# in the controller/action/model/wherever
#client = Viddler::Client.new # anonymous
#client.authenticate!("username", "password") # authenticate anon client
#client_auth = Viddler::Client.new("username", "password") # authenticated client
Guess like that you've best of both worlds :) Maybe even provide a way to create a new client with another API key like,
#client_other = Viddler::Client.new("username", "password", :api_key => "xyz890")
So... just my 2 cents.
PS: not sure how up-to-date it is, but there's already a ruby viddler wrapper, just FYI, http://viddler.rubyforge.org/rdoc/

LDAP through Ruby or Rails

I've been attempting to hook a Rails application up to ActiveDirectory. I'll be synchronizing data about users between AD and a database, currently MySQL (but may turn into SQL Server or PostgreSQL).
I've checked out activedirectory-ruby, and it looks really buggy (for a 1.0 release!?). It wraps Net::LDAP, so I tried using that instead, but it's really close to the actual syntax of LDAP, and I enjoyed the abstraction of ActiveDirectory-Ruby because of its ActiveRecord-like syntax.
Is there an elegant ORM-type tool for a directory server? Better yet, if there were some kind of scaffolding tool for LDAP (CRUD for users, groups, organizational units, and so on). Then I could quickly integrate that with my existing authentication code though Authlogic, and keep all of the data synchronized.
Here is sample code I use with the net-ldap gem to verify user logins from the ActiveDirectory server at my work:
require 'net/ldap' # gem install net-ldap
def name_for_login( email, password )
email = email[/\A\w+/].downcase # Throw out the domain, if it was there
email << "#mycompany.com" # I only check people in my company
ldap = Net::LDAP.new(
host: 'ldap.mycompany.com', # Thankfully this is a standard name
auth: { method: :simple, email: email, password:password }
)
if ldap.bind
# Yay, the login credentials were valid!
# Get the user's full name and return it
ldap.search(
base: "OU=Users,OU=Accounts,DC=mycompany,DC=com",
filter: Net::LDAP::Filter.eq( "mail", email ),
attributes: %w[ displayName ],
return_result:true
).first.displayName.first
end
end
The first.displayName.first code at the end looks a little goofy, and so might benefit from some explanation:
Net::LDAP#search always returns an array of results, even if you end up matching only one entry. The first call to first finds the first (and presumably only) entry that matched the email address.
The Net::LDAP::Entry returned by the search conveniently lets you access attributes via method name, so some_entry.displayName is the same as some_entry['displayName'].
Every attribute in a Net::LDAP::Entry is always an array of values, even when only one value is present. Although it might be silly to have a user with multiple "displayName" values, LDAP's generic nature means that it's possible. The final first invocation turns the array-of-one-string into just the string for the user's full name.
Have you tried looking at these:
http://saush.wordpress.com/2006/07/18/rubyrails-user-authentication-with-microsoft-active-directory/
http://xaop.com/blog/2008/06/17/simple-windows-active-directory-ldap-authentication-with-rails/
This is more anecdotal than a real answer...
I had a similar experience using Samba and OpenLDAP server. I couldn't find a library to really do what I wanted so I rolled my own helper classes.
I used ldapbrowser to see what fields Samba filled in when I created a user the "official" way and and basically duplicated that.
The only tricky/non-standard LDAP thing was the crazy password encryption we have:
userPass:
"{MD5}" + Base64.encode64(Digest::MD5.digest(pass))
sambaNTPassword:
OpenSSL::Digest::MD4.hexdigest(Iconv.iconv("UCS-2", "UTF-8", pass).join).upcase
For the def authenticate(user, pass) function I try to get LDAP to bind to the domain using their credentials, if I catch an exception then the login failed, otherwise let them in.
Sorry, cannot comment yet... perhaps someone can relocate this appropriately.
#Phrogz's solution works well, but bind_simple (inside bind) raises an Net::LDAP::LdapError exception due to auth[:username] not being set as shown here:
https://github.com/ruby-ldap/ruby-net-ldap/blob/master/lib/net/ldap.rb
The corrected replaces:
auth: { method: :simple, email: email, password:password }
with:
auth: { method: :simple, username: email, password:password }
I began using ruby-activedirectory, and even extended it/fixed a few things, hosting judy-activedirectory in Github.
Doing the next iteration, I've discovered ActiveLdap has a much better code base, and I'm seriously contemplating switching to it. Does anyone have personal experience with this?
Have you checked out thoughtbot's ldap-activerecord-gateway? It might be something for you to consider...
http://github.com/thoughtbot/ldap-activerecord-gateway/tree/master

Resources