diff --git a/packages/gotrue/lib/src/constants.dart b/packages/gotrue/lib/src/constants.dart index 200f940b..1e5f4f54 100644 --- a/packages/gotrue/lib/src/constants.dart +++ b/packages/gotrue/lib/src/constants.dart @@ -1,3 +1,4 @@ +import 'package:gotrue/src/types/api_version.dart'; import 'package:gotrue/src/types/auth_response.dart'; import 'package:gotrue/src/version.dart'; @@ -22,7 +23,14 @@ class Constants { static const autoRefreshTickThreshold = 3; /// The name of the header that contains API version. - static const apiVersionHeaderName = 'x-supabase-api-version'; + static const apiVersionHeaderName = 'X-Supabase-Api-Version'; +} + +class ApiVersions { + static final v20240101 = ApiVersion( + name: '2024-01-01', + timestamp: DateTime.parse('2024-01-01T00:00:00.0Z'), + ); } enum AuthChangeEvent { diff --git a/packages/gotrue/lib/src/fetch.dart b/packages/gotrue/lib/src/fetch.dart index 4fdbfd9e..c369f55d 100644 --- a/packages/gotrue/lib/src/fetch.dart +++ b/packages/gotrue/lib/src/fetch.dart @@ -65,9 +65,9 @@ class GotrueFetch { String? errorCode; - final ApiVersion? version = ApiVersion.fromResponse(error); + final responseApiVersion = ApiVersion.fromResponse(error); - if (version?.isSameOrAfter(ApiVersions.v20240101) ?? false) { + if (responseApiVersion?.isSameOrAfter(ApiVersions.v20240101) ?? false) { errorCode = _getErrorCode(data, 'code'); } else { errorCode = _getErrorCode(data, 'error_code'); @@ -86,7 +86,6 @@ class GotrueFetch { throw AuthWeakPasswordException( message: _getErrorMessage(data), statusCode: error.statusCode.toString(), - errorCode: ErrorCode.weakPassword.code, reasons: List.from(data['weak_password']['reasons']), ); } @@ -94,7 +93,6 @@ class GotrueFetch { throw AuthWeakPasswordException( message: _getErrorMessage(data), statusCode: error.statusCode.toString(), - errorCode: errorCode, reasons: List.from(data['weak_password']?['reasons'] ?? []), ); } @@ -102,7 +100,7 @@ class GotrueFetch { throw AuthApiException( _getErrorMessage(data), statusCode: error.statusCode.toString(), - errorCode: errorCode, + code: errorCode, ); } @@ -115,7 +113,7 @@ class GotrueFetch { // Set the API version header if not already set if (!headers.containsKey(Constants.apiVersionHeaderName)) { - headers[Constants.apiVersionHeaderName] = ApiVersions.v20240101.asString; + headers[Constants.apiVersionHeaderName] = ApiVersions.v20240101.name; } if (options?.jwt != null) { diff --git a/packages/gotrue/lib/src/gotrue_client.dart b/packages/gotrue/lib/src/gotrue_client.dart index e23d396d..08c4e90e 100644 --- a/packages/gotrue/lib/src/gotrue_client.dart +++ b/packages/gotrue/lib/src/gotrue_client.dart @@ -762,7 +762,7 @@ class GoTrueClient { throw AuthException( errorDescription, statusCode: errorCode, - errorCode: error, + code: error, ); } diff --git a/packages/gotrue/lib/src/types/api_version.dart b/packages/gotrue/lib/src/types/api_version.dart index 2dac0e87..b3b9840f 100644 --- a/packages/gotrue/lib/src/types/api_version.dart +++ b/packages/gotrue/lib/src/types/api_version.dart @@ -6,15 +6,16 @@ const String _apiVersionRegex = r'^2[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])'; /// Represents the API versions supported by the package. -class ApiVersions { - const ApiVersions._(); - static ApiVersion v20240101 = ApiVersion(DateTime(2024, 1, 1)); -} - -/// Represents the API version specified by a [date] in the format YYYY-MM-DD. +/// Represents the API version specified by a [name] in the format YYYY-MM-DD. class ApiVersion { - ApiVersion(this.date); + const ApiVersion({ + required this.name, + required this.timestamp, + }); + + final String name; + final DateTime timestamp; /// Parses the API version from the string date. static ApiVersion? fromString(String version) { @@ -22,24 +23,19 @@ class ApiVersion { return null; } - final DateTime? date = DateTime.tryParse(version); - if (date == null) return null; - return ApiVersion(date); + final DateTime? timestamp = DateTime.tryParse('${version}T00:00:00.0Z'); + if (timestamp == null) return null; + return ApiVersion(name: version, timestamp: timestamp); } /// Parses the API version from the response headers. static ApiVersion? fromResponse(Response response) { - final String? version = response.headers[Constants.apiVersionHeaderName]; + final version = response.headers[Constants.apiVersionHeaderName]; return version != null ? fromString(version) : null; } - final DateTime date; - - /// Return only the date part of the DateTime. - String get asString => date.toIso8601String().split('T').first; - /// Returns true if this version is the same or after [other]. bool isSameOrAfter(ApiVersion other) { - return date.isAfter(other.date) || date == other.date; + return timestamp.isAfter(other.timestamp) || name == other.name; } } diff --git a/packages/gotrue/lib/src/types/auth_exception.dart b/packages/gotrue/lib/src/types/auth_exception.dart index 0e2b7eef..683e6b2f 100644 --- a/packages/gotrue/lib/src/types/auth_exception.dart +++ b/packages/gotrue/lib/src/types/auth_exception.dart @@ -10,13 +10,17 @@ class AuthException implements Exception { /// Error code associated with the error. Most errors coming from /// HTTP responses will have a code, though some errors that occur /// before a response is received will not have one present. - final String? errorCode; + /// In that case [statusCode] will also be null. + /// + /// Find the full list of error codes in our documentation. + /// https://supabase.com/docs/reference/dart/auth-error-codes + final String? code; - const AuthException(this.message, {this.statusCode, this.errorCode}); + const AuthException(this.message, {this.statusCode, this.code}); @override String toString() => - 'AuthException(message: $message, statusCode: $statusCode, errorCode: $errorCode)'; + 'AuthException(message: $message, statusCode: $statusCode, errorCode: $code)'; @override bool operator ==(Object other) { @@ -25,12 +29,11 @@ class AuthException implements Exception { return other is AuthException && other.message == message && other.statusCode == statusCode && - other.errorCode == errorCode; + other.code == code; } @override - int get hashCode => - message.hashCode ^ statusCode.hashCode ^ errorCode.hashCode; + int get hashCode => message.hashCode ^ statusCode.hashCode ^ code.hashCode; } class AuthPKCEGrantCodeExchangeError extends AuthException { @@ -42,7 +45,6 @@ class AuthSessionMissingException extends AuthException { : super( message ?? 'Auth session missing!', statusCode: '400', - errorCode: ErrorCode.sessionNotFound.code, ); } @@ -50,12 +52,11 @@ class AuthRetryableFetchException extends AuthException { AuthRetryableFetchException({ String message = 'AuthRetryableFetchException', super.statusCode, - super.errorCode, }) : super(message); } class AuthApiException extends AuthException { - AuthApiException(super.message, {super.statusCode, super.errorCode}); + AuthApiException(super.message, {super.statusCode, super.code}); } class AuthUnknownException extends AuthException { @@ -70,8 +71,7 @@ class AuthWeakPasswordException extends AuthException { AuthWeakPasswordException({ required String message, - required String statusCode, + required super.statusCode, required this.reasons, - String? errorCode, - }) : super(message, statusCode: statusCode, errorCode: errorCode); + }) : super(message, code: ErrorCode.weakPassword.code); } diff --git a/packages/gotrue/lib/src/types/error_code.dart b/packages/gotrue/lib/src/types/error_code.dart index dbcfb6be..5dd90309 100644 --- a/packages/gotrue/lib/src/types/error_code.dart +++ b/packages/gotrue/lib/src/types/error_code.dart @@ -3,7 +3,6 @@ import 'package:collection/collection.dart'; enum ErrorCode { - unknown('unknown'), unexpectedFailure('unexpected_failure'), validationFailed('validation_failed'), badJson('bad_json'), @@ -43,6 +42,7 @@ enum ErrorCode { smsSendFailed('sms_send_failed'), emailNotConfirmed('email_not_confirmed'), phoneNotConfirmed('phone_not_confirmed'), + reauthNonceMissing('reauth_nonce_missing'), samlRelayStateNotFound('saml_relay_state_not_found'), samlRelayStateExpired('saml_relay_state_expired'), samlIdpNotFound('saml_idp_not_found'), diff --git a/packages/gotrue/test/api_version_test.dart b/packages/gotrue/test/api_version_test.dart index fe47f0e2..21695245 100644 --- a/packages/gotrue/test/api_version_test.dart +++ b/packages/gotrue/test/api_version_test.dart @@ -11,8 +11,8 @@ void main() { Constants.apiVersionHeaderName: validHeader, }); final version = ApiVersion.fromResponse(response); - expect(version?.date, DateTime(2024, 1, 1)); - expect(version?.asString, validHeader); + expect(version?.name, validHeader); + expect(version?.timestamp, DateTime(2024, 1, 1)); }); test('should return null object for invalid header', () { diff --git a/packages/gotrue/test/client_test.dart b/packages/gotrue/test/client_test.dart index 7212fab7..70f892a0 100644 --- a/packages/gotrue/test/client_test.dart +++ b/packages/gotrue/test/client_test.dart @@ -110,7 +110,7 @@ void main() { fail('signUp with weak password should throw exception'); } on AuthException catch (error) { expect(error, isA()); - expect(error.errorCode, ErrorCode.weakPassword.code); + expect(error.code, ErrorCode.weakPassword.code); } catch (error) { fail('signUp threw ${error.runtimeType} instead of AuthException'); } @@ -142,7 +142,7 @@ void main() { } on AuthException catch (error) { expect(error.message, errorMessage); expect(error.statusCode, '401'); - expect(error.errorCode, 'unauthorized_client'); + expect(error.code, 'unauthorized_client'); } catch (error) { fail( 'getSessionFromUrl threw ${error.runtimeType} instead of AuthException'); @@ -317,7 +317,7 @@ void main() { await client.updateUser(UserAttributes(password: password)); fail('updateUser did not throw'); } on AuthException catch (error) { - expect(error.errorCode, ErrorCode.samePassword.code); + expect(error.code, ErrorCode.samePassword.code); } }); diff --git a/packages/gotrue/test/fetch_test.dart b/packages/gotrue/test/fetch_test.dart index 173656f8..1a10b6f2 100644 --- a/packages/gotrue/test/fetch_test.dart +++ b/packages/gotrue/test/fetch_test.dart @@ -75,7 +75,7 @@ Future _testFetchRequest(Client client) async { try { await fetch.request(_mockUrl, RequestMethodType.get); } on AuthException catch (error) { - expect(error.errorCode, 'weak_password'); + expect(error.code, 'weak_password'); expect(error.message, 'error_message'); } catch (error) { fail('Should have thrown AuthException');