diksuchi-onelink
v1.2.0
Published
Official authentication SDK for Diksuchi platforms - OAuth2 with PKCE, React hooks, and enterprise security
Maintainers
Readme
diksuchi-onelink
Official authentication SDK for Diksuchi platforms - OAuth2 with PKCE, React hooks, and enterprise security
🚀 Features
- ✅ OAuth 2.0 with PKCE - Secure authorization code flow
- ✅ React Hooks & Components - Pre-built UI components and hooks
- ✅ TypeScript First - Full type safety and IntelliSense
- ✅ Auto Token Refresh - Automatic token renewal before expiry
- ✅ Server Middleware - Next.js, Express, and generic middleware
- ✅ Role-Based Access Control - App-specific permissions
- ✅ Zero Dependencies - Only
josefor JWT verification - ✅ Tree-Shakeable - Import only what you need
- ✅ SSR Ready - Works with Next.js and server-side rendering
📦 Installation
npm install diksuchi-onelink
# or
yarn add diksuchi-onelink
# or
pnpm add diksuchi-onelink� Browser Compatibility
diksuchi-onelink works in all modern browsers that support:
- ES2020 features
- Web Crypto API (
crypto.subtle) fetchAPIPromiseandasync/await
Supported Browsers
| Browser | Minimum Version | |---------|----------------| | Chrome | 80+ | | Firefox | 75+ | | Safari | 13.1+ | | Edge | 80+ | | Opera | 67+ |
Node.js Support
- Node.js 18.x or higher (for server-side usage)
- Requires Web Crypto API (available in Node 15+)
Required Browser APIs
The package uses these Web APIs:
- Web Crypto API - For PKCE generation and SHA-256 hashing
- Fetch API - For OAuth token requests
- Storage API - For token persistence (localStorage/sessionStorage)
- URL API - For OAuth redirect handling
Polyfills
For older browsers, you may need polyfills for:
crypto.subtle(use subtle-crypto polyfill)fetch(use whatwg-fetch)
�🎯 Quick Start
React Application (5 minutes setup)
import { DiksuchiAuthProvider, useAuth, LoginButton } from 'diksuchi-onelink/react';
// 1. Wrap your app with DiksuchiAuthProvider
function App() {
return (
<DiksuchiAuthProvider
config={{
clientId: 'your-app-id', // e.g., 'projectshub'
authServerUrl: 'https://diksuchi-auth-identity.vercel.app',
}}
autoHandleCallback={true}
>
<YourApp />
</DiksuchiAuthProvider>
);
}
// 2. Use authentication in your components
function YourApp() {
const { user, isAuthenticated, login, logout } = useAuth();
if (!isAuthenticated) {
return <LoginButton />;
}
return (
<div>
<h1>Welcome, {user?.name || user?.email}!</h1>
<button onClick={() => logout()}>Logout</button>
</div>
);
}Vanilla JavaScript
import { DiksuchiAuth } from 'diksuchi-onelink';
const auth = new DiksuchiAuth({
clientId: 'your-app-id',
});
// Login
await auth.login();
// On callback page
const user = await auth.handleCallback();
console.log('Logged in as:', user.email);
// Get access token
const token = auth.getAccessToken();
// Check roles
if (auth.hasRole('projectshub', 'creator')) {
console.log('User is a creator!');
}
// Logout
await auth.logout();Server-Side (Next.js API Routes)
import { createNextAuthMiddleware } from 'diksuchi-onelink/server';
import { NextRequest, NextResponse } from 'next/server';
const withAuth = createNextAuthMiddleware({
requiredRoles: {
app: 'projectshub',
roles: ['creator'],
},
});
export const GET = withAuth(async (request: NextRequest, user) => {
// user is automatically verified and typed
return NextResponse.json({
message: `Hello ${user.email}!`,
roles: user.apps.projectshub,
});
});📚 Documentation
Table of Contents
- Configuration
- React Hooks
- Components
- Client API
- Server Middleware
- Role-Based Access Control
- Advanced Usage
⚙️ Configuration
DiksuchiAuthConfig
interface DiksuchiAuthConfig {
/** OAuth client ID for your application (required) */
clientId: string;
/** Auth server URL (default: https://diksuchi-auth-identity.vercel.app) */
authServerUrl?: string;
/** Redirect URI (auto-detected if not provided) */
redirectUri?: string;
/** OAuth scopes (default: ['openid', 'profile', 'email']) */
scopes?: string[];
/** Storage type (default: sessionStorage) */
storage?: 'localStorage' | 'sessionStorage' | 'memory';
/** Auto-refresh tokens (default: true) */
autoRefresh?: boolean;
/** Refresh threshold in seconds (default: 60) */
refreshThreshold?: number;
/** Enable debug logging (default: false) */
debug?: boolean;
/** Callbacks */
onAuthStateChange?: (user: DiksuchiUser | null) => void;
onTokenRefresh?: (tokens: TokenResponse) => void;
onError?: (error: AuthError) => void;
}Pre-defined Client IDs
import { DiksuchiApp } from 'diksuchi-onelink';
DiksuchiApp.Main // 'diksuchi'
DiksuchiApp.ProjectsHub // 'projectshub'
DiksuchiApp.ChallengeHub // 'challengehub'
DiksuchiApp.PDFHub // 'pdfhub'
DiksuchiApp.AICodeEditor // 'aicodeeditor'🎣 React Hooks
useAuth()
Main hook for authentication state and actions.
import { useAuth } from 'diksuchi-onelink/react';
function MyComponent() {
const {
user, // Current user or null
isAuthenticated, // Boolean
isLoading, // Boolean
accessToken, // JWT access token
error, // AuthError or null
login, // Function to initiate login
logout, // Function to logout
refreshToken, // Function to manually refresh token
hasRole, // Check single role
checkRoles, // Check multiple roles
} = useAuth();
}useUser()
Get current user.
const user = useUser(); // DiksuchiUser | nulluseIsAuthenticated()
Check authentication status.
const isAuthenticated = useIsAuthenticated(); // booleanuseHasRole()
Check if user has a specific role.
const isCreator = useHasRole('projectshub', 'creator'); // booleanuseAccessToken()
Get access token for API calls.
const token = useAccessToken();
// Use in fetch
fetch('/api/data', {
headers: {
'Authorization': `Bearer ${token}`,
},
});🧩 Components
LoginButton
Pre-built login button with loading state.
<LoginButton
options={{ loginHint: '[email protected]' }}
onLoginStart={() => console.log('Login started')}
className="btn-primary"
>
Sign in with Diksuchi
</LoginButton>LogoutButton
Pre-built logout button.
<LogoutButton
options={{ revokeAllSessions: true }}
onLogoutComplete={() => console.log('Logged out')}
>
Sign out
</LogoutButton>AuthGuard
Protect routes/components behind authentication.
<AuthGuard
fallback={<LoginPage />}
loginRedirect={true}
requiredRoles={{
app: 'projectshub',
roles: ['creator'],
}}
forbiddenFallback={<AccessDenied />}
>
<ProtectedContent />
</AuthGuard>RoleGuard
Conditionally render based on roles.
<RoleGuard
app="projectshub"
roles={['admin', 'moderator']}
requireAll={false} // User needs at least one role
fallback={<div>Access denied</div>}
>
<AdminPanel />
</RoleGuard>UserProfile
Display user information.
<UserProfile
showEmail={true}
showPicture={true}
className="user-profile"
/>CallbackHandler
Handle OAuth callback on callback page.
// pages/auth/callback.tsx
import { CallbackHandler } from 'diksuchi-onelink/react';
import { useRouter } from 'next/router';
export default function Callback() {
const router = useRouter();
return (
<CallbackHandler
onSuccess={(user) => {
console.log('Logged in:', user);
router.push('/dashboard');
}}
onError={(error) => {
console.error('Login failed:', error);
router.push('/login');
}}
loadingComponent={<div>Logging you in...</div>}
/>
);
}💻 Client API
DiksuchiAuth Class
import { DiksuchiAuth } from 'diksuchi-onelink';
const auth = new DiksuchiAuth({
clientId: 'projectshub',
debug: true,
});Methods
login(options?)
Initiate OAuth login flow.
await auth.login({
loginHint: '[email protected]',
prompt: 'login', // Force fresh login
additionalScopes: ['admin'],
});handleCallback(callbackUrl?)
Handle OAuth callback and exchange code for tokens.
const user = await auth.handleCallback();logout(options?)
Logout user and clear tokens.
await auth.logout({
revokeAllSessions: true,
returnTo: '/goodbye',
});refreshToken(options?)
Manually refresh access token.
const tokens = await auth.refreshToken({ force: true });getUser()
Get current user.
const user = auth.getUser();getAccessToken()
Get access token for API calls.
const token = auth.getAccessToken();isAuthenticated()
Check if user is authenticated.
if (auth.isAuthenticated()) {
// User is logged in
}hasRole(app, role)
Check if user has specific role.
if (auth.hasRole('projectshub', 'creator')) {
// User is a creator
}checkRoles(app, roles)
Check multiple roles.
const check = auth.checkRoles('projectshub', ['admin', 'moderator']);
if (check.hasRole) {
console.log('User has all required roles');
} else {
console.log('Missing roles:', check.missingRoles);
}subscribe(listener)
Subscribe to auth state changes.
const unsubscribe = auth.subscribe((state) => {
console.log('Auth state changed:', state);
});
// Cleanup
unsubscribe();🔐 Server Middleware
Next.js App Router
// app/api/protected/route.ts
import { createNextAuthMiddleware } from 'diksuchi-onelink/server';
import { NextRequest, NextResponse } from 'next/server';
const withAuth = createNextAuthMiddleware({
requiredRoles: {
app: 'projectshub',
roles: ['creator'],
},
requireEmailVerified: true,
});
export const GET = withAuth(async (request: NextRequest, user) => {
return NextResponse.json({ user });
});Express
import express from 'express';
import { createExpressAuthMiddleware } from 'diksuchi-onelink/server';
const app = express();
const requireAuth = createExpressAuthMiddleware({
requiredRoles: {
app: 'projectshub',
roles: ['creator'],
},
});
app.get('/api/protected', requireAuth, (req, res) => {
res.json({ user: req.user });
});Generic Framework
import { createAuthMiddleware } from 'diksuchi-onelink/server';
const auth = createAuthMiddleware({
requiredRoles: {
app: 'projectshub',
roles: ['creator'],
},
});
// In your handler
const authHeader = request.headers.get('authorization');
const user = await auth.authenticate(authHeader);Token Verification
import { verifyToken } from 'diksuchi-onelink/server';
const user = await verifyToken(accessToken);
if (user) {
console.log('Valid user:', user.email);
}🛡️ Role-Based Access Control
User Object
interface DiksuchiUser {
sub: string; // User ID
email: string; // User email
name?: string; // User name
emailVerified?: boolean;
picture?: string; // Profile picture URL
apps: { // App-specific roles
[appId: string]: string[];
};
}Example User
{
sub: "user_123",
email: "[email protected]",
name: "John Doe",
apps: {
diksuchi: ["student", "instructor"],
projectshub: ["creator", "admin"],
pdfhub: ["user"]
}
}Check Roles
import { hasRole, hasAnyRole, hasAllRoles } from 'diksuchi-onelink/server';
// Single role
if (hasRole(user, 'projectshub', 'creator')) {
// User is a creator
}
// Any role
if (hasAnyRole(user, 'projectshub', ['admin', 'moderator'])) {
// User is admin OR moderator
}
// All roles
if (hasAllRoles(user, 'diksuchi', ['student', 'instructor'])) {
// User is both student AND instructor
}🔧 Advanced Usage
Custom Storage
import { DiksuchiAuth } from 'diksuchi-onelink';
const auth = new DiksuchiAuth({
clientId: 'projectshub',
storage: 'localStorage', // Persists across browser sessions
});Manual Token Management
import { TokenManager } from 'diksuchi-onelink';
const tokenManager = new TokenManager('localStorage');
// Save tokens
tokenManager.saveTokens(tokens);
// Check expiration
if (tokenManager.isTokenExpired(60)) {
// Token expires in less than 60 seconds
}
// Get time until expiry
const seconds = tokenManager.getTimeUntilExpiry();PKCE Utilities
import { generatePKCEChallenge, generateState } from 'diksuchi-onelink';
// Generate PKCE challenge
const pkce = await generatePKCEChallenge();
console.log(pkce.verifier); // Store this
console.log(pkce.challenge); // Send to server
// Generate state for CSRF protection
const state = generateState();Error Handling
import { isAuthError, AuthErrorCode } from 'diksuchi-onelink';
try {
await auth.login();
} catch (error) {
if (isAuthError(error)) {
switch (error.code) {
case AuthErrorCode.TOKEN_EXPIRED:
// Handle expired token
break;
case AuthErrorCode.UNAUTHORIZED:
// Handle unauthorized
break;
default:
console.error(error.message);
}
}
}🌟 Examples
Complete Next.js App
// app/layout.tsx
import { DiksuchiAuthProvider } from 'diksuchi-onelink/react';
export default function RootLayout({ children }) {
return (
<html>
<body>
<DiksuchiAuthProvider
config={{ clientId: 'projectshub' }}
autoHandleCallback={true}
>
{children}
</DiksuchiAuthProvider>
</body>
</html>
);
}
// app/page.tsx
import { useAuth, LoginButton, LogoutButton } from 'diksuchi-onelink/react';
export default function Home() {
const { user, isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <LoginButton>Sign In</LoginButton>;
}
return (
<div>
<h1>Welcome {user?.name}!</h1>
<LogoutButton>Sign Out</LogoutButton>
</div>
);
}
// app/api/data/route.ts
import { createNextAuthMiddleware } from 'diksuchi-onelink/server';
const withAuth = createNextAuthMiddleware();
export const GET = withAuth(async (request, user) => {
return Response.json({ data: 'Protected data', user });
});📄 License
MIT © Diksuchi EdTech
🤝 Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📖 Docs: Documentation
🎉 Related Packages
- @diksuchi/ui - Component library
- @diksuchi/api - API client
Made with ❤️ by Diksuchi EdTech
