authClient.device.deny has wrong error type #2055

Closed
opened 2026-03-13 09:23:22 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @3ddelano on GitHub (Oct 1, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Using better-auth version 1.3.24

  1. Setup the better-auth server and client with the Device Authorization plugin.
  2. See the types of the authClient.device.deny error object
const { error } = await authClient.device.deny({ userCode: "1"})

Current vs. Expected behavior

The current error type is:

{
    code?: string | undefined;
    message?: string | undefined;
    status: number;
    statusText: string;
} | null

While the expected error type should be:

{
    error: "invalid_request" | "expired_token";
    error_description: string;
    status: number;
    statusText: string;
}

What version of Better Auth are you using?

1.3.24

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:28:30 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6030",
    "release": "24.6.0",
    "cpuCount": 12,
    "cpuModel": "Apple M3 Pro",
    "totalMemory": "18.00 GB",
    "freeMemory": "0.09 GB"
  },
  "node": {
    "version": "v22.5.1",
    "env": "development"
  },
  "packageManager": {
    "name": "bun",
    "version": "1.2.21"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "15.5.4"
    },
    {
      "name": "react",
      "version": "19.1.0"
    }
  ],
  "databases": [
    {
      "name": "@prisma/client",
      "version": "^6.16.2"
    }
  ],
  "betterAuth": {
    "version": "1.3.24",
    "config": {
      "appName": "Test",
      "socialProviders": {
        "github": {
          "clientId": "[REDACTED]",
          "clientSecret": "[REDACTED]"
        }
      },
      "plugins": [
        {
          "name": "last-login-method",
          "config": {
            "id": "last-login-method",
            "hooks": {
              "after": [
                {}
              ]
            },
            "schema": {
              "user": {
                "fields": {
                  "lastLoginMethod": {
                    "type": "string",
                    "input": false,
                    "required": false,
                    "fieldName": "lastLoginMethod"
                  }
                }
              }
            }
          }
        },
        {
          "name": "device-authorization",
          "config": {
            "id": "device-authorization",
            "schema": {
              "deviceCode": {
                "fields": {
                  "deviceCode": {
                    "type": "string",
                    "required": true
                  },
                  "userCode": {
                    "type": "string",
                    "required": true
                  },
                  "userId": {
                    "type": "string",
                    "required": false
                  },
                  "expiresAt": {
                    "type": "date",
                    "required": true
                  },
                  "status": {
                    "type": "string",
                    "required": true
                  },
                  "lastPolledAt": {
                    "type": "date",
                    "required": false
                  },
                  "pollingInterval": {
                    "type": "number",
                    "required": false
                  },
                  "clientId": {
                    "type": "string",
                    "required": false
                  },
                  "scope": {
                    "type": "string",
                    "required": false
                  }
                }
              }
            },
            "endpoints": {},
            "$ERROR_CODES": {
              "INVALID_DEVICE_CODE": "Invalid device code",
              "EXPIRED_DEVICE_CODE": "Device code has expired",
              "EXPIRED_USER_CODE": "User code has expired",
              "AUTHORIZATION_PENDING": "Authorization pending",
              "ACCESS_DENIED": "Access denied",
              "INVALID_USER_CODE": "Invalid user code",
              "DEVICE_CODE_ALREADY_PROCESSED": "Device code already processed",
              "POLLING_TOO_FREQUENTLY": "Polling too frequently",
              "USER_NOT_FOUND": "User not found",
              "FAILED_TO_CREATE_SESSION": "Failed to create session",
              "INVALID_DEVICE_CODE_STATUS": "Invalid device code status",
              "AUTHENTICATION_REQUIRED": "Authentication required"
            }
          }
        }
      ]
    }
  }
}

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

Types

Auth config (if applicable)

import { betterAuth } from "better-auth"
import { deviceAuthorization } from "better-auth/plugins";


export const auth = betterAuth({
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID as string,
      clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
    },
  },
  plugins: [
    deviceAuthorization({
      expiresIn: "30m",
      interval: "1s",
    }),
  ]
});

Additional context

No response

Originally created by @3ddelano on GitHub (Oct 1, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Using better-auth version 1.3.24 1. Setup the better-auth server and client with the Device Authorization plugin. 2. See the types of the **authClient.device.deny** error object ```js const { error } = await authClient.device.deny({ userCode: "1"}) ``` ### Current vs. Expected behavior The current error type is: ```js { code?: string | undefined; message?: string | undefined; status: number; statusText: string; } | null ``` While the expected error type should be: ```js { error: "invalid_request" | "expired_token"; error_description: string; status: number; statusText: string; } ``` ### What version of Better Auth are you using? 1.3.24 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:28:30 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6030", "release": "24.6.0", "cpuCount": 12, "cpuModel": "Apple M3 Pro", "totalMemory": "18.00 GB", "freeMemory": "0.09 GB" }, "node": { "version": "v22.5.1", "env": "development" }, "packageManager": { "name": "bun", "version": "1.2.21" }, "frameworks": [ { "name": "next", "version": "15.5.4" }, { "name": "react", "version": "19.1.0" } ], "databases": [ { "name": "@prisma/client", "version": "^6.16.2" } ], "betterAuth": { "version": "1.3.24", "config": { "appName": "Test", "socialProviders": { "github": { "clientId": "[REDACTED]", "clientSecret": "[REDACTED]" } }, "plugins": [ { "name": "last-login-method", "config": { "id": "last-login-method", "hooks": { "after": [ {} ] }, "schema": { "user": { "fields": { "lastLoginMethod": { "type": "string", "input": false, "required": false, "fieldName": "lastLoginMethod" } } } } } }, { "name": "device-authorization", "config": { "id": "device-authorization", "schema": { "deviceCode": { "fields": { "deviceCode": { "type": "string", "required": true }, "userCode": { "type": "string", "required": true }, "userId": { "type": "string", "required": false }, "expiresAt": { "type": "date", "required": true }, "status": { "type": "string", "required": true }, "lastPolledAt": { "type": "date", "required": false }, "pollingInterval": { "type": "number", "required": false }, "clientId": { "type": "string", "required": false }, "scope": { "type": "string", "required": false } } } }, "endpoints": {}, "$ERROR_CODES": { "INVALID_DEVICE_CODE": "Invalid device code", "EXPIRED_DEVICE_CODE": "Device code has expired", "EXPIRED_USER_CODE": "User code has expired", "AUTHORIZATION_PENDING": "Authorization pending", "ACCESS_DENIED": "Access denied", "INVALID_USER_CODE": "Invalid user code", "DEVICE_CODE_ALREADY_PROCESSED": "Device code already processed", "POLLING_TOO_FREQUENTLY": "Polling too frequently", "USER_NOT_FOUND": "User not found", "FAILED_TO_CREATE_SESSION": "Failed to create session", "INVALID_DEVICE_CODE_STATUS": "Invalid device code status", "AUTHENTICATION_REQUIRED": "Authentication required" } } } ] } } } ``` ### Which area(s) are affected? (Select all that apply) Types ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" import { deviceAuthorization } from "better-auth/plugins"; export const auth = betterAuth({ socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID as string, clientSecret: process.env.GITHUB_CLIENT_SECRET as string, }, }, plugins: [ deviceAuthorization({ expiresIn: "30m", interval: "1s", }), ] }); ``` ### Additional context _No response_
GiteaMirror added the plugin label 2026-03-13 09:23:22 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Oct 1, 2025):

Hi @3ddelano! I'm Dosu and I’m helping the better-auth team.

The error type returned by authClient.device.deny is currently generic ({ code?: string; message?: string; status: number; statusText: string; }) rather than the expected OAuth error format ({ error: "invalid_request" | "expired_token"; error_description: string; status: number; statusText: string; }). This mismatch happens because BetterAuth client functions use a response object pattern for error handling, which can flatten or wrap server-side OAuth errors into a more generic shape on the client side. This design is discussed in issue #3443.

Recent updates to the Device Authorization plugin and client API methods (including device.deny) did not address or clarify the error type returned by this method, and the demo code continues to use generic error handling (PR #4410). No documentation currently specifies the intended error type for device.deny.

If you need to enforce a specific error shape in your code, you can wrap the client call and transform the error object to match your expectations, or use a wrapper function that rejects on error as shown in issue #3443.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Oct 1, 2025): <!-- Greeting --> Hi @3ddelano! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The error type returned by `authClient.device.deny` is currently generic (`{ code?: string; message?: string; status: number; statusText: string; }`) rather than the expected OAuth error format (`{ error: "invalid_request" | "expired_token"; error_description: string; status: number; statusText: string; }`). This mismatch happens because BetterAuth client functions use a response object pattern for error handling, which can flatten or wrap server-side OAuth errors into a more generic shape on the client side. This design is discussed in [issue #3443](https://github.com/better-auth/better-auth/issues/3443). Recent updates to the Device Authorization plugin and client API methods (including `device.deny`) did not address or clarify the error type returned by this method, and the demo code continues to use generic error handling ([PR #4410](https://github.com/better-auth/better-auth/pull/4410)). No documentation currently specifies the intended error type for `device.deny`. If you need to enforce a specific error shape in your code, you can wrap the client call and transform the error object to match your expectations, or use a wrapper function that rejects on error as shown in [issue #3443](https://github.com/better-auth/better-auth/issues/3443). <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/bd5be09a-bbc2-4448-9b43-b4fd635d494d?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/bd5be09a-bbc2-4448-9b43-b4fd635d494d?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/bd5be09a-bbc2-4448-9b43-b4fd635d494d?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/bd5be09a-bbc2-4448-9b43-b4fd635d494d?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/bd5be09a-bbc2-4448-9b43-b4fd635d494d?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/bd5be09a-bbc2-4448-9b43-b4fd635d494d?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/bd5be09a-bbc2-4448-9b43-b4fd635d494d?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/5021)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2055