# packages/web/README.md · versecafe/solid-native

[View on GitCafe](https://git.cafe/versecafe/solid-native/blob/8ec111b04c5c361b1bd7ff86ec086a87a275264f/packages/web/README.md)

Repository: [versecafe/solid-native](https://git.cafe/versecafe/solid-native)

Visibility: public

Requested revision: 8ec111b04c5c361b1bd7ff86ec086a87a275264f

Requested commit: 8ec111b04c5c361b1bd7ff86ec086a87a275264f

Commit: 8ec111b04c5c361b1bd7ff86ec086a87a275264f

Blob: 83a23cf52903d58e7b5d13ee8b95ce40491fc510

Size: 4395 bytes

[Immutable source](https://git.cafe/versecafe/solid-native/blob/8ec111b04c5c361b1bd7ff86ec086a87a275264f/packages/web/README.md?format=markdown)

````
# Web and incoming links

```ts
import * as web from '@solid-native/web';
import * as linking from '@solid-native/linking';

await web.open('https://github.com/owner/repo');
await linking.open('mailto:hello@example.com');

const off = linking.onOpen(({ url }) => {
  // Pass through your router's configured prefixes and route validation.
  router.openLink(url);
});
```

`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
const { verifier, challenge } = await web.createPkce();
// A separate random verifier is also suitable as opaque OAuth state.
const state = (await web.createPkce()).verifier;
const redirectUrl = 'solid-native://oauth/callback';
const query = Object.entries({
  client_id: publicClientId,
  response_type: 'code',
  redirect_uri: redirectUrl,
  state,
  code_challenge: challenge,
  code_challenge_method: 'S256',
})
  .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
  .join('&');

const result = await web.authenticate({
  url: `https://your-identity-provider.example/authorize?${query}`,
  redirectUrl,
  state,
});
if (result) {
  // Exchange result.code with verifier using the provider's documented flow.
  // Never embed a confidential OAuth client secret in the application.
}
```

`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.

````
