#password auth guide for react native expo?

15 messages · Page 1 of 1 (latest)

warm stirrup
#

pls guide me.

eternal robinBOT
#

Hi @warm stirrup. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To set up password authentication for a React Native Expo project using Convex, I can guide you through the process. Here's a step-by-step guide based on the available information:

  1. First, make sure you have set up your Convex project. If you haven't, you can create a new project by running:
npm create convex@latest

Choose "React Native" and "Convex Auth" when prompted.

  1. Install the necessary dependencies:
npm install @convex-dev/auth @auth/[email protected]
  1. Run the initialization command:
npx @convex-dev/auth
  1. Add authentication tables to your schema. In your convex/schema.ts file, add:
import { defineSchema } from "convex/server";
import { authTables } from "@convex-dev/auth/server";

const schema = defineSchema({
  ...authTables,
  // Your other tables...
});

export default schema;
  1. Set up the React Native provider. Replace ConvexProvider with ConvexAuthProvider in your app's entry point:
import { ConvexAuthProvider } from "@convex-dev/auth/react";
import { ConvexReactClient } from "convex/react";

const convex = new ConvexReactClient(process.env.EXPO_PUBLIC_CONVEX_URL!);

export default function App() {
  return (
    <ConvexAuthProvider client={convex}>
      {/* Your app components */}
    </ConvexAuthProvider>
  );
}
  1. Configure the Password provider in your convex/auth.ts file:
import { Password } from "@convex-dev/auth/providers/Password";
import { convexAuth } from "@convex-dev/auth/server";

export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
  providers: [Password],
});
  1. Create a sign-in form component. Here's a basic example:
import { useAuthActions } from "@convex-dev/auth/react";
import { useState } from "react";
import { View, TextInput, Button } from "react-native";
#

export function SignIn() {
const { signIn } = useAuthActions();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [step, setStep] = useState<"signUp" | "signIn">("signIn");

const handleSubmit = () => {
const formData = new FormData();
formData.append("email", email);
formData.append("password", password);
formData.append("flow", step);
signIn("password", formData);
};

return (
<View>
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
/>
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button
title={step === "signIn" ? "Sign in" : "Sign up"}
onPress={handleSubmit}
/>
<Button
title={step === "signIn" ? "Sign up instead" : "Sign in instead"}
onPress={() => setStep(step === "signIn" ? "signUp" : "signIn")}
/>
</View>
);
}


This guide provides a basic setup for password authentication in a React Native Expo project using Convex. Remember to handle errors, add proper styling, and implement additional features like password reset as needed.

