From 6931f0f1103eb388f04eb5fa547e8dc2e6cc51ea Mon Sep 17 00:00:00 2001 From: Luis Date: Tue, 7 Dec 2021 20:37:24 -0500 Subject: [PATCH] - Code formatted --- example/lib/main.dart | 153 ++++++++++++++------------- lib/src/oauth_web_screen.dart | 78 +++++++------- lib/src/oauth_web_view.dart | 190 ++++++++++++++++++++-------------- test/oauth_webauth_test.dart | 4 +- 4 files changed, 231 insertions(+), 194 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index bc7223a..c31e683 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -32,13 +32,21 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - - static const String authorizationEndpointUrl = String.fromEnvironment('AUTHORIZATION_ENDPOINT_URL', defaultValue: 'https://test-auth-endpoint.com'); - static const String tokenEndpointUrl = String.fromEnvironment('TOKEN_ENDPOINT_URL', defaultValue: 'https://test-token-endpoint.com'); - static const String clientSecret = String.fromEnvironment('CLIENT_SECRET', defaultValue: 'XXXXXXXXX'); - static const String clientId = String.fromEnvironment('CLIENT_ID', defaultValue: 'realmClientID'); - static const String redirectUrl = String.fromEnvironment('REDIRECT_URL', defaultValue: 'https://test-redirect-to.com'); - final List scopes = const String.fromEnvironment('SCOPES', defaultValue: 'https://test-redirect-to.com').split(' '); + static const String authorizationEndpointUrl = String.fromEnvironment( + 'AUTHORIZATION_ENDPOINT_URL', + defaultValue: 'https://test-auth-endpoint.com'); + static const String tokenEndpointUrl = String.fromEnvironment( + 'TOKEN_ENDPOINT_URL', + defaultValue: 'https://test-token-endpoint.com'); + static const String clientSecret = + String.fromEnvironment('CLIENT_SECRET', defaultValue: 'XXXXXXXXX'); + static const String clientId = + String.fromEnvironment('CLIENT_ID', defaultValue: 'realmClientID'); + static const String redirectUrl = String.fromEnvironment('REDIRECT_URL', + defaultValue: 'https://test-redirect-to.com'); + final List scopes = const String.fromEnvironment('SCOPES', + defaultValue: 'https://test-redirect-to.com') + .split(' '); String authResponse = 'Authorization data will be shown here'; @@ -61,8 +69,7 @@ class _MyHomePageState extends State { onPressed: loginV1, child: const Text('Login variant 1'), style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(Colors.green) - ), + backgroundColor: MaterialStateProperty.all(Colors.green)), ), const SizedBox(height: 4), ElevatedButton( @@ -78,31 +85,33 @@ class _MyHomePageState extends State { void loginV1() async { final result = await OAuthWebScreen.start( - context: context, - authorizationEndpointUrl: authorizationEndpointUrl, - tokenEndpointUrl: tokenEndpointUrl, - clientSecret: clientSecret, - clientId: clientId, - redirectUrl: redirectUrl, - scopes: scopes, - promptValues: const ['login'], - loginHint: 'luis901101@gmail.com', - onCertificateValidate: (certificate) {///This is recommended - /// Do certificate validations here - /// If false is returned then a CertificateException() will be thrown - return true; - }, - textLocales: { ///Optionally texts can be localized - OAuthWebView.backButtonTooltipKey: 'Ir atrás', - OAuthWebView.forwardButtonTooltipKey: 'Ir adelante', - OAuthWebView.reloadButtonTooltipKey: 'Recargar', - OAuthWebView.clearCacheButtonTooltipKey: 'Limpiar caché', - OAuthWebView.closeButtonTooltipKey: 'Cerrar', - OAuthWebView.clearCacheWarningMessageKey: '¿Está seguro que desea limpiar la caché?', - } - ); - if(result != null) { - if(result is Credentials) { + context: context, + authorizationEndpointUrl: authorizationEndpointUrl, + tokenEndpointUrl: tokenEndpointUrl, + clientSecret: clientSecret, + clientId: clientId, + redirectUrl: redirectUrl, + scopes: scopes, + promptValues: const ['login'], + loginHint: 'johndoe@mail.com', + onCertificateValidate: (certificate) { + ///This is recommended + /// Do certificate validations here + /// If false is returned then a CertificateException() will be thrown + return true; + }, + textLocales: { + ///Optionally texts can be localized + OAuthWebView.backButtonTooltipKey: 'Ir atrás', + OAuthWebView.forwardButtonTooltipKey: 'Ir adelante', + OAuthWebView.reloadButtonTooltipKey: 'Recargar', + OAuthWebView.clearCacheButtonTooltipKey: 'Limpiar caché', + OAuthWebView.closeButtonTooltipKey: 'Cerrar', + OAuthWebView.clearCacheWarningMessageKey: + '¿Está seguro que desea limpiar la caché?', + }); + if (result != null) { + if (result is Credentials) { authResponse = getPrettyCredentialsJson(result); } else { authResponse = result.toString(); @@ -115,44 +124,46 @@ class _MyHomePageState extends State { void loginV2() { OAuthWebScreen.start( - context: context, - authorizationEndpointUrl: authorizationEndpointUrl, - tokenEndpointUrl: tokenEndpointUrl, - clientSecret: clientSecret, - clientId: clientId, - redirectUrl: redirectUrl, - scopes: scopes, - promptValues: const ['login'], - loginHint: 'luis901101@gmail.com', - onCertificateValidate: (certificate) { ///This is recommended - /// Do certificate validations here - /// If false is returned then a CertificateException() will be thrown - return true; - }, - textLocales: { ///Optionally text can be localized - OAuthWebView.backButtonTooltipKey: 'Ir atrás', - OAuthWebView.forwardButtonTooltipKey: 'Ir adelante', - OAuthWebView.reloadButtonTooltipKey: 'Recargar', - OAuthWebView.clearCacheButtonTooltipKey: 'Limpiar caché', - OAuthWebView.closeButtonTooltipKey: 'Cerrar', - OAuthWebView.clearCacheWarningMessageKey: '¿Está seguro que desea limpiar la caché?', - }, - onSuccess: (credentials) { - setState(() { - authResponse = getPrettyCredentialsJson(credentials); + context: context, + authorizationEndpointUrl: authorizationEndpointUrl, + tokenEndpointUrl: tokenEndpointUrl, + clientSecret: clientSecret, + clientId: clientId, + redirectUrl: redirectUrl, + scopes: scopes, + promptValues: const ['login'], + loginHint: 'johndoe@mail.com', + onCertificateValidate: (certificate) { + ///This is recommended + /// Do certificate validations here + /// If false is returned then a CertificateException() will be thrown + return true; + }, + textLocales: { + ///Optionally text can be localized + OAuthWebView.backButtonTooltipKey: 'Ir atrás', + OAuthWebView.forwardButtonTooltipKey: 'Ir adelante', + OAuthWebView.reloadButtonTooltipKey: 'Recargar', + OAuthWebView.clearCacheButtonTooltipKey: 'Limpiar caché', + OAuthWebView.closeButtonTooltipKey: 'Cerrar', + OAuthWebView.clearCacheWarningMessageKey: + '¿Está seguro que desea limpiar la caché?', + }, + onSuccess: (credentials) { + setState(() { + authResponse = getPrettyCredentialsJson(credentials); + }); + }, + onError: (error) { + setState(() { + authResponse = error.toString(); + }); + }, + onCancel: () { + setState(() { + authResponse = 'User cancelled authentication'; + }); }); - }, - onError: (error) { - setState(() { - authResponse = error.toString(); - }); - }, - onCancel: () { - setState(() { - authResponse = 'User cancelled authentication'; - }); - } - ); } String getPrettyCredentialsJson(Credentials credentials) { diff --git a/lib/src/oauth_web_screen.dart b/lib/src/oauth_web_screen.dart index 74dd6ca..ecaa917 100644 --- a/lib/src/oauth_web_screen.dart +++ b/lib/src/oauth_web_screen.dart @@ -1,47 +1,44 @@ - import 'package:flutter/material.dart'; import 'package:oauth2/oauth2.dart'; import 'package:oauth_webauth/src/oauth_web_view.dart'; class OAuthWebScreen extends StatelessWidget { - - static Future? start({ - Key? key, - required BuildContext context, - required String authorizationEndpointUrl, - required String tokenEndpointUrl, - required String redirectUrl, - required String clientId, - String? clientSecret, - List? scopes, - String? loginHint, - List? promptValues, - ValueChanged? onSuccess, - ValueChanged? onError, - VoidCallback? onCancel, - CertificateValidator? onCertificateValidate, - ThemeData? themeData, - Map? textLocales - }) => Navigator.push(context, - MaterialPageRoute(builder: (context) => - OAuthWebScreen( - authorizationEndpointUrl: authorizationEndpointUrl, - tokenEndpointUrl: tokenEndpointUrl, - redirectUrl: redirectUrl, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - loginHint: loginHint, - promptValues: promptValues, - onSuccess: onSuccess, - onError: onError, - onCancel: onCancel, - onCertificateValidate: onCertificateValidate, - themeData: themeData, - textLocales: textLocales, - ) - )); - + static Future? start( + {Key? key, + required BuildContext context, + required String authorizationEndpointUrl, + required String tokenEndpointUrl, + required String redirectUrl, + required String clientId, + String? clientSecret, + List? scopes, + String? loginHint, + List? promptValues, + ValueChanged? onSuccess, + ValueChanged? onError, + VoidCallback? onCancel, + CertificateValidator? onCertificateValidate, + ThemeData? themeData, + Map? textLocales}) => + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => OAuthWebScreen( + authorizationEndpointUrl: authorizationEndpointUrl, + tokenEndpointUrl: tokenEndpointUrl, + redirectUrl: redirectUrl, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + loginHint: loginHint, + promptValues: promptValues, + onSuccess: onSuccess, + onError: onError, + onCancel: onCancel, + onCertificateValidate: onCertificateValidate, + themeData: themeData, + textLocales: textLocales, + ))); final String authorizationEndpointUrl; final String tokenEndpointUrl; @@ -137,11 +134,10 @@ class OAuthWebScreen extends StatelessWidget { } Future onBackPressed() async { - if(!((await paymentViewStateKey.currentState?.onBackPressed()) ?? false)) { + if (!((await paymentViewStateKey.currentState?.onBackPressed()) ?? false)) { return false; } onCancel?.call(); return true; } - } diff --git a/lib/src/oauth_web_view.dart b/lib/src/oauth_web_view.dart index 4428d9b..dd35405 100644 --- a/lib/src/oauth_web_view.dart +++ b/lib/src/oauth_web_view.dart @@ -12,14 +12,14 @@ import 'package:webviewx/webviewx.dart'; typedef CertificateValidator = bool Function(X509Certificate certificate); class OAuthWebView extends StatefulWidget { - static const String firstLoadHeroTag = 'firstLoadOAuthWebAuthHeroTag'; static const String backButtonTooltipKey = 'backButtonTooltipKey'; static const String forwardButtonTooltipKey = 'forwardButtonTooltipKey'; static const String reloadButtonTooltipKey = 'reloadButtonTooltipKey'; static const String clearCacheButtonTooltipKey = 'clearCacheButtonTooltipKey'; static const String closeButtonTooltipKey = 'closeButtonTooltipKey'; - static const String clearCacheWarningMessageKey = 'clearCacheWarningMessageKey'; + static const String clearCacheWarningMessageKey = + 'clearCacheWarningMessageKey'; final String authorizationEndpointUrl; final String tokenEndpointUrl; @@ -70,8 +70,8 @@ class OAuthWebView extends StatefulWidget { OAuthWebViewState createState() => OAuthWebViewState(); } -class OAuthWebViewState extends State with WidgetsBindingObserver { - +class OAuthWebViewState extends State + with WidgetsBindingObserver { bool ready = false; bool showToolbar = false; bool isLoading = true; @@ -100,7 +100,7 @@ class OAuthWebViewState extends State with WidgetsBindingObserver @override void initState() { super.initState(); - toolbarTimerShow = Timer(const Duration(seconds: 5), (){ + toolbarTimerShow = Timer(const Duration(seconds: 5), () { setState(() { showToolbar = true; }); @@ -119,32 +119,44 @@ class OAuthWebViewState extends State with WidgetsBindingObserver ); authorizationUri = authorizationUri.replace( - queryParameters: Map.from(authorizationUri.queryParameters) - ..addAll({ - 'state': const Base64Encoder.urlSafe().convert(DateTime.now().toIso8601String().codeUnits), - 'nonce': const Base64Encoder.urlSafe().convert(DateTime.now().millisecondsSinceEpoch.toString().codeUnits), - if (widget.loginHint != null) 'login_hint': widget.loginHint!, - if (widget.promptValues?.isNotEmpty ?? false) 'prompt': widget.promptValues!.join(' '), - })); + queryParameters: Map.from(authorizationUri.queryParameters) + ..addAll({ + 'state': const Base64Encoder.urlSafe() + .convert(DateTime.now().toIso8601String().codeUnits), + 'nonce': const Base64Encoder.urlSafe().convert( + DateTime.now().millisecondsSinceEpoch.toString().codeUnits), + if (widget.loginHint != null) 'login_hint': widget.loginHint!, + if (widget.promptValues?.isNotEmpty ?? false) + 'prompt': widget.promptValues!.join(' '), + })); webView = initWebView(); } void initTooltips() { - if(tooltipsAlreadyInitialized) return; - backButtonTooltip = widget.textLocales?[OAuthWebView.backButtonTooltipKey] ?? 'Go back'; - forwardButtonTooltip = widget.textLocales?[OAuthWebView.forwardButtonTooltipKey] ?? 'Go forward'; - reloadButtonTooltip = widget.textLocales?[OAuthWebView.reloadButtonTooltipKey] ?? 'Reload'; - clearCacheButtonTooltip = widget.textLocales?[OAuthWebView.clearCacheButtonTooltipKey] ?? 'Clear cache'; - closeButtonTooltip = widget.textLocales?[OAuthWebView.closeButtonTooltipKey] ?? MaterialLocalizations.of(context).closeButtonTooltip; - clearCacheWarningMessage = widget.textLocales?[OAuthWebView.clearCacheWarningMessageKey] ?? 'Are you sure you want to clear cache?'; + if (tooltipsAlreadyInitialized) return; + backButtonTooltip = + widget.textLocales?[OAuthWebView.backButtonTooltipKey] ?? 'Go back'; + forwardButtonTooltip = + widget.textLocales?[OAuthWebView.forwardButtonTooltipKey] ?? + 'Go forward'; + reloadButtonTooltip = + widget.textLocales?[OAuthWebView.reloadButtonTooltipKey] ?? 'Reload'; + clearCacheButtonTooltip = + widget.textLocales?[OAuthWebView.clearCacheButtonTooltipKey] ?? + 'Clear cache'; + closeButtonTooltip = + widget.textLocales?[OAuthWebView.closeButtonTooltipKey] ?? + MaterialLocalizations.of(context).closeButtonTooltip; + clearCacheWarningMessage = + widget.textLocales?[OAuthWebView.clearCacheWarningMessageKey] ?? + 'Are you sure you want to clear cache?'; tooltipsAlreadyInitialized = true; } Widget initWebView() { - final Widget content; - if(kIsWeb) { + if (kIsWeb) { // content = WebView( // initialUrl: authorizationUri.toString(), // javascriptMode: JavascriptMode.unrestricted, @@ -167,14 +179,16 @@ class OAuthWebViewState extends State with WidgetsBindingObserver initialContent: authorizationUri.toString(), initialSourceType: SourceType.url, javascriptMode: JavascriptMode.unrestricted, - userAgent: 'Mozilla/5.0', /// This custom userAgent is mandatory due to security constraints of Google's OAuth2 policies (https://developers.googleblog.com/2021/06/upcoming-security-changes-to-googles-oauth-2.0-authorization-endpoint.html) + userAgent: 'Mozilla/5.0', + + /// This custom userAgent is mandatory due to security constraints of Google's OAuth2 policies (https://developers.googleblog.com/2021/06/upcoming-security-changes-to-googles-oauth-2.0-authorization-endpoint.html) onWebViewCreated: (controller) { webViewXController = controller; }, navigationDelegate: (request) async => - onNavigateTo(request.content.source) ? - NavigationDecision.navigate : - NavigationDecision.prevent, + onNavigateTo(request.content.source) + ? NavigationDecision.navigate + : NavigationDecision.prevent, onPageFinished: (url) => hideLoading(), ); } else { @@ -188,26 +202,30 @@ class OAuthWebViewState extends State with WidgetsBindingObserver crossPlatform: InAppWebViewOptions( useShouldOverrideUrlLoading: true, supportZoom: false, - userAgent: 'Mozilla/5.0', /// This custom userAgent is mandatory due to security constraints of Google's OAuth2 policies (https://developers.googleblog.com/2021/06/upcoming-security-changes-to-googles-oauth-2.0-authorization-endpoint.html) + userAgent: 'Mozilla/5.0', + + /// This custom userAgent is mandatory due to security constraints of Google's OAuth2 policies (https://developers.googleblog.com/2021/06/upcoming-security-changes-to-googles-oauth-2.0-authorization-endpoint.html) ), ), initialUrlRequest: URLRequest(url: authorizationUri), onReceivedServerTrustAuthRequest: (controller, challenge) async { - return ServerTrustAuthResponse(action: ServerTrustAuthResponseAction.PROCEED); + return ServerTrustAuthResponse( + action: ServerTrustAuthResponseAction.PROCEED); }, onWebViewCreated: (controller) { inAppWebViewControllerController = controller; }, shouldOverrideUrlLoading: (controller, navigationAction) async { final url = navigationAction.request.url?.toString() ?? ''; - return onNavigateTo(url) ? - NavigationActionPolicy.ALLOW : - NavigationActionPolicy.CANCEL; + return onNavigateTo(url) + ? NavigationActionPolicy.ALLOW + : NavigationActionPolicy.CANCEL; }, onLoadStart: (controller, url) async { - if(url == authorizationUri) { - final certificate = (await controller.getCertificate())?.x509Certificate; - if(certificate != null && !onCertificateValidate(certificate)) { + if (url == authorizationUri) { + final certificate = + (await controller.getCertificate())?.x509Certificate; + if (certificate != null && !onCertificateValidate(certificate)) { onError(const CertificateException('Invalid certificate')); } } @@ -221,13 +239,15 @@ class OAuthWebViewState extends State with WidgetsBindingObserver } return GestureDetector( - onLongPressDown: (details) {},/// To avoid long press for text selection or open link on new tab + onLongPressDown: (details) {}, + + /// To avoid long press for text selection or open link on new tab child: content, ); } void showLoading() { - if(!isLoading) { + if (!isLoading) { setState(() { isLoading = true; }); @@ -235,7 +255,7 @@ class OAuthWebViewState extends State with WidgetsBindingObserver } Future hideLoading() async { - if(isLoading) { + if (isLoading) { ready = true; showToolbar = true; toolbarTimerShow.cancel(); @@ -247,7 +267,7 @@ class OAuthWebViewState extends State with WidgetsBindingObserver } bool onNavigateTo(String url) { - if(url != 'about:blank') showLoading(); + if (url != 'about:blank') showLoading(); if (url.startsWith(redirectUrlEncoded)) { onSuccess(url); return false; @@ -260,10 +280,9 @@ class OAuthWebViewState extends State with WidgetsBindingObserver final parameters = Uri.dataFromString(responseRedirect).queryParameters; try { - final client = await authorizationCodeGrant.handleAuthorizationResponse(parameters); - widget.onSuccess( - client.credentials - ); + final client = + await authorizationCodeGrant.handleAuthorizationResponse(parameters); + widget.onSuccess(client.credentials); } catch (e) { onError(e); } @@ -286,13 +305,14 @@ class OAuthWebViewState extends State with WidgetsBindingObserver String? tooltip, VoidCallback? onPressed, bool respectLoading = true, - }) => IconButton( - iconSize: 30, - tooltip: tooltip, - icon: Icon(iconData), - color: Theme.of(context).colorScheme.secondary, - onPressed: respectLoading && isLoading ? null : onPressed, - ); + }) => + IconButton( + iconSize: 30, + tooltip: tooltip, + icon: Icon(iconData), + color: Theme.of(context).colorScheme.secondary, + onPressed: respectLoading && isLoading ? null : onPressed, + ); @override Widget build(BuildContext context) { @@ -312,7 +332,9 @@ class OAuthWebViewState extends State with WidgetsBindingObserver tag: OAuthWebView.firstLoadHeroTag, child: AnimatedSwitcher( duration: const Duration(milliseconds: 200), - child: !ready && isLoading ? const CircularProgressIndicator() : const SizedBox(), + child: !ready && isLoading + ? const CircularProgressIndicator() + : const SizedBox(), ), ), ), @@ -344,7 +366,8 @@ class OAuthWebViewState extends State with WidgetsBindingObserver iconButton( iconData: Icons.arrow_forward_ios_rounded, tooltip: forwardButtonTooltip, - onPressed: !allowGoForward ? null : () => controllerGoForward(), + onPressed: + !allowGoForward ? null : () => controllerGoForward(), ), iconButton( iconData: Icons.refresh_rounded, @@ -355,27 +378,31 @@ class OAuthWebViewState extends State with WidgetsBindingObserver iconData: Icons.cleaning_services_rounded, tooltip: clearCacheButtonTooltip, onPressed: () { - showDialog(context: context, builder: (context) { - return AlertDialog( - title: Text(clearCacheButtonTooltip), - content: Text(clearCacheWarningMessage), - actions: [ - TextButton( - child: Text(MaterialLocalizations.of(context).cancelButtonLabel), - onPressed: () { - Navigator.pop(context); - }, - ), - TextButton( - child: Text(MaterialLocalizations.of(context).okButtonLabel), - onPressed: () { - Navigator.pop(context); - controllerClearCache(); - }, - ), - ], - ); - }); + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(clearCacheButtonTooltip), + content: Text(clearCacheWarningMessage), + actions: [ + TextButton( + child: Text(MaterialLocalizations.of(context) + .cancelButtonLabel), + onPressed: () { + Navigator.pop(context); + }, + ), + TextButton( + child: Text(MaterialLocalizations.of(context) + .okButtonLabel), + onPressed: () { + Navigator.pop(context); + controllerClearCache(); + }, + ), + ], + ); + }); }, ), iconButton( @@ -391,13 +418,14 @@ class OAuthWebViewState extends State with WidgetsBindingObserver ); }, ); - return widget.themeData != null ? Theme( - data: widget.themeData!, - child: content, - ) : content; + return widget.themeData != null + ? Theme( + data: widget.themeData!, + child: content, + ) + : content; } - Future controllerGoBack() async { showLoading(); webViewXController?.goBack(); @@ -424,11 +452,15 @@ class OAuthWebViewState extends State with WidgetsBindingObserver } Future controllerCanGoForward() async { - return await webViewXController?.canGoForward() ?? await inAppWebViewControllerController?.canGoForward() ?? false; + return await webViewXController?.canGoForward() ?? + await inAppWebViewControllerController?.canGoForward() ?? + false; } Future controllerCanGoBack() async { - return await webViewXController?.canGoBack() ?? await inAppWebViewControllerController?.canGoBack() ?? false; + return await webViewXController?.canGoBack() ?? + await inAppWebViewControllerController?.canGoBack() ?? + false; } @override @@ -443,7 +475,7 @@ class OAuthWebViewState extends State with WidgetsBindingObserver } Future onBackPressed() async { - if(await controllerCanGoBack()) { + if (await controllerCanGoBack()) { controllerGoBack(); return false; } diff --git a/test/oauth_webauth_test.dart b/test/oauth_webauth_test.dart index 51560e0..a08c2ed 100644 --- a/test/oauth_webauth_test.dart +++ b/test/oauth_webauth_test.dart @@ -1,5 +1,3 @@ // import 'package:flutter_test/flutter_test.dart'; - -void main() { -} +void main() {}