Coder Social home page Coder Social logo

microsoft / aad_b2c_webview Goto Github PK

View Code? Open in Web Editor NEW
32.0 4.0 22.0 381 KB

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.

License: MIT License

Kotlin 0.16% Ruby 3.41% Swift 1.63% Objective-C 0.05% Dart 40.32% CMake 23.74% C++ 26.58% C 1.81% HTML 2.31%

aad_b2c_webview's Introduction

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

Embedded web view for Azure AD B2C for providing in-app experience Redirects to the route specified in redirectRoute after successful sign in Successfully secures the id token or access token in the app using flutter secure storage Navigates to screen in app after successful sign in

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

Simple In-built Sign In Button

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

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>

Change the redirect URL in our flutter code to https://myurl.com/myappname and add this as a redirect URL in the Azure AD B2C project

Our updated version of the main.dart file is now as follows:

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

import 'counterdemo.dart';

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

onRedirect(BuildContext context) {
  Navigator.of(context).pushNamedAndRemoveUntil('/myappname', (Route<dynamic> route) => false);
}

class MyApp extends StatelessWidget {

  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primaryColor: const Color(0xFF2F56D2),
        textTheme: const TextTheme(
          headlineLarge: TextStyle(
            color: Colors.black,
            fontSize: 32,
            fontWeight: FontWeight.w700,
            fontFamily: 'UberMove',
          ),
          bodyLarge: TextStyle(
            color: Color(0xFF8A8A8A),
            fontSize: 17,
            fontWeight: FontWeight.w400,
            fontFamily: 'UberMoveText',
          ),
          displayMedium: TextStyle(
            fontSize: 18,
            color: Colors.black,
            fontWeight: FontWeight.w700,
            fontFamily: 'UberMove',
          ),
        ),
      ),
      debugShowCheckedModeBanner: false,
      initialRoute: '/',
      routes: {
        // When navigating to the "/" route, build the Create Account widget.
        '/': (context) => const LoginPage(),
        '/myappname': (context) => const CounterDemo(),
      },
    );
  }
}

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

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  String? jwtToken;
  String? refreshToken;

  @override
  Widget build(BuildContext context) {
    const aadB2CClientID = "<app-id>";
    const aadB2CRedirectURL = "https://myurl.com/myappname";
    const aadB2CUserFlowName = "B2C_1_APPNAME_Signin";
    const aadB2CScopes = ['openid', 'offline_access'];
    const aadB2TenantName = "<tenantName>";
    const aadB2CUserAuthFlow =
        "https://$aadB2TenantName.b2clogin.com/$aadB2TenantName.onmicrosoft.com";

    return Scaffold(
      appBar: AppBar(
        title: const Text("AAD B2C Login"),
        backgroundColor: const Color(0xFF2F56D2)
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            /// Login flow
            AADLoginButton(
              userFlowUrl: aadB2CUserAuthFlow,
              clientId: aadB2CClientID,
              userFlowName: aadB2CUserFlowName,
              redirectUrl: aadB2CRedirectURL,
              context: context,
              scopes: aadB2CScopes,
              onAnyTokenRetrieved: (Token anyToken) {},
              onIDToken: (Token token) {
                jwtToken = token.value;
              },
              onAccessToken: (Token token) {
              },
              onRefreshToken: (Token token) {
                refreshToken = token.value;
              },
              onRedirect: (context) => onRedirect(context),
            ),

            /// Refresh token

            TextButton(
              onPressed: () async {
                if (refreshToken != null) {
                  AzureTokenResponse? response =
                  await ClientAuthentication.refreshTokens(
                    refreshToken: refreshToken!,
                    tenant: aadB2TenantName,
                    policy: aadB2CUserAuthFlow,
                    clientId: aadB2CClientID,
                  );
                  if (response != null) {
                    refreshToken = response.refreshToken;
                    jwtToken = response.idToken;
                  }
                }
              },
              child: const Text("Refresh my token"),
            ),
            TextButton(
              onPressed: () async {
                Navigator.pushNamed(context, '/myappname');
              },
              child: const Text("Go To Counter Demo"),
            )
          ],
        ),
      ),
    );
  }
}

Custom Sign in

Simply call page direct or use custom build sign in button to call web view page

import 'package:aad_b2c_webview/aad_b2c_webview.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class MyLoginPage extends StatelessWidget {
  const MyLoginPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    const aadB2CClientID = "<clientId>";
    const aadB2CRedirectURL = "<azure_active_directory_url_redirect>";
    const aadB2CUserFlowName = "B2C_<name_of_userflow>";
    const aadB2CScopes = ['openid', 'offline_access'];
    const aadB2CUserAuthFlow =
        "https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com"; // https://login.microsoftonline.com/<azureTenantId>/oauth2/v2.0/token/
    const aadB2TenantName = "<tenant-name>";

    return Scaffold(
      body: ADB2CEmbedWebView(
        tenantBaseUrl: aadB2CUserAuthFlow,
        userFlowName: aadB2CUserFlowName,
        clientId: aadB2CClientID,
        redirectUrl: aadB2CRedirectURL,
        scopes: aadB2CScopes,
        onAnyTokenRetrieved: (Token anyToken) {},
        onIDToken: (Token token) {},
        onAccessToken: (Token token) {},
        onRefreshToken: (Token token) {},
      ),
    );
  }
}

Send Optional Parameters

In the AADLoginButton widget, 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.

Please adapt the example to suit your specific use case or requirements.

aad_b2c_webview's People

Contributors

baxi19 avatar craig-waite avatar dependabot[bot] avatar filip8600 avatar fullstackweb-developer avatar imfelixng avatar jchirinosodio avatar jnelle avatar microsoftopensource avatar mohanajuhi166 avatar mpp-marcink avatar sleeknoah avatar szczygieldev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

aad_b2c_webview's Issues

Bug with login using web view - method call in the wrong place

Hi, first thanks for creating this library, great work and it's saving some time for us :)
After I tried to implement the web view in my app, I found issues related to it. I will try to explain it in detail and I hope you will not mind because I want to help you improve this package.

So you have the method onPageFinishedTasks(URL, response); that is being called inside of onPageFinished. That method for checking the URL is called only once when the login screen is initially loaded, and that's it. If I input the credentials for login nothing happens, because onPageFinished is calling onPageFinishedTasks(URL, response) only once, and that's the case when the login screen is being loaded.
Further debugging leads me to the conclusion that the right place to call the method onPageFinishedTasks(URL, response); is inside of navigationDelegate property because that field is being triggered every time the user interacts with the screen and tries to log in.
I tried this solution in my app and it's working perfectly, also I have tried your's and it's not working. If you have any questions or maybe I was not clear enough please text me I am open to help :)

Not compatible with webview_flutter ^4

I would like to know if support for webview_flutter ^4 is on the current roadmap.
If not, can I contribute a new version that supports webview_flutter ^4?

Tasks

Web support

Hi and thanks for an amazing package. Is there any plans on going web as well? That would be awesome!

Add Support For Enable Keep Me Signed In Flow - Flutter Username Password autofill from next session if already added remember me option

Hey @mohanajuhi166 @microsoftopensource @Baxi19 @Sleeknoah

I was recently wanted to add the feature option to enable keep me signed in feature using microsoft b2c login, But I don't find any support using this package. I've added changes to B2C policy as guided in the following official blog,
https://github.com/azure-ad-b2c/unit-tests/tree/main/session#enable-keep-me-signed-in-kmsi

So can you please help adding support from flutter side keep me signed in feature using this package?

Thanks

WebView does not handle email and password autocomplete during re-login

Hello,

I’m currently using version aad_b2c_webview: ^0.0.57 of the SDK.

When prompting the user to log in again, the WebView does not manage the email and password autocomplete that the browser usually handles. This behavior works as expected when the login is performed directly through the policy in the browser, but not within the WebView context.

Is there a way for this package to recognize the last-used credentials and automatically trigger the autocomplete for the user's email and password during re-login? If not, are there any recommendations or configurations to enable such behavior?

@Leonardo-Ferreira
image

Which redirect url should I use?

Hello, first, congrats on the project, it makes the user experience with azure ad b2c much better.
I was trying to setup on my app, but I'm having trouble with the redirect url.
For example if my bundle id is com.example.mobile and the redirect url on azure ad b2c for ios is msauth.com.example.mobile://auth, what should I replace that as redirect url?
I tried to create a SPA redirect url on azure ad b2c, put a random url and it redirected after login, but I need to make it come back to the app instead of visiting another url.
Any ideas on how to fix this? Thanks!

Not getting access token

I am always getting access token as null, I can able to get IDToken and RefreshToken.

Could you please assist to resolve this issue

Tasks

Not able to logout user for azure b2c login

Hey @mohanajuhi166 @microsoftopensource I was trying this official flutter package following is my code for azure b2c login.

 const aadB2CClientID = 'random-95eb-val-a09b-token';
    const aadB2CRedirectURL = 'https://login.live.com/oauth0_desktop.srf';
    const aadB2CUserFlowName = 'B2C_1A_signin_Mobile_Dev';
    const aadB2CScopes = ['openid', 'offline_access'];
    const aadB2TenantName = 'testTenantName';
    const aadB2CUserAuthFlow = 'https://<aadB2TenantName>.b2clogin.com/tfp/<aadB2TenantName>.onmicrosoft.com/';
    
    return Scaffold(
      body: ADB2CEmbedWebView(
        tenantBaseUrl: aadB2CUserAuthFlow,
        userFlowName: aadB2CUserFlowName,
        clientId: aadB2CClientID,
        redirectUrl: aadB2CRedirectURL,
        scopes: aadB2CScopes,
        onAnyTokenRetrieved: (Token anyToken) {},
        onIDToken: (Token token) {
          log('idToken==> ${token?.value ?? ''}');
        },
        onAccessToken: (Token token) {
           accessToken = token.value;
          log('accessToken==> ${accessToken ?? ''}');
        },
        onRefreshToken: (Token token) {
          refreshToken = token.value;
          log('refreshToken==> ${refreshToken ?? ''}');
        },
        optionalParameters: const [],
        onRedirect: (context) {
          log('accessToken==> ${accessToken ?? ''}');
          log('refreshToken==> ${refreshToken ?? ''}');

          return {
                  // Push user to new screen.
          };
        },
      ),
    );

