TakWolf-Deprecated / CNode-Material-Design

CNode 社区第三方 Android 客户端,原生 App,Material Design 风格,支持夜间模式。
https://cnodejs.org
Apache License 2.0
1.34k stars 347 forks source link

客户端实现GitHub直接登录 #37

Closed TakWolf closed 8 years ago

TakWolf commented 8 years ago

问题来源于:https://cnodejs.org/topic/57345764c3e4ef7657ab127b

研究了一下,基本可行,思路如下:

初始化一个WebView,载入CNode登录地址:https://cnodejs.org/auth/github

这句会重定向到:https://github.com/login/oauth/authorize?response_type=code&redirect_uri=http://cnodejs.org/auth/github/callback&client_id=0625d398dd9166a196e9

这是简单模式的Auth2.0,clientId是公开的且没有客户端认证

GitHub登录成功后,携带token回调: https://cnodejs.org/auth/github/callback?code=xxxxxxx

CNode验证成功后,会重定向到首页:https://cnodejs.org/

这时截断这个重定向,取出CNode的session_cookie,用这个session_cookie去抓取设置页面:https://cnodejs.org/setting

解析Dom文档,取出accessToken:

<div class='inner'>
<div><span>字符串:</span>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx</div>
<div>
<span>二维码:</span>
<span id="access-token-qrcode"></span>
</div>

补充:这个只是研究,只为证明Auth登录在客户端可以做,实际不会真正实现到线上环境中。

Auth本身就是为客户端设计的一种授权模式,而web端使用的Auth登录,通常是一种简化版的Auth,移除了签名以及客户端认证,添加了回调来响应通知。(浏览器看成是一种特殊的客户端)

CNode的API没有提供Auth的验证接口,所以抓取页面解析accessToken的做法只是一个hack,不是一个正常的方式!!

正常的方式是,API中提供一个接口,用authToken换 accessToken


补充2:闲着无聊,还是把这个实现了,233

项目地址在:https://github.com/TakWolf/CNode-OAuth-Login-Android

实现节点为:https://github.com/TakWolf/CNode-Material-Design/commit/48e4bb346f7ffc85d582b0ad295196666b8de05b

awong1900 commented 8 years ago

服务器需要增加啥? 我改一版服务器方便你测试。我搭了一个copy在我的服务器上。http://bbs.zuqiuxunlian.com

alsotang commented 8 years ago

别折腾这块,没意义。。。就扫码登陆。

2016-05-16 20:05 GMT+08:00 Ten Wong notifications@github.com:

服务器需要增加啥? 我改一版服务器方便你测试。我搭了一个copy在我的服务器上。bbs.zuqiuxunlian.com

— You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub https://github.com/TakWolf/CNode-Material-Design/issues/37#issuecomment-219409776

awong1900 commented 8 years ago

@alsotang LOL 给点鼓励啊。 如果有时间,可以整一下顺便学习一下。 500px上用facebook登录有相似之处。

TakWolf commented 8 years ago

@awong1900 @alsotang

没事研究,只是想表明这个在客户端上是可以实现的。

这个就是标准的Auth授权流程,没有啥复杂的原理,过程也简单明了。

另外,通过抓取页面解析出accessToken,这根本就不是正常的手段!!这是hack!!就算真的实现了这个代码,也不会放到线上环境中。

awong1900 commented 8 years ago

@TakWolf 恩。 尝试直接用chrome等第三方浏览器打开,然后重定向到app,就不用hack了。

TakWolf commented 8 years ago

@awong1900

在Chrome中授权之后打开APP吗? 是Android还是iOS? 给一个链接研究一下

awong1900 commented 8 years ago

android 和 ios都可以。安装DO Button by IFTTT, 然后有个channel绑定,然后选择github服务。最好翻墙。我用BlueStacks测试。 image image image image

awong1900 commented 8 years ago

还有一个亮点是,如果绑定的服务也是第三方登录,也可以继续用第三方登录。这个可以参考trello的channel服务

TakWolf commented 8 years ago

@awong1900 研究了一下Android,方式是一样的都是Auth2.0,流程都是一样的,授权动作跳转到了浏览器。 最后一步回调成功之后,浏览器返回App用的是私有协议。Android的参考:http://developer.android.com/training/app-indexing/deep-linking.html

用浏览器不用WebView的原因是验证授权重定向全由服务器搞定了,客户端可以省不少事

awong1900 commented 8 years ago

@TakWolf

以上是否可行?

TakWolf commented 8 years ago

@awong1900 实现方式可以有很多种,这里只研究do button它的流程。

反编译do button,在manifest中发现这个配置:

<activity android:name="com.ifttt.dobutton.LinkActivity" android:theme="@style/Theme.IFTTT.Lib">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="dobutton"/> // 私有协议
    </intent-filter>
</activity>

Android打开媒体的Action,如果只有一个接收方式,则会直接调用,如果有两个以上,则会竞争,由用户选择。于是,我们创建一个新的项目,注册一个Activity如下:

 <activity
            android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="dobutton" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="https" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http" />
            </intent-filter>
        </activity>

这样就会跟浏览器类型以及dobutton的私有类型产生竞争,然后我们在oncreate中监听url,就可以跟踪do button具体打开链接了:

public class MainActivity extends AppCompatActivity {

