> For the complete documentation index, see [llms.txt](https://sdkdocs.deva.me/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sdkdocs.deva.me/examples/authenticated-app.md).

# Authenticated App

Build a complete application with OAuth authentication, protected routes, and session management using the Deva SDK.

***

## What You'll Build

A React application demonstrating:

* **OAuth 2.0 + OIDC** authentication with PKCE security
* **Protected routes** requiring authentication
* **Public routes** accessible to everyone
* **Session persistence** across page refreshes
* **Auto-redirect** to intended destination after login
* **User profile** display and management
* **Logout handling** with token revocation

***

## Example Structure

```
example-authenticated-app/
├── src/
│   ├── components/
│   │   └── ProtectedRoute.tsx    # Route protection wrapper
│   ├── pages/
│   │   ├── LandingPage.tsx       # Public home page
│   │   ├── AboutPage.tsx         # Public about page
│   │   ├── LoginPage.tsx         # Login page
│   │   ├── DashboardPage.tsx     # Protected dashboard
│   │   ├── ProfilePage.tsx       # Protected profile
│   │   └── SettingsPage.tsx      # Protected settings
│   ├── App.tsx                   # Main app with routing
│   └── main.tsx                  # App entry point
└── package.json
```

***

## Routes

**Public Routes** (accessible without login):

* `/` - Landing page
* `/about` - About page
* `/login` - Login page

**Protected Routes** (require authentication):

* `/dashboard` - Personal dashboard
* `/profile` - User profile
* `/settings` - Account settings

***

## Code for Each Page

### App.tsx - Main Application

```tsx
import "@bitplanet/deva-sdk/style.css";
import { DevaProvider } from "@bitplanet/deva-sdk";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { ProtectedRoute } from "./components/ProtectedRoute";
import { LandingPage } from "./pages/LandingPage";
import { AboutPage } from "./pages/AboutPage";
import { LoginPage } from "./pages/LoginPage";
import { DashboardPage } from "./pages/DashboardPage";
import { ProfilePage } from "./pages/ProfilePage";
import { SettingsPage } from "./pages/SettingsPage";

export default function App() {
  return (
    <DevaProvider
      clientId={import.meta.env.VITE_DEVA_CLIENT_ID}
      redirectUri={window.location.origin}
      env={import.meta.env.VITE_DEVA_ENV}
    >
      <BrowserRouter>
        <Routes>
          {/* Public routes */}
          <Route path="/" element={<LandingPage />} />
          <Route path="/about" element={<AboutPage />} />
          <Route path="/login" element={<LoginPage />} />

          {/* Protected routes */}
          <Route
            path="/dashboard"
            element={
              <ProtectedRoute>
                <DashboardPage />
              </ProtectedRoute>
            }
          />
          <Route
            path="/profile"
            element={
              <ProtectedRoute>
                <ProfilePage />
              </ProtectedRoute>
            }
          />
          <Route
            path="/settings"
            element={
              <ProtectedRoute>
                <SettingsPage />
              </ProtectedRoute>
            }
          />

          <Route path="*" element={<Navigate to="/" replace />} />
        </Routes>
      </BrowserRouter>
    </DevaProvider>
  );
}
```

***

### ProtectedRoute.tsx - Route Protection

```tsx
import { ReactNode } from "react";
import { useDeva } from "deva-sdk";
import { Navigate, useLocation } from "react-router-dom";

export function ProtectedRoute({ children }: { children: ReactNode }) {
  const { isReady, isAuthenticated } = useDeva();
  const location = useLocation();

  if (!isReady) {
    return (
      <div className="flex items-center justify-center h-screen">
        <div className="text-center">
          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto mb-4" />
          <p className="text-gray-600">Loading...</p>
        </div>
      </div>
    );
  }

  if (!isAuthenticated) {
    sessionStorage.setItem("redirectAfterLogin", location.pathname);
    return <Navigate to="/login" replace />;
  }

  return <>{children}</>;
}
```

***

### LandingPage.tsx - Home Page

```tsx
import { useDeva } from "@bitplanet/deva-sdk";

export function LandingPage() {
  const { isAuthenticated, user } = useDeva();

  return (
    <div className="min-h-screen bg-gradient-to-b from-blue-50 to-white">
      <nav className="bg-white shadow">
        <div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
          <h1 className="text-xl font-bold">My App</h1>
          <div className="flex items-center gap-4">
            <a href="/about" className="text-gray-600 hover:text-gray-900">
              About
            </a>
            {isAuthenticated ? (
              <a
                href="/dashboard"
                className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
              >
                Dashboard
              </a>
            ) : (
              <a
                href="/login"
                className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
              >
                Sign In
              </a>
            )}
          </div>
        </div>
      </nav>

      <main className="max-w-7xl mx-auto px-4 py-16 text-center">
        <h2 className="text-5xl font-bold mb-6">Welcome to My App</h2>
        <p className="text-xl text-gray-600 mb-8">
          Build amazing applications with Deva SDK
        </p>
        {isAuthenticated && user && (
          <p className="text-gray-700">
            Welcome back, <strong>{user.persona?.display_name || user.name}</strong>!
          </p>
        )}
      </main>
    </div>
  );
}

```

***

### AboutPage.tsx - About Page

```tsx
export function AboutPage() {
  return (
    <div className="min-h-screen bg-gray-50">
      <nav className="bg-white shadow">
        <div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
          <h1 className="text-xl font-bold">About</h1>
          <a href="/" className="text-gray-600 hover:text-gray-900">
            Home
          </a>
        </div>
      </nav>

      <main className="max-w-3xl mx-auto px-4 py-8">
        <div className="bg-white rounded-lg shadow p-6">
          <h2 className="text-2xl font-bold mb-4">About My App</h2>
          <p className="text-gray-700 mb-4">
            This is a demonstration of authentication patterns using the @10planet/deva-sdk.
          </p>
          <p className="text-gray-700">
            Features include:
          </p>
          <ul className="list-disc list-inside text-gray-700 mt-2 space-y-1">
            <li>OAuth 2.0 + OIDC authentication with PKCE</li>
            <li>Protected and public routes</li>
            <li>Session persistence across page refreshes</li>
            <li>Auto-redirect after login</li>
            <li>User profile display</li>
            <li>Logout handling</li>
          </ul>
        </div>
      </main>
    </div>
  );
}

```

***

### LoginPage.tsx - Login Page

```tsx
import { useDeva } from "@bitplanet/deva-sdk";
import { Navigate } from "react-router-dom";

export function LoginPage() {
  const { isReady, isAuthenticated, login } = useDeva();

  if (isAuthenticated) {
    const redirectTo = sessionStorage.getItem("redirectAfterLogin") || "/dashboard";
    sessionStorage.removeItem("redirectAfterLogin");
    return <Navigate to={redirectTo} replace />;
  }

  const handleLogin = async () => {
    await login();
  };

  return (
    <div className="min-h-screen bg-gray-50 flex items-center justify-center">
      <div className="max-w-md w-full bg-white rounded-lg shadow-lg p-8">
        <div className="text-center mb-8">
          <h1 className="text-3xl font-bold mb-2">Sign In</h1>
          <p className="text-gray-600">
            Sign in with your Deva account to continue
          </p>
        </div>

        <button
          onClick={handleLogin}
          disabled={!isReady}
          className="w-full bg-blue-600 text-white py-3 rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition"
        >
          {isReady ? "Sign in with Deva" : "Loading..."}
        </button>

        <p className="text-center text-sm text-gray-600 mt-6">
          Don't have an account?{" "}
          <a
            href="https://deva.me"
            target="_blank"
            rel="noopener noreferrer"
            className="text-blue-600 hover:underline"
          >
            Sign up on Deva
          </a>
        </p>
      </div>
    </div>
  );
}

```

***

### DashboardPage.tsx - Protected Dashboard

```tsx
import { useDeva } from "@bitplanet/deva-sdk";

export function DashboardPage() {
  const { user, logout } = useDeva();

  return (
    <div className="min-h-screen bg-gray-50">
      <nav className="bg-white shadow">
        <div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
          <h1 className="text-xl font-bold">Dashboard</h1>
          <div className="flex items-center gap-4">
            <a href="/profile" className="text-gray-600 hover:text-gray-900">
              Profile
            </a>
            <a href="/settings" className="text-gray-600 hover:text-gray-900">
              Settings
            </a>
            <button
              onClick={logout}
              className="text-gray-600 hover:text-gray-900"
            >
              Logout
            </button>
          </div>
        </div>
      </nav>

      <main className="max-w-7xl mx-auto px-4 py-8">
        <div className="bg-white rounded-lg shadow p-6 mb-6">
          <h2 className="text-2xl font-bold mb-4">
            Welcome, {user?.persona?.display_name || user?.name}!
          </h2>
          <p className="text-gray-600">
            This is your personal dashboard.
          </p>
        </div>

        <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
          <div className="bg-white rounded-lg shadow p-6">
            <h3 className="font-semibold mb-2">Total Posts</h3>
            <p className="text-3xl font-bold text-blue-600">24</p>
          </div>
          <div className="bg-white rounded-lg shadow p-6">
            <h3 className="font-semibold mb-2">Followers</h3>
            <p className="text-3xl font-bold text-green-600">1,234</p>
          </div>
          <div className="bg-white rounded-lg shadow p-6">
            <h3 className="font-semibold mb-2">Following</h3>
            <p className="text-3xl font-bold text-purple-600">567</p>
          </div>
        </div>
      </main>
    </div>
  );
}
```

***

### ProfilePage.tsx - User Profile

```tsx
import { useDeva } from "@bitplanet/deva-sdk";

export function ProfilePage() {
  const { user, logout } = useDeva();

  return (
    <div className="min-h-screen bg-gray-50">
      <nav className="bg-white shadow">
        <div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
          <h1 className="text-xl font-bold">Profile</h1>
          <div className="flex items-center gap-4">
            <a href="/dashboard" className="text-gray-600 hover:text-gray-900">
              Dashboard
            </a>
            <button
              onClick={logout}
              className="text-gray-600 hover:text-gray-900"
            >
              Logout
            </button>
          </div>
        </div>
      </nav>

      <main className="max-w-3xl mx-auto px-4 py-8">
        <div className="bg-white rounded-lg shadow p-6">
          <div className="flex items-center gap-4 mb-6">
            <img
              src={user?.persona?.avatar || user?.picture || ''}
              alt={user?.persona?.username || user?.preferred_username || 'User avatar'}
              className="w-20 h-20 rounded-full"
            />
            <div>
              <h2 className="text-2xl font-bold">
                {user?.persona?.display_name || user?.name}
              </h2>
              <p className="text-gray-600">@{user?.persona?.username || user?.preferred_username}</p>
            </div>
          </div>

          <div className="space-y-4">
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-1">
                Email
              </label>
              <p className="text-gray-900">
                {user?.email || "Not provided"}
                {user?.email_verified && (
                  <span className="ml-2 text-green-600">✓ Verified</span>
                )}
              </p>
            </div>

            <div>
              <label className="block text-sm font-medium text-gray-700 mb-1">
                User ID
              </label>
              <p className="text-gray-900 font-mono text-sm">{user?.id}</p>
            </div>

            <div>
              <label className="block text-sm font-medium text-gray-700 mb-1">
                Description
              </label>
              <p className="text-gray-900">
                {user?.persona?.description || user?.description || "No description provided"}
              </p>
            </div>
          </div>
        </div>
      </main>
    </div>
  );
}
```

***

### SettingsPage.tsx - Account Settings

```tsx
import { useDeva } from "@bitplanet/deva-sdk";

export function SettingsPage() {
  const { logout } = useDeva();

  return (
    <div className="min-h-screen bg-gray-50">
      <nav className="bg-white shadow">
        <div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
          <h1 className="text-xl font-bold">Settings</h1>
          <div className="flex items-center gap-4">
            <a href="/dashboard" className="text-gray-600 hover:text-gray-900">
              Dashboard
            </a>
            <button
              onClick={logout}
              className="text-gray-600 hover:text-gray-900"
            >
              Logout
            </button>
          </div>
        </div>
      </nav>

      <main className="max-w-3xl mx-auto px-4 py-8">
        <div className="bg-white rounded-lg shadow p-6">
          <h2 className="text-2xl font-bold mb-6">Account Settings</h2>

          <div className="space-y-6">
            <div>
              <h3 className="font-semibold mb-2">Notifications</h3>
              <label className="flex items-center">
                <input type="checkbox" className="mr-2" />
                <span>Email notifications</span>
              </label>
            </div>

            <div>
              <h3 className="font-semibold mb-2">Privacy</h3>
              <label className="flex items-center">
                <input type="checkbox" className="mr-2" />
                <span>Private profile</span>
              </label>
            </div>

            <div className="pt-6 border-t">
              <button
                onClick={logout}
                className="bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700"
              >
                Sign Out
              </button>
            </div>
          </div>
        </div>
      </main>
    </div>
  );
}
```

***

## Setup & Installation

### Prerequisites

* Node.js 18+ installed
* pnpm package manager
* A Deva account at [deva.me](https://deva.me)

### Get Deva Credentials

1. Visit [deva.me](https://deva.me) and sign in
2. Go to **Settings → Apps**
3. Click **Create New Application**
4. Fill in:
   * **Name**: My Authenticated App
   * **Redirect URIs**: `http://localhost:5174`
   * **Origin URIs**: `http://localhost:5174`
5. Copy your **Client ID**

### Install and Run

```bash
# Clone or navigate to example
cd example-authenticated-app

# Install dependencies
pnpm install

# Create .env file
cp .env.example .env

# Edit .env and add your Client ID
VITE_DEVA_CLIENT_ID=your_client_id_here

# Start dev server
pnpm run dev
```

Open `http://localhost:5174` in your browser.

### Test the App

1. Click **Sign In** button
2. Authenticate with your Deva account
3. Explore:
   * **Dashboard** - View your stats
   * **Profile** - See your user information
   * **Settings** - Manage preferences
4. Click **Logout** to end session

***

## Notes

* OAuth flow uses PKCE for security
* Tokens stored in sessionStorage (cleared on browser close)
* Authentication state persists across page refreshes within same session
* `isReady` prevents flash of incorrect auth state during initialization
* Logout revokes tokens server-side and clears local storage
* Redirect URI must match exactly what's configured in Deva app settings

***

## Related Documentation

* [OAuth Integration](/authentication/oauth-integration.md) - OAuth 2.0 + OIDC details
* [useDeva Hook](/hooks-api/use-deva.md) - Authentication hook reference
* [Session Persistence](/authentication/session-persistence.md) - Token storage
* [Login Implementation](/authentication/login-implementation.md) - Login patterns
* [Logout Handling](/authentication/logout-handling.md) - Logout patterns
