[GH-ISSUE #1709] Bug : Microsoft The provided value for the input parameter 'redirect_uri' is not valid. #8879

Closed
opened 2026-04-13 04:07:39 -05:00 by GiteaMirror · 5 comments
Owner

Originally created by @aretwojay on GitHub (Mar 6, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/1709

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

I try to configure two microsoft providers for Next.js@14 app :

Image

Current vs. Expected behavior

I expected to be connected with my personnal Microsoft account

But when I attempt to connect with a personnal microsoft account, I getting this error :

Image

What version of Better Auth are you using?

1.2.3

Provide environment information

- OS : [MacOS 15.3.1]
- Browser : Arc Browser 1.8.3

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

Backend

Auth config (if applicable)

import { ac, admin, client, superadmin } from "@/auth/permissions";
import adminPrisma from "@/lib/adminPrisma";
import { betterAuth, BetterAuthOptions, Session } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import {
  admin as adminPlugin,
  customSession,
  genericOAuth,
  magicLink,
} from "better-auth/plugins";
import { z } from "zod";
import { sendVerificationRequest } from "./auth/send-verification-request";
import { getDataFromKV, setDataToKV } from "./lib/kv";
import { UserSchema } from "./schemas/UserSchema";

const baseURL = process.env.HOSTNAME_NEXTLEAD || "http://localhost:3000";

type SessionType = Session & {
  user: z.infer<typeof UserSchema>;
  impersonatedBy?: string | null;
};

const options = {
  baseURL: baseURL,
  database: prismaAdapter(adminPrisma, {
    provider: "postgresql",
  }),
  emailAndPassword: {
    enabled: true,
  },
  user: {
    additionalFields: {
      organizationId: {
        type: "string",
      },
      providerId: {
        type: "string",
      },
    },
  },
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60, // Cache duration in seconds
    },
    fields: {
      expiresAt: "expires", // e.g., "expires_at" or your existing field name
      token: "sessionToken", // e.g., "session_token" or your existing field name
    },
  },
  account: {
    accountLinking: {
      enabled: true,
    },
    fields: {
      accountId: "providerAccountId",
      providerId: "provider",
      refreshToken: "refresh_token",
      accessToken: "access_token",
      accessTokenExpiresAt: "expires_at",
      idToken: "id_token",
    },
  },
  verification: {
    // better auth not recognizing our model name, we have to override it
    modelName: "Verification",
  },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
      accessType: "offline",
      prompt: "consent",
      mapProfileToUser(profile) {
        return {
          ...profile,
          providerId: "google",
        };
      },
    },
  },
  plugins: [
    adminPlugin({
      defaultRole: "client",
      ac: ac,
      roles: {
        admin,
        superadmin,
        client,
      },
      adminRoles: ["superadmin", "admin"],
    }),
    magicLink({
      sendMagicLink: async ({ email, token, url }, request) => {
        await sendVerificationRequest({
          identifier: email,
          url,
          provider: {
            from: process.env.NEXT_PUBLIC_RESEND_NO_REPLY || "",
            apiKey:
              process.env.RESEND_FULL_ACCESS_API_KEY || "api_key_placeholder",
          },
        });
      },
    }),
    genericOAuth({
      config: [
        {
          providerId: "microsoft-entra-id-fgta",
          clientId: process.env.FGTA_AUTH_MICROSOFT_ENTRA_ID_ID as string,
          clientSecret: process.env
            .FGTA_AUTH_MICROSOFT_ENTRA_ID_SECRET as string,
          discoveryUrl: `https://login.microsoftonline.com/${process.env.FGTA_AUTH_MICROSOFT_ENTRA_ID_TENANT_ID}/v2.0/.well-known/openid-configuration`,
          redirectURI: `${process.env.HOSTNAME_FGTA || "http://localhost:3000"}/api/auth/oauth2/callback/microsoft-entra-id-fgta`,
          responseType: "code",
          prompt: "select_account",
          scopes: ["offline_access", "openid", "profile", "email", "Mail.Send"],
          mapProfileToUser(profile) {
            return {
              ...profile,
              providerId: "microsoft-entra-id-fgta",
            };
          },
        },
        {
          providerId: "microsoft-entra-id",
          clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID as string,
          clientSecret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET as string,
          discoveryUrl: `https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration`,
          redirectURI: `${baseURL}/api/auth/oauth2/callback/microsoft-entra-id`,
          responseType: "code",
          prompt: "select_account",
          scopes: ["offline_access", "openid", "profile", "email", "Mail.Send"],
          mapProfileToUser(profile) {
            return {
              ...profile,
              providerId: "microsoft-entra-id",
            };
          },
        },
        // Add more providers as needed
      ],
    }),
  ],
} satisfies BetterAuthOptions;

export const auth = betterAuth({
  ...options,
  plugins: [
    ...(options.plugins ?? []),
    customSession(async ({ user, session }) => {
      const cacheKey = `user:${user.id}`;

      // Check cache
      const cachedUser = await getDataFromKV(cacheKey);
      if (cachedUser) {
        // console.log("Cache hit");
        // console.log(cachedUser);
        return cachedUser as SessionType;
      }

      // Query database
      // console.log("Cache miss");
      const prismaUser = await adminPrisma.user.findUnique({
        include: {
          organization: true,
        },
        where: { id: user.id },
      });
      if (!prismaUser) {
        return {
          ...session,
        } as SessionType;
      }
      const sessionData: SessionType = {
        user: {
          ...user,
          ...prismaUser,
        },
        ...session,
      };
      // Store in cache
      if (prismaUser) {
        await setDataToKV(cacheKey, JSON.stringify(sessionData)); // Cache for 1 hour
      }

      // now both user and session will infer the fields added by plugins and your custom fields
      return sessionData;
    }, options),
  ],
});

Additional context

No response

Originally created by @aretwojay on GitHub (Mar 6, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/1709 ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce I try to configure two microsoft providers for Next.js@14 app : <img width="971" alt="Image" src="https://github.com/user-attachments/assets/9aa540b6-b258-4363-9bad-5fbdeebd6124" /> ### Current vs. Expected behavior I expected to be connected with my personnal Microsoft account But when I attempt to connect with a personnal microsoft account, I getting this error : ![Image](https://github.com/user-attachments/assets/d44a0d13-c357-4c4f-8b86-0e6bd7c13c33) ### What version of Better Auth are you using? 1.2.3 ### Provide environment information ```bash - OS : [MacOS 15.3.1] - Browser : Arc Browser 1.8.3 ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { ac, admin, client, superadmin } from "@/auth/permissions"; import adminPrisma from "@/lib/adminPrisma"; import { betterAuth, BetterAuthOptions, Session } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { admin as adminPlugin, customSession, genericOAuth, magicLink, } from "better-auth/plugins"; import { z } from "zod"; import { sendVerificationRequest } from "./auth/send-verification-request"; import { getDataFromKV, setDataToKV } from "./lib/kv"; import { UserSchema } from "./schemas/UserSchema"; const baseURL = process.env.HOSTNAME_NEXTLEAD || "http://localhost:3000"; type SessionType = Session & { user: z.infer<typeof UserSchema>; impersonatedBy?: string | null; }; const options = { baseURL: baseURL, database: prismaAdapter(adminPrisma, { provider: "postgresql", }), emailAndPassword: { enabled: true, }, user: { additionalFields: { organizationId: { type: "string", }, providerId: { type: "string", }, }, }, session: { cookieCache: { enabled: true, maxAge: 5 * 60, // Cache duration in seconds }, fields: { expiresAt: "expires", // e.g., "expires_at" or your existing field name token: "sessionToken", // e.g., "session_token" or your existing field name }, }, account: { accountLinking: { enabled: true, }, fields: { accountId: "providerAccountId", providerId: "provider", refreshToken: "refresh_token", accessToken: "access_token", accessTokenExpiresAt: "expires_at", idToken: "id_token", }, }, verification: { // better auth not recognizing our model name, we have to override it modelName: "Verification", }, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, accessType: "offline", prompt: "consent", mapProfileToUser(profile) { return { ...profile, providerId: "google", }; }, }, }, plugins: [ adminPlugin({ defaultRole: "client", ac: ac, roles: { admin, superadmin, client, }, adminRoles: ["superadmin", "admin"], }), magicLink({ sendMagicLink: async ({ email, token, url }, request) => { await sendVerificationRequest({ identifier: email, url, provider: { from: process.env.NEXT_PUBLIC_RESEND_NO_REPLY || "", apiKey: process.env.RESEND_FULL_ACCESS_API_KEY || "api_key_placeholder", }, }); }, }), genericOAuth({ config: [ { providerId: "microsoft-entra-id-fgta", clientId: process.env.FGTA_AUTH_MICROSOFT_ENTRA_ID_ID as string, clientSecret: process.env .FGTA_AUTH_MICROSOFT_ENTRA_ID_SECRET as string, discoveryUrl: `https://login.microsoftonline.com/${process.env.FGTA_AUTH_MICROSOFT_ENTRA_ID_TENANT_ID}/v2.0/.well-known/openid-configuration`, redirectURI: `${process.env.HOSTNAME_FGTA || "http://localhost:3000"}/api/auth/oauth2/callback/microsoft-entra-id-fgta`, responseType: "code", prompt: "select_account", scopes: ["offline_access", "openid", "profile", "email", "Mail.Send"], mapProfileToUser(profile) { return { ...profile, providerId: "microsoft-entra-id-fgta", }; }, }, { providerId: "microsoft-entra-id", clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID as string, clientSecret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET as string, discoveryUrl: `https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration`, redirectURI: `${baseURL}/api/auth/oauth2/callback/microsoft-entra-id`, responseType: "code", prompt: "select_account", scopes: ["offline_access", "openid", "profile", "email", "Mail.Send"], mapProfileToUser(profile) { return { ...profile, providerId: "microsoft-entra-id", }; }, }, // Add more providers as needed ], }), ], } satisfies BetterAuthOptions; export const auth = betterAuth({ ...options, plugins: [ ...(options.plugins ?? []), customSession(async ({ user, session }) => { const cacheKey = `user:${user.id}`; // Check cache const cachedUser = await getDataFromKV(cacheKey); if (cachedUser) { // console.log("Cache hit"); // console.log(cachedUser); return cachedUser as SessionType; } // Query database // console.log("Cache miss"); const prismaUser = await adminPrisma.user.findUnique({ include: { organization: true, }, where: { id: user.id }, }); if (!prismaUser) { return { ...session, } as SessionType; } const sessionData: SessionType = { user: { ...user, ...prismaUser, }, ...session, }; // Store in cache if (prismaUser) { await setDataToKV(cacheKey, JSON.stringify(sessionData)); // Cache for 1 hour } // now both user and session will infer the fields added by plugins and your custom fields return sessionData; }, options), ], }); ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-13 04:07:39 -05:00
Author
Owner

@sd44 commented on GitHub (Mar 9, 2025):

Same issue.

I followed better-auth ms doc, changed redirect URL to http://localhost:3000/api/auth/callback/microsoft on azure, not callback/microsoft-entra-id. Then it's OK.

<!-- gh-comment-id:2708869558 --> @sd44 commented on GitHub (Mar 9, 2025): Same issue. I followed [better-auth ms doc](https://better-auth.vercel.app/docs/authentication/microsoft), changed redirect URL to http://localhost:3000/api/auth/callback/microsoft on azure, **not** _callback/microsoft-entra-id_. Then it's OK.
Author
Owner

@aretwojay commented on GitHub (Mar 10, 2025):

Same issue.

I followed better-auth ms doc, changed redirect URL to http://localhost:3000/api/auth/callback/microsoft on azure, not callback/microsoft-entra-id. Then it's OK.

Yes I did that too but it's not working either, I don't know why I keep getting the error

<!-- gh-comment-id:2710308192 --> @aretwojay commented on GitHub (Mar 10, 2025): > Same issue. > > I followed [better-auth ms doc](https://better-auth.vercel.app/docs/authentication/microsoft), changed redirect URL to http://localhost:3000/api/auth/callback/microsoft on azure, **not** _callback/microsoft-entra-id_. Then it's OK. Yes I did that too but it's not working either, I don't know why I keep getting the error
Author
Owner

@sd44 commented on GitHub (Mar 10, 2025):

try to follow better-auth doc? It's very simple and easy.

https://better-auth.vercel.app/docs/authentication/microsoft

And check your Microsoft Graph App manifest,it may be has those key/value.

{

	"api": {
	
		"requestedAccessTokenVersion": 2,
		
	},

	"web": {
		"homePageUrl": null,
		"logoutUrl": null,
		"redirectUris": [
			"http://localhost:3000/api/auth/callback/microsoft"
		],
		"implicitGrantSettings": {
			"enableAccessTokenIssuance": false,
			"enableIdTokenIssuance": false
		},
		"redirectUriSettings": [
			{
				"uri": "http://localhost:3000/api/auth/callback/microsoft",
				"index": null
			}
		]
	},
	"spa": {
		"redirectUris": []
	}
}
<!-- gh-comment-id:2710484980 --> @sd44 commented on GitHub (Mar 10, 2025): try to follow better-auth doc? It's very simple and easy. https://better-auth.vercel.app/docs/authentication/microsoft And check your Microsoft Graph App manifest,it may be has those key/value. ``` { "api": { "requestedAccessTokenVersion": 2, }, "web": { "homePageUrl": null, "logoutUrl": null, "redirectUris": [ "http://localhost:3000/api/auth/callback/microsoft" ], "implicitGrantSettings": { "enableAccessTokenIssuance": false, "enableIdTokenIssuance": false }, "redirectUriSettings": [ { "uri": "http://localhost:3000/api/auth/callback/microsoft", "index": null } ] }, "spa": { "redirectUris": [] } } ```
Author
Owner

@dosubot[bot] commented on GitHub (Jun 14, 2025):

Hi, @aretwojay. I'm Dosu, and I'm helping the better-auth team manage their backlog. I'm marking this issue as stale.

Issue Summary:

  • You reported a bug with configuring Microsoft providers in a Next.js@14 app.
  • The error involves an invalid 'redirect_uri' when connecting a personal Microsoft account.
  • sd44 suggested using http://localhost:3000/api/auth/callback/microsoft as the redirect URL, which worked for them.
  • Despite following these steps, you still encounter the error.
  • sd44 also recommended checking the Microsoft Graph App manifest for specific settings.

Next Steps:

  • Please confirm if this issue is still relevant with the latest version of the better-auth repository by commenting here.
  • If there is no further activity, this issue will be automatically closed in 7 days.

Thank you for your understanding and contribution!

<!-- gh-comment-id:2972847798 --> @dosubot[bot] commented on GitHub (Jun 14, 2025): Hi, @aretwojay. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog. I'm marking this issue as stale. **Issue Summary:** - You reported a bug with configuring Microsoft providers in a Next.js@14 app. - The error involves an invalid 'redirect_uri' when connecting a personal Microsoft account. - sd44 suggested using `http://localhost:3000/api/auth/callback/microsoft` as the redirect URL, which worked for them. - Despite following these steps, you still encounter the error. - sd44 also recommended checking the Microsoft Graph App manifest for specific settings. **Next Steps:** - Please confirm if this issue is still relevant with the latest version of the better-auth repository by commenting here. - If there is no further activity, this issue will be automatically closed in 7 days. Thank you for your understanding and contribution!
Author
Owner

@DrewJohnsonGT commented on GitHub (Jul 7, 2025):

For anyone else that runs into this - what finally worked for me was re-creating the Microsoft app registration from scratch.
I had an older app registration I had used with next-auth that once I switched to better-auth would always throw the invalid redirect_uri error even with a valid redirect_uri
Same exact configuration on the new app registration and works great!

<!-- gh-comment-id:3043414961 --> @DrewJohnsonGT commented on GitHub (Jul 7, 2025): For anyone else that runs into this - what finally worked for me was re-creating the Microsoft app registration from scratch. I had an older app registration I had used with next-auth that once I switched to better-auth would always throw the invalid redirect_uri error even with a valid redirect_uri Same exact configuration on the new app registration and works great!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#8879