The whole flow is working perfectly fine, i'm getting accesstoken and refreshtoken both. So how can I manage the session for this user? How to logout user?

Can you please provide some solution on this?

Thanks.

Tasks

Not compatible with the latest version of flutter

Thanks for making this useful package.
But I can't use this with flutter 3.0.5

The lower bound of "sdk: '>=2.3.0 <3.0.0'" must be 2.12.0 or higher to enable null safety.

I really want to use this package in my project, is there any solution?
Please let me know how I can contribute to this repository to make this possible.

Tasks

No tasks being tracked yet.

Library stops working after 0.0.51 for Android

Good evening,

I was using the 0.0.51 version and it works fine on Android.
To make it work on iOS I had to upgrade it but now the same stopped working on Android.

After some tests I noticed iOS starts working after version 0.0.53 while Andrid stops working after 0.0.51

My implementation is pretty much linear

return ADB2CEmbedWebView( loadingReplacement: const Loading(), optionalParameters: const [], tenantBaseUrl: aadB2CUserAuthFlow, userFlowName: aadB2CUserFlowName, clientId: aadB2CClientID, redirectUrl: aadB2CRedirectURL, onRedirect: (context) => Loading.modalLoading(), scopes: aadB2CScopes, onAnyTokenRetrieved: (Token anyToken) { ... }, onIDToken: (Token token) { ... }, onAccessToken: (Token token) { ... }, onRefreshToken: (Token token) { ... }, );

Debugging inside the library the errors seems to be the following:

Response ({"error":"invalid_request","error_description":"AADB2C90117: The scope 'openid%20offline_access%20profile%20https://myWebSite.onmicrosoft.com/exm.be.webapi/access_as_user' provided in the request is not supported.\r\nCorrelation ID: 53dc1c3f-a814-428c-8adf-a3153d5eb8b3\r\nTimestamp: 2024-03-07 13:24:06Z\r\n"})

It's strange the scopes are the problem, because on iOS the login is working fine and on Android it's working until 0.0.52

Do I have to modify something when upgrading over 0.0.51 for Android to work?

Redirect uri

Hi,

First of all thanks for this library

I have implemented the code given in the example in pubdev. After pressing the button, I am able to receive in the callbacks of the widget (onIDToken and onAnyTokenRetrieved) receiving correctly the token given by B2c auth. The callback onRedirect is also being call correctly and i am able to navigate to another screen (my home screen). My only issue, is that after logging in the browser can't find the redirect url of the schema, the message shown in the browser of the mobile phone after login is: .com.example/authapp://oauthredirect/?code........ could not be loaded because net::ERR_UNKNOWN_URL_SCHEME

I had created (in android) a schema in the manifest adding in the activity tag

<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="com.example.authapp"
       android:host="oauthredirect"
       />
</intent-filter>

I tried to handle the url by installing the library uni_links and adding a listener in the main function :

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    initUniLinks();

    // TODO: implement initState
    super.initState();
  }

  void initUniLinks() async {
    getInitialLink().then((String? link) {
      if (link != null) {
        // Handle the link
      }
    });

    linkStream.listen((String? link) {
      if (link != null &&
          link.startsWith('com.example.authapp://oauthredirect')) {
        final Uri uri = Uri.parse(link);
        final String code = uri.queryParameters['code']!;
        // Now you have the code, proceed with your authentication flow
      }
      if (link != null) {
        // Handle the link
      }
    }, onError: (err) {
      // Handle the error
    });
  }

But when I debug the code, the breakpoint in the listener (breakpoint in line if (link != null &&
link.startsWith('com.example.authapp://oauthredirect')) ) is never reached.

I am a little bit lost what I need to do in order to show instead of a broken url text a loading progress or something like that during this fraction of second between the the token retrieved and the redirect callback that is sending the user to the home page.

Thanks.

Please help on React Native b2c login

I need MSAL plugin for react native

Tasks

No tasks being tracked yet.

Google login error that says “403 disallowed_useragent,”

hi there,

I configured and tested the Azure AD B2C setup for Microsoft and Google logins. It worked.
When i use the ADB2CEmbedWebView from my Android code, Microsoft login works, but Google login errors out with “403 disallowed_useragent,”.

On google's support website, it clearly states that "_Embedded WebViews puts your security at risk because they could let third parties access and change communications between you and Google.".

update: 7/3/24 - After enabling Facebook access, get the same message "logging in to Facebook from an embedded browser is disabled".

Does this mean its not a good practice to use Embedded webviews? @mohanajuhi166 would be keen to get your thoughts

Redirecting on IOS

Hi guys, i have issue with redirection after login on IOS. Tried different redirect URI's and nothing is happening. In logs on azure I can see that user is successfully log in, but after i click Sign in button nothing is happening. Any idea how to solve redirection issue? Thank you

Occasional white screen

I am using ADB2CEmbedWebView to login in a Flutter app. Occasionally the url is not reached and the app is just showing a white screen. This mostly happens running the app in Android Studio.

Before I run ADB2CEmbedWebView I lookup the url's dns to ensure it can be reached and then only run ADB2CEmbedWebView.

Can someone please let me know how to catch the exception or fix it?

import 'dart:async';

import 'package:aad_b2c_webview/aad_b2c_webview.dart';
import 'package:dnsolve/dnsolve.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:logger/logger.dart';
import 'package:mhrm/constants/app_colors.dart';
import 'package:mhrm/constants/app_constants.dart';
import 'package:mhrm/constants/typography.dart';
import 'package:mhrm/navigation/navigation_utils.dart';
import 'package:mhrm/providers/notifiers_provider.dart';
import 'package:mhrm/services/notifiers/auth/states/auth_state.dart';
import 'package:mhrm/utils/app_localizations.dart';
import 'package:mhrm/widgets/generic/dialog/one_button_popup.dart';

class LoginPage extends ConsumerStatefulWidget {
  const LoginPage({super.key});

