From the last session, we implemented the _layout.tsx file in Step 5. Now we will write the login.tsx page:
app/
├── (auth)/
│ └── login.tsx <--- this tutorial
├── (protected)/
│ └── protected.tsx
├── _layout.tsx <--- done
└── index.tsx
Login page
This file will do the following things:
- Prepare the browser session
- Create deep link
- Start the OAuth flow
- Activate the session & redirect the user
1. Completing the browser auth session
We will tell Expo to finalize and clean up that browser session so the app can regain control.
WebBrowser.maybeCompleteAuthSession();
2. Create deep link
Create the deep link that the OAuth provider will come back to
const redirectUrl = Linking.createURL("/oauth-native-callback");
Expo automatically prefixes this path with our app’s scheme:
oauthpro2://oauth-native-callback
Our app's scheme:
app.json
{"expo":
{"name": "oauth-pro2",
"scheme": "oauthpro2", <--- App's scheme name
...
3. Start the OAuth flow
const { createdSessionId, setActive } =
await startOAuthFlow({ redirectUrl });
4. Make it active & redirects
if (createdSessionId) {
// activate the created session
await setActive({ session: createdSessionId });
// redirect to the protected page
router.replace("/protected");
}
Complete source code
login.tsx
import { useOAuth } from "@clerk/clerk-expo";
import * as Linking from "expo-linking";
import { Link, useRouter } from "expo-router";
import * as WebBrowser from "expo-web-browser";
import React from "react";
import { ActivityIndicator, Text, TouchableOpacity, View } from "react-native";
WebBrowser.maybeCompleteAuthSession();
export default function Login() {
const { startOAuthFlow } = useOAuth({ strategy: "oauth_google" });
const router = useRouter();
const [loading, setLoading] = React.useState(false);
/// Google Login button handler////
const onGoogleSignInPress = async () => {
try {
setLoading(true);
// Creates a deep link by prefixing the path with the app scheme/host.
// Example: myapp://oauth-native-callback (must match Clerk redirect config in Step 2).
const redirectUrl = Linking.createURL("/oauth-native-callback");
// Clerk's OAuth flow
// Starts the OAuth browser flow and returns the created session (if any)
// plus a helper to activate it in the Clerk client.
const { createdSessionId, setActive } = await startOAuthFlow({redirectUrl, });
if (createdSessionId) {
// call the setActive helper with the createdSessionId to tell Clerk
// which session should be active on the device
await setActive!({ session: createdSessionId });
router.replace("/protected");
}
} catch (err) {
console.error("OAuth error:", JSON.stringify(err, null, 2));
} finally {
setLoading(false);
}
};
return (
<View>
<Text>Login</Text>
<TouchableOpacity
onPress={onGoogleSignInPress}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#3A2F2F" />
) : (
<Text> Sign in with Google </Text>
)}
</TouchableOpacity>
</View>
);
}
Preview of the final flow!
Yes! It logged us in and landed us to the protected screen :)
For reference, the source code is hosted on:
GitHub: OAuth Prototype
https://github.com/pscientist/oauth-pro2
Next we will work on the Logout button so it completes the flow!
app/
├── (auth)/
│ └── login.tsx <--- done
├── (protected)/
│ └── protected.tsx <--- next to do
├── _layout.tsx <--- done
└── index.tsx





Top comments (0)