diff --git a/packages/core/src/lib/utils/providers.ts b/packages/core/src/lib/utils/providers.ts index 2e1fd68492..84425f9a1d 100644 --- a/packages/core/src/lib/utils/providers.ts +++ b/packages/core/src/lib/utils/providers.ts @@ -32,7 +32,19 @@ export default function parseProviders(params: { const { options: userOptions, ...defaults } = provider const id = (userOptions?.id ?? defaults.id) as string - // TODO: Support if properties have different types, e.g. authorization: string or object + // `authorization`/`token`/`userinfo` accept a string shorthand for just + // the endpoint URL. If a user only overrides part of that config (e.g. + // `authorization: { params: { scope } }`, without a new `url`), `merge` + // would otherwise see a string next to an object, discard the string + // default entirely, and lose the base URL. Promoting the string default + // to `{ url }` first lets `merge` combine them instead. + const defaultsAsRecord = defaults as Record + for (const key of ["authorization", "token", "userinfo"] as const) { + const value = defaultsAsRecord[key] + if (typeof value === "string") { + defaultsAsRecord[key] = { url: value } + } + } const merged = merge(defaults, userOptions, { signinUrl: `${url}/signin/${id}`, callbackUrl: `${url}/callback/${id}`, diff --git a/packages/core/test/providers.test.ts b/packages/core/test/providers.test.ts new file mode 100644 index 0000000000..2be5d2b778 --- /dev/null +++ b/packages/core/test/providers.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from "vitest" +import parseProviders from "../src/lib/utils/providers" +import Spotify from "../src/providers/spotify" +import { testConfig } from "./utils" + +describe("parseProviders", () => { + it("merges a params-only authorization override into a string-shorthand default without losing the base URL", () => { + // Spotify's `authorization` default is a plain string + // ("https://accounts.spotify.com/authorize?scope=user-read-email"). + // Overriding just the scope, as the docs recommend, must not discard + // that base URL. + const config = testConfig({ + providers: [ + Spotify({ + clientId: "client-id", + clientSecret: "client-secret", + authorization: { + params: { scope: "user-read-email playlist-read-private" }, + }, + }), + ], + }) + + const { providers } = parseProviders({ + url: new URL("http://localhost:3000/auth"), + config, + }) + + const spotify = providers[0] as any + + expect(spotify.authorization.url).toBeInstanceOf(URL) + expect(spotify.authorization.url.origin).toBe( + "https://accounts.spotify.com" + ) + expect(spotify.authorization.url.pathname).toBe("/authorize") + expect(spotify.authorization.url.searchParams.get("scope")).toBe( + "user-read-email playlist-read-private" + ) + }) +})