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

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

问题描述

我之前关于余烬的问题之后-simple-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 的会话被标记为已认证,然后应用程序重定向到特定的认证路由.但在这种情况下,当我的后端返回真实的"access_token 时,会话应该被验证.

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 身份验证器,仅更改了身份验证"方法并使用了 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天全站免登陆