Replit offers two authentication approaches: Replit Auth for zero-setup user login using Replit accounts, and OAuth with external providers like Google, GitHub, or Discord for broader user bases. Replit Auth requires no configuration and gives you user ID, name, and profile image automatically. OAuth requires storing client IDs and secrets in Replit Secrets, configuring callback URLs that match your deployment domain, and managing sessions with cookies or JWTs. Always use HTTPS callback URLs and store all credentials in Secrets, never in code.
Authentication in Replit: Zero-Setup Replit Auth and OAuth with External Providers
Adding user authentication is one of the most common requirements for web applications, and Replit provides two paths depending on your audience. Replit Auth lets users log in with their Replit account with zero configuration, making it ideal for tools and apps targeting the Replit community. For apps serving a broader audience, OAuth with providers like Google, GitHub, or Discord gives users familiar login options. This tutorial covers both approaches, including session management, secure credential storage, and deployment considerations.
Prerequisites
- A Replit account with a web application project (Node.js or Python)
- Basic understanding of HTTP cookies and sessions
- A Google Cloud Console or GitHub Developer account for OAuth setup
- Familiarity with Replit Secrets for environment variable management
Step-by-step guide
Implement Replit Auth for zero-setup login
Implement Replit Auth for zero-setup login
Replit Auth is the simplest authentication option. It uses Replit accounts, requiring no API keys, no OAuth configuration, and no session management setup. When a user clicks the login button, Replit handles the entire authentication flow and provides your app with the user's ID, username, display name, and profile image URL through request headers. Replit Auth works automatically in development and deployed environments. The downside is that users must have a Replit account to log in, which limits your audience to the Replit community.
1// Replit Auth - read user info from request headers2import express from 'express';34const app = express();56// Replit Auth sends user data via headers7app.get('/api/me', (req, res) => {8 const userId = req.headers['x-replit-user-id'];9 const userName = req.headers['x-replit-user-name'];10 const userRoles = req.headers['x-replit-user-roles'];11 const profileImage = req.headers['x-replit-user-profile-image'];1213 if (!userId) {14 return res.status(401).json({ error: 'Not logged in' });15 }1617 res.json({18 id: userId,19 name: userName,20 roles: userRoles,21 profileImage,22 });23});2425// Frontend login button (add to your HTML/React)26// <script authed="location.reload()" src="https://auth.util.repl.co/script.js"></script>Expected result: Users can log in with their Replit account by clicking the login button. Your backend reads user info from headers.
Register your app with an OAuth provider
Register your app with an OAuth provider
For OAuth with external providers, start by registering your app. For Google, go to the Google Cloud Console, create a project, enable the Google+ API, and create OAuth 2.0 credentials under APIs & Services > Credentials. For GitHub, go to Settings > Developer Settings > OAuth Apps and create a new app. You need to set the callback URL to match your deployment domain. In development, use your Replit dev URL. In production, use your deployed .replit.app domain or custom domain. Copy the Client ID and Client Secret for the next step.
Expected result: You have a Client ID and Client Secret from your OAuth provider, and the callback URL matches your Replit app domain.
Store OAuth credentials in Replit Secrets
Store OAuth credentials in Replit Secrets
Never hardcode OAuth client secrets in your source code. Open Tools > Secrets and add your credentials. Create GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and SESSION_SECRET (a random string for encrypting session cookies). If you are using GitHub OAuth, create GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET instead. After adding these to workspace Secrets, remember to also add them in the Deployments pane configuration before publishing. Missing deployment secrets are the top cause of OAuth failures in production.
Expected result: All OAuth credentials and the session secret are stored in Replit Secrets, not in any source code file.
Implement the OAuth flow with Passport.js
Implement the OAuth flow with Passport.js
Install Passport.js and the strategy for your chosen provider. Passport handles the OAuth redirect flow: your app redirects users to the provider's login page, the provider redirects back with an authorization code, and Passport exchanges that code for an access token and user profile. Configure the callback URL to match what you registered with the provider. Store the user information in a session or database after successful authentication.
1import express from 'express';2import session from 'express-session';3import passport from 'passport';4import { Strategy as GoogleStrategy } from 'passport-google-oauth20';56const app = express();78// Session configuration9app.use(session({10 secret: process.env.SESSION_SECRET,11 resave: false,12 saveUninitialized: false,13 cookie: {14 secure: process.env.REPLIT_DEPLOYMENT === '1',15 maxAge: 24 * 60 * 60 * 1000, // 24 hours16 },17}));1819app.use(passport.initialize());20app.use(passport.session());2122// Google OAuth strategy23passport.use(new GoogleStrategy({24 clientID: process.env.GOOGLE_CLIENT_ID,25 clientSecret: process.env.GOOGLE_CLIENT_SECRET,26 callbackURL: '/auth/google/callback',27}, (accessToken, refreshToken, profile, done) => {28 // Save or find user in database29 const user = {30 id: profile.id,31 name: profile.displayName,32 email: profile.emails?.[0]?.value,33 avatar: profile.photos?.[0]?.value,34 };35 done(null, user);36}));3738passport.serializeUser((user, done) => done(null, user));39passport.deserializeUser((user, done) => done(null, user));Expected result: Passport is configured with your OAuth provider and session management is ready.
Add authentication routes and protect endpoints
Add authentication routes and protect endpoints
Create routes for initiating the OAuth flow, handling the callback, checking auth status, and logging out. The login route redirects users to the OAuth provider. The callback route handles the redirect back from the provider. Add a middleware function that checks if the user is authenticated and use it to protect routes that require login. Return 401 Unauthorized for API endpoints and redirect to the login page for page requests.
1// Auth routes2app.get('/auth/google',3 passport.authenticate('google', { scope: ['profile', 'email'] })4);56app.get('/auth/google/callback',7 passport.authenticate('google', { failureRedirect: '/login' }),8 (req, res) => res.redirect('/')9);1011app.get('/auth/logout', (req, res) => {12 req.logout((err) => {13 if (err) console.error('Logout error:', err);14 res.redirect('/');15 });16});1718// Auth check middleware19function requireAuth(req, res, next) {20 if (req.isAuthenticated()) return next();21 res.status(401).json({ error: 'Authentication required' });22}2324// Protected route25app.get('/api/profile', requireAuth, (req, res) => {26 res.json(req.user);27});2829// Check auth status (for frontend)30app.get('/api/auth/status', (req, res) => {31 res.json({32 authenticated: req.isAuthenticated(),33 user: req.user || null,34 });35});Expected result: Users can log in via Google, view their profile on protected routes, and log out. Unauthenticated requests receive a 401 response.
Update callback URLs for deployment
Update callback URLs for deployment
When you deploy your app, the domain changes from your development URL to the production .replit.app domain or custom domain. Update the callback URL in your OAuth provider's settings to include the production domain. You can add multiple callback URLs in most providers to support both development and production. If you use a custom domain, update the callback URL there too. A redirect URI mismatch is the second most common OAuth error after missing secrets. For complex multi-environment OAuth setups, RapidDev can help design an authentication architecture that scales.
1// Dynamic callback URL based on environment2const callbackURL = process.env.REPLIT_DEPLOYMENT === '1'3 ? `https://${process.env.REPLIT_DOMAINS}/auth/google/callback`4 : `https://${process.env.REPLIT_DEV_DOMAIN}/auth/google/callback`;56passport.use(new GoogleStrategy({7 clientID: process.env.GOOGLE_CLIENT_ID,8 clientSecret: process.env.GOOGLE_CLIENT_SECRET,9 callbackURL,10}, strategyCallback));Expected result: OAuth works in both development and production because the callback URL matches the registered URL in your provider settings.
Complete working example
1// src/server.js2// Express server with Google OAuth and session management34import express from 'express';5import session from 'express-session';6import passport from 'passport';7import { Strategy as GoogleStrategy } from 'passport-google-oauth20';89const app = express();10const isProduction = process.env.REPLIT_DEPLOYMENT === '1';1112// Validate required secrets13const required = ['GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'SESSION_SECRET'];14for (const key of required) {15 if (!process.env[key]) {16 console.error(`Missing secret: ${key}. Add it in Tools > Secrets.`);17 if (isProduction) process.exit(1);18 }19}2021app.use(express.json());22app.use(session({23 secret: process.env.SESSION_SECRET || 'dev-secret',24 resave: false,25 saveUninitialized: false,26 cookie: { secure: isProduction, maxAge: 86400000 },27}));28app.use(passport.initialize());29app.use(passport.session());3031passport.use(new GoogleStrategy({32 clientID: process.env.GOOGLE_CLIENT_ID,33 clientSecret: process.env.GOOGLE_CLIENT_SECRET,34 callbackURL: '/auth/google/callback',35}, (accessToken, refreshToken, profile, done) => {36 done(null, {37 id: profile.id,38 name: profile.displayName,39 email: profile.emails?.[0]?.value,40 avatar: profile.photos?.[0]?.value,41 });42}));4344passport.serializeUser((user, done) => done(null, user));45passport.deserializeUser((user, done) => done(null, user));4647// Health check48app.get('/', (req, res) => res.json({ status: 'ok' }));4950// Auth routes51app.get('/auth/google',52 passport.authenticate('google', { scope: ['profile', 'email'] }));53app.get('/auth/google/callback',54 passport.authenticate('google', { failureRedirect: '/' }),55 (req, res) => res.redirect('/'));56app.get('/auth/logout', (req, res) => {57 req.logout(() => res.redirect('/'));58});59app.get('/api/auth/status', (req, res) => {60 res.json({ authenticated: req.isAuthenticated(), user: req.user || null });61});6263// Protected route64app.get('/api/profile', (req, res) => {65 if (!req.isAuthenticated()) return res.status(401).json({ error: 'Login required' });66 res.json(req.user);67});6869const PORT = process.env.PORT || 3000;70app.listen(PORT, '0.0.0.0', () => console.log(`Server on port ${PORT}`));Common mistakes when implementing authentication in Replit apps
Why it's a problem: Hardcoding OAuth client secrets in source code instead of using Secrets
How to avoid: Store GOOGLE_CLIENT_SECRET and SESSION_SECRET in Tools > Secrets. Read them with process.env in your code. Hardcoded secrets can be seen by anyone who accesses or remixes your project.
Why it's a problem: Forgetting to update the OAuth callback URL for the deployed domain
How to avoid: Add your production .replit.app URL to the allowed redirect URIs in your OAuth provider settings. A mismatch causes a 'redirect_uri_mismatch' error.
Why it's a problem: Not adding OAuth secrets to the deployment configuration
How to avoid: Workspace secrets do not transfer to deployments. Add GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and SESSION_SECRET in the Deployments pane before publishing.
Why it's a problem: Using cookie.secure = true in development
How to avoid: Secure cookies only work over HTTPS. Set secure to true only in production using process.env.REPLIT_DEPLOYMENT === '1'. In development, leave it false.
Why it's a problem: Not implementing logout functionality
How to avoid: Always provide a logout route that calls req.logout() and redirects the user. Without it, users cannot end their session, which is a security concern.
Best practices
- Use Replit Auth for apps targeting the Replit community and OAuth for broader audiences
- Store all OAuth client secrets and session keys in Replit Secrets, never in source code
- Add OAuth secrets to both workspace Secrets and the deployment configuration
- Set cookie.secure to true in production to prevent session hijacking over HTTP
- Register separate OAuth apps for development and production callback URLs
- Validate required secrets at startup and exit with a clear error in production if any are missing
- Use relative callback URLs like /auth/google/callback to simplify multi-environment setups
- Always provide an auth status API endpoint for your frontend to check login state
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to add Google OAuth login to my Express.js app on Replit. Show me how to set up Passport.js with Google strategy, manage sessions, store credentials in Replit Secrets, and handle the callback URL for both development and production environments.
Add Google OAuth authentication to my Express app. Install passport and passport-google-oauth20. Configure the Google strategy with credentials from Replit Secrets. Add login, callback, logout, and auth status routes. Protect the /api/profile route so only authenticated users can access it. Use sessions with secure cookies in production.
Frequently asked questions
Replit Auth is a zero-configuration login system that uses Replit accounts. Users log in with their Replit credentials and your app receives their profile through request headers. OAuth works with external providers like Google or GitHub and serves a broader audience beyond Replit users.
Default in-memory sessions are lost when an Autoscale instance scales to zero. Use a session store backed by your PostgreSQL database or an external Redis service to persist sessions across restarts.
The callback URL in your code does not match the one registered with your OAuth provider. Update the allowed redirect URIs in Google Cloud Console or GitHub Developer Settings to include your current Replit domain.
Yes. Install and configure a separate Passport strategy for each provider (Google, GitHub, Discord, etc.). Each provider gets its own login and callback routes. Store all credentials in Replit Secrets.
Replit Auth is secure for apps where all users have Replit accounts. It uses Replit's own authentication infrastructure with encrypted headers. For apps serving the general public, use OAuth with established providers.
After successful OAuth, save the user profile to your PostgreSQL database. Use the provider's unique user ID as the identifier. On subsequent logins, look up the existing user instead of creating a duplicate.
In-memory sessions are lost on every deployment. Use a database-backed session store to persist sessions across deployments. The connect-pg-simple package works well with Replit's built-in PostgreSQL.
Yes. Register your Replit development URL as an allowed callback URL in your OAuth provider settings. The dev URL is available via the REPLIT_DEV_DOMAIN environment variable. OAuth works in the preview pane during development.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation