[英]On Google App Engine, can I relate a Google OAuth 2 Token and a SACSID token I got using Android's AccountManager?
[英]How do I use Google's token from AccountManager in my web app? (Rails with omniauth-google-oauth2)
omniauth-google-oauth2
的網絡應用程序,可以使用Google帳戶進行身份驗證。 然后使用cookie( session[:user_id]
)保持session[:user_id]
。 這有效。 AccountManager
獲取訪問令牌。 這也有效。 我正在嘗試讓Android應用程序發出請求,使Web應用程序驗證該令牌並啟動與Android應用程序的會話(cookie在Android應用程序上是持久的)。
我注意到來自Google的回調網址是: http://my.domain.com/auth/google_oauth2/callback
: http://my.domain.com/auth/google_oauth2/callback
,所以我嘗試添加參數state="/"
和code="<TOKEN FROM AccountManager>"
。
這導致:
(google_oauth2) Callback phase initiated.
(google_oauth2) Authentication failure! invalid_credentials: OmniAuth::Strategies::OAuth2::CallbackError, OmniAuth::Strategies::OAuth2::CallbackError
Started GET "/auth/google_oauth2/callback?state=%2F&code=<TOKEN FROM AccountManager>" for 5.102.217.111 at 2013-08-10 18:38:58 +0300
OmniAuth::Strategies::OAuth2::CallbackError (OmniAuth::Strategies::OAuth2::CallbackError):
omniauth-oauth2 (1.1.1) lib/omniauth/strategies/oauth2.rb:71:in `callback_phase'
omniauth (1.1.4) lib/omniauth/strategy.rb:226:in `callback_call'
omniauth (1.1.4) lib/omniauth/strategy.rb:182:in `call!'
omniauth (1.1.4) lib/omniauth/strategy.rb:164:in `call'
omniauth (1.1.4) lib/omniauth/builder.rb:49:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
rack (1.4.5) lib/rack/etag.rb:23:in `call'
rack (1.4.5) lib/rack/conditionalget.rb:25:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/head.rb:14:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/params_parser.rb:21:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/flash.rb:242:in `call'
rack (1.4.5) lib/rack/session/abstract/id.rb:210:in `context'
rack (1.4.5) lib/rack/session/abstract/id.rb:205:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/cookies.rb:341:in `call'
activerecord (3.2.13) lib/active_record/query_cache.rb:64:in `call'
activerecord (3.2.13) lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
activesupport (3.2.13) lib/active_support/callbacks.rb:405:in `_run__1566251561940761300__call__2926332968477140393__callbacks'
activesupport (3.2.13) lib/active_support/callbacks.rb:405:in `__run_callback'
activesupport (3.2.13) lib/active_support/callbacks.rb:385:in `_run_call_callbacks'
activesupport (3.2.13) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (3.2.13) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/reloader.rb:65:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/remote_ip.rb:31:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
railties (3.2.13) lib/rails/rack/logger.rb:32:in `call_app'
railties (3.2.13) lib/rails/rack/logger.rb:16:in `block in call'
activesupport (3.2.13) lib/active_support/tagged_logging.rb:22:in `tagged'
railties (3.2.13) lib/rails/rack/logger.rb:16:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/request_id.rb:22:in `call'
rack (1.4.5) lib/rack/methodoverride.rb:21:in `call'
rack (1.4.5) lib/rack/runtime.rb:17:in `call'
activesupport (3.2.13) lib/active_support/cache/strategy/local_cache.rb:72:in `call'
rack (1.4.5) lib/rack/lock.rb:15:in `call'
actionpack (3.2.13) lib/action_dispatch/middleware/static.rb:63:in `call'
railties (3.2.13) lib/rails/engine.rb:479:in `call'
railties (3.2.13) lib/rails/application.rb:223:in `call'
rack (1.4.5) lib/rack/content_length.rb:14:in `call'
railties (3.2.13) lib/rails/rack/log_tailer.rb:17:in `call'
rack (1.4.5) lib/rack/handler/webrick.rb:59:in `service'
/home/oded/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/2.0.0/webrick/httpserver.rb:138:in `service'
/home/oded/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run'
/home/oded/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/2.0.0/webrick/server.rb:295:in `block in start_thread'
Rendered /home/oded/.rvm/gems/ruby-2.0.0-p195/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/_trace.erb (0.8ms)
Rendered /home/oded/.rvm/gems/ruby-2.0.0-p195/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.7ms)
Rendered /home/oded/.rvm/gems/ruby-2.0.0-p195/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (5.3ms)
Web應用程序和Android應用程序均在Google API控制台中獲得授權。
我不想使用GoogleAuthUtil
有三個原因:我已經完全實現了AccountManager,它至少使我的應用程序大小加倍,我看不出它是如何解決我的網絡應用程序的問題。
作為臨時解決方案,我創建了一個WebView活動,該活動與Web應用程序完成相同的過程。 這不是一個好的解決方案,因為用戶需要輸入他的電子郵件和密碼,他無法判斷是否是網絡釣魚。
的Gemfile:
gem 'omniauth-google-oauth2'
SessionsControler(app / controllers / sessions_controller.rb):
def new
redirect_to "/auth/google_oauth2"
end
def create
auth = request.env["omniauth.auth"]
account = case auth['provider']
when GoogleAccount::PROVIDER then GoogleAccount.find_by_omniauth(auth)
end
session[:user_id] = account.user.id
respond_to do |format|
format.html { redirect_to root_url, :notice => "Signed in!" }
format.json { render json: { result: true } }
end
end
GoogleAccount(app / models / google_account.rb):
after_validation :create_user!, on: :create
def self.find_by_omniauth(auth)
google_account = find_by_uid(auth['uid']) || new(uid: auth['uid'])
google_account.attributes = auth['info'].select { |k, v| k.in?(attribute_names) }
google_account.save!
google_account
end
SessionManager.java:
public void login() {
_accountManager = AccountManager.get(_activity);
final Account[] accounts = _accountManager.getAccountsByType("com.google");
String[] accountNames = new String[accounts.length];
for (int i = 0; i < accounts.length; i++) {
accountNames[i] = accounts[i].name;
}
new AlertDialog.Builder(_activity).
setItems(accountNames, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
_account = accounts[which];
getAuthToken();
}
}).
show();
}
private void getAuthToken() {
_accountManager.getAuthToken(
_account, "ah", null, _activity,
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle;
try {
bundle = result.getResult();
} catch (OperationCanceledException e) {
// TODO: handle errors
e.printStackTrace();
return;
} catch (AuthenticatorException e) {
// TODO: handle errors
e.printStackTrace();
return;
} catch (IOException e) {
// TODO: handle errors
e.printStackTrace();
return;
}
Intent launch = (Intent) bundle.get(AccountManager.KEY_INTENT);
if (launch != null) {
_activity.startActivityForResult(launch, _accountManagerRequestCode);
return;
}
onGetAuthToken(bundle);
}
},
null
);
}
public void onAccountManagerResult(int resultCode, Intent data) {
switch (resultCode) {
case Activity.RESULT_OK:
_accountManager = AccountManager.get(_activity);
getAuthToken();
break;
}
}
private void onGetAuthToken(Bundle bundle) {
final String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
login(
token,
new LoginResponseHandler() {
@Override
public void onResult(boolean result, boolean hadAnonymous, String message, Map<String, List<String>> errors) {
if (result) {
setLoggedIn(true);
} else {
// Token expired?
_accountManager.invalidateAuthToken("com.google", token);
getAuthToken();
}
}
});
}
public static void login(String token, LoginResponseHandler loginResponseHandler) {
RequestParams requestParams = defaultRequestParams();
requestParams.put("code", token);
requestParams.put("state", "/");
_client.get(
BASE_URL + "/auth/google_oauth2/callback", requestParams,
new JsonLoginResponseHandler(loginResponseHandler));
}
我設法在不使用omniauth的情況下手動驗證服務器端的令牌。
AccountManager#getAuthToken
。 authTokenType
參數(以前稱為"ah"
)需要使用客戶端ID填充,如下所示: "audience:server:client_id:" + CLIENT_ID
。 客戶端ID是從API控制台檢索的,並且是屬於您的Web應用程序的客戶端ID。 它看起來像這樣: 123456789.apps.googleusercontent.com
。 SessionsController(app / controllers / sessions_controller.rb):
def create
auth = request.env["omniauth.auth"]
if auth
account = case auth['provider']
when GoogleAccount::OAUTH_PROVIDER then GoogleAccount.find_by_omniauth(auth)
end
else
account = case params[:provider]
when GoogleAccount::PROVIDER then GoogleAccount.find_by_token(params[:token])
end
end
render text: "Unauthoirized", status: :unauthorized and return if account.nil?
session[:user_id] = account.user.id
respond_to do |format|
format.html { redirect_to root_url, :notice => "Signed in!" }
format.json { render json: { result: true } }
end
end
GoogleAccount(app / models / google_account.rb):
def self.find_by_token(token)
validator = GoogleIDToken::Validator.new
jwt = validator.check(token, CLIENT_ID)
return if jwt.nil?
google_account = where(uid: jwt['id']).first_or_initialize
google_account.email = jwt['email']
google_account.save!
google_account
end
jwt
是一個看起來像這樣的哈希:
{"iss"=>"accounts.google.com",
"verified_email"=>"true",
"email_verified"=>"true",
"email"=>"my.email@gmail.com",
"aud"=>"123456789.apps.googleusercontent.com", # aka CLIENT_ID
"cid"=>"123456789-somerandomletters.apps.googleusercontent.com", # this is the device ID, you can validate that too with another parameter
"azp"=>"123456789-samerandomletters.apps.googleusercontent.com",
"id"=>"10000000000000000000", # this is Google's user ID for this user, keep it
"sub"=>"10000000000000000000",
"iat"=>1376306389,
"exp"=>1376310289}
服務器現在沒有獲得在登錄Web時獲得的相同詳細信息,因為令牌只保存電子郵件,但這對我來說已經足夠了。 我也不驗證CID(設備ID),不確定何時需要它。
使用Google Play服務,您可以調用GoogleAuthUtil.getToken來獲取Android應用中的令牌。 請參閱http://developer.android.com/google/play-services/auth.html 。
我使用rest-client手動驗證服務器上的令牌。 我基於我過去使用的Twitter OAuth Echo上的標題。
require 'rest_client'
require 'json'
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :require_login_or_oauth!
private
def authorization_header
'X-Verify-Credentials-Authorization'
end
def service_provider_header
'X-Auth-Service-Provider'
end
def verify_url
'https://www.googleapis.com/oauth2/v1/tokeninfo'
end
def client_ids
%w(-- your client ids here--)
end
def find_oauth_user
if request.headers[authorization_header].nil? or verify_url != request.headers[service_provider_header]
logger.warn "bad authorization headers"
render :text => "Bad authorization headers", :status => :unauthorized
return nil
end
begin
response = RestClient.get verify_url, { :params => { :access_token => request.headers[authorization_header] }, :accept => :json }
if 200 != response.code
render :text => "OAuth validation failed", :status => :unauthorized
return nil
end
body = JSON.parse(response.to_str)
return nil if not client_ids.include? body['audience']
# TODO log bad client
# TODO possibly validate scope
if identity = Identity.find_by_uid(body['user_id'])
user = identity.user
logger.info "Authenticated #{user.id} as #{identity.email}"
return user
end
return nil
rescue RestClient::Exception => e
logger.warn e
render :text => "OAuth validation failed", :status => :unauthorized
return nil
end
end
def require_login_or_oauth!
if "application/json" == request.format
user = find_oauth_user
if user
sign_in(:user, user)
session[:user_id] = user.id
else
redirect_to root_url, status: 401
end
else
authenticate_user!
end
end
end
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.