mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> <!-- RECURSEML_SUMMARY:START --> ## High-level PR Summary This PR adds a developer-friendly error guard to prevent a common mistake where developers try to destructure the user object from `getUser()` or `useUser()` calls (e.g., `const { user } = await app.getUser()`), when the method already returns the user object directly. The fix implements a property getter that throws a helpful error message, attaches it to all user objects (client-side `CurrentUser`, server-side `ServerUser`, and `ServerTeamUser`), and includes E2E tests to verify the behavior. Additionally, it fixes a typo in the Convex example and addresses seed script execution issues. ⏱️ Estimated Review Time: 5-15 minutes <details> <summary>💡 Review Order Suggestion</summary> | Order | File Path | |-------|-----------| | 1 | `packages/template/src/lib/stack-app/users/index.ts` | | 2 | `packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts` | | 3 | `packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts` | | 4 | `apps/e2e/tests/js/app.test.ts` | | 5 | `examples/convex/convex/myFunctions.ts` | | 6 | `apps/backend/prisma/seed.ts` | | 7 | `.github/workflows/check-prisma-migrations.yaml` | </details> <details> <summary>⚠️ Inconsistent Changes Detected</summary> | File Path | Warning | |-----------|---------| | `.github/workflows/check-prisma-migrations.yaml` | Adds database cleanup step for auto-migration metadata in CI workflow, which appears unrelated to the main purpose of adding a user getter error guard | | `apps/backend/prisma/seed.ts` | Restructures the seed script execution logic and removes the main module check handler, which seems unrelated to the user getter error fix | </details> [](https://discord.gg/n3SsVDAW6U) [ <!-- RECURSEML_SUMMARY:END --> <!-- ELLIPSIS_HIDDEN --> ---- > [!IMPORTANT] > Adds a guard to prevent destructuring of `user` objects and includes a test to verify the error message. > > - **Behavior**: > - Adds `attachUserDestructureGuard` function in `users/index.ts` to throw an error when attempting to destructure `user`. > - Updates `getUser()` in `client-app-impl.ts` and `server-app-impl.ts` to use `attachUserDestructureGuard`. > - Adds test in `app.test.ts` to verify error message when destructuring `user`. > - **Misc**: > - Fixes string concatenation error in `myFunctions.ts`. > - Adds step in `check-prisma-migrations.yaml` to remove auto-migration metadata. > > <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup> for9be5c8a0e2. You can [customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this summary. It will automatically update as commits are pushed.</sup> <!-- ELLIPSIS_HIDDEN --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Added a protective guard that prevents destructuring the user from current user objects on both client and server, displaying a clear, actionable error with guidance to use supported access methods. Valid usage is unaffected; no API changes. * **Tests** * Introduced end-to-end tests verifying the new error message and behavior across sign-up, sign-in, and current user retrieval on client and server. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
477 lines
18 KiB
TypeScript
477 lines
18 KiB
TypeScript
import { KnownErrors } from "@stackframe/stack-shared";
|
|
import { CurrentUserCrud } from "@stackframe/stack-shared/dist/interface/crud/current-user";
|
|
import { UsersCrud } from "@stackframe/stack-shared/dist/interface/crud/users";
|
|
import { InternalSession } from "@stackframe/stack-shared/dist/sessions";
|
|
import { encodeBase64 } from "@stackframe/stack-shared/dist/utils/bytes";
|
|
import { GeoInfo } from "@stackframe/stack-shared/dist/utils/geo";
|
|
import { ReadonlyJson } from "@stackframe/stack-shared/dist/utils/json";
|
|
import { ProviderType } from "@stackframe/stack-shared/dist/utils/oauth";
|
|
import { Result } from "@stackframe/stack-shared/dist/utils/results";
|
|
import { ApiKeyCreationOptions, UserApiKey, UserApiKeyFirstView } from "../api-keys";
|
|
import { AsyncStoreProperty } from "../common";
|
|
import { OAuthConnection } from "../connected-accounts";
|
|
import { ContactChannel, ContactChannelCreateOptions, ServerContactChannel, ServerContactChannelCreateOptions } from "../contact-channels";
|
|
import { Customer } from "../customers";
|
|
import { NotificationCategory } from "../notification-categories";
|
|
import { AdminTeamPermission, TeamPermission } from "../permissions";
|
|
import { AdminOwnedProject, AdminProjectCreateOptions } from "../projects";
|
|
import { EditableTeamMemberProfile, ServerTeam, ServerTeamCreateOptions, Team, TeamCreateOptions } from "../teams";
|
|
|
|
const userGetterErrorMessage = "Stack Auth: useUser() already returns the user object. Use `const user = useUser()` (or `const user = await app.getUser()`) instead of destructuring it like `const { user } = ...`.";
|
|
|
|
export function attachUserDestructureGuard(target: object): void {
|
|
const descriptor = Object.getOwnPropertyDescriptor(target, "user");
|
|
if (descriptor?.get === guardGetter) {
|
|
return;
|
|
}
|
|
|
|
Object.defineProperty(target, "user", {
|
|
get: guardGetter,
|
|
configurable: false,
|
|
enumerable: false,
|
|
});
|
|
}
|
|
|
|
function guardGetter(): never {
|
|
throw new Error(userGetterErrorMessage);
|
|
}
|
|
|
|
export type OAuthProvider = {
|
|
readonly id: string,
|
|
readonly type: string,
|
|
readonly userId: string,
|
|
readonly accountId?: string,
|
|
readonly email?: string,
|
|
readonly allowSignIn: boolean,
|
|
readonly allowConnectedAccounts: boolean,
|
|
update(data: { allowSignIn?: boolean, allowConnectedAccounts?: boolean }): Promise<Result<void,
|
|
InstanceType<typeof KnownErrors.OAuthProviderAccountIdAlreadyUsedForSignIn>
|
|
>>,
|
|
delete(): Promise<void>,
|
|
};
|
|
|
|
export type ServerOAuthProvider = {
|
|
readonly id: string,
|
|
readonly type: string,
|
|
readonly userId: string,
|
|
readonly accountId: string,
|
|
readonly email?: string,
|
|
readonly allowSignIn: boolean,
|
|
readonly allowConnectedAccounts: boolean,
|
|
update(data: { accountId?: string, email?: string, allowSignIn?: boolean, allowConnectedAccounts?: boolean }): Promise<Result<void,
|
|
InstanceType<typeof KnownErrors.OAuthProviderAccountIdAlreadyUsedForSignIn>
|
|
>>,
|
|
delete(): Promise<void>,
|
|
};
|
|
|
|
|
|
export type Session = {
|
|
getTokens(): Promise<{ accessToken: string | null, refreshToken: string | null }>,
|
|
};
|
|
|
|
/**
|
|
* Contains everything related to the current user session.
|
|
*/
|
|
export type Auth = {
|
|
readonly _internalSession: InternalSession,
|
|
readonly currentSession: Session,
|
|
signOut(options?: { redirectUrl?: URL | string }): Promise<void>,
|
|
|
|
/**
|
|
* Returns headers for sending authenticated HTTP requests to external servers. Most commonly used in cross-origin
|
|
* requests. Similar to `getAuthJson`, but specifically for HTTP requests.
|
|
*
|
|
* If you are using `tokenStore: "cookie"`, you don't need this for same-origin requests. However, most
|
|
* browsers now disable third-party cookies by default, so we must pass authentication tokens by header instead
|
|
* if the client and server are on different origins.
|
|
*
|
|
* This function returns a header object that can be used with `fetch` or other HTTP request libraries to send
|
|
* authenticated requests.
|
|
*
|
|
* On the server, you can then pass in the `Request` object to the `tokenStore` option
|
|
* of your Stack app. Please note that CORS does not allow most headers by default, so you
|
|
* must include `x-stack-auth` in the [`Access-Control-Allow-Headers` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers)
|
|
* of the CORS preflight response.
|
|
*
|
|
* If you are not using HTTP (and hence cannot set headers), you will need to use the `getAuthJson()` function
|
|
* instead.
|
|
*
|
|
* Example:
|
|
*
|
|
* ```ts
|
|
* // client
|
|
* const res = await fetch("https://api.example.com", {
|
|
* headers: {
|
|
* ...await stackApp.getAuthHeaders()
|
|
* // you can also add your own headers here
|
|
* },
|
|
* });
|
|
*
|
|
* // server
|
|
* function handleRequest(req: Request) {
|
|
* const user = await stackServerApp.getUser({ tokenStore: req });
|
|
* return new Response("Welcome, " + user.displayName);
|
|
* }
|
|
* ```
|
|
*/
|
|
getAuthHeaders(): Promise<{ "x-stack-auth": string }>,
|
|
|
|
/**
|
|
* Creates a JSON-serializable object containing the information to authenticate a user on an external server.
|
|
* Similar to `getAuthHeaders`, but returns an object that can be sent over any protocol instead of just
|
|
* HTTP headers.
|
|
*
|
|
* While `getAuthHeaders` is the recommended way to send authentication tokens over HTTP, your app may use
|
|
* a different protocol, for example WebSockets or gRPC. This function returns a token object that can be JSON-serialized and sent to the server in any way you like.
|
|
*
|
|
* On the server, you can pass in this token object into the `tokenStore` option to fetch user details.
|
|
*
|
|
* Example:
|
|
*
|
|
* ```ts
|
|
* // client
|
|
* const res = await rpcCall(rpcEndpoint, {
|
|
* data: {
|
|
* auth: await stackApp.getAuthJson(),
|
|
* },
|
|
* });
|
|
*
|
|
* // server
|
|
* function handleRequest(data) {
|
|
* const user = await stackServerApp.getUser({ tokenStore: data.auth });
|
|
* return new Response("Welcome, " + user.displayName);
|
|
* }
|
|
* ```
|
|
*/
|
|
getAuthJson(): Promise<{ accessToken: string | null, refreshToken: string | null }>,
|
|
registerPasskey(options?: { hostname?: string }): Promise<Result<undefined, KnownErrors["PasskeyRegistrationFailed"] | KnownErrors["PasskeyWebAuthnError"]>>,
|
|
};
|
|
|
|
/**
|
|
* ```
|
|
* +----------+-------------+-------------------+
|
|
* | \ | !Server | Server |
|
|
* +----------+-------------+-------------------+
|
|
* | !Session | User | ServerUser |
|
|
* | Session | CurrentUser | CurrentServerUser |
|
|
* +----------+-------------+-------------------+
|
|
* ```
|
|
*
|
|
* The fields on each of these types are available iff:
|
|
* BaseUser: true
|
|
* Auth: Session
|
|
* ServerBaseUser: Server
|
|
* UserExtra: Session OR Server
|
|
*
|
|
* The types are defined as follows (in the typescript manner):
|
|
* User = BaseUser
|
|
* CurrentUser = BaseUser & Auth & UserExtra
|
|
* ServerUser = BaseUser & ServerBaseUser & UserExtra
|
|
* CurrentServerUser = BaseUser & ServerBaseUser & Auth & UserExtra
|
|
**/
|
|
|
|
export type BaseUser = {
|
|
readonly id: string,
|
|
|
|
readonly displayName: string | null,
|
|
|
|
/**
|
|
* The user's email address.
|
|
*
|
|
* Note: This might NOT be unique across multiple users, so always use `id` for unique identification.
|
|
*/
|
|
readonly primaryEmail: string | null,
|
|
readonly primaryEmailVerified: boolean,
|
|
readonly profileImageUrl: string | null,
|
|
|
|
readonly signedUpAt: Date,
|
|
|
|
readonly clientMetadata: any,
|
|
readonly clientReadOnlyMetadata: any,
|
|
|
|
/**
|
|
* Whether the user has a password set.
|
|
*/
|
|
readonly hasPassword: boolean,
|
|
readonly otpAuthEnabled: boolean,
|
|
readonly passkeyAuthEnabled: boolean,
|
|
|
|
readonly isMultiFactorRequired: boolean,
|
|
readonly isAnonymous: boolean,
|
|
toClientJson(): CurrentUserCrud["Client"]["Read"],
|
|
|
|
/**
|
|
* @deprecated, use contact channel's usedForAuth instead
|
|
*/
|
|
readonly emailAuthEnabled: boolean,
|
|
/**
|
|
* @deprecated
|
|
*/
|
|
readonly oauthProviders: readonly { id: string }[],
|
|
}
|
|
|
|
export type UserExtra = {
|
|
setDisplayName(displayName: string): Promise<void>,
|
|
/** @deprecated Use contact channel's sendVerificationEmail instead */
|
|
sendVerificationEmail(): Promise<KnownErrors["EmailAlreadyVerified"] | void>,
|
|
setClientMetadata(metadata: any): Promise<void>,
|
|
updatePassword(options: { oldPassword: string, newPassword: string}): Promise<KnownErrors["PasswordConfirmationMismatch"] | KnownErrors["PasswordRequirementsNotMet"] | void>,
|
|
setPassword(options: { password: string }): Promise<KnownErrors["PasswordRequirementsNotMet"] | void>,
|
|
|
|
/**
|
|
* A shorthand method to update multiple fields of the user at once.
|
|
*/
|
|
update(update: UserUpdateOptions): Promise<void>,
|
|
|
|
useContactChannels(): ContactChannel[], // THIS_LINE_PLATFORM react-like
|
|
listContactChannels(): Promise<ContactChannel[]>,
|
|
createContactChannel(data: ContactChannelCreateOptions): Promise<ContactChannel>,
|
|
|
|
useNotificationCategories(): NotificationCategory[], // THIS_LINE_PLATFORM react-like
|
|
listNotificationCategories(): Promise<NotificationCategory[]>,
|
|
|
|
delete(): Promise<void>,
|
|
|
|
getConnectedAccount(id: ProviderType, options: { or: 'redirect', scopes?: string[] }): Promise<OAuthConnection>,
|
|
getConnectedAccount(id: ProviderType, options?: { or?: 'redirect' | 'throw' | 'return-null', scopes?: string[] }): Promise<OAuthConnection | null>,
|
|
|
|
// IF_PLATFORM react-like
|
|
useConnectedAccount(id: ProviderType, options: { or: 'redirect', scopes?: string[] }): OAuthConnection,
|
|
useConnectedAccount(id: ProviderType, options?: { or?: 'redirect' | 'throw' | 'return-null', scopes?: string[] }): OAuthConnection | null,
|
|
// END_PLATFORM
|
|
|
|
hasPermission(scope: Team, permissionId: string): Promise<boolean>,
|
|
hasPermission(permissionId: string): Promise<boolean>,
|
|
|
|
getPermission(scope: Team, permissionId: string): Promise<TeamPermission | null>,
|
|
getPermission(permissionId: string): Promise<TeamPermission | null>,
|
|
|
|
listPermissions(scope: Team, options?: { recursive?: boolean }): Promise<TeamPermission[]>,
|
|
listPermissions(options?: { recursive?: boolean }): Promise<TeamPermission[]>,
|
|
|
|
// IF_PLATFORM react-like
|
|
usePermissions(scope: Team, options?: { recursive?: boolean }): TeamPermission[],
|
|
usePermissions(options?: { recursive?: boolean }): TeamPermission[],
|
|
|
|
usePermission(scope: Team, permissionId: string): TeamPermission | null,
|
|
usePermission(permissionId: string): TeamPermission | null,
|
|
// END_PLATFORM
|
|
|
|
readonly selectedTeam: Team | null,
|
|
setSelectedTeam(team: Team | null): Promise<void>,
|
|
createTeam(data: TeamCreateOptions): Promise<Team>,
|
|
leaveTeam(team: Team): Promise<void>,
|
|
|
|
getActiveSessions(): Promise<ActiveSession[]>,
|
|
revokeSession(sessionId: string): Promise<void>,
|
|
getTeamProfile(team: Team): Promise<EditableTeamMemberProfile>,
|
|
useTeamProfile(team: Team): EditableTeamMemberProfile, // THIS_LINE_PLATFORM react-like
|
|
|
|
createApiKey(options: ApiKeyCreationOptions<"user">): Promise<UserApiKeyFirstView>,
|
|
|
|
useOAuthProviders(): OAuthProvider[], // THIS_LINE_PLATFORM react-like
|
|
listOAuthProviders(): Promise<OAuthProvider[]>,
|
|
|
|
useOAuthProvider(id: string): OAuthProvider | null, // THIS_LINE_PLATFORM react-like
|
|
getOAuthProvider(id: string): Promise<OAuthProvider | null>,
|
|
}
|
|
& AsyncStoreProperty<"apiKeys", [], UserApiKey[], true>
|
|
& AsyncStoreProperty<"team", [id: string], Team | null, false>
|
|
& AsyncStoreProperty<"teams", [], Team[], true>
|
|
& AsyncStoreProperty<"permission", [scope: Team, permissionId: string, options?: { recursive?: boolean }], TeamPermission | null, false>
|
|
& AsyncStoreProperty<"permissions", [scope: Team, options?: { recursive?: boolean }], TeamPermission[], true>;
|
|
|
|
export type InternalUserExtra =
|
|
& {
|
|
createProject(newProject: AdminProjectCreateOptions): Promise<AdminOwnedProject>,
|
|
transferProject(projectIdToTransfer: string, newTeamId: string): Promise<void>,
|
|
}
|
|
& AsyncStoreProperty<"ownedProjects", [], AdminOwnedProject[], true>
|
|
|
|
export type User = BaseUser;
|
|
|
|
export type CurrentUser = BaseUser & Auth & UserExtra & Customer;
|
|
|
|
export type CurrentInternalUser = CurrentUser & InternalUserExtra;
|
|
|
|
export type ProjectCurrentUser<ProjectId> = ProjectId extends "internal" ? CurrentInternalUser : CurrentUser;
|
|
|
|
export type TokenPartialUser = Pick<
|
|
User,
|
|
| "id"
|
|
| "displayName"
|
|
| "primaryEmail"
|
|
| "primaryEmailVerified"
|
|
| "isAnonymous"
|
|
>
|
|
|
|
export type SyncedPartialUser = TokenPartialUser & Pick<
|
|
User,
|
|
| "id"
|
|
| "displayName"
|
|
| "primaryEmail"
|
|
| "primaryEmailVerified"
|
|
| "profileImageUrl"
|
|
| "signedUpAt"
|
|
| "clientMetadata"
|
|
| "clientReadOnlyMetadata"
|
|
| "isAnonymous"
|
|
| "hasPassword"
|
|
>;
|
|
|
|
|
|
export type ActiveSession = {
|
|
id: string,
|
|
userId: string,
|
|
createdAt: Date,
|
|
isImpersonation: boolean,
|
|
lastUsedAt: Date | undefined,
|
|
isCurrentSession: boolean,
|
|
geoInfo?: GeoInfo,
|
|
};
|
|
|
|
export type UserUpdateOptions = {
|
|
displayName?: string,
|
|
clientMetadata?: ReadonlyJson,
|
|
selectedTeamId?: string | null,
|
|
totpMultiFactorSecret?: Uint8Array | null,
|
|
profileImageUrl?: string | null,
|
|
otpAuthEnabled?: boolean,
|
|
passkeyAuthEnabled?:boolean,
|
|
}
|
|
export function userUpdateOptionsToCrud(options: UserUpdateOptions): CurrentUserCrud["Client"]["Update"] {
|
|
return {
|
|
display_name: options.displayName,
|
|
client_metadata: options.clientMetadata,
|
|
selected_team_id: options.selectedTeamId,
|
|
totp_secret_base64: options.totpMultiFactorSecret != null ? encodeBase64(options.totpMultiFactorSecret) : options.totpMultiFactorSecret,
|
|
profile_image_url: options.profileImageUrl,
|
|
otp_auth_enabled: options.otpAuthEnabled,
|
|
passkey_auth_enabled: options.passkeyAuthEnabled,
|
|
};
|
|
}
|
|
|
|
|
|
export type ServerBaseUser = {
|
|
setPrimaryEmail(email: string | null, options?: { verified?: boolean | undefined }): Promise<void>,
|
|
|
|
readonly lastActiveAt: Date,
|
|
|
|
readonly serverMetadata: any,
|
|
setServerMetadata(metadata: any): Promise<void>,
|
|
setClientReadOnlyMetadata(metadata: any): Promise<void>,
|
|
|
|
createTeam(data: Omit<ServerTeamCreateOptions, "creatorUserId">): Promise<ServerTeam>,
|
|
|
|
useContactChannels(): ServerContactChannel[], // THIS_LINE_PLATFORM react-like
|
|
listContactChannels(): Promise<ServerContactChannel[]>,
|
|
createContactChannel(data: ServerContactChannelCreateOptions): Promise<ServerContactChannel>,
|
|
|
|
update(user: ServerUserUpdateOptions): Promise<void>,
|
|
|
|
grantPermission(scope: Team, permissionId: string): Promise<void>,
|
|
grantPermission(permissionId: string): Promise<void>,
|
|
|
|
revokePermission(scope: Team, permissionId: string): Promise<void>,
|
|
revokePermission(permissionId: string): Promise<void>,
|
|
|
|
getPermission(scope: Team, permissionId: string): Promise<TeamPermission | null>,
|
|
getPermission(permissionId: string): Promise<TeamPermission | null>,
|
|
|
|
hasPermission(scope: Team, permissionId: string): Promise<boolean>,
|
|
hasPermission(permissionId: string): Promise<boolean>,
|
|
|
|
listPermissions(scope: Team, options?: { recursive?: boolean }): Promise<TeamPermission[]>,
|
|
listPermissions(options?: { recursive?: boolean }): Promise<TeamPermission[]>,
|
|
|
|
// IF_PLATFORM react-like
|
|
usePermissions(scope: Team, options?: { recursive?: boolean }): TeamPermission[],
|
|
usePermissions(options?: { recursive?: boolean }): TeamPermission[],
|
|
|
|
usePermission(scope: Team, permissionId: string): TeamPermission | null,
|
|
usePermission(permissionId: string): TeamPermission | null,
|
|
// END_PLATFORM
|
|
|
|
useOAuthProviders(): ServerOAuthProvider[], // THIS_LINE_PLATFORM react-like
|
|
listOAuthProviders(): Promise<ServerOAuthProvider[]>,
|
|
|
|
useOAuthProvider(id: string): ServerOAuthProvider | null, // THIS_LINE_PLATFORM react-like
|
|
getOAuthProvider(id: string): Promise<ServerOAuthProvider | null>,
|
|
|
|
/**
|
|
* Creates a new session object with a refresh token for this user. Can be used to impersonate them.
|
|
*/
|
|
createSession(options?: { expiresInMillis?: number, isImpersonation?: boolean }): Promise<Session>,
|
|
}
|
|
& AsyncStoreProperty<"team", [id: string], ServerTeam | null, false>
|
|
& AsyncStoreProperty<"teams", [], ServerTeam[], true>
|
|
& AsyncStoreProperty<"permission", [scope: Team, permissionId: string, options?: { direct?: boolean }], AdminTeamPermission | null, false>
|
|
& AsyncStoreProperty<"permissions", [scope: Team, options?: { direct?: boolean }], AdminTeamPermission[], true>;
|
|
|
|
/**
|
|
* A user including sensitive fields that should only be used on the server, never sent to the client
|
|
* (such as sensitive information and serverMetadata).
|
|
*/
|
|
export type ServerUser = ServerBaseUser & BaseUser & UserExtra & Customer<true>;
|
|
|
|
export type CurrentServerUser = Auth & ServerUser;
|
|
|
|
export type CurrentInternalServerUser = CurrentServerUser & InternalUserExtra;
|
|
|
|
export type ProjectCurrentServerUser<ProjectId> = ProjectId extends "internal" ? CurrentInternalServerUser : CurrentServerUser;
|
|
|
|
export type SyncedPartialServerUser = SyncedPartialUser & Pick<
|
|
ServerUser,
|
|
| "serverMetadata"
|
|
>;
|
|
|
|
export type ServerUserUpdateOptions = {
|
|
primaryEmail?: string | null,
|
|
primaryEmailVerified?: boolean,
|
|
primaryEmailAuthEnabled?: boolean,
|
|
clientReadOnlyMetadata?: ReadonlyJson,
|
|
serverMetadata?: ReadonlyJson,
|
|
password?: string,
|
|
} & UserUpdateOptions;
|
|
export function serverUserUpdateOptionsToCrud(options: ServerUserUpdateOptions): CurrentUserCrud["Server"]["Update"] {
|
|
return {
|
|
display_name: options.displayName,
|
|
primary_email: options.primaryEmail,
|
|
client_metadata: options.clientMetadata,
|
|
client_read_only_metadata: options.clientReadOnlyMetadata,
|
|
server_metadata: options.serverMetadata,
|
|
selected_team_id: options.selectedTeamId,
|
|
primary_email_auth_enabled: options.primaryEmailAuthEnabled,
|
|
primary_email_verified: options.primaryEmailVerified,
|
|
password: options.password,
|
|
profile_image_url: options.profileImageUrl,
|
|
totp_secret_base64: options.totpMultiFactorSecret != null ? encodeBase64(options.totpMultiFactorSecret) : options.totpMultiFactorSecret,
|
|
};
|
|
}
|
|
|
|
|
|
export type ServerUserCreateOptions = {
|
|
primaryEmail?: string | null,
|
|
primaryEmailAuthEnabled?: boolean,
|
|
password?: string,
|
|
otpAuthEnabled?: boolean,
|
|
displayName?: string,
|
|
primaryEmailVerified?: boolean,
|
|
clientMetadata?: any,
|
|
clientReadOnlyMetadata?: any,
|
|
serverMetadata?: any,
|
|
}
|
|
export function serverUserCreateOptionsToCrud(options: ServerUserCreateOptions): UsersCrud["Server"]["Create"] {
|
|
return {
|
|
primary_email: options.primaryEmail,
|
|
password: options.password,
|
|
otp_auth_enabled: options.otpAuthEnabled,
|
|
primary_email_auth_enabled: options.primaryEmailAuthEnabled,
|
|
display_name: options.displayName,
|
|
primary_email_verified: options.primaryEmailVerified,
|
|
client_metadata: options.clientMetadata,
|
|
client_read_only_metadata: options.clientReadOnlyMetadata,
|
|
server_metadata: options.serverMetadata,
|
|
};
|
|
}
|