@better-auth/oauth-provider 1.7.0-beta.9 → 1.7.0-rc.1

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.
@@ -1,4 +1,4 @@
1
- import { c as ResourceServerMetadata } from "./oauth-Di-k6QeJ.mjs";
1
+ import { c as ResourceServerMetadata } from "./oauth-ScTJEcFV.mjs";
2
2
  import { ResourceRequestInput, VerifyAccessTokenRequestOptions } from "better-auth/oauth2";
3
3
  import { JWTPayload, JWTVerifyOptions } from "jose";
4
4
  import { BetterAuthOptions } from "better-auth/types";
@@ -1,5 +1,5 @@
1
1
  import { o as getJwtPlugin, s as getOAuthProviderPlugin } from "./utils-DO8lmoDw.mjs";
2
- import { t as PACKAGE_VERSION } from "./version-M87fotrf.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version--aseHtsu.mjs";
3
3
  import { t as raiseResourceServerChallenge } from "./resource-challenge-B-cqv4ur.mjs";
4
4
  import { APIError } from "better-call";
5
5
  import { logger } from "@better-auth/core/env";
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { r as oauthProvider } from "./oauth-C7baUP-L.mjs";
1
+ import { r as oauthProvider } from "./oauth-BrNRbP2A.mjs";
2
2
  import * as _better_fetch_fetch0 from "@better-fetch/fetch";
3
3
 
4
4
  //#region src/client.d.ts
package/dist/client.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as buildSignedOAuthQuery } from "./signed-query-Df1MNiSH.mjs";
2
- import { t as PACKAGE_VERSION } from "./version-M87fotrf.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version--aseHtsu.mjs";
3
3
  import { safeJSONParse } from "@better-auth/core/utils/json";
4
4
  //#region src/client.ts
5
5
  const oauthProviderClient = () => {
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as OAuthProviderExtension, B as StoreTokenType, C as OAuthConsent, D as OAuthOpaqueAccessToken, E as OAuthMetadataExtensionInput, F as OAuthTokenResponse, H as VerificationValue, I as OAuthUserInfoExtensionInput, L as Prompt, M as OAuthResource, N as OAuthResourceInput, O as OAuthOptions, P as OAuthTokenIssueParams, R as SchemaClient, S as OAuthClientResource, T as OAuthExtensionGrantHandlerInput, U as ClientRegistrationRequest, V as StoredAuthorizationQuery, W as ResourceUriSchema, _ as OAuthClaimExtensionInput, a as GrantType, b as OAuthClientAuthenticationResult, c as ResourceServerMetadata, d as ActiveAccessTokenPayload, f as AuthorizePrompt, g as OAuthAuthorizationQuery, h as OAuthAuthenticatedClient, i as Confirmation, j as OAuthRefreshToken, k as OAuthProviderApi, l as TokenEndpointAuthMethod, m as InitialAccessTokenAuthorization, n as AuthServerMetadata, o as OAuthClient, p as ClientDiscovery, r as BearerMethodsSupported, s as OIDCMetadata, t as AuthMethod, u as TokenType, v as OAuthClientAuthenticationInput, w as OAuthExtensionGrantHandler, x as OAuthClientAuthenticationStrategy, y as OAuthClientAuthenticationRequest, z as Scope } from "./oauth-Di-k6QeJ.mjs";
2
- import { a as OAuthEndpointErrorResult, c as OAuthFieldErrorCode, i as getIssuer, l as OAuthFieldErrorCodeMap, n as getOAuthProviderState, o as OAuthEndpointRedirectContext, r as oauthProvider, s as OAuthErrorCode, t as DEFAULT_OAUTH_SCOPES, u as OAuthRedirectOnError } from "./oauth-C7baUP-L.mjs";
1
+ import { A as OAuthProviderExtension, B as StoreTokenType, C as OAuthConsent, D as OAuthOpaqueAccessToken, E as OAuthMetadataExtensionInput, F as OAuthTokenResponse, H as VerificationValue, I as OAuthUserInfoExtensionInput, L as Prompt, M as OAuthResource, N as OAuthResourceInput, O as OAuthOptions, P as OAuthTokenIssueParams, R as SchemaClient, S as OAuthClientResource, T as OAuthExtensionGrantHandlerInput, U as ClientRegistrationRequest, V as StoredAuthorizationQuery, W as ResourceUriSchema, _ as OAuthClaimExtensionInput, a as GrantType, b as OAuthClientAuthenticationResult, c as ResourceServerMetadata, d as ActiveAccessTokenPayload, f as AuthorizePrompt, g as OAuthAuthorizationQuery, h as OAuthAuthenticatedClient, i as Confirmation, j as OAuthRefreshToken, k as OAuthProviderApi, l as TokenEndpointAuthMethod, m as InitialAccessTokenAuthorization, n as AuthServerMetadata, o as OAuthClient, p as ClientDiscovery, r as BearerMethodsSupported, s as OIDCMetadata, t as AuthMethod, u as TokenType, v as OAuthClientAuthenticationInput, w as OAuthExtensionGrantHandler, x as OAuthClientAuthenticationStrategy, y as OAuthClientAuthenticationRequest, z as Scope } from "./oauth-ScTJEcFV.mjs";
2
+ import { a as OAuthEndpointErrorResult, c as OAuthFieldErrorCode, i as getIssuer, l as OAuthFieldErrorCodeMap, n as getOAuthProviderState, o as OAuthEndpointRedirectContext, r as oauthProvider, s as OAuthErrorCode, t as DEFAULT_OAUTH_SCOPES, u as OAuthRedirectOnError } from "./oauth-BrNRbP2A.mjs";
3
3
  import { getSessionFromCtx } from "better-auth/api";
