-
Notifications
You must be signed in to change notification settings - Fork 386
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for RESTful JSON APIs access tokens and OAuth2.0 For Login #415
Open
fzagarzazu
wants to merge
6
commits into
NoamB:master
Choose a base branch
from
fzagarzazu:access_token
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
fca01bb
Add support for RESTful JSON APIs access tokens and OAuth2.0 For Login
fzagarzazu 6439062
Fix, do not hit the database when access_token request argument is blank
fzagarzazu ec97aa5
Fix, curl example in README file was missing a comma
fzagarzazu 58e8511
Add missing bits of information and link to demo in README
fzagarzazu af7ab3c
Use change in access_token migration
fzagarzazu 8c432e6
Restore find_by_sorcery_token method
fzagarzazu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -190,6 +190,190 @@ External (see lib/sorcery/controller/submodules/external.rb): | |
* OAuth1 and OAuth2 support (currently twitter & facebook) | ||
* configurable db field names and authentications table. | ||
|
||
AccessToken (see lib/sorcery/model/submodules/access_token.rb) | ||
* OAuth 2.0 For Login - RESTful JSON APIs. | ||
* Please read instructions below. | ||
|
||
== AccessToken Submodule (rails-api) | ||
|
||
This submodule is intended to be used with rails as the backend server to | ||
rich client-side applications. | ||
|
||
See the rails-api project for building RESTful JSON APIs. | ||
|
||
=== Supported Modes: | ||
|
||
* *single_token*: One access token per user, shared between all user clients. Creates an access token on user creation. | ||
|
||
* *session*: Allows multiple tokens per user, a user can have many clients acting on its behalf, with this mode each client can have its own access token. A maximum value can be defined using the 'max_per_user' configuration option. | ||
|
||
=== Expiration: | ||
|
||
Tokens expiration can be configured by setting the 'duration' value (in seconds), | ||
this value will be used against token's creation time to know if the token has expired. | ||
|
||
Expiration can also be evaluated against token's last actvity time by setting | ||
'duration_from_last_activity' to true. | ||
|
||
Expired tokens will be automatically deleted for each user after login. | ||
|
||
Tokens are deleted on client logout. | ||
|
||
==== Permanent Tokens: | ||
|
||
All tokens are set to expire by default, permanent tokens can be created by setting | ||
its 'expirable' attribute to false. | ||
|
||
Example use case: mobile applications where the user never logs in/out. | ||
|
||
=== Security Considerations: | ||
|
||
* Use of TLS is *required* (HTTPS). | ||
|
||
=== OAuth 2.0 For Login: | ||
|
||
Outsources user authentication to OAuth 2.0 providers. | ||
|
||
Flow: Implicit Grant [1] | ||
|
||
==== How does it work? | ||
|
||
Client-side application gets an access token from an OAuth 2.0 provider, | ||
validates the received token, and sends the token with the rest of its | ||
properties to this API server for 'login'. | ||
|
||
The API server then attempts to login the user by sending a request to the | ||
provider with the access token, if the access token is valid the API server | ||
uses the user identifier included in the provider's response to find | ||
and login (and optionaly create) the external user in the local database. | ||
|
||
Please see links below for more information, in particular Ryan Boyd's | ||
excellent talk about OAuth 2.0 [2] and Google's OAuth2 playground | ||
(use the settings button to change the OAuth flow to Client-side, | ||
you can also enter your own OAuth credentials) [4]. | ||
|
||
==== Notes: | ||
- After login, the API server can then return one of its own api_access_token to the client application. | ||
- Tested with the following providers: Google. | ||
- The client-side application only needs to store the OAuth Client ID, the secret can be set in the API server configuration file. | ||
|
||
==== References: | ||
|
||
1. {https://tools.ietf.org/html/draft-ietf-oauth-v2-30#section-1.3.2}[https://tools.ietf.org/html/draft-ietf-oauth-v2-30#section-1.3.2] | ||
2. {Google I/O 2012 - OAuth 2.0 for Identity and Data Access}[http://www.youtube.com/watch?v=YLHyeSuBspI] | ||
3. {https://developers.google.com/accounts/docs/OAuth2}[https://developers.google.com/accounts/docs/OAuth2] | ||
4. {https://developers.google.com/oauthplayground}[https://developers.google.com/oauthplayground] | ||
|
||
|
||
=== Setup Example (OAuth 2.0 For Login): | ||
|
||
* Update: demo with rails-api and Google's OAuth 2.0 For Login: https://github.com/fzagarzazu/sorcery_access_token_demo | ||
|
||
* Installation | ||
rails generate sorcery:install access_token external | ||
|
||
* Example configuration file config/initializers/sorcery.rb | ||
Rails.application.config.sorcery.submodules = [:access_token, :external] | ||
config.restful_json_api = true | ||
config.external_providers = [:google] | ||
config.google.key = "client_id" | ||
config.google.secret = "client_secret" | ||
config.user_config do |user| | ||
user.username_attribute_names = [:email] | ||
user.authentications_class = Authentication | ||
user.access_token_mode = 'single_token' | ||
user.access_token_duration = 10.minutes.to_i | ||
user.access_token_duration_from_last_activity = true | ||
user.access_token_register_last_activity = true | ||
end | ||
|
||
* Example user model: | ||
|
||
|
||
class User < ActiveRecord::Base | ||
authenticates_with_sorcery! do |config| | ||
config.authentications_class = Authentication | ||
end | ||
attr_accessible :email, :password, :password_confirmation, :authentications_attributes | ||
has_many :access_tokens, :dependent => :delete_all | ||
has_many :authentications, :dependent => :destroy | ||
|
||
accepts_nested_attributes_for :authentications | ||
|
||
end | ||
|
||
* Example authentication model | ||
|
||
class Authentication < ActiveRecord::Base | ||
attr_accessible :user_id, :provider, :uid | ||
belongs_to :user | ||
end | ||
|
||
|
||
* Example sessions controller | ||
|
||
|
||
class SessionsController < ApplicationController | ||
skip_before_filter :require_login, :except => [:destroy] | ||
|
||
def create | ||
# Login | ||
if params[:provider] && params[:access_token_hash] | ||
login_or_create_from_client_side(params[:provider], params[:access_token_hash]) | ||
elsif params[:email] && params[:password] | ||
login(params[:email], params[:password]) | ||
end | ||
|
||
# Response | ||
if @api_access_token | ||
render :json => {:access_token => @api_access_token.token } | ||
else | ||
head :unauthorized | ||
end | ||
end | ||
|
||
def destroy | ||
logout | ||
head :ok | ||
end | ||
end | ||
|
||
* Example application controller | ||
|
||
class ApplicationController < ActionController::API | ||
before_filter :require_login | ||
end | ||
|
||
|
||
=== Notes: | ||
|
||
* It should be compatible with the following submodules: | ||
activity_logging, brute_force_protection, external, reset_password, user_activation | ||
|
||
* curl examples: | ||
|
||
# Examples, remember to use HTTPS. | ||
|
||
# GET request | ||
curl -v -H "Accept: application/json" -H "Content-type: application/json" \ | ||
-X GET http://localhost:3000/apples?access_token=DdQoWMGzsVdPAL5egq67 | ||
|
||
# POST login request with email and password credentials | ||
curl -v -H "Accept: application/json" -H "Content-type: application/json" \ | ||
-X POST -d '{"email": "[email protected]", "password": "secret_passwd"}' http://localhost:3000/sessions | ||
|
||
# POST login request with provider's access token | ||
curl -v -H "Accept: application/json" -H "Content-type: application/json" \ | ||
-X POST -d '{"provider": "google", "access_token_hash": { \ | ||
"access_token": "9d2H0I9AEa834B45cRTfBEcA82bF010B53BFGT83eFDB6097", \ | ||
"token_type": "Bearer", "expires_in": 3600 } }' http://localhost:3000/sessions | ||
|
||
* Mongoid and MongoMapper | ||
|
||
Support included but not completely tested, please review code before using it. | ||
|
||
* Code review, feedback and contributions much appreciated. | ||
|
||
|
||
== Next Planned Features: | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
lib/generators/sorcery/templates/migration/access_token.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
class SorceryAccessToken < ActiveRecord::Migration | ||
def self.up | ||
create_table :access_tokens do |t| | ||
t.string :token, :default => nil | ||
t.boolean :expirable, :default => true | ||
t.datetime :last_activity_at | ||
t.references :<%= model_class_name.downcase %> | ||
|
||
t.timestamps | ||
end | ||
end | ||
|
||
def self.down | ||
drop_table :access_tokens | ||
end | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use
change
method here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kirs sure