Using Generic OAuth Plugin to connect to Follow Up Boss
@Better Auth i’m trying to use the Generic OAuth plugin to enable an oauth link flow with followupboss. i have the following config:
where 
FUB_CLIENT_ID and FUB_CLIENT_SECRET are secrets available in the server environment. i’m trying to invoke the link flow for authed users using the following code:
here’s what i see after invoking authClient.oauth2.link(...):
1. goes to https://login.followupboss.com/login?start_url=REDACTED where i login to FUB
2. goes to https://outlyne.followupboss.com/oauth/authorize?url=oauth%2Fauthorize&response_type=code&client_id=REDACTED&state=REDACTED&scope=&redirect_uri=https%3A%2F%2Foutlyne.com%2Fapi%2Fauth%2Foauth2%2Fcallback%2Ffollowupboss&oauth_fresh_login=1, where i see “Outlyne wants to access your Follow Up Boss Account” and have the option to Deny or GRANT ACCESS and i click GRANT ACCESS
3. goes to https://outlyne.com/api/auth/error?error=oauth_code_verification_failed with Better Auth Error
with logger.level: 'debug' , i get this logged from a request with url https://outlyne.com/api/auth/oauth2/callback/followupboss?state=REDACTED&response=approved&code=REDACTED:
Generic OAuth | Better Auth
Authenticate users with any OAuth provider
8 Replies
It looks like Follow Up Boss's token endpoint is expecting your client credentials via a Basic Authorization header, but your current configuration isn’t including that header. To resolve the error, you should enable basic authentication in your provider config. For example, update your config like this:
By explicitly setting 
authentication: 'basic', Better Auth will include the necessary header when exchanging the authorization code, which should fix the "invalid_client" error [1]. If the issue persists, make sure your secret values are correctly set in the environment and check your logs with debug level enabled for further insights.
Sources:
[1] Generic OAuth | Better AuthGeneric OAuth | Better Auth
Authenticate users with any OAuth provider
@Better Auth good call! now i’m getting a different error, which looks like progress:
looking at the FUB docs, it seems like one of the listed parameters must be missing. here’s the relevant excerpt from those docs:
<quote>
Step 3: Exchanging auth_code for tokens
Perform the following request to exchange the auth_code for tokens:
POST https://app.followupboss.com/oauth/token
Your request must be Authenticated with HTTP Basic Authentication over HTTPS. Construct the authentication string by Base64-encoding your OAuth Client App's Client ID and Secret:
Authorization: Basic base64("<client_id>:<client_secret>")
All parameters must be sent as URL-encoded form data (application/x-www-form-urlencoded).
grant_type <string>    This parameter tells the authorization server the type of grant you are trying to redeem.
Supported values:
- authorization_code (for this Use Case)
- refresh_token
code <string>    This is the authorization code (auth_code) that was provided in the previous response.
redirect_uri <string>    This is the redirect URL specified when creating your OAuth Client App. We only use this value as an additional verification factor. Be sure to URL encode the value of this parameter.
state <string>    This is an opaque value provided by the requesting system to prevent CSRF attacks and to maintain the state between requests and callbacks.
A successful response will include the Access & Refresh tokens representing the FUB User. The Token Exchange Response section details the response payload structure for successful and failed responses.
</quote>
so i’m guessing one of grant_type,  code, redirect_uri, or state must be missing.Follow Up Boss
Authentication & Authorization
Step 1: Requesting User Consent The first step in the OAuth flow is to obtain the User's consent. The requesting application should redirect the FUB User to the following URL to obtain consent: GET https://app.followupboss.com/oauth/authorize ?response_type=auth_code &client_id=<your_client_i...
It looks like the FollowUpBoss token endpoint isn’t receiving one or more of the required URL-encoded fields. As the FUB docs state, your request must include:
• grant_type (set to "authorization_code")
• code (the auth code from the callback)
• redirect_uri (your registered redirect URL)
• state (an opaque value for CSRF protection) In many cases with Better Auth’s generic OAuth plugin, these parameters are automatically included from the authorization flow—but if any of these (especially state or redirect_uri) are missing or misconfigured, you’ll see this error [1]. I recommend verifying your OAuth configuration to ensure that all four fields are set on the token request. Also, double-check that you’re sending the request as URL-encoded form data and that HTTP Basic Auth (constructed via standard Base64 encoding) is correctly applied as described by the FUB docs and reflected in Better Auth’s recent changes [2]. This should line up your request with the expectations of the FUB endpoint and help resolve the "Missing required params" error. Sources: [1] Allow to include
• code (the auth code from the callback)
• redirect_uri (your registered redirect URL)
• state (an opaque value for CSRF protection) In many cases with Better Auth’s generic OAuth plugin, these parameters are automatically included from the authorization flow—but if any of these (especially state or redirect_uri) are missing or misconfigured, you’ll see this error [1]. I recommend verifying your OAuth configuration to ensure that all four fields are set on the token request. Also, double-check that you’re sending the request as URL-encoded form data and that HTTP Basic Auth (constructed via standard Base64 encoding) is correctly applied as described by the FUB docs and reflected in Better Auth’s recent changes [2]. This should line up your request with the expectations of the FUB endpoint and help resolve the "Missing required params" error. Sources: [1] Allow to include
client_id field in the body of the token request (problems with Modrinth OAuth)
[2] chore(oauth): separate betterFetch from request formatGitHub
better-auth/better-auth
The most comprehensive authentication framework for TypeScript - better-auth/better-auth
GitHub
Build software better, together
GitHub is where people build software. More than 150 million people use GitHub to discover, fork, and contribute to over 420 million projects.

GitHub
Allow to include 
client_id field in the body of the token request...Is this suited for github? Yes, this is suited for github Is your feature request related to a problem? Please describe. I can't set up the OAuth flow for the Morinth site using the genericOAut...
GitHub
chore(oauth): separate betterFetch from request format by dvanmali ...
Separates betterFetch from the actual request format needed for testing functions.
Adds a resource parameter to the function which has a specific format described by RFC 8707
Partial: #3458
Summa...
@Better Auth ok i looked at the better-auth source (the generic-oauth plugin’s 
validateAuthorizationCode function, which calls validateAuthorizationCode from better-auth/src/oauth2/validate-authorization-code.ts).
the grant_type, code, and  redirect_uri are all automatically handled, but i don’t see anything related to the state param, so i’m pretty confident that is the missing piece. i see that tokenUrlParams can be used as part of the genericOAuth config, but it seems like state is not a static value i can specify at the config level but rather a random string used to prevent against CSRF attacks.You're absolutely right that the Better Auth flow automatically handles most parameters (like grant_type, code, and redirect_uri) for you. The state parameter, however, isn't meant to be a static value configured at the plugin level—it’s dynamically generated on a per-request basis for CSRF protection. This dynamic generation ensures each OAuth flow's state is unique and secure, which is a core best practice in OAuth implementations.
For cases where a provider might not send a state parameter (or you need a different behavior), Better Auth introduced the optional configuration flag (stateOptional) that lets you bypass state validation [11]. This approach avoids compromising security since using a static state value would defeat the purpose of having state protection in the first place.
So, in summary, while you can configure additional static parameters with tokenUrlParams [18], the state parameter remains dynamic to protect against CSRF, unless you explicitly opt into a scenario where state validation is optional.
Sources:
[11] fix: Generic OAuth and SSO do not work with IDP initiated flows.
[18] feat(generic-oauth): add support for additional token URL params in generic OAuth
GitHub
Build software better, together
GitHub is where people build software. More than 150 million people use GitHub to discover, fork, and contribute to over 420 million projects.

GitHub
fix: Generic OAuth and SSO do not work with IDP initiated flows. by...
Modifies parseState to safely access c.body, which, as the rest of the file suggests, can be undefined. (For context, see #491.)
Additionally, this pull request introduces support for OAuth provide...
GitHub
feat(generic-oauth): add support for additional token URL params in...
fix #3402
Summary by cubic
Added support for passing extra parameters to the token URL in generic OAuth, allowing more flexible provider integrations.
New Features
Added a new tokenUrlParams op...
@Better Auth it seems to me that the issue here is that Follow Up Boss wants the 
state provided as a param as a part of the request to validate the authorization code, which is perhaps not usually a requirement and so is not currently implemented. should i create a github issue documenting that requirement and requesting that it be added as an option?It sounds like a solid plan. In cases where a provider—like Follow Up Boss—requires a parameter (in this case, the state) that's not typically part of the default validateAuthorizationCode request, raising an issue on GitHub is the best way to document the need and spark discussion for adding it as an optional configuration. This approach is similar to previous enhancements where additional parameters or custom validation logic were requested for validateAuthorizationCode [1], [3]. 
So yes, please go ahead and create a GitHub issue outlining the requirement. That way, the maintainers and community can review and consider it for a future update.
Sources:
[1] Add additional parameters similar to authorizationUrlParams to validateAuthorizationCode
[3] Support custom validateAuthorizationCode in generic-oauth plugin
GitHub
better-auth/better-auth
The most comprehensive authentication framework for TypeScript - better-auth/better-auth
GitHub
better-auth/better-auth
The most comprehensive authentication framework for TypeScript - better-auth/better-auth
GitHub
Add additional parameters similar to authorizationUrlParams to vali...
Is this suited for github? Yes, this is suited for github Is your feature request related to a problem? Please describe. I encountered a minor issue while integrating with OAuth 2.0. In the /oauth2...
GitHub
Support custom validateAuthorizationCode in generic-oauth plugin ·...
Is this suited for github? Yes, this is suited for github Is your feature request related to a problem? Please describe. The generic-oauth plugin currently doesn't support customizing the valid...
ok i created an issue: https://github.com/better-auth/better-auth/issues/5026
GitHub
Support custom callback for configuring Generic OAuth token url (to...
Is this suited for github? Yes, this is suited for github Is your feature request related to a problem? Please describe. I am trying to integrate with Follow Up Boss via the Generic OAuth plugin. I...