4
4
  import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
5
5
  import { AuthContext, GenericEndpointContext } from "@better-auth/core";
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { C as STANDARD_CLAIM_NAMES, D as getRequestedUserInfoClaims, E as filterClaimsRequestUserInfoClaims, S as STANDARD_CLAIMS, T as claimsRequestParameterSchema, _ as invalidateResourceCache, a as invalidateRefreshFamily, b as resolveResourcePolicy, c as ResourceUriSchema, d as clientRegistrationRequestSchema, f as JWS_ALGORITHMS, g as getResource, h as extractRepeatedResourceFromForm, i as getOAuthProviderApi, l as SafeUrlSchema, m as buildClientResourceLinkId, o as tokenEndpoint, p as assertIdentifierValid, r as decodeRefreshToken, s as userInfoEndpoint, t as introspectEndpoint, u as authorizationQuerySchema, v as isAudienceClaimAllowed, w as getSupportedClaims, x as seedResources, y as logEnforcePerClientResourcesResolution } from "./introspect-Bzbar3iY.mjs";
1
+ import { C as STANDARD_CLAIM_NAMES, D as getRequestedUserInfoClaims, E as filterClaimsRequestUserInfoClaims, S as STANDARD_CLAIMS, T as claimsRequestParameterSchema, _ as invalidateResourceCache, a as invalidateRefreshFamily, b as resolveResourcePolicy, c as ResourceUriSchema, d as clientRegistrationRequestSchema, f as JWS_ALGORITHMS, g as getResource, h as extractRepeatedResourceFromForm, i as getOAuthProviderApi, l as SafeUrlSchema, m as buildClientResourceLinkId, o as tokenEndpoint, p as assertIdentifierValid, r as decodeRefreshToken, s as userInfoEndpoint, t as introspectEndpoint, u as authorizationQuerySchema, v as isAudienceClaimAllowed, w as getSupportedClaims, x as seedResources, y as logEnforcePerClientResourcesResolution } from "./introspect-DvHp2a64.mjs";
2
2
  import { D as applyOAuthProviderMetadataExtensions, E as verifyOAuthQueryParams, F as getSupportedGrantTypes, L as isExtensionTokenEndpointAuthMethod, M as getClientDiscoveries, P as getSupportedAuthMethods, R as validateOAuthProviderExtensions, S as storeToken, T as validateClientCredentials, _ as removePromptFromQuery, a as getClient, b as searchParamsToQuery, c as getStoredToken, d as mergeDiscoveryMetadata, g as removeMaxAgeFromQuery, h as parsePrompt, i as extractClientCredentials, j as extendOAuthProvider, l as isPKCERequired, m as parseClientMetadata, n as decryptStoredClientSecret, o as getJwtPlugin, p as parseBearerToken, r as destructureCredentials, t as clientAllowsGrant, u as isSessionFreshForSignedQuery, w as toResourceList, x as storeClientSecret, y as resolveSubjectIdentifier } from "./utils-DO8lmoDw.mjs";
3
3
  import { a as setSignedOAuthQueryParameterNames, i as postLoginClearedParam, n as canonicalizeOAuthQueryParams, o as signedQueryIssuedAtParam, r as getSignedQueryIssuedAt } from "./signed-query-Df1MNiSH.mjs";
4
4
  import { n as consumeClientAssertion, r as isPrivateHostname } from "./client-assertion-D-tAYsKC.mjs";
5
- import { t as PACKAGE_VERSION } from "./version-M87fotrf.mjs";
5
+ import { t as PACKAGE_VERSION } from "./version--aseHtsu.mjs";
6
6
  import { t as raiseResourceServerChallenge } from "./resource-challenge-B-cqv4ur.mjs";
7
7
  import { isBrowserFetchRequest } from "@better-auth/core/utils/fetch-metadata";
8
8
  import { isLoopbackHost, isLoopbackIP } from "@better-auth/core/utils/host";
@@ -2829,6 +2829,18 @@ const schema = {
2829
2829
  type: "date",
2830
2830
  required: false
2831
2831
  },
2832
+ rotatedAt: {
2833
+ type: "date",
2834
+ required: false
2835
+ },
2836
+ rotationReplayResponse: {
2837
+ type: "string",
2838
+ required: false
2839
+ },
2840
+ rotationReplayExpiresAt: {
2841
+ type: "date",
2842
+ required: false
2843
+ },
2832
2844
  authTime: {
2833
2845
  type: "date",
2834
2846
  required: false
@@ -3023,6 +3035,7 @@ const oauthProvider = (options) => {
3023
3035
  accessTokenExpiresIn: 3600,
3024
3036
  m2mAccessTokenExpiresIn: 3600,
3025
3037
  refreshTokenExpiresIn: 2592e3,
3038
+ refreshTokenReuseInterval: 0,
3026
3039
  allowUnauthenticatedClientRegistration: false,
3027
3040
  allowDynamicClientRegistration: false,
3028
3041
  disableJwtPlugin: false,
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-wcPFST8Q.mjs";
2
2
  import { A as collectExtensionUserInfoClaims, C as toAudienceClaim, F as getSupportedGrantTypes, I as hasUserInfoClaimExtension, N as getExtensionGrantHandler, O as collectExtensionAccessTokenClaims, S as storeToken, T as validateClientCredentials, a as getClient, c as getStoredToken, f as normalizeTimestampValue, i as extractClientCredentials, k as collectExtensionIdTokenClaims, l as isPKCERequired, m as parseClientMetadata, n as decryptStoredClientSecret, o as getJwtPlugin, r as destructureCredentials, t as clientAllowsGrant, v as resolveSessionAuthTime, w as toResourceList, y as resolveSubjectIdentifier } from "./utils-DO8lmoDw.mjs";
3
3
  import { APIError } from "better-auth/api";
4
- import { generateRandomString } from "better-auth/crypto";
4
+ import { generateRandomString, symmetricDecrypt, symmetricEncrypt } from "better-auth/crypto";
5
5
  import { APIError as APIError$1 } from "better-call";
6
6
  import { logger } from "@better-auth/core/env";
7
7
  import * as z from "zod";
@@ -1524,6 +1524,13 @@ async function createRefreshToken(ctx, opts, user, referenceId, authorizationCod
1524
1524
  })).id,
1525
1525
  token: await encodeRefreshToken(opts, token, sessionId)
1526
1526
  };
1527
+ const rotatedAt = /* @__PURE__ */ new Date(iat * 1e3);
1528
+ const rotationUpdate = {
1529
+ revoked: rotatedAt,
1530
+ rotatedAt
1531
+ };
1532
+ const reuseInterval = opts.refreshTokenReuseInterval ?? 0;
1533
+ if (reuseInterval > 0) rotationUpdate.rotationReplayExpiresAt = /* @__PURE__ */ new Date((iat + reuseInterval) * 1e3);
1527
1534
  if (!await ctx.context.adapter.incrementOne({
1528
1535
  model: "oauthRefreshToken",
1529
1536
  where: [{
@@ -1535,7 +1542,7 @@ async function createRefreshToken(ctx, opts, user, referenceId, authorizationCod
1535
1542
  value: null
1536
1543
  }],
1537
1544
  increment: {},
1538
- set: { revoked: /* @__PURE__ */ new Date(iat * 1e3) }
1545
+ set: rotationUpdate
1539
1546
  })) throw new APIError("BAD_REQUEST", {
1540
1547
  error_description: "invalid refresh token",
1541
1548
  error: "invalid_grant"
@@ -1604,6 +1611,126 @@ async function resolveDpopTokenBinding(ctx, opts, params) {
1604
1611
  throw error;
1605
1612
  }
1606
1613
  }
1614
+ function normalizeReplayValues(values) {
1615
+ return values ? [...new Set(values)].sort() : void 0;
1616
+ }
1617
+ function sameReplayValues(left, right) {
1618
+ const normalizedLeft = normalizeReplayValues(left);
1619
+ const normalizedRight = normalizeReplayValues(right);
1620
+ if (normalizedLeft === void 0 || normalizedRight === void 0) return normalizedLeft === normalizedRight;
1621
+ if (normalizedLeft.length !== normalizedRight.length) return false;
1622
+ return normalizedLeft.every((value, index) => value === normalizedRight[index]);
1623
+ }
1624
+ function confirmationReplayKey(confirmation) {
1625
+ if (!confirmation) return;
1626
+ if ("jkt" in confirmation) return `jkt:${confirmation.jkt}`;
1627
+ return `x5t#S256:${confirmation["x5t#S256"]}`;
1628
+ }
1629
+ function sameConfirmation(left, right) {
1630
+ return confirmationReplayKey(left) === confirmationReplayKey(right);
1631
+ }
1632
+ function isConfirmation(value) {
1633
+ if (!value || typeof value !== "object") return false;
1634
+ const confirmation = value;
1635
+ return typeof confirmation.jkt === "string" && confirmation["x5t#S256"] === void 0 || typeof confirmation["x5t#S256"] === "string" && confirmation.jkt === void 0;
1636
+ }
1637
+ function isTokenType(value) {
1638
+ return value === "Bearer" || value === "DPoP";
1639
+ }
1640
+ function isOAuthTokenResponse(value) {
1641
+ if (!value || typeof value !== "object") return false;
1642
+ const response = value;
1643
+ return typeof response.access_token === "string" && typeof response.expires_in === "number" && typeof response.expires_at === "number" && isTokenType(response.token_type) && typeof response.scope === "string" && (response.refresh_token === void 0 || typeof response.refresh_token === "string") && (response.id_token === void 0 || typeof response.id_token === "string");
1644
+ }
1645
+ function isRefreshTokenRotationReplay(value) {
1646
+ if (!value || typeof value !== "object") return false;
1647
+ const replay = value;
1648
+ const request = replay.request;
1649
+ return !!request && Array.isArray(request.effectiveScopes) && request.effectiveScopes.every((scope) => typeof scope === "string") && (request.requestedResources === void 0 || Array.isArray(request.requestedResources) && request.requestedResources.every((resource) => typeof resource === "string")) && (request.confirmation === void 0 || isConfirmation(request.confirmation)) && isOAuthTokenResponse(replay.response);
1650
+ }
1651
+ function buildRefreshTokenRotationReplayRequest(params) {
1652
+ const requestedResources = normalizeReplayValues(params.requestedResources);
1653
+ return {
1654
+ effectiveScopes: normalizeReplayValues(params.effectiveScopes) ?? [],
1655
+ ...requestedResources ? { requestedResources } : {},
1656
+ ...params.confirmation ? { confirmation: params.confirmation } : {}
1657
+ };
1658
+ }
1659
+ function sameRefreshTokenRotationReplayRequest(left, right) {
1660
+ return sameReplayValues(left.effectiveScopes, right.effectiveScopes) && sameReplayValues(left.requestedResources, right.requestedResources) && sameConfirmation(left.confirmation, right.confirmation);
1661
+ }
1662
+ async function encryptRefreshTokenRotationReplay(ctx, replay) {
1663
+ return symmetricEncrypt({
1664
+ key: ctx.context.secretConfig,
1665
+ data: JSON.stringify(replay)
1666
+ });
1667
+ }
1668
+ async function decryptRefreshTokenRotationReplay(ctx, value) {
1669
+ const decrypted = await symmetricDecrypt({
1670
+ key: ctx.context.secretConfig,
1671
+ data: value
1672
+ });
1673
+ const parsed = JSON.parse(decrypted);
1674
+ if (!isRefreshTokenRotationReplay(parsed)) return;
1675
+ return parsed;
1676
+ }
1677
+ function isWithinRefreshTokenReuseInterval(refreshToken) {
1678
+ return !!refreshToken.rotatedAt && !!refreshToken.rotationReplayExpiresAt && refreshToken.rotationReplayExpiresAt >= /* @__PURE__ */ new Date();
1679
+ }
1680
+ async function storeRefreshTokenRotationReplay(ctx, opts, refreshToken, request, response) {
1681
+ if ((opts.refreshTokenReuseInterval ?? 0) <= 0) return;
1682
+ await ctx.context.adapter.update({
1683
+ model: "oauthRefreshToken",
1684
+ where: [{
1685
+ field: "id",
1686
+ value: refreshToken.id
1687
+ }],
1688
+ update: { rotationReplayResponse: await encryptRefreshTokenRotationReplay(ctx, {
1689
+ request,
1690
+ response
1691
+ }) }
1692
+ });
1693
+ }
1694
+ async function getRefreshTokenRotationReplay(ctx, refreshToken, request) {
1695
+ if (!isWithinRefreshTokenReuseInterval(refreshToken)) return;
1696
+ if (!refreshToken.rotationReplayResponse) return;
1697
+ try {
1698
+ const replay = await decryptRefreshTokenRotationReplay(ctx, refreshToken.rotationReplayResponse);
1699
+ if (!replay || !sameRefreshTokenRotationReplayRequest(replay.request, request)) return;
1700
+ return replay.response;
1701
+ } catch (error) {
1702
+ ctx.context.logger.error("refresh token rotation replay failed", error);
1703
+ return;
1704
+ }
1705
+ }
1706
+ async function resolveRefreshTokenRotationReplayRequest(ctx, opts, params) {
1707
+ const iat = Math.floor(Date.now() / 1e3);
1708
+ const grantIssuance = await resolveResourceGrantIssuance(ctx, opts, {
1709
+ clientId: params.client.clientId,
1710
+ requestedScopes: params.scopes,
1711
+ resources: params.resources,
1712
+ refreshToken: params.refreshToken,
1713
+ iat,
1714
+ scopeExpiresAtSeconds: iat + (opts.accessTokenExpiresIn ?? 3600)
1715
+ });
1716
+ const confirmation = await resolveDpopTokenBinding(ctx, opts, {
1717
+ client: params.client,
1718
+ grantIssuance,
1719
+ refreshToken: params.refreshToken
1720
+ });
1721
+ return buildRefreshTokenRotationReplayRequest({
1722
+ effectiveScopes: grantIssuance.effectiveScopes,
1723
+ requestedResources: params.resources,
1724
+ confirmation
1725
+ });
1726
+ }
1727
+ function replayRefreshTokenRotationResponse(ctx, response) {
1728
+ const now = Math.floor(Date.now() / 1e3);
1729
+ return ctx.json({
1730
+ ...response,
1731
+ expires_in: Math.max(0, response.expires_at - now)
1732
+ });
1733
+ }
1607
1734
  async function createUserTokens(ctx, opts, params) {
1608
1735
  const { client, scopes, user, grantType, referenceId, sessionId, nonce, refreshToken: existingRefreshToken, authTime, verificationValue } = params;
1609
1736
  const iat = Math.floor(Date.now() / 1e3);
@@ -1695,7 +1822,7 @@ async function createUserTokens(ctx, opts, params) {
1695
1822
  sid: sessionId
1696
1823
  }, existingRefreshToken, authTime, refreshResources, confirmation, requestedUserInfoClaims) : void 0]);
