README.md

Web and incoming links

ts
1import * as web from '@solid-native/web';
2import * as linking from '@solid-native/linking';
3
4await web.open('https://github.com/owner/repo');
5await linking.open('mailto:hello@example.com');
6
7const off = linking.onOpen(({ url }) => {
8 // Pass through your router's configured prefixes and route validation.
9 router.openLink(url);
10});

web.open uses Safari View Controller / Android Custom Tabs. Its promise acknowledges presentation, not page load or dismissal. linking.open hands an absolute URL to its external OS handler and rejects if it cannot open. These are not embedded WebViews; a future WebView component belongs in @solid-native/web, not another auth-specific browser package.

linking.onOpen allows one app-root consumer, cleaned up with its Solid owner or the returned off. Cold and live links enter an ordered native inbox before JS delivery. Identical URLs remain distinct. Unsubscribing leaves undelivered entries in the inbox. Delivery acknowledges a synchronous listener invocation, not the completion of asynchronous navigation. Listener exceptions are reported and do not block subsequent entries. The inbox is process-local, not a persistent queue; an abrupt process/runtime failure between delivery and acknowledgement is not an exactly-once guarantee.

Browser sign-in

ts
1const { verifier, challenge } = await web.createPkce();
2// A separate random verifier is also suitable as opaque OAuth state.
3const state = (await web.createPkce()).verifier;
4const redirectUrl = 'solid-native://oauth/callback';
5const query = Object.entries({
6 client_id: publicClientId,
7 response_type: 'code',
8 redirect_uri: redirectUrl,
9 state,
10 code_challenge: challenge,
11 code_challenge_method: 'S256',
12})
13 .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
14 .join('&');
15
16const result = await web.authenticate({
17 url: `https://your-identity-provider.example/authorize?${query}`,
18 redirectUrl,
19 state,
20});
21if (result) {
22 // Exchange result.code with verifier using the provider's documented flow.
23 // Never embed a confidential OAuth client secret in the application.
24}

createPkce uses native cryptographic randomness and SHA-256 (RFC 7636 S256). authenticate uses ASWebAuthenticationSession / Custom Tabs, requires an HTTPS authorization URL and a registered custom-scheme callback, and permits one active web presentation. It checks scheme, host, port, encoded path, exactly one matching state, and exactly one nonempty code or OAuth error. User cancellation returns null; malformed callbacks, state mismatch, OAuth errors, and presentation failures reject. It does not add parameters, exchange tokens, manage refresh, persist a session, or verify an ID token. Use fresh state and PKCE for every attempt. Do not log callback URLs or authorization codes. HTTPS app/universal-link callbacks, implicit-token flows, ephemeral-session controls, and process-death continuation are not supported by this initial API.

Native host integration

The repository's sample host already compiles/registers all modules. It uses solid-native://app/... for navigation and reserves solid-native://oauth/callback for auth. Replace these with app-specific schemes in production.

Android requires androidx.browser:browser, the JSI adapter/module registration, and both activities declared in android/app/src/main/AndroidManifest.xml:

  • SolidWebAuthActivity is not exported and must not be noHistory. It stays beneath the browser to receive callbacks and recognize Back cancellation.
  • Exported SolidWebRedirectActivity has only the dedicated OAuth intent filter. It forwards to the existing auth activity; late callbacks are discarded, never sent to normal linking.
  • MainActivity captures navigation URLs in onCreate and onNewIntent; ordinary route filters must not overlap auth callbacks.

iOS requires the package sources, CFBundleURLTypes, and forwarding application (or scene, for a scene-based host) URL/user-activity callbacks to SolidLinking. Reserve OAuth endpoints from this forwarding, including after an auth session ends. ASWebAuthenticationSession owns its callback. HTTPS incoming app links also require your signed associated-domain entitlements and server association files; this repository does not claim a domain or configure those files for you.