@molecule/app-auth 3.2.3 → 3.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -303
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
|
3
3
|
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
4
|
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
5
|
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
-
Generated: 2026-11-
|
|
6
|
+
Generated: 2026-11-04T14:20:03.859Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/app-auth
|
|
@@ -706,189 +706,85 @@ interface AuthClient<
|
|
|
706
706
|
T = UserProfile,
|
|
707
707
|
TRegister extends RegisterExtraShape<TRegister> = Record<never, never>,
|
|
708
708
|
> {
|
|
709
|
-
/**
|
|
710
|
-
* Returns the current authentication state snapshot.
|
|
711
|
-
*
|
|
712
|
-
* READING the state starts the client's one session restore when nobody has called
|
|
713
|
-
* {@link initialize} yet — lazily and exactly once, sharing the promise an explicit
|
|
714
|
-
* `initialize()` returns. A hook (`useAuth()`), a subscriber, `isAuthenticated()` and
|
|
715
|
-
* `getUser()` are reads too, so a client whose entry file dropped the template's
|
|
716
|
-
* `authClient.initialize()` line still restores instead of reporting `'loading'`
|
|
717
|
-
* forever.
|
|
718
|
-
*/
|
|
709
|
+
/** Returns the current authentication state snapshot. */
|
|
719
710
|
getState(): AuthState<T>
|
|
720
711
|
|
|
721
|
-
/**
|
|
722
|
-
* Returns whether the user is currently authenticated.
|
|
723
|
-
*/
|
|
712
|
+
/** Returns whether the user is currently authenticated. */
|
|
724
713
|
isAuthenticated(): boolean
|
|
725
714
|
|
|
726
|
-
/**
|
|
727
|
-
* Gets the current user.
|
|
728
|
-
*/
|
|
715
|
+
/** Gets the current user. */
|
|
729
716
|
getUser(): T | null
|
|
730
717
|
|
|
731
|
-
/**
|
|
732
|
-
* Optional ownership marker for the current session. Compare values only within
|
|
733
|
-
* this client: replacing or ending a session changes it, including re-adopting
|
|
734
|
-
* the same token and user. Profile updates and same-session refreshes keep it.
|
|
735
|
-
* Consumers can use it to discard stale async work without treating editable
|
|
736
|
-
* user fields as an immutable account identifier. Providers without this
|
|
737
|
-
* capability require the consumer's own stable identity check.
|
|
738
|
-
*/
|
|
718
|
+
/** Optional ownership marker for the current session. */
|
|
739
719
|
getSessionRevision?(): number
|
|
740
720
|
|
|
741
|
-
/**
|
|
742
|
-
* Updates the cached user object (state + persistent storage) without
|
|
743
|
-
* hitting the network. Intended for local refreshes after a per-app
|
|
744
|
-
* mutation (e.g., the user just PATCHed their own profile and the
|
|
745
|
-
* server returned the canonical row). A non-null profile does NOT change
|
|
746
|
-
* tokens or sign the client in: `authenticated` and `status` are unchanged,
|
|
747
|
-
* so an anonymous client stays anonymous. Use {@link login} /
|
|
748
|
-
* {@link register} for a credential exchange, or {@link setSession} when
|
|
749
|
-
* the token was issued outside the client.
|
|
750
|
-
*
|
|
751
|
-
* Passing `null` on a signed-in client, or while any session operation is
|
|
752
|
-
* pending, ends the local session: it clears the access token, refresh token
|
|
753
|
-
* and cached user, stops automatic refresh, and settles `authenticated: false`
|
|
754
|
-
* with `status: 'unauthenticated'` in one notification. Every older operation
|
|
755
|
-
* loses session ownership, and a persistent sign-out marker prevents a reload
|
|
756
|
-
* from restoring its credentials or a late cookie. A late credential exchange
|
|
757
|
-
* is revoked best-effort when a logout endpoint is configured, without blocking
|
|
758
|
-
* the caller. This method does not otherwise call the server for a settled
|
|
759
|
-
* session; use {@link logout} for explicit server revocation. On a settled
|
|
760
|
-
* anonymous client, `null` only clears the cached profile and leaves tokens
|
|
761
|
-
* and auth state unchanged.
|
|
762
|
-
*/
|
|
721
|
+
/** Updates the cached user object (state + persistent storage) without hitting the network. */
|
|
763
722
|
setUser(user: T | null): void
|
|
764
723
|
|
|
765
|
-
/**
|
|
766
|
-
* Gets the current access token.
|
|
767
|
-
*/
|
|
724
|
+
/** Gets the current access token. */
|
|
768
725
|
getAccessToken(): string | null
|
|
769
726
|
|
|
770
|
-
/**
|
|
771
|
-
* Stores the access token in the configured token storage adapter (in a
|
|
772
|
-
* browser, Web Storage; `'memory'` where no browser store exists). Use this
|
|
773
|
-
* to seed the token after an out-of-band exchange (e.g. the OAuth
|
|
774
|
-
* code→token redirect) instead of writing to `localStorage` directly,
|
|
775
|
-
* which would violate the storage contract and make a stored bearer token
|
|
776
|
-
* JS-readable (XSS-exfiltratable). Pass `null` to clear it.
|
|
777
|
-
*
|
|
778
|
-
* This changes token storage only: passing `null` does not by itself sign a
|
|
779
|
-
* signed-in client out, and a non-null token does not sign an anonymous
|
|
780
|
-
* client in (`authenticated` and `status` are unchanged). Use {@link logout}
|
|
781
|
-
* to end a session, or {@link setUser} with `null` for local-only sign-out.
|
|
782
|
-
* Use {@link login} / {@link register} for a credential exchange, or call
|
|
783
|
-
* {@link setSession} with the token and user to establish an already-issued
|
|
784
|
-
* session.
|
|
785
|
-
*/
|
|
727
|
+
/** Stores the access token in the configured token storage adapter (in a browser, Web Storage; `'memory'` where no browser store exists). */
|
|
786
728
|
setAccessToken(token: string | null): void
|
|
787
729
|
|
|
788
|
-
/**
|
|
789
|
-
* Adopts a session an out-of-band exchange already established: stores the
|
|
790
|
-
* access token (and the refresh token when given), caches the user, and
|
|
791
|
-
* sets the COMPLETE state — `initialized`, `authenticated`, `status:
|
|
792
|
-
* 'authenticated'`, no error — in one update, with one subscriber
|
|
793
|
-
* notification. Every hook reading the shared client sees the user as
|
|
794
|
-
* signed in immediately.
|
|
795
|
-
*
|
|
796
|
-
* This is the way to seed an OAuth/custom sign-in. Calling
|
|
797
|
-
* {@link setAccessToken} and {@link setUser} instead does NOT sign the
|
|
798
|
-
* client in.
|
|
799
|
-
*/
|
|
730
|
+
/** Adopts a session an out-of-band exchange already established: stores the access token (and the refresh token when given), caches the user, and sets the COMPLET… */
|
|
800
731
|
setSession(session: AuthSessionSeed<T>): void
|
|
801
732
|
|
|
802
|
-
/**
|
|
803
|
-
* Gets the refresh token.
|
|
804
|
-
*/
|
|
733
|
+
/** Gets the refresh token. */
|
|
805
734
|
getRefreshToken(): string | null
|
|
806
735
|
|
|
807
|
-
/**
|
|
808
|
-
* Logs in with credentials.
|
|
809
|
-
*/
|
|
736
|
+
/** Logs in with credentials. */
|
|
810
737
|
login(credentials: LoginCredentials): Promise<AuthResult<T>>
|
|
811
738
|
|
|
812
|
-
/**
|
|
813
|
-
* Logs out the current user. The server request is best-effort: a missing
|
|
814
|
-
* logout route (404), any other HTTP failure, or a network error never makes
|
|
815
|
-
* this method reject. It always clears the access token, refresh token and
|
|
816
|
-
* cached user, stops automatic refresh, and publishes one settled
|
|
817
|
-
* `authenticated: false`, `status: 'unauthenticated'` transition. Every pending
|
|
818
|
-
* session operation loses ownership; a persistent marker prevents reload from
|
|
819
|
-
* restoring an older session, and late credentials are revoked best-effort.
|
|
820
|
-
*
|
|
821
|
-
* When the API has no logout route, set {@link AuthClientConfigOptions.logoutEndpoint}
|
|
822
|
-
* to `null` (or `''`) to skip the request. Local sign-out still happens.
|
|
823
|
-
*/
|
|
739
|
+
/** Logs out the current user. */
|
|
824
740
|
logout(): Promise<void>
|
|
825
741
|
|
|
826
|
-
/**
|
|
827
|
-
* Registers a new user. Accepts the base fields plus the app's own fields
|
|
828
|
-
* (`TRegister`) at the top level — see {@link RegisterData}.
|
|
829
|
-
*/
|
|
742
|
+
/** Registers a new user. */
|
|
830
743
|
register(data: RegisterData<TRegister>): Promise<AuthResult<T>>
|
|
831
744
|
|
|
832
|
-
/**
|
|
833
|
-
* Refreshes the access token.
|
|
834
|
-
*/
|
|
745
|
+
/** Refreshes the access token. */
|
|
835
746
|
refresh(): Promise<AuthResult<T>>
|
|
836
747
|
|
|
837
|
-
/**
|
|
838
|
-
* Requests a password reset.
|
|
839
|
-
*/
|
|
748
|
+
/** Requests a password reset. */
|
|
840
749
|
requestPasswordReset(data: PasswordResetRequest): Promise<void>
|
|
841
750
|
|
|
842
|
-
/**
|
|
843
|
-
* Confirms a password reset.
|
|
844
|
-
*/
|
|
751
|
+
/** Confirms a password reset. */
|
|
845
752
|
confirmPasswordReset(data: PasswordResetConfirm): Promise<void>
|
|
846
753
|
|
|
847
|
-
/**
|
|
848
|
-
* Updates the current user's profile.
|
|
849
|
-
* Returns the saved profile: the answer applied onto the signed-in user field
|
|
850
|
-
* by field, with or without an `id` — a field it carries replaces the value
|
|
851
|
-
* (`null` clears it), one it omits or leaves `undefined` is kept, so a field
|
|
852
|
-
* cleared by omission must arrive as `null`. A non-object answer is returned
|
|
853
|
-
* as it came. An answer for a replaced or ended session leaves the current one
|
|
854
|
-
* untouched.
|
|
855
|
-
*/
|
|
754
|
+
/** Updates the current user's profile. */
|
|
856
755
|
updateProfile(data: Partial<T>): Promise<T>
|
|
857
756
|
|
|
858
|
-
/**
|
|
859
|
-
* Changes the current user's password.
|
|
860
|
-
*/
|
|
757
|
+
/** Changes the current user's password. */
|
|
861
758
|
changePassword(oldPassword: string, newPassword: string): Promise<void>
|
|
862
759
|
|
|
863
|
-
/**
|
|
864
|
-
* Initializes auth state (e.g., from stored tokens).
|
|
865
|
-
*
|
|
866
|
-
* LAZY and IDEMPOTENT: the first read of the state ({@link getState},
|
|
867
|
-
* {@link subscribe}, `useAuth()`) starts this same restore when nobody has called
|
|
868
|
-
* `initialize()`, and every later call shares the one promise instead of sending a
|
|
869
|
-
* second request. An app that keeps the template's explicit call behaves exactly as
|
|
870
|
-
* before; an app that loses that line still restores its session.
|
|
871
|
-
*
|
|
872
|
-
* With a stored token AND a stored user it marks the session `'authenticated'`
|
|
873
|
-
* SYNCHRONOUSLY — before its first `await` — so the first render is the
|
|
874
|
-
* signed-in UI and no page waits on a verification round trip. Any check
|
|
875
|
-
* (a refresh, the cookie-restore probe) settles behind that render, and only an
|
|
876
|
-
* explicit 401/403 ends the session; a network/5xx/404 failure keeps it and
|
|
877
|
-
* reports `'unavailable'`. With no stored user the client stays `'loading'`
|
|
878
|
-
* until the cookie restore decides.
|
|
879
|
-
*
|
|
880
|
-
* Do not put this call (or a `/me` request of your own) in front of the first
|
|
881
|
-
* render, and do not render a loading screen over a session it just restored:
|
|
882
|
-
* `whenRestored()` already resolves as soon as the restore has settled, and the
|
|
883
|
-
* HTTP gate built on it holds requests for exactly that window.
|
|
884
|
-
*/
|
|
760
|
+
/** Initializes auth state (e.g., from stored tokens). */
|
|
885
761
|
initialize(): Promise<void>
|
|
886
762
|
|
|
887
|
-
/**
|
|
888
|
-
|
|
889
|
-
|
|
763
|
+
/** Re-checks a restored session against the server, in the background — the boot-time validation an app with a stored token needs when a revoked or expired token… */
|
|
764
|
+
validateSession?(): Promise<void>
|
|
765
|
+
|
|
766
|
+
/** Resolves when the session restore started by `initialize()` has settled — the point after which the client knows whether this visitor is signed in. */
|
|
767
|
+
whenRestored?(): Promise<void>
|
|
768
|
+
|
|
769
|
+
/** Subscribes to auth state changes. */
|
|
770
|
+
subscribe(callback: (state: AuthState<T>) => void): () => void
|
|
771
|
+
|
|
772
|
+
/** Subscribes to auth state changes (alias for subscribe). */
|
|
773
|
+
onAuthChange(callback: (state: AuthState<T>) => void): () => void
|
|
774
|
+
|
|
775
|
+
/** Gets the current access token (alias for getAccessToken). */
|
|
776
|
+
getToken?(): string | null
|
|
777
|
+
|
|
778
|
+
/** Adds an auth event listener. */
|
|
779
|
+
addEventListener(listener: AuthEventListener<T>): () => void
|
|
780
|
+
|
|
781
|
+
/** Destroys the auth client. */
|
|
782
|
+
destroy(): void
|
|
783
|
+
}
|
|
890
784
|
```
|
|
891
785
|
|
|
786
|
+
_(Member comments are shortened to their first sentence to fit; the full declaration is in `node_modules/@molecule/app-auth/dist/types.d.ts`.)_
|
|
787
|
+
|
|
892
788
|
#### `StorageAdapter`
|
|
893
789
|
|
|
894
790
|
Minimal key-value storage adapter used by the auth client for
|
|
@@ -909,43 +805,24 @@ Every option {@link AuthClientConfig} accepts, typed by the profile.
|
|
|
909
805
|
Use `AuthClientConfig<T>` rather than this type: it adds the profile-shape
|
|
910
806
|
check. This interface exists so each member's documentation is addressable.
|
|
911
807
|
|
|
912
|
-
|
|
808
|
+
```typescript
|
|
913
809
|
interface AuthClientConfigOptions<T extends object = UserProfile> {
|
|
914
|
-
/**
|
|
915
|
-
* API base URL.
|
|
916
|
-
*/
|
|
810
|
+
/** API base URL. */
|
|
917
811
|
baseURL?: string
|
|
918
812
|
|
|
919
|
-
/**
|
|
920
|
-
* Login endpoint.
|
|
921
|
-
*/
|
|
813
|
+
/** Login endpoint. */
|
|
922
814
|
loginEndpoint?: string
|
|
923
815
|
|
|
924
|
-
/**
|
|
925
|
-
* Logout endpoint. Defaults to `/users/logout`. Set to `null` (or `''`) when
|
|
926
|
-
* the API has no logout route: {@link AuthClient.logout} skips the request
|
|
927
|
-
* and still clears the local session.
|
|
928
|
-
*/
|
|
816
|
+
/** Logout endpoint. */
|
|
929
817
|
logoutEndpoint?: string | null
|
|
930
818
|
|
|
931
|
-
/**
|
|
932
|
-
* Register endpoint.
|
|
933
|
-
*/
|
|
819
|
+
/** Register endpoint. */
|
|
934
820
|
registerEndpoint?: string
|
|
935
821
|
|
|
936
|
-
/**
|
|
937
|
-
* Refresh endpoint.
|
|
938
|
-
*/
|
|
822
|
+
/** Refresh endpoint. */
|
|
939
823
|
refreshEndpoint?: string
|
|
940
824
|
|
|
941
|
-
/**
|
|
942
|
-
* The write route used by `updateProfile`, independently of `currentUserEndpoint`.
|
|
943
|
-
* Defaults to `/auth/profile`. A resource route with an account id can resolve
|
|
944
|
-
* from the signed-in user, e.g. `(user) => '/accounts/' + encodeURIComponent(user.id)`.
|
|
945
|
-
* A profile with no `id` resolves from a field it does have, e.g.
|
|
946
|
-
* `(user) => '/people/' + encodeURIComponent(user.username)`.
|
|
947
|
-
* The resolver sees the current user before the patch, never caller-submitted identity.
|
|
948
|
-
*/
|
|
825
|
+
/** The write route used by `updateProfile`, independently of `currentUserEndpoint`. */
|
|
949
826
|
profileEndpoint?: string | ((user: Readonly<T>) => string)
|
|
950
827
|
|
|
951
828
|
/** HTTP method for `updateProfile()` (default `'PATCH'`; `'PUT'` for a `PUT /api/user` route). */
|
|
@@ -954,149 +831,63 @@ interface AuthClientConfigOptions<T extends object = UserProfile> {
|
|
|
954
831
|
/** Request-body adapter for `updateProfile()` — default the `Partial<T>` as given, e.g. `(data) => ({ user: data })`; see the module docs. */
|
|
955
832
|
profileRequestBody?: (data: Partial<T>) => unknown
|
|
956
833
|
|
|
957
|
-
/**
|
|
958
|
-
* The API route that returns the signed-in user (default `/users/me`). Called
|
|
959
|
-
* with credentials by `initialize()` to re-establish the session after a full
|
|
960
|
-
* page load when the token did not survive the reload itself — i.e. when
|
|
961
|
-
* `storage` is `'memory'` and the API sets an httpOnly session cookie — and by
|
|
962
|
-
* {@link AuthClient.validateSession} on every reload to confirm a stored token
|
|
963
|
-
* is still valid. Must return the user (as `user` or `props`).
|
|
964
|
-
*
|
|
965
|
-
* Set it whenever the app serves its own current-user route: a wrong value
|
|
966
|
-
* answers 404, which `validateSession` reports as `'unavailable'` and keeps the
|
|
967
|
-
* stored token instead of signing the user out — but the session then stays
|
|
968
|
-
* unconfirmed.
|
|
969
|
-
*/
|
|
834
|
+
/** The API route that returns the signed-in user (default `/users/me`). */
|
|
970
835
|
currentUserEndpoint?: string
|
|
971
836
|
|
|
972
|
-
/**
|
|
973
|
-
* When to probe {@link currentUserEndpoint} for an HttpOnly cookie session.
|
|
974
|
-
* The default `'hint'` probes only when the readable `mol_auth=1` cookie is
|
|
975
|
-
* present or this client already holds a cookie-session candidate. The API
|
|
976
|
-
* must set that presence hint alongside its HttpOnly cookie for automatic
|
|
977
|
-
* restoration after a full page load.
|
|
978
|
-
*
|
|
979
|
-
* Choose `'always'` for an external cookie API without that hint. Pair it with
|
|
980
|
-
* `storage: 'memory'` and the API's current-user endpoint; the request includes
|
|
981
|
-
* credentials. An anonymous load then makes a probe too. A local sign-out
|
|
982
|
-
* suppresses probes until a successful sign-in establishes another session.
|
|
983
|
-
* Network/5xx/404 failures leave a retryable candidate; 401/403 ends it.
|
|
984
|
-
*/
|
|
837
|
+
/** When to probe {@link currentUserEndpoint} for an HttpOnly cookie session. */
|
|
985
838
|
cookieRestore?: 'hint' | 'always'
|
|
986
839
|
|
|
987
|
-
/**
|
|
988
|
-
* Maximum duration of an auth request, including reading its response body, in
|
|
989
|
-
* milliseconds (default `10000`). Must be finite, positive, and below `2147483648`.
|
|
990
|
-
* A restore timeout settles as `'unavailable'` and preserves the session for retry,
|
|
991
|
-
* so initial-session render holds cannot wait forever on an unresponsive server.
|
|
992
|
-
*/
|
|
840
|
+
/** Maximum duration of an auth request, including reading its response body, in milliseconds (default `10000`). */
|
|
993
841
|
requestTimeout?: number
|
|
994
842
|
|
|
995
|
-
/**
|
|
996
|
-
* Storage key prefix.
|
|
997
|
-
*/
|
|
843
|
+
/** Storage key prefix. */
|
|
998
844
|
storagePrefix?: string
|
|
999
845
|
|
|
1000
|
-
/**
|
|
1001
|
-
* The EXACT `StorageAdapter` key the access token is written under (default
|
|
1002
|
-
* `${storagePrefix}accessToken`).
|
|
1003
|
-
*
|
|
1004
|
-
* Set it when something outside this package reads the token at its own key —
|
|
1005
|
-
* an e2e suite's contract (for example, `localStorage.getItem('app.jwt')`), a
|
|
1006
|
-
* second library, a host page — instead of mirroring the token into
|
|
1007
|
-
* `localStorage` beside the client. The refresh token and the cached user keep
|
|
1008
|
-
* the prefix.
|
|
1009
|
-
*
|
|
1010
|
-
* In a browser the client's storage default is {@link createBrowserStorageAdapter},
|
|
1011
|
-
* so `tokenKey` alone writes the token to exactly that key and a reload keeps
|
|
1012
|
-
* the session. An EXPLICIT `storage: 'memory'` beside it warns at construction:
|
|
1013
|
-
* the value then lives in a process-local Map nothing outside this client can
|
|
1014
|
-
* read, and a reload signs the user out.
|
|
1015
|
-
*/
|
|
846
|
+
/** The EXACT `StorageAdapter` key the access token is written under (default `${storagePrefix}accessToken`). */
|
|
1016
847
|
tokenKey?: string
|
|
1017
848
|
|
|
1018
|
-
/**
|
|
1019
|
-
* Scheme of the `Authorization` header the access token is sent with:
|
|
1020
|
-
* `Bearer` (default), or e.g. `Token` for an API that reads
|
|
1021
|
-
* `Authorization: Token <jwt>`.
|
|
1022
|
-
*/
|
|
849
|
+
/** Scheme of the `Authorization` header the access token is sent with: `Bearer` (default), or e.g. `Token` for an API that reads `Authorization: Token <jwt>`. */
|
|
1023
850
|
authScheme?: string
|
|
1024
851
|
|
|
1025
|
-
/**
|
|
1026
|
-
* Shape the login/register request body for an API that expects another
|
|
1027
|
-
* envelope (default: the payload as given). `register()` hands this callback
|
|
1028
|
-
* exactly what the caller passed — including the app's own fields, typed
|
|
1029
|
-
* through `createJWTAuthClient<UserProfile, { username: string }>` — so the
|
|
1030
|
-
* body can be sent as-is under the envelope the API wants.
|
|
1031
|
-
*
|
|
1032
|
-
* A nested-account API, for example, nests the payload under `user` and registers
|
|
1033
|
-
* with `{ user: { username, email, password } }`:
|
|
1034
|
-
*
|
|
1035
|
-
* ```typescript
|
|
1036
|
-
* const client = createJWTAuthClient<UserProfile, { username: string }>({
|
|
1037
|
-
* requestBody: (kind, payload) => ({ user: payload }),
|
|
1038
|
-
* // …
|
|
1039
|
-
* })
|
|
1040
|
-
* await client.register({ username, email, password })
|
|
1041
|
-
* ```
|
|
1042
|
-
*
|
|
1043
|
-
* A caller that keeps the untyped payload can map the base fields instead:
|
|
1044
|
-
* `(kind, p) => ({ user: { username: p.name, email: p.email, password: p.password } })`
|
|
1045
|
-
* with `register({ email, password, name: username })`.
|
|
1046
|
-
*
|
|
1047
|
-
* @param kind - Which request.
|
|
1048
|
-
* @param payload - The credentials or registration data.
|
|
1049
|
-
* @returns The JSON body to send.
|
|
1050
|
-
*/
|
|
852
|
+
/** Shape the login/register request body for an API that expects another envelope (default: the payload as given). */
|
|
1051
853
|
requestBody?: (kind: 'login' | 'register', payload: Record<string, unknown>) => unknown
|
|
1052
854
|
|
|
1053
|
-
/**
|
|
1054
|
-
* Turn an answer that carries a user into the client's
|
|
1055
|
-
* `{ accessToken, refreshToken?, user }` (default: the answer as given).
|
|
1056
|
-
* For a nested user answer, keep the wire bearer OUT of the profile, because
|
|
1057
|
-
* the client caches `user` in Web Storage where a token copy is readable by
|
|
1058
|
-
* any script on the origin:
|
|
1059
|
-
*
|
|
1060
|
-
* ```typescript
|
|
1061
|
-
* (data) => {
|
|
1062
|
-
* const { token, ...user } = data.user
|
|
1063
|
-
* return { accessToken: token, user }
|
|
1064
|
-
* }
|
|
1065
|
-
* ```
|
|
1066
|
-
*
|
|
1067
|
-
* It is the ONE user-shape adapter, and the client applies it on EVERY path
|
|
1068
|
-
* that adopts a user from an API answer — `login()`, `register()`,
|
|
1069
|
-
* `validateSession()`, the cookie restore `initialize()`/`refresh()` run, the
|
|
1070
|
-
* token-refresh answer, and `updateProfile()`. Those answers carry the raw wire
|
|
1071
|
-
* user (a current-user route typically answers a bare `{ user }`), so applying
|
|
1072
|
-
* the same mapping is what keeps the adapted profile after a reload instead of
|
|
1073
|
-
* replacing it with the unadapted one. A mapper that
|
|
1074
|
-
* cannot read a given answer (it throws, or returns no `user`) leaves that
|
|
1075
|
-
* answer's user as it came.
|
|
1076
|
-
*
|
|
1077
|
-
* It is NOT applied to `setSession()` or `setUser()`: those receive a profile
|
|
1078
|
-
* the caller already built in this client's shape, not a wire answer.
|
|
1079
|
-
*
|
|
1080
|
-
* The return type does NOT infer the profile: an untyped
|
|
1081
|
-
* `createJWTAuthClient({ mapAuthResponse })` stays `UserProfile`, so a mapper
|
|
1082
|
-
* that returns a no-id profile fails to compile there instead of silently
|
|
1083
|
-
* replacing the default with whatever the mapper happened to return. The same
|
|
1084
|
-
* rule holds for every config member below (`profileEndpoint`,
|
|
1085
|
-
* `profileRequestBody`, `shouldRestoreUser`): none of them supplies the
|
|
1086
|
-
* profile. Name the profile at the call
|
|
1087
|
-
* (`createJWTAuthClient<MyUser>({ mapAuthResponse })`) to use a custom one.
|
|
1088
|
-
*
|
|
1089
|
-
* @param data - The parsed response body.
|
|
1090
|
-
* @returns The auth result.
|
|
1091
|
-
*/
|
|
855
|
+
/** Turn an answer that carries a user into the client's `{ accessToken, refreshToken?, user }` (default: the answer as given). */
|
|
1092
856
|
mapAuthResponse?: (data: unknown) => AuthResult<NoInfer<T>> | AuthChallenge
|
|
1093
857
|
|
|
1094
|
-
/**
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
858
|
+
/** Where the access/refresh token and the cached user are kept — the field that decides whether a page reload keeps the user signed in. **In a browser the default… */
|
|
859
|
+
storage?: 'memory' | StorageAdapter
|
|
860
|
+
|
|
861
|
+
/** Decide whether a user returned by the cookie-restore probe (`currentUserEndpoint`) constitutes a signed-in CLIENT session. */
|
|
862
|
+
shouldRestoreUser?: (user: T) => boolean
|
|
863
|
+
|
|
864
|
+
/** Refresh the access token before it expires. */
|
|
865
|
+
autoRefresh?: boolean
|
|
866
|
+
|
|
867
|
+
/** Refresh tokens this many seconds before expiry. */
|
|
868
|
+
refreshBuffer?: number
|
|
869
|
+
|
|
870
|
+
/** Forgot password / password reset request endpoint. */
|
|
871
|
+
forgotPasswordEndpoint?: string
|
|
872
|
+
|
|
873
|
+
/** Password reset confirmation endpoint. */
|
|
874
|
+
resetPasswordEndpoint?: string
|
|
875
|
+
|
|
876
|
+
/** Change password endpoint. */
|
|
877
|
+
changePasswordEndpoint?: string | ((user: Readonly<T>) => string)
|
|
878
|
+
|
|
879
|
+
/** HTTP method for `changePassword()` (default `'POST'`). */
|
|
880
|
+
changePasswordMethod?: 'POST' | 'PATCH'
|
|
881
|
+
|
|
882
|
+
/** Available OAuth providers (e.g., ['github', 'google', 'gitlab']). */
|
|
883
|
+
oauthProviders?: string[]
|
|
884
|
+
|
|
885
|
+
/** OAuth endpoint path (default: '/oauth'). */
|
|
886
|
+
oauthEndpoint?: string
|
|
887
|
+
}
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
_(Member comments are shortened to their first sentence to fit; the full declaration is in `node_modules/@molecule/app-auth/dist/types.d.ts`.)_
|
|
1100
891
|
|
|
1101
892
|
#### `TokenStorage`
|
|
1102
893
|
|
|
@@ -1385,8 +1176,30 @@ rejected; see {@link AuthProfileShape} for the persistence contract behind
|
|
|
1385
1176
|
that list.
|
|
1386
1177
|
|
|
1387
1178
|
The type argument is NEVER inferred from the config: not from
|
|
1388
|
-
|
|
1389
|
-
|
|
1179
|
+
`mapAuthResponse`, `profileEndpoint`, `profileRequestBody` or
|
|
1180
|
+
`shouldRestoreUser`, and not from a config variable the caller annotated as
|
|
1181
|
+
`AuthClientConfig<MyUser>`. An untyped call stays typed `UserProfile`, so the
|
|
1182
|
+
default configuration keeps requiring `id: string`. Name the profile at the
|
|
1183
|
+
call (`createJWTAuthClient<MyUser>(config)`) — which also checks this config
|
|
1184
|
+
against `MyUser`.
|
|
1185
|
+
|
|
1186
|
+
**Callers upgrading from 1.8 — three shapes now need the profile at the call.**
|
|
1187
|
+
Each was accepted by 1.8 and is a type error here; each has a one-line fix:
|
|
1188
|
+
|
|
1189
|
+
1. A mapper that supplied the profile
|
|
1190
|
+
(`createJWTAuthClient({ mapAuthResponse: (d): AuthResult<MyUser> => … })`)
|
|
1191
|
+
is now typed `UserProfile`. Name it on the factory:
|
|
1192
|
+
`createJWTAuthClient<MyUser>({ mapAuthResponse })`.
|
|
1193
|
+
2. A config variable annotated `AuthClientConfig<MyUser>` with a profile that
|
|
1194
|
+
extends `UserProfile` used to supply the type argument. Pass it:
|
|
1195
|
+
`createJWTAuthClient<MyUser>(config)`. (A no-id profile was already
|
|
1196
|
+
rejected by 1.8's `T extends UserProfile`.)
|
|
1197
|
+
3. Generic forwarding written `function make<T extends UserProfile>(config: AuthClientConfig<T>)`
|
|
1198
|
+
no longer satisfies the factory's constraint. Copy the named constraint:
|
|
1199
|
+
`function make<T extends AuthProfileConstraint<T>>(config: AuthClientConfig<T>)`.
|
|
1200
|
+
|
|
1201
|
+
`any` is accepted at every entrypoint (`createJWTAuthClient<any>()`,
|
|
1202
|
+
`getClient<any>()`), so a caller that deliberately opted out keeps compiling.
|
|
1390
1203
|
|
|
1391
1204
|
```typescript
|
|
1392
1205
|
type AuthClientConfig<T extends object = UserProfile> = AuthClientConfigOptions<T> &
|
|
@@ -1426,10 +1239,10 @@ reload out of the box; each of these replaces that default:
|
|
|
1426
1239
|
|
|
1427
1240
|
```ts
|
|
1428
1241
|
storage: 'memory' // memory only: pair it with an httpOnly cookie session
|
|
1242
|
+
storage: createBrowserStorageAdapter({ kind: 'session' }) // survives a reload, dies with the tab
|
|
1243
|
+
storage: { getItem: (k) => …, setItem: (k, v) => …, removeItem: (k) => … } // your own
|
|
1429
1244
|
```
|
|
1430
1245
|
|
|
1431
|
-
_(description truncated — read the full text in `node_modules/@molecule/app-auth/dist/browser-storage.d.ts`.)_
|
|
1432
|
-
|
|
1433
1246
|
```typescript
|
|
1434
1247
|
function createBrowserStorageAdapter(options?: BrowserStorageAdapterOptions): StorageAdapter
|
|
1435
1248
|
```
|