Authentication

The easiest way to add Google OAuth authentication to your Plash apps
Note

TODO: More explanation on the T&C’s between plash<->devs<->users<->plash

Why Use Plash Auth?

Setting up Google OAuth authentication traditionally requires: - Google Cloud Console project setup and OAuth consent screen configuration - Secure credential management and rotation in production - Managing redirect URLs across development, staging, and production environments - Complex local testing workarounds (OAuth typically breaks without HTTPS and registered domains)

Plash Auth eliminates this complexity by providing a simple wrapper around the OAuth flow. We handle all the Google Cloud setup, credential management, and redirect configuration for you.

How It Works

Plash provides a streamlined OAuth flow:

  1. Your app requests a sign-in URL from Plash
  2. User authenticates with Google through our secure flow
  3. Plash redirects back to your app with a signed JWT containing the user’s Google ID
  4. Your app verifies the JWT for authentication

The system automatically adapts to your environment—real OAuth in production, mock auth for local development and testing.

Tip

Trade-offs: Plash Auth provides the user’s globally unique Google ID for authentication. If you need access to user data (name, email) or Google services (Drive, Gmail), you’ll need to implement your own OAuth flow (FastHTML OAuth documentation).

For most applications that just need secure user authentication, Plash Auth is the simplest solution.

Tutorial Setup

This tutorial will show you how to add Google OAuth authentication to your FastHTML apps deployed on Plash. With Plash’s built-in auth system, you can easily implement secure sign-in functionality without managing OAuth secrets or redirect URLs yourself.

Prerequisites:

In this tutorial we’ll focus on FastHTML. But any Plash app can technically make use of Plash Auth.

1. Create Your Auth App

Let’s create a new directory for our auth example:

mkdir -p plash-auth-tutorial
cd plash-auth-tutorial

2. Create the Main Application

Create your main.py file with the auth functionality:

import os

# Check environment variable
plash_production = os.getenv('PLASH_PRODUCTION')

if plash_production: 
    from plash_cli.auth import make_plash_signin_url, goog_id_from_signin_reply, APP_SIGNIN_PATH
else: 
    from plash_cli.auth_mock import make_plash_signin_url, goog_id_from_signin_reply, APP_SIGNIN_PATH

from fasthtml.common import *

app, rt = fast_app()

@rt
def index(session): 
    user_id = session.get('user_id')
    if user_id:
        return Div(
            H1("Welcome to My Plash App!"),
            P(f"You are logged in as user: {user_id}"),
            A("Protected Page", href="/protected"),
            Br(), Br(),
            A("Logout", href="/logout")
        )
    else:
        return Div(
            H1("Welcome to My Plash App!"),
            P("Please sign in to access the app."),
            A("Sign in with Google", href=make_plash_signin_url(session))
        )

@rt('/protected')
def protected_page(session):
    user_id = session.get('user_id')
    if not user_id:
        return RedirectResponse('/', status_code=303)
    
    return Div(
        H1("Protected Page"),
        P(f"This page is only accessible to authenticated users."),
        P(f"Your user ID: {user_id}"),
        A("Back to Home", href="/")
    )

@rt('/logout')
def logout(session):
    session.pop('user_id', None)
    return RedirectResponse('/', status_code=303)

@rt(APP_SIGNIN_PATH)
def plash_signin_completed(session, signin_reply: str):
    uid = goog_id_from_signin_reply(session, signin_reply)
    if uid is None:
        return Div(
            H2("Login Failed"),
            P("There was an error signing you in. Please try again."),
            A("Try Again", href="/")
        )
    else:
        session['user_id'] = uid
        return RedirectResponse('/', status_code=303)

serve()

3. Add Required Dependencies

Create your requirements.txt file with the necessary packages:

python-fasthtml
fastcore
httpx>=0.28.1
git+https://github.com/AnswerDotAI/plash_cli.git@feat-add-oauth
Note

We’re using the feat-add-oauth branch of plash_cli which contains the latest auth functionality. Once this feature is merged, you can use the main branch.

4. Understanding the Code

Let’s break down the key components of our auth app:

Environment Detection

plash_production = os.getenv('PLASH_PRODUCTION')

Plash automatically sets the PLASH_PRODUCTION environment variable when your app runs in production. This allows your app to use real OAuth in production and mock auth for local testing.

Key Functions

  • make_plash_signin_url(session): Generates a sign-in URL that starts the OAuth flow
  • goog_id_from_signin_reply(session, signin_reply): Verifies the OAuth response and returns the user’s Google ID
  • APP_SIGNIN_PATH: The path (/signin_completed) where Plash redirects after authentication

Session Management

The app stores the user ID in the session after successful authentication:

session['user_id'] = uid

Protected Routes

To let only logged-in users access a given route, you can check if the user_id is set in the session.

@rt('/protected')
def protected_page(session):
    # Only accessible to logged-in users
    if not session.get('user_id'): return RedirectResponse('/', status_code=303)

5. Deploy Your Auth App

Now let’s deploy the app to see authentication in action:

plash_deploy
Initializing deployment...
✅ Upload complete! Your app is currently being built.
It will be live at https://auth-tutorial-example.pla.sh

6. Test Your Auth App

Visit your deployed app and test the authentication flow:

plash_view
Opening browser to view app :
https://auth-tutorial-example.pla.sh

You should see:

  1. A welcome page with a “Sign in with Google” link
  2. When clicked, you’ll be redirected to Google’s OAuth consent screen
  3. After granting permission, you’ll be redirected back to your app
  4. Now logged in, you can access the protected page and logout functionality

Troubleshooting

Common Issues

“Invalid request” error during OAuth - Make sure your app is deployed and accessible at the URL Plash expects - Check that your requirements.txt includes the correct plash_cli version

Mock auth not working locally - Ensure PLASH_PRODUCTION environment variable is not set when testing locally - Verify you’re importing from plash_cli.auth_mock in development mode

Session not persisting - Make sure you’re using FastHTML’s session management correctly - Check that cookies are enabled in your browser