### Install Dependencies and Run Project Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Commands for installing project dependencies, starting the development server, building for production, and previewing the production build. ```shell # Install dependencies npm install # Start development server npm run dev # Build for production npm run build # Preview production build npm run preview ``` -------------------------------- ### Install and Run React Project Source: https://github.com/bitwarden/passwordless-react-example/blob/main/README.md Install project dependencies using npm and start the development server. This command assumes you have Node.js and npm installed. ```shell npm install npm run dev ``` -------------------------------- ### JWT Token Utilities Usage Example Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Example demonstrating how to retrieve a JWT token from local storage, check if it's expired, and log user details if valid. ```typescript // Usage example: const token = localStorage.getItem('token'); if (token && !isTokenExpired(token)) { const decoded = decodeJwt(token); console.log('User ID:', decoded.nameid); console.log('Username:', decoded.unique_name); console.log('Expires:', new Date(decoded.exp! * 1000)); } ``` -------------------------------- ### Protected Route Guard Usage (RequireAuth) Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Example of how to integrate the RequireAuth route guard into your React Router configuration to protect specific routes. ```tsx // Usage in App.tsx: }> } /> } /> ``` -------------------------------- ### Guest-Only Route Guard Usage (RequireGuest) Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Example of how to integrate the RequireGuest route guard into your React Router configuration to protect public routes. ```tsx // Usage in App.tsx: }> } /> } /> } /> ``` -------------------------------- ### Configure Application Routes Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Sets up the main application routes, including public, guest-only, and protected routes using React Router. It checks authentication status on app load. ```tsx // src/App.tsx import { useEffect } from 'react' import Layout from "./components/Layout.tsx"; import { Route, Routes } from "react-router-dom"; import LoginPage from "./pages/LoginPage.tsx"; import RequireAuth from "./auth/RequireAuth.tsx"; import PublicPage from "./pages/PublicPage.tsx"; import RegisterPage from "./pages/RegisterPage.tsx"; import UnauthorizedPage from "./pages/UnauthorizedPage.tsx"; import ProfilePage from "./pages/ProfilePage.tsx"; import useAuth from "./hooks/UseAuth.ts"; import RequireGuest from "./auth/RequireGuest.tsx"; const App = () => { const { checkAuthStatus } = useAuth(); useEffect(() => { // Check if the user is authenticated when the app loads checkAuthStatus(); }, []); return ( {/* Guest-only routes (redirect to profile if logged in) */} }> } /> } /> } /> {/* Public route */} } /> {/* Protected routes (require authentication) */} }> } /> ); } export default App; ``` -------------------------------- ### Application Entry Point with AuthProvider and BrowserRouter Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt The main entry point of the React application. It wraps the root component with AuthProvider for authentication state management and BrowserRouter for routing. Ensure these providers are correctly set up for global access. ```tsx // src/main.tsx import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import App from './App.tsx' import './index.css' import { BrowserRouter } from "react-router-dom"; import { AuthProvider } from "./auth/AuthProvider.tsx"; createRoot(document.getElementById('root')!).render( , ) ``` -------------------------------- ### Implement Backend API Client in TypeScript Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt This client facilitates user registration, login, and credential retrieval by interacting with your backend server. Ensure the VITE_BACKEND_URL environment variable is correctly configured. ```typescript // src/services/your-backend/YourBackendClient.ts import UserLoginRequest from "./contracts/UserLoginRequest.ts"; import UserRegisterRequest from "./contracts/UserRegisterRequest.ts"; import VerifiedUserResponse from "./contracts/VerifiedUserResponse.ts"; import RegisterTokenResponse from "./contracts/RegisterTokenResponse.ts"; import CredentialResponse from "./contracts/CredentialResponse.ts"; export default class YourBackendClient { private readonly backendUrl: string; constructor() { this.backendUrl = import.meta.env.VITE_BACKEND_URL!; } async login(request: UserLoginRequest): Promise { const response = await fetch(`${this.backendUrl}/auth/login`, { method: 'post', body: JSON.stringify(request), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }); if (!response.ok) { const problemDetails = await response.json(); if (problemDetails && problemDetails.detail) { throw new Error(problemDetails.detail); } else { throw new Error(`An unknown error prevented us from logging in.`); } } return await response.json(); } async register(request: UserRegisterRequest): Promise { const response = await fetch(`${this.backendUrl}/auth/register`, { method: 'post', body: JSON.stringify(request), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }); if (!response.ok) { const problemDetails = await response.json(); if (problemDetails && problemDetails.detail) { throw new Error(problemDetails.detail); } else { throw new Error(`An unknown error prevented us from obtaining a registration token.`); } } return await response.json(); } async getCredentials(userId: string): Promise { const token: string = localStorage.getItem('token')!; const response = await fetch(`${this.backendUrl}/users/${userId}/credentials`, { method: 'get', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }); if (!response.ok) { const problemDetails = await response.json(); if (problemDetails && problemDetails.detail) { throw new Error(problemDetails.detail); } else { throw new Error(`An unknown error prevented us from fetching credentials.`); } } const data = await response.json(); return data.map((item: any) => ({ ...item, createdAt: item.createdAt ? new Date(item.createdAt + 'Z') : undefined, })); } } ``` -------------------------------- ### Environment Configuration for Passwordless React Source: https://github.com/bitwarden/passwordless-react-example/blob/main/README.md Configure your backend URL, API key, and Passwordless API URL in the .env file. Ensure these match your Passwordless.dev account settings. ```shell VITE_BACKEND_URL=https://demo.passwordless.dev VITE_PASSWORDLESS_API_KEY=pwdemo:public:5aec1f24f65343239bf4e1c9a852e871 VITE_PASSWORDLESS_API_URL=https://v4.passwordless.dev ``` -------------------------------- ### Register User with Passkey using Passwordless.dev Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Handles the complete user registration flow, including requesting a token from your backend and then using it to register a new passkey with Passwordless.dev. Ensure your backend client and UserRegisterRequest contract are correctly implemented. ```tsx // src/pages/RegisterPage.tsx import { useEffect, useRef, useState } from "react"; import * as Passwordless from "@passwordlessdev/passwordless-client"; import { ToastContainer, toast } from 'react-toastify'; import YourBackendClient from "../services/your-backend/YourBackendClient"; import UserRegisterRequest from "../services/your-backend/contracts/UserRegisterRequest.ts"; export default function RegisterPage() { const [username, setUsername] = useState(""); const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [alias, setAlias] = useState(""); const handleSubmit = async () => { let registerToken = null; try { // Step 1: Request a registration token from your backend const yourBackendClient = new YourBackendClient(); const registerRequest: UserRegisterRequest = { firstName: firstName, lastName: lastName, username: username, alias: alias }; registerToken = await yourBackendClient.register(registerRequest); } catch (error: unknown) { if (error instanceof Error) { toast(error.message, { className: 'toast-error' }); } } // Step 2: Use the token to register a passkey with Passwordless.dev if (registerToken) { const p = new Passwordless.Client({ apiKey: import.meta.env.VITE_PASSWORDLESS_API_KEY!, apiUrl: import.meta.env.VITE_PASSWORDLESS_API_URL }); const finalResponse = await p.register(registerToken.token); if (finalResponse) { toast(`Registered '${username}'!`); } } }; return (

Register

setFirstName(e.target.value)} /> setLastName(e.target.value)} /> setUsername(e.target.value)} /> setAlias(e.target.value)} />
); } ``` -------------------------------- ### Display User Profile and Credentials Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Renders the user's profile information and a list of their registered passkey credentials. Fetches username and credentials upon component mount. ```tsx // src/pages/ProfilePage.tsx import { useEffect, useState } from "react"; import useAuth from "../hooks/UseAuth"; import CredentialResponse from "../services/your-backend/contracts/CredentialResponse.ts"; import YourBackendClient from "../services/your-backend/YourBackendClient.ts"; export default function ProfilePage() { const auth = useAuth(); const [username, setUsername] = useState(); const [credentials, setCredentials] = useState(); useEffect(() => { const fetchUsername = async () => { setUsername(auth.session?.username); }; const fetchCredentials = async () => { const backend = new YourBackendClient(); try { const response = await backend.getCredentials(auth.session!.userId!); setCredentials(response); } catch (e) { console.error(e); } } fetchUsername(); fetchCredentials(); }, [auth]); return (

Hi {username}!

Your Credentials

    {credentials?.map((credential, index) => (
  • Country: {credential.country}

    Device: {credential.device}

    Created at: {credential.createdAt?.toLocaleString()}

  • ))}
); } ``` -------------------------------- ### Configure Environment Variables for Passwordless.dev Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Set environment variables in a .env file to configure the Passwordless.dev API connection and backend URL. Ensure these variables are correctly set for the application to connect to the API. ```shell # .env file VITE_BACKEND_URL=https://demo.passwordless.dev VITE_PASSWORDLESS_API_KEY=pwdemo:public:5aec1f24f65343239bf4e1c9a852e871 VITE_PASSWORDLESS_API_URL=https://v4.passwordless.dev ``` -------------------------------- ### Login User with Passkey using Passwordless.dev Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Facilitates user login using Passwordless.dev, supporting both alias-based signin and discoverable credentials for passkey autofill. After authentication, the token is verified with your backend, and a JWT is stored for session management. ```tsx // src/pages/LoginPage.tsx import { useState } from "react"; import * as Passwordless from "@passwordlessdev/passwordless-client"; import YourBackendClient from "../services/your-backend/YourBackendClient"; import UserLoginRequest from "../services/your-backend/contracts/UserLoginRequest.ts"; export default function LoginPage() { const [alias, setAlias] = useState(""); const handleSubmit = async (e: React.MouseEvent) => { e.preventDefault(); // Step 1: Initialize Passwordless client const passwordless = new Passwordless.Client({ apiUrl: import.meta.env.VITE_PASSWORDLESS_API_URL!, apiKey: import.meta.env.VITE_PASSWORDLESS_API_KEY! }); // Step 2: Authenticate with passkey (alias or discoverable) let token = null; if (alias !== "") { // Login with a specific alias token = await passwordless.signinWithAlias(alias); } else { // Login with discoverable credentials (passkey autofill) token = await passwordless.signinWithDiscoverable(); } if (!token) { return; } // Step 3: Verify token with your backend const yourBackendClient = new YourBackendClient(); const loginRequest: UserLoginRequest = { token: token.token! }; const verifiedToken = await yourBackendClient.login(loginRequest); // Step 4: Store JWT and redirect localStorage.setItem('token', verifiedToken.jwtToken); window.location.href = "/profile"; } return (

Sign In

setAlias(e.target.value)} value={alias} />
); } ``` -------------------------------- ### Define User Registration Request Contract Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Defines the structure for user registration requests sent to the backend. Includes username, first name, last name, and an optional alias. ```typescript // src/services/your-backend/contracts/UserRegisterRequest.ts export default class UserRegisterRequest { username: string = ''; firstName: string = ''; lastName: string = ''; alias?: string; } ``` -------------------------------- ### Define User Login Request Contract Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Defines the structure for user login requests. Currently, it only contains an optional token field. ```typescript // src/services/your-backend/contracts/UserLoginRequest.ts export default class UserLoginRequest { token?: string; } ``` -------------------------------- ### Session Data Model Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Defines the structure for user session data, including username, user ID, and roles. ```typescript export default class Session { username?: string; userId?: string; role?: string[]; } ``` -------------------------------- ### Define Registration Token Response Contract Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Defines the structure for the response containing a registration token after a successful registration attempt. ```typescript // src/services/your-backend/contracts/RegisterTokenResponse.ts export default class RegisterTokenResponse { token: string = ''; } ``` -------------------------------- ### Define Credential Response Contract Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Defines the structure for a user credential response. Includes optional fields for creation date, country, and device. ```typescript // src/services/your-backend/contracts/CredentialResponse.ts export default class CredentialResponse { createdAt?: Date; country?: string; device?: string; } ``` -------------------------------- ### Auth-Aware Navigation Menu Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt A navigation menu component that conditionally renders links based on the user's authentication state. Uses React Router for navigation. ```tsx // src/components/Menu.tsx import { Link } from 'react-router-dom'; import useAuth from "../hooks/UseAuth.ts"; import { AuthContextProps } from "../auth/AuthProvider.tsx"; const Menu = () => { const auth: AuthContextProps = useAuth(); return (
); } export default Menu; ``` -------------------------------- ### Guest-Only Route Guard (RequireGuest) Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt React component that acts as a route guard, ensuring that only unauthenticated users can access public routes like login or registration. Redirects authenticated users to their profile page. ```tsx import { useLocation, Navigate, Outlet } from "react-router-dom"; import useAuth from "../hooks/UseAuth.ts"; const RequireGuest = () => { const session = useAuth(); const location = useLocation(); return ( session.isAuthenticated === false ? : ); } export default RequireGuest; ``` -------------------------------- ### Define Verified User Response Contract Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Defines the structure for the response after a successful user verification, containing a JWT token. ```typescript // src/services/your-backend/contracts/VerifiedUserResponse.ts export default class VerifiedUserResponse { jwtToken: string = ''; } ``` -------------------------------- ### AuthProvider for Global Authentication State Management Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Manages global authentication state using React Context. It checks JWT token validity from local storage and maintains session information. Call `checkAuthStatus` to refresh the authentication state. ```tsx // src/auth/AuthProvider.tsx import { createContext, useEffect, useState } from 'react'; import Session from "./Session.ts"; import { decodeJwt, isTokenExpired } from "./TokenUtility.ts"; import JwtToken from "./JwtToken.ts"; export interface AuthContextProps { session: Session | undefined; isAuthenticated: boolean; checkAuthStatus: () => void; } const AuthContext = createContext(undefined); export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [isAuthenticated, setIsAuthenticated] = useState(false); const [session, setSession] = useState(); const checkAuthStatus = () => { const jwt = localStorage.getItem('token'); if (jwt && !isTokenExpired(jwt)) { setIsAuthenticated(true); const jwtToken: JwtToken = decodeJwt(jwt); const s = new Session(); s.username = jwtToken.unique_name; s.userId = jwtToken.nameid; setSession(s); } else { setIsAuthenticated(false); setSession(undefined); } }; useEffect(() => { checkAuthStatus(); }, []); return ( {children} ); }; export default AuthContext; ``` -------------------------------- ### useAuth Custom Hook for Authentication Context Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt A custom hook to easily access the authentication context from any component. It throws an error if used outside of an AuthProvider, ensuring proper application structure. ```tsx // src/hooks/UseAuth.ts import { useContext } from 'react'; import AuthProvider, { AuthContextProps } from '../auth/AuthProvider.tsx'; const useAuth = (): AuthContextProps => { const context = useContext(AuthProvider); if (!context) { throw new Error("useAuth must be used within an AuthProvider"); } return context; } export default useAuth; // Usage example in a component: import useAuth from "../hooks/UseAuth"; function MyComponent() { const { isAuthenticated, session, checkAuthStatus } = useAuth(); return (
{isAuthenticated ? (

Welcome, {session?.username}!

) : (

Please log in

)}
); } ``` -------------------------------- ### Protected Route Guard (RequireAuth) Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt React component that acts as a route guard, ensuring that only authenticated users can access certain routes. Redirects unauthenticated users to the login page. ```tsx import { useLocation, Navigate, Outlet } from "react-router-dom"; import useAuth from "../hooks/UseAuth.ts"; const RequireAuth = () => { const { isAuthenticated } = useAuth(); const location = useLocation(); return ( isAuthenticated === true ? : ); } export default RequireAuth; ``` -------------------------------- ### JWT Token Payload Model Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Defines the expected structure of a JWT token's payload, including unique name, name ID, and expiration time. ```typescript export default class JwtToken { unique_name?: string; nameid?: string; exp?: number; } ``` -------------------------------- ### Decode JWT Token Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Decodes a JWT token string into a JwtToken object. Requires the token to be in the standard three-part format. ```typescript import JwtToken from "./JwtToken.ts"; export function decodeJwt(jwtToken: string): JwtToken { const base64Url: string = jwtToken.split('.')[1]; const base64: string = base64Url.replace(/-/g, '+').replace(/_/g, '/'); const jsonPayload: string = decodeURIComponent(window.atob(base64).split('').map(function(c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); return JSON.parse(jsonPayload); } ``` -------------------------------- ### Check if JWT Token is Expired Source: https://context7.com/bitwarden/passwordless-react-example/llms.txt Checks if a JWT token has expired. It decodes the token and compares the expiration time with the current time. Returns true if the token is expired or invalid. ```typescript export function isTokenExpired(token: string): boolean { try { const decodedToken = decodeJwt(token); if (decodedToken.exp) { const currentTime = Date.now() / 1000; return decodedToken.exp < currentTime; } return false; } catch (error) { console.error('Invalid token:', error); return true; } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.