@better-auth/oauth-provider 1.7.0-beta.4 → 1.7.0-beta.6

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.
@@ -0,0 +1,63 @@
1
+ import { isAPIError } from "better-auth/api";
2
+ import { APIError as APIError$1 } from "better-call";
3
+ import { DPOP_SIGNING_ALGORITHMS } from "better-auth/oauth2";
4
+ //#region src/resource-challenge.ts
5
+ const DPOP_CHALLENGE_ERRORS = new Set(["invalid_dpop_proof"]);
6
+ function quoteAuthParam(value) {
7
+ return value.replace(/[\r\n]+/g, " ").replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
8
+ }
9
+ function extractDpopError(error) {
10
+ const body = error.body;
11
+ return {
12
+ errorCode: typeof body?.error === "string" ? body.error : void 0,
13
+ description: typeof body?.error_description === "string" ? body.error_description : typeof body?.message === "string" ? body.message : error.message
14
+ };
15
+ }
16
+ function isDpopChallengeError(error) {
17
+ const { errorCode, description } = extractDpopError(error);
18
+ return !!errorCode && (DPOP_CHALLENGE_ERRORS.has(errorCode) || errorCode === "invalid_token" && description.includes("DPoP"));
19
+ }
20
+ function buildDpopChallenge(error, opts) {
21
+ const { errorCode, description } = extractDpopError(error);
22
+ const algorithms = opts?.dpopSigningAlgorithms ?? DPOP_SIGNING_ALGORITHMS;
23
+ return [
24
+ `DPoP error="${quoteAuthParam(errorCode ?? "invalid_dpop_proof")}"`,
25
+ `error_description="${quoteAuthParam(description)}"`,
26
+ `algs="${quoteAuthParam(algorithms.join(" "))}"`
27
+ ].join(", ");
28
+ }
29
+ /**
30
+ * Raise an OAuth resource-server challenge for a failed access-token request.
31
+ *
32
+ * Missing/invalid bearer credentials are reported with RFC 6750 plus the RFC
33
+ * 9728 `resource_metadata` pointer. DPoP-bound-token failures are reported with
34
+ * RFC 9449's `DPoP` challenge so clients know which proof algorithms to use.
35
+ * Non-URL resources (for example a `urn:` or a client id) resolve their
36
+ * metadata URL through `resourceMetadataMappings`.
37
+ *
38
+ * @internal
39
+ */
40
+ function raiseResourceServerChallenge(error, resource, opts) {
41
+ if (isAPIError(error) && error.status === "UNAUTHORIZED") {
42
+ if (isDpopChallengeError(error)) throw new APIError$1("UNAUTHORIZED", { message: error.message }, { "WWW-Authenticate": buildDpopChallenge(error, opts) });
43
+ const wwwAuthenticateValue = (Array.isArray(resource) ? resource : [resource]).map((value) => {
44
+ const url = URL.canParse?.(value) ? new URL(value) : null;
45
+ if (url && url.origin !== "null") {
46
+ const resourcePath = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
47
+ let challenge = `Bearer resource_metadata="${url.origin}/.well-known/oauth-protected-resource${resourcePath}${url.search}"`;
48
+ if (opts?.scope) challenge += `, scope="${quoteAuthParam(opts.scope)}"`;
49
+ return challenge;
50
+ }
51
+ const resourceMetadata = opts?.resourceMetadataMappings?.[value];
52
+ if (!resourceMetadata) throw new APIError$1("INTERNAL_SERVER_ERROR", { message: `missing resource_metadata mapping for ${value}` });
53
+ let challenge = `Bearer resource_metadata="${resourceMetadata}"`;
54
+ if (opts?.scope) challenge += `, scope="${quoteAuthParam(opts.scope)}"`;
55
+ return challenge;
56
+ }).join(", ");
57
+ throw new APIError$1("UNAUTHORIZED", { message: error.message }, { "WWW-Authenticate": wwwAuthenticateValue });
58
+ }
59
+ if (error instanceof Error) throw error;
60
+ throw new Error(error);
61
+ }
62
+ //#endregion
63
+ export { raiseResourceServerChallenge as t };
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };
@@ -0,0 +1,44 @@
1
+ //#region src/signed-query.ts
2
+ const signedQueryIssuedAtParam = "ba_iat";
3
+ const postLoginClearedParam = "ba_pl";
4
+ const signedQueryParameterNameParam = "ba_param";
5
+ function canonicalizeOAuthQueryParams(params) {
6
+ const canonicalParams = new URLSearchParams();
7
+ const entries = [...params.entries()].sort(([keyA, valueA], [keyB, valueB]) => {
8
+ if (keyA < keyB) return -1;
9
+ if (keyA > keyB) return 1;
10
+ if (valueA < valueB) return -1;
11
+ if (valueA > valueB) return 1;
12
+ return 0;
13
+ });
14
+ for (const [key, value] of entries) canonicalParams.append(key, value);
15
+ return canonicalParams;
16
+ }
17
+ function setSignedOAuthQueryParameterNames(params) {
18
+ params.delete(signedQueryParameterNameParam);
19
+ const signedParameterNames = [...new Set([...params.keys(), signedQueryParameterNameParam])].sort();
20
+ for (const parameterName of signedParameterNames) params.append(signedQueryParameterNameParam, parameterName);
21
+ }
22
+ function getSignedOAuthQueryParameterNames(params) {
23
+ const signedParameterNames = params.getAll(signedQueryParameterNameParam);
24
+ if (!signedParameterNames.length) return;
25
+ return new Set(signedParameterNames);
26
+ }
27
+ function buildSignedOAuthQuery(search) {
28
+ const params = new URLSearchParams(search);
29
+ if (!params.has("sig")) return;
30
+ const signedParameterNames = getSignedOAuthQueryParameterNames(params);
31
+ if (!signedParameterNames) return;
32
+ const signedParams = new URLSearchParams();
33
+ for (const [key, value] of params.entries()) if (key === "sig" || key === signedQueryParameterNameParam || signedParameterNames.has(key)) signedParams.append(key, value);
34
+ return signedParams.toString();
35
+ }
36
+ function getSignedQueryIssuedAt(oauthQuery) {
37
+ const raw = new URLSearchParams(oauthQuery).get(signedQueryIssuedAtParam);
38
+ if (!raw) return null;
39
+ const issuedAt = Number(raw);
40
+ if (!Number.isFinite(issuedAt) || issuedAt <= 0) return null;
41
+ return new Date(issuedAt);
42
+ }
43
+ //#endregion
44
+ export { setSignedOAuthQueryParameterNames as a, postLoginClearedParam as i, canonicalizeOAuthQueryParams as n, signedQueryIssuedAtParam as o, getSignedQueryIssuedAt as r, buildSignedOAuthQuery as t };