[GH-ISSUE #7357] Apple Sign-In: 'user' param (name) lost in callback redirection #28116

Closed
opened 2026-04-17 19:31:08 -05:00 by GiteaMirror · 5 comments
Owner

Originally created by @tseacen on GitHub (Jan 14, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/7357

Originally assigned to: @bytaesu on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Initiate an Apple Sign-In flow that returns the user's name (e.g., first-time sign-in).
  2. Apple posts the user JSON object (containing name) to the callback URL.
  3. better-auth handles this POST, parses the body, and redirects to the callback URL with query parameters (converting POST body to Query Params).
  4. The redirection targets GET /callback/:id.

Current vs. Expected behavior

Current Behavior: The user parameter (containing the name) is correctly part of the query parameters after redirection.
However, in api/routes/callback.ts around line 150 (in source) or line 89 (in compiled mjs), the code attempts to read the user object from c.body.user.
Since the request is now a GET request (following the 302 Redirect), c.body is empty/undefined. Result: userInfo.name falls back to email or remains empty, even though Apple sent the name. (Maybe you need to know I make the request since an expo app).

Expected Behavior: The code should read from the unified queryOrBody object (which is already populated earlier in the function) or check c.query.user when handling a GET request, effectively capturing the name passed in the query parameters.

What version of Better Auth are you using?

1.4.12

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:40 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6000",
    "release": "25.2.0",
    "cpuCount": 10,
    "cpuModel": "Apple M1 Pro",
    "totalMemory": "16.00 GB",
    "freeMemory": "0.21 GB"
  },
  "node": {
    "version": "v25.2.1",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.6.2"
  },
  "frameworks": [
    {
      "name": "express",
      "version": "^5.2.1"
    }
  ],
  "databases": [
    {
      "name": "@prisma/client",
      "version": "^7.2.0"
    }
  ],
  "betterAuth": {
    "version": "^1.4.1",
    "config": {
      "secret": "[REDACTED]",
      "baseURL": "http://...",
      "basePath": "/v1/auth",
      "logger": {
        "level": "debug"
      },
      "emailAndPassword": {
        "enabled": true
      },
      "plugins": [
        {
          "name": "open-api",
          "config": {
            "id": "open-api",
            "endpoints": {}
          }
        },
        {
          "name": "expo",
          "config": {
            "id": "expo",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {}
          }
        }
      ],
      "session": {
        "expiresIn": 31536000,
        "updateAge": 604800
      },
      "user": {
        "changeEmail": {
          "enabled": true
        },
        "deleteUser": {
          "enabled": true
        }
      },
      "emailVerification": {},
      "socialProviders": {
        "google": {
          "prompt": "select_account",
          "clientId": "[REDACTED]",
          "clientSecret": "[REDACTED]",
          "enabled": true
        },
        "apple": {
          "clientId": "[REDACTED]",
          "clientSecret": "[REDACTED]",
          "appBundleIdentifier": "com.verpicalabs......",
          "disableDefaultScope": true,
          "scope": [
            "name",
            "email"
          ]
        }
      },
      "trustedOrigins": [
        "exp://*",
        "...://",
        "...://",
        "...://",
        "*",
        "https://appleid.apple.com"
      ]
    }
  }
}

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

Other

Auth config (if applicable)

export const auth = betterAuth({
    database: prismaAdapter(prisma, {
        provider: "postgresql",
    }),

    secret: z.string().parse(process.env.BETTER_AUTH_SECRET),
    baseURL: z.url().parse(process.env.BETTER_AUTH_URL),
    basePath: "/v1/auth",

    logger: {
        level: isDev ? "debug" : "error",
    },

    emailAndPassword: {
        enabled: true,
        sendVerificationEmail: async () => {
            // TODO: Implement actual email sending
        },
    },

    plugins: plugins,

    session: {
        expiresIn: 60 * 60 * 24 * 365,
        updateAge: 60 * 60 * 24 * 7,
    },

    user: {
        changeEmail: {
            enabled: true,
        },
        deleteUser: {
            enabled: true,
        }
    },

    emailVerification: {
        sendVerificationEmail: async () => {
            // TODO: Implement actual email sending
        }
    },

    socialProviders: {
        google: {
            prompt: "select_account",
            clientId: process.env.GOOGLE_CLIENT_ID ?? "",
            clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
        },
        apple: {
            clientId: process.env.APPLE_SERVICE_ID ?? "",
            clientSecret: appleClientSecret,
            appBundleIdentifier: process.env.APPLE_APP_BUNDLE_IDENTIFIER ?? "",
            disableDefaultScope: true,
            scope: ["name", "email"],
            mapProfileToUser: async (profile) => {
                console.log(profile);
                return profile
            },
        },
    },

    trustedOrigins,
});

Additional context

Location of the bug (I guess): In src/api/routes/callback.ts (or dist/api/routes/callback.mjs):

// Current Code (Bug)
const userInfo = await provider.getUserInfo({
    ...tokens,
    user: c.body?.user ? safeJSONParse(c.body.user) : void 0
}).then((res) => res?.user);
Proposed Fix: Should use queryOrBody which is defined at the top of the function to handle both GET and POST data sources.

// Proposed Fix
const userInfo = await provider.getUserInfo({
    ...tokens,
    user: queryOrBody?.user ? safeJSONParse(queryOrBody.user) : void 0
}).then((res) => res?.user);

Originally created by @tseacen on GitHub (Jan 14, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7357 Originally assigned to: @bytaesu on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Initiate an Apple Sign-In flow that returns the user's name (e.g., first-time sign-in). 2. Apple posts the user JSON object (containing name) to the callback URL. 3. better-auth handles this POST, parses the body, and redirects to the callback URL with query parameters (converting POST body to Query Params). 4. The redirection targets GET /callback/:id. ### Current vs. Expected behavior **Current Behavior:** The user parameter (containing the name) is correctly part of the query parameters after redirection. However, in api/routes/callback.ts around line 150 (in source) or line 89 (in compiled mjs), the code attempts to read the user object from c.body.user. Since the request is now a GET request (following the 302 Redirect), c.body is empty/undefined. Result: userInfo.name falls back to email or remains empty, even though Apple sent the name. (Maybe you need to know I make the request since an expo app). **Expected Behavior:** The code should read from the unified queryOrBody object (which is already populated earlier in the function) or check c.query.user when handling a GET request, effectively capturing the name passed in the query parameters. ### What version of Better Auth are you using? 1.4.12 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:40 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6000", "release": "25.2.0", "cpuCount": 10, "cpuModel": "Apple M1 Pro", "totalMemory": "16.00 GB", "freeMemory": "0.21 GB" }, "node": { "version": "v25.2.1", "env": "development" }, "packageManager": { "name": "npm", "version": "11.6.2" }, "frameworks": [ { "name": "express", "version": "^5.2.1" } ], "databases": [ { "name": "@prisma/client", "version": "^7.2.0" } ], "betterAuth": { "version": "^1.4.1", "config": { "secret": "[REDACTED]", "baseURL": "http://...", "basePath": "/v1/auth", "logger": { "level": "debug" }, "emailAndPassword": { "enabled": true }, "plugins": [ { "name": "open-api", "config": { "id": "open-api", "endpoints": {} } }, { "name": "expo", "config": { "id": "expo", "hooks": { "after": [ {} ] }, "endpoints": {} } } ], "session": { "expiresIn": 31536000, "updateAge": 604800 }, "user": { "changeEmail": { "enabled": true }, "deleteUser": { "enabled": true } }, "emailVerification": {}, "socialProviders": { "google": { "prompt": "select_account", "clientId": "[REDACTED]", "clientSecret": "[REDACTED]", "enabled": true }, "apple": { "clientId": "[REDACTED]", "clientSecret": "[REDACTED]", "appBundleIdentifier": "com.verpicalabs......", "disableDefaultScope": true, "scope": [ "name", "email" ] } }, "trustedOrigins": [ "exp://*", "...://", "...://", "...://", "*", "https://appleid.apple.com" ] } } } ``` ### Which area(s) are affected? (Select all that apply) Other ### Auth config (if applicable) ```typescript export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), secret: z.string().parse(process.env.BETTER_AUTH_SECRET), baseURL: z.url().parse(process.env.BETTER_AUTH_URL), basePath: "/v1/auth", logger: { level: isDev ? "debug" : "error", }, emailAndPassword: { enabled: true, sendVerificationEmail: async () => { // TODO: Implement actual email sending }, }, plugins: plugins, session: { expiresIn: 60 * 60 * 24 * 365, updateAge: 60 * 60 * 24 * 7, }, user: { changeEmail: { enabled: true, }, deleteUser: { enabled: true, } }, emailVerification: { sendVerificationEmail: async () => { // TODO: Implement actual email sending } }, socialProviders: { google: { prompt: "select_account", clientId: process.env.GOOGLE_CLIENT_ID ?? "", clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "", }, apple: { clientId: process.env.APPLE_SERVICE_ID ?? "", clientSecret: appleClientSecret, appBundleIdentifier: process.env.APPLE_APP_BUNDLE_IDENTIFIER ?? "", disableDefaultScope: true, scope: ["name", "email"], mapProfileToUser: async (profile) => { console.log(profile); return profile }, }, }, trustedOrigins, }); ``` ### Additional context **Location of the bug (I guess):** In `src/api/routes/callback.ts` (or `dist/api/routes/callback.mjs`): ``` // Current Code (Bug) const userInfo = await provider.getUserInfo({ ...tokens, user: c.body?.user ? safeJSONParse(c.body.user) : void 0 }).then((res) => res?.user); Proposed Fix: Should use queryOrBody which is defined at the top of the function to handle both GET and POST data sources. // Proposed Fix const userInfo = await provider.getUserInfo({ ...tokens, user: queryOrBody?.user ? safeJSONParse(queryOrBody.user) : void 0 }).then((res) => res?.user); ```
GiteaMirror added the locked label 2026-04-17 19:31:08 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 14, 2026):

It looks like you've encountered an issue with the Apple Sign-In callback where the user's name parameter is not being handled correctly during the redirection process. From your description, it's clear that the data sent from Apple is lost when the redirect occurs, preventing you from accessing the user's name.

To resolve this, it's been proposed that the code should utilize the queryOrBody object that is defined earlier in the function to capture user data whether it's coming from a GET or POST request. This can be done by modifying the existing code as follows:

// Current Code (Bug)
const userInfo = await provider.getUserInfo({
    ...tokens,
    user: c.body?.user ? safeJSONParse(c.body.user) : void 0
}).then((res) => res?.user);

// Proposed Fix
const userInfo = await provider.getUserInfo({
    ...tokens,
    user: queryOrBody?.user ? safeJSONParse(queryOrBody.user) : void 0
}).then((res) => res?.user);

This change will help ensure that you correctly read the user data from the query parameters when handling the GET request following the redirect [1].

If you require further assistance or would like to report this issue, feel free to tag team members like @bekacru or @ping__ for more in-depth support.

What other aspects of Better Auth are you curious about or need help with?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3749671908 --> @better-auth-agent[bot] commented on GitHub (Jan 14, 2026): It looks like you've encountered an issue with the Apple Sign-In callback where the user's name parameter is not being handled correctly during the redirection process. From your description, it's clear that the data sent from Apple is lost when the redirect occurs, preventing you from accessing the user's name. To resolve this, it's been proposed that the code should utilize the `queryOrBody` object that is defined earlier in the function to capture user data whether it's coming from a GET or POST request. This can be done by modifying the existing code as follows: ```javascript // Current Code (Bug) const userInfo = await provider.getUserInfo({ ...tokens, user: c.body?.user ? safeJSONParse(c.body.user) : void 0 }).then((res) => res?.user); // Proposed Fix const userInfo = await provider.getUserInfo({ ...tokens, user: queryOrBody?.user ? safeJSONParse(queryOrBody.user) : void 0 }).then((res) => res?.user); ``` This change will help ensure that you correctly read the user data from the query parameters when handling the GET request following the redirect [[1]](https://www.better-auth.com/docs/authentication/apple). If you require further assistance or would like to report this issue, feel free to tag team members like @bekacru or @ping__ for more in-depth support. What other aspects of Better Auth are you curious about or need help with? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@tseacen commented on GitHub (Jan 14, 2026):

Hi @Bekacru @ping-maxwell.
The proposed fix works on my side. Let me know if this looks ok to you. If so, I will open a PR within 24h.

<!-- gh-comment-id:3749845273 --> @tseacen commented on GitHub (Jan 14, 2026): Hi @Bekacru @ping-maxwell. The proposed fix works on my side. Let me know if this looks ok to you. If so, I will open a PR within 24h.
Author
Owner

@bytaesu commented on GitHub (Jan 14, 2026):

Hi @tseacen,

Could you check if this issue is resolved with this version? It seems related 🧐

npm i https://pkg.pr.new/better-auth/better-auth@7181
<!-- gh-comment-id:3750038836 --> @bytaesu commented on GitHub (Jan 14, 2026): Hi @tseacen, Could you check if this issue is resolved with this version? It seems related 🧐 ``` npm i https://pkg.pr.new/better-auth/better-auth@7181 ``` - #7181
Author
Owner

@tseacen commented on GitHub (Jan 14, 2026):

Hi @bytaesu, I've tested and can confirm it resolves the issue! Thanks for that and sorry for the duplicate.

<!-- gh-comment-id:3750245479 --> @tseacen commented on GitHub (Jan 14, 2026): Hi @bytaesu, I've tested and can confirm it resolves the issue! Thanks for that and sorry for the duplicate.
Author
Owner

@bytaesu commented on GitHub (Jan 14, 2026):

@tseacen, thanks for letting me know 😊

I'll handle it together!

<!-- gh-comment-id:3750257131 --> @bytaesu commented on GitHub (Jan 14, 2026): @tseacen, thanks for letting me know 😊 I'll handle it together!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28116