Public Clients (SPAs & Mobile)
Use this guide if your application is a Public Client, such as a Single-Page Application (SPA) built with React, Angular, or Vue, or a native mobile application. These applications cannot securely store a client_secret and must use the Authorization Code Flow with PKCE.
Recommendation: Use a Library
It is highly recommended to use a certified OIDC client library to handle the complexities of the OIDC flow securely. These libraries automatically manage state, PKCE code generation, token storage, and token validation for you.
Integration Steps
- Install and Configure an OIDC Library: Choose a library for your framework (e.g.,
react-oidc-contextfor React) and install it. Configure it with your Visual Passcodesclient_id, the Visual Passcodes discovery URLhttps://your-server.com/api/oidc/{tenant_id}/.well-known/openid-configuration, and your application'sredirect_uri. - Protect Routes: Use the library's components or hooks to protect the parts of your application that require a user to be logged in. The library will automatically trigger the login flow when an unauthenticated user tries to access a protected route.
- Handle Login and Callback: The library will handle the entire OIDC flow automatically:
- Generating and storing the PKCE
code_verifier. - Constructing the authorization URL with the
code_challenge. - Redirecting the user to Visual Passcodes.
- Handling the callback and exchanging the
authorization_codefor tokens (including thecode_verifierin the request).
- Generating and storing the PKCE
- Access User Info and Tokens: Once the user is authenticated, use the library's functions to get user profile information from the
id_tokenand theaccess_tokenfor calling APIs. - Backend API Validation: When your SPA calls your own protected backend API, it must include the
access_tokenin theAuthorizationheader. Your backend must then validate this token by checking its signature against Visual Passcodes' public keys from the JWKS Endpoint before trusting its contents and returning data.
Code Example
- React
This example uses the react-oidc-context library to integrate a React SPA with Visual Passcodes.
src/main.jsx (Provider Configuration)
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';
import { OidcProvider } from 'react-oidc-context';
// This configuration object tells the library how to communicate with Visual Passcodes.
const oidcConfig = {
authority: `https://your-server.com/api/oidc/{tenant_id}`, // Base issuer URL
client_id: 'your_public_client_id', // Your public client ID from Visual Passcodes
redirect_uri: window.location.origin + '/authentication/callback', // Must match a registered URI
response_type: 'code', // Enables Authorization Code Flow with PKCE
scope: 'openid profile email', // Standard OIDC scopes
};
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<OidcProvider configuration={oidcConfig}>
<App />
</OidcProvider>
</React.StrictMode>,
);
src/main.jsx
import React from 'react';
import { useAuth } from 'react-oidc-context';
function App() {
// The useAuth hook provides all authentication state and functions.
const { isAuthenticated, isLoading, user, login, logout, accessToken } = useAuth();
const fetchProtectedApi = async () => {
// When calling a protected API, include the access token.
const response = await fetch('https://your-protected-api.com/data', {
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});
const data = await response.json();
console.log('API Response:', data);
};
if (isLoading) {
return <div>Loading authentication...</div>;
}
return (
<div className="App">
<h1>Visual Passcodes Public Client (React)</h1>
{isAuthenticated ? (
<div>
<p>Welcome, {user?.profile.name}!</p>
<p>Your email is: {user?.profile.email}</p>
<button onClick={fetchProtectedApi}>Call Protected API</button>
<button onClick={() => logout()}>Log Out</button>
</div>
) : (
<button onClick={() => login()}>Log In with Visual Auth</button>
)}
</div>
);
}
export default App;