Войти на Flutter oAuth2 с разногласиями. Ошибка с URI перенаправления
Я хочу сделать кнопку входа в систему с дискордом. Для этого я использую flutter_web_auth, но Discord показывает мне ошибку с URI перенаправления.
Недействительный OAuth2 redirect_uri
URI перенаправления не поддерживается клиентом
Я настроил flutter_web_auth по запросу:
AndroidManifest.xml
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.area" />
</intent-filter>
</activity>
функция
void loginWithDiscord() async {
// App specific variables
const clientId = 'myClientId' ;
const callbackUrlScheme = 'com.area';
const redirectUri = 'com.area://home'; // OR 'com.area:/';
// Construct the url
final url = Uri.https('discord.com', '/api/oauth2/authorize', {
'response_type': 'code',
'client_id': clientId,
'redirect_uri': redirectUri,
'scope': 'identify',
});
// Present the dialog to the user
final result = await FlutterWebAuth.authenticate(
url: url.toString(), callbackUrlScheme: callbackUrlScheme);
// Extract code from resulting url
final code = Uri.parse(result).queryParameters['code'];
// Use this code to get an access token
final response = await http
.post(Uri.parse('https://discord.com/api/oauth2/authorize'), body: {
'client_id': clientId,
'redirect_uri': redirectUri,
'grant_type': 'authorization_code',
'code': code,
});
// Get the access token from the response
final accessToken = jsonDecode(response.body)['access_token'] as String;
print(accessToken);
}
1 ответ
Ваша проблема аналогична этой (https://github.com/discord/discord-api-docs/issues/5106):
Discord OAuth2 с мобильным телефоном требует PCKE (ключ подтверждения для обмена кодом): https://datatracker.ietf.org/doc/html/rfc7636
Proof Key for Code Exchange — это расширение потока кода авторизации для предотвращения атак CSRF и внедрения кода авторизации. Этот метод предполагает, что клиент сначала создает секрет для каждого запроса авторизации, а затем снова использует этот секрет при обмене кода авторизации на токен доступа. Таким образом, если код будет перехвачен, он не будет полезен, поскольку запрос токена зависит от исходного секрета. (https://www.oauth.com/oauth2-servers/pkce/)
В вашем случае вам необходимо настроить a и a , которые будут отправлены в запросе на авторизацию с методом вызова кода.
После того, как вы получитеauthorization_code
, вы отправите запрос на конечную точку токена, вам нужно использовать в этот момент.
Пример :
code_verifier = высокоэнтропийная криптографическая случайная STRING с использованием незарезервированных символов [AZ] / [az] / [0-9] / "-" / "." / "_" / "~" из раздела 2.3 [RFC3986], с минимальной длиной 43 символа и максимальной длиной 128 символов.
метод создания
String generateCodeVerifier() {
const String _charset =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
return List.generate(
128, (i) => _charset[Random.secure().nextInt(_charset.length)]).join();
}
После создания , вам нужно сгенерироватьcode_challenge
изcode_verifier
.
String generateCodeChallenge(String codeVerifier) {
var bytes = ascii.encode(codeVerifier);
var digest = sha256.convert(bytes);
String codeChallenge = base64Url
.encode(digest.bytes)
.replaceAll("=", "")
.replaceAll("+", "-")
.replaceAll("/", "_");
return codeChallenge;
}
Метод S256 вычисляет хеш SHA-256 входных данных, а затем кодирует хеш-значение, используя Base64-URL. Тогда для этого примераcode_challenge_method
являетсяS256
.
Теперь вы готовы использовать Discord OAuth2:
final _clientId = "CLIENT_ID";
final _clientSecret = "CLIENT_SECRET";
final _redirectUrl = "com.area://home"; // The one you set on the Discord Portal Developer
final _customUriScheme = "com.area";
late String _codeVerifier;
late String _codeChallenge;
@override
void initState() {
super.initState();
_codeVerifier = generateCodeVerifier();
_codeChallenge = generateCodeChallenge(_codeVerifier);
}
void _loginWithDiscord() async {
// First, get the authorization code, you need to use the code_challenge and code_challenge_method at this moment
final url = Uri.https('discordapp.com', '/api/oauth2/authorize', {
'response_type': 'code',
"redirect_uri": _redirectUrl,
'client_id': _clientId,
'grant_type': 'authorization_code',
'scope': 'identify',
'code_challenge': _codeChallenge,
'code_challenge_method': 'S256',
final result = await FlutterWebAuth.authenticate(
url: url.toString(), callbackUrlScheme: _customUriScheme);
// get the code
var code = result.split("code=")[2].split("&")[0];
// Now that you have the authorization_code, get the TOKEN with a post request, you need to use the code_verifier at this moment
final url2 = Uri.https('discordapp.com', '/api/oauth2/token');
final response = await http.post(
url2,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": _redirectUrl,
"client_id": _clientId,
"client_secret": _clientSecret,
"code_verifier": _codeVerifier
},
);
// if the request is a sucess, get the access token :
if(response.statusCode == 200) {
var jsonResponse = convert.jsonDecode(response.body) as Map<String, dynamic>;
final accessToken = jsonResponse["access_token"];
// your code
} else {
// if the request failed
print(response.statusCode);
print(response.body);
}
});```