1697
1824
  const idToken = isIdToken ? await createIdToken(ctx, opts, user, client, effectiveScopes, nonce, sessionId, authTime, accessToken, additionalIdTokenClaims) : void 0;
1698
- return ctx.json({
1825
+ const responseBody = {
1699
1826
  ...customFields,
1700
1827
  ...params.tokenResponse ?? {},
1701
1828
  access_token: accessToken,
@@ -1705,7 +1832,17 @@ async function createUserTokens(ctx, opts, params) {
1705
1832
  refresh_token: refreshToken?.token,
1706
1833
  scope: effectiveScopes.join(" "),
1707
1834
  id_token: idToken
1708
- });
1835
+ };
1836
+ if (existingRefreshToken?.id && refreshToken?.id) try {
1837
+ await storeRefreshTokenRotationReplay(ctx, opts, existingRefreshToken, buildRefreshTokenRotationReplayRequest({
1838
+ effectiveScopes,
1839
+ requestedResources: params.resources,
1840
+ confirmation
1841
+ }), responseBody);
1842
+ } catch (error) {
1843
+ ctx.context.logger.error("failed to store refresh token rotation replay", error);
1844
+ }
1845
+ return ctx.json(responseBody);
1709
1846
  }
1710
1847
  /** Checks verification value */
1711
1848
  async function checkVerificationValue(ctx, opts, code, client_id, redirect_uri, resource) {
@@ -1948,13 +2085,6 @@ async function handleRefreshTokenGrant(ctx, opts) {
1948
2085
  error_description: "invalid refresh token",
1949
2086
  error: "invalid_grant"
1950
2087
  });
1951
- if (refreshToken.revoked) {
1952
- await invalidateRefreshFamily(ctx, client_id, refreshToken.userId);
1953
- throw new APIError("BAD_REQUEST", {
1954
- error_description: "invalid refresh token",
1955
- error: "invalid_grant"
1956
- });
1957
- }
1958
2088
  if (resources && refreshToken.resources && !resources.every((v) => refreshToken.resources?.includes(v))) throw new APIError("BAD_REQUEST", {
1959
2089
  error_description: "requested resource invalid",
1960
2090
  error: "invalid_target"
@@ -1969,6 +2099,26 @@ async function handleRefreshTokenGrant(ctx, opts) {
1969
2099
  });
1970
2100
  }
1971
2101
  const client = await validateClientCredentials(ctx, opts, client_id, client_secret, requestedScopes ?? scopes, preVerified, "refresh_token", authMethod);
2102
+ if (refreshToken.revoked) {
2103
+ if (isWithinRefreshTokenReuseInterval(refreshToken)) {
2104
+ const replay = await getRefreshTokenRotationReplay(ctx, refreshToken, await resolveRefreshTokenRotationReplayRequest(ctx, opts, {
2105
+ client,
2106
+ refreshToken,
2107
+ scopes: requestedScopes ?? scopes,
2108
+ resources: resources ?? refreshToken.resources
2109
+ }));
2110
+ if (replay) return replayRefreshTokenRotationResponse(ctx, replay);
2111
+ throw new APIError("BAD_REQUEST", {
2112
+ error_description: "invalid refresh token",
2113
+ error: "invalid_grant"
2114
+ });
2115
+ }
2116
+ await invalidateRefreshFamily(ctx, client_id, refreshToken.userId);
2117
+ throw new APIError("BAD_REQUEST", {
2118
+ error_description: "invalid refresh token",
2119
+ error: "invalid_grant"
2120
+ });
2121
+ }
1972
2122
  const user = await ctx.context.internalAdapter.findUserById(refreshToken.userId);
1973
2123
  if (!user) throw new APIError("BAD_REQUEST", {
1974
2124
  error_description: "user not found",
@@ -1,4 +1,4 @@
1
- import { C as OAuthConsent, F as OAuthTokenResponse, L as Prompt, M as OAuthResource, O as OAuthOptions, a as GrantType, l as TokenEndpointAuthMethod, n as AuthServerMetadata, o as OAuthClient, z as Scope } from "./oauth-Di-k6QeJ.mjs";
1
+ import { C as OAuthConsent, L as Prompt, M as OAuthResource, O as OAuthOptions, a as GrantType, l as TokenEndpointAuthMethod, n as AuthServerMetadata, o as OAuthClient, u as TokenType, z as Scope } from "./oauth-ScTJEcFV.mjs";
2
2
  import * as better_call0 from "better-call";
3
3
  import * as z from "zod";
4
4
  import * as jose from "jose";
@@ -561,7 +561,15 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
561
561
  };
562
562
  };
563
563
  };
564
- }, OAuthTokenResponse>;
564
+ }, {
565
+ expires_in: number;
566
+ access_token: string;
567
+ expires_at: number;
568
+ token_type: TokenType;
569
+ refresh_token: string | undefined;
570
+ scope: string;
571
+ id_token: string | undefined;
572
+ }>;
565
573
  oauth2Introspect: better_call0.StrictEndpoint<"/oauth2/introspect", {
566
574
  method: "POST";
567
575
  body: z.ZodObject<{
@@ -2301,6 +2309,18 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
2301
2309
  type: "date";
2302
2310
  required: false;
2303
2311
  };
2312
+ rotatedAt: {
2313
+ type: "date";
2314
+ required: false;
2315
+ };
2316
+ rotationReplayResponse: {
2317
+ type: "string";
2318
+ required: false;
2319
+ };
2320
+ rotationReplayExpiresAt: {
2321
+ type: "date";
2322
+ required: false;
2323
+ };
2304
2324
  authTime: {
2305
2325
  type: "date";
2306
2326
  required: false;
@@ -358,6 +358,18 @@ declare const schema: {
358
358
  type: "date";
359
359
  required: false;
360
360
  };
361
+ rotatedAt: {
362
+ type: "date";
363
+ required: false;
364
+ };
365
+ rotationReplayResponse: {
366
+ type: "string";
367
+ required: false;
368
+ };
369
+ rotationReplayExpiresAt: {
370
+ type: "date";
371
+ required: false;
372
+ };
361
373
  authTime: {
362
374
  type: "date";
363
375
  required: false;
@@ -1118,6 +1130,18 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
1118
1130
  * @default 2592000 (30 days)
1119
1131
  */
1120
1132
  refreshTokenExpiresIn?: number;
1133
+ /**
1134
+ * Seconds that a rotated refresh token can be reused to receive the same
1135
+ * token response for the same effective scopes, requested resources, and
1136
+ * sender constraint.
1137
+ *
1138
+ * Matching reuse inside this interval is treated as refresh-response replay,
1139
+ * not refresh-token replay. Set to `0` to treat any rotated refresh token
1140
+ * reuse as replay.
1141
+ *
1142
+ * @default 0
1143
+ */
1144
+ refreshTokenReuseInterval?: number;
1121
1145
  /**
1122
1146
  * The amount of time in seconds that the authorization code is valid for.
1123
1147
  *
@@ -2323,9 +2347,25 @@ interface OAuthRefreshToken<Scopes extends readonly Scope[] = InternallySupporte
2323
2347
  expiresAt: Date;
2324
2348
  createdAt: Date;
2325
2349
  /**
2326
- * When token was revoked. If set, token is considered a replay attack.
2350
+ * When this token stopped being active.
2351
+ *
2352
+ * A token can be revoked by /oauth2/revoke or consumed by rotation. Use
2353
+ * `rotatedAt` to distinguish rotation from explicit revocation.
2327
2354
  */
2328
2355
  revoked?: Date;
2356
+ /**
2357
+ * When this refresh token was consumed by rotation.
2358
+ */
2359
+ rotatedAt?: Date;
2360
+ /**
2361
+ * Encrypted token endpoint response and request fingerprint to replay during
2362
+ * the configured refresh-token reuse interval.
2363
+ */
2364
+ rotationReplayResponse?: string;
2365
+ /**
2366
+ * When the cached rotation response stops being replayable.
2367
+ */
2368
+ rotationReplayExpiresAt?: Date;
2329
2369
  /**
2330
2370
  * The time the user originally authenticated.
2331
2371
  * Persisted so refreshed ID tokens can include a correct `auth_time` claim.
@@ -1,5 +1,5 @@
1
1
  //#endregion
2
2
  //#region src/version.ts
3
- const PACKAGE_VERSION = "1.7.0-beta.9";
3
+ const PACKAGE_VERSION = "1.7.0-rc.1";
4
4
  //#endregion
5
5
  export { PACKAGE_VERSION as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/oauth-provider",
3
- "version": "1.7.0-beta.9",
3
+ "version": "1.7.0-rc.1",
4
4
  "description": "An oauth provider plugin for Better Auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -57,21 +57,21 @@
57
57
  }
58
58
  },
59
59
  "dependencies": {
60
- "jose": "^6.1.3",
60
+ "jose": "^6.2.3",
61
61
  "zod": "^4.3.6"
62
62
  },
63
63
  "devDependencies": {
64
64
  "listhen": "^1.9.0",
65
65
  "tsdown": "0.21.1",
66
- "@better-auth/core": "1.7.0-beta.9",
67
- "better-auth": "1.7.0-beta.9"
66
+ "@better-auth/core": "1.7.0-rc.1",
67
+ "better-auth": "1.7.0-rc.1"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "@better-auth/utils": "0.4.2",
71
71
  "@better-fetch/fetch": "1.3.1",
72
- "better-call": "1.3.6",
73
- "@better-auth/core": "^1.7.0-beta.9",
74
- "better-auth": "^1.7.0-beta.9"
72
+ "better-call": "1.3.7",
73
+ "@better-auth/core": "^1.7.0-rc.1",
74
+ "better-auth": "^1.7.0-rc.1"
75
75
  },
76
76
  "scripts": {
77
77
  "build": "tsdown",