Skip to content

feat: typed login() rejection shape across platforms (+ useAssetLinks plist passthrough docs/constant) - #111

Merged
dianaKhortiuk-frontegg merged 2 commits into
frontegg:masterfrom
airowe:feat/typed-login-errors
Jul 30, 2026
Merged

feat: typed login() rejection shape across platforms (+ useAssetLinks plist passthrough docs/constant)#111
dianaKhortiuk-frontegg merged 2 commits into
frontegg:masterfrom
airowe:feat/typed-login-errors

Conversation

@airowe

@airowe airowe commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Implements #110 — a documented, typed error shape for login() rejections across platforms — plus companion plumbing for frontegg/frontegg-ios-swift#293.

What

Typed login errors (commit 1):

  • TS exports: FronteggLoginErrorCode ('user_cancelled' | 'oauth_failed' | 'network' | 'unknown'), FronteggLoginError { code, message, userCancelled, nativeCode?, nativeMessage? }, normalizeLoginError(); login() rethrows the normalized shape so consumers get it even against older native layers. Documented in docs/usage.md.
  • iOS: fixes a latent bug — the current rejecter(error.failureReason, …) passes nil (failureReason only exists on the inner FronteggError.Authentication enum), so JS always received EUNSPECIFIED. New normalizedLoginError(_:) switches on the real error cases (verified against FronteggSwift 1.3.13, the podspec pin): .operationCanceled → user_cancelled; the OAuth family → oauth_failed; .networkError → network; conservative unknown otherwise, raw reason preserved in userInfo.
  • Android: the module already forwards the SDK's login callback into the promise (FR-25938) but rejects everything as flat LOGIN_ERROR; now normalized — CanceledByUserException → user_cancelled, FailedToAuthenticateException → oauth_failed, IOException → network — with the raw exception class/message preserved.

useAssetLinks passthrough (commit 2): the wrapper passes no config to native init (FronteggSwift decodes Frontegg.plist itself), so a JS option would be dead plumbing. Instead: docs/setup.md documents the plist key as the passthrough (Codable ignores unknown keys today; takes effect automatically if/when #293's native option ships), and getConstants() now exposes useAssetLinks (typed via a new FronteggConstants interface), matching how Android surfaces its config.

Validation

  • yarn test: 8/8 (5 new tests covering the normalization paths)
  • yarn typecheck / eslint: clean vs. baseline (the two example/ errors and one warning exist identically on master)
  • Android: :frontegg_react-native:testDebugUnitTest BUILD SUCCESSFUL via example/android (module Kotlin + new JVM tests)
  • iOS: swiftc -parse clean; the error-mapping function additionally typechecked against the real FronteggSwift 1.3.13 error model. A full pod-install host-app build is the remaining verification — draft until we've done that.

One mapping judgment call flagged for review: iOS .authError(.other(Error)) may wrap URLError network failures; we left it unknown (raw reason preserved) rather than guess.

airowe added 2 commits July 27, 2026 14:08
Implements frontegg#110.

login() now rejects with a stable, documented FronteggLoginError:
code ('user_cancelled' | 'oauth_failed' | 'network' | 'unknown'),
message, userCancelled, plus the raw platform details preserved as
nativeCode / nativeMessage.

- iOS: the previous reject code was error.failureReason on the outer
  FronteggError, which is nil (only the inner Authentication enum
  implements it), so JS always saw EUNSPECIFIED. Map the FronteggError
  cases onto the stable codes and carry the raw failure reason in the
  NSError userInfo.
- Android: the SDK's (Exception?) -> Unit login callback was already
  forwarded into the promise (rejecting as LOGIN_ERROR); normalize the
  rejection code instead (CanceledByUserException -> user_cancelled,
  FailedToAuthenticateException -> oauth_failed, IOException -> network)
  and carry the exception class name/message in userInfo.
- JS: normalizeLoginError() guarantees the shape even for older native
  layers or unexpected rejections; types exported from the package root.
- Docs: new 'Handling login errors' section in docs/usage.md.
- Tests: jest coverage for login()/normalizeLoginError; Android JVM
  unit tests for the new code mapping.
Companion to frontegg/frontegg-ios-swift#293 (option proposed in a
separate native PR); follow-up to the typed login() errors work for
frontegg#110.

iOS configuration is plist-driven: FronteggSwift decodes Frontegg.plist
directly, so the plist is the wrapper's config passthrough. Unknown keys
are ignored by the SDK's Codable decode, which makes setting
<key>useAssetLinks</key> today a safe no-op that takes effect with no
wrapper change once a FronteggSwift version supporting it ships.

- Read the useAssetLinks key from Frontegg.plist in the bridge and
  expose it via getConstants() (parity with Android's useAssetsLinks
  BuildConfig constant), so JS can introspect the configuration.
- Type getConstants() with a FronteggConstants interface including the
  optional useAssetLinks flag.
- Document the plist-key forwarding in docs/setup.md.
@dianaKhortiuk-frontegg
dianaKhortiuk-frontegg marked this pull request as ready for review July 30, 2026 07:53

@dianaKhortiuk-frontegg dianaKhortiuk-frontegg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Verified the JS layer locally, because CI did not run it.

Only Android E2E and iOS E2E reported on this PR — Install | Build | Test and Lint and Typecheck never ran, so the 77 lines of new tests and the new TypeScript types were unverified. Ran them against the PR head:

  • jest8/8 pass, including login resolve/reject and all normalizeLoginError cases (stable codes, unknown-code passthrough, null/non-error rejections).
  • tsc --noEmit — no errors in src/.
  • eslint — 0 errors. The one no-shadow warning on FronteggNative.ts:177 is pre-existing on master and untouched here.

The fix itself is right: the iOS bridge rejected with error.failureReason, which is nil on the outer FronteggError (only the inner Authentication enum implements it), so every iOS login failure surfaced as EUNSPECIFIED. Android's mapping is unit-tested for all four codes.

Two follow-ups, neither blocking:

  1. The iOS mapping has no unit test — this repo has no Swift unit-test target (only ReactNativeExampleUITests), so there's nowhere to put one. Worth adding that target separately.
  2. The missing CI jobs are worth investigating — a PR that changes JS/TS should not merge without the JS test and typecheck jobs running.

@dianaKhortiuk-frontegg
dianaKhortiuk-frontegg merged commit c354a7e into frontegg:master Jul 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants