Ember-simple-auth、Torii 和 Facebook Oauth2 的工作流程 [英] Workflow for Ember-simple-auth, Torii and 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屋!