diff --git a/packages/sdk-react-native/package.json b/packages/sdk-react-native/package.json index 16757d7da..173071f50 100644 --- a/packages/sdk-react-native/package.json +++ b/packages/sdk-react-native/package.json @@ -59,7 +59,7 @@ "@types/react": ">=16.8.0 <20", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-native-keychain": "^8.1.0 || ^9.2.2", - "react-native-inappbrowser-reborn": "^3.7.0", + "react-native-app-auth": "^8.0.3", "react-native-passkey": "^3.0.0" }, "peerDependenciesMeta": { diff --git a/packages/sdk-react-native/src/contexts/TurnkeyContext.tsx b/packages/sdk-react-native/src/contexts/TurnkeyContext.tsx index b26a18134..23d3762ef 100644 --- a/packages/sdk-react-native/src/contexts/TurnkeyContext.tsx +++ b/packages/sdk-react-native/src/contexts/TurnkeyContext.tsx @@ -49,8 +49,7 @@ import { TURNKEY_OAUTH_ORIGIN_URL, TURNKEY__OAUTH_REDIRECT_URL, } from "../constants"; -import { InAppBrowser } from "react-native-inappbrowser-reborn"; - +import { authorize } from "react-native-app-auth"; export interface TurnkeyContextType { session: Session | undefined; client: TurnkeyClient | undefined; @@ -111,7 +110,7 @@ export interface TurnkeyContextType { } export const TurnkeyContext = createContext( - undefined, + undefined ); export interface TurnkeyConfig { @@ -161,7 +160,7 @@ export const TurnkeyProvider: FC<{ } scheduleSessionExpiration(sessionKey, session!.expiry); - }), + }) ); // load the selected session if it's still valid @@ -174,7 +173,7 @@ export const TurnkeyProvider: FC<{ const clientInstance = createClient( selectedSession!.publicKey, selectedSession!.privateKey, - config.apiBaseUrl, + config.apiBaseUrl ); setSession(selectedSession!); @@ -185,7 +184,7 @@ export const TurnkeyProvider: FC<{ await clearSession({ sessionKey: selectedSessionKey }); config.onSessionExpired?.( - selectedSession ?? ({ key: selectedSessionKey } as Session), + selectedSession ?? ({ key: selectedSessionKey } as Session) ); } } else { @@ -208,7 +207,7 @@ export const TurnkeyProvider: FC<{ */ const clearTimeouts = () => { Object.values(expiryTimeoutsRef.current).forEach((timer) => - clearTimeout(timer), + clearTimeout(timer) ); expiryTimeoutsRef.current = {}; }; @@ -229,7 +228,7 @@ export const TurnkeyProvider: FC<{ */ const scheduleSessionExpiration = async ( sessionKey: string, - expiryTime: number, + expiryTime: number ) => { // clear existing timeout if it exists if (expiryTimeoutsRef.current[sessionKey]) { @@ -275,14 +274,14 @@ export const TurnkeyProvider: FC<{ // schedule warning expiryTimeoutsRef.current[`${sessionKey}-warning`] = setTimeout( warnBeforeExpiry, - timeUntilExpiry - warningThreshold, + timeUntilExpiry - warningThreshold ); } // schedule expiration expiryTimeoutsRef.current[sessionKey] = setTimeout( expireSession, - timeUntilExpiry, + timeUntilExpiry ); }; @@ -305,7 +304,7 @@ export const TurnkeyProvider: FC<{ const clientInstance = createClient( session!.publicKey, session!.privateKey, - config.apiBaseUrl, + config.apiBaseUrl ); setClient(clientInstance); @@ -320,7 +319,7 @@ export const TurnkeyProvider: FC<{ return undefined; } }, - [createClient, config], + [createClient, config] ); /** @@ -381,7 +380,7 @@ export const TurnkeyProvider: FC<{ return activity; }, - [client, session, refreshUser], + [client, session, refreshUser] ); /** @@ -413,7 +412,7 @@ export const TurnkeyProvider: FC<{ return isCompressed ? publicKey : publicKeyUncompressed; }, - [], + [] ); /** @@ -455,13 +454,13 @@ export const TurnkeyProvider: FC<{ if (existingSessionKeys.length >= MAX_SESSIONS) { throw new TurnkeyReactNativeError( - `Maximum session limit of ${MAX_SESSIONS} reached. Please clear an existing session before creating a new one.`, + `Maximum session limit of ${MAX_SESSIONS} reached. Please clear an existing session before creating a new one.` ); } if (existingSessionKeys.includes(sessionKey)) { throw new TurnkeyReactNativeError( - `session key "${sessionKey}" already exists. Please choose a unique session key or clear the existing session.`, + `session key "${sessionKey}" already exists. Please choose a unique session key or clear the existing session.` ); } @@ -477,7 +476,7 @@ export const TurnkeyProvider: FC<{ const clientInstance = createClient( publicKey, privateKey, - config.apiBaseUrl, + config.apiBaseUrl ); const user = await fetchUser(clientInstance, config.organizationId); if (!user) { @@ -504,7 +503,7 @@ export const TurnkeyProvider: FC<{ config.onSessionCreated?.(newSession); return newSession; }, - [config, setSelectedSession], + [config, setSelectedSession] ); /** @@ -548,13 +547,13 @@ export const TurnkeyProvider: FC<{ if (existingSessionKeys.length >= MAX_SESSIONS) { throw new TurnkeyReactNativeError( - `Maximum session limit of ${MAX_SESSIONS} reached. Please clear an existing session before creating a new one.`, + `Maximum session limit of ${MAX_SESSIONS} reached. Please clear an existing session before creating a new one.` ); } if (existingSessionKeys.includes(sessionKey)) { throw new TurnkeyReactNativeError( - `session key "${sessionKey}" already exists. Please choose a unique session key or clear the existing session.`, + `session key "${sessionKey}" already exists. Please choose a unique session key or clear the existing session.` ); } @@ -569,7 +568,7 @@ export const TurnkeyProvider: FC<{ const clientInstance = createClient( publicKey, privateKey, - config.apiBaseUrl, + config.apiBaseUrl ); const user = await fetchUser(clientInstance, subOrganizationId); if (!user) { @@ -596,7 +595,7 @@ export const TurnkeyProvider: FC<{ config.onSessionCreated?.(newSession); return newSession; }, - [config, setSelectedSession], + [config, setSelectedSession] ); /** @@ -628,14 +627,14 @@ export const TurnkeyProvider: FC<{ const keyToRefresh = sessionKey ?? (await getSelectedSessionKey()); if (!keyToRefresh) { throw new TurnkeyReactNativeError( - "Session not found when refreshing the session. Either the provided sessionKey is invalid, or no session is currently selected.", + "Session not found when refreshing the session. Either the provided sessionKey is invalid, or no session is currently selected." ); } const sessionToRefresh = await getSession(keyToRefresh); if (!isValidSession(sessionToRefresh)) { throw new TurnkeyReactNativeError( - `You cannot refresh session with key "${keyToRefresh}" because it is already expired.`, + `You cannot refresh session with key "${keyToRefresh}" because it is already expired.` ); } @@ -647,7 +646,7 @@ export const TurnkeyProvider: FC<{ const currentClient = createClient( sessionToRefresh!.publicKey, sessionToRefresh!.privateKey, - config.apiBaseUrl, + config.apiBaseUrl ); const sessionResponse = await currentClient.createReadWriteSession({ type: "ACTIVITY_TYPE_CREATE_READ_WRITE_SESSION_V2", @@ -663,7 +662,7 @@ export const TurnkeyProvider: FC<{ ?.credentialBundle; if (!bundle) { throw new TurnkeyReactNativeError( - "Failed to create read/write session when refreshing the session", + "Failed to create read/write session when refreshing the session" ); } @@ -674,12 +673,12 @@ export const TurnkeyProvider: FC<{ const newClient = createClient( newPublicKey, newPrivateKey, - config.apiBaseUrl, + config.apiBaseUrl ); const user = await fetchUser(newClient, config.organizationId); if (!user) { throw new TurnkeyReactNativeError( - "User not found when refreshing the session", + "User not found when refreshing the session" ); } @@ -702,7 +701,7 @@ export const TurnkeyProvider: FC<{ return newSession; }, - [config, session], + [config, session] ); /** @@ -727,7 +726,7 @@ export const TurnkeyProvider: FC<{ if (!keyToClear) { throw new TurnkeyReactNativeError( - "Session not found. Either the provided sessionKey is invalid, or no session is currently selected.", + "Session not found. Either the provided sessionKey is invalid, or no session is currently selected." ); } @@ -747,10 +746,10 @@ export const TurnkeyProvider: FC<{ delete expiryTimeoutsRef.current[keyToClear]; config.onSessionCleared?.( - clearedSession ?? ({ key: keyToClear } as Session), + clearedSession ?? ({ key: keyToClear } as Session) ); }, - [session, config], + [session, config] ); /** @@ -827,7 +826,7 @@ export const TurnkeyProvider: FC<{ return activity; }, - [client, session, refreshUser], + [client, session, refreshUser] ); /** @@ -895,7 +894,7 @@ export const TurnkeyProvider: FC<{ return activity; }, - [client, session, refreshUser], + [client, session, refreshUser] ); /** @@ -929,7 +928,7 @@ export const TurnkeyProvider: FC<{ if (exportBundle == null || embeddedKey == null) { throw new TurnkeyReactNativeError( - "Export bundle or embedded key not initialized", + "Export bundle or embedded key not initialized" ); } @@ -940,7 +939,7 @@ export const TurnkeyProvider: FC<{ returnMnemonic: true, }); }, - [client, session], + [client, session] ); /** @@ -986,7 +985,7 @@ export const TurnkeyProvider: FC<{ return signRawPayloadResult; }, - [client, session], + [client, session] ); /** @@ -1021,10 +1020,6 @@ export const TurnkeyProvider: FC<{ redirectUri?: string; onSuccess: (oidcToken: string) => void; }): Promise => { - if (!(await InAppBrowser.isAvailable())) { - throw new TurnkeyReactNativeError("InAppBrowser is not available"); - } - const finalRedirectUri = redirectUri ? redirectUri : `${TURNKEY__OAUTH_REDIRECT_URL}?scheme=${encodeURIComponent(scheme)}`; @@ -1036,36 +1031,48 @@ export const TurnkeyProvider: FC<{ `&redirectUri=${encodeURIComponent(finalRedirectUri)}` + `&nonce=${encodeURIComponent(nonce)}`; - const result = await InAppBrowser.openAuth(oauthUrl, scheme, { - dismissButtonStyle: "cancel", - animated: true, - modalPresentationStyle: "fullScreen", - modalTransitionStyle: "coverVertical", - modalEnabled: true, - enableBarCollapsing: false, - showTitle: true, - enableUrlBarHiding: true, - enableDefaultShare: true, + const result = await authorize({ + issuer: originUri, + clientId: encodeURIComponent(clientId), + redirectUrl: encodeURIComponent(finalRedirectUri), + additionalParameters: { + nonce: encodeURIComponent(nonce), + }, + scopes: ["openid", "profile", "email"], }); - if (!result || result.type !== "success" || !result.url) { - throw new TurnkeyReactNativeError( - "OAuth flow did not complete successfully", - ); - } - - const resultUrl = new URL(result.url); - const oidcToken = resultUrl.searchParams.get("id_token"); - - if (!oidcToken) { - throw new TurnkeyReactNativeError( - "oidcToken not found in the response", - ); - } - - onSuccess(oidcToken); + console.log("OAuth result:", result); + + // const result = await InAppBrowser.openAuth(oauthUrl, scheme, { + // dismissButtonStyle: "cancel", + // animated: true, + // modalPresentationStyle: "fullScreen", + // modalTransitionStyle: "coverVertical", + // modalEnabled: true, + // enableBarCollapsing: false, + // showTitle: true, + // enableUrlBarHiding: true, + // enableDefaultShare: true, + // }); + + // if (!result || result.type !== "success" || !result.url) { + // throw new TurnkeyReactNativeError( + // "OAuth flow did not complete successfully" + // ); + // } + + // const resultUrl = new URL(result.url); + // const oidcToken = resultUrl.searchParams.get("id_token"); + + // if (!oidcToken) { + // throw new TurnkeyReactNativeError( + // "oidcToken not found in the response" + // ); + // } + + // onSuccess(oidcToken); }, - [], + [] ); const providerValue = useMemo( @@ -1106,7 +1113,7 @@ export const TurnkeyProvider: FC<{ exportWallet, signRawPayload, handleGoogleOAuth, - ], + ] ); return ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d4ad040d..0985a70d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2119,9 +2119,9 @@ importers: react: specifier: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 - react-native-inappbrowser-reborn: - specifier: ^3.7.0 - version: 3.7.0(react-native@0.76.5) + react-native-app-auth: + specifier: ^8.0.3 + version: 8.0.3(react-native@0.76.5) react-native-keychain: specifier: ^8.1.0 || ^9.2.2 version: 8.1.0 @@ -21679,11 +21679,6 @@ packages: is-inside-container: 1.0.0 is-wsl: 2.2.0 - /opencollective-postinstall@2.0.3: - resolution: {integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==} - hasBin: true - dev: false - /optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -22865,15 +22860,18 @@ packages: warning: 4.0.3 dev: false - /react-native-inappbrowser-reborn@3.7.0(react-native@0.76.5): - resolution: {integrity: sha512-Ia53jYNtFcbNaX5W3QfOmN25I7bcvuDiQmSY5zABXjy4+WI20bPc9ua09li55F8yDCjv3C99jX6vKms68mBV7g==} - requiresBuild: true + /react-native-app-auth@8.0.3(react-native@0.76.5): + resolution: {integrity: sha512-x5OhxjlMDRcHRYVWAGh7u+ExQ/fTGBn3tMoHDopez/GPMSgcEqzvsSDrtM85DlG+eL79lSQSJ+X/wt/DPiUZKg==} peerDependencies: - react-native: '>=0.56' + react-native: '>=0.63.0' dependencies: invariant: 2.2.4 - opencollective-postinstall: 2.0.3 react-native: 0.76.5(@babel/core@7.26.9)(@babel/preset-env@7.20.2)(@types/react@18.3.3)(react@18.3.1) + react-native-base64: 0.0.2 + dev: false + + /react-native-base64@0.0.2: + resolution: {integrity: sha512-Fu/J1a2y0X22EJDWqJR2oEa1fpP4gTFjYxk8ElJdt1Yak3HOXmFJ7EohLVHU2DaQkgmKfw8qb7u/48gpzveRbg==} dev: false /react-native-keychain@8.1.0: