From 24796723d29b55c89a3eb50fb5bc35660f2e74a5 Mon Sep 17 00:00:00 2001 From: Luis Date: Wed, 2 Mar 2022 13:40:34 -0500 Subject: [PATCH] - `contentLocale` property can be set to indicate the web auth to use that locale for internationalization. --- .pubignore | 2 +- CHANGELOG.md | 4 ++ README.md | 9 ++- example/.gitignore | 2 +- example/.pubignore | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 6 +- example/lib/main.dart | 74 +++++++++++++------- example/pubspec.lock | 13 +++- lib/src/oauth_web_screen.dart | 40 ++++++----- lib/src/oauth_web_view.dart | 9 ++- pubspec.yaml | 2 +- 11 files changed, 109 insertions(+), 54 deletions(-) diff --git a/.pubignore b/.pubignore index 5a17b5f..3294f1a 100644 --- a/.pubignore +++ b/.pubignore @@ -27,6 +27,6 @@ .packages .pub-cache/ .pub/ -/build/ +build/ .flutter-plugins .flutter-plugins-dependencies \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 47bd280..237957a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ Types of changes - `Fixed` for any bug fixes. - `Security` in case of vulnerabilities. +## 1.1.0+3 (2022-03-02) +### Added +- `contentLocale` property can be set to indicate the web auth to use that locale for internationalization. + ## 1.0.0+2 ### Changed - README updated diff --git a/README.md b/README.md index bc3dccc..580730a 100644 --- a/README.md +++ b/README.md @@ -16,16 +16,19 @@ Other plugins like [Flutter AppAuth](https://pub.dev/packages/flutter_appauth) u - UI: users will notice a breaking UI difference when system browser opens to handle the identity provider authentication process. - In iOS an annoying system dialog shows up every time the user tries to authenticate, indicating that the current app and browser could share their information. - Your system browser cache is shared with your app which is good and **bad**, bad because any cache problem due to your every day navigation use could affect your app authentication and the only way to clean cache it's by cleaning system browser cache at operating system level. +- Authentication page will use the locale from System Browser which in fact uses the Operating System locale, this means if your app uses different language than the Operating System then authentication page will show different internationalization than your app. ## Features - With this plugin you will get: - Full control over the UI, WebView will run inside your app so Theme and Color Scheme will be yours to choose, in fact you can add AppBar or FloatingActionButton or whatever you thinks it's necessary to your UI. - No system dialog will be shown when users tries to authenticate. - Users will not be affected by any system browser problem cache and also will be able to clean app browser cache from the authentication screen itself. +- Authentication page locale can be set from app using the `contentLocale` property to ensure the same locale. By default Operating System locale will be used if no `contentLocale` is specified. -## Getting started +**Note:** +- `contentLocale` will apply only if the authentication page supports the specified `Locale('...')` and accepts the header: `'Accept-Language': 'es-ES'` +## Getting started As stated before this plugin uses WebView implementation specifically the plugin [flutter_inappwebview](https://pub.dev/packages/flutter_inappwebview). For any WebView related problem please check the documentation of that plugin at [docs](https://inappwebview.dev/docs/). ### Android setup @@ -88,6 +91,7 @@ void loginV1() async { /// If false is returned then a CertificateException() will be thrown return true; }, + contentLocale: Locale('es'), textLocales: { ///Optionally texts can be localized OAuthWebView.backButtonTooltipKey: 'Ir atrás', @@ -131,6 +135,7 @@ void loginV2() { /// If false is returned then a CertificateException() will be thrown return true; }, + contentLocale: Locale('es'), textLocales: { ///Optionally text can be localized OAuthWebView.backButtonTooltipKey: 'Ir atrás', diff --git a/example/.gitignore b/example/.gitignore index 0fa6b67..0d920e6 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -29,7 +29,7 @@ .packages .pub-cache/ .pub/ -/build/ +build/ # Web related lib/generated_plugin_registrant.dart diff --git a/example/.pubignore b/example/.pubignore index 5a17b5f..3294f1a 100644 --- a/example/.pubignore +++ b/example/.pubignore @@ -27,6 +27,6 @@ .packages .pub-cache/ .pub/ -/build/ +build/ .flutter-plugins .flutter-plugins-dependencies \ No newline at end of file diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 5f76db1..b90f882 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -76,7 +76,6 @@ E0DAAC17531E652E02363B34 /* Pods-Runner.release.xcconfig */, 47363B92604AC4D5C9BCBE9B /* Pods-Runner.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -356,6 +355,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = PT79KLNGFU; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -484,6 +484,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = PT79KLNGFU; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -506,6 +507,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = PT79KLNGFU; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/example/lib/main.dart b/example/lib/main.dart index c31e683..7e316f6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -10,6 +10,7 @@ void main() { class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -49,6 +50,8 @@ class _MyHomePageState extends State { .split(' '); String authResponse = 'Authorization data will be shown here'; + Locale? contentLocale; + final locales = const [null, Locale('es'), Locale('en')]; @override Widget build(BuildContext context) { @@ -65,6 +68,24 @@ class _MyHomePageState extends State { authResponse, ), const SizedBox(height: 16), + SizedBox( + width: 120, + child: DropdownButtonFormField( + items: locales + .map((e) => DropdownMenuItem( + value: e, + child: Text(e?.toLanguageTag() ?? 'Default OS'))) + .toList(), + value: contentLocale, + onChanged: (locale) { + setState(() { + contentLocale = locale; + }); + }, + decoration: + const InputDecoration(label: Text('Content language')), + ), + ), ElevatedButton( onPressed: loginV1, child: const Text('Login variant 1'), @@ -85,31 +106,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: '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é?', - }); + 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é?', + }, + contentLocale: contentLocale, + ); if (result != null) { if (result is Credentials) { authResponse = getPrettyCredentialsJson(result); @@ -149,6 +172,7 @@ class _MyHomePageState extends State { OAuthWebView.clearCacheWarningMessageKey: '¿Está seguro que desea limpiar la caché?', }, + contentLocale: contentLocale, onSuccess: (credentials) { setState(() { authResponse = getPrettyCredentialsJson(credentials); diff --git a/example/pubspec.lock b/example/pubspec.lock index 4c06f1c..c6d1bc8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -109,6 +109,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: @@ -129,14 +136,14 @@ packages: path: ".." relative: true source: path - version: "1.0.0+1" + version: "1.1.0+3" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" plugin_platform_interface: dependency: transitive description: @@ -197,7 +204,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.3" + version: "0.4.9" typed_data: dependency: transitive description: diff --git a/lib/src/oauth_web_screen.dart b/lib/src/oauth_web_screen.dart index ecaa917..5f23d12 100644 --- a/lib/src/oauth_web_screen.dart +++ b/lib/src/oauth_web_screen.dart @@ -3,23 +3,25 @@ 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}) => + 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, + Locale? contentLocale, + }) => Navigator.push( context, MaterialPageRoute( @@ -38,6 +40,7 @@ class OAuthWebScreen extends StatelessWidget { onCertificateValidate: onCertificateValidate, themeData: themeData, textLocales: textLocales, + contentLocale: contentLocale, ))); final String authorizationEndpointUrl; @@ -65,6 +68,7 @@ class OAuthWebScreen extends StatelessWidget { final ThemeData? themeData; final Map? textLocales; + final Locale? contentLocale; late final BuildContext context; final paymentViewStateKey = GlobalKey(); @@ -85,6 +89,7 @@ class OAuthWebScreen extends StatelessWidget { this.onCertificateValidate, this.themeData, this.textLocales, + this.contentLocale, }) : super(key: key); @override @@ -111,6 +116,7 @@ class OAuthWebScreen extends StatelessWidget { onCertificateValidate: onCertificateValidate, themeData: themeData, textLocales: textLocales, + contentLocale: contentLocale, ), ), ); diff --git a/lib/src/oauth_web_view.dart b/lib/src/oauth_web_view.dart index dd35405..a4fe135 100644 --- a/lib/src/oauth_web_view.dart +++ b/lib/src/oauth_web_view.dart @@ -47,6 +47,7 @@ class OAuthWebView extends StatefulWidget { final ThemeData? themeData; final Map? textLocales; + final Locale? contentLocale; const OAuthWebView({ Key? key, @@ -64,6 +65,7 @@ class OAuthWebView extends StatefulWidget { this.onCertificateValidate, this.themeData, this.textLocales, + this.contentLocale, }) : super(key: key); @override @@ -207,7 +209,12 @@ class OAuthWebViewState extends State /// 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), + initialUrlRequest: URLRequest( + url: authorizationUri, + headers: widget.contentLocale == null + ? null + : {'Accept-Language': widget.contentLocale!.toLanguageTag()}, + ), onReceivedServerTrustAuthRequest: (controller, challenge) async { return ServerTrustAuthResponse( action: ServerTrustAuthResponseAction.PROCEED); diff --git a/pubspec.yaml b/pubspec.yaml index e2dc650..66df67a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: oauth_webauth description: This plugin provides an alternative to AppAuth using WebView for OAuth 2.0 authorization/authentication. -version: 1.0.0+2 +version: 1.1.0+3 homepage: https://github.com/luis901101/oauth_webauth environment: