From dd742882fd312b1431d5710b6bb7927e1fc547e6 Mon Sep 17 00:00:00 2001 From: cuqmbr Date: Sun, 12 Nov 2023 10:01:21 +0200 Subject: [PATCH] add authentication service --- lib/constants/jwt_claims.dart | 10 + lib/models/global_instances/global_user.dart | 10 + lib/network/authentication_service.dart | 170 ++++++++ pubspec.lock | 390 +++++++++++++++++++ pubspec.yaml | 6 + 5 files changed, 586 insertions(+) create mode 100644 lib/constants/jwt_claims.dart create mode 100644 lib/models/global_instances/global_user.dart create mode 100644 lib/network/authentication_service.dart diff --git a/lib/constants/jwt_claims.dart b/lib/constants/jwt_claims.dart new file mode 100644 index 0000000..935d647 --- /dev/null +++ b/lib/constants/jwt_claims.dart @@ -0,0 +1,10 @@ +class JwtClaims { + + static const String id = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'; + + static const String email = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'; + + static const String phone = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone'; + + static const String roles ='http://schemas.microsoft.com/ws/2008/06/identity/claims/role'; +} diff --git a/lib/models/global_instances/global_user.dart b/lib/models/global_instances/global_user.dart new file mode 100644 index 0000000..06168a2 --- /dev/null +++ b/lib/models/global_instances/global_user.dart @@ -0,0 +1,10 @@ +class GlobalUser { + + static String? id; + + static String? email; + + static String? phone; + + static List? roles; +} diff --git a/lib/network/authentication_service.dart b/lib/network/authentication_service.dart new file mode 100644 index 0000000..be65417 --- /dev/null +++ b/lib/network/authentication_service.dart @@ -0,0 +1,170 @@ +import 'package:graphql/client.dart'; +import 'package:jwt_decoder/jwt_decoder.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:shopping_assistant_mobile_client/constants/jwt_claims.dart'; +import 'package:uuid/uuid.dart'; + +class AuthenticationService { + final GraphQLClient client = GraphQLClient( + cache: GraphQLCache(), + link: HttpLink('https://shopping-assistant-api-dev.azurewebsites.net/graphql'), + ); + + late SharedPreferences prefs; + + AuthenticationService() { + SharedPreferences.getInstance().then((result) => {prefs = result}); + } + + Future getAccessToken() async { + var accessToken = prefs.getString('accessToken'); + var refreshToken = prefs.getString('refreshToken'); + + if (accessToken == null && refreshToken != null) { + print('WTF??'); + } else if (accessToken == null && refreshToken == null) { + accessToken = await accessGuest(); + print('Got new access token $accessToken'); + } else if (JwtDecoder.isExpired(accessToken!)) { + accessToken = await refreshAccessToken(); + print('Refreshed access token $accessToken'); + } + + print('Returned access token $accessToken'); + return accessToken!; + } + + Future login(String? email, String? phone, String password) async { + const String loginQuery = r''' + mutation Login($login: AccessUserModelInput!) { + login(login: $login) { + accessToken + refreshToken + } + } + '''; + + final MutationOptions options = MutationOptions( + document: gql(loginQuery), + variables: { + 'login': { + 'email': email, + 'phone': phone, + 'password': password, + }, + }, + ); + + final QueryResult result = await client.mutate(options); + + if (result.hasException) { + print(result.exception.toString()); + } + + final accessToken = result.data?['login']['accessToken'] as String; + final refreshToken = result.data?['login']['refreshToken'] as String; + + prefs.setString('accessToken', accessToken); + prefs.setString('refreshToken', refreshToken); + } + + Future accessGuest() async { + String? guestId = prefs.getString('guestId'); + guestId ??= const Uuid().v4(); + prefs.setString('guestId', guestId); + + const String accessGuestMutation = r''' + mutation AccessGuest($guest: AccessGuestModelInput!) { + accessGuest(guest: $guest) { + accessToken + refreshToken + } + } + '''; + + final MutationOptions options = MutationOptions( + document: gql(accessGuestMutation), + variables: { + 'guest': {'guestId': guestId}, + }, + ); + + final QueryResult result = await client.mutate(options); + + if (result.hasException) { + print(result.exception.toString()); + } + + final String accessToken = + result.data?['accessGuest']['accessToken'] as String; + final String refreshToken = + result.data?['accessGuest']['refreshToken'] as String; + + prefs.setString('accessToken', accessToken); + prefs.setString('refreshToken', refreshToken); + + return accessToken; + } + + Future refreshAccessToken() async { + var accessToken = prefs.getString('accessToken'); + var refreshToken = prefs.getString('refreshToken'); + + const String refreshAccessTokenMutation = r''' + mutation RefreshAccessToken($model: TokensModelInput!) { + refreshAccessToken(model: $model) { + accessToken + refreshToken + } + } + '''; + + final QueryOptions options = QueryOptions( + document: gql(refreshAccessTokenMutation), + variables: { + 'model': { + 'accessToken': accessToken, + 'refreshToken': refreshToken, + }, + }, + ); + + final QueryResult result = await client.query(options); + + if (result.hasException) { + print(result.exception.toString()); + } + + accessToken = result.data?['refreshAccessToken']['accessToken'] as String; + refreshToken = result.data?['refreshAccessToken']['refreshToken'] as String; + + prefs.setString('accessToken', accessToken); + prefs.setString('refreshToken', refreshToken); + + return accessToken; + } + + String getIdFromAccessToken(String accessToken) { + var decodedToken = JwtDecoder.decode(accessToken); + return decodedToken[JwtClaims.id]; + } + + String getEmailFromAccessToken(String accessToken) { + var decodedToken = JwtDecoder.decode(accessToken); + return decodedToken[JwtClaims.email]; + } + + String getPhoneFromAccessToken(String accessToken) { + var decodedToken = JwtDecoder.decode(accessToken); + return decodedToken[JwtClaims.phone]; + } + + // List getRolesFromAccessToken(String accessToken) { + // var decodedToken = JwtDecoder.decode(accessToken); + // List roles = []; + // for (var role in decodedToken[JwtClaims.roles] as List) { + // roles.add(role); + // } + // return roles; + // } +} diff --git a/pubspec.lock b/pubspec.lock index 0b808da..41b9539 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" async: dependency: transitive description: @@ -41,6 +49,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.2" + connectivity_plus: + dependency: transitive + description: + name: connectivity_plus + sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f + url: "https://pub.dev" + source: hosted + version: "5.0.1" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + url: "https://pub.dev" + source: hosted + version: "1.2.4" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -49,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" + source: hosted + version: "0.7.8" fake_async: dependency: transitive description: @@ -57,11 +97,35 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_hooks: + dependency: transitive + description: + name: flutter_hooks + sha256: "7c8db779c2d1010aa7f9ea3fbefe8f86524fcb87b69e8b0af31e1a4b55422dec" + url: "https://pub.dev" + source: hosted + version: "0.20.3" flutter_lints: dependency: "direct dev" description: @@ -75,6 +139,123 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + gql: + dependency: transitive + description: + name: gql + sha256: e5225e3be4d7eb4027406ab07cb68ad3a089deb3f7f6dc46edbdec78f2e5549f + url: "https://pub.dev" + source: hosted + version: "1.0.1-alpha+1696717343881" + gql_dedupe_link: + dependency: transitive + description: + name: gql_dedupe_link + sha256: "79625bc8029755ce6b26483adf0255c6b6114acc56e7ef81469a99f1ce2296db" + url: "https://pub.dev" + source: hosted + version: "2.0.4-alpha+1696717344020" + gql_error_link: + dependency: transitive + description: + name: gql_error_link + sha256: bfdb543137da89448cc5d003fd029c2e8718931d39d4a7dedb16f9169862fbb9 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + gql_exec: + dependency: transitive + description: + name: gql_exec + sha256: da419a3ebaae7672ed662c42d754ffba996347af7fe0ca031f1dd699334994d8 + url: "https://pub.dev" + source: hosted + version: "1.0.1-alpha+1696717343896" + gql_http_link: + dependency: transitive + description: + name: gql_http_link + sha256: "0789d397d46ce274942fcc73e18a080cd2584296dadc33d8ae53d0666d7fe981" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + gql_link: + dependency: transitive + description: + name: gql_link + sha256: bcbb09ae8b200f413aa2d21fbf6ce4c4ac1ac443e81c612f29ef1587f4c84122 + url: "https://pub.dev" + source: hosted + version: "1.0.1-alpha+1696717343909" + gql_transform_link: + dependency: transitive + description: + name: gql_transform_link + sha256: "0645fdd874ca1be695fd327271fdfb24c0cd6fa40774a64b946062f321a59709" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + graphql: + dependency: "direct main" + description: + name: graphql + sha256: "4ac531068107dffef188c74e7ff662777b729e9d5e0686f71623d4af1e3751c8" + url: "https://pub.dev" + source: hosted + version: "5.2.0-beta.6" + graphql_flutter: + dependency: "direct main" + description: + name: graphql_flutter + sha256: "39b5e830bc654ab02c5b776c31675841d1a8c95840fdd284efba713b1d47e65d" + url: "https://pub.dev" + source: hosted + version: "5.2.0-beta.6" + hive: + dependency: transitive + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + http: + dependency: "direct main" + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + jwt_decoder: + dependency: "direct main" + description: + name: jwt_decoder + sha256: "54774aebf83f2923b99e6416b4ea915d47af3bde56884eb622de85feabbc559f" + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -107,6 +288,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + normalize: + dependency: transitive + description: + name: normalize + sha256: "8a60e37de5b608eeaf9b839273370c71ebba445e9f73b08eee7725e0d92dbc43" + url: "https://pub.dev" + source: hosted + version: "0.8.2+1" path: dependency: transitive description: @@ -115,6 +312,142 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" + source: hosted + version: "5.4.0" + platform: + dependency: transitive + description: + name: platform + sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + url: "https://pub.dev" + source: hosted + version: "2.1.6" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + url: "https://pub.dev" + source: hosted + version: "2.3.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + url: "https://pub.dev" + source: hosted + version: "2.3.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" sky_engine: dependency: transitive description: flutter @@ -168,6 +501,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" vector_math: dependency: transitive description: @@ -184,5 +533,46 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.4-beta" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + win32: + dependency: transitive + description: + name: win32 + sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + url: "https://pub.dev" + source: hosted + version: "5.0.9" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + xml: + dependency: transitive + description: + name: xml + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" sdks: dart: ">=3.1.4 <4.0.0" + flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml index 40fa083..0840073 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,12 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + http: ^1.1.0 + jwt_decoder: ^2.0.1 + graphql: ^5.2.0-beta.6 + shared_preferences: ^2.2.2 + uuid: ^3.0.7 + graphql_flutter: ^5.2.0-beta.6 dev_dependencies: flutter_test: