[GH-ISSUE #1036] auth.api.signUpEmail return value problem #17192

Closed
opened 2026-04-15 15:12:59 -05:00 by GiteaMirror · 9 comments
Owner

Originally created by @wh5938316 on GitHub (Dec 27, 2024).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/1036

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

In the latest version, I noticed that the return value of auth.api.signUpEmail is { token: null }, instead of returning the user object as it used to (if I remember correctly). This is causing issues for me, as I need the created user object for subsequent processing in my application.

  1. What is the purpose of this change?
  2. If signUpEmail no longer returns the user object, how should I retrieve the created user after calling signUpEmail?
const data = await betterAuth().api.signUpEmail({
    body: {
      email: _.get(data, 'email'),
      password: _.get(data, 'password'),
      name: _.get(data, 'name')!,
    },
});

In version 1.0.0:
8e81ceb0e2/packages/better-auth/src/api/routes/sign-up.ts (L215-L218)

Now:
2c81db93be/packages/better-auth/src/api/routes/sign-up.ts (L233-L235)

Current vs. Expected behavior

I would like clarification on the following:

  1. Was this change intentional as part of a design decision?
  2. If so, how should I obtain the user information after calling signUpEmail?

What version of Better Auth are you using?

1.1.3

Provide environment information

Nest.js

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
});

Additional context

No response

Originally created by @wh5938316 on GitHub (Dec 27, 2024). Original GitHub issue: https://github.com/better-auth/better-auth/issues/1036 ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce In the latest version, I noticed that the return value of `auth.api.signUpEmail` is `{ token: null }`, instead of returning the user object as it used to (if I remember correctly). This is causing issues for me, as I need the created user object for subsequent processing in my application. 1. What is the purpose of this change? 2. If signUpEmail no longer returns the user object, how should I retrieve the created user after calling signUpEmail? ```javascript const data = await betterAuth().api.signUpEmail({ body: { email: _.get(data, 'email'), password: _.get(data, 'password'), name: _.get(data, 'name')!, }, }); ``` In version 1.0.0: https://github.com/better-auth/better-auth/blob/8e81ceb0e2727e220a9119a6bccca2c01fc878c3/packages/better-auth/src/api/routes/sign-up.ts#L215-L218 Now: https://github.com/better-auth/better-auth/blob/2c81db93be61b7154fe37ec9fd709806366ca93b/packages/better-auth/src/api/routes/sign-up.ts#L233-L235 ### Current vs. Expected behavior I would like clarification on the following: 1. Was this change intentional as part of a design decision? 2. If so, how should I obtain the user information after calling signUpEmail? ### What version of Better Auth are you using? 1.1.3 ### Provide environment information ```bash Nest.js ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-15 15:12:59 -05:00
Author
Owner

@wh5938316 commented on GitHub (Dec 27, 2024):

@Bekacru hi

This update originates from PR #968, which states:

Endpoints that previously returned the entire user object now only return a token. This change allows for storing sensitive fields in the user table and eliminates unnecessary returned data. As a result, all endpoints, except /get-session, now enforce this.

What I don’t understand is that the purpose of this PR is to allow storing sensitive fields in the user table and eliminate unnecessary returned data. However, I believe returning the created user in api.signUpEmail is necessary, and specifying the returned fields already avoids the issue of exposing sensitive fields.

Interestingly, in the current version, it seems that api.signInEmail is an exception, as it returns the user information.

2c81db93be/packages/better-auth/src/api/routes/sign-in.ts (L433-L448)

<!-- gh-comment-id:2563449221 --> @wh5938316 commented on GitHub (Dec 27, 2024): @Bekacru hi This update originates from PR #968, which states: > Endpoints that previously returned the entire user object now only return a token. This change allows for storing sensitive fields in the user table and eliminates unnecessary returned data. As a result, all endpoints, except /get-session, now enforce this. What I don’t understand is that the purpose of this PR is to allow storing sensitive fields in the user table and eliminate unnecessary returned data. However, I believe returning the created user in `api.signUpEmail` is necessary, and specifying the returned fields already avoids the issue of exposing sensitive fields. Interestingly, in the current version, it seems that `api.signInEmail` is an exception, as it returns the user information. https://github.com/better-auth/better-auth/blob/2c81db93be61b7154fe37ec9fd709806366ca93b/packages/better-auth/src/api/routes/sign-in.ts#L433-L448
Author
Owner

@Bekacru commented on GitHub (Dec 27, 2024):

I'll admit that we should have kept the user id and other simpler metadata like we do for sign in endpoint (which is left by mistake) for use cases that rely on the auth.api method to post process data so we don't break prod applications with a minor bump. I initially didn’t expect people to use this approach often. But, the change is actually intended to encourage people to leverage the underlying hook system for these tasks, rather than being tempted to create additional endpoints for sign-in, sign-up, or updating user information from the client after they got the user object from the response

So something you can do is write an after hook to do post process after a user is signed up like this