  @override
  ConsumerState<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends ConsumerState<LoginPage> {
  final Logger _logger = Logger();
  String? _userToken;
  late Timer _watchdogTimer;
  late Timer _reloadTimer;
  int _count = 0;
  bool _canReach = false;

  onRedirect(BuildContext context) async {
    _logger.d('LoginPage onRedirect ');
    var token = _userToken ?? "";
    await ref.read(authProvider.notifier).setAccessToken(token);
  }

  onRefreshToken(Token token) async {
    _logger.d('LoginPage onRefreshToken ');
    await ref.read(authProvider.notifier).setRefreshToken(token.value);
  }

  backToSplash() {
    _watchdogTimer.cancel();
    Future.delayed(Duration.zero, () {
      GoRouter.of(context).go(splashPath);
    });
  }

  reloadLogin() {
    Future.delayed(Duration.zero, () {
      GoRouter.of(context).go(loginPath);
    });
  }

  @override
  void initState() {
    _watchdogTimer = Timer(const Duration(seconds: 300), () {
      _logger.d('LoginPage timeout');
      backToSplash();
    });
    _reloadTimer = Timer(const Duration(milliseconds: 500), () {
      _logger.d('LoginPage Reload');
      reloadLogin();
    });
    super.initState();
  }

  @override
  void dispose() {
    _watchdogTimer.cancel();
    _reloadTimer.cancel();
    super.dispose();
  }

  Future<void> dNSolve() async {
    ResolveResponse response = await dnsLookup();

    if (response.answer!.records != null) {
      for (final record in response.answer!.records!) {
        _logger.d('LoginPage DNS resolved ${record.toBind}');
      }
      setState(() {
        _canReach = true;
      });
    } else {
      _logger.d('LoginPage $_count try DNS could not be resolved!! ');
      setState(() {
        _count++;
        _canReach = false;
      });
    }
  }

  Future<ResolveResponse> dnsLookup() async {
    final dnSolve = DNSolve();
    return await dnSolve.lookup(
      //'dgsgd',
      'https://mhrb2cdev.b2clogin.com',
      dnsSec: true,
      type: RecordType.srv,
    );
  }

  @override
  Widget build(BuildContext context) {
    final localizations = AppLocalizations.of(context);
    final AuthState authState = ref.watch(authProvider);
    if (_count < 4 && !_canReach) {
      dNSolve();
      if (_count >= 3) {
        Future.delayed(Duration.zero, () {
          OneButtonPopup.showPopupDialog(
            context: context,
            heading: localizations.translate('dns_error'),
            buttonString: localizations.translate('reload'),
            onButton: () {},
            body: Text(
              localizations.translate('dns_error_info'),
              style: TypeScale.s12w500grey8x404040,
            ),
          );
          backToSplash();
        });
      }
    }

    if (authState.isAuthenticated) {
      _watchdogTimer.cancel();
      Future.delayed(Duration.zero, () {
        GoRouter.of(context).go(homePath);
      });
    }
    if (_canReach) {
      return Scaffold(
        body: Column(
          children: [
            Expanded(
              child: ADB2CEmbedWebView(
                tenantBaseUrl: AppConstants.aadB2CUserAuthFlow,
                userFlowName: AppConstants.aadB2CUserFlowName,
                clientId: AppConstants.aadB2CClientID,
                redirectUrl: AppConstants.aadB2CRedirectURL,
                scopes: AppConstants.aadB2CScopes,
                onAnyTokenRetrieved: (Token anyToken) {
                  _logger.d('LoginPage onAnyTokenRetrieved type ${anyToken.type}');
                },
                onIDToken: (Token token) {
                  _logger.d('LoginPage onIDToken type ${token.type}');
                  _userToken = token.value;
                },
                onAccessToken: (Token token) {
                  _logger.d('LoginPage onAccessToken type ${token.type}');
                },
                onRefreshToken: onRefreshToken,
                onErrorOrCancel: (context) {
                  _logger.d('LoginPage ADB2CEmbedWebView onErrorOrCancel');
                  backToSplash();
                },
                optionalParameters: const [],
                onRedirect: onRedirect,
              ),
            ),
            ElevatedButton(
              style: ButtonStyle(
                backgroundColor: MaterialStateProperty.all(
                  AppColors.grey6x737373,
                ),
                shadowColor: MaterialStateProperty.all(
                  AppColors.grey2xD9D9D9,
                ),
                elevation: MaterialStateProperty.all<double>(0),
              ),
              onPressed: () {
                backToSplash();
              },
              child: Text(
                localizations.translate('reload_login_page'),
                style: TypeScale.s12w500white,
              ),
            ),
          ],
        ),
      );
    } else {
      return Container(
        decoration: const BoxDecoration(
          image: DecorationImage(
            image: AssetImage("assets/images/login_background.jpg"),
            fit: BoxFit.cover,
          ),
        ),
        child: Column(
          children: [
            const SizedBox(
              height: 142,
            ),
            Image.asset(
              "assets/images/logo/logo.png",
              fit: BoxFit.cover,
              width: 232,
              height: 155,
            ),
          ],
        ),
      );
    }
  }
}

I/flutter (17740): │ #0 AADB2CAuthService.refreshAuthToken (package:mhrm/services/auth/aadb2c_auth_service.dart:258:12)
I/flutter (17740): │ #1

I/flutter (17740): │ 🐛 refreshAuthToken ServiceResult.success(authTokenResponse.idToken!)

I/ViewRootImpl@ca848a1MainActivity: ViewPostIme pointer 0
I/ViewRootImpl@ca848a1MainActivity: ViewPostIme pointer 1
[GoRouter] going to /login
W/WindowOnBackDispatcher(17740): sendCancelIfRunning: isInProgress=falsecallback=io.flutter.embedding.android.FlutterActivity$1@ed8ff44

I/flutter (17740): │ #0 _LoginPageState.initState. (package:mhrm/screens/login/login_screen.dart:64:15)
I/flutter (17740): │ #1 Timer._createTimer. (dart:async-patch/timer_patch.dart:18:15)

I/flutter (17740): │ 🐛 LoginPage Reload

[GoRouter] going to /login

I/flutter (17740): │ #0 _LoginPageState.dNSolve (package:mhrm/screens/login/login_screen.dart:82:17)
I/flutter (17740): │ #1

I/flutter (17740): │ 🐛 LoginPage DNS resolved https://mhrb2cdev.b2clogin.com. 300 IN CNAME "prda.aadg.msidentity.com."

I/flutter (17740): │ #0 _LoginPageState.dNSolve (package:mhrm/screens/login/login_screen.dart:82:17)
I/flutter (17740): │ #1

I/flutter (17740): │ 🐛 LoginPage DNS resolved prda.aadg.msidentity.com. 9 IN CNAME "www.tm.a.prd.aadg.akadns.net."

W/ziparchive(17740): Unable to open '/data/app/~~IGlevzahHjeBRxkXyjMtMA==/com.google.android.trichromelibrary_636705433-6hI3TlGcTqglbAKrVN1oAg==/base.dm': No such file or directory
W/ziparchive(17740): Unable to open '/data/app/~~IGlevzahHjeBRxkXyjMtMA==/com.google.android.trichromelibrary_636705433-6hI3TlGcTqglbAKrVN1oAg==/base.dm': No such file or directory
W/mhrmpanelmember(17740): Entry not found
D/nativeloader(17740): Configuring clns-5 for other apk /data/app/~~IGlevzahHjeBRxkXyjMtMA==/com.google.android.trichromelibrary_636705433-6hI3TlGcTqglbAKrVN1oAg==/base.apk. target_sdk_version=34, uses_libraries=ALL, library_path=/data/app/~~DQlDmqjEPDgxDA_qXa2JGA==/com.google.android.webview-ZbwGemP2oeVUcabVu4t7EQ==/lib/arm64:/data/app/~~DQlDmqjEPDgxDA_qXa2JGA==/com.google.android.webview-ZbwGemP2oeVUcabVu4t7EQ==/base.apk!/lib/arm64-v8a:/data/app/~~IGlevzahHjeBRxkXyjMtMA==/com.google.android.trichromelibrary_636705433-6hI3TlGcTqglbAKrVN1oAg==/base.apk!/lib/arm64-v8a, permitted_path=/data:/mnt/expand
D/nativeloader(17740): Extending system_exposed_libraries: libaudiomirroring_jni.audiomirroring.samsung.so:lib.engmodejni.samsung.so:libheifcapture_jni.media.samsung.so:libjpegsq.media.samsung.so:libslljpeg.media.samsung.so:libsemimagecrop_jni.media.samsung.so:libsimba.media.samsung.so:libamDNN.media.samsung.so:libamDNN.media.samsung.so:libapex_motionphoto_utils_jni.media.samsung.so:libsume_mediabuffer_jni.media.samsung.so:libSlowShutter_jni.media.samsung.so:libapex_jni.media.samsung.so:libsume_jni.media.samsung.so:libneural.snap.samsung.so:libsecuresnap_aidl.snap.samsung.so:libsnap_aidl.snap.samsung.so:libeden_rt_stub.edensdk.samsung.so:libSDKRecognitionOCR.spenocr.samsung.so:libSDKMoireDetector.spenocr.samsung.so:libtensorflowlite_c.spenocr.samsung.so:libSFEffect.fonteffect.samsung.so:libperfsdk.performance.samsung.so:libvesgraphicframework.videoeditor.samsung.so:libvesinterface.videoeditor.samsung.so:libvesframework.videoeditor.samsung.so:libveframework.videoeditor.samsung.so:lib_vnd_client.dk.samsung.so:lib_native_client.dk.sa
D/nativeloader(17740): Configuring clns-6 for other apk /data/app/~~DQlDmqjEPDgxDA_qXa2JGA==/com.google.android.webview-ZbwGemP2oeVUcabVu4t7EQ==/base.apk. target_sdk_version=34, uses_libraries=, library_path=/data/app/~~DQlDmqjEPDgxDA_qXa2JGA==/com.google.android.webview-ZbwGemP2oeVUcabVu4t7EQ==/lib/arm64:/data/app/~~DQlDmqjEPDgxDA_qXa2JGA==/com.google.android.webview-ZbwGemP2oeVUcabVu4t7EQ==/base.apk!/lib/arm64-v8a:/data/app/~~IGlevzahHjeBRxkXyjMtMA==/com.google.android.trichromelibrary_636705433-6hI3TlGcTqglbAKrVN1oAg==/base.apk!/lib/arm64-v8a, permitted_path=/data:/mnt/expand
I/WebViewFactory(17740): Loading com.google.android.webview version 124.0.6367.54 (code 636705433)
I/cr_WVCFactoryProvider(17740): Loaded version=124.0.6367.54 minSdkVersion=29 isBundle=true multiprocess=true packageId=2
I/chromium(17740): [0422/083738.202748:INFO:variations_seed_loader.cc(66)] Failed to open file for reading.: No such file or directory (2)
I/cr_LibraryLoader(17740): Successfully loaded native library
I/cr_CachingUmaRecorder(17740): Flushed 6 samples from 6 histograms, 0 samples were dropped.
I/cr_CombinedPProvider(17740): #registerProvider() provider:WV.V7@eb97547 isPolicyCacheEnabled:false policyProvidersSize:0
I/cr_PolicyProvider(17740): #setManagerAndSource() 0
I/cr_CombinedPProvider(17740): #linkNativeInternal() 1
I/cr_AppResProvider(17740): #getApplicationRestrictionsFromUserManager() Bundle[EMPTY_PARCEL]
I/cr_PolicyProvider(17740): #notifySettingsAvailable() 0
I/cr_CombinedPProvider(17740): #onSettingsAvailable() 0
I/cr_CombinedPProvider(17740): #flushPolicies()
D/CompatibilityChangeReporter(17740): Compat change id reported: 214741472; UID 10542; state: ENABLED
D/CompatibilityChangeReporter(17740): Compat change id reported: 171228096; UID 10542; state: ENABLED

D/ConnectivityManager(17740): StackLog:
[android.net.ConnectivityManager.sendRequestForNetwork(ConnectivityManager.java:4401)]
[android.net.ConnectivityManager.registerDefaultNetworkCallbackForUid(ConnectivityManager.java:4965)]
[android.net.ConnectivityManager.registerDefaultNetworkCallback(ConnectivityManager.java:4932)]
[WV.mD.e(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:44)]
[WV.mD.b(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:1)]
[org.chromium.android_webview.AwContentsLifecycleNotifier.onFirstWebViewCreated(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:22)]
[J.N.MFiR_zHY(Native Method)]
[org.chromium.android_webview.AwContents.(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:450)]
[com.android.webview.chromium.j.run(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:147)]
[WV.WY.b(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:20)]
[WV.VY.run(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:3)]
[org.chromium.base.task.PostTask.d(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:11)]
[WV.WY.a(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:23)]
[com.android.webview.chromium.WebViewChromiumFactoryProvider.a(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:3)]
[com.android.webview.chromium.WebViewChromium.init(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:327)]
[android.webkit.WebView.(WebView.java:444)]
[android.webkit.WebView.(WebView.java:364)]
[android.webkit.WebView.(WebView.java:346)]
[android.webkit.WebView.(WebView.java:333)]
[android.webkit.WebView.(WebView.java:323)]
[io.flutter.plugins.webviewflutter.WebViewHostApiImpl$WebViewPlatformView.(WebViewHostApiImpl.java:109)]
[io.flutter.plugins.webviewflutter.WebViewHostApiImpl$WebViewPlatformView.(WebViewHostApiImpl.java:96)]
[io.flutter.plugins.webviewflutter.WebViewHostApiImpl$WebViewProxy.createWebView(WebViewHostApiImpl.java:55)]
[io.flutter.plugins.webviewflutter.WebViewHostApiImpl.create(WebViewHostApiImpl.java:237)]
[io.flutter.plugins.webviewflutter.GeneratedAndroidWebView$WebViewHostApi$-CC.lambda$setup$0(GeneratedAndroidWebView.java:1004)]
[io.flutter.plugins.webviewflutter.GeneratedAndroidWebView$WebViewHostApi$$ExternalSyntheticLambda0.onMessage(Unknown Source:2)]
[io.flutter.plugin.common.BasicMessageChannel$IncomingMessageHandler.onMessage(BasicMessageChannel.java:261)]
[io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:292)]
[io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:319)]
[io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)]

