diff --git a/example/lib/main.dart b/example/lib/main.dart index db92756..ae596b6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,7 +3,6 @@ import 'package:flutter/services.dart'; import 'dart:async'; import 'package:msal_flutter/msal_flutter.dart'; - void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @@ -12,22 +11,26 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - - static const String _authority = "https://msalfluttertest.b2clogin.com/tfp/msalfluttertest.onmicrosoft.com/B2C_1_sisu"; + static const String _authority = + "https://msalfluttertest.b2clogin.com/tfp/msalfluttertest.onmicrosoft.com/B2C_1_sisu"; static const String _clientId = "5913dfb1-7576-451c-a7ea-a7c5a3f8682a"; - + String _output = 'NONE'; PublicClientApplication pca; - Future _acquireToken() async{ - if(pca == null){ - pca = await PublicClientApplication.createPublicClientApplication(_clientId, authority: _authority); + Future _acquireToken() async { + if (pca == null) { + pca = await PublicClientApplication.createPublicClientApplication( + _clientId, + authority: _authority); } String res; - try{ - res = await pca.acquireToken(["https://msalfluttertest.onmicrosoft.com/msalbackend/user_impersonation"]); + try { + res = await pca.acquireToken([ + "https://msalfluttertest.onmicrosoft.com/msalbackend/user_impersonation" + ]); } on MsalUserCancelledException { res = "User cancelled"; } on MsalNoAccountException { @@ -36,7 +39,7 @@ class _MyAppState extends State { res = "invalid config"; } on MsalInvalidScopeException { res = "Invalid scope"; - }on MsalException { + } on MsalException { res = "Error getting token. Unspecified reason"; } @@ -46,14 +49,17 @@ class _MyAppState extends State { } Future _acquireTokenSilently() async { - if(pca == null){ - pca = await PublicClientApplication.createPublicClientApplication(_clientId, authority: _authority); + if (pca == null) { + pca = await PublicClientApplication.createPublicClientApplication( + _clientId, + authority: _authority); } - + String res; - try - { - res = await pca.acquireTokenSilent(["https://msalfluttertest.onmicrosoft.com/msalbackend/user_impersonation"]); + try { + res = await pca.acquireTokenSilent([ + "https://msalfluttertest.onmicrosoft.com/msalbackend/user_impersonation" + ]); } on MsalUserCancelledException { res = "User cancelled"; } on MsalNoAccountException { @@ -62,7 +68,7 @@ class _MyAppState extends State { res = "invalid config"; } on MsalInvalidScopeException { res = "Invalid scope"; - }on MsalException { + } on MsalException { res = "Error getting token silently!"; } @@ -73,23 +79,25 @@ class _MyAppState extends State { Future _logout() async { print("called logout"); - if(pca == null){ - pca = await PublicClientApplication.createPublicClientApplication(_clientId, authority: _authority); + if (pca == null) { + pca = await PublicClientApplication.createPublicClientApplication( + _clientId, + authority: _authority); } print("pca is not null"); String res; - try{ + try { await pca.logout(); res = "Account removed"; } on MsalException { res = "Error signing out"; - } on PlatformException catch (e){ + } on PlatformException catch (e) { res = "some other exception ${e.toString()}"; } print("setting state"); - setState((){ + setState(() { _output = res; }); } @@ -104,13 +112,15 @@ class _MyAppState extends State { body: Center( child: Column( children: [ - RaisedButton( onPressed: _acquireToken, - child: Text('AcquireToken()'),), - RaisedButton( onPressed: _acquireTokenSilently, - child: Text('AcquireTokenSilently()')), - RaisedButton( onPressed: _logout, - child: Text('Logout')), - Text( _output), + RaisedButton( + onPressed: _acquireToken, + child: Text('AcquireToken()'), + ), + RaisedButton( + onPressed: _acquireTokenSilently, + child: Text('AcquireTokenSilently()')), + RaisedButton(onPressed: _logout, child: Text('Logout')), + Text(_output), ], ), ), diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index 116ec73..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// // This is a basic Flutter widget test. -// // -// // To perform an interaction with a widget in your test, use the WidgetTester -// // utility that Flutter provides. For example, you can send tap and scroll -// // gestures. You can also use WidgetTester to find child widgets in the widget -// // tree, read text, and verify that the values of widget properties are correct. - -// import 'package:flutter/material.dart'; -// import 'package:flutter_test/flutter_test.dart'; - -// import 'package:msal_flutter_example/main.dart'; - -// void main() { -// testWidgets('Verify Platform version', (WidgetTester tester) async { -// // Build our app and trigger a frame. -// await tester.pumpWidget(MyApp()); - -// // Verify that platform version is retrieved. -// expect( -// find.byWidgetPredicate( -// (Widget widget) => widget is Text && -// widget.data.startsWith('Running on:'), -// ), -// findsOneWidget, -// ); -// }); -// } diff --git a/lib/src/msal_exception.dart b/lib/src/msal_exception.dart index e7d790c..1f06217 100644 --- a/lib/src/msal_exception.dart +++ b/lib/src/msal_exception.dart @@ -1,39 +1,61 @@ class MsalException implements Exception { - String errorMessage; - MsalException(this.errorMessage); + final String errorMessage; + + const MsalException(this.errorMessage); } class MsalChangedClientIdException extends MsalException { - MsalChangedClientIdException() - : super( - "Cannot create a client with a new client ID. Only 1 client id supported"); + const MsalChangedClientIdException() + : super("Cannot create a client with a new client ID. " + "Only 1 client id supported"); + + static MsalChangedClientIdException create() => + const MsalChangedClientIdException(); } class MsalUserCancelledException extends MsalException { - MsalUserCancelledException() : super("User cancelled login request"); + const MsalUserCancelledException() : super("User cancelled login request"); + + static MsalUserCancelledException create() => + const MsalUserCancelledException(); } class MsalNoAccountException extends MsalException { - MsalNoAccountException() + const MsalNoAccountException() : super("Cannot login silently. No account available"); + + static MsalNoAccountException create() => + const MsalNoAccountException(); } class MsalInvalidConfigurationException extends MsalException { - MsalInvalidConfigurationException(errorMessage) : super(errorMessage); + const MsalInvalidConfigurationException(String errorMessage) : super(errorMessage); + + static MsalInvalidConfigurationException create(String errorMessage) => + MsalInvalidConfigurationException(errorMessage); } class MsalInvalidScopeException extends MsalException { - MsalInvalidScopeException() : super("Invalid or no scope"); + const MsalInvalidScopeException() : super("Invalid or no scope"); + + static MsalInvalidScopeException create() => + const MsalInvalidScopeException(); } class MsalInitializationException extends MsalException { - MsalInitializationException() - : super( - "Error initializing client. Please ensure correctly configuration supplied"); + const MsalInitializationException() + : super("Error initializing client. Please ensure " + "correctly configuration supplied"); + + static MsalInitializationException create() => + const MsalInitializationException(); } class MsalUninitializedException extends MsalException { - MsalUninitializedException() - : super( - "Client not initialized. Client must be initialized before attempting to use"); + const MsalUninitializedException() + : super("Client not initialized. Client " + "must be initialized before attempting to use"); + + static MsalUninitializedException create() => + const MsalUninitializedException(); } diff --git a/lib/src/public_client_application.dart b/lib/src/public_client_application.dart index 6e235af..b0b748d 100644 --- a/lib/src/public_client_application.dart +++ b/lib/src/public_client_application.dart @@ -1,109 +1,72 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'msal_exception.dart'; /// Represents a PublicClientApplication used to authenticate using the implicit flow +@immutable class PublicClientApplication { static const MethodChannel _channel = const MethodChannel('msal_flutter'); - String _clientId, _authority; + final String _clientId; + final String? _authority; - /// Create a new PublicClientApplication authenticating as the given [clientId], - /// optionally against the selected [authority], defaulting to the common - PublicClientApplication(String clientId, {String authority}) { - throw Exception( - "Direct call is no longer supported in v1.0, please use static method createPublicClientApplication"); - } - - PublicClientApplication._create(String clientId, {String authority}) { - _clientId = clientId; - _authority = authority; - } + const PublicClientApplication._create(this._clientId, {String? authority}) + : _authority = authority; static Future createPublicClientApplication( - String clientId, - {String authority}) async { - var res = PublicClientApplication._create(clientId, authority: authority); + String clientId, { + String? authority, + }) async { + final res = PublicClientApplication._create(clientId, authority: authority); await res._initialize(); return res; } /// Acquire a token interactively for the given [scopes] - Future acquireToken(List scopes) async { - //create the arguments - var res = {'scopes': scopes}; - - //call platform - try { - final String token = await _channel.invokeMethod('acquireToken', res); - return token; - } on PlatformException catch (e) { - throw _convertException(e); - } - } + Future acquireToken(List scopes) => + _invokePlatformMethod('acquireToken', {'scopes': scopes}); /// Acquire a token silently, with no user interaction, for the given [scopes] - Future acquireTokenSilent(List scopes) async { - //create the arguments - var res = {'scopes': scopes}; + Future acquireTokenSilent(List scopes) => + _invokePlatformMethod('acquireTokenSilent', {'scopes': scopes}); - //call platform - try { - final String token = - await _channel.invokeMethod('acquireTokenSilent', res); - return token; - } on PlatformException catch (e) { - throw _convertException(e); - } - } + Future logout() => _invokePlatformMethod('logout'); + + // initialize the main client platform side + Future _initialize() async => _invokePlatformMethod( + 'initialize', + { + 'clientId': this._clientId, + //if authority has been set, add it as well + if (this._authority != null) 'authority': this._authority + }, + ); - Future logout() async { + Future _invokePlatformMethod(String method, + [Map arguments = const {}]) async { try { - await _channel.invokeMethod('logout', {}); + return await _channel.invokeMethod(method, arguments); } on PlatformException catch (e) { throw _convertException(e); } } - MsalException _convertException(PlatformException e) { - switch (e.code) { - case "CANCELLED": - return MsalUserCancelledException(); - case "NO_SCOPE": - return MsalInvalidScopeException(); - case "NO_ACCOUNT": - return MsalNoAccountException(); - case "NO_CLIENTID": - return MsalInvalidConfigurationException("Client Id not set"); - case "INVALID_AUTHORITY": - return MsalInvalidConfigurationException("Invalid authroity set."); - case "CONFIG_ERROR": - return MsalInvalidConfigurationException( - "Invalid configuration, please correct your settings and try again"); - case "NO_CLIENT": - return MsalUninitializedException(); - case "CHANGED_CLIENTID": - return MsalChangedClientIdException(); - case "INIT_ERROR": - return MsalInitializationException(); - case "AUTH_ERROR": - default: - return MsalException("Authentication error"); - } - } - - //initialize the main client platform side - Future _initialize() async { - var res = {'clientId': this._clientId}; - //if authority has been set, add it aswell - if (this._authority != null) { - res["authority"] = this._authority; - } + static final _exceptionMapping = { + "CANCELLED": MsalUninitializedException.create, + "NO_SCOPE": MsalInvalidScopeException.create, + "NO_ACCOUNT": MsalNoAccountException.create, + "NO_CLIENT": MsalUninitializedException.create, + "CHANGED_CLIENTID": MsalChangedClientIdException.create, + "INIT_ERROR": MsalInitializationException.create, + "NO_CLIENTID": () => MsalInvalidConfigurationException("Client Id not set"), + "INVALID_AUTHORITY": () => + MsalInvalidConfigurationException("Invalid authroity set."), + "CONFIG_ERROR": () => MsalInvalidConfigurationException( + "Invalid configuration, please correct your settings and try again"), + }; - try { - await _channel.invokeMethod('initialize', res); - } on PlatformException catch (e) { - throw _convertException(e); - } - } + MsalException _convertException(PlatformException e) => + _exceptionMapping[e.code]?.call() ?? + MsalException("Authentication error"); } diff --git a/pubspec.yaml b/pubspec.yaml index b0cb13d..d5aff52 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,10 @@ name: msal_flutter description: A Microsoft Authentication Library for Android and iOS -version: 2.0.0 +version: 3.0.0-nullsafety.0 homepage: https://github.com/moodio/msal-flutter repository: https://github.com/moodio/msal-flutter environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.12.0-0 <3.0.0" dependencies: flutter: diff --git a/test/msal_flutter_test.dart b/test/msal_flutter_test.dart deleted file mode 100644 index e8dcfd9..0000000 --- a/test/msal_flutter_test.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:msal_flutter/msal_flutter.dart'; - -void main() { - const MethodChannel channel = MethodChannel('msal_flutter'); - - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return '42'; - }); - }); - - tearDown(() { - channel.setMockMethodCallHandler(null); - }); - - test('getPlatformVersion', () async { - //expect(await MsalFlutter.acquireToken(scopes), '42'); - }); -}