const auth = betterAuth({
    hooks: {
        before: createAuthMiddleware( async(ctx) => {
             if(ctx.path === "/sign-up/email"){
                 const newSession = ctx.context.newSession; //this hold the full user and session object
             }
        })
    }
})
<!-- gh-comment-id:2563511210 --> @Bekacru commented on GitHub (Dec 27, 2024): I'll admit that we should have kept the user id and other simpler metadata like we do for sign in endpoint (which is left by mistake) for use cases that rely on the `auth.api` method to post process data so we don't break prod applications with a minor bump. I initially didn’t expect people to use this approach often. But, the change is actually intended to encourage people to leverage the underlying hook system for these tasks, rather than being tempted to create additional endpoints for sign-in, sign-up, or updating user information from the client after they got the user object from the response So something you can do is write an after hook to do post process after a user is signed up like this ```ts const auth = betterAuth({ hooks: { before: createAuthMiddleware( async(ctx) => { if(ctx.path === "/sign-up/email"){ const newSession = ctx.context.newSession; //this hold the full user and session object } }) } }) ```
Author
Owner

@wh5938316 commented on GitHub (Dec 27, 2024):

@Bekacru

This is one of my biggest concerns about better-auth. From my perspective, better-auth seems heavily tailored for meta-frameworks and often neglects backend frameworks like NestJS in its design.

I use NestJS, and I believe the community of developers using similar frameworks and requiring better-auth is vast (NestJS has over 4 million weekly downloads on npm). However, the experience of using better-auth with NestJS is not great—and it seems to be worsening over time, which is my biggest worry. While hooks can solve certain problems, they are not a great fit for use cases in NestJS.

I sincerely hope that better-auth considers this issue. I want better-auth to shine as an auth library across the entire JavaScript ecosystem, not just within meta-frameworks.

<!-- gh-comment-id:2563543182 --> @wh5938316 commented on GitHub (Dec 27, 2024): @Bekacru This is one of my biggest concerns about better-auth. From my perspective, better-auth seems heavily tailored for meta-frameworks and often neglects backend frameworks like NestJS in its design. I use NestJS, and I believe the community of developers using similar frameworks and requiring better-auth is vast (NestJS has over 4 million weekly downloads on npm). However, the experience of using better-auth with NestJS is not great—and it seems to be worsening over time, which is my biggest worry. While hooks can solve certain problems, they are not a great fit for use cases in NestJS. I sincerely hope that better-auth considers this issue. I want better-auth to shine as an auth library across the entire JavaScript ecosystem, not just within meta-frameworks.
Author
Owner

@Bekacru commented on GitHub (Dec 27, 2024):

valid concern. the thing is, Better Auth was designed to be used within a server and client scope, where you can call the Better Auth server using the provided client and that's mostly about it. The auth.api feature was mostly an afterthought so there is a way to provide essential actions that needs to be called on server like getSession without requiring to create a bunch of separated actions.

so now instead of exporting an action called getSession, we can reuse the same endpoint that the client uses to get the session so we have one getSession implementation. This also allowed us to extend this to all plugis, like the organization plugin now can expose getActiveOrganization or something, so that each plugin doesn’t also need to export separate server callable actions.

The auth.api.signUpEmail and other methods, which I didn't initially intended to be called in the server, were primarily an afterthought. That’s why they don’t provide as polished an API as the client.

That said, it turned out to be a happy accident that opened up a lot of additional use cases, including integration with frameworks like NestJS and other backend focused frameworks. So it's a fair callout and we should definitely keep this in mind when making future API decisions.

And I'll also review the singUpEmail and similar endpoints changes and we might revert it so we can still return some useful user info for post processing.

<!-- gh-comment-id:2563572221 --> @Bekacru commented on GitHub (Dec 27, 2024): valid concern. the thing is, Better Auth was designed to be used within a `server` and `client` scope, where you can call the Better Auth server using the provided `client` and that's mostly about it. The `auth.api` feature was mostly an afterthought so there is a way to provide essential actions that needs to be called on server like `getSession` without requiring to create a bunch of separated actions. so now instead of exporting an action called `getSession`, we can reuse the same endpoint that the client uses to get the session so we have one getSession implementation. This also allowed us to extend this to all plugis, like the organization plugin now can expose `getActiveOrganization` or something, so that each plugin doesn’t also need to export separate server callable actions. The `auth.api.signUpEmail` and other methods, which I didn't initially intended to be called in the server, were primarily an afterthought. That’s why they don’t provide as polished an API as the client. That said, it turned out to be a happy accident that opened up a lot of additional use cases, including integration with frameworks like NestJS and other backend focused frameworks. So it's a fair callout and we should definitely keep this in mind when making future API decisions. And I'll also review the singUpEmail and similar endpoints changes and we might revert it so we can still return some useful user info for post processing.
Author
Owner

@wh5938316 commented on GitHub (Dec 27, 2024):

