Skip to Content
GuideReact Integration
View .md

Alien SSO React Integration: AlienSsoProvider Setup Guide

Add “Sign in with Alien” to a React app with the React SDK’s provider, hook, and pre-built components — working sign-in in about five minutes.

Quick Start

Testing prerequisite. Completing a sign-in requires the Alien App on a phone with an Alien ID — that’s what scans the QR code and approves the request.

1. Install

npm install @alien-id/sso-react

The React SDK requires React 19.1.1 or higher and automatically includes @alien-id/sso as a dependency.

2. Wrap your app

import { AlienSsoProvider } from '@alien-id/sso-react'; function App() { return ( <AlienSsoProvider config={{ ssoBaseUrl: 'https://sso.alien-api.com', providerAddress: 'your-provider-address', }} > <YourApp /> </AlienSsoProvider> ); } export default App;

Provider address required. Register your app in the Developer Portal to get the providerAddress value.

3. Add the sign-in button

import { SignInButton } from '@alien-id/sso-react'; function LoginPage() { return ( <div> <h1>Welcome</h1> <SignInButton /> </div> ); }

Run it — you should see the Sign in with Alien ID button. Clicking it opens the QR modal; scanning the code with the Alien App approves the sign-in.

4. Read the user

import { useAuth } from '@alien-id/sso-react'; function Dashboard() { const { auth, logout } = useAuth(); if (!auth.isAuthenticated) { return <div>Not signed in</div>; } return ( <div> <p>User ID: {auth.tokenInfo?.sub}</p> <button onClick={logout}>Log out</button> </div> ); }

You now have working Alien sign-in. The provider renders the QR modal, polls for approval, exchanges the authorization code for tokens, and stores them. The rest of this page covers configuration, token refresh, and custom flows.

How Sign-In Works

The SDK drives the full OAuth flow for you: it generates a PKCE-backed deep link, shows it as a QR code, polls until the user approves in the Alien App, then exchanges the authorization code for tokens. Your code only reads the resulting auth state. See How Alien SSO works for the flow diagram and a step-by-step walkthrough.

Configuration

OptionTypeDefaultDescription
ssoBaseUrlstringBase URL of the SSO service (required)
providerAddressstringYour provider address from the Developer Portal (required)
pollingIntervalnumber5000Polling interval in ms

All other core client options — redirectUri, tokenStorage, dpop, allowInsecureSsoBaseUrl — pass through the config object unchanged. See the core API reference when you need them.

Auth State and Methods

The useAuth() hook provides the auth state and every method you need to drive a custom flow. The auth object contains:

{ isAuthenticated: boolean; token: string | null; // access token tokenInfo: TokenInfo | null; // verified ID-token claims }

tokenInfo holds the ID-token claims (sub, iss, aud, exp, …) that were verified at token exchange — see TokenInfo for the exact shape.

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(); // Abbreviated — the context also exposes getAccessToken, pollingInterval, // agentIdEnabled and agentIdSkillUrl. See the React API Reference.

Control the Sign-In Modal

The provider automatically renders the sign-in modal and handles the complete authentication flow — QR code display, polling, and token exchange.

Do not render <SignInModal /> yourself. The provider already mounts it; manual instances render nothing (an internal slot ensures only the provider-owned instance mounts). The component takes no props — open and close it exclusively via useAuth().openModal() / closeModal().

import { useAuth } from '@alien-id/sso-react'; function CustomButton() { const { openModal } = useAuth(); return <button onClick={openModal}>Sign In with Alien</button>; }

Keep Tokens Fresh

Use refreshToken to refresh the access token before it expires:

import { useAuth } from '@alien-id/sso-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 accessToken = await refreshToken(); if (!accessToken) { // Refresh failed, redirect to login return; } } // Make API call with fresh token const response = await fetch('/api/data', { headers: { Authorization: `Bearer ${auth.token}` } }); } }

Automatic Refresh with Axios

For automatic token refresh on API calls, use an axios interceptor:

import { useAuth } from '@alien-id/sso-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 accessToken = await refreshToken(); if (accessToken) { 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]); }

Build a Custom Sign-In UI

If you want a custom UI instead of the built-in modal, drive the flow yourself with generateDeeplink, pollAuth, and exchangeToken:

import { useAuth } from '@alien-id/sso-react'; import { useState, useEffect } from 'react'; import { QRCodeSVG } 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>Signed in as {auth.tokenInfo?.sub}</div>; } if (deepLink) { return ( <div> <QRCodeSVG value={deepLink} size={256} /> <p>Scan with Alien App</p> </div> ); } return <button onClick={handleSignIn}>Sign In with Alien</button>; }

Protect Routes

Gate routes on the auth state:

import { useAuth } from '@alien-id/sso-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-id/sso-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

The React SDK is fully typed. The client class and the shared flow types all come from @alien-id/sso:

import type { AlienSsoClient, AuthorizeResponse, PollResponse, TokenResponse, TokenInfo, } from '@alien-id/sso';

See the React API reference for the full list of exported types.

Next Steps

Last updated on