React Integration Guide
This guide shows how to integrate Alien SSO into React applications using the React SDK with pre-built hooks and components.
Requirements
- React 19.1.1 or higher
- React DOM 19.1.1 or higher
- A registered provider from the developer portal with provider address
Installation
npm install @alien_org/sso-sdk-reactThe React SDK automatically includes @alien_org/sso-sdk-core as a dependency.
Setup
Wrap Your App with Provider
import { AlienSsoProvider } from '@alien_org/sso-sdk-react';
function App() {
return (
<AlienSsoProvider
config={{
ssoBaseUrl: 'https://sso.alien-api.com',
providerAddress: 'your-provider-address'
}}
>
<YourApp />
</AlienSsoProvider>
);
}
export default App;Configuration Options
| Option | Type | Required | Description |
|---|---|---|---|
ssoBaseUrl | string | Yes | Base URL of the SSO service |
providerAddress | string | Yes | Your provider address from developer portal |
pollingInterval | number | No | Polling interval in ms (default: 5000) |
Using the useAuth Hook
The useAuth() hook provides access to authentication state and methods.
import { useAuth } from '@alien_org/sso-sdk-react';
function Dashboard() {
const { auth, logout } = useAuth();
if (!auth.isAuthenticated) {
return <div>Not authenticated</div>;
}
return (
<div>
<p>User ID: {auth.tokenInfo?.sub}</p>
<p>Expires: {new Date(auth.tokenInfo.exp * 1000).toLocaleString()}</p>
<button onClick={logout}>Logout</button>
</div>
);
}Auth State
The auth object contains:
{
isAuthenticated: boolean;
token: string | null; // Access token
tokenInfo: {
iss: string; // Issuer
sub: string; // User identifier
aud: string | string[]; // Audience (your provider address)
exp: number; // Expiration timestamp
iat: number; // Issued at timestamp
nonce?: string;
auth_time?: number;
} | null;
}Available Methods
const {
client, // Direct access to AlienSsoClient instance
auth, // Authentication state
queryClient, // React Query client instance
generateDeeplink, // Generate authentication deep link
pollAuth, // Poll for authentication status
exchangeToken, // Exchange authorization code for tokens
verifyAuth, // Verify current token (calls /oauth/userinfo)
refreshToken, // Refresh access token
logout, // Clear authentication state
openModal, // Open built-in sign-in modal
closeModal, // Close sign-in modal
isModalOpen // Modal open state
} = useAuth();Using Pre-built Components
SignInButton
A pre-styled button that opens the sign-in modal.
import { SignInButton } from '@alien_org/sso-sdk-react';
function LoginPage() {
return (
<div>
<h1>Welcome</h1>
<SignInButton />
</div>
);
}SignInModal
The modal is automatically rendered by AlienSsoProvider and handles the complete authentication flow including QR code display, polling, and token exchange.
Control it via the useAuth() hook:
import { useAuth } from '@alien_org/sso-sdk-react';
function CustomButton() {
const { openModal } = useAuth();
return <button onClick={openModal}>Sign In with Alien</button>;
}Token Refresh
The SDK provides a refreshToken method for refreshing access tokens:
import { useAuth } from '@alien_org/sso-sdk-react';
function MyComponent() {
const { refreshToken, auth, logout } = useAuth();
async function handleApiCall() {
// Check if token is expiring soon
if (auth.tokenInfo && auth.tokenInfo.exp * 1000 < Date.now() + 60000) {
const success = await refreshToken();
if (!success) {
// Refresh failed, redirect to login
return;
}
}
// Make API call with fresh token
const response = await fetch('/api/data', {
headers: {
Authorization: `Bearer ${auth.token}`
}
});
}
}Automatic Token Refresh with Axios
For automatic token refresh on API calls, use an axios interceptor:
import { useAuth } from '@alien_org/sso-sdk-react';
import axios from 'axios';
import { useMemo, useRef } from 'react';
export function useAxios() {
const { auth, logout, refreshToken } = useAuth();
const isRefreshing = useRef(false);
const failedQueue = useRef([]);
return useMemo(() => {
const instance = axios.create({
baseURL: '/api',
});
// Add token to requests
instance.interceptors.request.use((config) => {
if (auth.token) {
config.headers.Authorization = `Bearer ${auth.token}`;
}
return config;
});
// Handle 401 responses
instance.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status !== 401 || originalRequest._retry) {
return Promise.reject(error);
}
if (isRefreshing.current) {
// Queue request while refreshing
return new Promise((resolve, reject) => {
failedQueue.current.push({ resolve, reject });
}).then(() => instance(originalRequest));
}
originalRequest._retry = true;
isRefreshing.current = true;
try {
const success = await refreshToken();
if (success) {
failedQueue.current.forEach((p) => p.resolve());
failedQueue.current = [];
return instance(originalRequest);
}
throw new Error('Refresh failed');
} catch (refreshError) {
failedQueue.current.forEach((p) => p.reject(refreshError));
failedQueue.current = [];
logout();
return Promise.reject(refreshError);
} finally {
isRefreshing.current = false;
}
}
);
return instance;
}, [auth.token, logout, refreshToken]);
}Custom Authentication Flow
If you want to implement a custom UI instead of using the built-in modal:
import { useAuth } from '@alien_org/sso-sdk-react';
import { useState, useEffect } from 'react';
import QRCode from 'qrcode.react';
function CustomAuth() {
const { generateDeeplink, pollAuth, exchangeToken, auth } = useAuth();
const [deepLink, setDeepLink] = useState<string | null>(null);
const [pollingCode, setPollingCode] = useState<string | null>(null);
const handleSignIn = async () => {
const response = await generateDeeplink();
setDeepLink(response.deep_link);
setPollingCode(response.polling_code);
};
useEffect(() => {
if (!pollingCode) return;
const interval = setInterval(async () => {
const response = await pollAuth(pollingCode);
if (response.status === 'authorized') {
clearInterval(interval);
await exchangeToken(response.authorization_code!);
setDeepLink(null);
setPollingCode(null);
} else if (response.status === 'rejected' || response.status === 'expired') {
clearInterval(interval);
setDeepLink(null);
setPollingCode(null);
}
}, 5000);
return () => clearInterval(interval);
}, [pollingCode, pollAuth, exchangeToken]);
if (auth.isAuthenticated) {
return <div>Authenticated as {auth.tokenInfo?.sub}</div>;
}
if (deepLink) {
return (
<div>
<QRCode value={deepLink} size={256} />
<p>Scan with Alien App</p>
</div>
);
}
return <button onClick={handleSignIn}>Sign In with Alien</button>;
}Protected Routes
Create a protected route component:
import { useAuth } from '@alien_org/sso-sdk-react';
import { Navigate } from 'react-router-dom';
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { auth } = useAuth();
if (!auth.isAuthenticated) {
return <Navigate to="/login" />;
}
return <>{children}</>;
}
// Usage
<Route path="/dashboard" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />Complete Example
import { AlienSsoProvider, useAuth, SignInButton } from '@alien_org/sso-sdk-react';
import { useEffect } from 'react';
function App() {
return (
<AlienSsoProvider
config={{
ssoBaseUrl: 'https://sso.alien-api.com',
providerAddress: 'your-provider-address'
}}
>
<Dashboard />
</AlienSsoProvider>
);
}
function Dashboard() {
const { auth, logout, verifyAuth } = useAuth();
// Verify token on mount
useEffect(() => {
if (auth.token) {
verifyAuth();
}
}, []);
if (!auth.isAuthenticated) {
return (
<div>
<h1>Welcome to My App</h1>
<SignInButton />
</div>
);
}
return (
<div>
<h1>Dashboard</h1>
<p>User ID: {auth.tokenInfo?.sub}</p>
<p>Issuer: {auth.tokenInfo?.iss}</p>
<p>Expires: {new Date(auth.tokenInfo!.exp * 1000).toLocaleString()}</p>
<button onClick={logout}>Logout</button>
</div>
);
}
export default App;TypeScript Support
The React SDK is fully typed. Import types as needed:
import type { AlienSsoClient } from '@alien_org/sso-sdk-react';
import type {
AuthorizeResponse,
PollResponse,
TokenResponse,
TokenInfo,
} from '@alien_org/sso-sdk-core';Next Steps
- API Reference - React - Complete API documentation
- Demo App - Example implementation
- Core Integration Guide - For vanilla JavaScript/TypeScript