D/ConnectivityManager(17740): StackLog: [android.net.ConnectivityManager.sendRequestForNetwork(ConnectivityManager.java:4401)] [android.net.ConnectivityManager.sendRequestForNetwork(ConnectivityManager.java:4443)] [android.net.ConnectivityManager.registerNetworkCallback(ConnectivityManager.java:4825)] [WV.mD.e(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:93)] [WV.mD.b(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:1)] [org.chromium.android_webview.AwContentsLifecycleNotifier.onFirstWebViewCreated(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:22)] [J.N.MFiR_zHY(Native Method)] [org.chromium.android_webview.AwContents.(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:450)] [com.android.webview.chromium.j.run(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:147)] [WV.WY.b(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:20)] [WV.VY.run(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:3)] [org.chromium.base.task.PostTask.d(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:11)] [WV.WY.a(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:23)] [com.android.webview.chromium.WebViewChromiumFactoryProvider.a(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:3)] [com.android.webview.chromium.WebViewChromium.init(chromium-TrichromeWebViewGoogle6432.aab-stable-636705433:327)] [android.webkit.WebView.(WebView.java:444)] [android.webkit.WebView.(WebView.java:364)] [android.webkit.WebView.(WebView.java:346)] [android.webkit.WebView.(WebView.java:333)] [android.webkit.WebView.(WebView.java:323)] [io.flutter.plugins.webviewflutter.WebViewHostApiImpl$WebViewPlatformView.(WebViewHostApiImpl.java:109)] [io.flutter.plugins.webviewflutter.WebViewHostApiImpl$WebViewPlatformView.(WebViewHostApiImpl.java:96)] [io.flutter.plugins.webviewflutter.WebViewHostApiImpl$WebViewProxy.createWebView(WebViewHostApiImpl.java:55)] [io.flutter.plugins.webviewflutter.WebViewHostApiImpl.create(WebViewHostApiImpl.java:237)] [io.flutter.plugins.webviewflutter.GeneratedAndroidWebView$WebViewHostApi$-CC.lambda$setup$0(GeneratedAndroidWebView.java:1004)] [io.flutter.plugins.webviewflutter.GeneratedAndroidWebView$WebViewHostApi$$ExternalSyntheticLambda0.onMessage(Unknown Source:2)] [io.flutter.plugin.common.BasicMessageChannel$IncomingMessageHandler.onMessage(BasicMessageChannel.java:261)] [io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:292)] [io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:319)] [io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)]
W/cr_media(17740): BLUETOOTH_CONNECT permission is missing.
W/cr_media(17740): registerBluetoothIntentsIfNeeded: Requires BLUETOOTH permission
I/PlatformViewsController(17740): Hosting view in view hierarchy for platform view: 0
I/PlatformViewsController(17740): PlatformView is using ImageReader backend
D/CompatibilityChangeReporter(17740): Compat change id reported: 236825255; UID 10542; state: DISABLED
D/CompatibilityChangeReporter(17740): Compat change id reported: 193247900; UID 10542; state: ENABLED
D/NativeCustomFrequencyManager(17740): [NativeCFMS] BpCustomFrequencyManager::BpCustomFrequencyManager()
E/FrameEvents(17740): updateAcquireFence: Did not find frame.
D/OpenGLRenderer(17740): CFMS:: SetUp Pid : 17740 Tid : 17802
I/ViewRootImpl@ca848a1MainActivity: mWNT: t=0xb400007079986030 mBlastBufferQueue=0xb400007109961f30 fn= 2 caller= android.view.ViewRootImpl$6.onFrameDraw:5539 android.view.ViewRootImpl$2.onFrameDraw:2103 android.view.ThreadedRenderer$1.onFrameDraw:788
W/mhrmpanelmember(17740): Accessing hidden field Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String; (unsupported, reflection, allowed)
E/FrameEvents(17740): updateAcquireFence: Did not find frame.
W/AudioCapabilities(17740): Unsupported mime audio/x-ape
W/AudioCapabilities(17740): Unsupported mime audio/x-ima
W/AudioCapabilities(17740): Unsupported mime audio/mpeg-L1
W/AudioCapabilities(17740): Unsupported mime audio/mpeg-L2
W/VideoCapabilities(17740): Unsupported mime video/wvc1
W/VideoCapabilities(17740): Unsupported mime video/x-ms-wmv
W/cr_VAUtil(17740): Unknown level: 131072 for profile 65536 of codec video/avc

Tokens returned but App does not redirect properly

Hi,

I'm having trouble even getting the very basic demo app to work correctly. The problem I'm having is that I can log in to AAD B2C (and I can see from the network traffic that the tokens are all coming back correctly) via the plugin code provided, but once I'm logged in I get the following error:

image

I am currently only trying to get the flutter demo counter page hidden behind a login screen. Below is my main.dart file. The only other change I've made is to add manifestPlaceholders += [ appAuthRedirectScheme: 'com.my.app.name' ] to the defaultConfig section of the /android/app/build.gradle file.

Any help you could provide would be amazing as we've been going round in circles on this a for a couple of days now.

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

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

onRedirect(BuildContext context) {
  Navigator.pushNamed(context, '/');
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      initialRoute: '/',
      routes: {
        '/': (context) => const MyLoginPage(),
        '/oauthredirect/': (context) => const MyHomePage(title: 'Flutter Demo Redirect Route')
      },
    );
  }
}

class MyLoginPage extends StatelessWidget {
  const MyLoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    const aadB2CClientID = "################################";
    const aadB2CRedirectURL = "com.my.app.name://oauthredirect/";
    const aadB2CUserFlowName = "B2C_1_MyAppName_Signin";
    const aadB2CScopes = ['openid', 'offline_access'];
    const aadB2CUserAuthFlow =
        "https://<tenantName>.b2clogin.com/<tenantName>.onmicrosoft.com";
    const aadB2TenantName = "<tenantName>";
    return Scaffold(
      body: ADB2CEmbedWebView(
        tenantBaseUrl: aadB2CUserAuthFlow,
        userFlowName: aadB2CUserFlowName,
        clientId: aadB2CClientID,
        redirectUrl: aadB2CRedirectURL,
        scopes: aadB2CScopes,
        onAnyTokenRetrieved: (Token anyToken) {},
        onIDToken: (Token token) {},
        onAccessToken: (Token token) {},
        onRefreshToken: (Token token) {},
        optionalParameters: [],
        onRedirect: (context) => const MyHomePage(title: 'Flutter Demo On Redirect'),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

ResponseType as default / code throws error

Hallo everybody,
trying to Login with ADB2CEmbedWebView using responseType: Constants.default or authCode (which is the same) throws Dio exception.
responseType: idToken or accessToken works fine.

It comes to my understanding that the authCode is used to implement the UserFlow login and call and extract both idToken and refreshToken.

Thanks in advance and best regards.

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DioException [bad response]: This exception was thrown because the response has a status code of 400 and RequestOptions.validateStatus was configured to throw for this status code. E/flutter (11937): The status code of 400 has the following meaning: "Client error - the request contains bad syntax or cannot be fulfilled" E/flutter (11937): Read more about status codes at https://developer.mozilla.org/en-US/docs/Web/HTTP/Status E/flutter (11937): In order to resolve this exception you typically have either to verify and fix your request code or you have to fix the server code. E/flutter (11937): E/flutter (11937): #0 DioMixin.fetch.<anonymous closure> (package:dio/src/dio_mixin.dart:507:7) E/flutter (11937): #1 _FutureListener.handleError (dart:async/future_impl.dart:180:22) E/flutter (11937): #2 Future._propagateToListeners.handleError (dart:async/future_impl.dart:858:47) E/flutter (11937): #3 Future._propagateToListeners (dart:async/future_impl.dart:879:13) E/flutter (11937): #4 Future._completeError (dart:async/future_impl.dart:655:5) E/flutter (11937): #5 _SyncCompleter._completeError (dart:async/future_impl.dart:63:12) E/flutter (11937): #6 _Completer.completeError (dart:async/future_impl.dart:27:5) E/flutter (11937): #7 Future.any.onError (dart:async/future.dart:618:45) E/flutter (11937): #8 _RootZone.runBinary (dart:async/zone.dart:1666:54) E/flutter (11937): #9 _FutureListener.handleError (dart:async/future_impl.dart:177:22) E/flutter (11937): #10 Future._propagateToListeners.handleError (dart:async/future_impl.dart:858:47) E/flutter (11937): #11 Future._propagateToListeners (dart:async/future_impl.dart:879:13) E/flutter (11937): #12 Future._completeError (dart:async/future_impl.dart:655:5) E/flutter (11937): #13 Future._asyncCompleteError.<anonymous closure> (dart:async/future_impl.dart:745:7) E/flutter (11937): #14 _microtaskLoop (dart:async/schedule_microtask.dart:40:21) E/flutter (11937): #15 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5) E/flutter (11937):

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.