feat(server): passkey pre-refactor (#15060)

#### PR Dependency Tree


* **PR #15060** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* OpenApp native sign-in and native session exchange (JWT) for mobile &
desktop.
  * Centralized short-lived auth challenge store for one-time tokens.
* Encrypted per-endpoint token storage and native token handlers
(Android, iOS, Electron).

* **Improvements**
* Richer auth-method reporting (password, magic link, OAuth, passkey)
and improved sign-in flows.
* Hardened magic-link, OAuth, and session issuance; JWT-backed sessions
and websocket JWT support.
* UX tweaks: form-based password submit, OTP autocomplete, adjusted
captcha flow.

* **Bug Fixes**
  * Expanded tests and auth-state resets to avoid cross-test leakage.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-06-01 17:11:15 +08:00
committed by GitHub
parent 5b9d51b41b
commit ce9841df9d
74 changed files with 3719 additions and 939 deletions
@@ -2,7 +2,12 @@ import { configureElectronStateStorageImpls } from '@affine/core/desktop/storage
import { configureCommonModules } from '@affine/core/modules';
import { configureAppTabsHeaderModule } from '@affine/core/modules/app-tabs-header';
import { configureDesktopBackupModule } from '@affine/core/modules/backup';
import { ValidatorProvider } from '@affine/core/modules/cloud';
import {
AuthProvider,
ServerScope,
ServerService,
ValidatorProvider,
} from '@affine/core/modules/cloud';
import {
configureDesktopApiModule,
DesktopApiService,
@@ -63,6 +68,39 @@ export function setupModules() {
},
};
});
framework.scope(ServerScope).override(AuthProvider, p => {
const apis = p.get(DesktopApiService).api;
const serverService = p.get(ServerService);
const endpoint = serverService.server.baseUrl;
return {
async signInMagicLink(email, token, clientNonce) {
await apis.handler.auth.signInMagicLink(
endpoint,
email,
token,
clientNonce
);
},
async signInOauth(code, state, _provider, clientNonce) {
return await apis.handler.auth.signInOauth(
endpoint,
code,
state,
clientNonce
);
},
async signInPassword(credential) {
await apis.handler.auth.signInPassword(endpoint, credential);
},
async signInOpenAppSignInCode(code) {
await apis.handler.auth.signInOpenAppSignInCode(endpoint, code);
},
async signOut() {
await apis.handler.auth.signOut(endpoint);
},
};
});
const frameworkProvider = framework.provider();
@@ -2,7 +2,10 @@ import '@affine/core/bootstrap/electron';
import { apis } from '@affine/electron-api';
import { broadcastChannelStorages } from '@affine/nbstore/broadcast-channel';
import { cloudStorages } from '@affine/nbstore/cloud';
import {
cloudStorages,
configureSocketAuthMethod,
} from '@affine/nbstore/cloud';
import { bindNativeDBApis, sqliteStorages } from '@affine/nbstore/sqlite';
import {
bindNativeDBV1Apis,
@@ -18,6 +21,15 @@ import { OpConsumer } from '@toeverything/infra/op';
bindNativeDBApis(apis!.nbstore);
// oxlint-disable-next-line no-non-null-assertion
bindNativeDBV1Apis(apis!.db);
configureSocketAuthMethod((endpoint, cb) => {
// oxlint-disable-next-line no-non-null-assertion
apis!.auth
.readEndpointToken(endpoint)
.then(({ token }: { token?: string | null }) => {
cb(token ? { token, tokenType: 'jwt' } : {});
})
.catch(() => cb({}));
});
const storeManager = new StoreManagerConsumer([
...sqliteStorages,