TokenAction
TokenAction lets you create tokens to be used to authenticate actions. For example:
- Create a token for a user to confirm an account
- Create a token for a subscriber to unsubscribe from notifications
- Create a token for a user to confirm a change of email address
Getting Started
TokenAction works with Rails 3.1 onwards. Add it to your Gemfile:
gem 'token_action'
Install the gem and configure your application to use TokenAction with:
bundle
bundle exec rails generate token_action
If you are using ActiveRecord, run the migration:
bundle exec rake db:migrate
Basic Usage
Create a shared secret to confirm a user account, for example:
token = TokenAction::Token.create! kind: 'User', args: [1, :confirm]
token.token # a 20-character alphanumeric string like "j7NtCaYfUpZXyDCseKG2"
You can then send the token with instructions to the user via email. When the user visits /tokens/j7NtCaYfUpZXyDCseKG2/confirm, TokenAction will look up the token. If not found, it will log an informational message to the Rails logger, set flash[:alert] and redirect to the root_path. If found, it will call the redeem_token method on the User class, passing in the arguments 1 and :confirm. You must implement the public redeem_token method. For example:
class User < ActiveRecord::Base
def self.redeem_token(id, meth)
User.find(id).send(meth)
end
def confirm
update_attribute :confirmed, true
end
end
If an exception is raised, TokenAction will log a warning message to the Rails logger, set flash[:alert] and redirect to the root_path. If no exception is raised, TokenAction will set flash[:notice] and redirect to the root_path. That's it!
Customization
You can customize the redirect URLs, the routes, the flash messages and the tokens controller.
Redirect URLs
To change the default success and failure URLs from root_path, edit the config/initializers/token_action.rb file created by rails generate token_action. You may also set success and failure URLs for each token, by creating tokens with :success_url and :failure_url arguments:
token = TokenAction::Token.create! kind: 'Cat', success_url: cat_path(1), failure_url: '/oops'
Note: If you change your URL structure after creating tokens, TokenAction may attempt to redirect to an unroutable path. If a path is unroutable, TokenAction will redirect to another URL in this order of precedence:
Token#success_urlTokenAction.success_urlroot_path
If an exception was raised and a path is unroutable, it will redirect in this order of precedence:
Token#failure_urlTokenAction.failure_urlroot_path
Routes
The TokenAction generator will add mount TokenAction::Engine => '/token_action' to your routes, which defines:
get 'tokens/:token/*path', to: 'tokens#redeem'
As such, you can write URLs like tokens/xxx/confirm or tokens/xxx/unsubscribe or tokens/xxx/a/b/c.
To customize the first part of the route, replace mount TokenAction::Engine => '/token_action' with something like:
get 'jetons/:token/*path', to: 'token_action/tokens#redeem'
Flash messages
The TokenAction generator will create a config/locales/token_action.en.yml file with the default messages for when a token is not found, an exception is raised, or the action is performed successfully. You will probably want to change these messages depending on the action taken and the exception raised.
-
If a token was redeemed by accessing a URL like
tokens/xxx/confirm, TokenAction will look for a message in the scopetoken_action.tokens.confirm. If not found, it will default to the scopetoken_action.tokens.default. -
If a token was redeemed by accessing a long URL like
tokens/xxx/a/b/c, TokenAction will look for a message in the scopetoken_action.tokens.a.b.c.
You may want to raise an exception if an action has already been performed or if it is no longer valid, in which case you may want the failure message to change according to the exception raised.
-
If an
AlreadyConfirmedexception is raised, TokenAction will look for a message in the scopetoken_action.tokens.default.already_confirmed. If not found, it will default totoken_action.tokens.default.failure. -
If a namespaced
MyModule::AlreadyConfirmedexception is raised, TokenAction will look for a message in the scopetoken_action.tokens.default.my_module.already_confirmed.
Putting it all together, if an AlreadyConfirmed exception is raised accessing a URL like tokens/xxx/confirm, TokenAction will look for messages in this order of preference:
token_action.tokens.confirm.already_confirmedtoken_action.tokens.default.already_confirmedtoken_action.tokens.confirm.failuretoken_action.tokens.default.failure
Tokens controller
To customize the controller without monkey patching, create a new controller like:
class TokensController < TokenAction::TokensController
end
And replace the route with something like:
get 'tokens/:token/*path', to: 'tokens#redeem'
You can then override the redeem method in your new controller.
Supported ORMs
It should be easy to add adapters for MongoMapper, DataMapper, Sequel and CouchPotato. If you are using DataMapper or Sequel, you must use an ActiveModel compliance plugin:
If using Sequel, you will want to use the orm_adapter-sequel gem. If using CouchPotato, you will need to write your own adapter for ORM Adapter.
Caveats
- If you change the name of a class, update the
kindattribute on its tokens to avoid making them unprocessable. - Be careful when changing the behavior of a
redeem_tokenmethod, to avoid making tokens unprocessable.
Copyright (c) 2012 James McKinney, released under the MIT license