Flutter OAuth2认证插件angel3_auth_oauth2的使用

Flutter OAuth2认证插件angel3_auth_oauth2的使用

angel3_auth_oauth2 是一个用于通过 OAuth2 认证远程身份提供商(如 Facebook、Google、Azure AD 等)的库。下面是详细的使用步骤和示例代码。

使用

首先,创建一个选项对象:

configureServer(Angel app) async {
  // 加载从 Map 中的数据,例如应用配置:
  var opts = ExternalAuthOptions.fromMap(app.configuration['auth0'] as Map);

  // 在现场创建:
  var opts = ExternalAuthOptions(
      clientId: '<client-id>',
      clientSecret: '<client-secret>',
      redirectUri: Uri.parse('<callback>'));
}

在成功认证后,我们需要在自己的应用程序中识别用户:

typedef OAuth2Verifier = FutureOr<User> Function(oauth2.Client, RequestContext, ResponseContext);

/// 您可以使用纯函数来创建一个查询给定服务的验证器。
OAuth2Verifier oauth2verifier(Service<User> userService) {
  return (client) async {
    var response = await client.get('https://api.github.com/user');
    var ghUser = json.decode(response.body);
    var id = ghUser['id'] as int;

    var matchingUsers = await mappedUserService.index({
      'query': {'github_id': id}
    });

    if (matchingUsers.isNotEmpty) {
      // 返回相应的用户,如果存在的话。
      return matchingUsers.first;
    } else {
      // 否则,创建一个新用户
      return await mappedUserService.create(User(githubId: id));
    }
  };
}

现在,初始化一个 OAuth2Strategy,使用选项和验证器。你还需要为该策略实例提供一个名称。可以考虑使用远程认证提供者的名称(例如 facebook)。

configureServer(Angel app) {
  auth.strategies['github'] = OAuth2Strategy(
    options,
    authorizationEndpoint,
    tokenEndpoint,
    yourVerifier,

    // 当发生错误或用户拒绝请求时调用此函数。
    (e, req, res) async {
      res.write('Ooops: $e');
      await res.close();
    },
  );
}

最后,将其连接到一个 AngelAuth 实例,并将其连接到一个 Angel 服务器。设置两个路由:

  1. 重定向用户到外部提供者。
  2. 作为回调并处理访问代码。

对于回调路由,您可能希望显示一个关闭弹出窗口的 HTML 页面。在这种情况下,使用 confirmPopupAuthentication,它是 package:angel3_auth 的一部分,作为 callback 函数:

configureServer(Angel app) async {
  // ...
  var auth = AngelAuth<User>();
  auth.strategies['github'] = oauth2Strategy;

  // 重定向
  app.get('/auth/github', auth.authenticate('github'));

  // 回调
  app.get('/auth/github/callback', auth.authenticate(
    'github',
    AngelAuthOptions(callback: confirmPopupAuthentication())
  ));

  // 连接插件!!!
  await app.configure(auth);
}

自定义作用域分隔符

这个包应该对大多数 OAuth2 提供商(如 Github 或 Dropbox)开箱即用。但是,如果您的 OAuth2 作用域由除默认值(' ')之外的其他分隔符分隔,则可以在 OAuth2Strategy 构造函数中添加它:

configureServer(Angel app) async {
  OAuth2Strategy(..., delimiter: ' ');
}

处理非 JSON 响应

许多 OAuth2 提供商没有遵循规范,并且不会返回 application/json 响应。

您可以添加一个 getParameters 回调来解析任意响应的内容:

OAuth2Strategy(
    // ...
    getParameters: (contentType, body) {
      if (contentType.type == 'application') {
        if (contentType.subtype == 'x-www-form-urlencoded')
          return Uri.splitQueryString(body);
        else if (contentType.subtype == 'json') return JSON.decode(body);
      }

      throw FormatException('Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
    }
);

示例代码

import 'dart:convert';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
import 'package:angel3_auth_oauth2/angel3_auth_oauth2.dart';
import 'package:http_parser/http_parser.dart';
import 'package:logging/logging.dart';

var authorizationEndpoint =
    Uri.parse('http://github.com/login/oauth/authorize');

var tokenEndpoint = Uri.parse('https://github.com/login/oauth/access_token');

var options = ExternalAuthOptions(
  clientId: '6caeaf5d4c04936ec34f',
  clientSecret: '178360518cf9de4802e2346a4b6ebec525dc4427',
  redirectUri: Uri.parse('http://localhost:3000/auth/github/callback'),
);

/// Github 不正确地遵循 OAuth2 规范,因此这里有一个逻辑来解析他们的响应。
Map<String, dynamic> parseParamsFromGithub(MediaType contentType, String body) {
  if (contentType.type == 'application') {
    if (contentType.subtype == 'x-www-form-urlencoded') {
      return Uri.splitQueryString(body);
    } else if (contentType.subtype == 'json') {
      return (json.decode(body) as Map).cast<String, String>();
    }
  }

  throw FormatException(
      'Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
}

void main() async {
  // 创建服务器实例。
  var app = Angel();
  var http = AngelHttp(app);
  app.logger = Logger('angel')
    ..onRecord.listen((rec) {
      print(rec);
      if (rec.error != null) print(rec.error);
      if (rec.stackTrace != null) print(rec.stackTrace);
    });

  // 创建一个存储用户数据的服务。
  var userService = app.use('/users', MapService()).inner;
  var mappedUserService = userService.map(User.parse, User.serialize);

  // 设置认证插件。
  var auth = AngelAuth<User>(
      serializer: (user) async => user.id ?? '',
      deserializer: (id) => mappedUserService.read(id),
      jwtKey: 'oauth2 example secret',
      allowCookie: false);
  await app.configure(auth.configureServer);

  /// 创建一个策略类的实例。
  auth.strategies['github'] = OAuth2Strategy(
    options,
    authorizationEndpoint,
    tokenEndpoint,

    // 当用户接受请求以通过 Github 登录时调用此函数。
    (client, req, res) async {
      var response = await client.get(Uri.parse('https://api.github.com/user'));
      var ghUser = json.decode(response.body);
      var id = ghUser['id'] as int?;

      var matchingUsers = await mappedUserService.index({
        'query': {'github_id': id}
      });

      if (matchingUsers.isNotEmpty) {
        // 返回相应的用户,如果存在的话。
        return matchingUsers.first;
      } else {
        // 否则,创建一个新用户
        return await mappedUserService.create(User(githubId: id));
      }
    },

    // 当发生错误或用户拒绝请求时调用此函数。
    (e, req, res) async {
      res.write('Ooops: $e');
      await res.close();
    },

    // 当与 Github 交互时必须传递此解析器函数。
    //getParameters: parseParamsFromGithub,
  );

  // 挂载一些路由
  app.get('/auth/github', auth.authenticate('github'));
  app.get(
      '/auth/github/callback',
      auth.authenticate('github',
          AngelAuthOptions(callback: (req, res, jwt) async {
        // 在实际应用中,您可能会包含一个弹出回调脚本。
        //
        // 使用 `confirmPopupAuthentication`,它是 `package:angel_auth` 的一部分。
        var user = req.container!.make<User>();
        res.write('Your user info: ${user.toJson()}\n\n');
        res.write('Your JWT: $jwt');
        await res.close();
      })));

  // 开始监听。
  await http.startServer('127.0.0.1', 3000);
  print('Listening on ${http.uri}');
  print('View user listing: ${http.uri}/users');
  print('Sign in via Github: ${http.uri}/auth/github');
}

class User extends Model {
  int? githubId;

  User({super.id, this.githubId});

  static User parse(Map<String, dynamic> map) =>
      User(id: map['id'] as String?, githubId: map['github_id'] as int?);

  static Map<String, dynamic> serialize(User user) => user.toJson();

  Map<String, dynamic> toJson() => {'id': id, 'github_id': githubId};
}

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

1 回复

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


当然,以下是如何在Flutter项目中使用angel3_auth_oauth2插件进行OAuth2认证的示例代码。需要注意的是,angel3_auth_oauth2是一个Dart包,通常用于Dart后端服务,但如果你需要在Flutter前端处理OAuth2认证,通常会结合Flutter的HTTP客户端和OAuth2库(比如oauth2包)来实现。不过,为了贴近你的要求,我将展示如何使用angel3_auth_oauth2在Dart后端服务中进行OAuth2认证,并通过Flutter前端调用该服务。

后端(Dart/Angel Framework)

首先,确保你的pubspec.yaml文件中包含以下依赖:

dependencies:
  angel_framework: ^4.0.0
  angel3_auth: ^3.0.0
  angel3_auth_oauth2: ^3.0.0

然后,创建一个Dart文件(例如server.dart),并设置OAuth2认证:

import 'package:angel_framework/angel_framework.dart';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:angel3_auth_oauth2/angel3_auth_oauth2.dart';

void main() async {
  var app = Angel();

  // 配置AngelAuth
  var auth = AngelAuth(app);
  auth.serializers!.add(OAuth2Serializer('google'));

  // 配置OAuth2
  var googleOAuth2 = OAuth2Service(
    clientId: 'YOUR_CLIENT_ID',
    clientSecret: 'YOUR_CLIENT_SECRET',
    authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth',
    tokenEndpoint: 'https://www.googleapis.com/oauth2/v4/token',
    redirectUri: 'http://localhost:3000/auth/google/callback',
    scopes: ['email', 'profile'],
  );

  auth.configure('google', googleOAuth2);

  // 处理OAuth2回调
  app.get('/auth/google/callback', (req, res) async {
    var result = await auth.authenticate('google', req);
    if (result is AuthenticatedUser) {
      // 用户已认证
      res.write('Hello, ${result.user!.id}');
    } else {
      // 认证失败
      res.status = 401;
      res.write('Authentication failed');
    }
  });

  // 启动服务器
  await app.listen(3000);
  print('Server listening on port 3000');
}

在这个示例中,我们配置了一个Google OAuth2服务,并设置了回调处理。你需要替换YOUR_CLIENT_IDYOUR_CLIENT_SECRET为你的Google OAuth2客户端ID和密钥。

前端(Flutter)

在Flutter前端,你需要使用HTTP客户端(如http包)来重定向用户到OAuth2认证页面,并处理回调。以下是一个简单的Flutter示例:

首先,在pubspec.yaml中添加http依赖:

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3

然后,创建一个Flutter页面(例如main.dart),并添加以下代码:

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:uri';
import 'dart:convert';

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

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

class OAuth2Page extends StatefulWidget {
  @override
  _OAuth2PageState createState() => _OAuth2PageState();
}

class _OAuth2PageState extends State<OAuth2Page> {
  void _openOAuth2Login() async {
    String clientId = 'YOUR_CLIENT_ID';
    String redirectUri = Uri.encodeFull('http://localhost:3000/auth/google/callback');
    String authUrl = Uri(
      scheme: 'https',
      host: 'accounts.google.com',
      path: '/o/oauth2/auth',
      queryParameters: {
        'response_type': 'code',
        'client_id': clientId,
        'redirect_uri': redirectUri,
        'scope': 'email profile',
        'access_type': 'online',
      },
    ).toString();

    // 在WebView或系统浏览器中打开authUrl
    if (await canLaunch(authUrl)) {
      await launch(authUrl);
    } else {
      throw 'Could not launch $authUrl';
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('OAuth2 Login'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: _openOAuth2Login,
          child: Text('Login with Google'),
        ),
      ),
    );
  }
}

在这个Flutter示例中,我们定义了一个简单的页面,其中包含一个按钮,点击该按钮会打开Google OAuth2认证页面。请注意,这个示例假设用户在认证后会被重定向回http://localhost:3000/auth/google/callback,这在实际应用中可能不太现实,特别是在移动设备上。因此,你可能需要设置一个更合适的回调URL,或者使用其他方法来处理认证回调(例如,使用设备上的自定义URL方案或深度链接)。

注意:在实际应用中,处理OAuth2回调通常涉及更复杂的逻辑,包括处理错误、存储访问令牌、刷新令牌等。此外,由于angel3_auth_oauth2主要用于服务器端,你可能需要在Flutter前端使用专门的OAuth2客户端库(如flutter_oauth2)来处理认证流程。上面的示例主要用于演示基本概念,并可能需要根据你的具体需求进行调整。

回到顶部