@deliverart/sdk-js-authentication
v2.10.6
Published
Authentication module for DeliverArt SDK
Readme
@deliverart/sdk-js-authentication
Authentication plugin for the DeliverArt JavaScript SDK. Automatically adds Bearer token authentication to all API requests.
Installation
npm install @deliverart/sdk-js-authentication @deliverart/sdk-js-core
# or
pnpm add @deliverart/sdk-js-authentication @deliverart/sdk-js-core
# or
yarn add @deliverart/sdk-js-authentication @deliverart/sdk-js-coreExported Types
AuthenticationPlugin
class AuthenticationPlugin implements ApiClientPlugin<AuthenticationExtension>Plugin that adds JWT Bearer token authentication to all API requests.
Constructor:
constructor(config: AuthenticationConfig)
AuthenticationConfig
interface AuthenticationConfig {
getAccessToken: () => Promise<string | null>
}Configuration object for the authentication plugin.
Properties:
getAccessToken: () => Promise<string | null>(required) - Async function that returns the access token or null if not authenticated
AuthenticationExtension
interface AuthenticationExtension extends ApiExtension {
authentication: {
enable: () => void;
disable: () => void;
}
}Extension added to the API client when the plugin is installed.
Methods:
enable()- Enable authentication (adds Authorization header to requests)disable()- Disable authentication (skips adding Authorization header)
Errors
AuthenticationError
class AuthenticationError extends ErrorThrown when authentication is required but no access token is available.
Usage
Basic Setup
import { createApiClient } from '@deliverart/sdk-js-core';
import { AuthenticationPlugin } from '@deliverart/sdk-js-authentication';
const getAccessToken = async (): Promise<string | null> => {
// Retrieve token from your storage
return localStorage.getItem('accessToken');
};
const client = createApiClient({
baseUrl: 'https://api.deliverart.com'
})
.addPlugin(new AuthenticationPlugin({ getAccessToken }));Parameters:
getAccessToken(required) - Function that retrieves the current access token
With Different Token Storage
LocalStorage
const getAccessToken = async (): Promise<string | null> => {
return localStorage.getItem('accessToken');
};SessionStorage
const getAccessToken = async (): Promise<string | null> => {
return sessionStorage.getItem('accessToken');
};Cookies
import Cookies from 'js-cookie';
const getAccessToken = async (): Promise<string | null> => {
return Cookies.get('accessToken') ?? null;
};Next.js with Server Actions
import { cookies } from 'next/headers';
const getAccessToken = async (): Promise<string | null> => {
const cookieStore = await cookies();
return cookieStore.get('accessToken')?.value ?? null;
};Auth Provider (e.g., Auth0, Clerk)
import { useAuth } from '@clerk/nextjs';
const getAccessToken = async (): Promise<string | null> => {
const { getToken } = useAuth();
return await getToken();
};Enabling/Disabling Authentication
// Disable authentication temporarily
client.authentication.disable();
// Make unauthenticated requests
await client.call(new PublicRequest());
// Re-enable authentication
client.authentication.enable();
// Make authenticated requests
await client.call(new ProtectedRequest());Use Cases:
- Public endpoints that don't require authentication
- Guest checkout flows
- Login/registration requests
Complete Example with Next.js
// lib/sdk/client.ts
import { createApiClient, type ApiClient } from '@deliverart/sdk-js-core';
import { AuthenticationPlugin } from '@deliverart/sdk-js-authentication';
import { ErrorHandlerPlugin } from '@deliverart/sdk-js-error-handler';
const getAccessToken = async (): Promise<string | null> => {
// Server-side: get from cookies
if (typeof window === 'undefined') {
const { cookies } = await import('next/headers');
const cookieStore = await cookies();
return cookieStore.get('accessToken')?.value ?? null;
}
// Client-side: get from localStorage
return localStorage.getItem('accessToken');
};
export const sdk = createApiClient({
baseUrl: process.env.NEXT_PUBLIC_API_BASE_URL!
})
.addPlugin(new ErrorHandlerPlugin())
.addPlugin(new AuthenticationPlugin({ getAccessToken }));Login Flow Example
import { sdk } from './lib/sdk/client';
import { Login } from '@deliverart/sdk-js-auth';
// Disable authentication for login request
sdk.authentication.disable();
try {
const response = await sdk.call(new Login({
email: '[email protected]',
password: 'password123'
}));
// Store the token
localStorage.setItem('accessToken', response.token);
// Re-enable authentication for subsequent requests
sdk.authentication.enable();
// Now all requests will include the Authorization header
const user = await sdk.call(new GetCurrentUser());
} catch (error) {
console.error('Login failed:', error);
}Logout Flow Example
import { sdk } from './lib/sdk/client';
// Clear the token
localStorage.removeItem('accessToken');
// Disable authentication (optional, depends on your needs)
sdk.authentication.disable();
// Redirect to login page
window.location.href = '/login';Token Refresh Example
import { sdk } from './lib/sdk/client';
import { AuthenticationError } from '@deliverart/sdk-js-authentication';
let tokenRefreshPromise: Promise<string> | null = null;
const getAccessToken = async (): Promise<string | null> => {
let token = localStorage.getItem('accessToken');
// Check if token is expired (you need to implement this)
if (token && isTokenExpired(token)) {
// Ensure only one refresh happens at a time
if (!tokenRefreshPromise) {
tokenRefreshPromise = refreshToken();
}
try {
token = await tokenRefreshPromise;
localStorage.setItem('accessToken', token);
} finally {
tokenRefreshPromise = null;
}
}
return token;
};
async function refreshToken(): Promise<string> {
const refreshToken = localStorage.getItem('refreshToken');
if (!refreshToken) {
throw new AuthenticationError('No refresh token available');
}
// Disable authentication for refresh request
sdk.authentication.disable();
try {
const response = await sdk.call(new RefreshToken({ refreshToken }));
sdk.authentication.enable();
return response.accessToken;
} catch (error) {
// Refresh failed, redirect to login
localStorage.clear();
window.location.href = '/login';
throw error;
}
}
function isTokenExpired(token: string): boolean {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp * 1000 < Date.now();
} catch {
return true;
}
}Error Handling
import { AuthenticationError } from '@deliverart/sdk-js-authentication';
try {
await sdk.call(new GetUser('123'));
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Authentication failed:', error.message);
// Redirect to login page
window.location.href = '/login';
}
}How It Works
The plugin adds a request middleware that:
- Calls your
getAccessTokenfunction to retrieve the current token - If a token is found, adds it to the
Authorizationheader asBearer <token> - If no token is found and authentication is enabled, throws an
AuthenticationError - If authentication is disabled, skips adding the Authorization header
Best Practices
1. Centralized Token Management
Create a dedicated module for token management:
// lib/auth/tokens.ts
export const setAccessToken = (token: string) => {
localStorage.setItem('accessToken', token);
};
export const getAccessToken = (): string | null => {
return localStorage.getItem('accessToken');
};
export const clearTokens = () => {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
};2. Secure Token Storage
- Use
httpOnlycookies for tokens when possible (more secure) - Avoid storing sensitive tokens in localStorage on production if possible
- Consider using secure storage libraries for mobile apps
3. Handle Token Expiration
Implement automatic token refresh before the token expires:
const getAccessToken = async (): Promise<string | null> => {
const token = localStorage.getItem('accessToken');
if (token && willExpireSoon(token)) {
return await refreshAccessToken();
}
return token;
};
function willExpireSoon(token: string, bufferSeconds = 60): boolean {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp * 1000 < Date.now() + (bufferSeconds * 1000);
} catch {
return true;
}
}4. Global Error Handling
Set up global error handling for authentication errors:
import { ApiError } from '@deliverart/sdk-js-error-handler';
window.addEventListener('unhandledrejection', (event) => {
if (event.reason instanceof ApiError && event.reason.statusCode === 401) {
// Handle unauthorized errors globally
clearTokens();
window.location.href = '/login';
}
});License
MIT
