Войти на 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);
    }

});```
Другие вопросы по тегам