    protected WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webView = (WebView) findViewById(R.id.webview);
        webView.setWebViewClient(new WebViewClient() {

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                Log.d("ttaa", url);
                return false;
            }

        });
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.removeAllCookie();

        String url = getIntent().getDataString();
        if (!TextUtils.isEmpty(url)) {
            Log.d("ttaa", url);
            webView.loadUrl(url);            
        }

    }

}

首先,绑定channel动作(GitHub),由do button打开浏览器,url如下:

https://ifttt.com/channels/github/activate?activation_failure_path=dobutton://url//appviews/channels/2107379463/activate_fail?access_token=xxxxxxxxxxxxxxxx&app_version=2.1&bundle_id=com.ifttt.dobutton&mobile_app_id=&sdk_mode=&system_version=&use_for_profile=false&activation_success_path=dobutton://url//appviews/channels/2107379463/activate_done?access_token=xxxxxxxxxxxxxxxx&app_version=2.1&bundle_id=com.ifttt.dobutton&mobile_app_id=&sdk_mode=&system_version=&use_for_profile=false&is_web_view=1&user_id=123456&expires=1463458515&token=yyyyyyyyyyyyyyyyyyyyy

这个链接是指向ifttt.com的,参数包含用户token,因此对用户进行了鉴权操作,之后,重定向路线:

https://ifttt.com/channel_activation/github/start

https://ifttt.com/channel_activation/github/approve

https://shimmy.ifttt.com/github/oauth2/authorize?client_id=ifttt_production&redirect_uri=https://ifttt.com/channels/github/authorize&response_type=code&scope=ifttt&state=zzzzzzzzzzzzzzzzzzzzzzzzzzzz

// 从这里开始,就是正常的Auth2流程了:

https://github.com/login/oauth/authorize?scope=user,repo,notification,gist&client_id=8eebda59593bc42170e4&response_type=code&redirect_uri=https://ifttt.com/channels/github/authorize&state=zzzzzzzzzzzzzzzzzzzzzzzzzzzz

// 由于我没登录,跳转到github登录
https://github.com/login?return_to=someurl

Auth2.0授权成功之后,url线路图(略去Auth2.0中的部分,只看关键的步骤):

// 这个是Auth2成功回调地址:
https://ifttt.com/channels/github/authorize?code=qqqqqqqqqqqqqqqq&state=zzzzzzzzzzzzzzzzzzzzzzzzzzzz

https://ifttt.com/channels/github/activate_success

// 这一步是关键,使用私有协议打开APP
dobutton://url//appviews/channels/2107379463/activate_done?access_token=xxxxxxxxxxxxxxxx&app_version=2.1&bundle_id=com.ifttt.dobutton&mobile_app_id=&sdk_mode=&system_version=&use_for_profile=false

这里的access_token是最开始的access_token,用户比对一致后鉴权完成,APP开始刷新数据,整个授权完成。

---------------------------- 分隔符 -------------------------------------

实现方式有很多种,原理都是一样的,其实没必要纠结于do button是怎么完成的,自己选择合适的实现方式就可以。

do button的这种模式:

  1. 平台必须支持,也就是Android必须支持私有协议让浏览器启动APP并传参
  2. APP只需要向浏览器发送一个连接,然后等待浏览器回调就可以了,其他完全由服务器搞定。channel有很多,每个可能都有自己的授权方式,这些完全有服务器适配,客户端只关心一个私有协议,依赖也是最小的,节省了非常对的工作量。
  3. API对接上也可以跟iOS保持一致
TakWolf commented 8 years ago

补充一句:我更倾向于使用WebView来完成这个动作,因为chrome浏览器是标准实现,如果你在国内rom上使用,授权链接转到了国产浏览器或者类似QQ内置浏览器就完蛋了,他根本不会给你回调。

在WebView中,你只要拦截私有协议,然后做动作就行,功能上和chrome完全一样,但是更安全。

awong1900 commented 8 years ago

// 这一步是关键,使用私有协议打开APP dobutton://url//appviews/channels/2107379463/activate_done?access_token=xxxxxxxxxxxxxxxx&app_version=2.1&bundle_id=com.ifttt.dobutton&mobile_app_id=&sdk_mode=&system_version=&use_for_profile=false

这个链接是谁发出来的?

所以说https://github.com/login/oauth/authorize 这条API不是直接通过app发出来,而是服务器location过来的?

TakWolf commented 8 years ago

私有协议是浏览器发出了。 浏览器通常可以支持扩展协议,比如迅雷协议:thunder://xxxx Android支持这个特性

dobutton的授权链接是由服务器重定向来的,这样做的原因猜测:

  1. 兼容性,具体适配由服务端做,客户端不需要做适配,只要按照服务端api调用就行,减小工作量。
  2. 在第三方授权之前,他可能想要先进行他自己的用户鉴权
awong1900 commented 8 years ago

那现在梳理回来,如果cnode实现github登录,需要app和服务器端分别修改什么呢?

TakWolf commented 8 years ago

如果不用hack,CNode官方API支持Auth登录,两种方式:

  1. 模仿dobutton,最后一步用,服务器用私有协议打开APP,传回accessToken cnodeclient://auth/github/success?accessToken=xxxxxxxxxx
  2. API增加一个接口: /api/v1/auth?type=github&code=xxxxxx 返回: { success: true, access_token: xxxxx, ...... } 客户端在webview拦截auth授权成功回调地址,截获code参数