Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tokens returned but App does not redirect properly #27

Closed
craig-waite opened this issue Oct 26, 2023 · 7 comments
Closed

Tokens returned but App does not redirect properly #27

craig-waite opened this issue Oct 26, 2023 · 7 comments

Comments

@craig-waite
Copy link
Contributor

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),
      ),
    );
  }
}
@mohanajuhi166
Copy link
Collaborator

Hi @craig-waite for aadB2CRedirectURL
instead of com.my.app.name://oauthredirect/

Can you please try changing it to to a standard weburl like this:

https://.b2clogin.com/oauth2/nativeclient

Let me know.

@mohanajuhi166
Copy link
Collaborator

similar issue resolution:
#23

@armache
Copy link

armache commented Oct 27, 2023

Hi @craig-waite for aadB2CRedirectURL instead of com.my.app.name://oauthredirect/

Can you please try changing it to to a standard weburl like this:

https://.b2clogin.com/oauth2/nativeclient

Let me know.

Hi,

I've implemented the same sample scenario and used the suggested weburl: https://tenant-name.b2clogin.com/oauth2/nativeclient

As a result I am getting a blank page after authentication while the expectation is to be redirected to home page with a counter. Please advice.

@craig-waite
Copy link
Contributor Author

Thanks for the help on this. I am now able to log in without the app showing an error. I do now get the issue experienced by @armache above, but I'm assuming that is just because I'm not routing correctly in the Flutter code. I will update here once I have a properly working prototype to share.

@mohanajuhi166
Copy link
Collaborator

@craig-waite let me know. It just needs to route to one of the routes in flutter code.

@craig-waite
Copy link
Contributor Author

Hi All,

We've tracked down what we were missing in our code now. Thank you all for the pointers!

Here's what we changed to fix the issue:

  • 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 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"),
            )
          ],
        ),
      ),
    );
  }
}

@mohanajuhi166
Copy link
Collaborator

@craig-waite this is great! I would encourage you to open a PR and provide this documentation for an OSS contribution. Let me know.!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants