mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-06 04:19:20 +00:00
refactor: move oauth2 to core (#5135)
This commit is contained in:
@@ -11,13 +11,13 @@ import { getTestInstance } from "../../test-utils/test-instance";
|
||||
import { parseSetCookieHeader } from "../../cookies";
|
||||
import type { GoogleProfile } from "../../social-providers";
|
||||
import { DEFAULT_SECRET } from "../../utils/constants";
|
||||
import { getOAuth2Tokens } from "../../oauth2";
|
||||
import { getOAuth2Tokens } from "@better-auth/core/oauth2";
|
||||
import { signJWT } from "../../crypto/jwt";
|
||||
import { BASE_ERROR_CODES } from "../../error/codes";
|
||||
import type { Account } from "../../types";
|
||||
|
||||
let email = "";
|
||||
vi.mock("../../oauth2", async (importOriginal) => {
|
||||
vi.mock("@better-auth/core/oauth2", async (importOriginal) => {
|
||||
const original = (await importOriginal()) as any;
|
||||
return {
|
||||
...original,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as z from "zod";
|
||||
import { createAuthEndpoint } from "../call";
|
||||
import { APIError } from "better-call";
|
||||
import { generateState, decryptOAuthToken, setTokenUtil } from "../../oauth2";
|
||||
import type { OAuth2Tokens } from "@better-auth/core/oauth2";
|
||||
import {
|
||||
freshSessionMiddleware,
|
||||
@@ -10,6 +9,8 @@ import {
|
||||
} from "./session";
|
||||
import { BASE_ERROR_CODES } from "../../error/codes";
|
||||
import { SocialProviderListEnum } from "../../social-providers";
|
||||
import { generateState } from "../../oauth2/state";
|
||||
import { decryptOAuthToken, setTokenUtil } from "../../oauth2/utils";
|
||||
|
||||
export const listUserAccounts = createAuthEndpoint(
|
||||
"/list-accounts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as z from "zod";
|
||||
import { setSessionCookie } from "../../cookies";
|
||||
import { setTokenUtil } from "../../oauth2";
|
||||
import { setTokenUtil } from "../../oauth2/utils";
|
||||
import { handleOAuthUserInfo } from "../../oauth2/link-account";
|
||||
import { parseState } from "../../oauth2/state";
|
||||
import { HIDE_METADATA } from "../../utils/hide-metadata";
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
export * from "./create-authorization-url";
|
||||
export * from "./validate-authorization-code";
|
||||
export * from "./refresh-access-token";
|
||||
export * from "@better-auth/core/oauth2";
|
||||
export * from "./utils";
|
||||
export * from "./state";
|
||||
export * from "./link-account";
|
||||
|
||||
@@ -2,14 +2,14 @@ import { describe, expect, it, vi } from "vitest";
|
||||
import { getTestInstance } from "../test-utils/test-instance";
|
||||
import type { GoogleProfile } from "../social-providers";
|
||||
import { DEFAULT_SECRET } from "../utils/constants";
|
||||
import { getOAuth2Tokens } from "../oauth2";
|
||||
import { getOAuth2Tokens } from "@better-auth/core/oauth2";
|
||||
import { signJWT } from "../crypto/jwt";
|
||||
import type { User } from "../types";
|
||||
|
||||
let mockEmail = "";
|
||||
let mockEmailVerified = true;
|
||||
|
||||
vi.mock("../oauth2", async (importOriginal) => {
|
||||
vi.mock("@better-auth/core/oauth2", async (importOriginal) => {
|
||||
const original = (await importOriginal()) as any;
|
||||
return {
|
||||
...original,
|
||||
|
||||
@@ -1,40 +1,6 @@
|
||||
import type { OAuth2Tokens } from "@better-auth/core/oauth2";
|
||||
import { getDate } from "../utils/date";
|
||||
import { createHash } from "@better-auth/utils/hash";
|
||||
import { base64Url } from "@better-auth/utils/base64";
|
||||
import type { AuthContext } from "../types";
|
||||
import { symmetricDecrypt, symmetricEncrypt } from "../crypto";
|
||||
|
||||
export async function generateCodeChallenge(codeVerifier: string) {
|
||||
const codeChallengeBytes = await createHash("SHA-256").digest(codeVerifier);
|
||||
return base64Url.encode(new Uint8Array(codeChallengeBytes), {
|
||||
padding: false,
|
||||
});
|
||||
}
|
||||
|
||||
export function getOAuth2Tokens(data: Record<string, any>): OAuth2Tokens {
|
||||
return {
|
||||
tokenType: data.token_type,
|
||||
accessToken: data.access_token,
|
||||
refreshToken: data.refresh_token,
|
||||
accessTokenExpiresAt: data.expires_in
|
||||
? getDate(data.expires_in, "sec")
|
||||
: undefined,
|
||||
refreshTokenExpiresAt: data.refresh_token_expires_in
|
||||
? getDate(data.refresh_token_expires_in, "sec")
|
||||
: undefined,
|
||||
scopes: data?.scope
|
||||
? typeof data.scope === "string"
|
||||
? data.scope.split(" ")
|
||||
: data.scope
|
||||
: [],
|
||||
idToken: data.id_token,
|
||||
};
|
||||
}
|
||||
|
||||
export const encodeOAuthParameter = (value: string) =>
|
||||
encodeURIComponent(value).replace(/%20/g, "+");
|
||||
|
||||
export function decryptOAuthToken(token: string, ctx: AuthContext) {
|
||||
if (!token) return token;
|
||||
if (ctx.options.account?.encryptOAuthTokens) {
|
||||
|
||||
@@ -8,9 +8,9 @@ import { createAuthClient } from "../../client";
|
||||
import type { GoogleProfile } from "../../social-providers";
|
||||
import { signJWT } from "../../crypto";
|
||||
import { DEFAULT_SECRET } from "../../utils/constants";
|
||||
import { getOAuth2Tokens } from "../../oauth2";
|
||||
import { getOAuth2Tokens } from "@better-auth/core/oauth2";
|
||||
|
||||
vi.mock("../../oauth2", async (importOriginal) => {
|
||||
vi.mock("@better-auth/core/oauth2", async (importOriginal) => {
|
||||
const original = (await importOriginal()) as any;
|
||||
return {
|
||||
...original,
|
||||
|
||||
@@ -5,10 +5,10 @@ import { createAuthClient } from "../../client";
|
||||
import { anonymousClient } from "./client";
|
||||
import type { GoogleProfile } from "../../social-providers";
|
||||
import { DEFAULT_SECRET } from "../../utils/constants";
|
||||
import { getOAuth2Tokens } from "../../oauth2";
|
||||
import { getOAuth2Tokens } from "@better-auth/core/oauth2";
|
||||
import { signJWT } from "../../crypto/jwt";
|
||||
|
||||
vi.mock("../../oauth2", async (importOriginal) => {
|
||||
vi.mock("@better-auth/core/oauth2", async (importOriginal) => {
|
||||
const original = (await importOriginal()) as any;
|
||||
return {
|
||||
...original,
|
||||
|
||||
@@ -8,14 +8,14 @@ import { BASE_ERROR_CODES } from "../../error/codes";
|
||||
import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
} from "../../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
import type {
|
||||
OAuth2Tokens,
|
||||
OAuth2UserInfo,
|
||||
OAuthProvider,
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { handleOAuthUserInfo } from "../../oauth2/link-account";
|
||||
import { refreshAccessToken } from "../../oauth2/refresh-access-token";
|
||||
import { refreshAccessToken } from "@better-auth/core/oauth2";
|
||||
import { generateState, parseState } from "../../oauth2/state";
|
||||
import type {
|
||||
BetterAuthPlugin,
|
||||
|
||||
@@ -5,11 +5,11 @@ import { lastLoginMethodClient } from "./client";
|
||||
import { parseCookies, parseSetCookieHeader } from "../../cookies";
|
||||
import { DEFAULT_SECRET } from "../../utils/constants";
|
||||
import type { GoogleProfile } from "../../social-providers/google";
|
||||
import { getOAuth2Tokens } from "../../oauth2";
|
||||
import { getOAuth2Tokens } from "@better-auth/core/oauth2";
|
||||
import { signJWT } from "../../crypto/jwt";
|
||||
|
||||
// Mock OAuth2 functions to return valid tokens for testing
|
||||
vi.mock("../../oauth2", async (importOriginal) => {
|
||||
vi.mock("@better-auth/core/oauth2", async (importOriginal) => {
|
||||
const original = (await importOriginal()) as any;
|
||||
return {
|
||||
...original,
|
||||
|
||||
@@ -3,10 +3,10 @@ import { getTestInstance } from "../../test-utils/test-instance";
|
||||
import { oAuthProxy } from ".";
|
||||
import type { GoogleProfile } from "../../social-providers";
|
||||
import { DEFAULT_SECRET } from "../../utils/constants";
|
||||
import { getOAuth2Tokens } from "../../oauth2";
|
||||
import { getOAuth2Tokens } from "@better-auth/core/oauth2";
|
||||
import { signJWT } from "../../crypto/jwt";
|
||||
|
||||
vi.mock("../../oauth2", async (importOriginal) => {
|
||||
vi.mock("@better-auth/core/oauth2", async (importOriginal) => {
|
||||
const original = (await importOriginal()) as any;
|
||||
return {
|
||||
...original,
|
||||
|
||||
@@ -3,17 +3,16 @@ import { APIError, createAuthEndpoint, sessionMiddleware } from "../../api";
|
||||
import type { BetterAuthPlugin, User } from "../../types";
|
||||
import {
|
||||
createAuthorizationURL,
|
||||
generateState,
|
||||
parseState,
|
||||
setTokenUtil,
|
||||
validateAuthorizationCode,
|
||||
validateToken,
|
||||
} from "../../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { betterFetch, BetterFetchError } from "@better-fetch/fetch";
|
||||
import { decodeJwt } from "jose";
|
||||
import { handleOAuthUserInfo } from "../../oauth2/link-account";
|
||||
import { setSessionCookie } from "../../cookies";
|
||||
import type { OAuth2Tokens } from "@better-auth/core/oauth2";
|
||||
import { generateState, parseState } from "../../oauth2/state";
|
||||
import { setTokenUtil } from "../../oauth2/utils";
|
||||
|
||||
export interface SSOOptions {
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
refreshAccessToken,
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
export interface AppleProfile {
|
||||
/**
|
||||
* The subject registered claim identifies the principal that’s the subject
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { BetterAuthError } from "../error";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { createAuthorizationURL, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { logger } from "@better-auth/core/env";
|
||||
import { refreshAccessToken } from "../oauth2/refresh-access-token";
|
||||
import { refreshAccessToken } from "@better-auth/core/oauth2";
|
||||
|
||||
export interface AtlassianProfile {
|
||||
account_type?: string;
|
||||
|
||||
@@ -2,9 +2,12 @@ import { betterFetch } from "@better-fetch/fetch";
|
||||
import { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from "jose";
|
||||
import { BetterAuthError } from "../error";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { createAuthorizationURL, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { logger } from "@better-auth/core/env";
|
||||
import { refreshAccessToken } from "../oauth2/refresh-access-token";
|
||||
import { refreshAccessToken } from "@better-auth/core/oauth2";
|
||||
import { APIError } from "better-call";
|
||||
|
||||
export interface CognitoProfile {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
refreshAccessToken,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
export interface DiscordProfile extends Record<string, any> {
|
||||
/** the user's id (i.e. the numerical snowflake) */
|
||||
id: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
refreshAccessToken,
|
||||
validateAuthorizationCode,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface DropboxProfile {
|
||||
account_id: string;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { createAuthorizationURL, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { createRemoteJWKSet, jwtVerify, decodeJwt } from "jose";
|
||||
import { refreshAccessToken } from "../oauth2";
|
||||
import { refreshAccessToken } from "@better-auth/core/oauth2";
|
||||
export interface FacebookProfile {
|
||||
id: string;
|
||||
name: string;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { BetterAuthError } from "../error";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { createAuthorizationURL, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { logger } from "@better-auth/core/env";
|
||||
import { refreshAccessToken } from "../oauth2/refresh-access-token";
|
||||
import { refreshAccessToken } from "@better-auth/core/oauth2";
|
||||
|
||||
export interface FigmaProfile {
|
||||
id: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
refreshAccessToken,
|
||||
validateAuthorizationCode,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface GithubProfile {
|
||||
login: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface GitlabProfile extends Record<string, any> {
|
||||
id: number;
|
||||
|
||||
@@ -2,9 +2,12 @@ import { betterFetch } from "@better-fetch/fetch";
|
||||
import { decodeJwt } from "jose";
|
||||
import { BetterAuthError } from "../error";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { createAuthorizationURL, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { logger } from "@better-auth/core/env";
|
||||
import { refreshAccessToken } from "../oauth2/refresh-access-token";
|
||||
import { refreshAccessToken } from "@better-auth/core/oauth2";
|
||||
|
||||
export interface GoogleProfile {
|
||||
aud: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface HuggingFaceProfile {
|
||||
sub: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
interface Partner {
|
||||
/** Partner-specific ID (consent required: kakaotalk_message) */
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { createAuthorizationURL, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
|
||||
export interface KickProfile {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
refreshAccessToken,
|
||||
validateAuthorizationCode,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface LineIdTokenPayload {
|
||||
iss: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
refreshAccessToken,
|
||||
validateAuthorizationCode,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
interface LinearUser {
|
||||
id: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface LinkedInProfile {
|
||||
sub: string;
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
validateAuthorizationCode,
|
||||
createAuthorizationURL,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { logger } from "@better-auth/core/env";
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface NaverProfile {
|
||||
/** API response result code */
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
refreshAccessToken,
|
||||
validateAuthorizationCode,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface NotionProfile {
|
||||
object: "user";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { BetterAuthError } from "../error";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { createAuthorizationURL } from "../oauth2";
|
||||
import { createAuthorizationURL } from "@better-auth/core/oauth2";
|
||||
import { logger } from "@better-auth/core/env";
|
||||
import { decodeJwt } from "jose";
|
||||
import { base64 } from "@better-auth/utils/base64";
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
getOAuth2Tokens,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { base64 } from "@better-auth/utils/base64";
|
||||
|
||||
export interface RedditProfile {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { validateAuthorizationCode, refreshAccessToken } from "../oauth2";
|
||||
import {
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface RobloxProfile extends Record<string, any> {
|
||||
/** the user's id */
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { BetterAuthError } from "../error";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { createAuthorizationURL, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { logger } from "@better-auth/core/env";
|
||||
import { refreshAccessToken } from "../oauth2/refresh-access-token";
|
||||
import { refreshAccessToken } from "@better-auth/core/oauth2";
|
||||
|
||||
export interface SalesforceProfile {
|
||||
sub: string;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { validateAuthorizationCode, refreshAccessToken } from "../oauth2";
|
||||
import {
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface SlackProfile extends Record<string, any> {
|
||||
ok: boolean;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getTestInstance } from "../test-utils/test-instance";
|
||||
import { DEFAULT_SECRET } from "../utils/constants";
|
||||
import type { GoogleProfile } from "./google";
|
||||
import { parseSetCookieHeader } from "../cookies";
|
||||
import { getOAuth2Tokens, refreshAccessToken } from "../oauth2";
|
||||
import { getOAuth2Tokens, refreshAccessToken } from "@better-auth/core/oauth2";
|
||||
import { signJWT } from "../crypto/jwt";
|
||||
import { OAuth2Server } from "oauth2-mock-server";
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
@@ -13,7 +13,7 @@ import { getMigrations } from "../db";
|
||||
let server = new OAuth2Server();
|
||||
let port = 8005;
|
||||
|
||||
vi.mock("../oauth2", async (importOriginal) => {
|
||||
vi.mock("@better-auth/core/oauth2", async (importOriginal) => {
|
||||
const original = (await importOriginal()) as any;
|
||||
return {
|
||||
...original,
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface SpotifyProfile {
|
||||
id: string;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
refreshAccessToken,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
/**
|
||||
* [More info](https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/)
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
import { decodeJwt } from "jose";
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
refreshAccessToken,
|
||||
validateAuthorizationCode,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface TwitterProfile {
|
||||
data: {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
createAuthorizationURL,
|
||||
validateAuthorizationCode,
|
||||
refreshAccessToken,
|
||||
} from "../oauth2";
|
||||
} from "@better-auth/core/oauth2";
|
||||
|
||||
export interface VkProfile {
|
||||
user: {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { generateCodeChallenge, validateAuthorizationCode } from "../oauth2";
|
||||
import {
|
||||
generateCodeChallenge,
|
||||
validateAuthorizationCode,
|
||||
} from "@better-auth/core/oauth2";
|
||||
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
|
||||
export type LoginType =
|
||||
|
||||
@@ -86,10 +86,18 @@
|
||||
"typecheck": "tsc --project tsconfig.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@better-auth/utils": "0.3.0",
|
||||
"@better-fetch/fetch": "catalog:",
|
||||
"jose": "^6.1.0",
|
||||
"unbuild": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-call": "catalog:",
|
||||
"zod": "^4.1.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@better-auth/utils": "0.3.0",
|
||||
"@better-fetch/fetch": "catalog:",
|
||||
"jose": "^6.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { base64Url } from "@better-auth/utils/base64";
|
||||
import type { OAuth2Tokens, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import type { OAuth2Tokens, ProviderOptions } from "./oauth-provider";
|
||||
|
||||
export function createClientCredentialsTokenRequest({
|
||||
options,
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import type { ProviderOptions } from "./index";
|
||||
import { generateCodeChallenge } from "./utils";
|
||||
|
||||
export async function createAuthorizationURL({
|
||||
@@ -1,194 +1,22 @@
|
||||
import type { LiteralString } from "../types/helper";
|
||||
export type {
|
||||
OAuth2Tokens,
|
||||
OAuthProvider,
|
||||
OAuth2UserInfo,
|
||||
ProviderOptions,
|
||||
} from "./oauth-provider";
|
||||
|
||||
export interface OAuth2Tokens {
|
||||
tokenType?: string;
|
||||
accessToken?: string;
|
||||
refreshToken?: string;
|
||||
accessTokenExpiresAt?: Date;
|
||||
refreshTokenExpiresAt?: Date;
|
||||
scopes?: string[];
|
||||
idToken?: string;
|
||||
}
|
||||
|
||||
export type OAuth2UserInfo = {
|
||||
id: string | number;
|
||||
name?: string;
|
||||
email?: string | null;
|
||||
image?: string;
|
||||
emailVerified: boolean;
|
||||
};
|
||||
|
||||
export interface OAuthProvider<
|
||||
T extends Record<string, any> = Record<string, any>,
|
||||
O extends Record<string, any> = Partial<ProviderOptions>,
|
||||
> {
|
||||
id: LiteralString;
|
||||
createAuthorizationURL: (data: {
|
||||
state: string;
|
||||
codeVerifier: string;
|
||||
scopes?: string[];
|
||||
redirectURI: string;
|
||||
display?: string;
|
||||
loginHint?: string;
|
||||
}) => Promise<URL> | URL;
|
||||
name: string;
|
||||
validateAuthorizationCode: (data: {
|
||||
code: string;
|
||||
redirectURI: string;
|
||||
codeVerifier?: string;
|
||||
deviceId?: string;
|
||||
}) => Promise<OAuth2Tokens>;
|
||||
getUserInfo: (
|
||||
token: OAuth2Tokens & {
|
||||
/**
|
||||
* The user object from the provider
|
||||
* This is only available for some providers like Apple
|
||||
*/
|
||||
user?: {
|
||||
name?: {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
};
|
||||
email?: string;
|
||||
};
|
||||
},
|
||||
) => Promise<{
|
||||
user: OAuth2UserInfo;
|
||||
data: T;
|
||||
} | null>;
|
||||
/**
|
||||
* Custom function to refresh a token
|
||||
*/
|
||||
refreshAccessToken?: (refreshToken: string) => Promise<OAuth2Tokens>;
|
||||
revokeToken?: (token: string) => Promise<void>;
|
||||
/**
|
||||
* Verify the id token
|
||||
* @param token - The id token
|
||||
* @param nonce - The nonce
|
||||
* @returns True if the id token is valid, false otherwise
|
||||
*/
|
||||
verifyIdToken?: (token: string, nonce?: string) => Promise<boolean>;
|
||||
/**
|
||||
* Disable implicit sign up for new users. When set to true for the provider,
|
||||
* sign-in need to be called with with requestSignUp as true to create new users.
|
||||
*/
|
||||
disableImplicitSignUp?: boolean;
|
||||
/**
|
||||
* Disable sign up for new users.
|
||||
*/
|
||||
disableSignUp?: boolean;
|
||||
/**
|
||||
* Options for the provider
|
||||
*/
|
||||
options?: O;
|
||||
}
|
||||
|
||||
export type ProviderOptions<Profile extends Record<string, any> = any> = {
|
||||
/**
|
||||
* The client ID of your application.
|
||||
*
|
||||
* This is usually a string but can be any type depending on the provider.
|
||||
*/
|
||||
clientId?: unknown;
|
||||
/**
|
||||
* The client secret of your application
|
||||
*/
|
||||
clientSecret?: string;
|
||||
/**
|
||||
* The scopes you want to request from the provider
|
||||
*/
|
||||
scope?: string[];
|
||||
/**
|
||||
* Remove default scopes of the provider
|
||||
*/
|
||||
disableDefaultScope?: boolean;
|
||||
/**
|
||||
* The redirect URL for your application. This is where the provider will
|
||||
* redirect the user after the sign in process. Make sure this URL is
|
||||
* whitelisted in the provider's dashboard.
|
||||
*/
|
||||
redirectURI?: string;
|
||||
/**
|
||||
* The client key of your application
|
||||
* Tiktok Social Provider uses this field instead of clientId
|
||||
*/
|
||||
clientKey?: string;
|
||||
/**
|
||||
* Disable provider from allowing users to sign in
|
||||
* with this provider with an id token sent from the
|
||||
* client.
|
||||
*/
|
||||
disableIdTokenSignIn?: boolean;
|
||||
/**
|
||||
* verifyIdToken function to verify the id token
|
||||
*/
|
||||
verifyIdToken?: (token: string, nonce?: string) => Promise<boolean>;
|
||||
/**
|
||||
* Custom function to get user info from the provider
|
||||
*/
|
||||
getUserInfo?: (token: OAuth2Tokens) => Promise<{
|
||||
user: {
|
||||
id: string;
|
||||
name?: string;
|
||||
email?: string | null;
|
||||
image?: string;
|
||||
emailVerified: boolean;
|
||||
[key: string]: any;
|
||||
};
|
||||
data: any;
|
||||
}>;
|
||||
/**
|
||||
* Custom function to refresh a token
|
||||
*/
|
||||
refreshAccessToken?: (refreshToken: string) => Promise<OAuth2Tokens>;
|
||||
/**
|
||||
* Custom function to map the provider profile to a
|
||||
* user.
|
||||
*/
|
||||
mapProfileToUser?: (profile: Profile) =>
|
||||
| {
|
||||
id?: string;
|
||||
name?: string;
|
||||
email?: string | null;
|
||||
image?: string;
|
||||
emailVerified?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
| Promise<{
|
||||
id?: string;
|
||||
name?: string;
|
||||
email?: string | null;
|
||||
image?: string;
|
||||
emailVerified?: boolean;
|
||||
[key: string]: any;
|
||||
}>;
|
||||
/**
|
||||
* Disable implicit sign up for new users. When set to true for the provider,
|
||||
* sign-in need to be called with with requestSignUp as true to create new users.
|
||||
*/
|
||||
disableImplicitSignUp?: boolean;
|
||||
/**
|
||||
* Disable sign up for new users.
|
||||
*/
|
||||
disableSignUp?: boolean;
|
||||
/**
|
||||
* The prompt to use for the authorization code request
|
||||
*/
|
||||
prompt?:
|
||||
| "select_account"
|
||||
| "consent"
|
||||
| "login"
|
||||
| "none"
|
||||
| "select_account consent";
|
||||
/**
|
||||
* The response mode to use for the authorization code request
|
||||
*/
|
||||
responseMode?: "query" | "form_post";
|
||||
/**
|
||||
* If enabled, the user info will be overridden with the provider user info
|
||||
* This is useful if you want to use the provider user info to update the user info
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
overrideUserInfoOnSignIn?: boolean;
|
||||
};
|
||||
export { generateCodeChallenge, getOAuth2Tokens } from "./utils";
|
||||
export { createAuthorizationURL } from "./create-authorization-url";
|
||||
export {
|
||||
createAuthorizationCodeRequest,
|
||||
validateAuthorizationCode,
|
||||
validateToken,
|
||||
} from "./validate-authorization-code";
|
||||
export {
|
||||
createRefreshAccessTokenRequest,
|
||||
refreshAccessToken,
|
||||
} from "./refresh-access-token";
|
||||
export {
|
||||
clientCredentialsToken,
|
||||
createClientCredentialsTokenRequest,
|
||||
} from "./client-credentials-token";
|
||||
|
||||
194
packages/core/src/oauth2/oauth-provider.ts
Normal file
194
packages/core/src/oauth2/oauth-provider.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
import type { LiteralString } from "../types";
|
||||
|
||||
export interface OAuth2Tokens {
|
||||
tokenType?: string;
|
||||
accessToken?: string;
|
||||
refreshToken?: string;
|
||||
accessTokenExpiresAt?: Date;
|
||||
refreshTokenExpiresAt?: Date;
|
||||
scopes?: string[];
|
||||
idToken?: string;
|
||||
}
|
||||
|
||||
export type OAuth2UserInfo = {
|
||||
id: string | number;
|
||||
name?: string;
|
||||
email?: string | null;
|
||||
image?: string;
|
||||
emailVerified: boolean;
|
||||
};
|
||||
|
||||
export interface OAuthProvider<
|
||||
T extends Record<string, any> = Record<string, any>,
|
||||
O extends Record<string, any> = Partial<ProviderOptions>,
|
||||
> {
|
||||
id: LiteralString;
|
||||
createAuthorizationURL: (data: {
|
||||
state: string;
|
||||
codeVerifier: string;
|
||||
scopes?: string[];
|
||||
redirectURI: string;
|
||||
display?: string;
|
||||
loginHint?: string;
|
||||
}) => Promise<URL> | URL;
|
||||
name: string;
|
||||
validateAuthorizationCode: (data: {
|
||||
code: string;
|
||||
redirectURI: string;
|
||||
codeVerifier?: string;
|
||||
deviceId?: string;
|
||||
}) => Promise<OAuth2Tokens>;
|
||||
getUserInfo: (
|
||||
token: OAuth2Tokens & {
|
||||
/**
|
||||
* The user object from the provider
|
||||
* This is only available for some providers like Apple
|
||||
*/
|
||||
user?: {
|
||||
name?: {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
};
|
||||
email?: string;
|
||||
};
|
||||
},
|
||||
) => Promise<{
|
||||
user: OAuth2UserInfo;
|
||||
data: T;
|
||||
} | null>;
|
||||
/**
|
||||
* Custom function to refresh a token
|
||||
*/
|
||||
refreshAccessToken?: (refreshToken: string) => Promise<OAuth2Tokens>;
|
||||
revokeToken?: (token: string) => Promise<void>;
|
||||
/**
|
||||
* Verify the id token
|
||||
* @param token - The id token
|
||||
* @param nonce - The nonce
|
||||
* @returns True if the id token is valid, false otherwise
|
||||
*/
|
||||
verifyIdToken?: (token: string, nonce?: string) => Promise<boolean>;
|
||||
/**
|
||||
* Disable implicit sign up for new users. When set to true for the provider,
|
||||
* sign-in need to be called with with requestSignUp as true to create new users.
|
||||
*/
|
||||
disableImplicitSignUp?: boolean;
|
||||
/**
|
||||
* Disable sign up for new users.
|
||||
*/
|
||||
disableSignUp?: boolean;
|
||||
/**
|
||||
* Options for the provider
|
||||
*/
|
||||
options?: O;
|
||||
}
|
||||
|
||||
export type ProviderOptions<Profile extends Record<string, any> = any> = {
|
||||
/**
|
||||
* The client ID of your application.
|
||||
*
|
||||
* This is usually a string but can be any type depending on the provider.
|
||||
*/
|
||||
clientId?: unknown;
|
||||
/**
|
||||
* The client secret of your application
|
||||
*/
|
||||
clientSecret?: string;
|
||||
/**
|
||||
* The scopes you want to request from the provider
|
||||
*/
|
||||
scope?: string[];
|
||||
/**
|
||||
* Remove default scopes of the provider
|
||||
*/
|
||||
disableDefaultScope?: boolean;
|
||||
/**
|
||||
* The redirect URL for your application. This is where the provider will
|
||||
* redirect the user after the sign in process. Make sure this URL is
|
||||
* whitelisted in the provider's dashboard.
|
||||
*/
|
||||
redirectURI?: string;
|
||||
/**
|
||||
* The client key of your application
|
||||
* Tiktok Social Provider uses this field instead of clientId
|
||||
*/
|
||||
clientKey?: string;
|
||||
/**
|
||||
* Disable provider from allowing users to sign in
|
||||
* with this provider with an id token sent from the
|
||||
* client.
|
||||
*/
|
||||
disableIdTokenSignIn?: boolean;
|
||||
/**
|
||||
* verifyIdToken function to verify the id token
|
||||
*/
|
||||
verifyIdToken?: (token: string, nonce?: string) => Promise<boolean>;
|
||||
/**
|
||||
* Custom function to get user info from the provider
|
||||
*/
|
||||
getUserInfo?: (token: OAuth2Tokens) => Promise<{
|
||||
user: {
|
||||
id: string;
|
||||
name?: string;
|
||||
email?: string | null;
|
||||
image?: string;
|
||||
emailVerified: boolean;
|
||||
[key: string]: any;
|
||||
};
|
||||
data: any;
|
||||
}>;
|
||||
/**
|
||||
* Custom function to refresh a token
|
||||
*/
|
||||
refreshAccessToken?: (refreshToken: string) => Promise<OAuth2Tokens>;
|
||||
/**
|
||||
* Custom function to map the provider profile to a
|
||||
* user.
|
||||
*/
|
||||
mapProfileToUser?: (profile: Profile) =>
|
||||
| {
|
||||
id?: string;
|
||||
name?: string;
|
||||
email?: string | null;
|
||||
image?: string;
|
||||
emailVerified?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
| Promise<{
|
||||
id?: string;
|
||||
name?: string;
|
||||
email?: string | null;
|
||||
image?: string;
|
||||
emailVerified?: boolean;
|
||||
[key: string]: any;
|
||||
}>;
|
||||
/**
|
||||
* Disable implicit sign up for new users. When set to true for the provider,
|
||||
* sign-in need to be called with with requestSignUp as true to create new users.
|
||||
*/
|
||||
disableImplicitSignUp?: boolean;
|
||||
/**
|
||||
* Disable sign up for new users.
|
||||
*/
|
||||
disableSignUp?: boolean;
|
||||
/**
|
||||
* The prompt to use for the authorization code request
|
||||
*/
|
||||
prompt?:
|
||||
| "select_account"
|
||||
| "consent"
|
||||
| "login"
|
||||
| "none"
|
||||
| "select_account consent";
|
||||
/**
|
||||
* The response mode to use for the authorization code request
|
||||
*/
|
||||
responseMode?: "query" | "form_post";
|
||||
/**
|
||||
* If enabled, the user info will be overridden with the provider user info
|
||||
* This is useful if you want to use the provider user info to update the user info
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
overrideUserInfoOnSignIn?: boolean;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import type { OAuth2Tokens, ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import type { OAuth2Tokens, ProviderOptions } from "./oauth-provider";
|
||||
import { base64 } from "@better-auth/utils/base64";
|
||||
|
||||
export function createRefreshAccessTokenRequest({
|
||||
36
packages/core/src/oauth2/utils.ts
Normal file
36
packages/core/src/oauth2/utils.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { base64Url } from "@better-auth/utils/base64";
|
||||
import type { OAuth2Tokens } from "./oauth-provider";
|
||||
|
||||
export function getOAuth2Tokens(data: Record<string, any>): OAuth2Tokens {
|
||||
const getDate = (seconds: number) => {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + seconds * 1000);
|
||||
};
|
||||
|
||||
return {
|
||||
tokenType: data.token_type,
|
||||
accessToken: data.access_token,
|
||||
refreshToken: data.refresh_token,
|
||||
accessTokenExpiresAt: data.expires_in
|
||||
? getDate(data.expires_in)
|
||||
: undefined,
|
||||
refreshTokenExpiresAt: data.refresh_token_expires_in
|
||||
? getDate(data.refresh_token_expires_in)
|
||||
: undefined,
|
||||
scopes: data?.scope
|
||||
? typeof data.scope === "string"
|
||||
? data.scope.split(" ")
|
||||
: data.scope
|
||||
: [],
|
||||
idToken: data.id_token,
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateCodeChallenge(codeVerifier: string) {
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(codeVerifier);
|
||||
const hash = await crypto.subtle.digest("SHA-256", data);
|
||||
return base64Url.encode(new Uint8Array(hash), {
|
||||
padding: false,
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { betterFetch } from "@better-fetch/fetch";
|
||||
import { jwtVerify } from "jose";
|
||||
import type { ProviderOptions } from "@better-auth/core/oauth2";
|
||||
import { getOAuth2Tokens } from "./utils";
|
||||
import type { ProviderOptions } from "./index";
|
||||
import { getOAuth2Tokens } from "./index";
|
||||
import { base64 } from "@better-auth/utils/base64";
|
||||
|
||||
export function createAuthorizationCodeRequest({
|
||||
23
pnpm-lock.yaml
generated
23
pnpm-lock.yaml
generated
@@ -355,7 +355,7 @@ importers:
|
||||
version: 12.23.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
geist:
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2(next@15.5.2(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.90.0))
|
||||
version: 1.4.2(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.90.0))
|
||||
input-otp:
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
@@ -599,7 +599,7 @@ importers:
|
||||
version: 15.8.3(@oramacloud/client@2.1.4)(@tanstack/react-router@1.131.27(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(algoliasearch@5.36.0)(lucide-react@0.542.0(react@19.1.1))(next@15.5.2(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.90.0))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(tailwindcss@4.1.13)
|
||||
geist:
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2(next@15.5.2(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.90.0))
|
||||
version: 1.4.2(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.90.0))
|
||||
gray-matter:
|
||||
specifier: ^4.0.3
|
||||
version: 4.0.3
|
||||
@@ -1092,6 +1092,15 @@ importers:
|
||||
specifier: ^4.1.5
|
||||
version: 4.1.5
|
||||
devDependencies:
|
||||
'@better-auth/utils':
|
||||
specifier: 0.3.0
|
||||
version: 0.3.0
|
||||
'@better-fetch/fetch':
|
||||
specifier: 'catalog:'
|
||||
version: 1.1.18
|
||||
jose:
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0
|
||||
unbuild:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.1(sass@1.90.0)(typescript@5.9.2)(vue@3.5.19(typescript@5.9.2))
|
||||
@@ -15245,7 +15254,7 @@ snapshots:
|
||||
postcss: 8.4.49
|
||||
resolve-from: 5.0.0
|
||||
optionalDependencies:
|
||||
expo: 54.0.10(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.8)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.2))(@react-native/metro-config@0.81.0(@babel/core@7.28.4))(@types/react@19.1.12)(react@19.1.1))(react@19.1.1)
|
||||
expo: 54.0.10(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.8)(graphql@16.11.0)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.1))(react@19.1.1)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
@@ -15330,7 +15339,7 @@ snapshots:
|
||||
'@expo/json-file': 10.0.7
|
||||
'@react-native/normalize-colors': 0.81.4
|
||||
debug: 4.4.1
|
||||
expo: 54.0.10(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.8)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.2))(@react-native/metro-config@0.81.0(@babel/core@7.28.4))(@types/react@19.1.12)(react@19.1.1))(react@19.1.1)
|
||||
expo: 54.0.10(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.8)(graphql@16.11.0)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.1))(react@19.1.1)
|
||||
resolve-from: 5.0.0
|
||||
semver: 7.7.2
|
||||
xml2js: 0.6.0
|
||||
@@ -19493,7 +19502,7 @@ snapshots:
|
||||
resolve-from: 5.0.0
|
||||
optionalDependencies:
|
||||
'@babel/runtime': 7.28.4
|
||||
expo: 54.0.10(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.8)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.2))(@react-native/metro-config@0.81.0(@babel/core@7.28.4))(@types/react@19.1.12)(react@19.1.1))(react@19.1.1)
|
||||
expo: 54.0.10(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.8)(graphql@16.11.0)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.1))(react@19.1.1)
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- supports-color
|
||||
@@ -21093,7 +21102,7 @@ snapshots:
|
||||
|
||||
expo-keep-awake@15.0.7(expo@54.0.10)(react@19.1.1):
|
||||
dependencies:
|
||||
expo: 54.0.10(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.8)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.2))(@react-native/metro-config@0.81.0(@babel/core@7.28.4))(@types/react@19.1.12)(react@19.1.1))(react@19.1.1)
|
||||
expo: 54.0.10(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.8)(graphql@16.11.0)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.1))(react@19.1.1)
|
||||
react: 19.1.1
|
||||
|
||||
expo-linking@7.1.7(expo@54.0.10)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.1))(react@19.1.1):
|
||||
@@ -21751,7 +21760,7 @@ snapshots:
|
||||
|
||||
function-bind@1.1.2: {}
|
||||
|
||||
geist@1.4.2(next@15.5.2(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.90.0)):
|
||||
geist@1.4.2(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.90.0)):
|
||||
dependencies:
|
||||
next: 15.5.2(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.90.0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user