auth0 / auth0-flutter

Auth0 SDK for Flutter
https://pub.dev/documentation/auth0_flutter/latest/
Apache License 2.0
57 stars 36 forks source link

Consolidate `WebAuthenticationException`s caused by a user cancelling the flow into a single exception. #395

Open danallen88 opened 6 months ago

danallen88 commented 6 months ago

Checklist

Describe the problem you'd like to have solved

Users cancelling a web authentication session workflow results in a WebAuthenticationException with an internal code field that describes the cause for the exception. Presumably this is because many things could cause this type of exception.

I have a use case where knowing specifically that the user cancelled the session is helpful, but in order to identify whether this action was taken by a user, I must inspect the code for each platform that can throw it (iOS and Android for me) and determine whether the code is either USER_CANCELLED (iOS) or a0.authentication_canceled (Android). These appear to be platform-specific codes that are emitted from a native layer.

Describe the ideal solution

Ideally there is an exception type that wraps any scenario where a user cancelled the web authentication session, perhaps by subclassing WebAuthenticationException with a new exception UserCancelledWebAuthenticationException. Internal to the SDK, this exception can be created by inspecting the variety of codes that indicate the exception, and create a single exception that could be caught and dealt with.

Alternatives and current workarounds

As it stands, this is the class I am using to capture these codes:

import 'package:auth0_flutter/auth0_flutter.dart'
    show WebAuthentication, WebAuthenticationException;

/// {@template web_authentication_exception_code}
/// Error codes that can be returned from a [WebAuthentication] session within a
/// [WebAuthenticationException].
///
/// These codes are emitted from the native layer which means that different
/// unique codes can exist for the same type of exception on different
/// platforms.
/// {@endtemplate}
enum WebAuthenticationExceptionCode {
  /// The user cancelled an iOS web authentication session.
  iOSUserCancelled('USER_CANCELLED'),

  /// The user cancelled an Android web authentication session.
  androidUserCancelled('a0.authentication_canceled');

  /// {@macro web_authentication_exception_code}
  const WebAuthenticationExceptionCode(this.code);

  /// The [String]-based code representing the reason for the exception.
  final String code;

  /// Whether the given [code] represents a user cancelling a web authentication
  /// session regardless of platform.
  static bool userCancelled(String code) =>
      code == WebAuthenticationExceptionCode.iOSUserCancelled.code ||
      code == WebAuthenticationExceptionCode.androidUserCancelled.code;
}

Then in my login method I am doing something like the following:

try {
      return await _auth0
          .webAuthentication(scheme: _customScheme)
          .login(audience: _audience);
    } on WebAuthenticationException catch (error, stackTrace) {
      if (WebAuthenticationExceptionCode.userCancelled(error.code)) {
        Error.throwWithStackTrace(
          LoginCancelledException(error),
          stackTrace,
        );
      }

      rethrow;
    } catch (error, stackTrace) {
      Error.throwWithStackTrace(
        LoginException(error),
        stackTrace,
      );
    }

Additional context

No response

MilesAdamson commented 5 months ago

I have the exact same thing in my app

      if (e.code == "USER_CANCELLED" ||
          e.code == "a0.authentication_canceled") {
        // user closed the login webpage
        store.dispatch(const LoginFailedAction("Login cancelled"));
        return;
      }

There are differences when tokens fail to refresh while offline as well. The android "code" is not even really a code, it's a full sentence (same as the message).

  static const _androidRefreshErrorCode =
      "An error occurred while trying to use the Refresh Token to renew the Credentials.";
  static const _iOSRefreshErrorCode = "RENEW_FAILED";

It would be very nice to have the codes be identical on all platforms