Skip to content

[auth 8] Add optional native Google and Apple transports #196

Description

@artiphishle

Roadmap: #188

Respect the [auth] roadmap first and the AGENTS.md file of every repository touched.

Previous roadmap step

[auth 7] (#195) provides adapter-neutral account/profile UI over the universal brokered OAuth baseline. This issue adds optional native provider transports as UX capabilities without forking the Auth domain, profile model or generated account UI.

Repository impact

Likely:

  • ankhorage/contracts
  • ankhorage/supabase-auth
  • ankhorage/studio
  • ankhorage/expo-runtime
  • ankhorage/orchestrator and/or dedicated native-auth module repositories
  • ankhorage/ankh for capability/readiness validation

Do not install provider-native packages into every generated app. Dependencies and Config Plugins must be planned only when a native transport is enabled for an enabled platform.

Goal

Add capability-driven native Google and Apple sign-in transports while keeping brokered browser OAuth available as the universal fallback/default.

Native transport changes only how a provider assertion is acquired. The configured Auth backend still owns creation of the normalized Ankhorage session.

Architecture

UI
  -> signInWithProvider('google' | 'apple')

Auth runtime coordinator
  -> choose configured transport for current platform

Provider transport
  -> brokeredRedirect
  -> nativeIdToken / nativeAssertion

Auth backend adapter
  -> exchange/verify assertion
  -> normalized AuthSession

The generated profile/account UI from [auth 7] must remain unchanged.

Neutral contract direction

Finalize a provider-neutral assertion/input contract, conceptually:

type ExternalIdentityAssertion =
  | {
      kind: 'oidc-id-token';
      provider: AuthOAuthProviderId;
      token: string;
      nonce?: string;
      authorizationCode?: string;
    }
  | {
      kind: 'provider-credential';
      provider: AuthOAuthProviderId;
      credential: unknown;
    };

Do not expose an untyped unknown credential publicly unless a safer discriminated contract cannot represent required providers. Provider-specific payloads may remain inside transport packages and be converted to a canonical backend-consumable assertion.

The Auth backend adapter should advertise supported assertion kinds/providers rather than every adapter pretending to support native tokens.

Google requirements

For native Google transport, support target-aware public client configuration:

Web Client ID
  public ID + trusted Web Client Secret
  Web Client ID remains first/primary backend audience where required

iOS Client ID
  public
  associated with stable bundle identifier

Android Client ID
  public
  associated with stable application ID and signing fingerprints

There are no trusted iOS/Android client secrets to store in generated apps/Vault.

The exact native package/API must be selected after current Expo SDK and provider guidance review. Prefer maintained native integrations and generated Config Plugin ownership. Do not freeze the implementation to a package from an example answer.

Apple requirements

Support native Sign in with Apple on enabled iOS targets and any other platform where the selected architecture/provider supports it intentionally.

Model the required Apple identifiers/keys/secret references with correct trusted/public separation. Do not treat Apple configuration as identical to Google fields.

Handle Apple-specific first-login name/email availability correctly: values returned only once must be persisted according to the profile initialization policy from [auth 6].

Platform-aware configuration

Extend [auth 4]'s requirement model so Studio requests native settings only when:

  • the platform is enabled;
  • the provider is enabled;
  • the native transport is selected;
  • the selected Auth backend can consume the assertion.

Example:

Web-only Google brokered redirect
  -> Web Client ID + Secret

Web + iOS, Google native on iOS
  -> Web Client ID + Secret + iOS Client ID

Web + iOS + Android, Google native on Native
  -> Web Client ID + Secret + iOS Client ID + Android Client ID/fingerprint readiness

Runtime/dependency planning

  • provider-native dependencies are conditional;
  • generated imports are static and planned at generation/build time;
  • no unreliable Metro runtime dynamic-import workaround;
  • Config Plugins, URL schemes, entitlements/capabilities and native files remain owned by reusable runtime/orchestrator modules;
  • Web bundles must not require native modules;
  • browser fallback remains available when configured and valid.

Supabase implementation

Implement assertion exchange behind @ankhorage/supabase-auth, for example through the supported Supabase ID-token/assertion methods, without leaking Supabase calls into generated screens.

  • validate advertised provider/assertion support;
  • normalize the resulting session through the existing canonical session path;
  • preserve profile verification/initialization lifecycle;
  • map provider/backend failures to neutral Auth error contracts;
  • do not store provider ID tokens longer than required for exchange.

Likely files

Contracts, non-exhaustive:

src/auth.ts
src/auth-oauth.test.ts
src/auth-flow-contract.test.ts

Supabase Auth:

src/createSupabaseAuthAdapter.ts
src/oauth.ts
src/types.ts
src/session.ts
src/*native* or assertion modules/tests

Studio/Expo Runtime/Orchestrator:

src/host/orchestrator/templates.ts
src/host/layout/templates/auth/*
src/ui/admin/pages/AuthAdminPage.tsx
planning/config-plugin modules

Exact provider package/module boundaries must be decided during implementation planning.

Phases

Phase 1 — assertion and transport capability contracts

Define neutral assertion inputs, backend capabilities, transport selection and result/error semantics.

Phase 2 — conditional native runtime modules

Implement reusable Google/Apple native transport modules, dependencies and Config Plugins only for enabled targets.

Phase 3 — Supabase assertion exchange

Consume native assertions behind @ankhorage/supabase-auth and return canonical sessions/profile lifecycle.

Phase 4 — Studio configuration and Doctor validation

Expose platform/provider/transport-specific requirements, callbacks, fingerprints and build readiness.

Phase 5 — real-device validation

Validate development/preview builds on iOS and Android with documented credential setup and deterministic non-secret CI coverage.

Tests

Cover at minimum:

  • transport selection by platform/provider/configuration;
  • unsupported native transport/backend combination rejected during planning/Doctor;
  • conditional dependency/config-plugin generation;
  • Web-only app receives no native provider dependency;
  • Google iOS/Android client IDs treated as public config, not secrets;
  • Web Client Secret remains trusted;
  • native assertion exchange returns canonical AuthSession;
  • nonce/code/provider error mapping;
  • Apple first-login profile values persist without being expected on later logins;
  • brokered browser fallback still works for providers/platforms without native transport;
  • fake second Auth backend can advertise/consume the neutral assertion contract without Supabase types.

Acceptance criteria

  1. Native Google and Apple are optional transport capabilities, not a separate Auth architecture.
  2. UI/profile/account contracts remain provider/platform neutral.
  3. Native dependencies/configuration are generated only when enabled.
  4. Google Web/iOS/Android client configuration is modeled with correct public/secret boundaries.
  5. Apple configuration and one-time profile data semantics are modeled correctly.
  6. Supabase consumes assertions behind its adapter and returns canonical sessions.
  7. Unsupported combinations fail at planning/Doctor time with actionable diagnostics.
  8. Brokered browser OAuth remains available and unaffected.
  9. Real native builds are validated without relying on Expo Go assumptions.
  10. Behavior-focused tests, package releases/changesets and full validation pass.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions