@better-auth/sso 1.4.18 → 1.4.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@better-auth/sso",
3
3
  "author": "Bereket Engida",
4
- "version": "1.4.18",
4
+ "version": "1.4.20",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
7
7
  "types": "dist/index.d.mts",
@@ -67,11 +67,12 @@
67
67
  "express": "^5.1.0",
68
68
  "oauth2-mock-server": "^8.2.0",
69
69
  "tsdown": "^0.17.2",
70
- "better-auth": "1.4.18"
70
+ "better-auth": "1.4.20"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@better-auth/utils": "0.3.0",
74
- "better-auth": "1.4.18"
74
+ "better-call": "1.1.8",
75
+ "better-auth": "1.4.20"
75
76
  },
76
77
  "scripts": {
77
78
  "test": "vitest",
@@ -286,7 +286,7 @@ describe("Domain verification", async () => {
286
286
 
287
287
  dnsMock.resolveTxt.mockResolvedValue([
288
288
  [
289
- `better-auth-token-saml-provider-1=${provider.domainVerificationToken}`,
289
+ `_better-auth-token-saml-provider-1=${provider.domainVerificationToken}`,
290
290
  ],
291
291
  ]);
292
292
 
@@ -471,7 +471,7 @@ describe("Domain verification", async () => {
471
471
  "v=spf1 ip4:50.242.118.232/29 include:_spf.google.com include:mail.zendesk.com ~all",
472
472
  ],
473
473
  [
474
- `better-auth-token-saml-provider-1=${provider.domainVerificationToken}`,
474
+ `_better-auth-token-saml-provider-1=${provider.domainVerificationToken}`,
475
475
  ],
476
476
  ]);
477
477
 
@@ -484,6 +484,9 @@ describe("Domain verification", async () => {
484
484
  });
485
485
 
486
486
  expect(response.status).toBe(204);
487
+ expect(dnsMock.resolveTxt).toHaveBeenCalledWith(
488
+ "_better-auth-token-saml-provider-1.hello.com",
489
+ );
487
490
  });
488
491
 
