Ember-simple-auth,Torii和Facebook的工作流程Oauth2 [英] Workflow for Ember-simple-auth, Torii and Facebook Oauth2

查看:128
本文介绍了Ember-simple-auth,Torii和Facebook的工作流程Oauth2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我以前的关于ember-简单的auth和torii ,我成功地通过他们的Facebook帐户验证我的用户。

After my previous question about ember-simple-auth and torii, I successfully authenticate my users with their Facebook accounts.

但是目前,torii的提供商facebook-oauth2正在从Facebook返回授权码;当承诺解决时,我将此授权码发送到我的后端,在此我向Facebook执行请求以获取用户的ID和电子邮件:然后我在后端验证用户,生成特定的访问令牌并发送回我的ember应用程序。

But currently, torii's provider facebook-oauth2 is returning an authorization code from Facebook ; when the promise resolves, I send this authorization code to my backend where I perform a request against Facebook to get the user's id and email : then I authenticate the user on my backend, generating a specific access token and sending back to my ember application.

客户端代码:

// app/controllers/login.js
import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';

export
default Ember.Controller.extend(LoginControllerMixin, {
    // This authenticator for a simple login/password authentication.
    authenticator: 'simple-auth-authenticator:oauth2-password-grant',
    actions: {
        // This method for login with Facebook.
        authenticateWithFacebook: function() {
            var _this = this;
            this.get('session').authenticate(
                'simple-auth-authenticator:torii',
                "facebook-oauth2"
            ).then(
                function() {
                    var authCode = _this.get('session.authorizationCode');
                    Ember.$.ajax({
                            type: "POST",
                            url: window.ENV.host + "/facebook/auth.json",
                            data: JSON.stringify({
                                    auth_code: authCode
                            }),
                            contentType: "application/json; charset=utf-8",
                            dataType: "json",
                            success: function(data) {
                                    // TODO : manage access_token and save it to the session
                            },
                            failure: function(errMsg) {
                                    // TODO : manage error
                            }
                    });
                },
                function(error) {
                    alert('There was an error when trying to sign you in: ' + error);
                }
            );
        }
    }
});

问题是:当验证者的承诺解决时,ember-simple-auth的会话被标记为已验证,应用程序重定向到特定的身份验证路由,但在这种情况下,当我的后端返回realaccess_t时,会话会被认证oken。

The problem is : the ember-simple-auth's session is marked as authenticated when the authenticate's promise resolves and then the app redirects to the specific authenticated route. But in this case the session should be authenticated when my backend returns the "real" access_token.

有没有办法使用ember-simple-auth-torii来管理这个工作流程,还是应该写我自己的验证器?

Is there a way to manage this workflow with ember-simple-auth-torii or should I write my own authenticator ?

推荐答案

我终于写了自己的认证者,就像Beerlington所说。但是我也给我的用户一个使用登录/密码进行验证的方法,所以我覆盖了ember-simple-auth-oauth2验证器,只改变了authenticate方法,并使用了ember-simple-auth-torii。

I finally wrote my own authenticator as Beerlington suggested. But also I give to my users a way to authenticate using login/password, so I overrode the ember-simple-auth-oauth2 authenticator, changing only the "authenticate" method and used ember-simple-auth-torii.

现在,我可以使用Torii从用户的Facebook帐户获取授权码,将此代码发送到我的后端,验证用户并生成将被管理的访问令牌通过ember-simple-auth像一个oauth2令牌。

Now I can use Torii to get the authorization code from the user's Facebook account, send this code to my backend, authentify the user and generate an access token that will be managed by ember-simple-auth like an oauth2 token.

这是代码:

// initializers/simple-auth-config.js
import Ember from 'ember';
import Oauth2 from 'simple-auth-oauth2/authenticators/oauth2';

/**
  Authenticator that extends simple-auth-oauth2 and wraps the
  [Torii library](https://github.com/Vestorly/torii)'s facebook-oauth2 provider.

    It is a mix between ember-simple-auth-torii and ember-simple-auth-oauth2.

    First it uses Torii to get the facebook access token or the authorization code.

    Then it performs a request to the backend's API in order to authenticate the
    user (fetching personnal information from Facebook, creating account, login,
    generate session and access token). Then it uses simple-auth's
    oauth2 authenticator to maintain the session.

    _The factory for this authenticator is registered as
    `'authenticator:facebook'` in Ember's container._

    @class Facebook
    @namespace Authenticators
    @extends Oauth2
*/
var FacebookAuthenticator = Oauth2.extend({
    /**
    @property torii
    @private
    */
    torii: null,

    /**
    @property provider
    @private
    */
    provider: "facebook-oauth2",

    /**
    Authenticates the session by opening the torii provider. For more
    documentation on torii, see the
    [project's README](https://github.com/Vestorly/torii#readme). Then it makes a
    request to the backend's token endpoint and manage the result to create
    the session.

    @method authenticate
    @return {Ember.RSVP.Promise} A promise that resolves when the provider successfully 
    authenticates a user and rejects otherwise
    */
    authenticate: function() {
        var _this = this;
        return new Ember.RSVP.Promise(function(resolve, reject) {
            _this.torii.open(_this.provider).then(function(data) {
                var data = {
                    facebook_auth_code: data.authorizationCode
                };
                _this.makeRequest(_this.serverTokenEndpoint, data).then(function(response) {
                    Ember.run(function() {
                        var expiresAt = _this.absolutizeExpirationTime(response.expires_in);
                        _this.scheduleAccessTokenRefresh(response.expires_in, expiresAt, response.refresh_token);
                        if (!Ember.isEmpty(expiresAt)) {
                            response = Ember.merge(response, {
                            expires_at: expiresAt
                        });
                        }
                        resolve(response);
                    });
                }, function(xhr, status, error) {
                    Ember.run(function() {
                            reject(xhr.responseJSON || xhr.responseText);
                    });
                });
            }, reject);
        });
    },
});