For more detailed information and advanced configurations, refer to the [Convex Auth documentation](<https://labs.convex.dev/auth>) and the [Password configuration guide](<https://labs.convex.dev/auth/config/passwords>).
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

@warm stirrup Kapa.ai is still learning and improving, please let me know how I did by reacting below.

warm stirrup
#

@eternal robin full example of using password auth in react native on client and authorized server function call

eternal robinBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Certainly! I'll provide you with a full example of using password authentication in React Native on the client-side and making an authorized server function call using Convex. This example will build upon the previous setup we discussed.

Let's start with the client-side implementation:

  1. Client-side (React Native):
import React, { useState } from 'react';
import { View, TextInput, Button, Text } from 'react-native';
import { useAuthActions, useConvexAuth } from "@convex-dev/auth/react";
import { useMutation, useQuery } from "convex/react";
import { api } from "../convex/_generated/api";

export function AuthExample() {
  const { signIn, signOut } = useAuthActions();
  const { isAuthenticated } = useConvexAuth();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [message, setMessage] = useState("");

  const createMessage = useMutation(api.messages.createMessage);
  const getMessages = useQuery(api.messages.getMessages);

  const handleSignIn = () => {
    const formData = new FormData();
    formData.append("email", email);
    formData.append("password", password);
    formData.append("flow", "signIn");
    signIn("password", formData);
  };

  const handleSignUp = () => {
    const formData = new FormData();
    formData.append("email", email);
    formData.append("password", password);
    formData.append("flow", "signUp");
    signIn("password", formData);
  };

  const handleCreateMessage = async () => {
    try {
      await createMessage({ text: message });
      setMessage("");
    } catch (error) {
      console.error("Error creating message:", error);
    }
  };
#

return (
<View>
{!isAuthenticated ? (
<>
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
/>
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button title="Sign In" onPress={handleSignIn} />
<Button title="Sign Up" onPress={handleSignUp} />
</>
) : (
<>
<Button title="Sign Out" onPress={signOut} />
<TextInput
placeholder="Enter message"
value={message}
onChangeText={setMessage}
/>
<Button title="Create Message" onPress={handleCreateMessage} />
<Text>Messages:</Text>
{getMessages?.map((msg, index) => (
<Text key={index}>{msg.text}</Text>
))}
</>
)}
</View>
);
}


Now, let's implement the server-side functions:

2. Server-side (Convex):

Create a new file `convex/messages.ts`:

```typescript
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
import { auth } from "./auth";

export const createMessage = mutation({
  args: { text: v.string() },
  handler: async (ctx, { text }) => {
    const userId = await auth.getUserId(ctx);
    if (userId === null) {
      throw new Error("Not authenticated");
    }
    await ctx.db.insert("messages", { text, userId });
  },
});

export const getMessages = query({
  handler: async (ctx) => {
    const userId = await auth.getUserId(ctx);
    if (userId === null) {
      throw new Error("Not authenticated");
    }
    return await ctx.db.query("messages").collect();
  },
});
  1. Update your schema in convex/schema.ts:
#
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
import { authTables } from "@convex-dev/auth/server";

export default defineSchema({
  ...authTables,
  messages: defineTable({
    text: v.string(),
    userId: v.string(),
  }),
});

This example demonstrates:

  1. Client-side authentication with sign-in, sign-up, and sign-out functionality.
  2. An authenticated mutation to create a message.
  3. An authenticated query to retrieve messages.
  4. Basic UI to interact with these features.

The server-side functions (createMessage and getMessages) use auth.getUserId(ctx) to ensure that only authenticated users can create or retrieve messages. If a user is not authenticated, these functions will throw an error.

Remember to wrap your main app component with ConvexAuthProvider as mentioned in the previous answer:

import { ConvexAuthProvider } from "@convex-dev/auth/react";
import { ConvexReactClient } from "convex/react";

const convex = new ConvexReactClient(process.env.EXPO_PUBLIC_CONVEX_URL!);

export default function App() {
  return (
    <ConvexAuthProvider client={convex}>
      <AuthExample />
    </ConvexAuthProvider>
  );
}

This setup provides a complete example of password authentication in React Native using Convex, including authorized server function calls. You can further customize and expand upon this example to fit your specific application needs.

For more detailed information on Convex Auth and its features, refer to the Convex Auth documentation.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@warm stirrup Kapa.ai is still learning and improving, please let me know how I did by reacting below.

warm stirrup
#

@eternal robin Auth is working fine as expected. I set up custom auth with password authentication. One problem I was facing was that if a user closed the app, waited for a while, and then reopened it, the token had expired, and the time needed to refresh the token was too long, making it seem like the user was logged out. That UX is bad. However, after reopening the app and generating a new token, everything works fine again.

eternal robinBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I understand your concern about the user experience when reopening the app after a period of inactivity. This is indeed a known issue with Convex Auth, particularly in React Native applications. Let me provide some insights based on the available information.

The problem you're describing is similar to what other users have reported. It appears to be related to token expiration and refresh mechanisms when the app is in the background for an extended period. Here are a few key points:

  1. Token Expiration: By default, Convex Auth tokens have a limited lifespan. When the app is inactive for a long time, these tokens can expire.

  2. Refresh Mechanism: The token refresh doesn't always happen immediately when the app is brought back to the foreground, leading to a temporary "logged out" state.

  3. Race Condition: There might be a race condition between the app trying to reconnect to Convex and the token refresh process.

Some users have reported that this issue is more likely to occur when the app has been in the background for a longer period, such as overnight Discord support thread.

While there isn't a definitive solution provided in the knowledge sources, here are a few suggestions that might help improve the user experience:

  1. Extend Token Duration: You can try increasing the session duration. This can be configured in your auth.config.ts file:

    export default {
      providers: [
        // Your providers
      ],
      session: {
        jwt: {
          durationMs: 60000 * 60 * 24 * 30, // 30 days
        },
      },
    };
    

    Discord support thread

#
  1. Handle Token Refresh: Implement a mechanism to proactively refresh the token when the app comes back to the foreground. You might need to modify your custom auth implementation to handle this.

  2. Loading State: Implement a loading state in your app that shows while the token is being refreshed. This can prevent the user from seeing a "logged out" state briefly.

  3. Error Handling: Implement robust error handling for authentication-related errors, allowing for graceful recovery and automatic re-authentication when possible.

It's worth noting that the Convex team is aware of this issue and is actively working on improvements. As mentioned by a Trusted Support Staff member: "We have some work in progress on this and is out today, I got to see some of it and wow, auth keeps getting more interesting" Discord support thread.

For the most up-to-date information and potential solutions, I recommend keeping an eye on the Convex Auth GitHub repository for updates and new releases that might address this issue.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: