代码之家  ›  专栏  ›  技术社区  ›  Florian Baierl

OAuth2对我动画列表的授权无效

  •  0
  • Florian Baierl  · 技术社区  · 5 年前

    我正在尝试使用Oauth2对“我的动画列表”用户进行身份验证(如下所示) guide )用于我的Android应用程序。

    第一步: 获取授权令牌

    在这里,我使用WebView提示用户输入用户名和密码。就我所知,这一步似乎奏效了。

    private static final String REDIRECT_URL = "http://localhost/oauth";
    private static final String CLIENT_ID = "9c..."; // omitted
    private static final String OAUTH_BASE_URL = "https://myanimelist.net/v1/oauth2/";
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
    
        /*
         * Before you can authenticate a user, your client needs to generate a Code Verifier and a
         * Code Challenge. A Code Verifier is a high-entropy, cryptographic, random string
         * containing only the characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~".
         * The length of the string must be between 43 and 128 characters.
         *
         * MAL only allows the plain transformation for the Code Challenge.
         * In other words, it means that you have to set the Code Challenge equal to the
         * Code Verifier.
         */
        String codeChallenge = PKCEGenerator.generateVerifier(128);
        
        webview = findViewById(R.id.login_webview);
        webview.getSettings().setJavaScriptEnabled(true);
        webview.setWebViewClient(new WebViewClient(){
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request){
                Log.d(TAG, "Redirecting to: " + request.getUrl());
                Uri url = request.getUrl();
                if(url.toString().contains(REDIRECT_URL)){
                    String authorizationCode = url.getQueryParameter("code");
                    Log.d(TAG, "Received authorization code: " + authorizationCode);
                    webview.setVisibility(View.GONE);
                    getUserAccessToken(authorizationCode, codeChallenge);
                }
                return false; 
            }
        });
        authenticateMAL(codeChallenge);
    }
    
    private void authenticateMAL(String codeChallenge) {
        Log.d(TAG, "Code challenge (" + codeChallenge.length() + "): " + codeChallenge);
        String loginUrl = OAUTH_BASE_URL + "authorize" +
                "?response_type=code" +
                "&redirect_uri=" + REDIRECT_URL +
                "&client_id=" + CLIENT_ID +
                "&code_challenge=" + codeChallenge;
        Log.d(TAG, "Login url: " + loginUrl);
        webview.loadUrl(loginUrl);
    }
    

    据我所知,这很有效。我拿到了 authorizationCode 正如所料。

    第二步: 获取用户访问令牌&刷新令牌

    这里,我正在使用 Mal4J 对于下一个身份验证步骤:

    private void getUserAccessToken(String authorizationCode, String codeChallenge) {
    
        Single.fromCallable(() -> {
            MyAnimeListAuthenticator authenticator = new MyAnimeListAuthenticator(
                    CLIENT_ID, null, authorizationCode, codeChallenge);
            return authenticator.getAccessToken();
        })
            .subscribeOn(Schedulers.io())
            .doOnError(throwable -> {
                Log.e(TAG, "Error while retrieving token!", throwable);
            })
            .onErrorComplete()
            .subscribe(token -> {
                Log.d(TAG, "--> access token: " + token.getToken());
                Log.d(TAG, "--> refresh token: " + token.getRefreshToken());
            });
    
    }
    

    不幸的是,这会导致以下错误:

    E/LoginActivity: Error while retrieving token!
        com.kttdevelopment.mal4j.HttpException: Server returned code 400 from 'https://myanimelist.net/v1/oauth2/token': 
            at com.kttdevelopment.mal4j.MyAnimeListAuthenticator.parseToken(MyAnimeListAuthenticator.java:505)
            at com.kttdevelopment.mal4j.MyAnimeListAuthenticator.<init>(MyAnimeListAuthenticator.java:139)
            at florian.baierl.daily_anime_news.ui.LoginActivity.lambda$getUserAccessToken$0(LoginActivity.java:99)
            at florian.baierl.daily_anime_news.ui.-$$Lambda$LoginActivity$-bBBIb9OKRzdaFNsFkQdJSeVW74.call(Unknown Source:4)
            at io.reactivex.rxjava3.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:43)
            at io.reactivex.rxjava3.core.Single.subscribe(Single.java:4813)
            at io.reactivex.rxjava3.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
            at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:614)
            at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65)
            at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56)
            at java.util.concurrent.FutureTask.run(FutureTask.java:266)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
            at java.lang.Thread.run(Thread.java:923)
    

    你知道为什么会这样吗?我是不是错过了Oauth2中一些Android特有的东西?就我所见,我正确地从步骤1中检索到了身份验证代码。在那之后,我的代码似乎非常直截了当,所以我看不出错误可能在哪里。任何提示都将不胜感激!

    编辑:

    这是请求的样子(来自android studio的概要视图):

    enter image description here

    以下是答复:

    enter image description here

    编辑2:

    将代码质询/验证程序硬编码为128次“A”( AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA )也不会改变行为:

    0 回复  |  直到 5 年前
        1
  •  1
  •   Kaankom    5 年前

    在授权请求中包含重定向uri时,还需要将其包含在/token请求中。也许不是这样。