搜索插件、代码仓与 APP 分类

插件详情v0.1.3

aad_b2c_webview

一个简单的 Flutter 包,用于嵌入 WebView 以访问 Azure AD B2C 用户流终结点。

近 30 天下载
694
喜欢数
25
Pub 分数
110

安装插件

flutter pub add aad_b2c_webview

插件介绍

基于插件公开信息、社区数据与项目使用情况整理的选型参考。

一个 Flutter 包,通过嵌入网页视图来访问用户流端点,简化与 Azure AD B2C 的集成。它支持在应用内直接实现无缝的身份验证流程,如注册、登录和密码重置。专为使用 Microsoft 身份平台的开发者设计,通过原生网页视图处理 B2C 策略的 UI 渲染,减少自定义身份验证逻辑的需求。该包轻量且专注于为 Flutter 应用与 Azure AD B2C 服务之间提供简单直接的桥梁。

适合场景

  • 集成 Azure AD B2C 登录流程
  • 通过B2C策略启用用户注册
  • 支持密码重置工作流
  • 在移动应用中嵌入B2C用户界面
  • 处理自定义身份验证流程

主要特点

  • 简单的网页视图集成
  • 直接访问B2C用户流程
  • 所需设置最少
  • 安全的令牌处理
  • 专为 Microsoft 身份验证生态系统打造

适合对象

  • 使用 Azure AD B2C 的 Flutter 应用
  • 需要安全认证的移动应用
  • 熟悉B2C政策的开发者
  • 构建企业级应用的团队
  • 需要最少自定义身份验证代码的项目

使用建议

  • 需要有效的 Azure AD B2C 配置
  • 仅限基于网页视图的流程
  • 无内置的令牌持久化
  • 依赖外部B2C策略URL
  • 不适用于完全原生UI身份验证

原始 README

以下为关联源码仓库的 README 原文快照,最新内容请访问源码仓库。

Azure AD B2C Embedded Webview

Azure AD B2C Embedded Webview is a very simple Flutter package that demonstrates how to use the embedded web view to sign in users with Azure AD B2C. Currently, using Flutter packages - appAuth and flutter_azure_b2c redirects to browser and doesn't provide in-app experience.

This package embeds the web view of the user flow endpoint using flutter_appview and redirects the user as per onRedirect callback method.

📚 Features

  • Login Button
  • Webview Interaction
  • Flutter UI Customization
  • Dynamic Flutter Layout
  • Refresh Token

🚀 Getting started

To use the package in your Flutter app, add the following code to your main.dart file:

dependencies:
  aad_b2c_webview: <latest_version>

🛠️ Usage

Parameters

It is recommended to use env to save the keys securely

const aadB2CClientID = "<app-id>";
const aadB2CRedirectURL = "https://jwt.ms";
const aadB2CUserFlowName = "B2C_1_APPNAME_Signin";
const aadB2CScopes = ['openid', 'offline_access'];
const aadB2CTenantName = "<tenantName>";
const aadB2CUserAuthFlow = "https://$aadB2CTenantName.b2clogin.com/$aadB2CTenantName.onmicrosoft.com";
Where to find?
Configure parameters in the code
B2CWebViewParams params = B2CWebViewParams(
  responseType: Constants.defaultResponseType,
  tenantBaseUrl: aadB2CUserAuthFlow,
  clientId: aadB2CClientID,
  userFlowName: aadB2CUserFlowName,
  redirectUrl: aadB2CRedirectURL,
  scopes: aadB2CScopes,
  containsChallenge: true,
  isLoginFlow: true,
);
Send Optional Parameters

You have the flexibility to include optional parameters in order to customize the URL according to your specific requirements. The optionalParameters parameter allows you to pass a list of OptionalParam objects, where each object consists of a key and a value that represent the parameter name and its corresponding value, respectively.

Here's an example of how you can utilize the optionalParameters parameter to add new parameters to the URL:

optionalParameters: [
  OptionalParam(key: "parameter1", value: "value1"),
  OptionalParam(key: "parameter2", value: "value2"),
],

In the above example, we include two optional parameters: "parameter1" with the value "value1" and "parameter2" with the value "value2". You can include multiple OptionalParam objects within the list to incorporate multiple optional parameters in the URL.

These optional parameters provide you with the ability to customize the behavior of the URL and conveniently transmit additional information as per your needs.

Login Button

To add the easy to use sign in with microsoft button simply use the AADB2CBase.button widget and a beautiful sign in button appears as shown below.

Configuration

Add the following /android/app/src/main/AndroidManifest.xml inside the tags:

<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="myurl.com" />
</intent-filter>
  • Add it to the redirect URL in the Azure AD B2C project. If you prefer, you can change the redirect URL in our Flutter code to yours (change it in the Azure AD B2C portal too)
Example
import 'package:aad_b2c_webview/aad_b2c_webview.dart';
import 'package:flutter/material.dart';

class B2CWithButton extends StatelessWidget {
  final B2CWebViewParams params;
  const B2CWithButton({super.key, required this.params});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text(
                'Azure B2C Button',
                style: TextStyle(
                  fontSize: 24.0,
                  fontWeight: FontWeight.bold,
                ),
              ),
              const SizedBox(height: 16.0),
              AADB2CBase.button(
                params: params,
                settings: ButtonSettingsEntity(
                  onError: _onError,
                  onSuccess: _onSuccess,
                  onKeepLoading: _onKeepLoading,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  _onSuccess(
      BuildContext context,
      accessToken,
      idToken,
      refreshToken,
      ) {
    var snackBar = const SnackBar(
      content: Text('Successfully Authenticated!'),
      backgroundColor: Colors.green,
    );
    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  }

  _onError(BuildContext context, String? error) {
    var snackBar = SnackBar(
      content: Text(error ?? ''),
      backgroundColor: Colors.redAccent,
    );
    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  }

  bool _onKeepLoading(String? url) =>
      url?.startsWith(params.redirectUrl) ?? false;
}

Click here to access the full example

Webview Interaction

Custom build sign in button to call web view page

Example
import 'dart:developer';
import 'package:aad_b2c_webview/aad_b2c_webview.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'widgets/button_widget.dart';

class B2CWithWebView extends StatefulWidget {
  final B2CWebViewParams params;
  const B2CWithWebView({super.key, required this.params});

  @override
  State<B2CWithWebView> createState() => _B2CWithWebViewState();
}

class _B2CWithWebViewState extends State<B2CWithWebView> {
  ActionController? _controller;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      resizeToAvoidBottomInset: false,
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Center(
                child: Text(
                  'Azure B2C WebView',
                  style: TextStyle(
                    fontSize: 24.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
              const SizedBox(height: 16.0),
              SizedBox(
                height: MediaQuery.sizeOf(context).height * 0.6,
                width: MediaQuery.sizeOf(context).width,
                child: AADB2CBase.webview(
                  params: widget.params,
                  settings: WebViewSettingsEntity(
                    onError: _onError,
                    onSuccess: _onSuccess,
                    controllerBuilder: (
                      BuildContext context,
                      ActionController action,
                    ) => _controller = action,
                  ),
                ),
              ),
              const SizedBox(height: 16.0),
              ButtonWidget(
                onTap: () async {
                  try {
                    await _controller?.insertAndClick(
                      listIdValue: [
                        ActionEntity(id: 'email', value: 'example@email.com'),
                        ActionEntity(id: 'password', value: '12345678'),
                      ],
                      buttonId: 'next',
                    );
                  } catch (error, trace) {
                    if (kDebugMode) log('Error: $error', stackTrace: trace);
                  }
                },
                title: 'Interact with webview',
                icon: Icons.move_up_outlined,
              ),
            ],
          ),
        ),
      ),
    );
  }

  _onSuccess(
    BuildContext context,
    accessToken,
    idToken,
    refreshToken,
  ) {
    var snackBar = const SnackBar(
      content: Text('Successfully Authenticated!'),
      backgroundColor: Colors.green,
    );
    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  }

  _onError(BuildContext context, String? error) {
    var snackBar = SnackBar(
      content: Text(error ?? ''),
      backgroundColor: Colors.redAccent,
    );
    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  }
}

Click here to access the full example

Flutter UI Customization

Get all the power of Flutter/Dart to customize the layout of Azure authentication screens

Sign In

Sign Up & Sign In & MFA

Full Examples

Dynamic Flutter Layout

Convert layout to flutter automatically

Example
import 'package:aad_b2c_webview/aad_b2c_webview.dart';
import 'package:example/core/commons.dart';
import 'package:flutter/material.dart';
import 'widgets/button_widget.dart';

class B2CWithDynamicCustomization extends StatefulWidget {
  final B2CWebViewParams params;
  const B2CWithDynamicCustomization({super.key, required this.params});

  @override
  State<B2CWithDynamicCustomization> createState() =>
      _B2CWithDynamicCustomizationState();
}

class _B2CWithDynamicCustomizationState
    extends State<B2CWithDynamicCustomization> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Scaffold(
        body: AADB2CBase.custom(
          params: widget.params,
          settings: CustomSettingsEntity(
            onError: (BuildContext context, String? error) {
              setState(() => _isLoading = false);
              var snackBar = SnackBar(
                content: Text(error ?? ''),
                backgroundColor: Colors.redAccent,
              );
              ScaffoldMessenger.of(context).showSnackBar(snackBar);
            },
            onSuccess: (context, accessToken, idToken, refreshToken) async {
              setState(() => _isLoading = false);
              var snackBar = const SnackBar(
                content: Text('Successfully Authenticated!'),
                backgroundColor: Colors.green,
              );
              ScaffoldMessenger.of(context).showSnackBar(snackBar);
            },
            pageBuilder: (_, __, controller, List<HtmlParseEntity>? htmlItems) {
              return Padding(
                padding: const EdgeInsets.all(32.0),
                child: Form(
                  key: _formKey,
                  child: CustomScrollView(
                    slivers: [
                      defaultImage(),
                      buildTitle('B2C Page Dynamic'),
                      SliverList.builder(
                        itemCount: htmlItems?.length ?? 0,
                        itemBuilder: (BuildContext context, int index) {
                          final item = htmlItems?[index];
                          if (item?.type == null) return const SizedBox();
                          return switch (item!.type) {
                            HtmlParseType.input => _inputBuilder(item),
                            HtmlParseType.button => _button(
                                controller,
                                item,
                                false,
                              ),
                            HtmlParseType.a => _button(
                                controller,
                                item,
                                true,
                              ),
                            HtmlParseType.none => const SizedBox(),
                          };
                        },
                      ),
                    ],
                  ),
                ),
              );
            },
          ),
        ),
      ),
    );
  }

  bool _isLoading = false;
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  Widget _inputBuilder(HtmlParseEntity params) {
    return TextFormField(
      controller: TextEditingController(),
      enabled: !_isLoading,
      decoration: InputDecoration(
        labelText: params.name,
      ),
      keyboardType: TextInputType.text,
    );
  }

  Widget _button(
    ActionController controller,
    HtmlParseEntity params,
    bool isLink,
  ) {
    onTap() async {
      if (_formKey.currentState?.validate() ?? false) {
        setState(() => _isLoading = true);
        String email = _emailController.text.trim();
        String password = _passwordController.text.trim();
        await controller.insertAndClick(
          listIdValue: [
            ActionEntity(id: 'email', value: email),
            ActionEntity(id: 'password', value: password),
          ],
          buttonId: 'next',
        );
      }
    }

    if (_isLoading) return _buildLoading();
    if (isLink) {
      return TextButton(
        onPressed: onTap,
        child: Text(
          params.textContent,
          style: const TextStyle(
            decorationStyle: TextDecorationStyle.solid,
          ),
        ),
      );
    }
    return ButtonWidget(
      title: params.textContent,
      icon: Icons.login_outlined,
      onTap: onTap,
    );
  }

  Widget _buildLoading() {
    return const Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        CircularProgressIndicator(),
      ],
    );
  }
}

Click here to access the full example

Refresh Token

Use the code below to refresh the token

Example
await controller.refreshToken(
  B2CAuthEntity(
    refreshToken: _refreshTokenController.text.trim(),
    userFlowName: aadB2CUserFlowName,
    tenantBaseUrl: aadB2CUserAuthFlow,
    clientId: aadB2CClientID,
  ),
);

Click here to access the full example

所属分类

依赖

依赖清单 7 项

  • get_it^7.7.0
  • http^1.2.2
  • pkce^1.1.0+1
  • webview_flutter^4.10.0
  • webview_flutter_android^4.2.0
  • webview_flutter_platform_interface^2.10.0
  • webview_flutter_wkwebview^3.18.4

源码仓库

使用此插件的项目

暂无相关数据。