export
default {
    name: 'simple-auth-config',
    before: 'simple-auth',
    after: 'torii',
    initialize: function(container, application) {
        window.ENV = window.ENV || {};
        window.ENV['simple-auth-oauth2'] = {
            serverTokenEndpoint: window.ENV.host + "/oauth/token",
            refreshAccessTokens: true
        };

        var torii = container.lookup('torii:main');
        var authenticator = FacebookAuthenticator.create({
            torii: torii
        });
        container.register('authenticator:facebook', authenticator, {
            instantiate: false
        });
    }
};

我的后端是Rails,并使用Doorkeeper来管理access_token和Devise。我覆盖了Doorkeeper :: TokensController来传递user_id与令牌,并管理Facebook的授权代码(如果有的代码应该重构):

My backend is in Rails and uses Doorkeeper to manage the access_token and Devise. I overrode Doorkeeper::TokensController to pass the user_id with the token and manage the facebook's authorization code if any (that code should be refactored) :

class TokensController < Doorkeeper::TokensController
    include Devise::Controllers::SignInOut # Include helpers to sign_in

    # The main accessor for the warden proxy instance
    # Used by Devise::Controllers::SignInOut::sign_in
    #
    def warden
        request.env['warden']
    end

    # Override this method in order to manage facebook authorization code and
    # add resource_owner_id in the token's response as
    # user_id.
    #
    def create
        if params[:facebook_auth_code]
            # Login with Facebook.
            oauth = Koala::Facebook::OAuth.new("app_id", "app_secret", "redirect_url")

            access_token = oauth.get_access_token params[:facebook_auth_code]
            graph = Koala::Facebook::API.new(access_token, "app_secret")
            facebook_user = graph.get_object("me", {}, api_version: "v2.1")

            user = User.find_or_create_by(email: facebook_user["email"]).tap do |u|
                u.facebook_id = facebook_user["id"]
                u.gender = facebook_user["gender"]
                u.username = "#{facebook_user["first_name"]} #{facebook_user["last_name"]}"
                u.password = Devise.friendly_token.first(8)
                u.save!
            end

            access_token = Doorkeeper::AccessToken.create!(application_id: nil, :resource_owner_id => user.id, expires_in: 7200)
            sign_in(:user, user)

            token_data = {
                access_token: access_token.token,
                token_type: "bearer",
                expires_in: access_token.expires_in,
                user_id: user.id.to_s
            }

            render json: token_data.to_json, status: :ok

        else
            # Doorkeeper's defaut behaviour when the user signs in with login/password.
            begin
                response = strategy.authorize
                self.headers.merge! response.headers
                self.response_body = response.body.merge(user_id: (response.token.resource_owner_id && response.token.resource_owner_id.to_s)).to_json
                self.status        = response.status
            rescue Doorkeeper::Errors::DoorkeeperError => e
                handle_token_exception e
            end

        end
    end
end

这是我在初始化程序doorkeeper.rb中用于验证用户的代码

Here is the code I use in the initializer doorkeeper.rb to authentify the user

Doorkeeper.configure do
  # Change the ORM that doorkeeper will use.
  # Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper
  orm :mongoid4

  resource_owner_from_credentials do |routes|
    request.params[:user] = {:email => request.params[:username], :password => request.params[:password]}
    request.env["devise.allow_params_authentication"] = true
    request.env["warden"].authenticate!(:scope => :user)
  end
  # This block will be called to check whether the resource owner is authenticated or not.
  resource_owner_authenticator do
    # Put your resource owner authentication logic here.
    # Example implementation:
    #   User.find_by_id(session[:user_id]) || redirect_to(new_user_session_url)
    #
    # USING DEVISE IS THE FOLLOWING WAY TO RETRIEVE THE USER
    current_user || warden.authenticate!(:scope => :user)
  end

  # Under some circumstances you might want to have applications auto-approved,
  # so that the user skips the authorization step.
  # For example if dealing with trusted a application.
  skip_authorization do |resource_owner, client|
     # client.superapp? or resource_owner.admin?
     true
  end
end

这篇关于Ember-simple-auth,Torii和Facebook的工作流程Oauth2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