最近工作需要,调研了国外几个大网站(facebook、twitter和youtube等)的应用开发相关事项,阅读官方API文档, 并整理资料与大家分享。本文是关于facebook认证和授权方面的资料整理,参考文档http://developers.facebook.com/docs/authentication/, 翻译和整理, 未经编码测试,估计很多错误烦请批评指正。
Facebook 平台使用OAuth2 协议作为认证和授权协议,它有两种认证流程,服务器端流程(Server-Sizde Flow)和客户端流程(Client-Side Flow), 这些认证流程可被用于开发网站应用,移动应用或者桌面应用。
此文档使用用户登录的例子,概述了facebook支持的两种认证和授权流程,在这个例子,服务器端使用PHP,客户端使用HTML/JavaScript, 但是它们能够很方便地转换为其它的编程语言。 两种认证和授权流程,服务器端(server-side)和客户端(client-side), 服务器端流程是由Web服务器调用Graph API完成认证和授权, 客户端流程是由客户端调用Graph API完成认证和授权, 例如使用运行在浏览器上的javascript或者本地移动应用或桌面应用。
无论使用何种流程,它都有几个步骤:用户认证、应用获得授权和应用认证。 用户认证保证用户的正确性, 应用授权保证用户准确知道他自己把什么数据和能力授权给了应用, 应用认证保证用户是把信息给了此应用而不是其它应用。一旦认证和授权完成,应用能够使用access token 访问用户的信息,并且作为用户的代表执行操作。
一. 服务器端流程(Server-Side Flow)
1. 用户访问需要用户登录才能操作的页面,或者点击登录按钮。 浏览器重定向到
https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL
其中YOUR_APP是应用在facebook创建的应用程序ID, YOUR_URL是用户登录成功或者失败后,回调(重定向)地址。重定向到上面的地址,facebook验证带到浏览器保存的cookie信息,如果此时用户已经登录,则完成认证。如果没有登录,则返回下面的界面,用户填写信息后点击登录
一旦完成用户的认证,服务器返回授权界面如下
默认情况下,授权给应用的权限只能访问基本信息,如果应用需要更多的权限,那么需要在上面的授权链接中加入scope参数,请求更多的权限,如下面的地址请求了访问用户邮箱地址和消息的权限
https://www.facebook.com/dialog/oauth?
client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&scope=email,read_stream
用户看到的授权界面比上一个授权界面增加了两项
如果用户按下Don't Allow, 应用不被授权,授权窗口重定向(通过HTTP 302)到用户的URL(打开授权窗口传入的URL), 并带有错误信息
http://YOUR_URL?error_reason=user_denied&
error=access_denied&error_description=The+user+denied+your+request.
如果用户同意授权,授权窗口重定向到应用制定的URL, 请带有授权码
http://YOUR_URL?code=A_CODE_GENERATED_BY_SERVER
Web服务器接收到该认证码,下一步,服务器发送带有应用ID(YOUR_APP_ID)和应用密钥(YOUR_APP_SECRET)和认证码的请求到地址https://graph.facebook.com/oauth/access_token如下
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&
client_secret=YOUR_APP_SECRET&code=THE_CODE_FROM_ABOVE
如果授权成功,在facebook服务器的影响body中带有access token
最后,认证和授权完成,服务器能够使用access token获取用户授权的信息。
下面的流程图描述了服务器端流程的HTPP调用流程
下面的代码是一个网站应用的可能实现
<?php $app_id = "YOUR_APP_ID"; $app_secret = "YOUR_APP_SECRET"; $my_url = "YOUR_URL"; session_start(); $code = $_REQUEST["code"]; if(empty($code)) { $_SESSION['state'] = md5(uniqid(rand(), TRUE)); //CSRF protection $dialog_url = "http://www.facebook.com/dialog/oauth?client_id=" . $app_id . "&redirect_uri=" . urlencode($my_url) . "&state=" . $_SESSION['state']; echo("<script> top.location.href='" . $dialog_url . "'</script>"); } if($_REQUEST['state'] == $_SESSION['state']) { $token_url = "https://graph.facebook.com/oauth/access_token?" . "client_id=" . $app_id . "&redirect_uri=" . urlencode($my_url) . "&client_secret=" . $app_secret . "&code=" . $code; $response = @file_get_contents($token_url); $params = null; parse_str($response, $params); $graph_url = "https://graph.facebook.com/me?access_token=" . $params['access_token']; $user = json_decode(file_get_contents($graph_url)); echo("Hello " . $user->name); } else { echo("The state does not match. You may be a victim of CSRF."); } ?>
2. 客户端认证流程(Client-Side Flow)
客户端和服务器端认证流程在请求认证窗口唯一不同的地方时,在参数中加入response_type,值为token
https://www.facebook.com/dialog/oauth? client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&response_type=token
一旦认证完成,浏览器重定向到应用提供的重定向地址,并携带access_token 和超时时间
http://YOUR_URL#access_token=166942940015970%7C2.sa0&expires_in=64090
由于access token是作为URL的URI fragment(不被发送到服务器), 只有 客户端代码(javascript等)能够取出它。应用认证是通过验证重定向地址是否是在创建应用时配置的相同的地址。下图描述了客户端验证的流程
下面是可能的上实现
<html>
<head>
<title>Client Flow Example</title>
</head>
<body>
<script>
function displayUser(user) {
var userName = document.getElementById('userName');
var greetingText = document.createTextNode('Greetings, '
+ user.name + '.');
userName.appendChild(greetingText);
}
var appID = "YOUR_APP_ID";
if (window.location.hash.length == 0) {
var path = 'https://www.facebook.com/dialog/oauth?';
var queryParams = ['client_id=' + appID,
'redirect_uri=' + window.location,
'response_type=token'];
var query = queryParams.join('&');
var url = path + query;
window.open(url);
} else {
var accessToken = window.location.hash.substring(1);
var path = "https://graph.facebook.com/me?";
var queryParams = [accessToken, 'callback=displayUser'];
var query = queryParams.join('&');
var url = path + query;
// use jsonp to call the graph
var script = document.createElement('script');
script.src = url;
document.body.appendChild(script);
}
</script>
<p id="userName"></p>
</body>
</html>