From 54e714f29f6662808f25541fd4726e7efa50ae10 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Wed, 8 Jan 2025 20:09:41 -0500 Subject: [PATCH 01/20] feat: add oauth popup logic --- account-kit/rn-signer/example/src/App.tsx | 8 + .../rn-signer/example/src/screens/oauth.tsx | 102 ++++++ account-kit/rn-signer/package.json | 304 +++++++++--------- account-kit/rn-signer/src/client.ts | 172 +++++++++- account-kit/rn-signer/src/errors.ts | 25 ++ account-kit/rn-signer/src/oauth.ts | 36 +++ .../rn-signer/src/utils/base64UrlEncode.ts | 9 + .../rn-signer/src/utils/getDeepLink.ts | 7 + yarn.lock | 175 ++++++++-- 9 files changed, 641 insertions(+), 197 deletions(-) create mode 100644 account-kit/rn-signer/example/src/screens/oauth.tsx create mode 100644 account-kit/rn-signer/src/errors.ts create mode 100644 account-kit/rn-signer/src/oauth.ts create mode 100644 account-kit/rn-signer/src/utils/base64UrlEncode.ts create mode 100644 account-kit/rn-signer/src/utils/getDeepLink.ts diff --git a/account-kit/rn-signer/example/src/App.tsx b/account-kit/rn-signer/example/src/App.tsx index 4316a9b890..642dfb459b 100644 --- a/account-kit/rn-signer/example/src/App.tsx +++ b/account-kit/rn-signer/example/src/App.tsx @@ -6,6 +6,7 @@ import { SafeAreaProvider } from "react-native-safe-area-context"; import OtpAuthScreen from "./screens/otp-auth"; import MagicLinkAuthScreen from "./screens/magic-link-auth"; +import OauthScreen from "./screens/oauth"; const linking = { enabled: "auto" as const /* Automatically generate paths for all screens */, @@ -31,6 +32,13 @@ const RootStack = createBottomTabNavigator({ tabBarIcon: () => 🔑, }, }, + OAuth: { + screen: OauthScreen, + linking: { path: "oauth" }, + options: { + tabBarLabel: "Oauth", + }, + }, }, }); diff --git a/account-kit/rn-signer/example/src/screens/oauth.tsx b/account-kit/rn-signer/example/src/screens/oauth.tsx new file mode 100644 index 0000000000..e4c996a0f0 --- /dev/null +++ b/account-kit/rn-signer/example/src/screens/oauth.tsx @@ -0,0 +1,102 @@ +/* eslint-disable import/extensions */ +import type { User } from "@account-kit/signer"; +import { useEffect, useState } from "react"; +import { View, Text, StyleSheet, TouchableOpacity } from "react-native"; + +import Config from "react-native-config"; +import { RNAlchemySigner } from "@account-kit/react-native-signer"; + +const signer = RNAlchemySigner({ + client: { connection: { apiKey: Config.API_KEY! } }, +}); + +export default function OTPAuthScreen() { + const [user, setUser] = useState(null); + + useEffect(() => { + // get the user if already logged in + signer.getAuthDetails().then(setUser); + }, []); + + return ( + + {!user ? ( + <> + { + signer + .authenticate({ + type: "oauth", + mode: "popup", + authProviderId: "google", + }) + .catch(console.error); + }} + > + Sign in with OAuth + + + ) : ( + <> + + Currently logged in as: {user.email} + + OrgId: {user.orgId} + Address: {user.address} + + signer.disconnect().then(() => setUser(null))} + > + Sign out + + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + justifyContent: "center", + backgroundColor: "#FFFFF", + paddingHorizontal: 20, + }, + textInput: { + width: "100%", + height: 40, + borderColor: "gray", + borderWidth: 1, + paddingHorizontal: 10, + backgroundColor: "rgba(0,0,0,0.05)", + marginTop: 20, + marginBottom: 10, + }, + box: { + width: 60, + height: 60, + marginVertical: 20, + }, + button: { + width: 200, + padding: 10, + height: 50, + backgroundColor: "rgb(147, 197, 253)", + borderRadius: 5, + alignItems: "center", + justifyContent: "center", + marginTop: 20, + }, + buttonText: { + color: "white", + fontWeight: "bold", + textAlign: "center", + }, + userText: { + marginBottom: 10, + fontSize: 18, + }, +}); diff --git a/account-kit/rn-signer/package.json b/account-kit/rn-signer/package.json index d89723e7d1..156b5499d6 100644 --- a/account-kit/rn-signer/package.json +++ b/account-kit/rn-signer/package.json @@ -1,154 +1,154 @@ { - "name": "@account-kit/react-native-signer", - "version": "4.8.0", - "author": "Alchemy", - "description": "React Native compatible Account Kit signer", - "source": "./src/index.tsx", - "main": "./lib/commonjs/index.js", - "module": "./lib/module/index.js", - "exports": { - ".": { - "import": { - "types": "./lib/typescript/module/src/index.d.ts", - "default": "./lib/module/index.js" - }, - "require": { - "types": "./lib/typescript/commonjs/src/index.d.ts", - "default": "./lib/commonjs/index.js" - } - } - }, - "files": [ - "src", - "lib", - "android", - "ios", - "cpp", - "*.podspec", - "react-native.config.json", - "!ios/build", - "!android/build", - "!android/gradle", - "!android/gradlew", - "!android/gradlew.bat", - "!android/local.properties", - "!**/__tests__", - "!**/__fixtures__", - "!**/__mocks__", - "!**/.*" - ], - "scripts": { - "test": "jest", - "typecheck": "tsc", - "build": "yarn typecheck", - "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", - "prepare": "bob build" - }, - "keywords": [ - "react-native", - "ios", - "android" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/alchemyplatform/aa-sdk.git" - }, - "bugs": { - "url": "https://github.com/alchemyplatform/aa-sdk/issues" - }, - "homepage": "https://github.com/alchemyplatform/aa-sdk#readme", - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/" - }, - "devDependencies": { - "@commitlint/config-conventional": "^17.0.2", - "@evilmartians/lefthook": "^1.5.0", - "@react-native-community/cli": "15.0.1", - "@react-native/eslint-config": "^0.76.5", - "@release-it/conventional-changelog": "^5.0.0", - "@types/jest": "^29.5.5", - "@types/react": "^18.2.44", - "commitlint": "^17.0.2", - "del-cli": "^5.1.0", - "eslint": "^8.51.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.1", - "jest": "^29.7.0", - "prettier": "^3.0.3", - "react": "18.3.1", - "react-native": "0.76.5", - "react-native-builder-bob": "^0.30.3", - "react-native-mmkv": "^3.1.0", - "release-it": "^15.0.0", - "turbo": "^1.10.7", - "typescript": "^5.2.2", - "typescript-template": "*" - }, - "resolutions": { - "@types/react": "^18.2.44" - }, - "peerDependencies": { - "react": "*", - "react-native": "*", - "react-native-config": "1.5.3", - "react-native-gesture-handler": "2.21.2", - "react-native-mmkv": "^3.1.0" - }, - "jest": { - "preset": "react-native", - "modulePathIgnorePatterns": [ - "/example/node_modules", - "/lib/" - ] - }, - "react-native-builder-bob": { - "source": "src", - "output": "lib", - "targets": [ - "codegen", - [ - "commonjs", - { - "esm": true - } - ], - [ - "module", - { - "esm": true - } - ], - [ - "typescript", - { - "project": "tsconfig.build.json", - "esm": true - } - ] - ] - }, - "codegenConfig": { - "name": "NativeTEKStamperSpec", - "type": "all", - "jsSrcsDir": "src", - "outputDir": { - "ios": "ios/generated", - "android": "android/generated" - }, - "android": { - "javaPackageName": "com.accountkit.reactnativesigner" - }, - "includesGeneratedCode": true - }, - "create-react-native-library": { - "type": "module-new", - "languages": "kotlin-objc", - "version": "0.42.2" - }, - "dependencies": { - "@aa-sdk/core": "^4.8.0", - "@account-kit/signer": "^4.8.0", - "viem": "^2.21.40" - } + "name": "@account-kit/react-native-signer", + "version": "4.8.0", + "author": "Alchemy", + "description": "React Native compatible Account Kit signer", + "source": "./src/index.tsx", + "main": "./lib/commonjs/index.js", + "module": "./lib/module/index.js", + "exports": { + ".": { + "import": { + "types": "./lib/typescript/module/src/index.d.ts", + "default": "./lib/module/index.js" + }, + "require": { + "types": "./lib/typescript/commonjs/src/index.d.ts", + "default": "./lib/commonjs/index.js" + } + } + }, + "files": [ + "src", + "lib", + "android", + "ios", + "cpp", + "*.podspec", + "react-native.config.json", + "!ios/build", + "!android/build", + "!android/gradle", + "!android/gradlew", + "!android/gradlew.bat", + "!android/local.properties", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__", + "!**/.*" + ], + "scripts": { + "test": "jest", + "typecheck": "tsc", + "build": "yarn typecheck", + "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", + "prepare": "bob build" + }, + "keywords": [ + "react-native", + "ios", + "android" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/alchemyplatform/aa-sdk.git" + }, + "bugs": { + "url": "https://github.com/alchemyplatform/aa-sdk/issues" + }, + "homepage": "https://github.com/alchemyplatform/aa-sdk#readme", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.0.2", + "@evilmartians/lefthook": "^1.5.0", + "@react-native-community/cli": "15.0.1", + "@react-native/eslint-config": "^0.76.5", + "@release-it/conventional-changelog": "^5.0.0", + "@types/jest": "^29.5.5", + "@types/react": "^18.2.44", + "commitlint": "^17.0.2", + "del-cli": "^5.1.0", + "eslint": "^8.51.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.1", + "jest": "^29.7.0", + "prettier": "^3.0.3", + "react": "18.3.1", + "react-native": "0.76.5", + "react-native-builder-bob": "^0.30.3", + "react-native-mmkv": "^3.1.0", + "release-it": "^15.0.0", + "turbo": "^1.10.7", + "typescript": "^5.2.2", + "typescript-template": "*" + }, + "resolutions": { + "@types/react": "^18.2.44" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-config": "1.5.3", + "react-native-gesture-handler": "2.21.2", + "react-native-mmkv": "^3.1.0" + }, + "jest": { + "preset": "react-native", + "modulePathIgnorePatterns": [ + "/example/node_modules", + "/lib/" + ] + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "codegen", + [ + "commonjs", + { + "esm": true + } + ], + [ + "module", + { + "esm": true + } + ], + [ + "typescript", + { + "project": "tsconfig.build.json", + "esm": true + } + ] + ] + }, + "codegenConfig": { + "name": "NativeTEKStamperSpec", + "type": "all", + "jsSrcsDir": "src", + "outputDir": { + "ios": "ios/generated", + "android": "android/generated" + }, + "android": { + "javaPackageName": "com.accountkit.reactnativesigner" + }, + "includesGeneratedCode": true + }, + "create-react-native-library": { + "type": "module-new", + "languages": "kotlin-objc", + "version": "0.42.2" + }, + "dependencies": { + "@aa-sdk/core": "^4.8.0", + "@account-kit/signer": "^4.8.0", + "viem": "^2.21.40" + } } diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index c9d2577a34..478085c358 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -8,6 +8,7 @@ import { type CreateAccountParams, type EmailAuthParams, type GetWebAuthnAttestationResult, + type KnownAuthProvider, type OauthConfig, type OauthParams, type OtpParams, @@ -15,11 +16,30 @@ import { type User, } from "@account-kit/signer"; import NativeTEKStamper from "./NativeTEKStamper"; +import { OAuthProvidersError } from "./errors"; import { z } from "zod"; +import { getDefaultScopeAndClaims, getOauthNonce } from "./oauth"; +import { base64UrlEncode } from "./utils/base64UrlEncode"; +import { InAppBrowser } from "react-native-inappbrowser-reborn"; +import { Alert, Linking } from "react-native"; + +type OauthState = { + authProviderId: string; + isCustomProvider?: boolean; + requestKey: string; + turnkeyPublicKey: string; + expirationSeconds?: number; + redirectUrl?: string; + openerOrigin?: string; +}; export const RNSignerClientParamsSchema = z.object({ connection: z.custom(), rootOrgId: z.string().optional(), + oauthCallbackUrl: z + .string() + .optional() + .default("https://signer.alchemy.com/callback"), }); export type RNSignerClientParams = z.input; @@ -27,18 +47,22 @@ export type RNSignerClientParams = z.input; // TODO: need to emit events export class RNSignerClient extends BaseSignerClient { private stamper = NativeTEKStamper; + oauthCallbackUrl: string; constructor(params: RNSignerClientParams) { - const { connection, rootOrgId } = RNSignerClientParamsSchema.parse(params); + const { connection, rootOrgId, oauthCallbackUrl } = + RNSignerClientParamsSchema.parse(params); super({ stamper: NativeTEKStamper, rootOrgId: rootOrgId ?? "24c1acf5-810f-41e0-a503-d5d13fa8e830", connection, }); + + this.oauthCallbackUrl = oauthCallbackUrl; } override async submitOtpCode( - args: Omit + args: Omit, ): Promise<{ bundle: string }> { this.eventEmitter.emit("authenticating", { type: "otpVerify" }); const publicKey = await this.stamper.init(); @@ -52,7 +76,7 @@ export class RNSignerClient extends BaseSignerClient { } override async createAccount( - params: CreateAccountParams + params: CreateAccountParams, ): Promise { if (params.type !== "email") { throw new Error("Only email account creation is supported"); @@ -74,7 +98,7 @@ export class RNSignerClient extends BaseSignerClient { } override async initEmailAuth( - params: Omit + params: Omit, ): Promise<{ orgId: string }> { this.eventEmitter.emit("authenticating", { type: "email" }); let targetPublicKey = await this.stamper.init(); @@ -117,15 +141,27 @@ export class RNSignerClient extends BaseSignerClient { return user; } override oauthWithRedirect( - _args: Extract + _args: Extract, ): Promise { throw new Error("Method not implemented."); } - override oauthWithPopup( - _args: Extract - ): Promise { - throw new Error("Method not implemented."); - } + override oauthWithPopup = async ( + _args: Extract, + ): Promise => { + try { + const providerUrl = await this.getOauthProviderUrl(_args); + + if (await InAppBrowser.isAvailable()) { + const res = await InAppBrowser.open(providerUrl, {}); + + Alert.alert(JSON.stringify(res)); + } + + return {} as User; + } catch (e) { + throw new Error("Error performing OAuth Operation"); + } + }; override async disconnect(): Promise { this.user = undefined; @@ -138,13 +174,121 @@ export class RNSignerClient extends BaseSignerClient { override lookupUserWithPasskey(_user?: User): Promise { throw new Error("Method not implemented."); } - protected override getOauthConfig(): Promise { - throw new Error("Method not implemented."); - } + protected override getWebAuthnAttestation( _options: CredentialCreationOptions, - _userDetails?: { username: string } + _userDetails?: { username: string }, ): Promise { throw new Error("Method not implemented."); } + + private getOauthProviderUrl = async (args: OauthParams): Promise => { + const { + authProviderId, + isCustomProvider, + auth0Connection, + scope: providedScope, + claims: providedClaims, + expirationSeconds, + } = args; + + const { codeChallenge, requestKey, authProviders } = + await this.getOauthConfig(); + + if (!authProviders) { + throw new OAuthProvidersError(); + } + + const authProvider = authProviders.find( + (provider) => + provider.id === authProviderId && + !!provider.isCustomProvider === !!isCustomProvider, + ); + + if (!authProvider) { + throw new Error(`OAuth provider with id ${authProviderId} not found`); + } + + let scope: string; + let claims: string | undefined; + + if (providedScope) { + scope = addOpenIdIfAbsent(providedScope); + claims = providedClaims; + } else { + if (isCustomProvider) { + throw new Error("scope must be provided for a custom provider"); + } + + const scopeAndClaims = getDefaultScopeAndClaims( + authProviderId as KnownAuthProvider, + ); + if (!scopeAndClaims) { + throw new Error( + `Default scope not known for provider ${authProviderId}`, + ); + } + ({ scope, claims } = scopeAndClaims); + } + const { authEndpoint, clientId } = authProvider; + const turnkeyPublicKey = await this.stamper.init(); + const nonce = getOauthNonce(turnkeyPublicKey); + + const appDeepLink = (await Linking.getInitialURL()) ?? undefined; + + const stateObject: OauthState = { + authProviderId, + isCustomProvider, + requestKey, + turnkeyPublicKey, + expirationSeconds, + redirectUrl: undefined, // We only use the 'Popup' mode in RN + openerOrigin: appDeepLink, + }; + + const state = base64UrlEncode( + new TextEncoder().encode(JSON.stringify(stateObject)).buffer, + ); + const authUrl = new URL(authEndpoint); + const params: Record = { + redirect_uri: this.oauthCallbackUrl, + response_type: "code", + scope, + state, + code_challenge: codeChallenge, + code_challenge_method: "S256", + prompt: "select_account", + client_id: clientId, + nonce, + }; + if (claims) { + params.claims = claims; + } + if (auth0Connection) { + params.connection = auth0Connection; + } + authUrl.search = new URLSearchParams(params).toString(); + return authUrl.toString(); + }; + + protected override getOauthConfig = async (): Promise => { + if (this.oauthConfig) { + return this.oauthConfig; + } else { + throw new Error( + "enablePopupOauth must be set in configuration or signer.preparePopupOauth must be called before using popup-based OAuth login", + ); + } + }; +} + +/** + * "openid" is a required scope in the OIDC protocol. Insert it if the user + * forgot. + * + * @param {string} scope scope param which may be missing "openid" + * @returns {string} scope which most definitely contains "openid" + */ +function addOpenIdIfAbsent(scope: string): string { + return scope.match(/\bopenid\b/) ? scope : `openid ${scope}`; } diff --git a/account-kit/rn-signer/src/errors.ts b/account-kit/rn-signer/src/errors.ts new file mode 100644 index 0000000000..8cda618931 --- /dev/null +++ b/account-kit/rn-signer/src/errors.ts @@ -0,0 +1,25 @@ +import { BaseError } from "@aa-sdk/core"; + +export class NotAuthenticatedError extends BaseError { + override name = "NotAuthenticatedError"; + constructor() { + super( + [ + "Signer not authenticated", + "Please authenticate to use this signer", + ].join("\n"), + { + docsPath: "/signers/alchemy-signer/introduction.html", + }, + ); + } +} + +export class OAuthProvidersError extends BaseError { + override name = "OAuthProvidersError"; + constructor() { + super("OAuth providers not found", { + docsPath: "/react/getting-started", + }); + } +} diff --git a/account-kit/rn-signer/src/oauth.ts b/account-kit/rn-signer/src/oauth.ts new file mode 100644 index 0000000000..2d28cf6fe7 --- /dev/null +++ b/account-kit/rn-signer/src/oauth.ts @@ -0,0 +1,36 @@ +import { sha256 } from "viem"; +import type { KnownAuthProvider } from "@account-kit/signer"; + +/** + * Turnkey requires the nonce in the id token to be in this format. + * + * @param {string} turnkeyPublicKey key from a Turnkey iframe + * @returns {string} nonce to be used in OIDC + */ +export function getOauthNonce(turnkeyPublicKey: string): string { + return sha256(new TextEncoder().encode(turnkeyPublicKey)).slice(2); +} + +export type ScopeAndClaims = { + scope: string; + claims?: string; +}; + +const DEFAULT_SCOPE_AND_CLAIMS: Record = { + google: { scope: "openid email" }, + apple: { scope: "openid email" }, + facebook: { scope: "openid email" }, + auth0: { scope: "openid email" }, +}; + +/** + * Returns the default scope and claims when using a known auth provider + * + * @param {string} knownAuthProviderId id of a known auth provider, e.g. "google" + * @returns {ScopeAndClaims | undefined} default scope and claims + */ +export function getDefaultScopeAndClaims( + knownAuthProviderId: KnownAuthProvider, +): ScopeAndClaims | undefined { + return DEFAULT_SCOPE_AND_CLAIMS[knownAuthProviderId]; +} diff --git a/account-kit/rn-signer/src/utils/base64UrlEncode.ts b/account-kit/rn-signer/src/utils/base64UrlEncode.ts new file mode 100644 index 0000000000..e8044f7c9b --- /dev/null +++ b/account-kit/rn-signer/src/utils/base64UrlEncode.ts @@ -0,0 +1,9 @@ +export const base64UrlEncode = ( + challenge: ArrayBuffer | ArrayBufferLike, +): string => { + return Buffer.from(challenge) + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); +}; diff --git a/account-kit/rn-signer/src/utils/getDeepLink.ts b/account-kit/rn-signer/src/utils/getDeepLink.ts new file mode 100644 index 0000000000..ccbac7c99b --- /dev/null +++ b/account-kit/rn-signer/src/utils/getDeepLink.ts @@ -0,0 +1,7 @@ +import { Platform } from "react-native"; +export const getDeepLink = (path = "") => { + const scheme = "rn-signer-demo"; + const prefix = + Platform.OS === "android" ? `${scheme}://my-host/` : `${scheme}://`; + return prefix + path; +}; diff --git a/yarn.lock b/yarn.lock index 00390a71a7..8411e427b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,6 +22,11 @@ resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz" integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== +"@adraffy/ens-normalize@^1.10.1": + version "1.11.0" + resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33" + integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg== + "@alloc/quick-lru@^5.2.0": version "5.2.0" resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" @@ -4755,6 +4760,13 @@ dependencies: "@noble/hashes" "1.4.0" +"@noble/curves@1.7.0", "@noble/curves@~1.7.0": + version "1.7.0" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz#0512360622439256df892f21d25b388f52505e45" + integrity sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw== + dependencies: + "@noble/hashes" "1.6.0" + "@noble/curves@^1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz" @@ -4769,6 +4781,13 @@ dependencies: "@noble/hashes" "1.4.0" +"@noble/curves@^1.6.0", "@noble/curves@~1.8.0": + version "1.8.0" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.0.tgz#fe035a23959e6aeadf695851b51a87465b5ba8f7" + integrity sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ== + dependencies: + "@noble/hashes" "1.7.0" + "@noble/curves@~1.4.0": version "1.4.2" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz" @@ -4791,6 +4810,21 @@ resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== +"@noble/hashes@1.6.0": + version "1.6.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz#d4bfb516ad6e7b5111c216a5cc7075f4cf19e6c5" + integrity sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ== + +"@noble/hashes@1.6.1", "@noble/hashes@~1.6.0": + version "1.6.1" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.1.tgz#df6e5943edcea504bac61395926d6fd67869a0d5" + integrity sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w== + +"@noble/hashes@1.7.0", "@noble/hashes@~1.7.0": + version "1.7.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz#5d9e33af2c7d04fee35de1519b80c958b2e35e39" + integrity sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w== + "@noble/hashes@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" @@ -6773,6 +6807,11 @@ resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz" integrity sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g== +"@scure/base@~1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.1.tgz#dd0b2a533063ca612c17aa9ad26424a2ff5aa865" + integrity sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ== + "@scure/bip32@1.3.1": version "1.3.1" resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz" @@ -6791,6 +6830,24 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" +"@scure/bip32@1.6.0": + version "1.6.0" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.0.tgz#6dbc6b4af7c9101b351f41231a879d8da47e0891" + integrity sha512-82q1QfklrUUdXJzjuRU7iG7D7XiFx5PHYVS0+oeNKhyDLT7WPqs6pBcM2W5ZdwOwKCwoE1Vy1se+DHjcXwCYnA== + dependencies: + "@noble/curves" "~1.7.0" + "@noble/hashes" "~1.6.0" + "@scure/base" "~1.2.1" + +"@scure/bip32@^1.5.0": + version "1.6.1" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.1.tgz#848eca1bc96f6b5ce6aa750798853fb142dace05" + integrity sha512-jSO+5Ud1E588Y+LFo8TaB8JVPNAZw/lGGao+1SepHDeTs2dFLurdNIAgUuDlwezqEjRjElkCJajVrtrZaBxvaQ== + dependencies: + "@noble/curves" "~1.8.0" + "@noble/hashes" "~1.7.0" + "@scure/base" "~1.2.1" + "@scure/bip39@1.2.1": version "1.2.1" resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz" @@ -6807,6 +6864,22 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" +"@scure/bip39@1.5.0": + version "1.5.0" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.0.tgz#c8f9533dbd787641b047984356531d84485f19be" + integrity sha512-Dop+ASYhnrwm9+HA/HwXg7j2ZqM6yk2fyLWb5znexjctFY3+E+eU8cIWI0Pql0Qx4hPZCijlGq4OL71g+Uz30A== + dependencies: + "@noble/hashes" "~1.6.0" + "@scure/base" "~1.2.1" + +"@scure/bip39@^1.4.0": + version "1.5.1" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.1.tgz#a056868d672c7203a6035c808893742a79e151f6" + integrity sha512-GnlufVSP9UdAo/H2Patfv22VTtpNTyfi+I3qCKpvuB5l1KWzEYx+l2TNpBy9Ksh4xTs3Rn06tBlpWCi/1Vz8gw== + dependencies: + "@noble/hashes" "~1.7.0" + "@scure/base" "~1.2.1" + "@sec-ant/readable-stream@^0.4.1": version "0.4.1" resolved "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz" @@ -8509,7 +8582,7 @@ dependencies: "@types/react" "^18" -"@types/react@*", "@types/react@^16.8.0 || ^17.0.0 || ^18.0.0", "@types/react@^18", "@types/react@^18.2.44": +"@types/react@*", "@types/react@^16.8.0 || ^17.0.0 || ^18.0.0", "@types/react@^18", "@types/react@^18.2.44", "@types/react@^18.2.6", "@types/react@~18.3.12": version "18.3.12" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.12.tgz#99419f182ccd69151813b7ee24b792fe08774f60" integrity sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw== @@ -8517,22 +8590,6 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@^18.2.6": - version "18.3.14" - resolved "https://registry.npmjs.org/@types/react/-/react-18.3.14.tgz#7ce43bbca0e15e1c4f67ad33ea3f83d75aa6756b" - integrity sha512-NzahNKvjNhVjuPBQ+2G7WlxstQ+47kXZNHlUvFakDViuIEfGY926GqhMueQFZ7woG+sPiQKlF36XfrIUVSUfFg== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - -"@types/react@~18.3.12": - version "18.3.16" - resolved "https://registry.npmjs.org/@types/react/-/react-18.3.16.tgz#5326789125fac98b718d586ad157442ceb44ff28" - integrity sha512-oh8AMIC4Y2ciKufU8hnKgs+ufgbA/dhPTACaZPM86AbwX9QwnFtSoPWEeRUj8fge+v6kFt78BXcDhAU1SrrAsw== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - "@types/resolve@^1.20.2": version "1.20.6" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz" @@ -9981,11 +10038,21 @@ abitype@1.0.5: resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.5.tgz" integrity sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw== +abitype@1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.7.tgz#876a0005d211e1c9132825d45bcee7b46416b284" + integrity sha512-ZfYYSktDQUwc2eduYu8C4wOs+RDPmnRYMh7zNfzeMtGGgb0U+6tLGjixUic6mXf5xKKCcgT5Qp6cv39tOARVFw== + abitype@^0.8.3: version "0.8.11" resolved "https://registry.npmjs.org/abitype/-/abitype-0.8.11.tgz" integrity sha512-bM4v2dKvX08sZ9IU38IN5BKmN+ZkOSd2oI4a9f0ejHYZQYV6cDr7j+d95ga0z2XHG36Y4jzoG5Z7qDqxp7fi/A== +abitype@^1.0.6: + version "1.0.8" + resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba" + integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" @@ -18037,6 +18104,11 @@ isows@1.0.4: resolved "https://registry.npmjs.org/isows/-/isows-1.0.4.tgz" integrity sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ== +isows@1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7" + integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== + issue-parser@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/issue-parser/-/issue-parser-6.0.0.tgz#b1edd06315d4f2044a9755daf85fdafde9b4014a" @@ -22662,6 +22734,11 @@ openai@^4.51.0: node-fetch "^2.6.7" web-streams-polyfill "^3.2.1" +opencollective-postinstall@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -22810,6 +22887,19 @@ outvariant@^1.4.0, outvariant@^1.4.2, outvariant@^1.4.3: resolved "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz" integrity sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA== +ox@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/ox/-/ox-0.6.0.tgz#ba8f89d68b5e8afe717c8a947ffacc0669ea155d" + integrity sha512-blUzTLidvUlshv0O02CnLFqBLidNzPoAZdIth894avUAotTuWziznv6IENv5idRuOSSP3dH8WzcYw84zVdu0Aw== + dependencies: + "@adraffy/ens-normalize" "^1.10.1" + "@noble/curves" "^1.6.0" + "@noble/hashes" "^1.5.0" + "@scure/bip32" "^1.5.0" + "@scure/bip39" "^1.4.0" + abitype "^1.0.6" + eventemitter3 "5.0.1" + p-cancelable@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" @@ -24355,6 +24445,14 @@ react-native-helmet-async@2.0.4: react-fast-compare "^3.2.2" shallowequal "^1.1.0" +react-native-inappbrowser-reborn@^3.7.0: + version "3.7.0" + resolved "https://registry.npmjs.org/react-native-inappbrowser-reborn/-/react-native-inappbrowser-reborn-3.7.0.tgz#849a43c3c7da22b65147649fe596836bcb494083" + integrity sha512-Ia53jYNtFcbNaX5W3QfOmN25I7bcvuDiQmSY5zABXjy4+WI20bPc9ua09li55F8yDCjv3C99jX6vKms68mBV7g== + dependencies: + invariant "^2.2.4" + opencollective-postinstall "^2.0.3" + react-native-is-edge-to-edge@^1.1.6: version "1.1.6" resolved "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.6.tgz#69ec13f70d76e9245e275eed4140d0873a78f902" @@ -28383,7 +28481,7 @@ vfile@^6.0.0, vfile@^6.0.1: unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" -viem@2.20.0, viem@^2.1.1, viem@^2.20.0, viem@^2.21.40, viem@^2.21.41: +viem@2.20.0, viem@^2.1.1, viem@^2.20.0: version "2.20.0" resolved "https://registry.npmjs.org/viem/-/viem-2.20.0.tgz" integrity sha512-cM4vs81HnSNbfceI1MLkx4pCVzbVjl9xiNSv5SCutYjUyFFOVSPDlEyhpg2iHinxx1NM4Qne3END5eLT8rvUdg== @@ -28398,6 +28496,21 @@ viem@2.20.0, viem@^2.1.1, viem@^2.20.0, viem@^2.21.40, viem@^2.21.41: webauthn-p256 "0.0.5" ws "8.17.1" +viem@^2.21.40, viem@^2.21.41: + version "2.22.5" + resolved "https://registry.npmjs.org/viem/-/viem-2.22.5.tgz#930846b41747cc9815257f4a009d2f8c00e02567" + integrity sha512-rked1t/qPQAFNTLoFU18/OCv5KdbIZPwAwlY2X7dtH8unt28kccN6RBrjEjhNrY6wQlbO4phGwslNUzd8a2faw== + dependencies: + "@noble/curves" "1.7.0" + "@noble/hashes" "1.6.1" + "@scure/bip32" "1.6.0" + "@scure/bip39" "1.5.0" + abitype "1.0.7" + isows "1.0.6" + ox "0.6.0" + webauthn-p256 "0.0.10" + ws "8.18.0" + vite-node@2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/vite-node/-/vite-node-2.0.4.tgz" @@ -28636,18 +28749,18 @@ web-vitals@0.2.4: resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz" integrity sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg== -webauthn-p256@0.0.5: - version "0.0.5" - resolved "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.5.tgz" - integrity sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg== +webauthn-p256@0.0.10, webauthn-p256@^0.0.10: + version "0.0.10" + resolved "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.10.tgz" + integrity sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA== dependencies: "@noble/curves" "^1.4.0" "@noble/hashes" "^1.4.0" -webauthn-p256@^0.0.10: - version "0.0.10" - resolved "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.10.tgz" - integrity sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA== +webauthn-p256@0.0.5: + version "0.0.5" + resolved "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.5.tgz" + integrity sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg== dependencies: "@noble/curves" "^1.4.0" "@noble/hashes" "^1.4.0" @@ -28979,6 +29092,11 @@ ws@8.17.1: resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== +ws@8.18.0, ws@^8.12.1, ws@^8.18.0, ws@^8.2.3: + version "8.18.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + ws@^6.2.3: version "6.2.3" resolved "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz" @@ -28996,11 +29114,6 @@ ws@^7.5.1: resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@^8.12.1, ws@^8.18.0, ws@^8.2.3: - version "8.18.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== - ws@~8.11.0: version "8.11.0" resolved "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz" From 3b017caf56d17c409604e82cb31a73a5e146becc Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Fri, 10 Jan 2025 17:21:02 -0500 Subject: [PATCH 02/20] feat: add basic oauth implementation for mobile --- account-kit/rn-signer/example/metro.config.js | 31 ++-- account-kit/rn-signer/example/package.json | 111 +++++++------- .../example/src/screens/magic-link-auth.tsx | 5 +- .../rn-signer/example/src/screens/oauth.tsx | 11 +- .../example/src/screens/otp-auth.tsx | 5 +- account-kit/rn-signer/package.json | 4 +- account-kit/rn-signer/src/client.ts | 100 ++++++++---- .../rn-signer/src/utils/base64UrlEncode.ts | 4 +- .../rn-signer/src/utils/buffer-polyfill.ts | 5 + yarn.lock | 144 ++---------------- 10 files changed, 186 insertions(+), 234 deletions(-) create mode 100644 account-kit/rn-signer/src/utils/buffer-polyfill.ts diff --git a/account-kit/rn-signer/example/metro.config.js b/account-kit/rn-signer/example/metro.config.js index ead41fe3b5..31c21922e1 100644 --- a/account-kit/rn-signer/example/metro.config.js +++ b/account-kit/rn-signer/example/metro.config.js @@ -1,8 +1,5 @@ const path = require("path"); const { getDefaultConfig } = require("@react-native/metro-config"); -const { getConfig } = require("react-native-builder-bob/metro-config"); -const pkg = require("../package.json"); - const rnSignerRoot = path.resolve(__dirname, ".."); const projectRoot = __dirname; // handles the hoisted modules @@ -11,33 +8,33 @@ const repoRoot = path.resolve(__dirname, "../../.."); const config = getDefaultConfig(projectRoot); const monorepoPackages = { - "@account-kit/signer": path.resolve(repoRoot, "account-kit/signer"), - "@aa-sdk/core": path.resolve(repoRoot, "aa-sdk/core"), - "@account-kit/logging": path.resolve(repoRoot, "account-kit/logging"), + "@account-kit/signer": path.resolve(repoRoot, "account-kit/signer"), + "@aa-sdk/core": path.resolve(repoRoot, "aa-sdk/core"), + "@account-kit/logging": path.resolve(repoRoot, "account-kit/logging"), }; config.watchFolders = [ - projectRoot, - rnSignerRoot, - ...Object.values(monorepoPackages), + projectRoot, + rnSignerRoot, + ...Object.values(monorepoPackages), ]; // Let Metro know where to resolve packages and in what order config.resolver.nodeModulesPaths = [ - path.resolve(projectRoot, "node_modules"), - path.resolve(rnSignerRoot, "node_modules"), - path.resolve(repoRoot, "node_modules"), + path.resolve(projectRoot, "node_modules"), + path.resolve(rnSignerRoot, "node_modules"), + path.resolve(repoRoot, "node_modules"), ]; // Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths` config.resolver.disableHierarchicalLookup = true; config.resolver.extraNodeModules = { - ...config.resolver.extraNodeModules, - ...require("node-libs-react-native"), - ...monorepoPackages, - crypto: require.resolve("crypto-browserify"), - stream: require.resolve("stream-browserify"), + ...config.resolver.extraNodeModules, + ...require("node-libs-react-native"), + ...monorepoPackages, + crypto: require.resolve("crypto-browserify"), + stream: require.resolve("stream-browserify"), }; /** diff --git a/account-kit/rn-signer/example/package.json b/account-kit/rn-signer/example/package.json index 1ee447fc59..fc3efaf703 100644 --- a/account-kit/rn-signer/example/package.json +++ b/account-kit/rn-signer/example/package.json @@ -1,57 +1,58 @@ { - "name": "@account-kit/react-native-signer-example", - "version": "0.0.1", - "private": true, - "scripts": { - "android": "react-native run-android && yarn start:redirect-server", - "ios": "react-native run-ios", - "start": "react-native start", - "start:redirect-server": "tsx ./redirect-server/index.ts", - "build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"", - "build:ios": "react-native build-ios --scheme ReactNativeSignerExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\"" - }, - "dependencies": { - "@account-kit/signer": "^4.3.0", - "@react-navigation/bottom-tabs": "^7.2.0", - "@react-navigation/native": "7.0.3", - "@react-navigation/native-stack": "7.1.0", - "dotenv": "^16.4.5", - "express": "^4.21.1", - "react": "18.3.1", - "react-native": "0.76.5", - "react-native-config": "1.5.3", - "react-native-gesture-handler": "2.21.2", - "react-native-mmkv": "3.1.0", - "react-native-safe-area-context": "4.14.0", - "react-native-screens": "4.2.0", - "util": "^0.12.5", - "viem": "^2.21.41", - "zustand": "^5.0.1" - }, - "peerDependencies": { - "@types/react": "18.3.1", - "react": "18.3.1", - "react-native": "0.76.1" - }, - "devDependencies": { - "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.3", - "@babel/runtime": "^7.25.0", - "@react-native-community/cli": "15.0.1", - "@react-native-community/cli-platform-android": "15.0.1", - "@react-native-community/cli-platform-ios": "15.0.1", - "@react-native/babel-preset": "0.76.5", - "@react-native/metro-config": "0.76.5", - "@react-native/typescript-config": "0.76.5", - "react-native-builder-bob": "^0.30.3", - "tsx": "^4.19.2" - }, - "engines": { - "node": ">=18" - }, - "workspaces": { - "nohoist": [ - "**" - ] - } + "name": "@account-kit/react-native-signer-example", + "version": "0.0.1", + "private": true, + "scripts": { + "android": "react-native run-android && yarn start:redirect-server", + "ios": "react-native run-ios", + "start": "react-native start", + "start:redirect-server": "tsx ./redirect-server/index.ts", + "build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"", + "build:ios": "react-native build-ios --scheme ReactNativeSignerExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\"" + }, + "dependencies": { + "@account-kit/signer": "^4.3.0", + "@react-navigation/bottom-tabs": "^7.2.0", + "@react-navigation/native": "7.0.3", + "@react-navigation/native-stack": "7.1.0", + "dotenv": "^16.4.5", + "express": "^4.21.1", + "react": "18.3.1", + "react-native": "0.76.5", + "react-native-config": "1.5.3", + "react-native-gesture-handler": "2.21.2", + "react-native-inappbrowser-reborn": "^3.7.0", + "react-native-mmkv": "3.1.0", + "react-native-safe-area-context": "4.14.0", + "react-native-screens": "4.2.0", + "util": "^0.12.5", + "viem": "^2.21.41", + "zustand": "^5.0.1" + }, + "peerDependencies": { + "@types/react": "18.3.1", + "react": "18.3.1", + "react-native": "0.76.1" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.3", + "@babel/runtime": "^7.25.0", + "@react-native-community/cli": "15.0.1", + "@react-native-community/cli-platform-android": "15.0.1", + "@react-native-community/cli-platform-ios": "15.0.1", + "@react-native/babel-preset": "0.76.5", + "@react-native/metro-config": "0.76.5", + "@react-native/typescript-config": "0.76.5", + "react-native-builder-bob": "^0.30.3", + "tsx": "^4.19.2" + }, + "engines": { + "node": ">=18" + }, + "workspaces": { + "nohoist": [ + "**" + ] + } } diff --git a/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx b/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx index fd59af8be2..34a43d6f2b 100644 --- a/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx +++ b/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx @@ -14,7 +14,10 @@ import Config from "react-native-config"; export default function MagicLinkAuthScreen() { const signer = RNAlchemySigner({ - client: { connection: { apiKey: Config.API_KEY! } }, + client: { + connection: { apiKey: Config.API_KEY! }, + appScheme: "rn-signer-demo", + }, }); const [email, setEmail] = useState(""); diff --git a/account-kit/rn-signer/example/src/screens/oauth.tsx b/account-kit/rn-signer/example/src/screens/oauth.tsx index e4c996a0f0..dfb46a4837 100644 --- a/account-kit/rn-signer/example/src/screens/oauth.tsx +++ b/account-kit/rn-signer/example/src/screens/oauth.tsx @@ -7,15 +7,21 @@ import Config from "react-native-config"; import { RNAlchemySigner } from "@account-kit/react-native-signer"; const signer = RNAlchemySigner({ - client: { connection: { apiKey: Config.API_KEY! } }, + client: { + connection: { apiKey: Config.API_KEY! }, + appScheme: "rn-signer-demo", + }, }); +console.log("API KEY: ", Config.API_KEY); + export default function OTPAuthScreen() { const [user, setUser] = useState(null); useEffect(() => { // get the user if already logged in signer.getAuthDetails().then(setUser); + signer.preparePopupOauth(); }, []); return ( @@ -28,8 +34,9 @@ export default function OTPAuthScreen() { signer .authenticate({ type: "oauth", - mode: "popup", + mode: "redirect", authProviderId: "google", + redirectUrl: "rn-signer-demo:///", }) .catch(console.error); }} diff --git a/account-kit/rn-signer/example/src/screens/otp-auth.tsx b/account-kit/rn-signer/example/src/screens/otp-auth.tsx index da8263c735..1fcaa15992 100644 --- a/account-kit/rn-signer/example/src/screens/otp-auth.tsx +++ b/account-kit/rn-signer/example/src/screens/otp-auth.tsx @@ -13,7 +13,10 @@ import Config from "react-native-config"; import { RNAlchemySigner } from "@account-kit/react-native-signer"; const signer = RNAlchemySigner({ - client: { connection: { apiKey: Config.API_KEY! } }, + client: { + connection: { apiKey: Config.API_KEY! }, + appScheme: "rn-signer-demo", + }, }); export default function OTPAuthScreen() { diff --git a/account-kit/rn-signer/package.json b/account-kit/rn-signer/package.json index 156b5499d6..d88d4e9459 100644 --- a/account-kit/rn-signer/package.json +++ b/account-kit/rn-signer/package.json @@ -80,6 +80,7 @@ "react-native": "0.76.5", "react-native-builder-bob": "^0.30.3", "react-native-mmkv": "^3.1.0", + "react-native-inappbrowser-reborn": "^3.7.0", "release-it": "^15.0.0", "turbo": "^1.10.7", "typescript": "^5.2.2", @@ -93,7 +94,8 @@ "react-native": "*", "react-native-config": "1.5.3", "react-native-gesture-handler": "2.21.2", - "react-native-mmkv": "^3.1.0" + "react-native-mmkv": "^3.1.0", + "react-native-inappbrowser-reborn": "^3.7.0" }, "jest": { "preset": "react-native", diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 478085c358..986573f0d1 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -1,5 +1,6 @@ /* eslint-disable import/extensions */ import "./utils/mmkv-localstorage-polyfill"; +import "./utils/buffer-polyfill"; import { type ConnectionConfig } from "@aa-sdk/core"; import { BaseSignerClient, @@ -10,6 +11,7 @@ import { type GetWebAuthnAttestationResult, type KnownAuthProvider, type OauthConfig, + type OauthMode, type OauthParams, type OtpParams, type SignupResponse, @@ -21,7 +23,7 @@ import { z } from "zod"; import { getDefaultScopeAndClaims, getOauthNonce } from "./oauth"; import { base64UrlEncode } from "./utils/base64UrlEncode"; import { InAppBrowser } from "react-native-inappbrowser-reborn"; -import { Alert, Linking } from "react-native"; +import { Alert } from "react-native"; type OauthState = { authProviderId: string; @@ -40,6 +42,7 @@ export const RNSignerClientParamsSchema = z.object({ .string() .optional() .default("https://signer.alchemy.com/callback"), + appScheme: z.string().optional(), }); export type RNSignerClientParams = z.input; @@ -48,8 +51,11 @@ export type RNSignerClientParams = z.input; export class RNSignerClient extends BaseSignerClient { private stamper = NativeTEKStamper; oauthCallbackUrl: string; + appScheme: string; + private appDeeplink: string; + constructor(params: RNSignerClientParams) { - const { connection, rootOrgId, oauthCallbackUrl } = + const { connection, rootOrgId, oauthCallbackUrl, appScheme } = RNSignerClientParamsSchema.parse(params); super({ @@ -58,11 +64,15 @@ export class RNSignerClient extends BaseSignerClient { connection, }); + this.appScheme = appScheme ?? ""; + + this.appDeeplink = `${this.appScheme}://`; + this.oauthCallbackUrl = oauthCallbackUrl; } override async submitOtpCode( - args: Omit, + args: Omit ): Promise<{ bundle: string }> { this.eventEmitter.emit("authenticating", { type: "otpVerify" }); const publicKey = await this.stamper.init(); @@ -76,7 +86,7 @@ export class RNSignerClient extends BaseSignerClient { } override async createAccount( - params: CreateAccountParams, + params: CreateAccountParams ): Promise { if (params.type !== "email") { throw new Error("Only email account creation is supported"); @@ -98,7 +108,7 @@ export class RNSignerClient extends BaseSignerClient { } override async initEmailAuth( - params: Omit, + params: Omit ): Promise<{ orgId: string }> { this.eventEmitter.emit("authenticating", { type: "email" }); let targetPublicKey = await this.stamper.init(); @@ -140,25 +150,40 @@ export class RNSignerClient extends BaseSignerClient { this.eventEmitter.emit(params.connectedEventName, user, params.bundle); return user; } - override oauthWithRedirect( - _args: Extract, - ): Promise { - throw new Error("Method not implemented."); - } + override oauthWithRedirect = async ( + _args: Extract + ): Promise => { + try { + const providerUrl = await this.getOauthProviderUrl(_args); + + if (await InAppBrowser.isAvailable()) { + const res = await InAppBrowser.openAuth(providerUrl, this.appDeeplink); + + Alert.alert(JSON.stringify(res)); + } + return new Promise((_, reject) => + setTimeout(() => reject("Failed to redirect to OAuth provider"), 1000) + ); + } catch (e) { + console.error("Error performing OAuth Operation", e); + throw new Error("Error performing OAuth Operation"); + } + }; override oauthWithPopup = async ( - _args: Extract, + _args: Extract ): Promise => { try { const providerUrl = await this.getOauthProviderUrl(_args); if (await InAppBrowser.isAvailable()) { - const res = await InAppBrowser.open(providerUrl, {}); + const res = await InAppBrowser.openAuth(providerUrl, this.appDeeplink); Alert.alert(JSON.stringify(res)); } return {} as User; } catch (e) { + console.error("Error performing OAuth Operation", e); throw new Error("Error performing OAuth Operation"); } }; @@ -177,7 +202,7 @@ export class RNSignerClient extends BaseSignerClient { protected override getWebAuthnAttestation( _options: CredentialCreationOptions, - _userDetails?: { username: string }, + _userDetails?: { username: string } ): Promise { throw new Error("Method not implemented."); } @@ -190,10 +215,13 @@ export class RNSignerClient extends BaseSignerClient { scope: providedScope, claims: providedClaims, expirationSeconds, + redirectUrl, + mode, } = args; - const { codeChallenge, requestKey, authProviders } = - await this.getOauthConfig(); + const res = await this.getOauthConfigForMode("popup"); + + const { codeChallenge, requestKey, authProviders } = res; if (!authProviders) { throw new OAuthProvidersError(); @@ -202,7 +230,7 @@ export class RNSignerClient extends BaseSignerClient { const authProvider = authProviders.find( (provider) => provider.id === authProviderId && - !!provider.isCustomProvider === !!isCustomProvider, + !!provider.isCustomProvider === !!isCustomProvider ); if (!authProvider) { @@ -221,11 +249,11 @@ export class RNSignerClient extends BaseSignerClient { } const scopeAndClaims = getDefaultScopeAndClaims( - authProviderId as KnownAuthProvider, + authProviderId as KnownAuthProvider ); if (!scopeAndClaims) { throw new Error( - `Default scope not known for provider ${authProviderId}`, + `Default scope not known for provider ${authProviderId}` ); } ({ scope, claims } = scopeAndClaims); @@ -234,22 +262,21 @@ export class RNSignerClient extends BaseSignerClient { const turnkeyPublicKey = await this.stamper.init(); const nonce = getOauthNonce(turnkeyPublicKey); - const appDeepLink = (await Linking.getInitialURL()) ?? undefined; - const stateObject: OauthState = { authProviderId, isCustomProvider, requestKey, turnkeyPublicKey, expirationSeconds, - redirectUrl: undefined, // We only use the 'Popup' mode in RN - openerOrigin: appDeepLink, + redirectUrl: mode === "redirect" ? redirectUrl : undefined, // We only use the 'Popup' mode in RN + openerOrigin: mode === "popup" ? this.appDeeplink : undefined, }; const state = base64UrlEncode( - new TextEncoder().encode(JSON.stringify(stateObject)).buffer, + new TextEncoder().encode(JSON.stringify(stateObject)).buffer ); const authUrl = new URL(authEndpoint); + const params: Record = { redirect_uri: this.oauthCallbackUrl, response_type: "code", @@ -267,16 +294,37 @@ export class RNSignerClient extends BaseSignerClient { if (auth0Connection) { params.connection = auth0Connection; } - authUrl.search = new URLSearchParams(params).toString(); - return authUrl.toString(); + + Object.keys(params).forEach((param) => { + params[param] && authUrl.searchParams.append(param, params[param]); + }); + + const [urlPath, searchParams] = authUrl.href.split("?"); + + // Ensure to prevent potential trailing backslashes. + return `${urlPath?.replace(/\/$/, "")}?${searchParams}`; }; protected override getOauthConfig = async (): Promise => { + const currentStamper = this.turnkeyClient.stamper; + const publicKey = await this.stamper.init(); + + // swap the stamper back in case the user logged in with a different stamper (passkeys) + this.setStamper(currentStamper); + const nonce = getOauthNonce(publicKey); + return this.request("/v1/prepare-oauth", { nonce }); + }; + + private getOauthConfigForMode = async ( + mode: OauthMode + ): Promise => { if (this.oauthConfig) { return this.oauthConfig; + } else if (mode === "redirect") { + return this.initOauth(); } else { throw new Error( - "enablePopupOauth must be set in configuration or signer.preparePopupOauth must be called before using popup-based OAuth login", + "enablePopupOauth must be set in configuration or signer.preparePopupOauth must be called before using popup-based OAuth login" ); } }; diff --git a/account-kit/rn-signer/src/utils/base64UrlEncode.ts b/account-kit/rn-signer/src/utils/base64UrlEncode.ts index e8044f7c9b..9089d741db 100644 --- a/account-kit/rn-signer/src/utils/base64UrlEncode.ts +++ b/account-kit/rn-signer/src/utils/base64UrlEncode.ts @@ -1,5 +1,7 @@ +import { Buffer } from "buffer"; + export const base64UrlEncode = ( - challenge: ArrayBuffer | ArrayBufferLike, + challenge: ArrayBuffer | ArrayBufferLike ): string => { return Buffer.from(challenge) .toString("base64") diff --git a/account-kit/rn-signer/src/utils/buffer-polyfill.ts b/account-kit/rn-signer/src/utils/buffer-polyfill.ts new file mode 100644 index 0000000000..e8304986da --- /dev/null +++ b/account-kit/rn-signer/src/utils/buffer-polyfill.ts @@ -0,0 +1,5 @@ +import { Buffer } from "buffer"; + +if (!global.Buffer) { + global.Buffer = Buffer; +} diff --git a/yarn.lock b/yarn.lock index 8411e427b7..db4c5f8b21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,11 +22,6 @@ resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz" integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== -"@adraffy/ens-normalize@^1.10.1": - version "1.11.0" - resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33" - integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg== - "@alloc/quick-lru@^5.2.0": version "5.2.0" resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" @@ -4760,13 +4755,6 @@ dependencies: "@noble/hashes" "1.4.0" -"@noble/curves@1.7.0", "@noble/curves@~1.7.0": - version "1.7.0" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz#0512360622439256df892f21d25b388f52505e45" - integrity sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw== - dependencies: - "@noble/hashes" "1.6.0" - "@noble/curves@^1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz" @@ -4781,13 +4769,6 @@ dependencies: "@noble/hashes" "1.4.0" -"@noble/curves@^1.6.0", "@noble/curves@~1.8.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.0.tgz#fe035a23959e6aeadf695851b51a87465b5ba8f7" - integrity sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ== - dependencies: - "@noble/hashes" "1.7.0" - "@noble/curves@~1.4.0": version "1.4.2" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz" @@ -4810,21 +4791,6 @@ resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== -"@noble/hashes@1.6.0": - version "1.6.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz#d4bfb516ad6e7b5111c216a5cc7075f4cf19e6c5" - integrity sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ== - -"@noble/hashes@1.6.1", "@noble/hashes@~1.6.0": - version "1.6.1" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.1.tgz#df6e5943edcea504bac61395926d6fd67869a0d5" - integrity sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w== - -"@noble/hashes@1.7.0", "@noble/hashes@~1.7.0": - version "1.7.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz#5d9e33af2c7d04fee35de1519b80c958b2e35e39" - integrity sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w== - "@noble/hashes@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" @@ -6807,11 +6773,6 @@ resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz" integrity sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g== -"@scure/base@~1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.1.tgz#dd0b2a533063ca612c17aa9ad26424a2ff5aa865" - integrity sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ== - "@scure/bip32@1.3.1": version "1.3.1" resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz" @@ -6830,24 +6791,6 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" -"@scure/bip32@1.6.0": - version "1.6.0" - resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.0.tgz#6dbc6b4af7c9101b351f41231a879d8da47e0891" - integrity sha512-82q1QfklrUUdXJzjuRU7iG7D7XiFx5PHYVS0+oeNKhyDLT7WPqs6pBcM2W5ZdwOwKCwoE1Vy1se+DHjcXwCYnA== - dependencies: - "@noble/curves" "~1.7.0" - "@noble/hashes" "~1.6.0" - "@scure/base" "~1.2.1" - -"@scure/bip32@^1.5.0": - version "1.6.1" - resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.1.tgz#848eca1bc96f6b5ce6aa750798853fb142dace05" - integrity sha512-jSO+5Ud1E588Y+LFo8TaB8JVPNAZw/lGGao+1SepHDeTs2dFLurdNIAgUuDlwezqEjRjElkCJajVrtrZaBxvaQ== - dependencies: - "@noble/curves" "~1.8.0" - "@noble/hashes" "~1.7.0" - "@scure/base" "~1.2.1" - "@scure/bip39@1.2.1": version "1.2.1" resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz" @@ -6864,22 +6807,6 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" -"@scure/bip39@1.5.0": - version "1.5.0" - resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.0.tgz#c8f9533dbd787641b047984356531d84485f19be" - integrity sha512-Dop+ASYhnrwm9+HA/HwXg7j2ZqM6yk2fyLWb5znexjctFY3+E+eU8cIWI0Pql0Qx4hPZCijlGq4OL71g+Uz30A== - dependencies: - "@noble/hashes" "~1.6.0" - "@scure/base" "~1.2.1" - -"@scure/bip39@^1.4.0": - version "1.5.1" - resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.1.tgz#a056868d672c7203a6035c808893742a79e151f6" - integrity sha512-GnlufVSP9UdAo/H2Patfv22VTtpNTyfi+I3qCKpvuB5l1KWzEYx+l2TNpBy9Ksh4xTs3Rn06tBlpWCi/1Vz8gw== - dependencies: - "@noble/hashes" "~1.7.0" - "@scure/base" "~1.2.1" - "@sec-ant/readable-stream@^0.4.1": version "0.4.1" resolved "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz" @@ -10038,21 +9965,11 @@ abitype@1.0.5: resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.5.tgz" integrity sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw== -abitype@1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.7.tgz#876a0005d211e1c9132825d45bcee7b46416b284" - integrity sha512-ZfYYSktDQUwc2eduYu8C4wOs+RDPmnRYMh7zNfzeMtGGgb0U+6tLGjixUic6mXf5xKKCcgT5Qp6cv39tOARVFw== - abitype@^0.8.3: version "0.8.11" resolved "https://registry.npmjs.org/abitype/-/abitype-0.8.11.tgz" integrity sha512-bM4v2dKvX08sZ9IU38IN5BKmN+ZkOSd2oI4a9f0ejHYZQYV6cDr7j+d95ga0z2XHG36Y4jzoG5Z7qDqxp7fi/A== -abitype@^1.0.6: - version "1.0.8" - resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba" - integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg== - abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" @@ -18104,11 +18021,6 @@ isows@1.0.4: resolved "https://registry.npmjs.org/isows/-/isows-1.0.4.tgz" integrity sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ== -isows@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7" - integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== - issue-parser@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/issue-parser/-/issue-parser-6.0.0.tgz#b1edd06315d4f2044a9755daf85fdafde9b4014a" @@ -22887,19 +22799,6 @@ outvariant@^1.4.0, outvariant@^1.4.2, outvariant@^1.4.3: resolved "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz" integrity sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA== -ox@0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/ox/-/ox-0.6.0.tgz#ba8f89d68b5e8afe717c8a947ffacc0669ea155d" - integrity sha512-blUzTLidvUlshv0O02CnLFqBLidNzPoAZdIth894avUAotTuWziznv6IENv5idRuOSSP3dH8WzcYw84zVdu0Aw== - dependencies: - "@adraffy/ens-normalize" "^1.10.1" - "@noble/curves" "^1.6.0" - "@noble/hashes" "^1.5.0" - "@scure/bip32" "^1.5.0" - "@scure/bip39" "^1.4.0" - abitype "^1.0.6" - eventemitter3 "5.0.1" - p-cancelable@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" @@ -28481,7 +28380,7 @@ vfile@^6.0.0, vfile@^6.0.1: unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" -viem@2.20.0, viem@^2.1.1, viem@^2.20.0: +viem@2.20.0, viem@^2.1.1, viem@^2.20.0, viem@^2.21.40, viem@^2.21.41: version "2.20.0" resolved "https://registry.npmjs.org/viem/-/viem-2.20.0.tgz" integrity sha512-cM4vs81HnSNbfceI1MLkx4pCVzbVjl9xiNSv5SCutYjUyFFOVSPDlEyhpg2iHinxx1NM4Qne3END5eLT8rvUdg== @@ -28496,21 +28395,6 @@ viem@2.20.0, viem@^2.1.1, viem@^2.20.0: webauthn-p256 "0.0.5" ws "8.17.1" -viem@^2.21.40, viem@^2.21.41: - version "2.22.5" - resolved "https://registry.npmjs.org/viem/-/viem-2.22.5.tgz#930846b41747cc9815257f4a009d2f8c00e02567" - integrity sha512-rked1t/qPQAFNTLoFU18/OCv5KdbIZPwAwlY2X7dtH8unt28kccN6RBrjEjhNrY6wQlbO4phGwslNUzd8a2faw== - dependencies: - "@noble/curves" "1.7.0" - "@noble/hashes" "1.6.1" - "@scure/bip32" "1.6.0" - "@scure/bip39" "1.5.0" - abitype "1.0.7" - isows "1.0.6" - ox "0.6.0" - webauthn-p256 "0.0.10" - ws "8.18.0" - vite-node@2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/vite-node/-/vite-node-2.0.4.tgz" @@ -28749,14 +28633,6 @@ web-vitals@0.2.4: resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz" integrity sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg== -webauthn-p256@0.0.10, webauthn-p256@^0.0.10: - version "0.0.10" - resolved "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.10.tgz" - integrity sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA== - dependencies: - "@noble/curves" "^1.4.0" - "@noble/hashes" "^1.4.0" - webauthn-p256@0.0.5: version "0.0.5" resolved "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.5.tgz" @@ -28765,6 +28641,14 @@ webauthn-p256@0.0.5: "@noble/curves" "^1.4.0" "@noble/hashes" "^1.4.0" +webauthn-p256@^0.0.10: + version "0.0.10" + resolved "https://registry.npmjs.org/webauthn-p256/-/webauthn-p256-0.0.10.tgz" + integrity sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA== + dependencies: + "@noble/curves" "^1.4.0" + "@noble/hashes" "^1.4.0" + "webextension-polyfill@>=0.10.0 <1.0": version "0.12.0" resolved "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.12.0.tgz" @@ -29092,11 +28976,6 @@ ws@8.17.1: resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== -ws@8.18.0, ws@^8.12.1, ws@^8.18.0, ws@^8.2.3: - version "8.18.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== - ws@^6.2.3: version "6.2.3" resolved "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz" @@ -29114,6 +28993,11 @@ ws@^7.5.1: resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== +ws@^8.12.1, ws@^8.18.0, ws@^8.2.3: + version "8.18.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + ws@~8.11.0: version "8.11.0" resolved "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz" From fea47ce087c31298cc348183621ff548051d9a93 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Fri, 10 Jan 2025 17:23:42 -0500 Subject: [PATCH 03/20] feat: add basic oauth implementation for mobile --- account-kit/rn-signer/example/metro.config.js | 28 +- account-kit/rn-signer/example/package.json | 112 +++---- account-kit/rn-signer/package.json | 308 +++++++++--------- account-kit/rn-signer/src/errors.ts | 2 +- account-kit/rn-signer/src/oauth.ts | 2 +- .../functions/createSMAV2Account.mdx | 28 -- .../functions/createSMAV2AccountClient.mdx | 52 --- .../getDefaultAllowlistModuleAddress.mdx | 37 --- ...etDefaultNativeTokenLimitModuleAddress.mdx | 37 --- .../getDefaultPaymasterGuardModuleAddress.mdx | 37 --- ...ultSingleSignerValidationModuleAddress.mdx | 37 --- .../getDefaultTimeRangeModuleAddress.mdx | 37 --- ...DefaultWebauthnValidationModuleAddress.mdx | 37 --- .../functions/installValidationActions.mdx | 69 ---- .../functions/nativeSMASigner.mdx | 53 --- .../functions/serializeHookConfig.mdx | 49 --- .../functions/serializeModuleEntity.mdx | 43 --- .../functions/serializeValidationConfig.mdx | 49 --- 18 files changed, 226 insertions(+), 791 deletions(-) delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/createSMAV2Account.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/createSMAV2AccountClient.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/getDefaultAllowlistModuleAddress.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/getDefaultNativeTokenLimitModuleAddress.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/getDefaultPaymasterGuardModuleAddress.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/getDefaultSingleSignerValidationModuleAddress.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/getDefaultTimeRangeModuleAddress.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/getDefaultWebauthnValidationModuleAddress.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/installValidationActions.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/nativeSMASigner.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/serializeHookConfig.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/serializeModuleEntity.mdx delete mode 100644 site/pages/reference/account-kit/smart-contracts/functions/serializeValidationConfig.mdx diff --git a/account-kit/rn-signer/example/metro.config.js b/account-kit/rn-signer/example/metro.config.js index 31c21922e1..4efbf1da77 100644 --- a/account-kit/rn-signer/example/metro.config.js +++ b/account-kit/rn-signer/example/metro.config.js @@ -8,33 +8,33 @@ const repoRoot = path.resolve(__dirname, "../../.."); const config = getDefaultConfig(projectRoot); const monorepoPackages = { - "@account-kit/signer": path.resolve(repoRoot, "account-kit/signer"), - "@aa-sdk/core": path.resolve(repoRoot, "aa-sdk/core"), - "@account-kit/logging": path.resolve(repoRoot, "account-kit/logging"), + "@account-kit/signer": path.resolve(repoRoot, "account-kit/signer"), + "@aa-sdk/core": path.resolve(repoRoot, "aa-sdk/core"), + "@account-kit/logging": path.resolve(repoRoot, "account-kit/logging"), }; config.watchFolders = [ - projectRoot, - rnSignerRoot, - ...Object.values(monorepoPackages), + projectRoot, + rnSignerRoot, + ...Object.values(monorepoPackages), ]; // Let Metro know where to resolve packages and in what order config.resolver.nodeModulesPaths = [ - path.resolve(projectRoot, "node_modules"), - path.resolve(rnSignerRoot, "node_modules"), - path.resolve(repoRoot, "node_modules"), + path.resolve(projectRoot, "node_modules"), + path.resolve(rnSignerRoot, "node_modules"), + path.resolve(repoRoot, "node_modules"), ]; // Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths` config.resolver.disableHierarchicalLookup = true; config.resolver.extraNodeModules = { - ...config.resolver.extraNodeModules, - ...require("node-libs-react-native"), - ...monorepoPackages, - crypto: require.resolve("crypto-browserify"), - stream: require.resolve("stream-browserify"), + ...config.resolver.extraNodeModules, + ...require("node-libs-react-native"), + ...monorepoPackages, + crypto: require.resolve("crypto-browserify"), + stream: require.resolve("stream-browserify"), }; /** diff --git a/account-kit/rn-signer/example/package.json b/account-kit/rn-signer/example/package.json index fc3efaf703..ddfdabd8d5 100644 --- a/account-kit/rn-signer/example/package.json +++ b/account-kit/rn-signer/example/package.json @@ -1,58 +1,58 @@ { - "name": "@account-kit/react-native-signer-example", - "version": "0.0.1", - "private": true, - "scripts": { - "android": "react-native run-android && yarn start:redirect-server", - "ios": "react-native run-ios", - "start": "react-native start", - "start:redirect-server": "tsx ./redirect-server/index.ts", - "build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"", - "build:ios": "react-native build-ios --scheme ReactNativeSignerExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\"" - }, - "dependencies": { - "@account-kit/signer": "^4.3.0", - "@react-navigation/bottom-tabs": "^7.2.0", - "@react-navigation/native": "7.0.3", - "@react-navigation/native-stack": "7.1.0", - "dotenv": "^16.4.5", - "express": "^4.21.1", - "react": "18.3.1", - "react-native": "0.76.5", - "react-native-config": "1.5.3", - "react-native-gesture-handler": "2.21.2", - "react-native-inappbrowser-reborn": "^3.7.0", - "react-native-mmkv": "3.1.0", - "react-native-safe-area-context": "4.14.0", - "react-native-screens": "4.2.0", - "util": "^0.12.5", - "viem": "^2.21.41", - "zustand": "^5.0.1" - }, - "peerDependencies": { - "@types/react": "18.3.1", - "react": "18.3.1", - "react-native": "0.76.1" - }, - "devDependencies": { - "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.3", - "@babel/runtime": "^7.25.0", - "@react-native-community/cli": "15.0.1", - "@react-native-community/cli-platform-android": "15.0.1", - "@react-native-community/cli-platform-ios": "15.0.1", - "@react-native/babel-preset": "0.76.5", - "@react-native/metro-config": "0.76.5", - "@react-native/typescript-config": "0.76.5", - "react-native-builder-bob": "^0.30.3", - "tsx": "^4.19.2" - }, - "engines": { - "node": ">=18" - }, - "workspaces": { - "nohoist": [ - "**" - ] - } + "name": "@account-kit/react-native-signer-example", + "version": "0.0.1", + "private": true, + "scripts": { + "android": "react-native run-android && yarn start:redirect-server", + "ios": "react-native run-ios", + "start": "react-native start", + "start:redirect-server": "tsx ./redirect-server/index.ts", + "build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"", + "build:ios": "react-native build-ios --scheme ReactNativeSignerExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\"" + }, + "dependencies": { + "@account-kit/signer": "^4.3.0", + "@react-navigation/bottom-tabs": "^7.2.0", + "@react-navigation/native": "7.0.3", + "@react-navigation/native-stack": "7.1.0", + "dotenv": "^16.4.5", + "express": "^4.21.1", + "react": "18.3.1", + "react-native": "0.76.5", + "react-native-config": "1.5.3", + "react-native-gesture-handler": "2.21.2", + "react-native-inappbrowser-reborn": "^3.7.0", + "react-native-mmkv": "3.1.0", + "react-native-safe-area-context": "4.14.0", + "react-native-screens": "4.2.0", + "util": "^0.12.5", + "viem": "^2.21.41", + "zustand": "^5.0.1" + }, + "peerDependencies": { + "@types/react": "18.3.1", + "react": "18.3.1", + "react-native": "0.76.1" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.3", + "@babel/runtime": "^7.25.0", + "@react-native-community/cli": "15.0.1", + "@react-native-community/cli-platform-android": "15.0.1", + "@react-native-community/cli-platform-ios": "15.0.1", + "@react-native/babel-preset": "0.76.5", + "@react-native/metro-config": "0.76.5", + "@react-native/typescript-config": "0.76.5", + "react-native-builder-bob": "^0.30.3", + "tsx": "^4.19.2" + }, + "engines": { + "node": ">=18" + }, + "workspaces": { + "nohoist": [ + "**" + ] + } } diff --git a/account-kit/rn-signer/package.json b/account-kit/rn-signer/package.json index d88d4e9459..f66ee9c273 100644 --- a/account-kit/rn-signer/package.json +++ b/account-kit/rn-signer/package.json @@ -1,156 +1,156 @@ { - "name": "@account-kit/react-native-signer", - "version": "4.8.0", - "author": "Alchemy", - "description": "React Native compatible Account Kit signer", - "source": "./src/index.tsx", - "main": "./lib/commonjs/index.js", - "module": "./lib/module/index.js", - "exports": { - ".": { - "import": { - "types": "./lib/typescript/module/src/index.d.ts", - "default": "./lib/module/index.js" - }, - "require": { - "types": "./lib/typescript/commonjs/src/index.d.ts", - "default": "./lib/commonjs/index.js" - } - } - }, - "files": [ - "src", - "lib", - "android", - "ios", - "cpp", - "*.podspec", - "react-native.config.json", - "!ios/build", - "!android/build", - "!android/gradle", - "!android/gradlew", - "!android/gradlew.bat", - "!android/local.properties", - "!**/__tests__", - "!**/__fixtures__", - "!**/__mocks__", - "!**/.*" - ], - "scripts": { - "test": "jest", - "typecheck": "tsc", - "build": "yarn typecheck", - "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", - "prepare": "bob build" - }, - "keywords": [ - "react-native", - "ios", - "android" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/alchemyplatform/aa-sdk.git" - }, - "bugs": { - "url": "https://github.com/alchemyplatform/aa-sdk/issues" - }, - "homepage": "https://github.com/alchemyplatform/aa-sdk#readme", - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/" - }, - "devDependencies": { - "@commitlint/config-conventional": "^17.0.2", - "@evilmartians/lefthook": "^1.5.0", - "@react-native-community/cli": "15.0.1", - "@react-native/eslint-config": "^0.76.5", - "@release-it/conventional-changelog": "^5.0.0", - "@types/jest": "^29.5.5", - "@types/react": "^18.2.44", - "commitlint": "^17.0.2", - "del-cli": "^5.1.0", - "eslint": "^8.51.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.1", - "jest": "^29.7.0", - "prettier": "^3.0.3", - "react": "18.3.1", - "react-native": "0.76.5", - "react-native-builder-bob": "^0.30.3", - "react-native-mmkv": "^3.1.0", - "react-native-inappbrowser-reborn": "^3.7.0", - "release-it": "^15.0.0", - "turbo": "^1.10.7", - "typescript": "^5.2.2", - "typescript-template": "*" - }, - "resolutions": { - "@types/react": "^18.2.44" - }, - "peerDependencies": { - "react": "*", - "react-native": "*", - "react-native-config": "1.5.3", - "react-native-gesture-handler": "2.21.2", - "react-native-mmkv": "^3.1.0", - "react-native-inappbrowser-reborn": "^3.7.0" - }, - "jest": { - "preset": "react-native", - "modulePathIgnorePatterns": [ - "/example/node_modules", - "/lib/" - ] - }, - "react-native-builder-bob": { - "source": "src", - "output": "lib", - "targets": [ - "codegen", - [ - "commonjs", - { - "esm": true - } - ], - [ - "module", - { - "esm": true - } - ], - [ - "typescript", - { - "project": "tsconfig.build.json", - "esm": true - } - ] - ] - }, - "codegenConfig": { - "name": "NativeTEKStamperSpec", - "type": "all", - "jsSrcsDir": "src", - "outputDir": { - "ios": "ios/generated", - "android": "android/generated" - }, - "android": { - "javaPackageName": "com.accountkit.reactnativesigner" - }, - "includesGeneratedCode": true - }, - "create-react-native-library": { - "type": "module-new", - "languages": "kotlin-objc", - "version": "0.42.2" - }, - "dependencies": { - "@aa-sdk/core": "^4.8.0", - "@account-kit/signer": "^4.8.0", - "viem": "^2.21.40" - } + "name": "@account-kit/react-native-signer", + "version": "4.8.0", + "author": "Alchemy", + "description": "React Native compatible Account Kit signer", + "source": "./src/index.tsx", + "main": "./lib/commonjs/index.js", + "module": "./lib/module/index.js", + "exports": { + ".": { + "import": { + "types": "./lib/typescript/module/src/index.d.ts", + "default": "./lib/module/index.js" + }, + "require": { + "types": "./lib/typescript/commonjs/src/index.d.ts", + "default": "./lib/commonjs/index.js" + } + } + }, + "files": [ + "src", + "lib", + "android", + "ios", + "cpp", + "*.podspec", + "react-native.config.json", + "!ios/build", + "!android/build", + "!android/gradle", + "!android/gradlew", + "!android/gradlew.bat", + "!android/local.properties", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__", + "!**/.*" + ], + "scripts": { + "test": "jest", + "typecheck": "tsc", + "build": "yarn typecheck", + "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", + "prepare": "bob build" + }, + "keywords": [ + "react-native", + "ios", + "android" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/alchemyplatform/aa-sdk.git" + }, + "bugs": { + "url": "https://github.com/alchemyplatform/aa-sdk/issues" + }, + "homepage": "https://github.com/alchemyplatform/aa-sdk#readme", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.0.2", + "@evilmartians/lefthook": "^1.5.0", + "@react-native-community/cli": "15.0.1", + "@react-native/eslint-config": "^0.76.5", + "@release-it/conventional-changelog": "^5.0.0", + "@types/jest": "^29.5.5", + "@types/react": "^18.2.44", + "commitlint": "^17.0.2", + "del-cli": "^5.1.0", + "eslint": "^8.51.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.1", + "jest": "^29.7.0", + "prettier": "^3.0.3", + "react": "18.3.1", + "react-native": "0.76.5", + "react-native-builder-bob": "^0.30.3", + "react-native-mmkv": "^3.1.0", + "react-native-inappbrowser-reborn": "^3.7.0", + "release-it": "^15.0.0", + "turbo": "^1.10.7", + "typescript": "^5.2.2", + "typescript-template": "*" + }, + "resolutions": { + "@types/react": "^18.2.44" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-config": "1.5.3", + "react-native-gesture-handler": "2.21.2", + "react-native-mmkv": "^3.1.0", + "react-native-inappbrowser-reborn": "^3.7.0" + }, + "jest": { + "preset": "react-native", + "modulePathIgnorePatterns": [ + "/example/node_modules", + "/lib/" + ] + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "codegen", + [ + "commonjs", + { + "esm": true + } + ], + [ + "module", + { + "esm": true + } + ], + [ + "typescript", + { + "project": "tsconfig.build.json", + "esm": true + } + ] + ] + }, + "codegenConfig": { + "name": "NativeTEKStamperSpec", + "type": "all", + "jsSrcsDir": "src", + "outputDir": { + "ios": "ios/generated", + "android": "android/generated" + }, + "android": { + "javaPackageName": "com.accountkit.reactnativesigner" + }, + "includesGeneratedCode": true + }, + "create-react-native-library": { + "type": "module-new", + "languages": "kotlin-objc", + "version": "0.42.2" + }, + "dependencies": { + "@aa-sdk/core": "^4.8.0", + "@account-kit/signer": "^4.8.0", + "viem": "^2.21.40" + } } diff --git a/account-kit/rn-signer/src/errors.ts b/account-kit/rn-signer/src/errors.ts index 8cda618931..4e642365c4 100644 --- a/account-kit/rn-signer/src/errors.ts +++ b/account-kit/rn-signer/src/errors.ts @@ -10,7 +10,7 @@ export class NotAuthenticatedError extends BaseError { ].join("\n"), { docsPath: "/signers/alchemy-signer/introduction.html", - }, + } ); } } diff --git a/account-kit/rn-signer/src/oauth.ts b/account-kit/rn-signer/src/oauth.ts index 2d28cf6fe7..fd02f8ee9d 100644 --- a/account-kit/rn-signer/src/oauth.ts +++ b/account-kit/rn-signer/src/oauth.ts @@ -30,7 +30,7 @@ const DEFAULT_SCOPE_AND_CLAIMS: Record = { * @returns {ScopeAndClaims | undefined} default scope and claims */ export function getDefaultScopeAndClaims( - knownAuthProviderId: KnownAuthProvider, + knownAuthProviderId: KnownAuthProvider ): ScopeAndClaims | undefined { return DEFAULT_SCOPE_AND_CLAIMS[knownAuthProviderId]; } diff --git a/site/pages/reference/account-kit/smart-contracts/functions/createSMAV2Account.mdx b/site/pages/reference/account-kit/smart-contracts/functions/createSMAV2Account.mdx deleted file mode 100644 index d789f0559b..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/createSMAV2Account.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -# This file is autogenerated -title: createSMAV2Account -description: Overview of the createSMAV2Account method ---- - -# createSMAV2Account - -Creates an SMAV2 account using defined parameters including chain, signer, salt, factory address, and more. -Handles account initialization code, nonce generation, transaction encoding, and more to construct a modular account with optional validation hooks. - -## Import - -```ts -import { createSMAV2Account } from "@account-kit/smart-contracts"; -``` - -## Parameters - -### config - -`CreateSMAV2AccountParams` -Configuration parameters for creating an SMAV2 account. Includes chain details, signer, salt, factory address, and more. - -## Returns - -`Promise` -A promise that resolves to an `MAV2Account` providing methods for nonce retrieval, transaction execution, and more. diff --git a/site/pages/reference/account-kit/smart-contracts/functions/createSMAV2AccountClient.mdx b/site/pages/reference/account-kit/smart-contracts/functions/createSMAV2AccountClient.mdx deleted file mode 100644 index 8b7bcd96ab..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/createSMAV2AccountClient.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -# This file is autogenerated - -title: createSMAV2AccountClient -description: Overview of the createSMAV2AccountClient method ---- - -# createSMAV2AccountClient - -Creates a SMAv2 account client using the provided configuration parameters. - -## Import - -```ts -import { createSMAV2AccountClient } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { http } from "viem"; -import { createSMAV2AccountClient } from "@account-kit/smart-contracts"; -import { LocalAccountSigner } from "@aa-sdk/core"; -import { sepolia } from "@account-kit/infra"; - -const MNEMONIC = "..."; -const RPC_URL = "..."; - -const signer = LocalAccountSigner.mnemonicToAccountSigner(MNEMONIC); - -const chain = sepolia; - -const transport = http(RPC_URL); - -const SMAV2SignerAccountClient = await createSMAV2AccountClient({ - chain, - signer, - transport, -}); -``` - -## Parameters - -### config - -`CreateSMAV2AccountClientParams` -The configuration parameters required to create the MAv2 account client - -## Returns - -`Promise` -A promise that resolves to a `SmartAccountClient` instance diff --git a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultAllowlistModuleAddress.mdx b/site/pages/reference/account-kit/smart-contracts/functions/getDefaultAllowlistModuleAddress.mdx deleted file mode 100644 index 5522e43076..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultAllowlistModuleAddress.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -# This file is autogenerated -title: getDefaultAllowlistModuleAddress -description: Overview of the getDefaultAllowlistModuleAddress method ---- - -# getDefaultAllowlistModuleAddress - -Maps a given chain to a specific address of the allowlist module by its chain ID. If no direct mapping exists, it defaults to returning a specific address. - -## Import - -```ts -import { getDefaultAllowlistModuleAddress } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { getDefaultAllowlistModuleAddress } from "@account-kit/smart-contracts"; -import { Chain, Address } from "viem"; - -const chain: Chain = ... -const allowlistModule: Address = getDefaultAllowlistModuleAddress(chain); -``` - -## Parameters - -### chain - -`Chain` -The chain object containing the chain ID to map - -## Returns - -`Address` -The allowlist module address associated with the specified chain ID or a default address if no specific mapping exists diff --git a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultNativeTokenLimitModuleAddress.mdx b/site/pages/reference/account-kit/smart-contracts/functions/getDefaultNativeTokenLimitModuleAddress.mdx deleted file mode 100644 index 54dcc49268..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultNativeTokenLimitModuleAddress.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -# This file is autogenerated -title: getDefaultNativeTokenLimitModuleAddress -description: Overview of the getDefaultNativeTokenLimitModuleAddress method ---- - -# getDefaultNativeTokenLimitModuleAddress - -Maps a given chain to a specific address of the native token limit module by its chain ID. If no direct mapping exists, it defaults to returning a specific address. - -## Import - -```ts -import { getDefaultNativeTokenLimitModuleAddress } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { getDefaultNativeTokenLimitModuleAddress } from "@account-kit/smart-contracts"; -import { Chain, Address } from "viem"; - -const chain: Chain = ... -const nativeTokenLimitAddress: Address = getDefaultNativeTokenLimitModuleAddress(chain); -``` - -## Parameters - -### chain - -`Chain` -The chain object containing the chain ID to map - -## Returns - -`Address` -The native token limit module address associated with the specified chain ID or a default address if no specific mapping exists diff --git a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultPaymasterGuardModuleAddress.mdx b/site/pages/reference/account-kit/smart-contracts/functions/getDefaultPaymasterGuardModuleAddress.mdx deleted file mode 100644 index 11e25ca3b0..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultPaymasterGuardModuleAddress.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -# This file is autogenerated -title: getDefaultPaymasterGuardModuleAddress -description: Overview of the getDefaultPaymasterGuardModuleAddress method ---- - -# getDefaultPaymasterGuardModuleAddress - -Maps a given chain to a specific address of the paymaster guard module by its chain ID. If no direct mapping exists, it defaults to returning a specific address. - -## Import - -```ts -import { getDefaultPaymasterGuardModuleAddress } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { getDefaultPaymasterGuardModuleAddress } from "@account-kit/smart-contracts"; -import { Chain, Address } from "viem"; - -const chain: Chain = ... -const paymasterGuardAddress: Address = getDefaultPaymasterGuardModuleAddress(chain); -``` - -## Parameters - -### chain - -`Chain` -The chain object containing the chain ID to map - -## Returns - -`Address` -The paymaster guard module address associated with the specified chain ID or a default address if no specific mapping exists diff --git a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultSingleSignerValidationModuleAddress.mdx b/site/pages/reference/account-kit/smart-contracts/functions/getDefaultSingleSignerValidationModuleAddress.mdx deleted file mode 100644 index 04c02888cc..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultSingleSignerValidationModuleAddress.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -# This file is autogenerated -title: getDefaultSingleSignerValidationModuleAddress -description: Overview of the getDefaultSingleSignerValidationModuleAddress method ---- - -# getDefaultSingleSignerValidationModuleAddress - -Maps a given chain to a specific address of the single signer validation module by its chain ID. If no direct mapping exists, it defaults to returning a specific address. - -## Import - -```ts -import { getDefaultSingleSignerValidationModuleAddress } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { getDefaultSingleSignerValidationModuleAddress } from "@account-kit/smart-contracts"; -import { Chain, Address } from "viem"; - -const chain: Chain = ... -const singleSignerValidationAddress: Address = getDefaultSingleSignerValidationModuleAddress(chain); -``` - -## Parameters - -### chain - -`Chain` -The chain object containing the chain ID to map - -## Returns - -`Address` -The single signer validation module address associated with the specified chain ID or a default address if no specific mapping exists diff --git a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultTimeRangeModuleAddress.mdx b/site/pages/reference/account-kit/smart-contracts/functions/getDefaultTimeRangeModuleAddress.mdx deleted file mode 100644 index 3de832b874..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultTimeRangeModuleAddress.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -# This file is autogenerated -title: getDefaultTimeRangeModuleAddress -description: Overview of the getDefaultTimeRangeModuleAddress method ---- - -# getDefaultTimeRangeModuleAddress - -Maps a given chain to a specific address of the time range module by its chain ID. If no direct mapping exists, it defaults to returning a specific address. - -## Import - -```ts -import { getDefaultTimeRangeModuleAddress } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { getDefaultTimeRangeModuleAddress } from "@account-kit/smart-contracts"; -import { Chain, Address } from "viem"; - -const chain: Chain = ... -const timeRangeModuleAddress: Address = getDefaultTimeRangeModuleAddress(chain); -``` - -## Parameters - -### chain - -`Chain` -The chain object containing the chain ID to map - -## Returns - -`Address` -The time range module address associated with the specified chain ID or a default address if no specific mapping exists diff --git a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultWebauthnValidationModuleAddress.mdx b/site/pages/reference/account-kit/smart-contracts/functions/getDefaultWebauthnValidationModuleAddress.mdx deleted file mode 100644 index fd27c9dac5..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/getDefaultWebauthnValidationModuleAddress.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -# This file is autogenerated -title: getDefaultWebauthnValidationModuleAddress -description: Overview of the getDefaultWebauthnValidationModuleAddress method ---- - -# getDefaultWebauthnValidationModuleAddress - -Maps a given chain to a specific address of the webauthn validation module by its chain ID. If no direct mapping exists, it defaults to returning a specific address. - -## Import - -```ts -import { getDefaultWebauthnValidationModuleAddress } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { getDefaultWebauthnValidationModuleAddress } from "@account-kit/smart-contracts"; -import { Chain, Address } from "viem"; - -const chain: Chain = ... -const webauthnValidationAddress: Address = getDefaultWebauthnValidationModuleAddress(chain); -``` - -## Parameters - -### chain - -`Chain` -The chain object containing the chain ID to map - -## Returns - -`Address` -The webauthn validation module address associated with the specified chain ID or a default address if no specific mapping exists diff --git a/site/pages/reference/account-kit/smart-contracts/functions/installValidationActions.mdx b/site/pages/reference/account-kit/smart-contracts/functions/installValidationActions.mdx deleted file mode 100644 index 602162dacc..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/installValidationActions.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -# This file is autogenerated - -title: installValidationActions -description: Overview of the installValidationActions method ---- - -# installValidationActions - -Provides validation installation and uninstallation functionalities for a MA v2 client, ensuring compatibility with `SmartAccountClient`. - -## Import - -```ts -import { installValidationActions } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { createSMAV2AccountClient, installValidationActions, getDefaultSingleSignerValidationModuleAddress, SingleSignerValidationModule } from "@account-kit/smart-contracts"; -import { Address } from "viem"; - -const client = (await createSMAV2AccountClient({ ... })).extend(installValidationActions); -const sessionKeyAddress: Address = "0x1234"; -const sessionKeyEntityId: number = 1; - -await client.installValidation({ -validationConfig: { -moduleAddress: getDefaultSingleSignerValidationModuleAddress( - client.chain -), -entityId: sessionKeyEntityId, -isGlobal: true, -isSignatureValidation: false, -isUserOpValidation: true, -}, -selectors: [], -installData: SingleSignerValidationModule.encodeOnInstallData({ -entityId: sessionKeyEntityId, -signer: sessionKeyAddress, -}), -hooks: [], -}); - -await client.uninstallValidation({ -moduleAddress: sessionKeyAddress, -entityId: sessionKeyEntityId, -uninstallData: SingleSignerValidationModule.encodeOnUninstallData({ -entityId: sessionKeyEntityId, -}), -hookUninstallDatas: [], -}); - -``` - -## Parameters - -### client - -`object` - -- The client instance which provides account and sendUserOperation functionality. - - ## Returns - - `object` - -- An object containing two methods, `installValidation` and `uninstallValidation`. diff --git a/site/pages/reference/account-kit/smart-contracts/functions/nativeSMASigner.mdx b/site/pages/reference/account-kit/smart-contracts/functions/nativeSMASigner.mdx deleted file mode 100644 index 0052b9b056..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/nativeSMASigner.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -# This file is autogenerated -title: nativeSMASigner -description: Overview of the nativeSMASigner method ---- - -# nativeSMASigner - -Creates an object with methods for generating a dummy signature, signing user operation hashes, signing messages, and signing typed data. - -## Import - -```ts -import { nativeSMASigner } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { nativeSMASigner } from "@account-kit/smart-contracts"; - -import { LocalAccountSigner } from "@aa-sdk/core"; - -const MNEMONIC = "...": - -const account = createSMAV2Account({ config }); - -const signer = LocalAccountSigner.mnemonicToAccountSigner(MNEMONIC); - -const messageSigner = nativeSMASigner(signer, chain, account.address); -``` - -## Parameters - -### signer - -`SmartAccountSigner` -Signer to use for signing operations - -### chain - -`Chain` -Chain object for the signer - -### accountAddress - -`Address` -address of the smart account using this signer - -## Returns - -`object` -an object with methods for signing operations and managing signatures diff --git a/site/pages/reference/account-kit/smart-contracts/functions/serializeHookConfig.mdx b/site/pages/reference/account-kit/smart-contracts/functions/serializeHookConfig.mdx deleted file mode 100644 index 94b6f1f3b1..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/serializeHookConfig.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -# This file is autogenerated - -title: serializeHookConfig -description: Overview of the serializeHookConfig method ---- - -# serializeHookConfig - -Serializes a `HookConfig` object into a `Hex` format by encoding the hook type, presence of post/pre hooks, address, and entity ID. - -## Import - -```ts -import { serializeHookConfig } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { type HookType, serializeHookConfig } from "@account-kit/smart-contracts"; -import { Address } from "viem"; - -const moduleAddress: Address = "0x1234"; -const entityId: number = 1234; -const hookType: HookType = HookType.Validation; -const hasPostHooks: boolean = false; -const hasPreHooks: boolean = true; - -const hookConfigHex = serializeHookConfig({ -moduleAddress, -entityId -hookType, -hasPostHooks, -hasPreHooks -}); -``` - -## Parameters - -### config - -`HookConfig` -The hook configuration containing address, entity ID, hook type, and post/pre hook indicators - -## Returns - -`Hex` -The serialized hook configuration in hexadecimal format diff --git a/site/pages/reference/account-kit/smart-contracts/functions/serializeModuleEntity.mdx b/site/pages/reference/account-kit/smart-contracts/functions/serializeModuleEntity.mdx deleted file mode 100644 index a95495eaa9..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/serializeModuleEntity.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -# This file is autogenerated - -title: serializeModuleEntity -description: Overview of the serializeModuleEntity method ---- - -# serializeModuleEntity - -Serializes a module entity into a hexadecimal format by concatenating the module address and entity ID. - -## Import - -```ts -import { serializeModuleEntity } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { serializeModuleEntity } from "@account-kit/smart-contracts"; -import { Address } from "viem"; - -const moduleAddress: Address = "0x1234"; -const entityId: number = 1234; - -const moduleEntityHex = serializeModuleEntity({ - moduleAddress, - entityId, -}); -``` - -## Parameters - -### config - -`ModuleEntity` -The module entity configuration containing the module address and entity ID - -## Returns - -`Hex` -A hexadecimal string representation of the serialized module entity diff --git a/site/pages/reference/account-kit/smart-contracts/functions/serializeValidationConfig.mdx b/site/pages/reference/account-kit/smart-contracts/functions/serializeValidationConfig.mdx deleted file mode 100644 index f66a26e095..0000000000 --- a/site/pages/reference/account-kit/smart-contracts/functions/serializeValidationConfig.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -# This file is autogenerated - -title: serializeValidationConfig -description: Overview of the serializeValidationConfig method ---- - -# serializeValidationConfig - -Serializes a validation configuration into a hexadecimal string representation. This involves converting boolean flags into bitwise representation and combining them with serialized module entity data. - -## Import - -```ts -import { serializeValidationConfig } from "@account-kit/smart-contracts"; -``` - -## Usage - -```ts -import { serializeValidationConfig } from "@account-kit/smart-contracts"; -import { Address } from "viem"; - -const moduleAddress: Address = "0x1234"; -const entityId: number = 1234; -const isGlobal: boolean = true; -const isSignatureValidation: boolean = false; -const isUserOpValidation: boolean = true; - -const validationConfigHex = serializeValidationConfig({ -moduleAddress, -entityId -isGlobal, -isSignatureValidation, -isUserOpValidation -}); -``` - -## Parameters - -### config - -`ValidationConfig` -The validation configuration object containing details to serialize - -## Returns - -`Hex` -A hexadecimal string representing the serialized configuration From 3163e181365a35a887ec1c92172097ef9f534b49 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Tue, 14 Jan 2025 13:49:27 -0500 Subject: [PATCH 04/20] fix: remove popup OAuth changes --- account-kit/rn-signer/example/package.json | 1 + .../rn-signer/example/src/screens/oauth.tsx | 5 +-- account-kit/rn-signer/package.json | 3 +- account-kit/rn-signer/src/client.ts | 41 +++++-------------- account-kit/rn-signer/src/index.tsx | 3 +- account-kit/rn-signer/src/signer.ts | 7 +++- yarn.lock | 8 ++++ 7 files changed, 30 insertions(+), 38 deletions(-) diff --git a/account-kit/rn-signer/example/package.json b/account-kit/rn-signer/example/package.json index ddfdabd8d5..b3977b3baf 100644 --- a/account-kit/rn-signer/example/package.json +++ b/account-kit/rn-signer/example/package.json @@ -25,6 +25,7 @@ "react-native-mmkv": "3.1.0", "react-native-safe-area-context": "4.14.0", "react-native-screens": "4.2.0", + "react-native-webview": "^13.13.1", "util": "^0.12.5", "viem": "^2.21.41", "zustand": "^5.0.1" diff --git a/account-kit/rn-signer/example/src/screens/oauth.tsx b/account-kit/rn-signer/example/src/screens/oauth.tsx index dfb46a4837..1d0f23f3a3 100644 --- a/account-kit/rn-signer/example/src/screens/oauth.tsx +++ b/account-kit/rn-signer/example/src/screens/oauth.tsx @@ -9,13 +9,10 @@ import { RNAlchemySigner } from "@account-kit/react-native-signer"; const signer = RNAlchemySigner({ client: { connection: { apiKey: Config.API_KEY! }, - appScheme: "rn-signer-demo", }, }); -console.log("API KEY: ", Config.API_KEY); - -export default function OTPAuthScreen() { +export default function OAuthScreen() { const [user, setUser] = useState(null); useEffect(() => { diff --git a/account-kit/rn-signer/package.json b/account-kit/rn-signer/package.json index f66ee9c273..bd5fdeb148 100644 --- a/account-kit/rn-signer/package.json +++ b/account-kit/rn-signer/package.json @@ -95,7 +95,8 @@ "react-native-config": "1.5.3", "react-native-gesture-handler": "2.21.2", "react-native-mmkv": "^3.1.0", - "react-native-inappbrowser-reborn": "^3.7.0" + "react-native-inappbrowser-reborn": "^3.7.0", + "react-native-webview": "^13.13.1" }, "jest": { "preset": "react-native", diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 986573f0d1..9cc0a3c7bf 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -42,7 +42,6 @@ export const RNSignerClientParamsSchema = z.object({ .string() .optional() .default("https://signer.alchemy.com/callback"), - appScheme: z.string().optional(), }); export type RNSignerClientParams = z.input; @@ -51,11 +50,9 @@ export type RNSignerClientParams = z.input; export class RNSignerClient extends BaseSignerClient { private stamper = NativeTEKStamper; oauthCallbackUrl: string; - appScheme: string; - private appDeeplink: string; constructor(params: RNSignerClientParams) { - const { connection, rootOrgId, oauthCallbackUrl, appScheme } = + const { connection, rootOrgId, oauthCallbackUrl } = RNSignerClientParamsSchema.parse(params); super({ @@ -64,10 +61,6 @@ export class RNSignerClient extends BaseSignerClient { connection, }); - this.appScheme = appScheme ?? ""; - - this.appDeeplink = `${this.appScheme}://`; - this.oauthCallbackUrl = oauthCallbackUrl; } @@ -155,9 +148,10 @@ export class RNSignerClient extends BaseSignerClient { ): Promise => { try { const providerUrl = await this.getOauthProviderUrl(_args); + const redirectUrl = _args.redirectUrl; if (await InAppBrowser.isAvailable()) { - const res = await InAppBrowser.openAuth(providerUrl, this.appDeeplink); + const res = await InAppBrowser.openAuth(providerUrl, redirectUrl); Alert.alert(JSON.stringify(res)); } @@ -169,24 +163,11 @@ export class RNSignerClient extends BaseSignerClient { throw new Error("Error performing OAuth Operation"); } }; - override oauthWithPopup = async ( + override oauthWithPopup( _args: Extract - ): Promise => { - try { - const providerUrl = await this.getOauthProviderUrl(_args); - - if (await InAppBrowser.isAvailable()) { - const res = await InAppBrowser.openAuth(providerUrl, this.appDeeplink); - - Alert.alert(JSON.stringify(res)); - } - - return {} as User; - } catch (e) { - console.error("Error performing OAuth Operation", e); - throw new Error("Error performing OAuth Operation"); - } - }; + ): Promise { + throw new Error("Method not implemented"); + } override async disconnect(): Promise { this.user = undefined; @@ -207,7 +188,7 @@ export class RNSignerClient extends BaseSignerClient { throw new Error("Method not implemented."); } - private getOauthProviderUrl = async (args: OauthParams): Promise => { + public getOauthProviderUrl = async (args: OauthParams): Promise => { const { authProviderId, isCustomProvider, @@ -219,7 +200,7 @@ export class RNSignerClient extends BaseSignerClient { mode, } = args; - const res = await this.getOauthConfigForMode("popup"); + const res = await this.getOauthConfigForMode(mode); const { codeChallenge, requestKey, authProviders } = res; @@ -268,8 +249,8 @@ export class RNSignerClient extends BaseSignerClient { requestKey, turnkeyPublicKey, expirationSeconds, - redirectUrl: mode === "redirect" ? redirectUrl : undefined, // We only use the 'Popup' mode in RN - openerOrigin: mode === "popup" ? this.appDeeplink : undefined, + redirectUrl: mode === "redirect" ? redirectUrl : undefined, // We only use the 'Redirect' mode in RN + openerOrigin: undefined, }; const state = base64UrlEncode( diff --git a/account-kit/rn-signer/src/index.tsx b/account-kit/rn-signer/src/index.tsx index e1a56584e0..f2923199fa 100644 --- a/account-kit/rn-signer/src/index.tsx +++ b/account-kit/rn-signer/src/index.tsx @@ -1,2 +1,3 @@ -// eslint-disable-next-line import/extensions +/* eslint-disable import/extensions */ export { RNAlchemySigner } from "./signer"; +export type { RNAlchemySignerType } from "./signer"; diff --git a/account-kit/rn-signer/src/signer.ts b/account-kit/rn-signer/src/signer.ts index 8c6e284556..7ed143e9f8 100644 --- a/account-kit/rn-signer/src/signer.ts +++ b/account-kit/rn-signer/src/signer.ts @@ -19,8 +19,8 @@ const RNAlchemySignerParamsSchema = z export type RNAlchemySignerParams = z.input; -class RNAlchemySignerSingleton extends BaseAlchemySigner { - private static instance: BaseAlchemySigner; +export class RNAlchemySignerSingleton extends BaseAlchemySigner { + private static instance: RNAlchemySignerSingleton; private constructor(params: RNAlchemySignerParams) { if (!!RNAlchemySignerSingleton.instance) { @@ -37,6 +37,7 @@ class RNAlchemySignerSingleton extends BaseAlchemySigner { } else { client = params_.client; } + super({ client, sessionConfig, @@ -56,3 +57,5 @@ export function RNAlchemySigner(params: RNAlchemySignerParams) { return instance; } + +export type RNAlchemySignerType = RNAlchemySignerSingleton; diff --git a/yarn.lock b/yarn.lock index db4c5f8b21..bf28fd1abf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24417,6 +24417,14 @@ react-native-webview@^11.26.0: escape-string-regexp "2.0.0" invariant "2.2.4" +react-native-webview@^13.13.1: + version "13.13.1" + resolved "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.13.1.tgz#a016557afc02126175f151caaed545abbdef4eb2" + integrity sha512-Qwrvdwl2U2bG8QlghfRzGR/PCwKzW/cXTCR/WfMWHeGoADp2CHuCaEpfYO/HhlLLGy3Jqizsy+sjFhnKa1AgrA== + dependencies: + escape-string-regexp "^4.0.0" + invariant "2.2.4" + react-native@0.76.5: version "0.76.5" resolved "https://registry.npmjs.org/react-native/-/react-native-0.76.5.tgz#3ce43d3c31f46cfd98e56ef2dfc70866c04ad185" From 96517d0f6ce162db32ba88fbdab3b3263cef4984 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Tue, 14 Jan 2025 17:57:06 -0500 Subject: [PATCH 05/20] feat: add auth completion using bundle --- .../example/src/screens/magic-link-auth.tsx | 1 - .../rn-signer/example/src/screens/oauth.tsx | 6 +- .../example/src/screens/otp-auth.tsx | 1 - account-kit/rn-signer/src/client.ts | 75 +++++++++++-------- .../rn-signer/src/utils/parseUrlParams.ts | 14 ++++ account-kit/signer/src/client/base.ts | 2 +- 6 files changed, 65 insertions(+), 34 deletions(-) create mode 100644 account-kit/rn-signer/src/utils/parseUrlParams.ts diff --git a/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx b/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx index 34a43d6f2b..8b53950657 100644 --- a/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx +++ b/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx @@ -16,7 +16,6 @@ export default function MagicLinkAuthScreen() { const signer = RNAlchemySigner({ client: { connection: { apiKey: Config.API_KEY! }, - appScheme: "rn-signer-demo", }, }); diff --git a/account-kit/rn-signer/example/src/screens/oauth.tsx b/account-kit/rn-signer/example/src/screens/oauth.tsx index 1d0f23f3a3..74c058d6f5 100644 --- a/account-kit/rn-signer/example/src/screens/oauth.tsx +++ b/account-kit/rn-signer/example/src/screens/oauth.tsx @@ -33,7 +33,11 @@ export default function OAuthScreen() { type: "oauth", mode: "redirect", authProviderId: "google", - redirectUrl: "rn-signer-demo:///", + redirectUrl: "rn-signer-demo://oauth", + }) + .then((user) => { + // Get user details after a successful authentication + setUser(user); }) .catch(console.error); }} diff --git a/account-kit/rn-signer/example/src/screens/otp-auth.tsx b/account-kit/rn-signer/example/src/screens/otp-auth.tsx index 1fcaa15992..e95614beef 100644 --- a/account-kit/rn-signer/example/src/screens/otp-auth.tsx +++ b/account-kit/rn-signer/example/src/screens/otp-auth.tsx @@ -15,7 +15,6 @@ import { RNAlchemySigner } from "@account-kit/react-native-signer"; const signer = RNAlchemySigner({ client: { connection: { apiKey: Config.API_KEY! }, - appScheme: "rn-signer-demo", }, }); diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 9cc0a3c7bf..a9df2a12ba 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -4,6 +4,7 @@ import "./utils/buffer-polyfill"; import { type ConnectionConfig } from "@aa-sdk/core"; import { BaseSignerClient, + OauthFailedError, type AlchemySignerClientEvents, type AuthenticatingEventMetadata, type CreateAccountParams, @@ -11,7 +12,6 @@ import { type GetWebAuthnAttestationResult, type KnownAuthProvider, type OauthConfig, - type OauthMode, type OauthParams, type OtpParams, type SignupResponse, @@ -23,7 +23,7 @@ import { z } from "zod"; import { getDefaultScopeAndClaims, getOauthNonce } from "./oauth"; import { base64UrlEncode } from "./utils/base64UrlEncode"; import { InAppBrowser } from "react-native-inappbrowser-reborn"; -import { Alert } from "react-native"; +import { parseSearchParams } from "./utils/parseUrlParams"; type OauthState = { authProviderId: string; @@ -50,6 +50,11 @@ export type RNSignerClientParams = z.input; export class RNSignerClient extends BaseSignerClient { private stamper = NativeTEKStamper; oauthCallbackUrl: string; + private validAuthenticatingTypes: AuthenticatingEventMetadata["type"][] = [ + "email", + "otp", + "oauth", + ]; constructor(params: RNSignerClientParams) { const { connection, rootOrgId, oauthCallbackUrl } = @@ -122,10 +127,7 @@ export class RNSignerClient extends BaseSignerClient { authenticatingType: AuthenticatingEventMetadata["type"]; idToken?: string; }): Promise { - if ( - params.authenticatingType !== "email" && - params.authenticatingType !== "otp" - ) { + if (!this.validAuthenticatingTypes.includes(params.authenticatingType)) { throw new Error("Unsupported authenticating type"); } @@ -145,19 +147,46 @@ export class RNSignerClient extends BaseSignerClient { } override oauthWithRedirect = async ( _args: Extract - ): Promise => { + ): Promise => { try { const providerUrl = await this.getOauthProviderUrl(_args); const redirectUrl = _args.redirectUrl; - if (await InAppBrowser.isAvailable()) { - const res = await InAppBrowser.openAuth(providerUrl, redirectUrl); - - Alert.alert(JSON.stringify(res)); - } - return new Promise((_, reject) => - setTimeout(() => reject("Failed to redirect to OAuth provider"), 1000) - ); + return new Promise(async (resolve, reject) => { + if (await InAppBrowser.isAvailable()) { + const res = await InAppBrowser.openAuth(providerUrl, redirectUrl); + + if (res.type !== "success" || !res.url) { + return reject( + new OauthFailedError("An error occured completing your request") + ); + } + + const authResult = parseSearchParams(res.url); + const bundle = authResult["alchemy-bundle"] ?? ""; + const orgId = authResult["alchemy-org-id"] ?? ""; + const idToken = authResult["alchemy-id-token"] ?? ""; + const isSignup = authResult["alchemy-is-signup"]; + const error = authResult["alchemy-error"]; + + if (bundle && orgId && idToken) { + this.completeAuthWithBundle({ + bundle, + orgId, + connectedEventName: "connectedOauth", + idToken, + authenticatingType: "oauth", + }).then((user) => { + if (isSignup) { + this.eventEmitter.emit("newUserSignup"); + } + resolve(user); + }, reject); + } else if (error) { + reject(new OauthFailedError(error)); + } + } + }); } catch (e) { console.error("Error performing OAuth Operation", e); throw new Error("Error performing OAuth Operation"); @@ -200,7 +229,7 @@ export class RNSignerClient extends BaseSignerClient { mode, } = args; - const res = await this.getOauthConfigForMode(mode); + const res = await this.getOauthConfig(); const { codeChallenge, requestKey, authProviders } = res; @@ -295,20 +324,6 @@ export class RNSignerClient extends BaseSignerClient { const nonce = getOauthNonce(publicKey); return this.request("/v1/prepare-oauth", { nonce }); }; - - private getOauthConfigForMode = async ( - mode: OauthMode - ): Promise => { - if (this.oauthConfig) { - return this.oauthConfig; - } else if (mode === "redirect") { - return this.initOauth(); - } else { - throw new Error( - "enablePopupOauth must be set in configuration or signer.preparePopupOauth must be called before using popup-based OAuth login" - ); - } - }; } /** diff --git a/account-kit/rn-signer/src/utils/parseUrlParams.ts b/account-kit/rn-signer/src/utils/parseUrlParams.ts new file mode 100644 index 0000000000..6decf15180 --- /dev/null +++ b/account-kit/rn-signer/src/utils/parseUrlParams.ts @@ -0,0 +1,14 @@ +export const parseSearchParams = (url: string) => { + const regex = /[?&]([^=#]+)=([^&#]*)/g; + + let params: Record = {}; + let match: RegExpExecArray | null; + + while ((match = regex.exec(url))) { + if (match[1] !== undefined && match[2] !== undefined) { + params[match[1]] = match[2]; + } + } + + return params; +}; diff --git a/account-kit/signer/src/client/base.ts b/account-kit/signer/src/client/base.ts index dc734d8124..0a3331c1a1 100644 --- a/account-kit/signer/src/client/base.ts +++ b/account-kit/signer/src/client/base.ts @@ -139,7 +139,7 @@ export abstract class BaseSignerClient { public abstract oauthWithRedirect( args: Extract - ): Promise; + ): Promise; public abstract oauthWithPopup( args: Extract From 66f9b6c976f16218188c3ac37422c60044698ad0 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Tue, 14 Jan 2025 18:27:09 -0500 Subject: [PATCH 06/20] fix: add error case when in-app browser is unavailable --- account-kit/rn-signer/src/client.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index a9df2a12ba..535e52b839 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -185,6 +185,10 @@ export class RNSignerClient extends BaseSignerClient { } else if (error) { reject(new OauthFailedError(error)); } + } else { + throw new Error( + "Couldn't perfom authentication operation. Browser not available." + ); } }); } catch (e) { From c1301825bc1c32030c087f37776fedf209640bff Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Tue, 14 Jan 2025 18:49:46 -0500 Subject: [PATCH 07/20] fix: remove errors file from rn-signer --- account-kit/rn-signer/src/client.ts | 2 +- account-kit/rn-signer/src/errors.ts | 25 ------------------------- account-kit/signer/src/index.ts | 1 + 3 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 account-kit/rn-signer/src/errors.ts diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 535e52b839..6ab6be3cbb 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -18,7 +18,7 @@ import { type User, } from "@account-kit/signer"; import NativeTEKStamper from "./NativeTEKStamper"; -import { OAuthProvidersError } from "./errors"; +import { OAuthProvidersError } from "@account-kit/signer"; import { z } from "zod"; import { getDefaultScopeAndClaims, getOauthNonce } from "./oauth"; import { base64UrlEncode } from "./utils/base64UrlEncode"; diff --git a/account-kit/rn-signer/src/errors.ts b/account-kit/rn-signer/src/errors.ts deleted file mode 100644 index 4e642365c4..0000000000 --- a/account-kit/rn-signer/src/errors.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BaseError } from "@aa-sdk/core"; - -export class NotAuthenticatedError extends BaseError { - override name = "NotAuthenticatedError"; - constructor() { - super( - [ - "Signer not authenticated", - "Please authenticate to use this signer", - ].join("\n"), - { - docsPath: "/signers/alchemy-signer/introduction.html", - } - ); - } -} - -export class OAuthProvidersError extends BaseError { - override name = "OAuthProvidersError"; - constructor() { - super("OAuth providers not found", { - docsPath: "/react/getting-started", - }); - } -} diff --git a/account-kit/signer/src/index.ts b/account-kit/signer/src/index.ts index c68a131b24..deb0f1735f 100644 --- a/account-kit/signer/src/index.ts +++ b/account-kit/signer/src/index.ts @@ -14,3 +14,4 @@ export type * from "./signer.js"; export { AlchemyWebSigner } from "./signer.js"; export type * from "./types.js"; export { AlchemySignerStatus } from "./types.js"; +export { OAuthProvidersError, NotAuthenticatedError } from "./errors.js"; From 08a9fa5aaa51b09e33acf50eb5ef937aacabb6c1 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Tue, 14 Jan 2025 19:15:27 -0500 Subject: [PATCH 08/20] fix: fix imports and linting --- account-kit/rn-signer/src/client.ts | 7 ++-- account-kit/rn-signer/src/oauth.ts | 36 ------------------- account-kit/signer/src/index.ts | 1 + .../functions/getDefaultScopeAndClaims.mdx | 27 ++++++++++++++ .../signer/functions/getOauthNonce.mdx | 27 ++++++++++++++ 5 files changed, 60 insertions(+), 38 deletions(-) delete mode 100644 account-kit/rn-signer/src/oauth.ts create mode 100644 site/pages/reference/account-kit/signer/functions/getDefaultScopeAndClaims.mdx create mode 100644 site/pages/reference/account-kit/signer/functions/getOauthNonce.mdx diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 6ab6be3cbb..fd38fd3fa4 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -18,9 +18,12 @@ import { type User, } from "@account-kit/signer"; import NativeTEKStamper from "./NativeTEKStamper"; -import { OAuthProvidersError } from "@account-kit/signer"; +import { + OAuthProvidersError, + getOauthNonce, + getDefaultScopeAndClaims, +} from "@account-kit/signer"; import { z } from "zod"; -import { getDefaultScopeAndClaims, getOauthNonce } from "./oauth"; import { base64UrlEncode } from "./utils/base64UrlEncode"; import { InAppBrowser } from "react-native-inappbrowser-reborn"; import { parseSearchParams } from "./utils/parseUrlParams"; diff --git a/account-kit/rn-signer/src/oauth.ts b/account-kit/rn-signer/src/oauth.ts deleted file mode 100644 index fd02f8ee9d..0000000000 --- a/account-kit/rn-signer/src/oauth.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { sha256 } from "viem"; -import type { KnownAuthProvider } from "@account-kit/signer"; - -/** - * Turnkey requires the nonce in the id token to be in this format. - * - * @param {string} turnkeyPublicKey key from a Turnkey iframe - * @returns {string} nonce to be used in OIDC - */ -export function getOauthNonce(turnkeyPublicKey: string): string { - return sha256(new TextEncoder().encode(turnkeyPublicKey)).slice(2); -} - -export type ScopeAndClaims = { - scope: string; - claims?: string; -}; - -const DEFAULT_SCOPE_AND_CLAIMS: Record = { - google: { scope: "openid email" }, - apple: { scope: "openid email" }, - facebook: { scope: "openid email" }, - auth0: { scope: "openid email" }, -}; - -/** - * Returns the default scope and claims when using a known auth provider - * - * @param {string} knownAuthProviderId id of a known auth provider, e.g. "google" - * @returns {ScopeAndClaims | undefined} default scope and claims - */ -export function getDefaultScopeAndClaims( - knownAuthProviderId: KnownAuthProvider -): ScopeAndClaims | undefined { - return DEFAULT_SCOPE_AND_CLAIMS[knownAuthProviderId]; -} diff --git a/account-kit/signer/src/index.ts b/account-kit/signer/src/index.ts index deb0f1735f..4d1842a17b 100644 --- a/account-kit/signer/src/index.ts +++ b/account-kit/signer/src/index.ts @@ -15,3 +15,4 @@ export { AlchemyWebSigner } from "./signer.js"; export type * from "./types.js"; export { AlchemySignerStatus } from "./types.js"; export { OAuthProvidersError, NotAuthenticatedError } from "./errors.js"; +export { getOauthNonce, getDefaultScopeAndClaims } from "./oauth.js"; diff --git a/site/pages/reference/account-kit/signer/functions/getDefaultScopeAndClaims.mdx b/site/pages/reference/account-kit/signer/functions/getDefaultScopeAndClaims.mdx new file mode 100644 index 0000000000..758b3cdd31 --- /dev/null +++ b/site/pages/reference/account-kit/signer/functions/getDefaultScopeAndClaims.mdx @@ -0,0 +1,27 @@ +--- +# This file is autogenerated +title: getDefaultScopeAndClaims +description: Overview of the getDefaultScopeAndClaims method +--- + +# getDefaultScopeAndClaims + +Returns the default scope and claims when using a known auth provider + +## Import + +```ts +import { getDefaultScopeAndClaims } from "@account-kit/signer"; +``` + +## Parameters + +### knownAuthProviderId + +`string` +id of a known auth provider, e.g. "google" + +## Returns + +`ScopeAndClaims | undefined` +default scope and claims diff --git a/site/pages/reference/account-kit/signer/functions/getOauthNonce.mdx b/site/pages/reference/account-kit/signer/functions/getOauthNonce.mdx new file mode 100644 index 0000000000..03dabff6f7 --- /dev/null +++ b/site/pages/reference/account-kit/signer/functions/getOauthNonce.mdx @@ -0,0 +1,27 @@ +--- +# This file is autogenerated +title: getOauthNonce +description: Overview of the getOauthNonce method +--- + +# getOauthNonce + +Turnkey requires the nonce in the id token to be in this format. + +## Import + +```ts +import { getOauthNonce } from "@account-kit/signer"; +``` + +## Parameters + +### turnkeyPublicKey + +`string` +key from a Turnkey iframe + +## Returns + +`string` +nonce to be used in OIDC From 7d875954be2f066c23bc5ef5434406b459a261af Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Wed, 15 Jan 2025 13:00:05 -0500 Subject: [PATCH 09/20] fix: remove react-native-webview package as it is currently unused --- account-kit/rn-signer/package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/account-kit/rn-signer/package.json b/account-kit/rn-signer/package.json index 6f4e2af7cb..5f052b0486 100644 --- a/account-kit/rn-signer/package.json +++ b/account-kit/rn-signer/package.json @@ -79,8 +79,8 @@ "react": "18.3.1", "react-native": "0.76.5", "react-native-builder-bob": "^0.30.3", - "react-native-mmkv": "^3.1.0", "react-native-inappbrowser-reborn": "^3.7.0", + "react-native-mmkv": "^3.1.0", "release-it": "^15.0.0", "turbo": "^1.10.7", "typescript": "^5.2.2", @@ -94,9 +94,8 @@ "react-native": "*", "react-native-config": "1.5.3", "react-native-gesture-handler": "2.21.2", - "react-native-mmkv": "^3.1.0", "react-native-inappbrowser-reborn": "^3.7.0", - "react-native-webview": "^13.13.1" + "react-native-mmkv": "^3.1.0" }, "jest": { "preset": "react-native", From 3549ecfd5494f676bad7cca8798bebd1aa57ae05 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Wed, 15 Jan 2025 13:16:57 -0500 Subject: [PATCH 10/20] fix: remove react-native-webview package as it is currently unused --- account-kit/rn-signer/example/package.json | 1 - yarn.lock | 8 -------- 2 files changed, 9 deletions(-) diff --git a/account-kit/rn-signer/example/package.json b/account-kit/rn-signer/example/package.json index b3977b3baf..ddfdabd8d5 100644 --- a/account-kit/rn-signer/example/package.json +++ b/account-kit/rn-signer/example/package.json @@ -25,7 +25,6 @@ "react-native-mmkv": "3.1.0", "react-native-safe-area-context": "4.14.0", "react-native-screens": "4.2.0", - "react-native-webview": "^13.13.1", "util": "^0.12.5", "viem": "^2.21.41", "zustand": "^5.0.1" diff --git a/yarn.lock b/yarn.lock index bf28fd1abf..db4c5f8b21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24417,14 +24417,6 @@ react-native-webview@^11.26.0: escape-string-regexp "2.0.0" invariant "2.2.4" -react-native-webview@^13.13.1: - version "13.13.1" - resolved "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.13.1.tgz#a016557afc02126175f151caaed545abbdef4eb2" - integrity sha512-Qwrvdwl2U2bG8QlghfRzGR/PCwKzW/cXTCR/WfMWHeGoADp2CHuCaEpfYO/HhlLLGy3Jqizsy+sjFhnKa1AgrA== - dependencies: - escape-string-regexp "^4.0.0" - invariant "2.2.4" - react-native@0.76.5: version "0.76.5" resolved "https://registry.npmjs.org/react-native/-/react-native-0.76.5.tgz#3ce43d3c31f46cfd98e56ef2dfc70866c04ad185" From 34e5851422260ebb40fe978ef0d112521b2daffa Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Wed, 15 Jan 2025 14:14:30 -0500 Subject: [PATCH 11/20] feat: abstract signer to sepaeate module for import --- account-kit/rn-signer/example/src/App.tsx | 1 + .../example/src/screens/magic-link-auth.tsx | 18 ++++++------------ .../rn-signer/example/src/screens/oauth.tsx | 9 +-------- .../rn-signer/example/src/screens/otp-auth.tsx | 9 +-------- account-kit/rn-signer/example/src/signer.ts | 10 ++++++++++ 5 files changed, 19 insertions(+), 28 deletions(-) create mode 100644 account-kit/rn-signer/example/src/signer.ts diff --git a/account-kit/rn-signer/example/src/App.tsx b/account-kit/rn-signer/example/src/App.tsx index 642dfb459b..369dae9c2e 100644 --- a/account-kit/rn-signer/example/src/App.tsx +++ b/account-kit/rn-signer/example/src/App.tsx @@ -37,6 +37,7 @@ const RootStack = createBottomTabNavigator({ linking: { path: "oauth" }, options: { tabBarLabel: "Oauth", + tabBarIcon: () => 🔐, }, }, }, diff --git a/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx b/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx index 8b53950657..4d4122959e 100644 --- a/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx +++ b/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx @@ -1,7 +1,6 @@ /* eslint-disable import/extensions */ -import { RNAlchemySigner } from "@account-kit/react-native-signer"; import type { User } from "@account-kit/signer"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { Linking, StyleSheet, @@ -10,15 +9,10 @@ import { TouchableOpacity, View, } from "react-native"; -import Config from "react-native-config"; -export default function MagicLinkAuthScreen() { - const signer = RNAlchemySigner({ - client: { - connection: { apiKey: Config.API_KEY! }, - }, - }); +import signer from "../signer"; +export default function MagicLinkAuthScreen() { const [email, setEmail] = useState(""); const [user, setUser] = useState(null); @@ -32,7 +26,7 @@ export default function MagicLinkAuthScreen() { .catch(console.error); }; - const handleIncomingURL = (event: { url: string }) => { + const handleIncomingURL = useCallback((event: { url: string }) => { const regex = /[?&]([^=#]+)=([^&#]*)/g; let params: Record = {}; @@ -51,7 +45,7 @@ export default function MagicLinkAuthScreen() { handleUserAuth({ bundle: params.bundle ?? "", }); - }; + }, []); useEffect(() => { // get the user if already logged in @@ -63,7 +57,7 @@ export default function MagicLinkAuthScreen() { const subscription = Linking.addEventListener("url", handleIncomingURL); return () => subscription.remove(); - }, []); + }, [handleIncomingURL]); return ( diff --git a/account-kit/rn-signer/example/src/screens/oauth.tsx b/account-kit/rn-signer/example/src/screens/oauth.tsx index 74c058d6f5..8c70452819 100644 --- a/account-kit/rn-signer/example/src/screens/oauth.tsx +++ b/account-kit/rn-signer/example/src/screens/oauth.tsx @@ -3,14 +3,7 @@ import type { User } from "@account-kit/signer"; import { useEffect, useState } from "react"; import { View, Text, StyleSheet, TouchableOpacity } from "react-native"; -import Config from "react-native-config"; -import { RNAlchemySigner } from "@account-kit/react-native-signer"; - -const signer = RNAlchemySigner({ - client: { - connection: { apiKey: Config.API_KEY! }, - }, -}); +import signer from "../signer"; export default function OAuthScreen() { const [user, setUser] = useState(null); diff --git a/account-kit/rn-signer/example/src/screens/otp-auth.tsx b/account-kit/rn-signer/example/src/screens/otp-auth.tsx index e95614beef..f2bad271ea 100644 --- a/account-kit/rn-signer/example/src/screens/otp-auth.tsx +++ b/account-kit/rn-signer/example/src/screens/otp-auth.tsx @@ -9,14 +9,7 @@ import { TouchableOpacity, } from "react-native"; -import Config from "react-native-config"; -import { RNAlchemySigner } from "@account-kit/react-native-signer"; - -const signer = RNAlchemySigner({ - client: { - connection: { apiKey: Config.API_KEY! }, - }, -}); +import signer from "../signer"; export default function OTPAuthScreen() { const [email, setEmail] = useState(""); diff --git a/account-kit/rn-signer/example/src/signer.ts b/account-kit/rn-signer/example/src/signer.ts new file mode 100644 index 0000000000..af340628c0 --- /dev/null +++ b/account-kit/rn-signer/example/src/signer.ts @@ -0,0 +1,10 @@ +import { RNAlchemySigner } from "@account-kit/react-native-signer"; +import Config from "react-native-config"; + +const signer = RNAlchemySigner({ + client: { + connection: { apiKey: Config.API_KEY! }, + }, +}); + +export default signer; From f1754b16faea5a4b8beb626bce5a98d5fa3d1466 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Thu, 16 Jan 2025 16:28:12 -0500 Subject: [PATCH 12/20] feat: refactor getOauthProviderUrl() for reusability --- account-kit/rn-signer/src/client.ts | 146 ++-------------- account-kit/signer/src/client/base.ts | 164 +++++++++++++++++- account-kit/signer/src/client/index.ts | 145 ++-------------- account-kit/signer/src/client/types.ts | 26 ++- account-kit/signer/src/oauth.ts | 11 ++ .../signer/src/utils/resolveRelativeUrl.ts | 6 + .../BaseSignerClient/getOauthProviderUrl.mdx | 56 ++++++ 7 files changed, 294 insertions(+), 260 deletions(-) create mode 100644 account-kit/signer/src/utils/resolveRelativeUrl.ts create mode 100644 site/pages/reference/account-kit/signer/classes/BaseSignerClient/getOauthProviderUrl.mdx diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index fd38fd3fa4..0a2c94a95f 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -10,7 +10,6 @@ import { type CreateAccountParams, type EmailAuthParams, type GetWebAuthnAttestationResult, - type KnownAuthProvider, type OauthConfig, type OauthParams, type OtpParams, @@ -18,26 +17,11 @@ import { type User, } from "@account-kit/signer"; import NativeTEKStamper from "./NativeTEKStamper"; -import { - OAuthProvidersError, - getOauthNonce, - getDefaultScopeAndClaims, -} from "@account-kit/signer"; +import { getOauthNonce } from "@account-kit/signer"; import { z } from "zod"; -import { base64UrlEncode } from "./utils/base64UrlEncode"; import { InAppBrowser } from "react-native-inappbrowser-reborn"; import { parseSearchParams } from "./utils/parseUrlParams"; -type OauthState = { - authProviderId: string; - isCustomProvider?: boolean; - requestKey: string; - turnkeyPublicKey: string; - expirationSeconds?: number; - redirectUrl?: string; - openerOrigin?: string; -}; - export const RNSignerClientParamsSchema = z.object({ connection: z.custom(), rootOrgId: z.string().optional(), @@ -149,11 +133,23 @@ export class RNSignerClient extends BaseSignerClient { return user; } override oauthWithRedirect = async ( - _args: Extract + args: Extract ): Promise => { try { - const providerUrl = await this.getOauthProviderUrl(_args); - const redirectUrl = _args.redirectUrl; + const oauthParams = args; + const turnkeyPublicKey = await this.stamper.init(); + const oauthCallbackUrl = this.oauthCallbackUrl; + const oauthConfig = await this.getOauthConfig(); + + const providerUrl = await this.getOauthProviderUrl({ + oauthParams, + turnkeyPublicKey, + oauthCallbackUrl, + oauthConfig, + usesRelativeUrl: false, + }); + + const redirectUrl = args.redirectUrl; return new Promise(async (resolve, reject) => { if (await InAppBrowser.isAvailable()) { @@ -166,6 +162,7 @@ export class RNSignerClient extends BaseSignerClient { } const authResult = parseSearchParams(res.url); + const bundle = authResult["alchemy-bundle"] ?? ""; const orgId = authResult["alchemy-org-id"] ?? ""; const idToken = authResult["alchemy-id-token"] ?? ""; @@ -224,104 +221,6 @@ export class RNSignerClient extends BaseSignerClient { throw new Error("Method not implemented."); } - public getOauthProviderUrl = async (args: OauthParams): Promise => { - const { - authProviderId, - isCustomProvider, - auth0Connection, - scope: providedScope, - claims: providedClaims, - expirationSeconds, - redirectUrl, - mode, - } = args; - - const res = await this.getOauthConfig(); - - const { codeChallenge, requestKey, authProviders } = res; - - if (!authProviders) { - throw new OAuthProvidersError(); - } - - const authProvider = authProviders.find( - (provider) => - provider.id === authProviderId && - !!provider.isCustomProvider === !!isCustomProvider - ); - - if (!authProvider) { - throw new Error(`OAuth provider with id ${authProviderId} not found`); - } - - let scope: string; - let claims: string | undefined; - - if (providedScope) { - scope = addOpenIdIfAbsent(providedScope); - claims = providedClaims; - } else { - if (isCustomProvider) { - throw new Error("scope must be provided for a custom provider"); - } - - const scopeAndClaims = getDefaultScopeAndClaims( - authProviderId as KnownAuthProvider - ); - if (!scopeAndClaims) { - throw new Error( - `Default scope not known for provider ${authProviderId}` - ); - } - ({ scope, claims } = scopeAndClaims); - } - const { authEndpoint, clientId } = authProvider; - const turnkeyPublicKey = await this.stamper.init(); - const nonce = getOauthNonce(turnkeyPublicKey); - - const stateObject: OauthState = { - authProviderId, - isCustomProvider, - requestKey, - turnkeyPublicKey, - expirationSeconds, - redirectUrl: mode === "redirect" ? redirectUrl : undefined, // We only use the 'Redirect' mode in RN - openerOrigin: undefined, - }; - - const state = base64UrlEncode( - new TextEncoder().encode(JSON.stringify(stateObject)).buffer - ); - const authUrl = new URL(authEndpoint); - - const params: Record = { - redirect_uri: this.oauthCallbackUrl, - response_type: "code", - scope, - state, - code_challenge: codeChallenge, - code_challenge_method: "S256", - prompt: "select_account", - client_id: clientId, - nonce, - }; - if (claims) { - params.claims = claims; - } - if (auth0Connection) { - params.connection = auth0Connection; - } - - Object.keys(params).forEach((param) => { - params[param] && authUrl.searchParams.append(param, params[param]); - }); - - const [urlPath, searchParams] = authUrl.href.split("?"); - - // Ensure to prevent potential trailing backslashes. - return `${urlPath?.replace(/\/$/, "")}?${searchParams}`; - }; - protected override getOauthConfig = async (): Promise => { const currentStamper = this.turnkeyClient.stamper; const publicKey = await this.stamper.init(); @@ -332,14 +231,3 @@ export class RNSignerClient extends BaseSignerClient { return this.request("/v1/prepare-oauth", { nonce }); }; } - -/** - * "openid" is a required scope in the OIDC protocol. Insert it if the user - * forgot. - * - * @param {string} scope scope param which may be missing "openid" - * @returns {string} scope which most definitely contains "openid" - */ -function addOpenIdIfAbsent(scope: string): string { - return scope.match(/\bopenid\b/) ? scope : `openid ${scope}`; -} diff --git a/account-kit/signer/src/client/base.ts b/account-kit/signer/src/client/base.ts index 0a3331c1a1..8a416b86ad 100644 --- a/account-kit/signer/src/client/base.ts +++ b/account-kit/signer/src/client/base.ts @@ -3,18 +3,21 @@ import { TurnkeyClient, type TSignedRequest } from "@turnkey/http"; import EventEmitter from "eventemitter3"; import { jwtDecode } from "jwt-decode"; import type { Hex } from "viem"; -import { NotAuthenticatedError } from "../errors.js"; +import { NotAuthenticatedError, OAuthProvidersError } from "../errors.js"; import { base64UrlEncode } from "../utils/base64UrlEncode.js"; import { assertNever } from "../utils/typeAssertions.js"; +import { resolveRelativeUrl } from "../utils/resolveRelativeUrl.js"; import type { AlchemySignerClientEvent, AlchemySignerClientEvents, AuthenticatingEventMetadata, CreateAccountParams, EmailAuthParams, + GetOauthProviderUrlArgs, GetWebAuthnAttestationResult, OauthConfig, OauthParams, + OauthState, OtpParams, SignerBody, SignerResponse, @@ -22,6 +25,12 @@ import type { SignupResponse, User, } from "./types.js"; +import type { OauthMode } from "../signer.js"; +import { + addOpenIdIfAbsent, + getDefaultScopeAndClaims, + getOauthNonce, +} from "../oauth.js"; export interface BaseSignerClientParams { stamper: TurnkeyClient["stamper"]; @@ -46,7 +55,6 @@ export abstract class BaseSignerClient { protected rootOrg: string; protected eventEmitter: EventEmitter; protected oauthConfig: OauthConfig | undefined; - /** * Create a new instance of the Alchemy Signer client * @@ -471,6 +479,158 @@ export abstract class BaseSignerClient { return result; }; + /** + * Returns the authentication url for the selected OAuth Proivder + * + * @example + * ```ts + * + * cosnt oauthParams = { + * authProviderId: "google", + * isCustomProvider: false, + * auth0Connection: undefined, + * scope: undefined, + * claims: undefined, + * mode: "redirect", + * redirectUrl: "https://your-url-path/oauth-return", + * expirationSeconds: 3000 + * }; + * + * const turnkeyPublicKey = await this.initIframeStamper(); + * const oauthCallbackUrl = this.oauthCallbackUrl; + * const oauthConfig = this.getOauthConfig() // Optional value for OauthConfig() + * const usesRelativeUrl = true // Optional value to determine if we use a relative (or absolute) url for the `redirect_url` + * + * const oauthProviderUrl = getOauthProviderUrl({ + * oauthParams, + * turnkeyPublicKey, + * oauthCallbackUrl + * }) + * + * ``` + * @param {GetOauthProviderUrlArgs} args Required. The Oauth provider's auth parameters + * + * @returns {Promise} returns the Oauth provider's url + */ + public getOauthProviderUrl = async ( + args: GetOauthProviderUrlArgs + ): Promise => { + const { + oauthParams, + turnkeyPublicKey, + oauthCallbackUrl, + oauthConfig, + usesRelativeUrl = true, + } = args; + + const { + authProviderId, + isCustomProvider, + auth0Connection, + scope: providedScope, + claims: providedClaims, + mode, + redirectUrl, + expirationSeconds, + } = oauthParams; + + const { codeChallenge, requestKey, authProviders } = + oauthConfig ?? (await this.getOauthConfigForMode(mode)); + + if (!authProviders) { + throw new OAuthProvidersError(); + } + + const authProvider = authProviders.find( + (provider) => + provider.id === authProviderId && + !!provider.isCustomProvider === !!isCustomProvider + ); + + if (!authProvider) { + throw new Error(`No auth provider found with id ${authProviderId}`); + } + + let scope: string; + let claims: string | undefined; + + if (providedScope) { + scope = addOpenIdIfAbsent(providedScope); + claims = providedClaims; + } else { + if (isCustomProvider) { + throw new Error("scope must be provided for a custom provider"); + } + const scopeAndClaims = getDefaultScopeAndClaims(authProviderId); + if (!scopeAndClaims) { + throw new Error( + `Default scope not known for provider ${authProviderId}` + ); + } + ({ scope, claims } = scopeAndClaims); + } + const { authEndpoint, clientId } = authProvider; + + const nonce = getOauthNonce(turnkeyPublicKey); + const stateObject: OauthState = { + authProviderId, + isCustomProvider, + requestKey, + turnkeyPublicKey, + expirationSeconds, + redirectUrl: + mode === "redirect" + ? usesRelativeUrl + ? resolveRelativeUrl(redirectUrl) + : redirectUrl + : undefined, + openerOrigin: mode === "popup" ? window.location.origin : undefined, + }; + const state = base64UrlEncode( + new TextEncoder().encode(JSON.stringify(stateObject)) + ); + const authUrl = new URL(authEndpoint); + const params: Record = { + redirect_uri: oauthCallbackUrl, + response_type: "code", + scope, + state, + code_challenge: codeChallenge, + code_challenge_method: "S256", + prompt: "select_account", + client_id: clientId, + nonce, + }; + if (claims) { + params.claims = claims; + } + if (auth0Connection) { + params.connection = auth0Connection; + } + + Object.keys(params).forEach((param) => { + params[param] && authUrl.searchParams.append(param, params[param]); + }); + + const [urlPath, searchParams] = authUrl.href.split("?"); + + return `${urlPath?.replace(/\/$/, "")}?${searchParams}`; + }; + + private getOauthConfigForMode = async ( + mode: OauthMode + ): Promise => { + if (this.oauthConfig) { + return this.oauthConfig; + } else if (mode === "redirect") { + return this.initOauth(); + } else { + throw new Error( + "enablePopupOauth must be set in configuration or signer.preparePopupOauth must be called before using popup-based OAuth login" + ); + } + }; + // eslint-disable-next-line eslint-rules/require-jsdoc-on-reexported-functions protected pollActivityCompletion = async < T extends keyof Awaited< diff --git a/account-kit/signer/src/client/index.ts b/account-kit/signer/src/client/index.ts index 6169a663c1..378ea32b9c 100644 --- a/account-kit/signer/src/client/index.ts +++ b/account-kit/signer/src/client/index.ts @@ -3,9 +3,8 @@ import { getWebAuthnAttestation } from "@turnkey/http"; import { IframeStamper } from "@turnkey/iframe-stamper"; import { WebauthnStamper } from "@turnkey/webauthn-stamper"; import { z } from "zod"; -import { OAuthProvidersError } from "../errors.js"; -import { getDefaultScopeAndClaims, getOauthNonce } from "../oauth.js"; -import type { AuthParams, OauthMode } from "../signer.js"; +import { getOauthNonce } from "../oauth.js"; +import type { AuthParams } from "../signer.js"; import { base64UrlEncode } from "../utils/base64UrlEncode.js"; import { generateRandomBuffer } from "../utils/generateRandomBuffer.js"; import { BaseSignerClient } from "./base.js"; @@ -17,7 +16,6 @@ import type { EmailAuthParams, ExportWalletParams, OauthConfig, - OauthParams, OtpParams, User, } from "./types.js"; @@ -46,16 +44,6 @@ export type AlchemySignerClientParams = z.input< typeof AlchemySignerClientParamsSchema >; -type OauthState = { - authProviderId: string; - isCustomProvider?: boolean; - requestKey: string; - turnkeyPublicKey: string; - expirationSeconds?: number; - redirectUrl?: string; - openerOrigin?: string; -}; - /** * A lower level client used by the AlchemySigner used to communicate with * Alchemy's signer service. @@ -462,7 +450,15 @@ export class AlchemySignerWebClient extends BaseSignerClient public override oauthWithRedirect = async ( args: Extract ): Promise => { - const providerUrl = await this.getOauthProviderUrl(args); + const turnkeyPublicKey = await this.initIframeStamper(); + + const oauthParams = args; + const providerUrl = await this.getOauthProviderUrl({ + oauthParams, + turnkeyPublicKey, + oauthCallbackUrl: this.oauthCallbackUrl, + }); + window.location.href = providerUrl; return new Promise((_, reject) => setTimeout(() => reject("Failed to redirect to OAuth provider"), 1000) @@ -498,7 +494,13 @@ export class AlchemySignerWebClient extends BaseSignerClient public override oauthWithPopup = async ( args: Extract ): Promise => { - const providerUrl = await this.getOauthProviderUrl(args); + const turnkeyPublicKey = await this.initIframeStamper(); + const oauthParams = args; + const providerUrl = await this.getOauthProviderUrl({ + oauthParams, + turnkeyPublicKey, + oauthCallbackUrl: this.oauthCallbackUrl, + }); const popup = window.open( providerUrl, "_blank", @@ -556,85 +558,6 @@ export class AlchemySignerWebClient extends BaseSignerClient }); }; - private getOauthProviderUrl = async (args: OauthParams): Promise => { - const { - authProviderId, - isCustomProvider, - auth0Connection, - scope: providedScope, - claims: providedClaims, - mode, - redirectUrl, - expirationSeconds, - } = args; - const { codeChallenge, requestKey, authProviders } = - await this.getOauthConfigForMode(mode); - if (!authProviders) { - throw new OAuthProvidersError(); - } - const authProvider = authProviders.find( - (provider) => - provider.id === authProviderId && - !!provider.isCustomProvider === !!isCustomProvider - ); - if (!authProvider) { - throw new Error(`No auth provider found with id ${authProviderId}`); - } - let scope: string; - let claims: string | undefined; - if (providedScope) { - scope = addOpenIdIfAbsent(providedScope); - claims = providedClaims; - } else { - if (isCustomProvider) { - throw new Error("scope must be provided for a custom provider"); - } - const scopeAndClaims = getDefaultScopeAndClaims(authProviderId); - if (!scopeAndClaims) { - throw new Error( - `Default scope not known for provider ${authProviderId}` - ); - } - ({ scope, claims } = scopeAndClaims); - } - const { authEndpoint, clientId } = authProvider; - const turnkeyPublicKey = await this.initIframeStamper(); - const nonce = getOauthNonce(turnkeyPublicKey); - const stateObject: OauthState = { - authProviderId, - isCustomProvider, - requestKey, - turnkeyPublicKey, - expirationSeconds, - redirectUrl: - mode === "redirect" ? resolveRelativeUrl(redirectUrl) : undefined, - openerOrigin: mode === "popup" ? window.location.origin : undefined, - }; - const state = base64UrlEncode( - new TextEncoder().encode(JSON.stringify(stateObject)) - ); - const authUrl = new URL(authEndpoint); - const params: Record = { - redirect_uri: this.oauthCallbackUrl, - response_type: "code", - scope, - state, - code_challenge: codeChallenge, - code_challenge_method: "S256", - prompt: "select_account", - client_id: clientId, - nonce, - }; - if (claims) { - params.claims = claims; - } - if (auth0Connection) { - params.connection = auth0Connection; - } - authUrl.search = new URLSearchParams(params).toString(); - return authUrl.toString(); - }; - private initIframeStamper = async () => { if (!this.iframeStamper.publicKey()) { await this.iframeStamper.init(); @@ -723,38 +646,6 @@ export class AlchemySignerWebClient extends BaseSignerClient const nonce = getOauthNonce(publicKey); return this.request("/v1/prepare-oauth", { nonce }); }; - - private getOauthConfigForMode = async ( - mode: OauthMode - ): Promise => { - if (this.oauthConfig) { - return this.oauthConfig; - } else if (mode === "redirect") { - return this.initOauth(); - } else { - throw new Error( - "enablePopupOauth must be set in configuration or signer.preparePopupOauth must be called before using popup-based OAuth login" - ); - } - }; -} - -function resolveRelativeUrl(url: string): string { - // Funny trick. - const a = document.createElement("a"); - a.href = url; - return a.href; -} - -/** - * "openid" is a required scope in the OIDC protocol. Insert it if the user - * forgot. - * - * @param {string} scope scope param which may be missing "openid" - * @returns {string} scope which most definitely contains "openid" - */ -function addOpenIdIfAbsent(scope: string): string { - return scope.match(/\bopenid\b/) ? scope : `openid ${scope}`; } /** diff --git a/account-kit/signer/src/client/types.ts b/account-kit/signer/src/client/types.ts index 5da1d2e968..b173b7613e 100644 --- a/account-kit/signer/src/client/types.ts +++ b/account-kit/signer/src/client/types.ts @@ -99,7 +99,9 @@ export type SignerEndpoints = [ { Route: "/v1/signup"; Body: - | (Omit & { redirectParams?: string }) + | (Omit & { + redirectParams?: string; + }) | { passkey: { challenge: string; @@ -117,7 +119,9 @@ export type SignerEndpoints = [ }, { Route: "/v1/auth"; - Body: Omit & { redirectParams?: string }; + Body: Omit & { + redirectParams?: string; + }; Response: { orgId: string; otpId?: string; @@ -177,3 +181,21 @@ export type GetWebAuthnAttestationResult = { challenge: ArrayBuffer; authenticatorUserId: ArrayBuffer; }; + +export type OauthState = { + authProviderId: string; + isCustomProvider?: boolean; + requestKey: string; + turnkeyPublicKey: string; + expirationSeconds?: number; + redirectUrl?: string; + openerOrigin?: string; +}; + +export type GetOauthProviderUrlArgs = { + oauthParams: OauthParams; + turnkeyPublicKey: string; + oauthCallbackUrl: string; + oauthConfig?: OauthConfig; + usesRelativeUrl?: boolean; +}; diff --git a/account-kit/signer/src/oauth.ts b/account-kit/signer/src/oauth.ts index 8338ffda59..3bb94dd692 100644 --- a/account-kit/signer/src/oauth.ts +++ b/account-kit/signer/src/oauth.ts @@ -34,3 +34,14 @@ export function getDefaultScopeAndClaims( ): ScopeAndClaims | undefined { return DEFAULT_SCOPE_AND_CLAIMS[knownAuthProviderId]; } + +/** + * "openid" is a required scope in the OIDC protocol. Insert it if the user + * forgot. + * + * @param {string} scope scope param which may be missing "openid" + * @returns {string} scope which most definitely contains "openid" + */ +export function addOpenIdIfAbsent(scope: string): string { + return scope.match(/\bopenid\b/) ? scope : `openid ${scope}`; +} diff --git a/account-kit/signer/src/utils/resolveRelativeUrl.ts b/account-kit/signer/src/utils/resolveRelativeUrl.ts new file mode 100644 index 0000000000..f0006ca4e9 --- /dev/null +++ b/account-kit/signer/src/utils/resolveRelativeUrl.ts @@ -0,0 +1,6 @@ +export function resolveRelativeUrl(url: string): string { + // Funny trick. + const a = document.createElement("a"); + a.href = url; + return a.href; +} diff --git a/site/pages/reference/account-kit/signer/classes/BaseSignerClient/getOauthProviderUrl.mdx b/site/pages/reference/account-kit/signer/classes/BaseSignerClient/getOauthProviderUrl.mdx new file mode 100644 index 0000000000..56fc2efb24 --- /dev/null +++ b/site/pages/reference/account-kit/signer/classes/BaseSignerClient/getOauthProviderUrl.mdx @@ -0,0 +1,56 @@ +--- +# This file is autogenerated + +title: getOauthProviderUrl +description: Overview of the getOauthProviderUrl method +--- + +# getOauthProviderUrl + +Returns the authentication url for the selected OAuth Proivder + +## Import + +```ts +import { BaseSignerClient } from "@account-kit/signer"; +``` + +## Usage + +```ts + +cosnt oauthParams = { +authProviderId: "google", +isCustomProvider: false, +auth0Connection: undefined, +scope: undefined, +claims: undefined, +mode: "redirect", +redirectUrl: "https://your-url-path/oauth-return", +expirationSeconds: 3000 +}; + +const turnkeyPublicKey = await this.initIframeStamper(); +const oauthCallbackUrl = this.oauthCallbackUrl; +const oauthConfig = this.getOauthConfig() // Optional value for OauthConfig() +const usesRelativeUrl = true // Optional value to determine if we use a relative (or absolute) url for the `redirect_url` + +const oauthProviderUrl = getOauthProviderUrl({ +oauthParams, +turnkeyPublicKey, +oauthCallbackUrl +}) + +``` + +## Parameters + +### args + +`GetOauthProviderUrlArgs` +Required. The Oauth provider's auth parameters + +## Returns + +`Promise` +returns the Oauth provider's url From 6150395067cece0a22e94bc5e1f8517845f03831 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Mon, 20 Jan 2025 11:22:15 -0500 Subject: [PATCH 13/20] fix: remove unused function - getDeepLink() --- account-kit/rn-signer/src/utils/getDeepLink.ts | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 account-kit/rn-signer/src/utils/getDeepLink.ts diff --git a/account-kit/rn-signer/src/utils/getDeepLink.ts b/account-kit/rn-signer/src/utils/getDeepLink.ts deleted file mode 100644 index ccbac7c99b..0000000000 --- a/account-kit/rn-signer/src/utils/getDeepLink.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Platform } from "react-native"; -export const getDeepLink = (path = "") => { - const scheme = "rn-signer-demo"; - const prefix = - Platform.OS === "android" ? `${scheme}://my-host/` : `${scheme}://`; - return prefix + path; -}; From 0aabec87394a1b14cbf645282df783e8b4df691a Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Mon, 20 Jan 2025 12:51:02 -0500 Subject: [PATCH 14/20] fix: fix oauthWithRedirect() method's return type --- account-kit/rn-signer/src/client.ts | 105 +++++++++++++--------------- 1 file changed, 48 insertions(+), 57 deletions(-) diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 0a2c94a95f..7f027dda5a 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -135,67 +135,58 @@ export class RNSignerClient extends BaseSignerClient { override oauthWithRedirect = async ( args: Extract ): Promise => { - try { - const oauthParams = args; - const turnkeyPublicKey = await this.stamper.init(); - const oauthCallbackUrl = this.oauthCallbackUrl; - const oauthConfig = await this.getOauthConfig(); - - const providerUrl = await this.getOauthProviderUrl({ - oauthParams, - turnkeyPublicKey, - oauthCallbackUrl, - oauthConfig, - usesRelativeUrl: false, - }); + // Ensure the In-App Browser required for authentication is available + if (!(await InAppBrowser.isAvailable())) { + throw new Error("Browser not available"); + } + + const oauthParams = args; + const turnkeyPublicKey = await this.stamper.init(); + const oauthCallbackUrl = this.oauthCallbackUrl; + const oauthConfig = await this.getOauthConfig(); + const providerUrl = await this.getOauthProviderUrl({ + oauthParams, + turnkeyPublicKey, + oauthCallbackUrl, + oauthConfig, + usesRelativeUrl: false, + }); + const redirectUrl = args.redirectUrl; + const res = await InAppBrowser.openAuth(providerUrl, redirectUrl); + + if (res.type !== "success" || !res.url) { + throw new OauthFailedError("An error occured completing your request"); + } - const redirectUrl = args.redirectUrl; - - return new Promise(async (resolve, reject) => { - if (await InAppBrowser.isAvailable()) { - const res = await InAppBrowser.openAuth(providerUrl, redirectUrl); - - if (res.type !== "success" || !res.url) { - return reject( - new OauthFailedError("An error occured completing your request") - ); - } - - const authResult = parseSearchParams(res.url); - - const bundle = authResult["alchemy-bundle"] ?? ""; - const orgId = authResult["alchemy-org-id"] ?? ""; - const idToken = authResult["alchemy-id-token"] ?? ""; - const isSignup = authResult["alchemy-is-signup"]; - const error = authResult["alchemy-error"]; - - if (bundle && orgId && idToken) { - this.completeAuthWithBundle({ - bundle, - orgId, - connectedEventName: "connectedOauth", - idToken, - authenticatingType: "oauth", - }).then((user) => { - if (isSignup) { - this.eventEmitter.emit("newUserSignup"); - } - resolve(user); - }, reject); - } else if (error) { - reject(new OauthFailedError(error)); - } - } else { - throw new Error( - "Couldn't perfom authentication operation. Browser not available." - ); - } + const authResult = parseSearchParams(res.url); + const bundle = authResult["alchemy-bundle"] ?? ""; + const orgId = authResult["alchemy-org-id"] ?? ""; + const idToken = authResult["alchemy-id-token"] ?? ""; + const isSignup = authResult["alchemy-is-signup"]; + const error = authResult["alchemy-error"]; + + if (bundle && orgId && idToken) { + const user = await this.completeAuthWithBundle({ + bundle, + orgId, + connectedEventName: "connectedOauth", + idToken, + authenticatingType: "oauth", }); - } catch (e) { - console.error("Error performing OAuth Operation", e); - throw new Error("Error performing OAuth Operation"); + + if (isSignup) { + this.eventEmitter.emit("newUserSignup"); + } + + return user; } + + // Throw the Alchemy error if available, otherwise throw a generic error. + throw new OauthFailedError( + error ?? "An error occured completing your request" + ); }; + override oauthWithPopup( _args: Extract ): Promise { From e470782c2baca2982775623f5e4a3654a499c949 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Mon, 20 Jan 2025 13:21:37 -0500 Subject: [PATCH 15/20] feat: add inAppBrowserError class --- account-kit/rn-signer/src/client.ts | 3 ++- account-kit/rn-signer/src/errors.ts | 10 ++++++++++ account-kit/signer/src/errors.ts | 4 +++- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 account-kit/rn-signer/src/errors.ts diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 7f027dda5a..2e1a96c926 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -21,6 +21,7 @@ import { getOauthNonce } from "@account-kit/signer"; import { z } from "zod"; import { InAppBrowser } from "react-native-inappbrowser-reborn"; import { parseSearchParams } from "./utils/parseUrlParams"; +import { InAppBrowserUnavailableError } from "./errors"; export const RNSignerClientParamsSchema = z.object({ connection: z.custom(), @@ -137,7 +138,7 @@ export class RNSignerClient extends BaseSignerClient { ): Promise => { // Ensure the In-App Browser required for authentication is available if (!(await InAppBrowser.isAvailable())) { - throw new Error("Browser not available"); + throw new InAppBrowserUnavailableError(); } const oauthParams = args; diff --git a/account-kit/rn-signer/src/errors.ts b/account-kit/rn-signer/src/errors.ts new file mode 100644 index 0000000000..3c03fe4b46 --- /dev/null +++ b/account-kit/rn-signer/src/errors.ts @@ -0,0 +1,10 @@ +import { BaseError } from "@aa-sdk/core"; + +export class InAppBrowserUnavailableError extends BaseError { + override name = "InAppBrowserUnavailableError"; + constructor() { + super( + "In-App Browser is not available. Please authenticate with a different method." + ); + } +} diff --git a/account-kit/signer/src/errors.ts b/account-kit/signer/src/errors.ts index 648e1681e1..4e642365c4 100644 --- a/account-kit/signer/src/errors.ts +++ b/account-kit/signer/src/errors.ts @@ -18,6 +18,8 @@ export class NotAuthenticatedError extends BaseError { export class OAuthProvidersError extends BaseError { override name = "OAuthProvidersError"; constructor() { - super("OAuth providers not found", { docsPath: "/react/getting-started" }); + super("OAuth providers not found", { + docsPath: "/react/getting-started", + }); } } From 14d09147c29086dd06efc337ca2fedcfb702afb6 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Tue, 21 Jan 2025 13:19:38 -0500 Subject: [PATCH 16/20] feat: use protected instead of public for getOauthProviderUrl --- account-kit/signer/src/client/base.ts | 2 +- .../BaseSignerClient/getOauthProviderUrl.mdx | 56 ------------------- 2 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 site/pages/reference/account-kit/signer/classes/BaseSignerClient/getOauthProviderUrl.mdx diff --git a/account-kit/signer/src/client/base.ts b/account-kit/signer/src/client/base.ts index 8a416b86ad..497e266bf8 100644 --- a/account-kit/signer/src/client/base.ts +++ b/account-kit/signer/src/client/base.ts @@ -512,7 +512,7 @@ export abstract class BaseSignerClient { * * @returns {Promise} returns the Oauth provider's url */ - public getOauthProviderUrl = async ( + protected getOauthProviderUrl = async ( args: GetOauthProviderUrlArgs ): Promise => { const { diff --git a/site/pages/reference/account-kit/signer/classes/BaseSignerClient/getOauthProviderUrl.mdx b/site/pages/reference/account-kit/signer/classes/BaseSignerClient/getOauthProviderUrl.mdx deleted file mode 100644 index 56fc2efb24..0000000000 --- a/site/pages/reference/account-kit/signer/classes/BaseSignerClient/getOauthProviderUrl.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -# This file is autogenerated - -title: getOauthProviderUrl -description: Overview of the getOauthProviderUrl method ---- - -# getOauthProviderUrl - -Returns the authentication url for the selected OAuth Proivder - -## Import - -```ts -import { BaseSignerClient } from "@account-kit/signer"; -``` - -## Usage - -```ts - -cosnt oauthParams = { -authProviderId: "google", -isCustomProvider: false, -auth0Connection: undefined, -scope: undefined, -claims: undefined, -mode: "redirect", -redirectUrl: "https://your-url-path/oauth-return", -expirationSeconds: 3000 -}; - -const turnkeyPublicKey = await this.initIframeStamper(); -const oauthCallbackUrl = this.oauthCallbackUrl; -const oauthConfig = this.getOauthConfig() // Optional value for OauthConfig() -const usesRelativeUrl = true // Optional value to determine if we use a relative (or absolute) url for the `redirect_url` - -const oauthProviderUrl = getOauthProviderUrl({ -oauthParams, -turnkeyPublicKey, -oauthCallbackUrl -}) - -``` - -## Parameters - -### args - -`GetOauthProviderUrlArgs` -Required. The Oauth provider's auth parameters - -## Returns - -`Promise` -returns the Oauth provider's url From 979d487e9c9a692f535cc9a5b3f807ead98e6d1a Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Thu, 23 Jan 2025 15:02:39 -0500 Subject: [PATCH 17/20] fix: remove getOauthNonce from exported signer members --- account-kit/rn-signer/src/client.ts | 13 +++--- account-kit/signer/src/client/base.ts | 20 +++++--- account-kit/signer/src/client/index.ts | 3 +- account-kit/signer/src/index.ts | 1 - account-kit/signer/src/oauth.ts | 11 ----- .../functions/RNAlchemySigner.mdx | 46 +++++++++++++++++++ .../functions/getDefaultScopeAndClaims.mdx | 27 ----------- .../signer/functions/getOauthNonce.mdx | 27 ----------- 8 files changed, 67 insertions(+), 81 deletions(-) create mode 100644 site/pages/reference/account-kit/react-native/functions/RNAlchemySigner.mdx delete mode 100644 site/pages/reference/account-kit/signer/functions/getDefaultScopeAndClaims.mdx delete mode 100644 site/pages/reference/account-kit/signer/functions/getOauthNonce.mdx diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 2e1a96c926..b9dedf087f 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -17,7 +17,6 @@ import { type User, } from "@account-kit/signer"; import NativeTEKStamper from "./NativeTEKStamper"; -import { getOauthNonce } from "@account-kit/signer"; import { z } from "zod"; import { InAppBrowser } from "react-native-inappbrowser-reborn"; import { parseSearchParams } from "./utils/parseUrlParams"; @@ -119,7 +118,10 @@ export class RNSignerClient extends BaseSignerClient { throw new Error("Unsupported authenticating type"); } - this.eventEmitter.emit("authenticating", { type: "email" }); + this.eventEmitter.emit("authenticating", { + type: params.authenticatingType, + }); + await this.stamper.init(); const result = await this.stamper.injectCredentialBundle(params.bundle); @@ -141,6 +143,8 @@ export class RNSignerClient extends BaseSignerClient { throw new InAppBrowserUnavailableError(); } + this.eventEmitter.emit("authenticating", { type: "oauth" }); + const oauthParams = args; const turnkeyPublicKey = await this.stamper.init(); const oauthCallbackUrl = this.oauthCallbackUrl; @@ -214,12 +218,9 @@ export class RNSignerClient extends BaseSignerClient { } protected override getOauthConfig = async (): Promise => { - const currentStamper = this.turnkeyClient.stamper; const publicKey = await this.stamper.init(); - // swap the stamper back in case the user logged in with a different stamper (passkeys) - this.setStamper(currentStamper); - const nonce = getOauthNonce(publicKey); + const nonce = this.getOauthNonce(publicKey); return this.request("/v1/prepare-oauth", { nonce }); }; } diff --git a/account-kit/signer/src/client/base.ts b/account-kit/signer/src/client/base.ts index 497e266bf8..0d787ccbbb 100644 --- a/account-kit/signer/src/client/base.ts +++ b/account-kit/signer/src/client/base.ts @@ -2,7 +2,7 @@ import { ConnectionConfigSchema, type ConnectionConfig } from "@aa-sdk/core"; import { TurnkeyClient, type TSignedRequest } from "@turnkey/http"; import EventEmitter from "eventemitter3"; import { jwtDecode } from "jwt-decode"; -import type { Hex } from "viem"; +import { sha256, type Hex } from "viem"; import { NotAuthenticatedError, OAuthProvidersError } from "../errors.js"; import { base64UrlEncode } from "../utils/base64UrlEncode.js"; import { assertNever } from "../utils/typeAssertions.js"; @@ -26,11 +26,7 @@ import type { User, } from "./types.js"; import type { OauthMode } from "../signer.js"; -import { - addOpenIdIfAbsent, - getDefaultScopeAndClaims, - getOauthNonce, -} from "../oauth.js"; +import { addOpenIdIfAbsent, getDefaultScopeAndClaims } from "../oauth.js"; export interface BaseSignerClientParams { stamper: TurnkeyClient["stamper"]; @@ -571,7 +567,7 @@ export abstract class BaseSignerClient { } const { authEndpoint, clientId } = authProvider; - const nonce = getOauthNonce(turnkeyPublicKey); + const nonce = this.getOauthNonce(turnkeyPublicKey); const stateObject: OauthState = { authProviderId, isCustomProvider, @@ -680,4 +676,14 @@ export abstract class BaseSignerClient { return this.pollActivityCompletion(activity, organizationId, resultKey); }; // #endregion + + /** + * Turnkey requires the nonce in the id token to be in this format. + * + * @param {string} turnkeyPublicKey key from a Turnkey iframe + * @returns {string} nonce to be used in OIDC + */ + protected getOauthNonce = (turnkeyPublicKey: string): string => { + return sha256(new TextEncoder().encode(turnkeyPublicKey)).slice(2); + }; } diff --git a/account-kit/signer/src/client/index.ts b/account-kit/signer/src/client/index.ts index 378ea32b9c..68b0552828 100644 --- a/account-kit/signer/src/client/index.ts +++ b/account-kit/signer/src/client/index.ts @@ -3,7 +3,6 @@ import { getWebAuthnAttestation } from "@turnkey/http"; import { IframeStamper } from "@turnkey/iframe-stamper"; import { WebauthnStamper } from "@turnkey/webauthn-stamper"; import { z } from "zod"; -import { getOauthNonce } from "../oauth.js"; import type { AuthParams } from "../signer.js"; import { base64UrlEncode } from "../utils/base64UrlEncode.js"; import { generateRandomBuffer } from "../utils/generateRandomBuffer.js"; @@ -643,7 +642,7 @@ export class AlchemySignerWebClient extends BaseSignerClient // swap the stamper back in case the user logged in with a different stamper (passkeys) this.setStamper(currentStamper); - const nonce = getOauthNonce(publicKey); + const nonce = this.getOauthNonce(publicKey); return this.request("/v1/prepare-oauth", { nonce }); }; } diff --git a/account-kit/signer/src/index.ts b/account-kit/signer/src/index.ts index 4d1842a17b..deb0f1735f 100644 --- a/account-kit/signer/src/index.ts +++ b/account-kit/signer/src/index.ts @@ -15,4 +15,3 @@ export { AlchemyWebSigner } from "./signer.js"; export type * from "./types.js"; export { AlchemySignerStatus } from "./types.js"; export { OAuthProvidersError, NotAuthenticatedError } from "./errors.js"; -export { getOauthNonce, getDefaultScopeAndClaims } from "./oauth.js"; diff --git a/account-kit/signer/src/oauth.ts b/account-kit/signer/src/oauth.ts index 3bb94dd692..e841f04cc6 100644 --- a/account-kit/signer/src/oauth.ts +++ b/account-kit/signer/src/oauth.ts @@ -1,16 +1,5 @@ -import { sha256 } from "viem"; import type { KnownAuthProvider } from "./signer"; -/** - * Turnkey requires the nonce in the id token to be in this format. - * - * @param {string} turnkeyPublicKey key from a Turnkey iframe - * @returns {string} nonce to be used in OIDC - */ -export function getOauthNonce(turnkeyPublicKey: string): string { - return sha256(new TextEncoder().encode(turnkeyPublicKey)).slice(2); -} - export type ScopeAndClaims = { scope: string; claims?: string; diff --git a/site/pages/reference/account-kit/react-native/functions/RNAlchemySigner.mdx b/site/pages/reference/account-kit/react-native/functions/RNAlchemySigner.mdx new file mode 100644 index 0000000000..fdcee4ace1 --- /dev/null +++ b/site/pages/reference/account-kit/react-native/functions/RNAlchemySigner.mdx @@ -0,0 +1,46 @@ +--- +# This file is autogenerated + +title: RNAlchemySigner +description: Overview of the RNAlchemySigner method +--- + +# RNAlchemySigner + +Factory function to create or retrieve a singleton instance of RNAlchemySigner. + +## Import + +```ts +import { RNAlchemySigner } from "@account-kit/react-native-signer"; +``` + +## Usage + +```ts twoslash +import { RNAlchemySigner } from "@account-kit/react-native-signer"; + +const signer = RNAlchemySigner({ + client: { + connection: { + apiKey: "YOUR_API_KEY", + }, + }, + // optional config to override default session manager configs + sessionConfig: { + expirationTimeMs: 1000 * 60 * 60, // 60 minutes + }, +}); +``` + +## Parameters + +### params + +`RNAlchemySignerParams` +The parameters required to configure the RNAlchemySigner instance. + +## Returns + +`RNAlchemySignerSingleton` +The singleton instance of RNAlchemySigner configured with the provided parameters. diff --git a/site/pages/reference/account-kit/signer/functions/getDefaultScopeAndClaims.mdx b/site/pages/reference/account-kit/signer/functions/getDefaultScopeAndClaims.mdx deleted file mode 100644 index 758b3cdd31..0000000000 --- a/site/pages/reference/account-kit/signer/functions/getDefaultScopeAndClaims.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -# This file is autogenerated -title: getDefaultScopeAndClaims -description: Overview of the getDefaultScopeAndClaims method ---- - -# getDefaultScopeAndClaims - -Returns the default scope and claims when using a known auth provider - -## Import - -```ts -import { getDefaultScopeAndClaims } from "@account-kit/signer"; -``` - -## Parameters - -### knownAuthProviderId - -`string` -id of a known auth provider, e.g. "google" - -## Returns - -`ScopeAndClaims | undefined` -default scope and claims diff --git a/site/pages/reference/account-kit/signer/functions/getOauthNonce.mdx b/site/pages/reference/account-kit/signer/functions/getOauthNonce.mdx deleted file mode 100644 index 03dabff6f7..0000000000 --- a/site/pages/reference/account-kit/signer/functions/getOauthNonce.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -# This file is autogenerated -title: getOauthNonce -description: Overview of the getOauthNonce method ---- - -# getOauthNonce - -Turnkey requires the nonce in the id token to be in this format. - -## Import - -```ts -import { getOauthNonce } from "@account-kit/signer"; -``` - -## Parameters - -### turnkeyPublicKey - -`string` -key from a Turnkey iframe - -## Returns - -`string` -nonce to be used in OIDC From 3928b9225e24247f51a4b2e2469232e44e90e7de Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Thu, 23 Jan 2025 15:04:21 -0500 Subject: [PATCH 18/20] fix: fix linting --- .../infra/functions/alchemyGasManagerMiddleware.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/pages/reference/account-kit/infra/functions/alchemyGasManagerMiddleware.mdx b/site/pages/reference/account-kit/infra/functions/alchemyGasManagerMiddleware.mdx index 6d484a1301..7fbd8327b1 100644 --- a/site/pages/reference/account-kit/infra/functions/alchemyGasManagerMiddleware.mdx +++ b/site/pages/reference/account-kit/infra/functions/alchemyGasManagerMiddleware.mdx @@ -18,13 +18,13 @@ import { alchemyGasManagerMiddleware } from "@account-kit/infra"; ## Usage ```ts -import { sepolia, alchemyGasManagerMiddleware } from "@account-kit/infra"; +import { sepolia, alchemyErc7677Middleware } from "@account-kit/infra"; import { http } from "viem"; const client = createSmartAccountClient({ transport: http("rpc-url"), chain: sepolia, - ...alchemyGasManagerMiddleware("policyId"), + ...alchemyErc7677Middleware("policyId"), }); ``` From bc3a9a1a8f6d69db49c812f773add1603c49c8c7 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Fri, 24 Jan 2025 11:30:47 -0500 Subject: [PATCH 19/20] feat: add ts extension to signer index exports --- account-kit/rn-signer/src/index.tsx | 4 +- account-kit/rn-signer/tsconfig.json | 57 +++++++++++++++-------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/account-kit/rn-signer/src/index.tsx b/account-kit/rn-signer/src/index.tsx index f2923199fa..5cd0de42cc 100644 --- a/account-kit/rn-signer/src/index.tsx +++ b/account-kit/rn-signer/src/index.tsx @@ -1,3 +1 @@ -/* eslint-disable import/extensions */ -export { RNAlchemySigner } from "./signer"; -export type { RNAlchemySignerType } from "./signer"; +export { RNAlchemySigner, type RNAlchemySignerType } from "./signer.ts"; diff --git a/account-kit/rn-signer/tsconfig.json b/account-kit/rn-signer/tsconfig.json index a56426a136..56a43e4ad9 100644 --- a/account-kit/rn-signer/tsconfig.json +++ b/account-kit/rn-signer/tsconfig.json @@ -1,30 +1,31 @@ { - "extends": "typescript-template/base.json", - "compilerOptions": { - "rootDir": ".", - "paths": { - "@account-kit/react-native-signer": ["./src/index"] - }, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "jsx": "react-jsx", - "lib": ["ESNext"], - "module": "ESNext", - "moduleResolution": "Bundler", - "noEmit": true, - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "noImplicitUseStrict": false, - "noStrictGenericChecks": false, - "noUncheckedIndexedAccess": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "ESNext", - "verbatimModuleSyntax": true - } + "extends": "typescript-template/base.json", + "compilerOptions": { + "rootDir": ".", + "paths": { + "@account-kit/react-native-signer": ["./src/index"] + }, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "lib": ["ESNext"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noImplicitUseStrict": false, + "noStrictGenericChecks": false, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "ESNext", + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true + } } From 8e58124b6bc1f2edc2ef1ce2de09d84dffcd3937 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Fri, 24 Jan 2025 11:34:48 -0500 Subject: [PATCH 20/20] fix: fix lintng --- account-kit/rn-signer/tsconfig.json | 58 ++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/account-kit/rn-signer/tsconfig.json b/account-kit/rn-signer/tsconfig.json index 56a43e4ad9..131535092c 100644 --- a/account-kit/rn-signer/tsconfig.json +++ b/account-kit/rn-signer/tsconfig.json @@ -1,31 +1,31 @@ { - "extends": "typescript-template/base.json", - "compilerOptions": { - "rootDir": ".", - "paths": { - "@account-kit/react-native-signer": ["./src/index"] - }, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "jsx": "react-jsx", - "lib": ["ESNext"], - "module": "ESNext", - "moduleResolution": "Bundler", - "noEmit": true, - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "noImplicitUseStrict": false, - "noStrictGenericChecks": false, - "noUncheckedIndexedAccess": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "ESNext", - "verbatimModuleSyntax": true, - "allowImportingTsExtensions": true - } + "extends": "typescript-template/base.json", + "compilerOptions": { + "rootDir": ".", + "paths": { + "@account-kit/react-native-signer": ["./src/index"] + }, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "lib": ["ESNext"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noImplicitUseStrict": false, + "noStrictGenericChecks": false, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "ESNext", + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true + } }