# 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sdkdocs.deva.me/examples/authenticated-app.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
