我正在Scala Play中实现离线Google OAuth流。
用户点击对话框"是的,我想让这个应用程序访问我的谷歌账户的以下数据…"。用户点击Ok后,谷歌服务器会向我的服务器发送一个令牌。示例响应如下(我的浏览器窗口的URL):
https://myapp.herokuapp.com/storeauthcode#code=123&loginhint=abc&client_id[crypticId].apps.googleusercontent.com&prompt=同意
如何在当前浏览器窗口中从当前url提取123子字符串(这是我的访问令牌)?:
def storeAuthCode = Action { request =>
//save token
//confirm login of user now
}
提取像查询参数这样的数据当然不起作用。
request.path
返回
/storeauthcode
但不是带有令牌的字符串。
#
之后的URL部分称为片段标识符。浏览器不会将片段标识符发送到服务器:
片段标识符的功能与URI的其他部分不同:即,它的处理完全在客户端进行,没有web服务器的参与——当然,服务器通常有助于确定MIME类型,而MIME类型决定了片段的处理。当代理(如Web浏览器)从Web服务器请求Web资源时,该代理将URI发送到服务器,但不发送片段。相反,代理等待服务器发送资源,然后代理根据文档类型和片段值处理资源。
如果有必要,可以在客户端解析出片段,然后将其作为请求体或查询字符串的一部分发送。
这个问题的另一个相关答案是@slashburn使用的OAuth2流对于用例来说是不正确的。Google将此流程称为"用于客户端Web应用程序的OAuth 2.0"。此流旨在供只能读取片段的客户端浏览器应用程序使用。当浏览器客户端请求response_type为"token"时,会启动流。
https://accounts.google.com/o/oauth2/v2/auth?
redirect_uri=http%3A%2F%2Foauth2.example.com%2Fcallback&
response_type=token&
client_id=client_id
要使用的适当流是"OAuth 2.0 for Web Server Applications",而不是客户端流。此流适用于使用web服务器与googleapi交互的web应用程序。response_type的值不是"token",而是"code"。
https://accounts.google.com/o/oauth2/v2/auth?
redirect_uri=http%3A%2F%2Foauth2.example.com%2Fcallback&
response_type=code&
client_id=client_id
这个流程稍微复杂一些,因为OAuth2服务器将发回一个临时代码。然后,服务器使用临时代码直接发布到OAuth2服务器,以使用如下所示的url和正文取回令牌。
https://www.googleapis.com/oauth2/v4/token
POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https://oauth2.example.com/code&
grant_type=authorization_code
服务器和客户端应用程序使用不同流的原因是为了防止访问代码在clear over The wire中传输。对于客户端流,该片段对浏览器来说完全是本地的,因此攻击者没有什么可闻的。对于服务器流,攻击者只能看到一次代码。如果没有服务器的秘密,一次性代码是无用的。服务器在其主体中张贴代码和秘密,以从服务器获得响应,并且通信的两个部分都通过HTTPS协议加密。