Flutter OAuth2认证插件native_oauth2的使用

Flutter OAuth2认证插件native_oauth2的使用

native_oauth2

一个用于通过原生平台API使用OAuth 2.0进行身份验证的Flutter插件。

该包为在iOS(使用SFAuthenticationSession)和Android(使用Chrome Custom Tabs)上与OAuth 2.0提供程序进行身份验证提供了简单的接口。


安装

pubspec.yaml文件中添加native_oauth2作为依赖项:

dependencies:
  native_oauth2: ^0.1.1

然后运行以下命令以安装:

flutter pub get

Android设置

AndroidManifest.xml文件中的<activity>标签内添加意图过滤器:

<intent-filter>
    <action android:name="android.intent.action.VIEW"/>

    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>

    <data
            android:scheme="custom-scheme"
            android:host="custom.host"
            android:path="/custom/path"/>
</intent-filter>

iOS设置

无需额外设置。


Web设置

当作为Web应用程序运行时,此插件提供两种模式:同一选项卡模式和弹出窗口模式。

同一选项卡模式

身份验证URL将在应用程序的同一选项卡中打开。不幸的是,这会导致所有应用程序状态丢失。为了使用此模式,您应该检查全局变量nativeOAuth2SameTabAuthResult以查找重定向(见示例)。如果您使用PKCE,则应在运行authenticate之前将代码验证器持久化到SessionStorage。

弹出窗口模式

身份验证URL将在弹出窗口中打开。一旦身份验证完成,重定向必须由静态HTML页面处理,并向源窗口发送消息。您应该创建一个像下面这样的HTML文件,并将其放在web/目录的根目录下,然后设置您的重定向URI指向该页面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Logged In</title>
</head>
<body>
<h1>You have been logged in. You will be redirected shortly</h1>

<script>
    // 这是重要部分
    // 您也可以在setTimeout中运行此代码,使用户感觉过程不那么突然
    // 消息必须有一个名为redirect的属性,其值应为window.location.href
    window.opener.postMessage({redirect: window.location.href}, window.location.origin)
    window.close()
</script>
</body>
</html>

使用方法

使用任意OAuth 2.0提供程序进行身份验证:

import 'package:native_oauth2/native_oauth2.dart';

void login() async {
  final plugin = NativeOAuth2();

  final provider = OAuthProvider(
    authUrlAuthority: authority,
    authUrlPath: path,
    clientId: clientId,
  );

  final result = await plugin.authenticate(
    provider: provider,
    redirectUri: Uri.parse('custom-scheme://custom.host/custom/path'),
    scope: ['openid', 'Some.Other.Scope'],
  );
}

示例代码

以下是完整的示例代码:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:native_oauth2/native_oauth2.dart';
import 'package:pkce/pkce.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MainPage(),
    );
  }
}

class MainPage extends StatefulWidget {
  const MainPage({Key? key}) : super(key: key);

  [@override](/user/override)
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  final _nativeOAuth2Plugin = NativeOAuth2();

  bool loading = false;

  final authority = '...'; // 替换为实际值
  final path = '...'; // 替换为实际值
  final clientId = '...'; // 替换为实际值
  final redirectUri = Uri.parse('...'); // 替换为实际值
  final scope = ['openid']; // 替换为实际值

