Generating Unique Token on the Fly with Rails

Generating unique token on the fly with Rails

The cleanest solution I found:

@seller.user_info_token = loop do
token = SecureRandom.urlsafe_base64
break token unless User.exists?(user_info_token: token)
end

And something very clean but with potential duplicates (very few though):

@seller.user_info_token = SecureRandom.uuid

Random UUID probability of duplicates

Edit: of course, add a unique index to your :user_info_token. It will be much quicker to search for a user with the same token and it will raise an exception if by chance, 2 users are saved at the exact same moment with the exact same token!

Generating a unique, but apparently random, identifier without loop+retry

Run a cronjob every few hours which repopulates a Redis collection with unused identifiers. Let it run in the background so when you need one, just pop it off the Redis collection.

Generating a unique URL with tokens in Rails 4 for an external form response

Add a token field to the feedback model with an index and add a callback to populate it on create e.g.
feedback.rb

before_create :add_token
private
def add_token
begin
self.token = SecureRandom.hex[0,10].upcase
end while self.class.exists?(token: token)
end

now add a new route for the providers feedback

resources :feedbacks do 
get 'provider'
put 'provider_update' # you might not need this one, if you are happy to use update
end

In your controller make sure they don't get rejected by devise

before_filter :authenticate_user!, except: [:provider, :provider_update]
...
def provider
@feedback = Feedback.find_by token: params[:token]
end

then in the app/views/feedback/provider.html.haml you can use url in simple_form to send it to the correct update location and only provide the input that they should see.

f.inputs :p_comment

Now update your mailer.

@url = provider_feedback_url(@feedback, token: @feedback.token)

You could do something similar to this using friendly id but you would still need to create some sort of unique slug and then use Feedback.friendly.find instead. I think you would want to combine it with a token to ensure it's still the provider giving the feedback - so the only benefit would really be hiding the true id/count. I think you should update p_* fields to provider_* so that the next dev knows what's in it - it's not the 90s!

Does SecureRandom generates a unique string or do I have to check?

Nothing is completely random in a computer generated string. Technically, there is a very low, remote possibility that two strings may be the same.

This is even more true in a highly concurrent system. If that's not the case, what you can do is to simply add an unique constraint (aka an unique index) at the database level. In this way, in the remote event that two strings will be equal, the database will reject the second one.

It's likely you will have an index on that field in any case if you need to use it for a query, therefore it should not add any unreasonable overhead.

As a side note, SecureRandom is a Ruby library, not a Rails library.

How to generate a secure random token for remember me functionality?

TL; DR

Use a well-maintained gem for generating login tokens when you can rather than rolling your own. However, understanding how to evaluate the relative strength of such tokens depends on the size of the numerical range and the entropy inherent in the generation of the tokens.

Understanding Space and Entropy

"A suffiently large space" is using the term in the mathematical or cryptographic sense. If a chosen random number can only vary between whole numbers from 1..10, you have a very small space. If your number can vary over 128 bits or more, then you have a much larger space from which to choose. This avoids the likelihood of collisions. Mathematically speaking, the amount of entropy and the seed value used to generate a pseudo-random value will also have a significant impact on the overall security and collision-resistance of the generated number.

What constitutes a sufficiently large space depends on your problem domain. In many cases, UUIDv4 as generated by Ruby's SecureRandom#uuid method is sufficiently random to be considered a universally unique identifier that is sufficiently random to avoid collisions. Because it is (pragmatically speaking) "universally unique," the utility value of salting it or hashing it with other information is probably unnecessary. However, it is still important to associate the UUID with a user ID or other unique attribute of a user so that the value can be used in a cookie, form data, or query parameter to associate it with an existing login, or with whatever other data it is that you're trying to persist.

Rather than doing this yourself, it is generally better to use a well-designed and well-maintained authentication mechanism like Devise to manage your Rails logins. The same is true for authorization, where other gems like CanCan may be useful. In either case, under the hood avoidance of collisions in authentication tokens are being handled for you.

If you are rolling your own, then understanding statistics, entropy, and the risks of deliberate or accidental collisions are extremely important. While a short answer here simply cannot do justice to the complexity of the underlying question, it should give you enough to get started and help you select the amount of randomness or uniqueness your current problem requires.

how to generate AuthenticityToken on rails

There is a view helper called form_authenticity_token that returns the current session's authenticity token.

In your view.html.erb:

 <form action="/blah" method="POST">
<input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden">
<input name="first_name" type="text">
</form>

Generate unique identifier in rails helper

Your view rendering was done within a second! hence the reason you are seeing the same id value for all inputs.

The statement below will have the same value for 60 seconds.

st = Time.now.strftime("%I%M%S%p")

As it appears you want the slider to be on each input, I would recommend using either a data attribute or simply a css class. An example using css class would be:

  <div class="row">
<div class="col-xs-12">
<%= answerf.text_field :scale, class: 'has-slider', type: 'text',
data: {'slider-id' => 'scale',
'slider-min' => '0',
'slider-max' => '10',
'slider-step' => '1',
'slider-value' => '5' } %>
</div>
</div>

Say we assign class has-slider, we could then use this class selector and execute slider() on the matched elements in javascript as:

# app/assets/javascripts/input_slider.js.coffee

$ ->
# Execute slider on all inputs with has-slider css class
$('input.has-slider').slider()

And, you seem to know why you have slider only on the first input; This is because all your inputs have same id value and DOM selection retrieves the first match. ids need to be unique within a document.

Update:

As you're adding input elements within the dom after page load, the $('input.has-slider').slider() call made on app/assets/javscripts/input_slider.js.coffee would not work. It would only work for elements already present in the DOM. In order to make it work for elements added after DOM load, you can use jQuery on() method.

You could trigger some custom event and call slider() method as follows:

# app/assets/javascripts/projects.js.coffee
$(document).on 'click', 'form .add_fields', (event) ->
...
$(this).before($(this).data('fields').replace(regexp, time)).trigger('show-slider')
..

Then you will listen on the show-slider event and call slider() as:

# app/assets/javascripts/input_slider.js.coffee

$ ->
# Execute slider on all inputs with has-slider css class
$('input.has-slider').slider()

$(document).on 'show-slider', (event) ->
$(input.has-slider').slider()

Hope this clears any confusion.

form_for - generate unique ids

You can use Object#object_id:

... Returns an integer identifier for obj. The same number will be
returned on all calls to id for a given object, and no two active
objects will share an id
.

<% object = @document || Document.new %>
<%= form_for object, :html => { multipart: true, id: object.object_id.to_s } do |f| %>

This can be hard to declare own object for each form_for, i recommend create array with 5 objects and iterate through it.



Related Topics



Leave a reply



Submit