Skip to main content

React Webviews

The @augmentos/react library simplifies building React-based webviews that integrate seamlessly with AugmentOS authentication. When users open your webview through the AugmentOS manager app, they are automatically authenticated without requiring a separate login process.

What Are React Webviews?

React webviews are web applications built with React that run inside the AugmentOS manager app. They provide rich user interfaces for:

  • Settings and Configuration: Let users customize your app's behavior
  • Data Visualization: Display charts, graphs, and analytics
  • Content Management: Create, edit, and organize user content
  • Dashboard Interfaces: Show personalized information and controls

The @augmentos/react library handles all the authentication complexity, automatically extracting and verifying user tokens from the AugmentOS system.

Installation

Install the React authentication library in your webview project:

npm install @augmentos/react
# or
yarn add @augmentos/react
# or
bun add @augmentos/react

Prerequisites

  • React 16.8+: The library uses React Hooks
  • AugmentOS TPA Server: Your backend must be deployed, either on the same domain as your frontend or on a different domain that allows CORS requests from your frontend.
  • Developer Console Setup: Set the webview url in the developer console to your frontend server

Basic Setup

1. Wrap Your App with the Authentication Provider

The AugmentosAuthProvider component manages authentication state for your entire React application:

// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { AugmentosAuthProvider } from '@augmentos/react';

/**
* Application entry point that provides AugmentOS authentication context
* to the entire React component tree
*/
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<AugmentosAuthProvider>
<App />
</AugmentosAuthProvider>
</React.StrictMode>
);

2. Access Authentication State

Use the useAugmentosAuth hook to access user information and authentication status:

// src/components/UserProfile.tsx
import React from 'react';
import { useAugmentosAuth } from '@augmentos/react';

/**
* Component that displays user authentication status and profile information.
* Demonstrates basic usage of the AugmentOS authentication hook.
*
* @returns {React.JSX.Element} User profile component with authentication state
*/
const UserProfile: React.FC = (): React.JSX.Element => {
const { userId, isLoading, error, isAuthenticated, logout } = useAugmentosAuth();

// Handle loading state during authentication
if (isLoading) {
return <div>Loading authentication...</div>;
}

// Handle authentication errors
if (error) {
return (
<div style={{ color: 'red' }}>
<p>Authentication Error: {error}</p>
<p>Please ensure you are opening this page from the AugmentOS app.</p>
</div>
);
}

// Handle unauthenticated state
if (!isAuthenticated || !userId) {
return <div>Not authenticated. Please open from the AugmentOS manager app.</div>;
}

// Display authenticated user information
return (
<div>
<h2>Welcome, AugmentOS User!</h2>
<p>User ID: <strong>{userId}</strong></p>
<button onClick={logout}>Logout</button>
</div>
);
};

export default UserProfile;

Complete Example Application

Here's a comprehensive example that demonstrates authentication, API calls, and error handling:

/**
* Main application component that demonstrates AugmentOS authentication integration.
* This file serves as the root component for testing the augmentos-react package functionality.
*/

import React, { useState } from 'react';
import { AugmentosAuthProvider, useAugmentosAuth } from '@augmentos/react';

/**
* Type definition for the API response from the notes endpoint
*/
interface NotesApiResponse {
[key: string]: any; // Allow flexible response structure
}

/**
* Content component that displays authentication status and user information.
* This component consumes the AugmentosAuth context to show loading states,
* errors, and authenticated user data. It also provides functionality to make
* authenticated API calls to the TPA backend.
*
* @returns {React.JSX.Element} The rendered content based on authentication state
*/
function Content(): React.JSX.Element {
const { userId, isLoading, error, isAuthenticated, frontendToken } = useAugmentosAuth();

// State for managing API call results and loading state
const [apiResult, setApiResult] = useState<NotesApiResponse | null>(null);
const [apiError, setApiError] = useState<string | null>(null);
const [isLoadingApi, setIsLoadingApi] = useState<boolean>(false);

/**
* Makes an authenticated API call to the TPA backend notes endpoint
* Uses the frontendToken for authorization
*/
const fetchNotesFromBackend = async (): Promise<void> => {
if (!frontendToken) {
setApiError("No frontend token available for backend call.");
return;
}

setIsLoadingApi(true);
setApiError(null);
setApiResult(null);

try {
const response = await fetch(`/api/notes`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${frontendToken}`,
},
});

if (!response.ok) {
throw new Error(`Backend request failed: ${response.status} ${response.statusText}`);
}

const data: NotesApiResponse = await response.json();
setApiResult(data);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
setApiError(errorMessage);
} finally {
setIsLoadingApi(false);
}
};

// Handle loading state
if (isLoading) {
return <p>Loading authentication...</p>;
}

// Handle error state
if (error) {
return <p style={{ color: 'red' }}>Error: {error}</p>;
}

// Handle unauthenticated state
if (!isAuthenticated) {
return <p>Not authenticated. Please log in.</p>;
}

// Handle authenticated state
return (
<div>
<p style={{ color: 'green' }}>✓ Successfully authenticated!</p>
<p>User ID: <strong>{userId}</strong></p>

{/* API Testing Section */}
<div style={{ marginTop: '30px', padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
<h2>Backend API Test</h2>
<p>Test authenticated calls to your TPA backend:</p>

<button
onClick={fetchNotesFromBackend}
disabled={isLoadingApi}
style={{
padding: '10px 20px',
backgroundColor: isLoadingApi ? '#ccc' : '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: isLoadingApi ? 'not-allowed' : 'pointer',
fontSize: '16px'
}}
>
{isLoadingApi ? 'Loading...' : 'Fetch Notes from Backend'}
</button>

{/* Display API loading state */}
{isLoadingApi && (
<p style={{ color: '#666', marginTop: '10px' }}>Making authenticated request...</p>
)}

{/* Display API errors */}
{apiError && (
<div style={{ marginTop: '15px', padding: '10px', backgroundColor: '#ffe6e6', border: '1px solid #ff9999', borderRadius: '4px' }}>
<strong style={{ color: '#cc0000' }}>API Error:</strong>
<p style={{ color: '#cc0000', margin: '5px 0 0 0' }}>{apiError}</p>
</div>
)}

{/* Display API results */}
{apiResult && (
<div style={{ marginTop: '15px', padding: '10px', backgroundColor: '#e6ffe6', border: '1px solid #99ff99', borderRadius: '4px' }}>
<strong style={{ color: '#006600' }}>API Response:</strong>
<pre style={{
backgroundColor: '#f5f5f5',
padding: '10px',
borderRadius: '4px',
overflow: 'auto',
maxHeight: '300px',
fontSize: '14px',
fontFamily: 'Monaco, Consolas, "Courier New", monospace'
}}>
{JSON.stringify(apiResult, null, 2)}
</pre>
</div>
)}
</div>
</div>
);
}

/**
* Root App component that provides authentication context to the entire application.
* This component wraps the Content component with the AugmentosAuthProvider
* to enable authentication functionality throughout the app.
*
* @returns {React.JSX.Element} The main application component
*/
function App(): React.JSX.Element {
return (
<AugmentosAuthProvider>
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<h1>AugmentOS React Test App</h1>
<Content />
</div>
</AugmentosAuthProvider>
);
}

export default App;

Making Authenticated API Calls

The frontendToken from useAugmentosAuth is a JWT token that you should include in the Authorization header when making requests to your TPA backend:

/**
* Hook for making authenticated API calls to the TPA backend
*
* @returns {Object} Functions for making authenticated requests
*/
const useAuthenticatedApi = () => {
const { frontendToken } = useAugmentosAuth();

/**
* Makes an authenticated GET request to the specified endpoint
*
* @param {string} endpoint - The API endpoint to call
* @returns {Promise<any>} The response data
* @throws {Error} If the request fails or token is missing
*/
const authenticatedGet = async (endpoint: string): Promise<any> => {
if (!frontendToken) {
throw new Error("No authentication token available");
}

const response = await fetch(endpoint, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${frontendToken}`,
},
});

if (!response.ok) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
}

return response.json();
};

/**
* Makes an authenticated POST request with JSON data
*
* @param {string} endpoint - The API endpoint to call
* @param {any} data - The data to send in the request body
* @returns {Promise<any>} The response data
* @throws {Error} If the request fails or token is missing
*/
const authenticatedPost = async (endpoint: string, data: any): Promise<any> => {
if (!frontendToken) {
throw new Error("No authentication token available");
}

const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${frontendToken}`,
},
body: JSON.stringify(data),
});

if (!response.ok) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
}

return response.json();
};

return { authenticatedGet, authenticatedPost };
};

Authentication Hook API

The useAugmentosAuth hook returns an object with the following properties:

interface AugmentosAuthContextType {
/** Unique identifier for the authenticated user */
userId: string | null;

/** JWT token for making authenticated requests to your TPA backend */
frontendToken: string | null;

/** True while authentication is being processed */
isLoading: boolean;

/** Error message if authentication fails */
error: string | null;

/** True if the user is successfully authenticated */
isAuthenticated: boolean;

/** Function to clear authentication state and log out */
logout: () => void;
}

How Authentication Works

  1. Token Extraction: When the AugmentOS manager opens your webview, it appends an aos_signed_user_token parameter to the URL
  2. Token Verification: The library verifies the token's signature against the AugmentOS Cloud public key
  3. User Identification: If valid, it extracts the userId and frontendToken from the token payload
  4. Persistence: The authentication data is stored in localStorage for the session
  5. Context Sharing: All authentication state is made available through React Context

CORS Configuration

If your webview frontend is hosted separately from your TPA backend, configure CORS properly:

// In your TPA server setup
import cors from 'cors';

/**
* Configure CORS to allow requests from your webview domain
*/
app.use(cors({
origin: [
'https://your-webview-domain.com',
'http://localhost:3000' // For development
],
credentials: true, // If you use cookies
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));

Troubleshooting

Common Issues

"AugmentOS signed user token not found"

  • Ensure your webview is opened through the AugmentOS manager app
  • Check that the manager app is correctly appending the token to your URL
  • Verify your webview URL is configured correctly in the Developer Console

"Token validation failed"

  • Verify the device clock is synchronized (token expiration is time-sensitive)
  • Check that you're using the latest version of @augmentos/react
  • Ensure the AugmentOS manager app is up to date

Backend authentication fails

  • Check that you're sending the Authorization: Bearer ${frontendToken} header
  • Verify your TPA server is configured to accept the frontend token
  • Ensure CORS is configured properly if frontend and backend are on different domains

Changes not reflecting

  • Clear browser cache and localStorage
  • Restart the AugmentOS manager app
  • Check browser developer tools for JavaScript errors

Debugging Tips

Enable debug logging to troubleshoot authentication issues:

// Add this to see authentication state changes
const Content = () => {
const auth = useAugmentosAuth();

// Log authentication state for debugging
React.useEffect(() => {
console.log('Auth state:', {
userId: auth.userId,
isAuthenticated: auth.isAuthenticated,
isLoading: auth.isLoading,
error: auth.error,
hasToken: !!auth.frontendToken
});
}, [auth]);

// ... rest of component
};

Next Steps