From 7442bba22f6e4f764f321e6a14bccb37b0d099f9 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Sat, 15 Feb 2020 20:10:36 +1100 Subject: [PATCH] [flutter_appauth_platform_interface] create platform interface library (#82) * move plugin to allow for adding platform interface later * create platform interface package with default method channel implementation * throw ArgumentError when grant type cannot be inferred * update platform interface pubspec and changelog * add execution of platform interface tests to cirrus script * fix path in cirrus script --- .cirrus.yml | 13 +- .idea/libraries/Dart_SDK.xml | 19 -- .idea/libraries/Flutter_for_Android.xml | 9 - .idea/modules.xml | 10 -- .../example_lib_main_dart.xml | 6 - .idea/workspace.xml | 45 ----- .vscode/launch.json | 13 -- README.md | 125 +------------ CHANGELOG.md => flutter_appauth/CHANGELOG.md | 0 LICENSE => flutter_appauth/LICENSE | 0 flutter_appauth/README.md | 122 +++++++++++++ .../analysis_options.yaml | 0 .../android}/.gitignore | 0 .../android}/build.gradle | 0 .../android}/gradle.properties | 0 .../android}/settings.gradle | 0 .../android}/src/main/AndroidManifest.xml | 0 .../flutterappauth/FlutterAppauthPlugin.java | 0 .../InsecureConnectionBuilder.java | 0 .../example}/.gitignore | 0 .../example}/.metadata | 0 .../example}/README.md | 0 .../example}/analysis_options.yaml | 0 .../example}/android/.gitignore | 0 .../example}/android/app/build.gradle | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../flutterappauthexample/MainActivity.java | 0 .../main/res/drawable/launch_background.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values/styles.xml | 0 .../example}/android/build.gradle | 0 .../example}/android/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../example}/android/settings.gradle | 0 .../example}/ios/.gitignore | 0 .../ios/Flutter/AppFrameworkInfo.plist | 0 .../example}/ios/Flutter/Debug.xcconfig | 0 .../example}/ios/Flutter/Release.xcconfig | 0 .../ios/Runner.xcodeproj/project.pbxproj | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../example}/ios/Runner/AppDelegate.h | 0 .../example}/ios/Runner/AppDelegate.m | 0 .../AppIcon.appiconset/Contents.json | 0 .../Icon-App-1024x1024@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../Icon-App-83.5x83.5@2x.png | Bin .../LaunchImage.imageset/Contents.json | 0 .../LaunchImage.imageset/LaunchImage.png | Bin .../LaunchImage.imageset/LaunchImage@2x.png | Bin .../LaunchImage.imageset/LaunchImage@3x.png | Bin .../LaunchImage.imageset/README.md | 0 .../Runner/Base.lproj/LaunchScreen.storyboard | 0 .../ios/Runner/Base.lproj/Main.storyboard | 0 .../example}/ios/Runner/Info.plist | 0 .../example}/ios/Runner/main.m | 0 .../example}/lib/main.dart | 0 .../example}/pubspec.yaml | 0 .../flutter_appauth.iml | 0 {ios => flutter_appauth/ios}/.gitignore | 0 {ios => flutter_appauth/ios}/Assets/.gitkeep | 0 .../ios}/Classes/FlutterAppauthPlugin.h | 0 .../ios}/Classes/FlutterAppauthPlugin.m | 0 .../ios}/flutter_appauth.podspec | 0 .../lib}/flutter_appauth.dart | 0 .../lib}/src/authorization_parameters.dart | 0 .../lib}/src/authorization_request.dart | 0 .../lib}/src/authorization_response.dart | 0 .../authorization_service_configuration.dart | 0 .../lib}/src/authorization_token_request.dart | 0 .../src/authorization_token_response.dart | 0 .../lib}/src/common_request_details.dart | 0 .../lib}/src/flutter_appauth.dart | 0 .../lib}/src/grant_types.dart | 0 .../lib}/src/mappable.dart | 0 .../lib}/src/token_request.dart | 0 .../lib}/src/token_response.dart | 0 pubspec.yaml => flutter_appauth/pubspec.yaml | 0 flutter_appauth_platform_interface/.gitignore | 75 ++++++++ .../.metadata | 4 +- .../CHANGELOG.md | 3 + flutter_appauth_platform_interface/LICENSE | 29 +++ flutter_appauth_platform_interface/README.md | 3 + .../flutter_appauth_platform_interface.dart | 9 + .../lib/src/authorization_parameters.dart | 17 ++ .../lib/src/authorization_request.dart | 28 +++ .../lib/src/authorization_response.dart | 13 ++ .../authorization_service_configuration.dart | 17 ++ .../lib/src/authorization_token_request.dart | 31 ++++ .../lib/src/authorization_token_response.dart | 17 ++ .../lib/src/common_request_details.dart | 42 +++++ .../lib/src/flutter_appauth_platform.dart | 47 +++++ .../lib/src/grant_types.dart | 5 + .../lib/src/mappable.dart | 3 + .../src/method_channel_flutter_appauth.dart | 67 +++++++ .../lib/src/token_request.dart | 76 ++++++++ .../lib/src/token_response.dart | 22 +++ .../pubspec.yaml | 18 ++ .../method_channel_flutter_appauth_test.dart | 167 ++++++++++++++++++ 118 files changed, 828 insertions(+), 227 deletions(-) delete mode 100644 .idea/libraries/Dart_SDK.xml delete mode 100644 .idea/libraries/Flutter_for_Android.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/runConfigurations/example_lib_main_dart.xml delete mode 100644 .idea/workspace.xml delete mode 100644 .vscode/launch.json rename CHANGELOG.md => flutter_appauth/CHANGELOG.md (100%) rename LICENSE => flutter_appauth/LICENSE (100%) create mode 100644 flutter_appauth/README.md rename analysis_options.yaml => flutter_appauth/analysis_options.yaml (100%) rename {android => flutter_appauth/android}/.gitignore (100%) rename {android => flutter_appauth/android}/build.gradle (100%) rename {android => flutter_appauth/android}/gradle.properties (100%) rename {android => flutter_appauth/android}/settings.gradle (100%) rename {android => flutter_appauth/android}/src/main/AndroidManifest.xml (100%) rename {android => flutter_appauth/android}/src/main/java/io/crossingthestreams/flutterappauth/FlutterAppauthPlugin.java (100%) rename {android => flutter_appauth/android}/src/main/java/io/crossingthestreams/flutterappauth/InsecureConnectionBuilder.java (100%) rename {example => flutter_appauth/example}/.gitignore (100%) rename {example => flutter_appauth/example}/.metadata (100%) rename {example => flutter_appauth/example}/README.md (100%) rename {example => flutter_appauth/example}/analysis_options.yaml (100%) rename {example => flutter_appauth/example}/android/.gitignore (100%) rename {example => flutter_appauth/example}/android/app/build.gradle (100%) rename {example => flutter_appauth/example}/android/app/src/main/AndroidManifest.xml (100%) rename {example => flutter_appauth/example}/android/app/src/main/java/io/crossingthestreams/flutterappauthexample/MainActivity.java (100%) rename {example => flutter_appauth/example}/android/app/src/main/res/drawable/launch_background.xml (100%) rename {example => flutter_appauth/example}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {example => flutter_appauth/example}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {example => flutter_appauth/example}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {example => flutter_appauth/example}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {example => flutter_appauth/example}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {example => flutter_appauth/example}/android/app/src/main/res/values/styles.xml (100%) rename {example => flutter_appauth/example}/android/build.gradle (100%) rename {example => flutter_appauth/example}/android/gradle.properties (100%) rename {example => flutter_appauth/example}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename {example => flutter_appauth/example}/android/settings.gradle (100%) rename {example => flutter_appauth/example}/ios/.gitignore (100%) rename {example => flutter_appauth/example}/ios/Flutter/AppFrameworkInfo.plist (100%) rename {example => flutter_appauth/example}/ios/Flutter/Debug.xcconfig (100%) rename {example => flutter_appauth/example}/ios/Flutter/Release.xcconfig (100%) rename {example => flutter_appauth/example}/ios/Runner.xcodeproj/project.pbxproj (100%) rename {example => flutter_appauth/example}/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {example => flutter_appauth/example}/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename {example => flutter_appauth/example}/ios/Runner.xcworkspace/contents.xcworkspacedata (100%) rename {example => flutter_appauth/example}/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {example => flutter_appauth/example}/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {example => flutter_appauth/example}/ios/Runner/AppDelegate.h (100%) rename {example => flutter_appauth/example}/ios/Runner/AppDelegate.m (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (100%) rename {example => flutter_appauth/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md (100%) rename {example => flutter_appauth/example}/ios/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename {example => flutter_appauth/example}/ios/Runner/Base.lproj/Main.storyboard (100%) rename {example => flutter_appauth/example}/ios/Runner/Info.plist (100%) rename {example => flutter_appauth/example}/ios/Runner/main.m (100%) rename {example => flutter_appauth/example}/lib/main.dart (100%) rename {example => flutter_appauth/example}/pubspec.yaml (100%) rename flutter_appauth.iml => flutter_appauth/flutter_appauth.iml (100%) rename {ios => flutter_appauth/ios}/.gitignore (100%) rename {ios => flutter_appauth/ios}/Assets/.gitkeep (100%) rename {ios => flutter_appauth/ios}/Classes/FlutterAppauthPlugin.h (100%) rename {ios => flutter_appauth/ios}/Classes/FlutterAppauthPlugin.m (100%) rename {ios => flutter_appauth/ios}/flutter_appauth.podspec (100%) rename {lib => flutter_appauth/lib}/flutter_appauth.dart (100%) rename {lib => flutter_appauth/lib}/src/authorization_parameters.dart (100%) rename {lib => flutter_appauth/lib}/src/authorization_request.dart (100%) rename {lib => flutter_appauth/lib}/src/authorization_response.dart (100%) rename {lib => flutter_appauth/lib}/src/authorization_service_configuration.dart (100%) rename {lib => flutter_appauth/lib}/src/authorization_token_request.dart (100%) rename {lib => flutter_appauth/lib}/src/authorization_token_response.dart (100%) rename {lib => flutter_appauth/lib}/src/common_request_details.dart (100%) rename {lib => flutter_appauth/lib}/src/flutter_appauth.dart (100%) rename {lib => flutter_appauth/lib}/src/grant_types.dart (100%) rename {lib => flutter_appauth/lib}/src/mappable.dart (100%) rename {lib => flutter_appauth/lib}/src/token_request.dart (100%) rename {lib => flutter_appauth/lib}/src/token_response.dart (100%) rename pubspec.yaml => flutter_appauth/pubspec.yaml (100%) create mode 100644 flutter_appauth_platform_interface/.gitignore rename .metadata => flutter_appauth_platform_interface/.metadata (75%) create mode 100644 flutter_appauth_platform_interface/CHANGELOG.md create mode 100644 flutter_appauth_platform_interface/LICENSE create mode 100644 flutter_appauth_platform_interface/README.md create mode 100644 flutter_appauth_platform_interface/lib/flutter_appauth_platform_interface.dart create mode 100644 flutter_appauth_platform_interface/lib/src/authorization_parameters.dart create mode 100644 flutter_appauth_platform_interface/lib/src/authorization_request.dart create mode 100644 flutter_appauth_platform_interface/lib/src/authorization_response.dart create mode 100644 flutter_appauth_platform_interface/lib/src/authorization_service_configuration.dart create mode 100644 flutter_appauth_platform_interface/lib/src/authorization_token_request.dart create mode 100644 flutter_appauth_platform_interface/lib/src/authorization_token_response.dart create mode 100644 flutter_appauth_platform_interface/lib/src/common_request_details.dart create mode 100644 flutter_appauth_platform_interface/lib/src/flutter_appauth_platform.dart create mode 100644 flutter_appauth_platform_interface/lib/src/grant_types.dart create mode 100644 flutter_appauth_platform_interface/lib/src/mappable.dart create mode 100644 flutter_appauth_platform_interface/lib/src/method_channel_flutter_appauth.dart create mode 100644 flutter_appauth_platform_interface/lib/src/token_request.dart create mode 100644 flutter_appauth_platform_interface/lib/src/token_response.dart create mode 100644 flutter_appauth_platform_interface/pubspec.yaml create mode 100644 flutter_appauth_platform_interface/test/method_channel_flutter_appauth_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index dec18122..a540a259 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,3 +1,12 @@ +task: + name: Run platform interface tests + container: + image: cirrusci/flutter:stable + pub_cache: + folder: ~/.pub-cache + build_script: + - cd flutter_appauth_platform_interface + - flutter test task: name: Build Android example container: @@ -5,7 +14,7 @@ task: pub_cache: folder: ~/.pub-cache build_script: - - cd example + - cd flutter_appauth/example - flutter build appbundle task: @@ -15,5 +24,5 @@ task: pub_cache: folder: ~/.pub-cache build_script: - - cd example + - cd flutter_appauth/example - flutter build ios --no-codesign diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml deleted file mode 100644 index 000218f8..00000000 --- a/.idea/libraries/Dart_SDK.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Flutter_for_Android.xml b/.idea/libraries/Flutter_for_Android.xml deleted file mode 100644 index 8f1af711..00000000 --- a/.idea/libraries/Flutter_for_Android.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 8152fccf..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/.idea/runConfigurations/example_lib_main_dart.xml b/.idea/runConfigurations/example_lib_main_dart.xml deleted file mode 100644 index 5fd9159d..00000000 --- a/.idea/runConfigurations/example_lib_main_dart.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index f82f7bb7..00000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 3287bb67..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Flutter", - "request": "launch", - "type": "dart" - } - ] -} \ No newline at end of file diff --git a/README.md b/README.md index 6256f438..793eb79f 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,5 @@ -# Flutter AppAuth Plugin +# flutter_appauth +A Flutter plugin that provides a wrapper for native AppAuth SDKs (https://appauth.io) used authenticating and authorizing users. The repository consists of the following folders -[![pub package](https://img.shields.io/pub/v/flutter_appauth.svg)](https://pub.dartlang.org/packages/flutter_appauth) -[![Build Status](https://api.cirrus-ci.com/github/MaikuB/flutter_appauth.svg)](https://cirrus-ci.com/github/MaikuB/flutter_appauth/) - -A Flutter bridge for AppAuth (https://appauth.io) used authenticating and authorizing users. Note that AppAuth also supports the PKCE extension that is required some providers so this plugin should work with them. - -**IMPORTANT NOTES**: -- This plugin requires apps to be using AndroidX. The Flutter tooling supports creating apps with AndroidX support but requires passing the `androidx` flag. Details on AndroidX compatibility and migration can be found [here](https://flutter.dev/docs/development/packages-and-plugins/androidx-compatibility) -- If Chrome Custom Tabs are not working in your Android app, check to make sure that you have the latest version of this plugin, Android Studio, Gradle distribution and Android Gradle plugin for your app. There was previously a known [issue](https://issuetracker.google.com/issues/119183822) with the Android tooling with AndroidX that should now be resolved since Android Studio 3.4 has been released - - -## Getting Started - -Please see the example that demonstrates how to sign into the IdentityServer4 demo site (https://demo.identityserver.io). It has also been tested with Azure B2C and Google Sign-in. It is suggested that developers check the documentation of the identity provider they are using to see what capabilities it supports e.g. how to logout, what values of the `prompt` parameter it supports etc. API docs can be found [here](https://pub.dartlang.org/documentation/flutter_appauth/latest/) - - -The first step is to create an instance of the plugin - -``` -FlutterAppAuth appAuth = FlutterAppAuth(); -``` - -Afterwards, you'll reach a point where end-users need to be authorized and authenticated. A convenience method is provided that will perform an authorization request and automatically exchange the authorization code. This can be done in a few different ways, one of which is to use the OpenID Connect Discovery - -```dart -final AuthorizationTokenResponse result = await appAuth.authorizeAndExchangeCode( - AuthorizationTokenRequest( - '', - '', - discoveryUrl: '', - scopes: ['openid','profile', 'email', 'offline_access', 'api'], - ), - ); -``` - -Here the `` and `` should be replaced by the values registered with your identity provider. The `` would be the URL for the discovery endpoint exposed by your provider that will return a document containing information about the OAuth 2.0 endpoints among other things. This URL is obtained by concatenating the issuer with the path `/.well-known/openid-configuration`. For example, the full URL for the IdentityServer4 demo site is `https://demo.identityserver.io/.well-known/openid-configuration`. As demonstrated in the above sample code, it's also possible specify the `scopes` being requested. - -Rather than using the full discovery URL, the issuer could be used instead so that the process retrieving the discovery document is skipped - -```dart -final AuthorizationTokenResponse result = await appAuth.authorizeAndExchangeCode( - AuthorizationTokenRequest( - '', - '', - issuer: '', - scopes: ['openid','profile', 'email', 'offline_access', 'api'], - ), - ); -``` - -If you already know the authorization and token endpoints, which may be because discovery isn't supported, then these could be explicitly specified - -```dart -final AuthorizationTokenResponse result = await appAuth.authorizeAndExchangeCode( - AuthorizationTokenRequest( - '', - '', - serviceConfiguration: AuthorizationServiceConfiguration('', ''), - scopes: ['openid','profile', 'email', 'offline_access', 'api'] - ), - ); -``` - -Upon completing the request successfully, the method should return an object (the `result` variable in the above sample code is an instance of the `AuthorizationTokenResponse` class) that contain details that should be stored for future use e.g. access token, refresh token etc. - -If you would prefer to not have the automatic code exchange to happen then can call the `authorize` method instead of the `authorizeAndExchangeCode` method. This will return an instance of the `AuthorizationResponse` class that will contain the code verifier that AppAuth generated (as part of implementing PKCE) when issuing the authorization request, the authorization code and additional parameters should they exist. Both of the code verifier and authorization code would need to be stored so they can then be reused to exchange the code later on e.g. - -```dart -final TokenResponse result = await appAuth.token(TokenRequest('', '', - authorizationCode: '', - discoveryUrl: '', - codeVerifier: '', - scopes: ['openid','profile', 'email', 'offline_access', 'api'])); -``` - -### Refreshing tokens - -Some providers may return a refresh token that could be used to refresh short-lived access tokens. A request to get a new access token before it expires could be made that would like similar to the following code - -```dart -final TokenResponse result = await appAuth.token(TokenRequest('', '', - discoveryUrl: '', - refreshToken: '', - scopes: ['openid','profile', 'email', 'offline_access', 'api'])); -``` - -## Android setup - -Go to the `build.gradle` file for your Android app to specify the custom scheme so that there should be a section in it that look similar to the following but replace `` with the desired value - -``` -... -android { - ... - defaultConfig { - ... - manifestPlaceholders = [ - 'appAuthRedirectScheme': '' - ] - } -} -``` - -## iOS setup - -Go to the `Info.plist` for your iOS app to specify the custom scheme so that there should be a section in it that look similar to the following but replace `` with the desired value - - -```xml -CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - - - - -``` +- flutter_appauth: code for the plugin +- flutter_appauth_platform_interface: the code for common platform interface \ No newline at end of file diff --git a/CHANGELOG.md b/flutter_appauth/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to flutter_appauth/CHANGELOG.md diff --git a/LICENSE b/flutter_appauth/LICENSE similarity index 100% rename from LICENSE rename to flutter_appauth/LICENSE diff --git a/flutter_appauth/README.md b/flutter_appauth/README.md new file mode 100644 index 00000000..6256f438 --- /dev/null +++ b/flutter_appauth/README.md @@ -0,0 +1,122 @@ +# Flutter AppAuth Plugin + +[![pub package](https://img.shields.io/pub/v/flutter_appauth.svg)](https://pub.dartlang.org/packages/flutter_appauth) +[![Build Status](https://api.cirrus-ci.com/github/MaikuB/flutter_appauth.svg)](https://cirrus-ci.com/github/MaikuB/flutter_appauth/) + +A Flutter bridge for AppAuth (https://appauth.io) used authenticating and authorizing users. Note that AppAuth also supports the PKCE extension that is required some providers so this plugin should work with them. + +**IMPORTANT NOTES**: +- This plugin requires apps to be using AndroidX. The Flutter tooling supports creating apps with AndroidX support but requires passing the `androidx` flag. Details on AndroidX compatibility and migration can be found [here](https://flutter.dev/docs/development/packages-and-plugins/androidx-compatibility) +- If Chrome Custom Tabs are not working in your Android app, check to make sure that you have the latest version of this plugin, Android Studio, Gradle distribution and Android Gradle plugin for your app. There was previously a known [issue](https://issuetracker.google.com/issues/119183822) with the Android tooling with AndroidX that should now be resolved since Android Studio 3.4 has been released + + +## Getting Started + +Please see the example that demonstrates how to sign into the IdentityServer4 demo site (https://demo.identityserver.io). It has also been tested with Azure B2C and Google Sign-in. It is suggested that developers check the documentation of the identity provider they are using to see what capabilities it supports e.g. how to logout, what values of the `prompt` parameter it supports etc. API docs can be found [here](https://pub.dartlang.org/documentation/flutter_appauth/latest/) + + +The first step is to create an instance of the plugin + +``` +FlutterAppAuth appAuth = FlutterAppAuth(); +``` + +Afterwards, you'll reach a point where end-users need to be authorized and authenticated. A convenience method is provided that will perform an authorization request and automatically exchange the authorization code. This can be done in a few different ways, one of which is to use the OpenID Connect Discovery + +```dart +final AuthorizationTokenResponse result = await appAuth.authorizeAndExchangeCode( + AuthorizationTokenRequest( + '', + '', + discoveryUrl: '', + scopes: ['openid','profile', 'email', 'offline_access', 'api'], + ), + ); +``` + +Here the `` and `` should be replaced by the values registered with your identity provider. The `` would be the URL for the discovery endpoint exposed by your provider that will return a document containing information about the OAuth 2.0 endpoints among other things. This URL is obtained by concatenating the issuer with the path `/.well-known/openid-configuration`. For example, the full URL for the IdentityServer4 demo site is `https://demo.identityserver.io/.well-known/openid-configuration`. As demonstrated in the above sample code, it's also possible specify the `scopes` being requested. + +Rather than using the full discovery URL, the issuer could be used instead so that the process retrieving the discovery document is skipped + +```dart +final AuthorizationTokenResponse result = await appAuth.authorizeAndExchangeCode( + AuthorizationTokenRequest( + '', + '', + issuer: '', + scopes: ['openid','profile', 'email', 'offline_access', 'api'], + ), + ); +``` + +If you already know the authorization and token endpoints, which may be because discovery isn't supported, then these could be explicitly specified + +```dart +final AuthorizationTokenResponse result = await appAuth.authorizeAndExchangeCode( + AuthorizationTokenRequest( + '', + '', + serviceConfiguration: AuthorizationServiceConfiguration('', ''), + scopes: ['openid','profile', 'email', 'offline_access', 'api'] + ), + ); +``` + +Upon completing the request successfully, the method should return an object (the `result` variable in the above sample code is an instance of the `AuthorizationTokenResponse` class) that contain details that should be stored for future use e.g. access token, refresh token etc. + +If you would prefer to not have the automatic code exchange to happen then can call the `authorize` method instead of the `authorizeAndExchangeCode` method. This will return an instance of the `AuthorizationResponse` class that will contain the code verifier that AppAuth generated (as part of implementing PKCE) when issuing the authorization request, the authorization code and additional parameters should they exist. Both of the code verifier and authorization code would need to be stored so they can then be reused to exchange the code later on e.g. + +```dart +final TokenResponse result = await appAuth.token(TokenRequest('', '', + authorizationCode: '', + discoveryUrl: '', + codeVerifier: '', + scopes: ['openid','profile', 'email', 'offline_access', 'api'])); +``` + +### Refreshing tokens + +Some providers may return a refresh token that could be used to refresh short-lived access tokens. A request to get a new access token before it expires could be made that would like similar to the following code + +```dart +final TokenResponse result = await appAuth.token(TokenRequest('', '', + discoveryUrl: '', + refreshToken: '', + scopes: ['openid','profile', 'email', 'offline_access', 'api'])); +``` + +## Android setup + +Go to the `build.gradle` file for your Android app to specify the custom scheme so that there should be a section in it that look similar to the following but replace `` with the desired value + +``` +... +android { + ... + defaultConfig { + ... + manifestPlaceholders = [ + 'appAuthRedirectScheme': '' + ] + } +} +``` + +## iOS setup + +Go to the `Info.plist` for your iOS app to specify the custom scheme so that there should be a section in it that look similar to the following but replace `` with the desired value + + +```xml +CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + + + + +``` diff --git a/analysis_options.yaml b/flutter_appauth/analysis_options.yaml similarity index 100% rename from analysis_options.yaml rename to flutter_appauth/analysis_options.yaml diff --git a/android/.gitignore b/flutter_appauth/android/.gitignore similarity index 100% rename from android/.gitignore rename to flutter_appauth/android/.gitignore diff --git a/android/build.gradle b/flutter_appauth/android/build.gradle similarity index 100% rename from android/build.gradle rename to flutter_appauth/android/build.gradle diff --git a/android/gradle.properties b/flutter_appauth/android/gradle.properties similarity index 100% rename from android/gradle.properties rename to flutter_appauth/android/gradle.properties diff --git a/android/settings.gradle b/flutter_appauth/android/settings.gradle similarity index 100% rename from android/settings.gradle rename to flutter_appauth/android/settings.gradle diff --git a/android/src/main/AndroidManifest.xml b/flutter_appauth/android/src/main/AndroidManifest.xml similarity index 100% rename from android/src/main/AndroidManifest.xml rename to flutter_appauth/android/src/main/AndroidManifest.xml diff --git a/android/src/main/java/io/crossingthestreams/flutterappauth/FlutterAppauthPlugin.java b/flutter_appauth/android/src/main/java/io/crossingthestreams/flutterappauth/FlutterAppauthPlugin.java similarity index 100% rename from android/src/main/java/io/crossingthestreams/flutterappauth/FlutterAppauthPlugin.java rename to flutter_appauth/android/src/main/java/io/crossingthestreams/flutterappauth/FlutterAppauthPlugin.java diff --git a/android/src/main/java/io/crossingthestreams/flutterappauth/InsecureConnectionBuilder.java b/flutter_appauth/android/src/main/java/io/crossingthestreams/flutterappauth/InsecureConnectionBuilder.java similarity index 100% rename from android/src/main/java/io/crossingthestreams/flutterappauth/InsecureConnectionBuilder.java rename to flutter_appauth/android/src/main/java/io/crossingthestreams/flutterappauth/InsecureConnectionBuilder.java diff --git a/example/.gitignore b/flutter_appauth/example/.gitignore similarity index 100% rename from example/.gitignore rename to flutter_appauth/example/.gitignore diff --git a/example/.metadata b/flutter_appauth/example/.metadata similarity index 100% rename from example/.metadata rename to flutter_appauth/example/.metadata diff --git a/example/README.md b/flutter_appauth/example/README.md similarity index 100% rename from example/README.md rename to flutter_appauth/example/README.md diff --git a/example/analysis_options.yaml b/flutter_appauth/example/analysis_options.yaml similarity index 100% rename from example/analysis_options.yaml rename to flutter_appauth/example/analysis_options.yaml diff --git a/example/android/.gitignore b/flutter_appauth/example/android/.gitignore similarity index 100% rename from example/android/.gitignore rename to flutter_appauth/example/android/.gitignore diff --git a/example/android/app/build.gradle b/flutter_appauth/example/android/app/build.gradle similarity index 100% rename from example/android/app/build.gradle rename to flutter_appauth/example/android/app/build.gradle diff --git a/example/android/app/src/main/AndroidManifest.xml b/flutter_appauth/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from example/android/app/src/main/AndroidManifest.xml rename to flutter_appauth/example/android/app/src/main/AndroidManifest.xml diff --git a/example/android/app/src/main/java/io/crossingthestreams/flutterappauthexample/MainActivity.java b/flutter_appauth/example/android/app/src/main/java/io/crossingthestreams/flutterappauthexample/MainActivity.java similarity index 100% rename from example/android/app/src/main/java/io/crossingthestreams/flutterappauthexample/MainActivity.java rename to flutter_appauth/example/android/app/src/main/java/io/crossingthestreams/flutterappauthexample/MainActivity.java diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/flutter_appauth/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable/launch_background.xml rename to flutter_appauth/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter_appauth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to flutter_appauth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/flutter_appauth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to flutter_appauth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/flutter_appauth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to flutter_appauth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter_appauth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to flutter_appauth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/flutter_appauth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to flutter_appauth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/values/styles.xml b/flutter_appauth/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from example/android/app/src/main/res/values/styles.xml rename to flutter_appauth/example/android/app/src/main/res/values/styles.xml diff --git a/example/android/build.gradle b/flutter_appauth/example/android/build.gradle similarity index 100% rename from example/android/build.gradle rename to flutter_appauth/example/android/build.gradle diff --git a/example/android/gradle.properties b/flutter_appauth/example/android/gradle.properties similarity index 100% rename from example/android/gradle.properties rename to flutter_appauth/example/android/gradle.properties diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/flutter_appauth/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to flutter_appauth/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/example/android/settings.gradle b/flutter_appauth/example/android/settings.gradle similarity index 100% rename from example/android/settings.gradle rename to flutter_appauth/example/android/settings.gradle diff --git a/example/ios/.gitignore b/flutter_appauth/example/ios/.gitignore similarity index 100% rename from example/ios/.gitignore rename to flutter_appauth/example/ios/.gitignore diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/flutter_appauth/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from example/ios/Flutter/AppFrameworkInfo.plist rename to flutter_appauth/example/ios/Flutter/AppFrameworkInfo.plist diff --git a/example/ios/Flutter/Debug.xcconfig b/flutter_appauth/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from example/ios/Flutter/Debug.xcconfig rename to flutter_appauth/example/ios/Flutter/Debug.xcconfig diff --git a/example/ios/Flutter/Release.xcconfig b/flutter_appauth/example/ios/Flutter/Release.xcconfig similarity index 100% rename from example/ios/Flutter/Release.xcconfig rename to flutter_appauth/example/ios/Flutter/Release.xcconfig diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/flutter_appauth/example/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from example/ios/Runner.xcodeproj/project.pbxproj rename to flutter_appauth/example/ios/Runner.xcodeproj/project.pbxproj diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter_appauth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to flutter_appauth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter_appauth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to flutter_appauth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter_appauth/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to flutter_appauth/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_appauth/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to flutter_appauth/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter_appauth/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to flutter_appauth/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner/AppDelegate.h b/flutter_appauth/example/ios/Runner/AppDelegate.h similarity index 100% rename from example/ios/Runner/AppDelegate.h rename to flutter_appauth/example/ios/Runner/AppDelegate.h diff --git a/example/ios/Runner/AppDelegate.m b/flutter_appauth/example/ios/Runner/AppDelegate.m similarity index 100% rename from example/ios/Runner/AppDelegate.m rename to flutter_appauth/example/ios/Runner/AppDelegate.m diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to flutter_appauth/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter_appauth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to flutter_appauth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/flutter_appauth/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/Main.storyboard rename to flutter_appauth/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/example/ios/Runner/Info.plist b/flutter_appauth/example/ios/Runner/Info.plist similarity index 100% rename from example/ios/Runner/Info.plist rename to flutter_appauth/example/ios/Runner/Info.plist diff --git a/example/ios/Runner/main.m b/flutter_appauth/example/ios/Runner/main.m similarity index 100% rename from example/ios/Runner/main.m rename to flutter_appauth/example/ios/Runner/main.m diff --git a/example/lib/main.dart b/flutter_appauth/example/lib/main.dart similarity index 100% rename from example/lib/main.dart rename to flutter_appauth/example/lib/main.dart diff --git a/example/pubspec.yaml b/flutter_appauth/example/pubspec.yaml similarity index 100% rename from example/pubspec.yaml rename to flutter_appauth/example/pubspec.yaml diff --git a/flutter_appauth.iml b/flutter_appauth/flutter_appauth.iml similarity index 100% rename from flutter_appauth.iml rename to flutter_appauth/flutter_appauth.iml diff --git a/ios/.gitignore b/flutter_appauth/ios/.gitignore similarity index 100% rename from ios/.gitignore rename to flutter_appauth/ios/.gitignore diff --git a/ios/Assets/.gitkeep b/flutter_appauth/ios/Assets/.gitkeep similarity index 100% rename from ios/Assets/.gitkeep rename to flutter_appauth/ios/Assets/.gitkeep diff --git a/ios/Classes/FlutterAppauthPlugin.h b/flutter_appauth/ios/Classes/FlutterAppauthPlugin.h similarity index 100% rename from ios/Classes/FlutterAppauthPlugin.h rename to flutter_appauth/ios/Classes/FlutterAppauthPlugin.h diff --git a/ios/Classes/FlutterAppauthPlugin.m b/flutter_appauth/ios/Classes/FlutterAppauthPlugin.m similarity index 100% rename from ios/Classes/FlutterAppauthPlugin.m rename to flutter_appauth/ios/Classes/FlutterAppauthPlugin.m diff --git a/ios/flutter_appauth.podspec b/flutter_appauth/ios/flutter_appauth.podspec similarity index 100% rename from ios/flutter_appauth.podspec rename to flutter_appauth/ios/flutter_appauth.podspec diff --git a/lib/flutter_appauth.dart b/flutter_appauth/lib/flutter_appauth.dart similarity index 100% rename from lib/flutter_appauth.dart rename to flutter_appauth/lib/flutter_appauth.dart diff --git a/lib/src/authorization_parameters.dart b/flutter_appauth/lib/src/authorization_parameters.dart similarity index 100% rename from lib/src/authorization_parameters.dart rename to flutter_appauth/lib/src/authorization_parameters.dart diff --git a/lib/src/authorization_request.dart b/flutter_appauth/lib/src/authorization_request.dart similarity index 100% rename from lib/src/authorization_request.dart rename to flutter_appauth/lib/src/authorization_request.dart diff --git a/lib/src/authorization_response.dart b/flutter_appauth/lib/src/authorization_response.dart similarity index 100% rename from lib/src/authorization_response.dart rename to flutter_appauth/lib/src/authorization_response.dart diff --git a/lib/src/authorization_service_configuration.dart b/flutter_appauth/lib/src/authorization_service_configuration.dart similarity index 100% rename from lib/src/authorization_service_configuration.dart rename to flutter_appauth/lib/src/authorization_service_configuration.dart diff --git a/lib/src/authorization_token_request.dart b/flutter_appauth/lib/src/authorization_token_request.dart similarity index 100% rename from lib/src/authorization_token_request.dart rename to flutter_appauth/lib/src/authorization_token_request.dart diff --git a/lib/src/authorization_token_response.dart b/flutter_appauth/lib/src/authorization_token_response.dart similarity index 100% rename from lib/src/authorization_token_response.dart rename to flutter_appauth/lib/src/authorization_token_response.dart diff --git a/lib/src/common_request_details.dart b/flutter_appauth/lib/src/common_request_details.dart similarity index 100% rename from lib/src/common_request_details.dart rename to flutter_appauth/lib/src/common_request_details.dart diff --git a/lib/src/flutter_appauth.dart b/flutter_appauth/lib/src/flutter_appauth.dart similarity index 100% rename from lib/src/flutter_appauth.dart rename to flutter_appauth/lib/src/flutter_appauth.dart diff --git a/lib/src/grant_types.dart b/flutter_appauth/lib/src/grant_types.dart similarity index 100% rename from lib/src/grant_types.dart rename to flutter_appauth/lib/src/grant_types.dart diff --git a/lib/src/mappable.dart b/flutter_appauth/lib/src/mappable.dart similarity index 100% rename from lib/src/mappable.dart rename to flutter_appauth/lib/src/mappable.dart diff --git a/lib/src/token_request.dart b/flutter_appauth/lib/src/token_request.dart similarity index 100% rename from lib/src/token_request.dart rename to flutter_appauth/lib/src/token_request.dart diff --git a/lib/src/token_response.dart b/flutter_appauth/lib/src/token_response.dart similarity index 100% rename from lib/src/token_response.dart rename to flutter_appauth/lib/src/token_response.dart diff --git a/pubspec.yaml b/flutter_appauth/pubspec.yaml similarity index 100% rename from pubspec.yaml rename to flutter_appauth/pubspec.yaml diff --git a/flutter_appauth_platform_interface/.gitignore b/flutter_appauth_platform_interface/.gitignore new file mode 100644 index 00000000..bb431f0d --- /dev/null +++ b/flutter_appauth_platform_interface/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/.metadata b/flutter_appauth_platform_interface/.metadata similarity index 75% rename from .metadata rename to flutter_appauth_platform_interface/.metadata index 2517d637..34217a6a 100644 --- a/.metadata +++ b/flutter_appauth_platform_interface/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b + revision: 9f5ff2306bb3e30b2b98eee79cd231b1336f41f4 channel: stable -project_type: plugin +project_type: package diff --git a/flutter_appauth_platform_interface/CHANGELOG.md b/flutter_appauth_platform_interface/CHANGELOG.md new file mode 100644 index 00000000..4c45f55e --- /dev/null +++ b/flutter_appauth_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +## [1.0.0] + +* Initial release of platform interface \ No newline at end of file diff --git a/flutter_appauth_platform_interface/LICENSE b/flutter_appauth_platform_interface/LICENSE new file mode 100644 index 00000000..cd01e3fe --- /dev/null +++ b/flutter_appauth_platform_interface/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, Michael Bui +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flutter_appauth_platform_interface/README.md b/flutter_appauth_platform_interface/README.md new file mode 100644 index 00000000..dd0d45d0 --- /dev/null +++ b/flutter_appauth_platform_interface/README.md @@ -0,0 +1,3 @@ +# flutter_appauth_platform_interface + +A common platform interface for the [`flutter_appauth`](https://pub.dev/packages/flutter_appauth) plugin. \ No newline at end of file diff --git a/flutter_appauth_platform_interface/lib/flutter_appauth_platform_interface.dart b/flutter_appauth_platform_interface/lib/flutter_appauth_platform_interface.dart new file mode 100644 index 00000000..3697e181 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/flutter_appauth_platform_interface.dart @@ -0,0 +1,9 @@ +export 'src/authorization_request.dart'; +export 'src/authorization_response.dart'; +export 'src/authorization_service_configuration.dart'; +export 'src/authorization_token_request.dart'; +export 'src/authorization_token_response.dart'; +export 'src/grant_types.dart'; +export 'src/token_request.dart'; +export 'src/token_response.dart'; +export 'src/flutter_appauth_platform.dart'; diff --git a/flutter_appauth_platform_interface/lib/src/authorization_parameters.dart b/flutter_appauth_platform_interface/lib/src/authorization_parameters.dart new file mode 100644 index 00000000..c4cd2f57 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/authorization_parameters.dart @@ -0,0 +1,17 @@ +import 'mappable.dart'; + +mixin AuthorizationParameters on Mappable { + /// Hint to the Authorization Server about the login identifier the End-User might use to log in + String loginHint; + + /// list of ASCII string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent + List promptValues; + + @override + Map toMap() { + final Map map = super.toMap(); + map['loginHint'] = loginHint; + map['promptValues'] = promptValues; + return map; + } +} diff --git a/flutter_appauth_platform_interface/lib/src/authorization_request.dart b/flutter_appauth_platform_interface/lib/src/authorization_request.dart new file mode 100644 index 00000000..835ca0a6 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/authorization_request.dart @@ -0,0 +1,28 @@ +import 'authorization_parameters.dart'; +import 'authorization_service_configuration.dart'; +import 'common_request_details.dart'; + +/// The details of an authorization request to get an authorization code +class AuthorizationRequest extends CommonRequestDetails + with AuthorizationParameters { + AuthorizationRequest(String clientId, String redirectUrl, + {String loginHint, + List scopes, + AuthorizationServiceConfiguration serviceConfiguration, + Map additionalParameters, + String issuer, + String discoveryUrl, + List promptValues, + bool allowInsecureConnections = false}) { + this.clientId = clientId; + this.redirectUrl = redirectUrl; + this.scopes = scopes; + this.serviceConfiguration = serviceConfiguration; + this.additionalParameters = additionalParameters; + this.issuer = issuer; + this.discoveryUrl = discoveryUrl; + this.loginHint = loginHint; + this.promptValues = promptValues; + this.allowInsecureConnections = allowInsecureConnections; + } +} diff --git a/flutter_appauth_platform_interface/lib/src/authorization_response.dart b/flutter_appauth_platform_interface/lib/src/authorization_response.dart new file mode 100644 index 00000000..58dd27b2 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/authorization_response.dart @@ -0,0 +1,13 @@ +class AuthorizationResponse { + AuthorizationResponse(this.authorizationCode, this.codeVerifier, + this.authorizationAdditionalParameters); + + /// The authorization code + final String authorizationCode; + + /// The code verifier generated by AppAuth when issue the authorization request. Use this when exchanging the [authorizationCode] for a token + final String codeVerifier; + + /// Additional parameters included in the response + final Map authorizationAdditionalParameters; +} diff --git a/flutter_appauth_platform_interface/lib/src/authorization_service_configuration.dart b/flutter_appauth_platform_interface/lib/src/authorization_service_configuration.dart new file mode 100644 index 00000000..57e0f394 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/authorization_service_configuration.dart @@ -0,0 +1,17 @@ +class AuthorizationServiceConfiguration { + AuthorizationServiceConfiguration( + this.authorizationEndpoint, this.tokenEndpoint) + : assert(tokenEndpoint != null && authorizationEndpoint != null, + 'Must specify both the authorization and token endpoints'); + + final String authorizationEndpoint; + + final String tokenEndpoint; + + Map toMap() { + return { + 'tokenEndpoint': tokenEndpoint, + 'authorizationEndpoint': authorizationEndpoint + }; + } +} diff --git a/flutter_appauth_platform_interface/lib/src/authorization_token_request.dart b/flutter_appauth_platform_interface/lib/src/authorization_token_request.dart new file mode 100644 index 00000000..44cbec51 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/authorization_token_request.dart @@ -0,0 +1,31 @@ +import 'authorization_parameters.dart'; +import 'authorization_service_configuration.dart'; +import 'grant_types.dart'; +import 'token_request.dart'; + +/// Details required for a combined authorization and code exchange request +class AuthorizationTokenRequest extends TokenRequest + with AuthorizationParameters { + AuthorizationTokenRequest(String clientId, String redirectUrl, + {String loginHint, + String clientSecret, + List scopes, + AuthorizationServiceConfiguration serviceConfiguration, + Map additionalParameters, + String issuer, + String discoveryUrl, + List promptValues, + bool allowInsecureConnections = false}) + : super(clientId, redirectUrl, + clientSecret: clientSecret, + discoveryUrl: discoveryUrl, + issuer: issuer, + scopes: scopes, + grantType: GrantType.authorizationCode, + serviceConfiguration: serviceConfiguration, + additionalParameters: additionalParameters, + allowInsecureConnections: allowInsecureConnections) { + this.loginHint = loginHint; + this.promptValues = promptValues; + } +} diff --git a/flutter_appauth_platform_interface/lib/src/authorization_token_response.dart b/flutter_appauth_platform_interface/lib/src/authorization_token_response.dart new file mode 100644 index 00000000..83aff80d --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/authorization_token_response.dart @@ -0,0 +1,17 @@ +import 'token_response.dart'; + +/// The details from making a successful combined authorization and token exchange request +class AuthorizationTokenResponse extends TokenResponse { + AuthorizationTokenResponse( + String accessToken, + String refreshToken, + DateTime accessTokenExpirationDateTime, + String idToken, + String tokenType, + this.authorizationAdditionalParameters, + Map tokenAdditionalParameters) + : super(accessToken, refreshToken, accessTokenExpirationDateTime, idToken, + tokenType, tokenAdditionalParameters); + + final Map authorizationAdditionalParameters; +} diff --git a/flutter_appauth_platform_interface/lib/src/common_request_details.dart b/flutter_appauth_platform_interface/lib/src/common_request_details.dart new file mode 100644 index 00000000..da259122 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/common_request_details.dart @@ -0,0 +1,42 @@ +import 'authorization_service_configuration.dart'; +import 'mappable.dart'; + +class CommonRequestDetails implements Mappable { + /// The client id + String clientId; + + /// The issuer + String issuer; + + /// The URL of where the discovery document can be found + String discoveryUrl; + + /// The redirect URL + String redirectUrl; + + /// The request scopes + List scopes; + + /// The details of the OAuth 2.0 endpoints that can be explicitly when discovery isn't used or not possible + AuthorizationServiceConfiguration serviceConfiguration; + + /// Additional parameters to include in the request + Map additionalParameters; + + /// Whether to allow non-HTTPS endpoints (only applicable on Android) + bool allowInsecureConnections; + + @override + Map toMap() { + return { + 'clientId': clientId, + 'issuer': issuer, + 'discoveryUrl': discoveryUrl, + 'redirectUrl': redirectUrl, + 'scopes': scopes, + 'serviceConfiguration': serviceConfiguration?.toMap(), + 'additionalParameters': additionalParameters, + 'allowInsecureConnections': allowInsecureConnections + }; + } +} diff --git a/flutter_appauth_platform_interface/lib/src/flutter_appauth_platform.dart b/flutter_appauth_platform_interface/lib/src/flutter_appauth_platform.dart new file mode 100644 index 00000000..38c0d10b --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/flutter_appauth_platform.dart @@ -0,0 +1,47 @@ +import 'package:flutter_appauth_platform_interface/src/method_channel_flutter_appauth.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'authorization_request.dart'; +import 'authorization_response.dart'; +import 'authorization_token_request.dart'; +import 'authorization_token_response.dart'; +import 'token_request.dart'; +import 'token_response.dart'; + +/// The platform interface that all implementations of flutter_appauth must implement. +abstract class FlutterAppAuthPlatform extends PlatformInterface { + FlutterAppAuthPlatform() : super(token: _token); + + /// The default instance of [FlutterAppAuthPlatform] to use. + /// + /// Defaults to [MethodChannelFlutterAppAuth]. + static get instance => _instance; + + static FlutterAppAuthPlatform _instance = MethodChannelFlutterAppAuth(); + + static final Object _token = Object(); + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [FlutterAppAuthPlatform] when they register themselves. + static set instance(FlutterAppAuthPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Convenience method for authorizing and then exchanges the authorization grant code + Future authorizeAndExchangeCode( + AuthorizationTokenRequest request) { + throw UnimplementedError( + 'authorizeAndExchangeCode() has not been implemented'); + } + + /// Sends an authorization request + Future authorize(AuthorizationRequest request) { + throw UnimplementedError('authorize() has not been implemented'); + } + + /// For exchanging tokens + Future token(TokenRequest request) { + throw UnimplementedError('token() has not been implemented'); + } +} diff --git a/flutter_appauth_platform_interface/lib/src/grant_types.dart b/flutter_appauth_platform_interface/lib/src/grant_types.dart new file mode 100644 index 00000000..e32217a2 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/grant_types.dart @@ -0,0 +1,5 @@ +class GrantType { + static const String authorizationCode = 'authorization_code'; + static const String refreshToken = 'refresh_token'; + static const String implicit = 'implicit'; +} diff --git a/flutter_appauth_platform_interface/lib/src/mappable.dart b/flutter_appauth_platform_interface/lib/src/mappable.dart new file mode 100644 index 00000000..67a193fb --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/mappable.dart @@ -0,0 +1,3 @@ +abstract class Mappable { + Map toMap(); +} diff --git a/flutter_appauth_platform_interface/lib/src/method_channel_flutter_appauth.dart b/flutter_appauth_platform_interface/lib/src/method_channel_flutter_appauth.dart new file mode 100644 index 00000000..8280398f --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/method_channel_flutter_appauth.dart @@ -0,0 +1,67 @@ +import 'package:flutter/services.dart'; + +import 'authorization_request.dart'; +import 'authorization_response.dart'; +import 'authorization_token_request.dart'; +import 'authorization_token_response.dart'; +import 'flutter_appauth_platform.dart'; +import 'token_request.dart'; +import 'token_response.dart'; + +const MethodChannel _channel = + MethodChannel('crossingthestreams.io/flutter_appauth'); + +class MethodChannelFlutterAppAuth extends FlutterAppAuthPlatform { + @override + Future authorize(AuthorizationRequest request) async { + final Map result = + await _channel.invokeMethod('authorize', request.toMap()); + if (result == null) { + return null; + } + return AuthorizationResponse( + result['authorizationCode'], + result['codeVerifier'], + result['authorizationAdditionalParameters']?.cast()); + } + + @override + Future authorizeAndExchangeCode( + AuthorizationTokenRequest request) async { + final Map result = await _channel.invokeMethod( + 'authorizeAndExchangeCode', request.toMap()); + if (result == null) { + return null; + } + return AuthorizationTokenResponse( + result['accessToken'], + result['refreshToken'], + result['accessTokenExpirationTime'] == null + ? null + : DateTime.fromMillisecondsSinceEpoch( + result['accessTokenExpirationTime'].toInt()), + result['idToken'], + result['tokenType'], + result['authorizationAdditionalParameters']?.cast(), + result['tokenAdditionalParameters']?.cast()); + } + + @override + Future token(TokenRequest request) async { + final Map result = + await _channel.invokeMethod('token', request.toMap()); + if (result == null) { + return null; + } + return TokenResponse( + result['accessToken'], + result['refreshToken'], + result['accessTokenExpirationTime'] == null + ? null + : DateTime.fromMillisecondsSinceEpoch( + result['accessTokenExpirationTime'].toInt()), + result['idToken'], + result['tokenType'], + result['tokenAdditionalParameters']?.cast()); + } +} diff --git a/flutter_appauth_platform_interface/lib/src/token_request.dart b/flutter_appauth_platform_interface/lib/src/token_request.dart new file mode 100644 index 00000000..9bf723a7 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/token_request.dart @@ -0,0 +1,76 @@ +import 'authorization_service_configuration.dart'; +import 'common_request_details.dart'; +import 'grant_types.dart'; + +/// Details for a token exchange request +class TokenRequest with CommonRequestDetails { + TokenRequest(String clientId, String redirectUrl, + {this.clientSecret, + List scopes, + AuthorizationServiceConfiguration serviceConfiguration, + Map additionalParameters, + this.refreshToken, + this.grantType, + String issuer, + String discoveryUrl, + this.authorizationCode, + this.codeVerifier, + bool allowInsecureConnections = false}) + : assert( + issuer != null || + discoveryUrl != null || + (serviceConfiguration?.authorizationEndpoint != null && + serviceConfiguration?.tokenEndpoint != null), + 'Either the issuer, discovery URL or service configuration must be provided') { + this.clientId = clientId; + this.redirectUrl = redirectUrl; + this.scopes = scopes; + this.serviceConfiguration = serviceConfiguration; + this.additionalParameters = additionalParameters; + this.issuer = issuer; + this.discoveryUrl = discoveryUrl; + this.allowInsecureConnections = allowInsecureConnections; + } + + /// The client secret + final String clientSecret; + + /// The refresh token + final String refreshToken; + + /// The grant type. This would be inferred if it hasn't been set based on if a refresh token or authorization code has been specified + final String grantType; + + /// The authorization code + final String authorizationCode; + + /// The code verifier to be sent with the authorization code. This should match the code verifier used when performing the authorization request + final String codeVerifier; + + @override + Map toMap() { + final Map map = super.toMap(); + final String inferredGrantType = _inferGrantType(); + map['clientSecret'] = clientSecret; + map['refreshToken'] = refreshToken; + map['authorizationCode'] = authorizationCode; + map['grantType'] = inferredGrantType; + map['codeVerifier'] = codeVerifier; + return map; + } + + String _inferGrantType() { + if (grantType != null) { + return grantType; + } + if (refreshToken != null) { + return GrantType.refreshToken; + } + if (authorizationCode != null) { + return GrantType.authorizationCode; + } + + throw ArgumentError.value( + null, 'grantType', 'Grant type not specified and cannot be inferred'); + } +} diff --git a/flutter_appauth_platform_interface/lib/src/token_response.dart b/flutter_appauth_platform_interface/lib/src/token_response.dart new file mode 100644 index 00000000..13e8b5f0 --- /dev/null +++ b/flutter_appauth_platform_interface/lib/src/token_response.dart @@ -0,0 +1,22 @@ +/// Details from making a successful token exchange +class TokenResponse { + TokenResponse( + this.accessToken, + this.refreshToken, + this.accessTokenExpirationDateTime, + this.idToken, + this.tokenType, + this.tokenAdditionalParameters); + + final String accessToken; + + final String refreshToken; + + final DateTime accessTokenExpirationDateTime; + + final String idToken; + + final String tokenType; + + final Map tokenAdditionalParameters; +} diff --git a/flutter_appauth_platform_interface/pubspec.yaml b/flutter_appauth_platform_interface/pubspec.yaml new file mode 100644 index 00000000..6aff27cf --- /dev/null +++ b/flutter_appauth_platform_interface/pubspec.yaml @@ -0,0 +1,18 @@ +name: flutter_appauth_platform_interface +description: A common platform interface for the flutter_appauth plugin. +version: 1.0.0 +homepage: https://github.com/MaikuB/flutter_appauth/tree/master/flutter_appauth_platform_interface + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.10.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^1.0.1 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^4.1.1 \ No newline at end of file diff --git a/flutter_appauth_platform_interface/test/method_channel_flutter_appauth_test.dart b/flutter_appauth_platform_interface/test/method_channel_flutter_appauth_test.dart new file mode 100644 index 00000000..edeeb81e --- /dev/null +++ b/flutter_appauth_platform_interface/test/method_channel_flutter_appauth_test.dart @@ -0,0 +1,167 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_appauth_platform_interface/src/method_channel_flutter_appauth.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flutter_appauth_platform_interface/flutter_appauth_platform_interface.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + const MethodChannel channel = + MethodChannel('crossingthestreams.io/flutter_appauth'); + final List log = []; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + }); + + tearDown(() { + log.clear(); + }); + + MethodChannelFlutterAppAuth flutterAppAuth = MethodChannelFlutterAppAuth(); + test('authorize', () async { + await flutterAppAuth.authorize(AuthorizationRequest( + 'someClientId', 'someRedirectUrl', + discoveryUrl: 'someDiscoveryUrl', loginHint: 'someLoginHint')); + expect( + log, + [ + isMethodCall('authorize', arguments: { + 'clientId': 'someClientId', + 'issuer': null, + 'redirectUrl': 'someRedirectUrl', + 'discoveryUrl': 'someDiscoveryUrl', + 'loginHint': 'someLoginHint', + 'scopes': null, + 'serviceConfiguration': null, + 'additionalParameters': null, + 'allowInsecureConnections': false, + 'promptValues': null, + }) + ], + ); + }); + + test('authorizeAndExchangeCode', () async { + await flutterAppAuth.authorizeAndExchangeCode(AuthorizationTokenRequest( + 'someClientId', 'someRedirectUrl', + discoveryUrl: 'someDiscoveryUrl', loginHint: 'someLoginHint')); + expect( + log, + [ + isMethodCall('authorizeAndExchangeCode', arguments: { + 'clientId': 'someClientId', + 'issuer': null, + 'redirectUrl': 'someRedirectUrl', + 'discoveryUrl': 'someDiscoveryUrl', + 'loginHint': 'someLoginHint', + 'scopes': null, + 'serviceConfiguration': null, + 'additionalParameters': null, + 'allowInsecureConnections': false, + 'promptValues': null, + 'clientSecret': null, + 'refreshToken': null, + 'authorizationCode': null, + 'grantType': 'authorization_code', + 'codeVerifier': null + }) + ], + ); + }); + + group('token', () { + test('cannot infer grant type', () async { + expect( + () async => await flutterAppAuth.token(TokenRequest( + 'someClientId', 'someRedirectUrl', + discoveryUrl: 'someDiscoveryUrl')), + throwsArgumentError); + }); + test('infers refresh token grant type', () async { + await flutterAppAuth.token(TokenRequest('someClientId', 'someRedirectUrl', + discoveryUrl: 'someDiscoveryUrl', refreshToken: 'someRefreshToken')); + expect( + log, + [ + isMethodCall('token', arguments: { + 'clientId': 'someClientId', + 'issuer': null, + 'redirectUrl': 'someRedirectUrl', + 'discoveryUrl': 'someDiscoveryUrl', + 'scopes': null, + 'serviceConfiguration': null, + 'additionalParameters': null, + 'allowInsecureConnections': false, + 'clientSecret': null, + 'refreshToken': 'someRefreshToken', + 'authorizationCode': null, + 'grantType': 'refresh_token', + 'codeVerifier': null + }) + ], + ); + }); + + test('infers authorization code grant type', () async { + await flutterAppAuth.token(TokenRequest('someClientId', 'someRedirectUrl', + discoveryUrl: 'someDiscoveryUrl', + authorizationCode: 'someAuthorizationCode')); + expect( + log, + [ + isMethodCall('token', arguments: { + 'clientId': 'someClientId', + 'issuer': null, + 'redirectUrl': 'someRedirectUrl', + 'discoveryUrl': 'someDiscoveryUrl', + 'scopes': null, + 'serviceConfiguration': null, + 'additionalParameters': null, + 'allowInsecureConnections': false, + 'clientSecret': null, + 'refreshToken': null, + 'authorizationCode': 'someAuthorizationCode', + 'grantType': 'authorization_code', + 'codeVerifier': null + }) + ], + ); + }); + + test('sends specified grant type', () async { + await flutterAppAuth.token(TokenRequest('someClientId', 'someRedirectUrl', + discoveryUrl: 'someDiscoveryUrl', grantType: 'someGrantType')); + expect( + log, + [ + isMethodCall('token', arguments: { + 'clientId': 'someClientId', + 'issuer': null, + 'redirectUrl': 'someRedirectUrl', + 'discoveryUrl': 'someDiscoveryUrl', + 'scopes': null, + 'serviceConfiguration': null, + 'additionalParameters': null, + 'allowInsecureConnections': false, + 'clientSecret': null, + 'refreshToken': null, + 'authorizationCode': null, + 'grantType': 'someGrantType', + 'codeVerifier': null + }) + ], + ); + }); + }); +} + +class FlutterAppAuthPlatformMock extends Mock + with MockPlatformInterfaceMixin + implements FlutterAppAuthPlatform {} + +class ImplementsFlutterAppAuthPlatform extends Mock + implements FlutterAppAuthPlatform {} + +class ExtendsFlutterAppAuthPlatform extends FlutterAppAuthPlatform {}