-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🐛 Bug Report: React Native - createOAuth2Session throws error #10
Comments
I would like to work on this issue please |
This is where the error occurs: Looks like href is read only in react native |
I try but don't show login form of google. Please check. Thanks |
React-native can't just open a web url. The Appwrite SDK was designed for web browsers, which make redirections, and for that reason certain parts like OAuth authentication won't work out of the box in react-native. There are a few ways around this though:
Hope this helps Note: this might be relevant for issue #1177797467 |
I was looking for a solution to avoid closed common other backend services. |
We're working a new dedicated React Native SDK. Moving this issue to the new repository |
Hey, nice that you're putting some efforts on a dedicated react-native sdk! :) Turns out that I've been trying appwrite in a RN app and this was the first thing that I came across. I managed to workaround the issue for now by overriding the I've used If anybody's interested on it until it's implemented in the sdk, here are my snippets: Use at your own risk!/lib/appwrite/account.tsimport * as WebBrowser from "expo-web-browser";
import { Account, AppwriteException, OAuthProvider } from "appwrite";
import { handleIncomingCookie } from "./handleIncomingCookie";
import { buildQueryParams } from "./uri";
export class OauthAwareAccount extends Account {
/**
* @deprecated Temporaryly use {@link createOAuthSession} instead, can't use this because of its return type (not a promise)
* @param provider
* @param success
* @param failure
* @param scopes
* @returns
*/
override createOAuth2Session(
provider: OAuthProvider,
success?: string | undefined,
failure?: string | undefined,
scopes?: string[] | undefined,
): void | URL {
return super.createOAuth2Session(provider, success, failure, scopes);
}
async createOAuthSession(
provider: OAuthProvider,
success?: string | undefined,
failure?: string | undefined,
scopes?: string[] | undefined,
): Promise<void | URL> {
if (!provider) {
throw new AppwriteException('Missing required parameter: "provider"');
}
const { endpoint, project } = this.client.config;
const apiPath = `/account/sessions/oauth2/${provider}`;
const payload: Record<string, string | string[] | undefined> = {
success,
failure,
scopes,
project,
};
const queryParams = buildQueryParams(payload);
const authUrl = `${endpoint}${apiPath}${queryParams ? `?${queryParams}` : ""}`;
const callbackUrl = `appwrite-callback-${project}`;
const browserResult = await WebBrowser.openAuthSessionAsync(
authUrl,
callbackUrl,
);
if (browserResult.type !== "success") {
return;
}
const url = browserResult.url;
if (!(await handleIncomingCookie(url, endpoint))) {
return;
}
return new URL(url);
}
} /lib/appwrite/handleIncomingCookie.tsimport CookieManager, { Cookie } from "@react-native-cookies/cookies";
import { AppwriteException } from "appwrite";
import { parseQueryParams } from "./uri";
export const handleIncomingCookie = async (url: string, endpoint: string) => {
if (!url.includes("appwrite-callback")) {
return false;
}
const queryParams = parseQueryParams(url);
if (!queryParams.key || !queryParams.secret || !queryParams.domain) {
throw new AppwriteException(
"Invalid OAuth2 Response. Key, Secret and Domain not available.",
500,
);
}
const domainUrl = new URL(endpoint);
const cookie: Cookie = {
name: queryParams.key,
value: queryParams.secret,
path: queryParams.path,
expires: queryParams.expires,
secure: "secure" in queryParams,
httpOnly: "httpOnly" in queryParams,
domain: domainUrl.hostname,
};
return CookieManager.set(domainUrl.toString(), cookie);
}; /lib/appwrite/uri.tsThe sdk has some utilities for this already, like export const buildQueryParams = (
params: Record<string, string | string[] | undefined>,
) =>
Object.keys(params).reduce((acc, currentKey) => {
const currentValueForKey = params[currentKey];
if (currentValueForKey === undefined) {
return acc;
}
if (Array.isArray(currentValueForKey)) {
const arrayQuery = currentValueForKey
.map(
(value) =>
`${encodeURIComponent(`${currentKey}[]`)}=${encodeURIComponent(value)}`,
)
.join("&");
return `${acc}&${arrayQuery}`;
}
return `${acc}&${encodeURIComponent(currentKey)}=${encodeURIComponent(currentValueForKey)}`;
}, "");
export const parseQueryParams = (url: string) => {
const queryParams = url.includes("?") ? url.split("?")[1] : url;
if (!queryParams) {
return {};
}
return queryParams.split("&").reduce(
(acc, curr) => {
const [key, value] = curr.split("=");
return { ...acc, [key as string]: value };
},
{} as Record<string, string | undefined>,
);
}; To use the code above simply instantiate a @eldadfux depending on what you envision for this on the react native sdk, I can PR something, just let me know. Hope this helps in some way! |
A simpler way to do it is with the following code:
In summary, it is using createOAuth2Token instead of createOAuth2Session because createOAuth2Token returns secret and userId which are necessary to create the session. |
could you tell me what is 'here-call-back-url' i don't get it |
The url to deep link back into your app. |
Thank you, that is what i need ! Without deeplink, you can use following logic. import {
openAuthSessionAsync,
WebBrowserAuthSessionResult,
} from "expo-web-browser";
...
const [browserResult, setBrowserResult] =
useState<WebBrowserAuthSessionResult | null>(null);
...
const signInWithProvider = async (provider: OAuthProvider) => {
console.log(`Signing in with ${provider}`);
const url = createOAuth2Token(provider);
if (url) {
const urlString = url.toString();
console.log(urlString);
if (Platform.OS !== "web") {
// Prevent the default behavior of linking to the default browser on native.
// event.preventDefault();
const result = await openAuthSessionAsync(urlString);
setBrowserResult(result);
}
} else {
console.error("Failed to obtain URL for provider:", provider);
}
};
// authService.js
export function createOAuth2Token(provider: OAuthProvider) {
try {
const token = account.createOAuth2Token(
provider,
SUCCESS_OAUTH2,
FAILURE_OAUTH2
);
return token;
} catch (error) {
throw new Error(error);
}
}
export async function createSession(userId: string, secret: string) {
try {
const session = await account.createSession(userId, secret);
return session;
} catch (error) {
throw new Error(error);
}
} |
This issue is fixed by using a dedicated RN SDK. |
Surprised that this issue is still open from 2022! Created some code in #35 if anyone needs up to date code for react native that works on mobile
|
👟 Reproduction steps
Start a new React Native project.
Enable OAuth.
👍 Expected behavior
Should have opened a window for selecting an account from the OAuth client like google
👎 Actual Behavior
Throwed Error
🎲 Appwrite version
Different version (specify in environment)
💻 Operating system
Windows
🧱 Your Environment
I am using Appwrite v:1.0.1.500
React Native 0.70.1
React 18.1.0
👀 Have you spent some time to check if this issue has been raised before?
🏢 Have you read the Code of Conduct?
The text was updated successfully, but these errors were encountered: