对 'My Anime List' 的 OAuth2 授权无效
OAuth2 authorization to 'My Anime List' not working
我正在尝试使用 Oauth2(遵循此 guide)为我的 Android 应用程序验证“我的动漫列表”用户。
第 1 步: 获取授权令牌
在这里,我使用 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
。
步骤 2: 获取用户访问令牌和刷新令牌
这里,我使用 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[=13=](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 工作室资料视图):
这是回复:
编辑 2:
将代码 challenge/verifier 硬编码到 128 次 'A' (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
) 也不会改变行为:
当您在授权请求中包含 redirect_uri 时,您还需要将其包含在 /token 请求中。也许不是那样。
我正在尝试使用 Oauth2(遵循此 guide)为我的 Android 应用程序验证“我的动漫列表”用户。
第 1 步: 获取授权令牌
在这里,我使用 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
。
步骤 2: 获取用户访问令牌和刷新令牌
这里,我使用 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[=13=](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 工作室资料视图):
这是回复:
编辑 2:
将代码 challenge/verifier 硬编码到 128 次 'A' (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
) 也不会改变行为:
当您在授权请求中包含 redirect_uri 时,您还需要将其包含在 /token 请求中。也许不是那样。