掘金如何使用Github账号登录?(OAuth2.0授权码模式实例详解)

397 阅读6分钟

1 缘起与目的

最近在做网关,一直在接触认证与授权相关问题。突然想到之前OAuth2.0相关知识,正好看到掘金在使用GitHub登录时使用了非常经典的OAuth2.0实现,所以不如就使用这个例子来详解一下这个协议。

本文并不详细介绍OAuth2.0的基础知识,阅读之前需要您对此有一定的了解。如果您了解不多也没关系,建议先看一下这两篇写OAuth2.0的文章,写的非常好,几乎是博客中介绍的最详细且易懂的了,笔者力荐。

OAuth 2.0 的一个简单解释 - 阮一峰的网络日志 (ruanyifeng.com)

OAuth 2.0 的四种方式 - 阮一峰的网络日志 (ruanyifeng.com)

2 掘金登录Github步骤图

掘金单点登录github过程图.png

3 分步骤详细解读

3.1 用户选择GitHub账号登录

3.2 携带参数跳转GitHub

这里掘金会开启浏览器小窗,跳转GitHub页面,让你去认证+授权。如下图所示。

让我们把url拿出来看下。

image.png

再处理一下,如下所示。

https://github.com/login/oauth/authorize?
    ?client_id=手动马赛克
    &redirect_uri=https://juejin.cn/passport/auth/login_success
    &state=手动马赛克
    &allow_signup=true
    &scope=user:email

如上url可以看到client_id、redirect_uri、state、scope等参数清晰可见。

client_id参数让GitHub知道是掘金在请求, redirect_uri参数是GitHub授权结束后跳转网址, scope参数表示要求的授权范围, state是为了防止csrf攻击的随机参数,详见文章最后。

3.3-3.4 GitHub认证与授权

这部分就不详细展开了,只放一下GitHub在点击授权那一刻的授权url。

https://github.com/login/oauth/authorize

image.png

可以看到除了本身OAuth2.0的部分参数外还有一些GitHub自己的授权参数,如authenticity_token等。

3.5 GitHub完成授权,跳转到开始掘金传入的入参redirect_uri

https://juejin.cn/passport/auth/login_success?code=手动马赛克&state=手动马赛克

image.png

这里可以看到GitHub已经返回了用于掘金获取GitHub信息的授权码code,另外还有用于防范csrf攻击的state

注意 :以下过程均是发生在掘金redirect_uri向服务器请求后发生的,由于此过程大部分是掘金后端服务请求GitHub服务,无法观测到,所以只是根据OAuth2.0猜测的。

3.6 掘金前端拿到授权码传给后端。

3.7 掘金后端带着授权码等信息去GitHub请求token。

3.8 掘金后端拿到token。

3.9 掘金后端拿着token去GitHub请求用户数据

3.10 GitHub校验后返回用户数据。

3.6 - 3.7 掘金前端拿到授权码传给后端,后端带着授权码等信息去GitHub请求token

https://github.com.com/oauth/token?
 client_id=CLIENT_ID&
 client_secret=CLIENT_SECRET&
 grant_type=authorization_code&
 code=AUTHORIZATION_CODE&
 redirect_uri=CALLBACK_URL

上面 URL 中,client_id参数和client_secret参数用来让 GitHub 确认 掘金 的身份(client_secret参数是保密的,因此只能在后端发请求),grant_type参数的值是AUTHORIZATION_CODE,表示采用的授权方式是授权码,code参数是上一步拿到的授权码,redirect_uri参数是为了校验确保它与第一步提供的redirect_uri 一致,这是为了确保授权码确实是由预期的客户端接收到的。

3.8 GitHub返回token

返回值大概如下

{    
  "access_token":"ACCESS_TOKEN",
  "token_type":"bearer",
  "expires_in":2592000,
  "refresh_token":"REFRESH_TOKEN",
  "scope":"user:email",
  "uid":100101,
  "info":{...}
}

access_token为请求令牌,用于后续掘金向GitHub请求用户数据。

expires_in为请求令牌过期时间。

refresh_token为刷新令牌。令牌的有效期到了,如果让用户重新走一遍上面的流程,再申请一个新的令牌,很可能体验不好,而且也没有必要。OAuth 2.0 允许第三方自动更新令牌。具体方法是,GitHub网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh_token 字段)。令牌到期前,掘金使用 refresh_token 发一个请求,去更新令牌。

3.9-3.10 掘金后端请求GitHub用户信息

因为看不到具体的请求,这里就不做过多赘述了。

3.11 掘金后端收到前端发送的redirect_uri请求,302到新url

Snipaste_2024-07-03_18-07-54.png

3.12 授权成功,关闭小窗

302后的新Url图示如下:

Snipaste_2024-07-03_18-08-01.png 新url会返回授权成功的html页面。并关闭小窗。

4 state的作用

前文提到了一下state,这里详细说明一下它的作用。

state 参数用于防止跨站请求伪造(CSRF)攻击。在授权请求过程中,客户端生成一个唯一的 state 值并将其包含在授权请求中。当授权服务器将用户重定向回客户端时,会附带相同的 state 值。客户端在接收到响应后,验证返回的 state 值是否与原始请求中生成的值一致,从而确保请求的合法性。

假设state不存在,存在如下场景:

有一个攻击A,登录了掘金,并且去关联GitHub账号,在授权时截获了自己的授权码回调Url,也就是如下URL:

https://juejin.cn/passport/auth/login_success?code=攻击者A的授权码

攻击者A将上述url精心封装成了一个A标签放到了掘金的网站上并发布了出来。

大冤种B在自己浏览器已经登录了掘金,这时候恰好看到了A精心封装的A标签,就点了一下。这时候掘金以为自己收到了GitHub的授权码,将该授权码(攻击者A的授权码)与正在登录中账号B做了关联。从此A就可以随时通过自己的GitHub账号登录B的掘金。

从上述过程可以看出,本质原因是授权与绑定信息分离,掘金无法验证绑定信息时前一次获取授权码时是同一个人。引入state就是为了保证授权的用户和关联账户的账号是同一个人。A请求时会带着与A关联的随机state,B再点A的url掘金会发现这个state不是跟B关联的,这就是state的作用。

当然,通常意义上授权码都有过期时限与只能使用一次的限制。

如果读者觉得笔者还是在不知所云,不知道在说啥,强烈建议您读一下以下文章,写的非常不错。

移花接木:针对OAuth2的CSRF攻击 - 简书 (jianshu.com)

5 结语

在这里班门弄斧了一下,其中很多流程都是笔者半猜半看写出来的,不一定准确,如果读者有异议,欢迎斧正。