489
492
  it("should verify a provider domain ownership (custom token verification prefix)", async () => {
@@ -498,7 +501,7 @@ describe("Domain verification", async () => {
498
501
  [
499
502
  "v=spf1 ip4:50.242.118.232/29 include:_spf.google.com include:mail.zendesk.com ~all",
500
503
  ],
501
- [`auth-prefix-saml-provider-1=${provider.domainVerificationToken}`],
504
+ [`_auth-prefix-saml-provider-1=${provider.domainVerificationToken}`],
502
505
  ]);
503
506
 
504
507
  const response = await auth.api.verifyDomain({
@@ -510,6 +513,45 @@ describe("Domain verification", async () => {
510
513
  });
511
514
 
512
515
  expect(response.status).toBe(204);
516
+ expect(dnsMock.resolveTxt).toHaveBeenCalledWith(
517
+ "_auth-prefix-saml-provider-1.hello.com",
518
+ );
519
+ });
520
+
521
+ it("should return bad request when provider ID exceeds DNS label limit", async () => {
522
+ const longProviderId = "a".repeat(50);
523
+ const { auth, getAuthHeaders } = createTestAuth();
524
+ const headers = await getAuthHeaders(testUser);
525
+
526
+ await auth.api.registerSSOProvider({
527
+ body: {
528
+ providerId: longProviderId,
529
+ issuer: "http://hello.com:8081",
530
+ domain: "http://hello.com:8081",
531
+ samlConfig: {
532
+ entryPoint: "http://idp.com:",
533
+ cert: "the-cert",
534
+ callbackUrl: "http://hello.com:8081/api/sso/saml2/callback",
535
+ spMetadata: {},
536
+ },
537
+ },
538
+ headers,
539
+ });
540
+
541
+ const response = await auth.api.verifyDomain({
542
+ body: {
543
+ providerId: longProviderId,
544
+ },
545
+ headers,
546
+ asResponse: true,
547
+ });
548
+
549
+ expect(response.status).toBe(400);
550
+ expect(await response.json()).toEqual({
551
+ message:
552
+ "Verification identifier exceeds the DNS label limit of 63 characters",
553
+ code: "IDENTIFIER_TOO_LONG",
554
+ });
513
555
  });
514
556
 
515
557
  it("should fail to verify an already verified domain", async () => {
@@ -519,7 +561,7 @@ describe("Domain verification", async () => {
519
561
 
520
562
  dnsMock.resolveTxt.mockResolvedValue([
521
563
  [
522
- `better-auth-token-saml-provider-1=${provider.domainVerificationToken}`,
564
+ `_better-auth-token-saml-provider-1=${provider.domainVerificationToken}`,
523
565
  ],
524
566
  ]);
525
567
 
@@ -5,13 +5,13 @@ import type { NormalizedSSOProfile } from "./types";
5
5
 
6
6
  export interface OrganizationProvisioningOptions {
7
7
  disabled?: boolean;
8
- defaultRole?: "member" | "admin";
8
+ defaultRole?: string;
9
9
  getRole?: (data: {
10
10
  user: User & Record<string, any>;
11
11
  userInfo: Record<string, any>;
12
12
  token?: OAuth2Tokens;
13
13
  provider: SSOProvider<SSOOptions>;
14
- }) => Promise<"member" | "admin">;
14
+ }) => Promise<string>;
15
15
  }
16
16
 
17
17
  export interface AssignOrganizationFromProviderOptions {
package/src/oidc.test.ts CHANGED
@@ -393,9 +393,7 @@ describe("SSO disable implicit sign in", async () => {
393
393
  "redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Ftest",
394
394
  );
395
395
  const { callbackURL } = await simulateOAuthFlow(res.url, headers);
396
- expect(callbackURL).toContain(
397
- "/api/auth/error/error?error=signup disabled",
398
- );
396
+ expect(callbackURL).toContain("/api/auth/error?error=signup disabled");
399
397
  });
400
398
 
401
399
  it("should create user with SSO provider when sign ups are disabled but sign up is requested", async () => {
@@ -8,10 +8,22 @@ import { generateRandomString } from "better-auth/crypto";
8
8
  import * as z from "zod/v4";
9
9
  import type { SSOOptions, SSOProvider } from "../types";
10
10
 
11
+ const DNS_LABEL_MAX_LENGTH = 63;
12
+ const DEFAULT_TOKEN_PREFIX = "better-auth-token";
13
+
11
14
  const domainVerificationBodySchema = z.object({
12
15
  providerId: z.string(),
13
16
  });
14
17
 
18
+ export function getVerificationIdentifier(
19
+ options: SSOOptions,
20
+ providerId: string,
21
+ ): string {
22
+ const tokenPrefix =
23
+ options.domainVerification?.tokenPrefix || DEFAULT_TOKEN_PREFIX;
24
+ return `_${tokenPrefix}-${providerId}`;
25
+ }
26
+
15
27
  export const requestDomainVerification = (options: SSOOptions) => {
16
28
  return createAuthEndpoint(
17
29
  "/sso/request-domain-verification",
@@ -83,15 +95,18 @@ export const requestDomainVerification = (options: SSOOptions) => {
83
95
  });
84
96
  }
85
97
 
98
+ const identifier = getVerificationIdentifier(
99
+ options,
100
+ provider.providerId,
101
+ );
102
+
86
103
  const activeVerification =
87
104
  await ctx.context.adapter.findOne<Verification>({
88
105
  model: "verification",
89
106
  where: [
90
107
  {
91
108
  field: "identifier",
92
- value: options.domainVerification?.tokenPrefix
93
- ? `${options.domainVerification?.tokenPrefix}-${provider.providerId}`
94
- : `better-auth-token-${provider.providerId}`,
109
+ value: identifier,
95
110
  },
96
111
  { field: "expiresAt", value: new Date(), operator: "gt" },
97
112
  ],
@@ -106,9 +121,7 @@ export const requestDomainVerification = (options: SSOOptions) => {
106
121
  await ctx.context.adapter.create<Verification>({
107
122
  model: "verification",
108
123
  data: {
109
- identifier: options.domainVerification?.tokenPrefix
110
- ? `${options.domainVerification?.tokenPrefix}-${provider.providerId}`
111
- : `better-auth-token-${provider.providerId}`,
124
+ identifier,
112
125
  createdAt: new Date(),
113
126
  updatedAt: new Date(),
114
127
  value: domainVerificationToken,
@@ -199,15 +212,25 @@ export const verifyDomain = (options: SSOOptions) => {
199
212
  });
200
213
  }
201
214
 
215
+ const identifier = getVerificationIdentifier(
216
+ options,
217
+ provider.providerId,
218
+ );
219
+
220
+ if (identifier.length > DNS_LABEL_MAX_LENGTH) {
221
+ throw new APIError("BAD_REQUEST", {
222
+ message: `Verification identifier exceeds the DNS label limit of ${DNS_LABEL_MAX_LENGTH} characters`,
223
+ code: "IDENTIFIER_TOO_LONG",
224
+ });
225
+ }
226
+
202
227
  const activeVerification =
203
228
  await ctx.context.adapter.findOne<Verification>({
204
229
  model: "verification",
205
230
  where: [
206
231
  {
207
232
  field: "identifier",
208
- value: options.domainVerification?.tokenPrefix
209
- ? `${options.domainVerification?.tokenPrefix}-${provider.providerId}`
210
- : `better-auth-token-${provider.providerId}`,
233
+ value: identifier,
211
234
  },
212
235
  { field: "expiresAt", value: new Date(), operator: "gt" },
213
236
  ],
@@ -237,9 +260,8 @@ export const verifyDomain = (options: SSOOptions) => {
237
260
  }
238
261
 
239
262
  try {
240
- const dnsRecords = await dns.resolveTxt(
241
- new URL(provider.domain).hostname,
242
- );
263
+ const hostname = new URL(provider.domain).hostname;
264
+ const dnsRecords = await dns.resolveTxt(`${identifier}.${hostname}`);
243
265
  records = dnsRecords.flat();
244
266
  } catch (error) {
245
267
  ctx.context.logger.warn(