Ember simple auth、Tori和Facebook Oauth2的工作流
之后,我成功地用我的用户的Facebook帐户验证了他们的身份 但目前,Tori的提供商facebook-oauth2正在从facebook返回授权码;当承诺解决后,我将此授权码发送到我的后端,在那里我对Facebook执行请求以获取用户id和电子邮件:然后我在后端对用户进行身份验证,生成特定的访问令牌并发送回我的余烬应用程序 客户端代码:Ember simple auth、Tori和Facebook Oauth2的工作流,facebook,authentication,ember.js,ember-simple-auth,Facebook,Authentication,Ember.js,Ember Simple Auth,之后,我成功地用我的用户的Facebook帐户验证了他们的身份 但目前,Tori的提供商facebook-oauth2正在从facebook返回授权码;当承诺解决后,我将此授权码发送到我的后端,在那里我对Facebook执行请求以获取用户id和电子邮件:然后我在后端对用户进行身份验证,生成特定的访问令牌并发送回我的余烬应用程序 客户端代码: // app/controllers/login.js import Ember from 'ember'; import LoginControllerM
// 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的会话被标记为已认证,然后应用程序重定向到特定的已认证路由。但是在这种情况下,当我的后端返回“真实”的访问令牌时,会话应该经过身份验证
有没有办法用ember simple auth Tori来管理此工作流,或者我应该编写自己的验证器?我花了几天时间试图找出如何使它与Tori一起工作,最后放弃了它,转而使用我自己的验证器。这是来自Tori和ember simple auth的混合代码,因此它不是最干净的,可能无法处理所有边缘情况。它基本上扩展了ember simple auth oauth2验证器,并添加了自定义代码以将访问令牌传递给API app/lib/facebook-authenticator.js
/* global FB */
import OAuth2Authenticator from 'simple-auth-oauth2/authenticators/oauth2';
import ajax from 'ic-ajax';
var fbPromise;
var settings = {
appId: '1234567890',
version: 'v2.1'
};
function fbLoad(){
if (fbPromise) { return fbPromise; }
fbPromise = new Ember.RSVP.Promise(function(resolve){
FB.init(settings);
Ember.run(null, resolve);
});
return fbPromise;
}
function fblogin() {
return new Ember.RSVP.Promise(function(resolve, reject){
FB.login(function(response){
if (response.authResponse) {
Ember.run(null, resolve, response.authResponse);
} else {
Ember.run(null, reject, response.status);
}
}, {scope: 'email'});
});
}
export default OAuth2Authenticator.extend({
authenticate: function() {
var _this = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
fbLoad().then(fblogin).then(function(response) {
ajax(MyApp.API_NAMESPACE + '/oauth/facebook', {
type: 'POST',
data: {
auth_token: response.accessToken,
user_id: response.userId
}
}).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);
});
}).catch(function(xhr) {
Ember.run(function() {
reject(xhr.textStatus);
});
});
});
});
},
loadFbLogin: function(){
fbLoad();
}.on('init')
});
我终于按照比灵顿的建议编写了自己的验证器。但我也给我的用户提供了一种使用登录/密码进行身份验证的方法,因此我覆盖了ember-simple-auth-oauth2身份验证程序,只更改了“authenticate”方法,并使用了ember-simple-auth-torii 现在,我可以使用Tori从用户的Facebook帐户获取授权代码,将此代码发送到我的后端,对用户进行身份验证,并生成一个访问令牌,该令牌将由ember simple auth管理,就像oauth2令牌一样 代码如下:
// 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::TokensController的权限,将用户id与令牌一起传递,并管理facebook的授权代码(如果有)(该代码应进行重构):
我用了这个:
import Ember from 'ember';
import Torii from 'ember-simple-auth/authenticators/torii';
import ENV from "../config/environment";
const { inject: { service } } = Ember;
export default Torii.extend({
torii: service(),
ajax: service(),
authenticate() {
const ajax = this.get('ajax');
return this._super(...arguments).then((data) => {
return ajax.request(ENV.APP.API_HOST + "/oauth/token", {
type: 'POST',
dataType: 'json',
data: { 'grant_type': 'assertion', 'auth_code': data.authorizationCode, 'data': data }
}).then((response) => {
return {
access_token: response.access_token,
provider: data.provider,
data: data
};
}).catch((error) => {
console.log(error);
});
});
}
});
好方法。我还编写了自己的验证器,扩展了Oauth2Authenticator,但我仍然使用ToriaAuthenticator的代码从提供程序获取授权代码(请参阅我的答案)。这里是否需要登录(:user,user)?如果您仍然返回有效的访问令牌,它会做什么?我在我的服务器上使用Desive/doorkeeper尝试了完全相同的方法,但登录方法总是引发异常并导致401个未经验证的响应。该方法用于向用户添加信息,如上次登录ip、上次登录at、登录计数等。。。我用门卫的配置编辑了我的答案。是的,很酷,谢谢你的更新。我刚刚意识到我得到401的原因是因为我在设计配置中使用了:confirmable!这是否意味着我们也应该推翻撤销行动并调用Devate的注销方法;但这也意味着我们应该重写特定的简单auth方法来调用后端。目前,它只会使客户端的会话无效。您能告诉我如何验证facebook在自定义服务器上接收到的令牌吗?请参阅接受的答案:在令牌控制器中创建我使用gem考拉获取带有facebook接收到的验证码的访问令牌。我正在使用后端节点。任何节点库都可以这样做吗?
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
import Ember from 'ember';
import Torii from 'ember-simple-auth/authenticators/torii';
import ENV from "../config/environment";
const { inject: { service } } = Ember;
export default Torii.extend({
torii: service(),
ajax: service(),
authenticate() {
const ajax = this.get('ajax');
return this._super(...arguments).then((data) => {
return ajax.request(ENV.APP.API_HOST + "/oauth/token", {
type: 'POST',
dataType: 'json',
data: { 'grant_type': 'assertion', 'auth_code': data.authorizationCode, 'data': data }
}).then((response) => {
return {
access_token: response.access_token,
provider: data.provider,
data: data
};
}).catch((error) => {
console.log(error);
});
});
}
});