@better-auth/sso 1.4.8-beta.7 → 1.4.8
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/.turbo/turbo-build.log +5 -5
- package/dist/client.d.mts +1 -1
- package/dist/{index-DNWhGQW-.d.mts → index-ZWFEs7WQ.d.mts} +9 -7
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +7 -5
- package/package.json +4 -3
- package/src/index.ts +1 -1
- package/src/linking/org-assignment.ts +21 -3
- package/src/routes/sso.ts +20 -11
- package/src/types.ts +0 -6
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @better-auth/sso@1.4.8
|
|
2
|
+
> @better-auth/sso@1.4.8 build /home/runner/work/better-auth/better-auth/packages/sso
|
|
3
3
|
> tsdown
|
|
4
4
|
|
|
5
5
|
[34mℹ[39m tsdown [2mv0.17.2[22m powered by rolldown [2mv1.0.0-beta.53[22m
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
[34mℹ[39m entry: [34msrc/index.ts, src/client.ts[39m
|
|
8
8
|
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
9
9
|
[34mℹ[39m Build start
|
|
10
|
-
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m92.
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m92.73 kB[22m [2m│ gzip: 18.16 kB[22m
|
|
11
11
|
[34mℹ[39m [2mdist/[22m[1mclient.mjs[22m [2m 0.15 kB[22m [2m│ gzip: 0.14 kB[22m
|
|
12
12
|
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m 1.48 kB[22m [2m│ gzip: 0.51 kB[22m
|
|
13
13
|
[34mℹ[39m [2mdist/[22m[32m[1mclient.d.mts[22m[39m [2m 0.49 kB[22m [2m│ gzip: 0.30 kB[22m
|
|
14
|
-
[34mℹ[39m [2mdist/[22m[32mindex-
|
|
15
|
-
[34mℹ[39m 5 files, total: 137.
|
|
16
|
-
[32m✔[39m Build complete in [
|
|
14
|
+
[34mℹ[39m [2mdist/[22m[32mindex-ZWFEs7WQ.d.mts[39m [2m42.70 kB[22m [2m│ gzip: 8.67 kB[22m
|
|
15
|
+
[34mℹ[39m 5 files, total: 137.55 kB
|
|
16
|
+
[32m✔[39m Build complete in [32m15873ms[39m
|
package/dist/client.d.mts
CHANGED
|
@@ -257,13 +257,7 @@ interface SSOOptions {
|
|
|
257
257
|
*
|
|
258
258
|
* If you want to allow account linking for specific trusted providers, enable the `accountLinking` option in your auth config and specify those
|
|
259
259
|
* providers in the `trustedProviders` list.
|
|
260
|
-
*
|
|
261
260
|
* @default false
|
|
262
|
-
*
|
|
263
|
-
* @deprecated This option is discouraged for new projects. Relying on provider-level `email_verified` is a weaker
|
|
264
|
-
* trust signal compared to using `trustedProviders` in `accountLinking` or enabling `domainVerification` for SSO.
|
|
265
|
-
* Existing configurations will continue to work, but new integrations should use explicit trust mechanisms.
|
|
266
|
-
* This option may be removed in a future major version.
|
|
267
261
|
*/
|
|
268
262
|
trustEmailVerified?: boolean | undefined;
|
|
269
263
|
/**
|
|
@@ -776,9 +770,17 @@ declare const registerSSOProvider: <O extends SSOOptions>(options: O) => better_
|
|
|
776
770
|
}, O["domainVerification"] extends {
|
|
777
771
|
enabled: true;
|
|
778
772
|
} ? {
|
|
773
|
+
redirectURI: string;
|
|
774
|
+
oidcConfig: OIDCConfig | null;
|
|
775
|
+
samlConfig: SAMLConfig | null;
|
|
776
|
+
} & Omit<SSOProvider<O>, "oidcConfig" | "samlConfig"> & {
|
|
779
777
|
domainVerified: boolean;
|
|
780
778
|
domainVerificationToken: string;
|
|
781
|
-
}
|
|
779
|
+
} : {
|
|
780
|
+
redirectURI: string;
|
|
781
|
+
oidcConfig: OIDCConfig | null;
|
|
782
|
+
samlConfig: SAMLConfig | null;
|
|
783
|
+
} & Omit<SSOProvider<O>, "oidcConfig" | "samlConfig">>;
|
|
782
784
|
declare const signInSSO: (options?: SSOOptions) => better_call0.StrictEndpoint<"/sign-in/sso", {
|
|
783
785
|
method: "POST";
|
|
784
786
|
body: z.ZodObject<{
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as KeyEncryptionAlgorithm, C as SAMLConfig, D as DataEncryptionAlgorithm, E as AlgorithmValidationOptions, O as DeprecatedAlgorithmBehavior, S as OIDCConfig, T as SSOProvider, _ as REQUIRED_DISCOVERY_FIELDS, a as fetchDiscoveryDocument, b as TimestampValidationOptions, c as normalizeUrl, d as validateDiscoveryUrl, f as DiscoverOIDCConfigParams, g as OIDCDiscoveryDocument, h as HydratedOIDCConfig, i as discoverOIDCConfig, j as SignatureAlgorithm, k as DigestAlgorithm, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as SSOOptions, x as validateSAMLTimestamp, y as SAMLConditions } from "./index-
|
|
1
|
+
import { A as KeyEncryptionAlgorithm, C as SAMLConfig, D as DataEncryptionAlgorithm, E as AlgorithmValidationOptions, O as DeprecatedAlgorithmBehavior, S as OIDCConfig, T as SSOProvider, _ as REQUIRED_DISCOVERY_FIELDS, a as fetchDiscoveryDocument, b as TimestampValidationOptions, c as normalizeUrl, d as validateDiscoveryUrl, f as DiscoverOIDCConfigParams, g as OIDCDiscoveryDocument, h as HydratedOIDCConfig, i as discoverOIDCConfig, j as SignatureAlgorithm, k as DigestAlgorithm, l as selectTokenEndpointAuthMethod, m as DiscoveryErrorCode, n as sso, o as needsRuntimeDiscovery, p as DiscoveryError, r as computeDiscoveryUrl, s as normalizeDiscoveryUrls, t as SSOPlugin, u as validateDiscoveryDocument, v as RequiredDiscoveryField, w as SSOOptions, x as validateSAMLTimestamp, y as SAMLConditions } from "./index-ZWFEs7WQ.mjs";
|
|
2
2
|
export { AlgorithmValidationOptions, DataEncryptionAlgorithm, DeprecatedAlgorithmBehavior, DigestAlgorithm, DiscoverOIDCConfigParams, DiscoveryError, DiscoveryErrorCode, HydratedOIDCConfig, KeyEncryptionAlgorithm, OIDCConfig, OIDCDiscoveryDocument, REQUIRED_DISCOVERY_FIELDS, RequiredDiscoveryField, SAMLConditions, SAMLConfig, SSOOptions, SSOPlugin, SSOProvider, SignatureAlgorithm, TimestampValidationOptions, computeDiscoveryUrl, discoverOIDCConfig, fetchDiscoveryDocument, needsRuntimeDiscovery, normalizeDiscoveryUrls, normalizeUrl, selectTokenEndpointAuthMethod, sso, validateDiscoveryDocument, validateDiscoveryUrl, validateSAMLTimestamp };
|
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import * as saml from "samlify";
|
|
|
4
4
|
import { generateRandomString } from "better-auth/crypto";
|
|
5
5
|
import * as z$1 from "zod/v4";
|
|
6
6
|
import z from "zod/v4";
|
|
7
|
+
import { base64 } from "@better-auth/utils/base64";
|
|
7
8
|
import { BetterFetchError, betterFetch } from "@better-fetch/fetch";
|
|
8
9
|
import { HIDE_METADATA, createAuthorizationURL, generateState, parseState, validateAuthorizationCode, validateToken } from "better-auth";
|
|
9
10
|
import { setSessionCookie } from "better-auth/cookies";
|
|
@@ -1297,14 +1298,15 @@ const registerSSOProvider = (options) => {
|
|
|
1297
1298
|
}
|
|
1298
1299
|
});
|
|
1299
1300
|
}
|
|
1300
|
-
|
|
1301
|
+
const result = {
|
|
1301
1302
|
...provider,
|
|
1302
1303
|
oidcConfig: safeJsonParse(provider.oidcConfig),
|
|
1303
1304
|
samlConfig: safeJsonParse(provider.samlConfig),
|
|
1304
1305
|
redirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,
|
|
1305
1306
|
...options?.domainVerification?.enabled ? { domainVerified } : {},
|
|
1306
1307
|
...options?.domainVerification?.enabled ? { domainVerificationToken } : {}
|
|
1307
|
-
}
|
|
1308
|
+
};
|
|
1309
|
+
return ctx.json(result);
|
|
1308
1310
|
});
|
|
1309
1311
|
};
|
|
1310
1312
|
const signInSSOBodySchema = z.object({
|
|
@@ -1782,7 +1784,7 @@ const callbackSSOSAML = (options) => {
|
|
|
1782
1784
|
} catch (error) {
|
|
1783
1785
|
ctx.context.logger.error("SAML response validation failed", {
|
|
1784
1786
|
error,
|
|
1785
|
-
decodedResponse:
|
|
1787
|
+
decodedResponse: new TextDecoder().decode(base64.decode(SAMLResponse))
|
|
1786
1788
|
});
|
|
1787
1789
|
throw new APIError("BAD_REQUEST", {
|
|
1788
1790
|
message: "Invalid SAML response",
|
|
@@ -2016,7 +2018,7 @@ const acsEndpoint = (options) => {
|
|
|
2016
2018
|
} catch (error) {
|
|
2017
2019
|
ctx.context.logger.error("SAML response validation failed", {
|
|
2018
2020
|
error,
|
|
2019
|
-
decodedResponse:
|
|
2021
|
+
decodedResponse: new TextDecoder().decode(base64.decode(SAMLResponse))
|
|
2020
2022
|
});
|
|
2021
2023
|
throw new APIError("BAD_REQUEST", {
|
|
2022
2024
|
message: "Invalid SAML response",
|
|
@@ -2067,7 +2069,7 @@ const acsEndpoint = (options) => {
|
|
|
2067
2069
|
throw ctx.redirect(`${redirectUrl}?error=unsolicited_response&error_description=IdP-initiated+SSO+not+allowed`);
|
|
2068
2070
|
}
|
|
2069
2071
|
}
|
|
2070
|
-
const assertionIdAcs = extractAssertionId(
|
|
2072
|
+
const assertionIdAcs = extractAssertionId(new TextDecoder().decode(base64.decode(SAMLResponse)));
|
|
2071
2073
|
if (assertionIdAcs) {
|
|
2072
2074
|
const issuer = idp.entityMeta.getEntityID();
|
|
2073
2075
|
const conditions = extract.conditions;
|
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.8
|
|
4
|
+
"version": "1.4.8",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.mts",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
+
"@better-auth/utils": "0.3.0",
|
|
55
56
|
"@better-fetch/fetch": "1.1.21",
|
|
56
57
|
"fast-xml-parser": "^5.2.5",
|
|
57
58
|
"jose": "^6.1.0",
|
|
@@ -66,10 +67,10 @@
|
|
|
66
67
|
"express": "^5.1.0",
|
|
67
68
|
"oauth2-mock-server": "^8.2.0",
|
|
68
69
|
"tsdown": "^0.17.2",
|
|
69
|
-
"better-auth": "1.4.8
|
|
70
|
+
"better-auth": "1.4.8"
|
|
70
71
|
},
|
|
71
72
|
"peerDependencies": {
|
|
72
|
-
"better-auth": "1.4.8
|
|
73
|
+
"better-auth": "1.4.8"
|
|
73
74
|
},
|
|
74
75
|
"scripts": {
|
|
75
76
|
"test": "vitest",
|
package/src/index.ts
CHANGED
|
@@ -153,7 +153,7 @@ export function sso<O extends SSOOptions>(options?: O | undefined): any {
|
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
await assignOrganizationByDomain(ctx, {
|
|
156
|
+
await assignOrganizationByDomain(ctx as any, {
|
|
157
157
|
user: newSession.user,
|
|
158
158
|
provisioningOptions: options?.organizationProvisioning,
|
|
159
159
|
domainVerification: options?.domainVerification,
|
|
@@ -1,7 +1,25 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { OAuth2Tokens, User } from "better-auth";
|
|
2
2
|
import type { SSOOptions, SSOProvider } from "../types";
|
|
3
3
|
import type { NormalizedSSOProfile } from "./types";
|
|
4
4
|
|
|
5
|
+
interface EndpointContext {
|
|
6
|
+
context: {
|
|
7
|
+
options: {
|
|
8
|
+
plugins?: Array<{ id: string }>;
|
|
9
|
+
};
|
|
10
|
+
adapter: {
|
|
11
|
+
findOne: <T>(options: {
|
|
12
|
+
model: string;
|
|
13
|
+
where: Array<{ field: string; value: unknown }>;
|
|
14
|
+
}) => Promise<T | null>;
|
|
15
|
+
create: (options: {
|
|
16
|
+
model: string;
|
|
17
|
+
data: Record<string, unknown>;
|
|
18
|
+
}) => Promise<unknown>;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
5
23
|
export interface OrganizationProvisioningOptions {
|
|
6
24
|
disabled?: boolean;
|
|
7
25
|
defaultRole?: "member" | "admin";
|
|
@@ -26,7 +44,7 @@ export interface AssignOrganizationFromProviderOptions {
|
|
|
26
44
|
* Used in SSO flows (OIDC, SAML) where the provider is already linked to an org.
|
|
27
45
|
*/
|
|
28
46
|
export async function assignOrganizationFromProvider(
|
|
29
|
-
ctx:
|
|
47
|
+
ctx: EndpointContext,
|
|
30
48
|
options: AssignOrganizationFromProviderOptions,
|
|
31
49
|
): Promise<void> {
|
|
32
50
|
const { user, profile, provider, token, provisioningOptions } = options;
|
|
@@ -96,7 +114,7 @@ export interface AssignOrganizationByDomainOptions {
|
|
|
96
114
|
* (e.g., Google OAuth with @acme.com email gets added to Acme's org).
|
|
97
115
|
*/
|
|
98
116
|
export async function assignOrganizationByDomain(
|
|
99
|
-
ctx:
|
|
117
|
+
ctx: EndpointContext,
|
|
100
118
|
options: AssignOrganizationByDomainOptions,
|
|
101
119
|
): Promise<void> {
|
|
102
120
|
const { user, provisioningOptions, domainVerification } = options;
|
package/src/routes/sso.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { base64 } from "@better-auth/utils/base64";
|
|
1
2
|
import { BetterFetchError, betterFetch } from "@better-fetch/fetch";
|
|
2
3
|
import type { User, Verification } from "better-auth";
|
|
3
4
|
import {
|
|
@@ -836,14 +837,20 @@ export const registerSSOProvider = <O extends SSOOptions>(options: O) => {
|
|
|
836
837
|
});
|
|
837
838
|
}
|
|
838
839
|
|
|
840
|
+
type SSOProviderResponse = {
|
|
841
|
+
redirectURI: string;
|
|
842
|
+
oidcConfig: OIDCConfig | null;
|
|
843
|
+
samlConfig: SAMLConfig | null;
|
|
844
|
+
} & Omit<SSOProvider<O>, "oidcConfig" | "samlConfig">;
|
|
845
|
+
|
|
839
846
|
type SSOProviderReturn = O["domainVerification"] extends { enabled: true }
|
|
840
|
-
? {
|
|
847
|
+
? SSOProviderResponse & {
|
|
841
848
|
domainVerified: boolean;
|
|
842
849
|
domainVerificationToken: string;
|
|
843
|
-
}
|
|
844
|
-
:
|
|
850
|
+
}
|
|
851
|
+
: SSOProviderResponse;
|
|
845
852
|
|
|
846
|
-
|
|
853
|
+
const result = {
|
|
847
854
|
...provider,
|
|
848
855
|
oidcConfig: safeJsonParse<OIDCConfig>(
|
|
849
856
|
provider.oidcConfig as unknown as string,
|
|
@@ -856,7 +863,9 @@ export const registerSSOProvider = <O extends SSOOptions>(options: O) => {
|
|
|
856
863
|
...(options?.domainVerification?.enabled
|
|
857
864
|
? { domainVerificationToken }
|
|
858
865
|
: {}),
|
|
859
|
-
}
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
return ctx.json(result as SSOProviderReturn);
|
|
860
869
|
},
|
|
861
870
|
);
|
|
862
871
|
};
|
|
@@ -1807,8 +1816,8 @@ export const callbackSSOSAML = (options?: SSOOptions) => {
|
|
|
1807
1816
|
} catch (error) {
|
|
1808
1817
|
ctx.context.logger.error("SAML response validation failed", {
|
|
1809
1818
|
error,
|
|
1810
|
-
decodedResponse:
|
|
1811
|
-
|
|
1819
|
+
decodedResponse: new TextDecoder().decode(
|
|
1820
|
+
base64.decode(SAMLResponse),
|
|
1812
1821
|
),
|
|
1813
1822
|
});
|
|
1814
1823
|
throw new APIError("BAD_REQUEST", {
|
|
@@ -2237,8 +2246,8 @@ export const acsEndpoint = (options?: SSOOptions) => {
|
|
|
2237
2246
|
} catch (error) {
|
|
2238
2247
|
ctx.context.logger.error("SAML response validation failed", {
|
|
2239
2248
|
error,
|
|
2240
|
-
decodedResponse:
|
|
2241
|
-
|
|
2249
|
+
decodedResponse: new TextDecoder().decode(
|
|
2250
|
+
base64.decode(SAMLResponse),
|
|
2242
2251
|
),
|
|
2243
2252
|
});
|
|
2244
2253
|
throw new APIError("BAD_REQUEST", {
|
|
@@ -2334,8 +2343,8 @@ export const acsEndpoint = (options?: SSOOptions) => {
|
|
|
2334
2343
|
}
|
|
2335
2344
|
|
|
2336
2345
|
// Assertion Replay Protection
|
|
2337
|
-
const samlContentAcs =
|
|
2338
|
-
|
|
2346
|
+
const samlContentAcs = new TextDecoder().decode(
|
|
2347
|
+
base64.decode(SAMLResponse),
|
|
2339
2348
|
);
|
|
2340
2349
|
const assertionIdAcs = extractAssertionId(samlContentAcs);
|
|
2341
2350
|
|
package/src/types.ts
CHANGED
|
@@ -231,13 +231,7 @@ export interface SSOOptions {
|
|
|
231
231
|
*
|
|
232
232
|
* If you want to allow account linking for specific trusted providers, enable the `accountLinking` option in your auth config and specify those
|
|
233
233
|
* providers in the `trustedProviders` list.
|
|
234
|
-
*
|
|
235
234
|
* @default false
|
|
236
|
-
*
|
|
237
|
-
* @deprecated This option is discouraged for new projects. Relying on provider-level `email_verified` is a weaker
|
|
238
|
-
* trust signal compared to using `trustedProviders` in `accountLinking` or enabling `domainVerification` for SSO.
|
|
239
|
-
* Existing configurations will continue to work, but new integrations should use explicit trust mechanisms.
|
|
240
|
-
* This option may be removed in a future major version.
|
|
241
235
|
*/
|
|
242
236
|
trustEmailVerified?: boolean | undefined;
|
|
243
237
|
/**
|