如何在Flutter应用程序中正确设置redirect_uri?

Eig*_*ice 2 android google-oauth flutter

我正在尝试从我的 Flutter 应用程序将用户登录到 Google API,但无法让它自动获取令牌。我得到的最接近的是在身份验证屏幕中看到令牌字符串,并被要求将其复制/粘贴回应用程序中。我怀疑这与redirect_uri参数有关。

我尝试使用oauth2_clientflutter_appauth,结果几乎相同。设置客户端时,如果我使用 Google 提供的第一个 redirect_uri urn:ietf:wg:oauth:2.0:oob,在授予权限后,它会在身份验证屏幕中显示令牌,并指示用户复制它并将其粘贴回应用程序中。如果我使用在 AndroidManifest.xml 和 build.gradle 中设置的 uri,而不是同意屏幕,我会在浏览器中收到以下消息:

“redirect_url 的参数值无效:缺少方案:ai.autonet.afterme”

最后,如果我使用"http://localhost"(Google 提供的第二个 uri),我会收到“请求超时”。

我的 Google 客户端配置如下所示:

"client_id":"somethingsomething.apps.googleusercontent.com","project_id":"afterme-850af","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]
Run Code Online (Sandbox Code Playgroud)

这是 flutter_appauth 实现的最简单版本:

main.dart
import 'package:flutter/material.dart';
    import 'package:flutter_appauth/flutter_appauth.dart';
    import 'package:oauth2_client/access_token_response.dart';
    import 'package:http/http.dart' as http;

    const AUTH_ENDIPOINT = 'https://accounts.google.com/o/oauth2/auth';
    const CLIENT_ID =
        'somethingsomething.apps.googleusercontent.com';
    const REDIRECT_URI = 'ai.autonet.afterme';
    const TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
    var scopes = [
        "https://www.googleapis.com/auth/youtube",
        "https://www.googleapis.com/auth/youtube.upload",
    ];
    void main() {
        runApp(MyApp());
    }

    var httpClient = http.Client();

    class MyApp extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
        return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
            ),
            home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
        }
    }

    class MyHomePage extends StatefulWidget {
        MyHomePage({Key key, this.title}) : super(key: key);

        final String title;
        FlutterAppAuth appAuth = FlutterAppAuth();

        authorize() async {
        final AuthorizationTokenResponse result =
            await appAuth.authorizeAndExchangeCode(AuthorizationTokenRequest(
                CLIENT_ID, REDIRECT_URI,
                serviceConfiguration: AuthorizationServiceConfiguration(
                    AUTH_ENDPOINT,
                    TOKEN_ENDPOINT),
                scopes: scopes));
        print(result.accessToken.toString());
        }

        @override
        _MyHomePageState createState() => _MyHomePageState();
    }

    class _MyHomePageState extends State<MyHomePage> {
        getResources() async {
        http.Response resp = await httpClient
            .get('GET https://www.googleapis.com/youtube/v3/videos');
        print(resp.body);
        }


        @override
        Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
            title: Text(widget.title),
            ),
            body: Center(
            child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                
                RaisedButton(
                    child: Text("Permission"), onPressed: () => widget.authorize()),
                ],
            ),
            ),
            
        );
        }
    }
----------------------------------------------------------------
AndroidManifest.xml:
        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="ai.autonet.afterme">
            
            <!-- io.flutter.app.FlutterApplication is an android.app.Application that
                calls FlutterMain.startInitialization(this); in its onCreate method.
                In most cases you can leave this as-is, but you if you want to provide
                additional functionality it is fine to subclass or reimplement
                FlutterApplication and put your custom class here. -->
            <application
                android:name="io.flutter.app.FlutterApplication"
                android:label="afterme"
                android:icon="@mipmap/ic_launcher">
                <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="ai.autonet.afterme" />
                </intent-filter>
            </activity>
                <activity
                    android:name=".MainActivity"
                    android:launchMode="singleTop"
                    android:theme="@style/LaunchTheme"
                    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
                    android:hardwareAccelerated="true"
                    android:windowSoftInputMode="adjustResize">
                    <!-- Specifies an Android theme to apply to this Activity as soon as
                        the Android process has started. This theme is visible to the user
                        while the Flutter UI initializes. After that, this theme continues
                        to determine the Window background behind the Flutter UI. -->
                    <meta-data
                    android:name="io.flutter.embedding.android.NormalTheme"
                    android:resource="@style/NormalTheme"
                    />
                    <!-- Displays an Android View that continues showing the launch screen
                        Drawable until Flutter paints its first frame, then this splash
                        screen fades out. A splash screen is useful to avoid any visual
                        gap between the end of Android's launch screen and the painting of
                        Flutter's first frame. -->
                    <meta-data
                    android:name="io.flutter.embedding.android.SplashScreenDrawable"
                    android:resource="@drawable/launch_background"
                    />
                    <intent-filter>
                        <action android:name="android.intent.action.MAIN"/>
                        <category android:name="android.intent.category.LAUNCHER"/>
                    </intent-filter>
                </activity>
                <!-- Don't delete the meta-data below.
                    This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
                <meta-data
                    android:name="flutterEmbedding"
                    android:value="2" />
            </application>
        </manifest>
-------------------------------------------------------------------------------------
build.gradle:
  ...
    defaultConfig {
        applicationId "ai.autonet.afterme"
        minSdkVersion 18
        targetSdkVersion 29
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        manifestPlaceholders = [
        'appAuthRedirectScheme': 'ai.autonet.afterme'
        ]
    }
    ...
Run Code Online (Sandbox Code Playgroud)

任何帮助都会受到重视。

Ted*_*ddy 6

iOS和Android的flutter中设置redirect_uri的正确方法如下:

  1. 第一步 Android - 在 android/app/build.gradle 下

    defaultConfig {
     applicationId "com.testingapp" // Set the applicationId
     minSdkVersion 18
     targetSdkVersion 30
     versionCode flutterVersionCode.toInteger()
     versionName flutterVersionName
     manifestPlaceholders = [
             'appAuthRedirectScheme': 'com.testingapp' // Add this also using your application ID URI as the based for all your derived URi
     ]
    }
    
    Run Code Online (Sandbox Code Playgroud)

iOS - ios/Runner/Info.plist

<dict>
    <key>CFBundleURLTypes</key>
      <array>
          <dict>
             <key>CFBundleTypeRole</key>
             <string>Editor</string>
         <key>CFBundleURLSchemes</key>
         <array>
            <string>com.testingapp</string> //Again Application Id should be added here
         </array>
      </dict>
   </array>

...
Run Code Online (Sandbox Code Playgroud)

然后在您的应用程序中您可以按如下方式使用它:

const AUTH0_REDIRECT_URI = "com.testingapp://login-callback";
Run Code Online (Sandbox Code Playgroud)

请注意,“://login-callback”已添加到此处的应用程序 ID,它未在其他任何地方定义。

最后在您的提供程序中也添加它。就我而言,我使用的是 keycloak。

在此输入图像描述

  • @arrmani88 在“android/app/build.gradle”中修复“manifestPlaceholders”的问题,使用“+=”而不是“=” (3认同)

Cha*_*ska 5

您是否尝试过添加://到计划的末尾?我不确定您是否向 Google 提供了有效的方案名称。

你可以尝试一下ai.autonet.afterme://还是ai.autonet.afterme://auth