  [@override](/user/override)
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      if (kIsWeb) {
        final sameTabAuthentication = nativeOAuth2SameTabAuthResult;
        final redirect = sameTabAuthentication.redirect;
        // 检查sameTabAuthentication的redirect是否匹配redirectUri
        if (redirect.toString().startsWith(redirectUri.toString())) {
          showSimpleDialog(sameTabAuthentication);
        }
      }
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Native OAuth2 Example App'),
      ),
      body: Center(
        child: Builder(
          builder: (context) {
            if (loading) {
              return const CircularProgressIndicator();
            } else {
              return ElevatedButton(
                onPressed: () => login(context),
                child: const Text('LOGIN'),
              );
            }
          },
        ),
      ),
    );
  }

  void login(BuildContext context) async {
    final provider = OAuthProvider(
      authUrlAuthority: authority,
      authUrlPath: path,
      clientId: clientId,
    );

    final pkcePair = PkcePair.generate();

    try {
      setState(() {
        loading = true;
      });

      final response = await _nativeOAuth2Plugin.authenticate(
          provider: provider,
          redirectUri: redirectUri,
          scope: scope,
          codeChallenge: pkcePair.codeChallenge,
          codeChallengeMethod: 'S256',
          prompt: 'select_account',
          webMode: const WebAuthenticationMode.sameTab());

      if (!mounted) return;

      showSimpleDialog(response);
    } finally {
      setState(() {
        loading = false;
      });
    }
  }

  void showSimpleDialog(Object? obj) {
    showDialog(
      context: context,
      builder: (_) => AlertDialog(
        content: Text(obj.toString()),
      ),
    );
  }
}

更多关于Flutter OAuth2认证插件native_oauth2的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter OAuth2认证插件native_oauth2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,native_oauth2 是一个用于处理OAuth2认证的插件。它允许你使用原生平台(如Android和iOS)的OAuth2库来进行认证,从而提供更好的用户体验和安全性。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 native_oauth2 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  native_oauth2: ^0.1.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 配置OAuth2客户端

在使用 native_oauth2 之前,你需要配置OAuth2客户端。通常,你需要提供以下信息:

  • clientId: 你的OAuth2客户端的ID。
  • clientSecret: 你的OAuth2客户端的密钥(可选,取决于OAuth2提供者)。
  • redirectUri: 重定向URI,用于接收授权码。
  • authorizationEndpoint: 授权端点的URL。
  • tokenEndpoint: 令牌端点的URL。
  • scopes: 请求的权限范围。

3. 使用 native_oauth2 进行认证

以下是一个简单的示例,展示如何使用 native_oauth2 进行OAuth2认证:

import 'package:flutter/material.dart';
import 'package:native_oauth2/native_oauth2.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: OAuth2Example(),
    );
  }
}

class OAuth2Example extends StatefulWidget {
  @override
  _OAuth2ExampleState createState() => _OAuth2ExampleState();
}

class _OAuth2ExampleState extends State<OAuth2Example> {
  final OAuth2Client _oauth2Client = OAuth2Client(
    clientId: 'your_client_id',
    clientSecret: 'your_client_secret', // 可选
    redirectUri: 'your_redirect_uri',
    authorizationEndpoint: 'https://example.com/oauth/authorize',
    tokenEndpoint: 'https://example.com/oauth/token',
    scopes: ['read', 'write'],
  );

  String _accessToken = '';

  Future<void> _authenticate() async {
    try {
      final OAuth2Response response = await _oauth2Client.authenticate();
      setState(() {
        _accessToken = response.accessToken;
      });
    } catch (e) {
      print('Authentication failed: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('OAuth2 Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            if (_accessToken.isNotEmpty)
              Text('Access Token: $_accessToken')
            else
              Text('Not authenticated'),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _authenticate,
              child: Text('Authenticate'),
            ),
          ],
        ),
      ),
    );
  }
}

4. 处理重定向URI

在Android和iOS上,你需要配置应用来处理重定向URI。

Android

android/app/src/main/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="your_redirect_uri_scheme" />
    </intent-filter>
</activity>

iOS

ios/Runner/Info.plist 文件中,添加以下内容:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>your_redirect_uri_scheme</string>
        </array>
    </dict>
</array>

5. 处理令牌刷新

如果令牌过期,你可能需要刷新令牌。native_oauth2 提供了 refreshToken 方法来处理令牌刷新:

Future<void> _refreshToken() async {
  try {
    final OAuth2Response response = await _oauth2Client.refreshToken();
    setState(() {
      _accessToken = response.accessToken;
    });
  } catch (e) {
    print('Token refresh failed: $e');
  }
}

6. 处理注销

你可以使用 logout 方法来注销用户:

Future<void> _logout() async {
  await _oauth2Client.logout();
  setState(() {
    _accessToken = '';
  });
}
回到顶部