From 7e08c9b44394dec552892b07eecaf514a84654c8 Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Tue, 31 Oct 2023 22:53:13 +0000 Subject: [PATCH] Ensure Dart `expiresAt` uses the UTC time zone --- .../lib/src/credentials.dart | 4 +- .../test/credentials_test.dart | 95 +++++++++++++------ ...ethod_channel_auth0_flutter_auth_test.dart | 4 +- ...d_channel_auth0_flutter_web_auth_test.dart | 2 +- ...thod_channel_credentials_manager_test.dart | 2 +- 5 files changed, 71 insertions(+), 36 deletions(-) diff --git a/auth0_flutter_platform_interface/lib/src/credentials.dart b/auth0_flutter_platform_interface/lib/src/credentials.dart index 08dfca4e..156bd870 100644 --- a/auth0_flutter_platform_interface/lib/src/credentials.dart +++ b/auth0_flutter_platform_interface/lib/src/credentials.dart @@ -63,7 +63,7 @@ class Credentials { idToken: result['idToken'] as String, accessToken: result['accessToken'] as String, refreshToken: result['refreshToken'] as String?, - expiresAt: DateTime.parse(result['expiresAt'] as String), + expiresAt: DateTime.parse(result['expiresAt'] as String).toUtc(), scopes: Set.from(result['scopes'] as List), user: UserProfile.fromMap(Map.from( result['userProfile'] as Map)), @@ -74,7 +74,7 @@ class Credentials { 'idToken': idToken, 'accessToken': accessToken, 'refreshToken': refreshToken, - 'expiresAt': expiresAt.toIso8601String(), + 'expiresAt': expiresAt.toUtc().toIso8601String(), 'scopes': scopes.toList(), 'tokenType': tokenType, }; diff --git a/auth0_flutter_platform_interface/test/credentials_test.dart b/auth0_flutter_platform_interface/test/credentials_test.dart index 3fced30b..c9a63930 100644 --- a/auth0_flutter_platform_interface/test/credentials_test.dart +++ b/auth0_flutter_platform_interface/test/credentials_test.dart @@ -6,12 +6,10 @@ import 'package:intl/intl.dart'; void main() { initializeDateFormatting(); - test('Credentials throws when expiresAt Locale set to ar', () async { - final dateTime = DateTime(2022); - final isoDateTimeString = _formatISOTime(dateTime, 'ar'); - - expect( - () => Credentials.fromMap({ + group('Credentials.fromMap', () { + test('expiresAt is a UTC DateTime', () async { + const isoDateTimeString = '2023-11-01T22:16:35.760Z'; + final credentials = Credentials.fromMap({ 'accessToken': 'accessToken', 'idToken': 'idToken', 'refreshToken': 'refreshToken', @@ -19,38 +17,75 @@ void main() { 'scopes': ['a'], 'userProfile': {'sub': '123', 'name': 'John Doe'}, 'tokenType': 'Bearer', - }), - throwsA(isA()), - ); + }); + + expect(credentials.expiresAt.isUtc, true); + }); + + test('Credentials throws when expiresAt Locale set to ar', () async { + final dateTime = DateTime(2022); + final isoDateTimeString = _formatISOTime(dateTime, 'ar'); + + expect( + () => Credentials.fromMap({ + 'accessToken': 'accessToken', + 'idToken': 'idToken', + 'refreshToken': 'refreshToken', + 'expiresAt': isoDateTimeString, + 'scopes': ['a'], + 'userProfile': {'sub': '123', 'name': 'John Doe'}, + 'tokenType': 'Bearer', + }), + throwsA(isA()), + ); + }); + + test('Credentials does not throw when expiresAt Locale set to US', + () async { + initializeDateFormatting(); + final dateTime = DateTime(2022); + final isoDateTimeString = _formatISOTime(dateTime, 'en_US'); + + expect( + Credentials.fromMap({ + 'accessToken': 'accessToken', + 'idToken': 'idToken', + 'refreshToken': 'refreshToken', + 'expiresAt': isoDateTimeString, + 'scopes': ['a'], + 'userProfile': {'sub': '123', 'name': 'John Doe'}, + 'tokenType': 'Bearer', + }), + isA(), + ); + }); }); - test('Credentials does not throw when expiresAt Locale set to US', () async { - initializeDateFormatting(); - final dateTime = DateTime(2022); - final isoDateTimeString = _formatISOTime(dateTime, 'en_US'); + group('toMap', () { + test('expiresAt is a ISO 8601 date with UTC time zone', () async { + final dateTime = DateTime(2023, 11, 1, 22, 16, 35, 760); + final credentials = Credentials( + accessToken: 'accessToken', + idToken: 'idToken', + refreshToken: 'refreshToken', + expiresAt: dateTime, + scopes: {'a'}, + user: const UserProfile(sub: '123', name: 'John Doe'), + tokenType: 'Bearer'); - expect( - Credentials.fromMap({ - 'accessToken': 'accessToken', - 'idToken': 'idToken', - 'refreshToken': 'refreshToken', - 'expiresAt': isoDateTimeString, - 'scopes': ['a'], - 'userProfile': {'sub': '123', 'name': 'John Doe'}, - 'tokenType': 'Bearer', - }), - isA(), - ); + expect(credentials.toMap()['expiresAt'], '2023-11-01T22:16:35.760Z'); + }); }); } String _formatISOTime(final DateTime date, final String locale) { final duration = date.timeZoneOffset; + final stringDate = DateFormat('yyyy-MM-ddTHH:mm:ss.mmm', locale).format(date); + final hours = duration.inHours.toString().padLeft(2, '0'); + final minutes = + (duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0'); if (duration.isNegative) { - // ignore: lines_longer_than_80_chars - return "${DateFormat('yyyy-MM-ddTHH:mm:ss.mmm', locale).format(date)}-${duration.inHours.toString().padLeft(2, '0')}${(duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0')}"; - } else { - // ignore: lines_longer_than_80_chars - return "${DateFormat('yyyy-MM-ddTHH:mm:ss.mmm', locale).format(date)}+${duration.inHours.toString().padLeft(2, '0')}${(duration.inMinutes - (duration.inHours * 60)).toString().padLeft(2, '0')}"; + return '$stringDate-$hours$minutes'; } + return '$stringDate+$hours$minutes'; } diff --git a/auth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dart b/auth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dart index d57f7f89..9f299f8c 100644 --- a/auth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dart +++ b/auth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dart @@ -12,7 +12,7 @@ class MethodCallHandler { static const Map loginResultRequired = { 'accessToken': 'accessToken', 'idToken': 'idToken', - 'expiresAt': '2022-01-01', + 'expiresAt': '2023-11-01T22:16:35.760Z', 'scopes': ['a', 'b'], 'userProfile': {'sub': '123', 'name': 'John Doe'}, 'tokenType': 'Bearer' @@ -47,7 +47,7 @@ class MethodCallHandler { 'accessToken': 'accessToken', 'idToken': 'idToken', 'refreshToken': 'refreshToken', - 'expiresAt': '2022-01-01', + 'expiresAt': '2023-11-01T22:16:35.760Z', 'scopes': ['a', 'b'], 'userProfile': {'sub': '123', 'name': 'John Doe'}, 'tokenType': 'Bearer' diff --git a/auth0_flutter_platform_interface/test/method_channel_auth0_flutter_web_auth_test.dart b/auth0_flutter_platform_interface/test/method_channel_auth0_flutter_web_auth_test.dart index 88067ad2..5139706e 100644 --- a/auth0_flutter_platform_interface/test/method_channel_auth0_flutter_web_auth_test.dart +++ b/auth0_flutter_platform_interface/test/method_channel_auth0_flutter_web_auth_test.dart @@ -12,7 +12,7 @@ class MethodCallHandler { static const Map loginResultRequired = { 'accessToken': 'accessToken', 'idToken': 'idToken', - 'expiresAt': '2022-01-01', + 'expiresAt': '2023-11-01T22:16:35.760Z', 'scopes': ['a', 'b'], 'userProfile': {'sub': '123', 'name': 'John Doe'}, 'tokenType': 'Bearer' diff --git a/auth0_flutter_platform_interface/test/method_channel_credentials_manager_test.dart b/auth0_flutter_platform_interface/test/method_channel_credentials_manager_test.dart index f9d31fdd..eecbad68 100644 --- a/auth0_flutter_platform_interface/test/method_channel_credentials_manager_test.dart +++ b/auth0_flutter_platform_interface/test/method_channel_credentials_manager_test.dart @@ -12,7 +12,7 @@ class MethodCallHandler { static const Map credentials = { 'accessToken': 'accessToken', 'idToken': 'idToken', - 'expiresAt': '2022-01-01', + 'expiresAt': '2023-11-01T22:16:35.760Z', 'scopes': ['a', 'b'], 'userProfile': {'sub': '123', 'name': 'John Doe'}, 'tokenType': 'Bearer'