I’m really glad to hear that you recognize this point. The code quality of better-auth is outstanding, and its usability within meta-frameworks is excellent. I truly believe it has the potential to revolutionize the auth ecosystem across the entire JavaScript landscape.

One of the biggest challenges I’ve faced when using better-auth with NestJS is related to route guarding. In NestJS, I implement this through middleware and guards. Ideally, within a single request lifecycle, the session should only be retrieved once. If the entire request lifecycle is handled within better-auth, this isn’t an issue. However, when it’s not, the session ends up being fetched twice (once in the middleware and once again in the API).

I wonder if it’s possible for better-auth’s $context to be injected externally. This could potentially address the issue. I’d like to open an issue to discuss this further.

<!-- gh-comment-id:2563591188 --> @wh5938316 commented on GitHub (Dec 27, 2024): I’m really glad to hear that you recognize this point. The code quality of better-auth is outstanding, and its usability within meta-frameworks is excellent. I truly believe it has the potential to revolutionize the auth ecosystem across the entire JavaScript landscape. One of the biggest challenges I’ve faced when using better-auth with NestJS is related to route guarding. In NestJS, I implement this through middleware and guards. Ideally, within a single request lifecycle, the session should only be retrieved once. If the entire request lifecycle is handled within better-auth, this isn’t an issue. However, when it’s not, the session ends up being fetched twice (once in the middleware and once again in the API). I wonder if it’s possible for better-auth’s $context to be injected externally. This could potentially address the issue. I’d like to open an issue to discuss this further.
Author
Owner

@Bekacru commented on GitHub (Dec 27, 2024):

Doesn't Nestjs provide a way to inject the session through out the routing chain?

<!-- gh-comment-id:2563608614 --> @Bekacru commented on GitHub (Dec 27, 2024): Doesn't Nestjs provide a way to inject the session through out the routing chain?
Author
Owner

@wh5938316 commented on GitHub (Dec 27, 2024):

I haven’t thoroughly reviewed the full codebase of better-auth, especially better-call, so I’m not sure if my understanding is entirely correct.

Currently, I inject the session and user into the request before processing each guarded route using the following approach:

@Injectable()
export class GlobalGuardMiddleware implements NestMiddleware {
  constructor(private readonly authService: AuthService) {}

  async use(req: Request, _res: Response, next: NextFunction) {
    const result = await this.authService.auth.api.getSession({
      headers: fromNodeHeaders(req.headers),
    });

    if (!result) {
      next();
      return;
    }

    const { session, user } = result;

    req['user'] = user as User;
    req['session'] = session as Session;

    next();
  }
}

However, if I call api.createInvitation within a route handler, the better-auth sessionMiddleware would also fetch the session from the database again, right?

<!-- gh-comment-id:2563652512 --> @wh5938316 commented on GitHub (Dec 27, 2024): I haven’t thoroughly reviewed the full codebase of `better-auth`, especially `better-call`, so I’m not sure if my understanding is entirely correct. Currently, I inject the session and user into the request before processing each guarded route using the following approach: ```javascript @Injectable() export class GlobalGuardMiddleware implements NestMiddleware { constructor(private readonly authService: AuthService) {} async use(req: Request, _res: Response, next: NextFunction) { const result = await this.authService.auth.api.getSession({ headers: fromNodeHeaders(req.headers), }); if (!result) { next(); return; } const { session, user } = result; req['user'] = user as User; req['session'] = session as Session; next(); } } ``` However, if I call api.createInvitation within a route handler, the better-auth `sessionMiddleware` would also fetch the session from the database again, right?
Author
Owner

@Bekacru commented on GitHub (Dec 27, 2024):

Yeah it does fetch twice. We can potentially make the api to accept session or the actual ctx object. but if there is a cookie cache this wouldn't be a big issue other than for requests that cache miss.

<!-- gh-comment-id:2563668356 --> @Bekacru commented on GitHub (Dec 27, 2024): Yeah it does fetch twice. We can potentially make the api to accept `session` or the actual ctx object. but if there is a cookie cache this wouldn't be a big issue other than for requests that cache miss.
Author
Owner

@wh5938316 commented on GitHub (Dec 27, 2024):

Yes, this isn’t a big issue, so when I realized this on the first day of using better-auth, I didn’t consider it a reason to not use it with NestJS. It just triggered a bit of my OCD (haha).

I mentioned this to highlight a point: when better-auth tries to cover the entire lifecycle of a request in backend frameworks, there might be some challenges or potential issues.

<!-- gh-comment-id:2563680961 --> @wh5938316 commented on GitHub (Dec 27, 2024): Yes, this isn’t a big issue, so when I realized this on the first day of using better-auth, I didn’t consider it a reason to not use it with NestJS. It just triggered a bit of my OCD (haha). I mentioned this to highlight a point: when better-auth tries to cover the entire lifecycle of a request in backend frameworks, there might be some challenges or potential issues.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#17192