From 9c5b5d103a428b6462d53b5fec64273b46923e5c Mon Sep 17 00:00:00 2001 From: YannMarti <100758384+YannMarti@users.noreply.github.com> Date: Thu, 12 May 2022 16:11:44 +0200 Subject: [PATCH] basic auth0 integration --- vidaia/android/app/build.gradle | 3 + vidaia/lib/main.dart | 13 ++- vidaia/lib/pages/auth0_testing_page.dart | 131 +++++++++++++++++++++++ vidaia/lib/pages/login_page.dart | 44 ++++---- vidaia/lib/pages/login_testing.dart | 26 +++++ vidaia/lib/pages/profile_testing.dart | 39 +++++++ vidaia/lib/utils/auth0.dart | 25 +++++ vidaia/lib/utils/globals.dart | 1 + vidaia/pubspec.lock | 16 ++- vidaia/pubspec.yaml | 2 + 10 files changed, 274 insertions(+), 26 deletions(-) create mode 100644 vidaia/lib/pages/auth0_testing_page.dart create mode 100644 vidaia/lib/pages/login_testing.dart create mode 100644 vidaia/lib/pages/profile_testing.dart create mode 100644 vidaia/lib/utils/auth0.dart diff --git a/vidaia/android/app/build.gradle b/vidaia/android/app/build.gradle index e5201aa..9ff1517 100644 --- a/vidaia/android/app/build.gradle +++ b/vidaia/android/app/build.gradle @@ -48,6 +48,9 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + manifestPlaceholders += [ + 'appAuthRedirectScheme': 'com.auth0.vidaia' + ] } buildTypes { diff --git a/vidaia/lib/main.dart b/vidaia/lib/main.dart index 0c31015..83c12d5 100644 --- a/vidaia/lib/main.dart +++ b/vidaia/lib/main.dart @@ -2,9 +2,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:vidaia/pages/home/home_page_loader.dart'; +import 'package:vidaia/pages/auth0_testing_page.dart'; import 'package:vidaia/repositories/dataRepository.dart'; -import 'package:vidaia/pages/login_page.dart'; GetIt getIt = GetIt.instance; @@ -35,10 +34,14 @@ const primaryColorDark = Color(0xFF112823); const backgroundColor = Color(0xFFf8faf7); const backgroundColorDark = Color(0xFFebeeea); -class MyApp extends StatelessWidget { +class MyApp extends StatefulWidget { MyApp({Key? key}) : super(key: key); - // This widget is the root of your application. + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { @override Widget build(BuildContext context) { return MaterialApp( @@ -91,7 +94,7 @@ class MyApp extends StatelessWidget { ), ), darkTheme: ThemeData.dark(), - home: HomePage2(), + home: Auth0TestPage(), ); } } diff --git a/vidaia/lib/pages/auth0_testing_page.dart b/vidaia/lib/pages/auth0_testing_page.dart new file mode 100644 index 0000000..7f5c845 --- /dev/null +++ b/vidaia/lib/pages/auth0_testing_page.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_appauth/flutter_appauth.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:vidaia/pages/profile_testing.dart'; +import 'package:vidaia/utils/auth0.dart'; + +import 'login_testing.dart'; +final FlutterAppAuth appAuth = FlutterAppAuth(); +final FlutterSecureStorage secureStorage = const FlutterSecureStorage(); +const AUTH0_DOMAIN = 'saynode.eu.auth0.com'; +const AUTH0_CLIENT_ID = 'NtesFp8kbFYekf05rleULMSdBS5hEWRN'; + +const AUTH0_REDIRECT_URI = 'com.auth0.vidaia://login-callback'; +const AUTH0_ISSUER = 'https://$AUTH0_DOMAIN'; + +class Auth0TestPage extends StatefulWidget { + + Auth0TestPage(); + + @override + State createState() => _Auth0TestPageState(); +} + +class _Auth0TestPageState extends State { + bool isBusy = false; + bool isLoggedIn = false; + String errorMessage = ''; + String? name; + String? picture; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Auth0 Test'), + ), + body: Center( + child: isBusy + ? CircularProgressIndicator() + : isLoggedIn + ? Profile(logoutAction, name!, picture!) + : Login(loginAction, errorMessage), + ), + ); + } + Future loginAction() async { + setState(() { + isBusy = true; + errorMessage = ''; + }); + + try { + final AuthorizationTokenResponse? result = + await appAuth.authorizeAndExchangeCode( + AuthorizationTokenRequest( + AUTH0_CLIENT_ID, + AUTH0_REDIRECT_URI, + issuer: 'https://$AUTH0_DOMAIN', + scopes: ['openid', 'profile', 'offline_access'], + // promptValues: ['login'] + ), + ); + + final idToken = parseIdToken(result!.idToken!); + final profile = await getUserDetails(result.accessToken!); + + await secureStorage.write( + key: 'refresh_token', value: result.refreshToken); + + setState(() { + isBusy = false; + isLoggedIn = true; + name = idToken['name']; + picture = profile['picture']; + }); + } catch (e, s) { + print('login error: $e - stack: $s'); + + setState(() { + isBusy = false; + isLoggedIn = false; + errorMessage = e.toString(); + }); + } + } + + void logoutAction() async { + await secureStorage.delete(key: 'refresh_token'); + setState(() { + isLoggedIn = false; + isBusy = false; + }); + } + @override + void initState() { + initAction(); + super.initState(); + } + + void initAction() async { + final storedRefreshToken = await secureStorage.read(key: 'refresh_token'); + if (storedRefreshToken == null) return; + + setState(() { + isBusy = true; + }); + + try { + final response = await appAuth.token(TokenRequest( + AUTH0_CLIENT_ID, + AUTH0_REDIRECT_URI, + issuer: AUTH0_ISSUER, + refreshToken: storedRefreshToken, + )); + + final idToken = parseIdToken(response!.idToken!); + final profile = await getUserDetails(response.accessToken!); + + secureStorage.write(key: 'refresh_token', value: response.refreshToken); + + setState(() { + isBusy = false; + isLoggedIn = true; + name = idToken['name']; + picture = profile['picture']; + }); + } catch (e, s) { + print('error on refresh token: $e - stack: $s'); + logoutAction(); + } + } +} \ No newline at end of file diff --git a/vidaia/lib/pages/login_page.dart b/vidaia/lib/pages/login_page.dart index 691e5ea..4f93f6a 100644 --- a/vidaia/lib/pages/login_page.dart +++ b/vidaia/lib/pages/login_page.dart @@ -52,26 +52,30 @@ class LoginPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - color: primaryColor, - child: FlutterLogin( - userType: LoginUserType.name, - logo: const AssetImage('assets/images/vidaia-live-sustainably.png'), - messages: - LoginMessages(userHint: 'Email', passwordHint: 'password'.tr()), - onLogin: _authUser, - onSignup: _signupUser, - onSubmitAnimationCompleted: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => HomePage2()), - ); - if (!mnemonicNoted) { - showMnemonicAlert(context); - } - }, - onRecoverPassword: _recoverPassword, - ), + return Column( + children: [ + Container( + color: primaryColor, + child: FlutterLogin( + userType: LoginUserType.name, + logo: const AssetImage('assets/images/vidaia-live-sustainably.png'), + messages: + LoginMessages(userHint: 'Email', passwordHint: 'password'.tr()), + onLogin: _authUser, + onSignup: _signupUser, + onSubmitAnimationCompleted: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => HomePage2()), + ); + if (!mnemonicNoted) { + showMnemonicAlert(context); + } + }, + onRecoverPassword: _recoverPassword, + ), + ), + ], ); } } diff --git a/vidaia/lib/pages/login_testing.dart b/vidaia/lib/pages/login_testing.dart new file mode 100644 index 0000000..ad84ea3 --- /dev/null +++ b/vidaia/lib/pages/login_testing.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class Login extends StatelessWidget { + final loginAction; + final String loginError; + + const Login(this.loginAction, this.loginError); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + loginAction(); + }, + child: Text('Login'), + ), + Text(loginError), + + + ], + ); + } +} \ No newline at end of file diff --git a/vidaia/lib/pages/profile_testing.dart b/vidaia/lib/pages/profile_testing.dart new file mode 100644 index 0000000..ff9300c --- /dev/null +++ b/vidaia/lib/pages/profile_testing.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +class Profile extends StatelessWidget { + final logoutAction; + final String name; + final String picture; + + Profile(this.logoutAction, this.name, this.picture); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 150, + height: 150, + decoration: BoxDecoration( + border: Border.all(color: Colors.blue, width: 4.0), + shape: BoxShape.circle, + image: DecorationImage( + fit: BoxFit.fill, + image: NetworkImage(picture), + ), + ), + ), + SizedBox(height: 24.0), + Text('Name: $name'), + SizedBox(height: 48.0), + ElevatedButton( + onPressed: () { + logoutAction(); + }, + child: Text('Logout'), + ), + ], + ); + } +} \ No newline at end of file diff --git a/vidaia/lib/utils/auth0.dart b/vidaia/lib/utils/auth0.dart new file mode 100644 index 0000000..7700a03 --- /dev/null +++ b/vidaia/lib/utils/auth0.dart @@ -0,0 +1,25 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:vidaia/pages/auth0_testing_page.dart'; + +Map parseIdToken(String idToken) { + final parts = idToken.split(r'.'); + assert(parts.length == 3); + + return jsonDecode( + utf8.decode(base64Url.decode(base64Url.normalize(parts[1])))); +} + +Future> getUserDetails(String accessToken) async { + final url = Uri.parse('https://$AUTH0_DOMAIN/userinfo'); + final response = await http.get( + url, + headers: {'Authorization': 'Bearer $accessToken'}, + ); + + if (response.statusCode == 200) { + return jsonDecode(response.body); + } else { + throw Exception('Failed to get user details'); + } + } diff --git a/vidaia/lib/utils/globals.dart b/vidaia/lib/utils/globals.dart index cc812ca..a91b5f7 100644 --- a/vidaia/lib/utils/globals.dart +++ b/vidaia/lib/utils/globals.dart @@ -15,6 +15,7 @@ Wallet? wallet; String? qrCode; bool mnemonicNoted = false; +String token = ''; //priv c337cf0b3c7c3e4b7f5480b985724e0f221120554459b5c247870d2789726089 diff --git a/vidaia/pubspec.lock b/vidaia/pubspec.lock index 467f69f..cef3291 100644 --- a/vidaia/pubspec.lock +++ b/vidaia/pubspec.lock @@ -300,6 +300,20 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_appauth: + dependency: "direct main" + description: + name: flutter_appauth + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.2" + flutter_appauth_platform_interface: + dependency: transitive + description: + name: flutter_appauth_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" flutter_lints: dependency: "direct dev" description: @@ -435,7 +449,7 @@ packages: source: hosted version: "0.15.0" http: - dependency: transitive + dependency: "direct main" description: name: http url: "https://pub.dartlang.org" diff --git a/vidaia/pubspec.yaml b/vidaia/pubspec.yaml index 5daa126..d457347 100644 --- a/vidaia/pubspec.yaml +++ b/vidaia/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: easy_localization: ^3.0.0 carousel_slider: ^4.0.0 flutter_secure_storage: ^5.0.2 + http: ^0.13.4 # The following adds the Cupertino Icons font to your application. @@ -55,6 +56,7 @@ dependencies: url_launcher: ^6.1.0 contained_tab_bar_view: ^0.8.0 mobile_scanner: ^1.0.0 + flutter_appauth: ^2.4.2 dev_dependencies: flutter_test: