@rationalbloks/frontblok-auth 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # @rationalbloks/frontblok-auth
2
+
3
+ **Universal Frontend Authentication for RationalBloks Apps**
4
+
5
+ Pure authentication, API, and token management. **NO STYLING** - each template app provides its own aesthetics.
6
+
7
+ ---
8
+
9
+ ## 🎯 Philosophy
10
+
11
+ ```
12
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
13
+ β”‚ FRONTBLOK-AUTH (npm package) β”‚
14
+ β”‚ β”‚
15
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
16
+ β”‚ β”‚ AUTH β”‚ β”‚ API β”‚ β”‚ PROVIDERS β”‚ β”‚
17
+ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
18
+ β”‚ β”‚ β€’ Tokens β”‚ β”‚ β€’ BaseApi β”‚ β”‚ β€’ createAppRoot β”‚ β”‚
19
+ β”‚ β”‚ β€’ Storage β”‚ β”‚ β€’ Client β”‚ β”‚ β€’ createAuthProvider β”‚ β”‚
20
+ β”‚ β”‚ β€’ Context β”‚ β”‚ β€’ Types β”‚ β”‚ β€’ useAuth β”‚ β”‚
21
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
22
+ β”‚ β”‚
23
+ β”‚ ↓ NO STYLING ↓ β”‚
24
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
25
+ β”‚
26
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
27
+ β”‚ β”‚ β”‚
28
+ β–Ό β–Ό β–Ό
29
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
30
+ β”‚ Template #1 β”‚ β”‚ Template #2 β”‚ β”‚ Template #3 β”‚
31
+ β”‚ (rationalbloksβ”‚ β”‚ (Investment β”‚ β”‚ (E-commerce β”‚
32
+ β”‚ front) β”‚ β”‚ Portfolio) β”‚ β”‚ Template) β”‚
33
+ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
34
+ β”‚ βœ“ Own theme β”‚ β”‚ βœ“ Own theme β”‚ β”‚ βœ“ Own theme β”‚
35
+ β”‚ βœ“ Own styles β”‚ β”‚ βœ“ Own styles β”‚ β”‚ βœ“ Own styles β”‚
36
+ β”‚ βœ“ Own Navbar β”‚ β”‚ βœ“ Own Navbar β”‚ β”‚ βœ“ Own Navbar β”‚
37
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
38
+ ```
39
+
40
+ ---
41
+
42
+ ## πŸ“¦ Installation
43
+
44
+ ### For Published Package (Production)
45
+ ```bash
46
+ npm install @rationalbloks/frontblok-auth
47
+ ```
48
+
49
+ ### For Local Development (npm link)
50
+ ```bash
51
+ # In frontblok-auth directory
52
+ cd frontblok-auth
53
+ npm install
54
+ npm run build
55
+ npm link
56
+
57
+ # In your app directory
58
+ cd your-app
59
+ npm link @rationalbloks/frontblok-auth
60
+ ```
61
+
62
+ ---
63
+
64
+ ## πŸš€ Usage
65
+
66
+ ### 1. Bootstrap Your App
67
+
68
+ ```tsx
69
+ // main.tsx
70
+ import { createAppRoot } from '@rationalbloks/frontblok-auth';
71
+ import './styles/globals.css'; // YOUR app's styling
72
+ import App from './App';
73
+
74
+ createAppRoot(App, {
75
+ googleClientId: import.meta.env.VITE_GOOGLE_CLIENT_ID || '',
76
+ });
77
+ ```
78
+
79
+ ### 2. Create Your Auth Provider
80
+
81
+ ```tsx
82
+ // contexts/ClientAuthContext.tsx
83
+ import { createAuthProvider, useAuth } from '@rationalbloks/frontblok-auth';
84
+ import { myApi } from '../services/myApi';
85
+
86
+ export const ClientAuthProvider = createAuthProvider(myApi);
87
+ export const useClientAuth = useAuth;
88
+ ```
89
+
90
+ ### 3. Extend BaseApi for Your App
91
+
92
+ ```tsx
93
+ // services/myApi.ts
94
+ import { BaseApi, getStoredUser, getStoredToken, isAuthenticated } from '@rationalbloks/frontblok-auth';
95
+ import type { User } from '@rationalbloks/frontblok-auth';
96
+
97
+ // Re-export utilities
98
+ export { getStoredUser, getStoredToken, isAuthenticated };
99
+ export type { User };
100
+
101
+ // Your custom endpoints
102
+ class MyAppApi extends BaseApi {
103
+ async getProjects() {
104
+ return this.get<Project[]>('/projects');
105
+ }
106
+
107
+ async createProject(data: CreateProjectDTO) {
108
+ return this.post<Project>('/projects', data);
109
+ }
110
+ }
111
+
112
+ export const myApi = new MyAppApi({
113
+ baseURL: import.meta.env.VITE_API_URL,
114
+ });
115
+ ```
116
+
117
+ ---
118
+
119
+ ## πŸ“š API Reference
120
+
121
+ ### Auth Exports
122
+
123
+ | Export | Description |
124
+ |--------|-------------|
125
+ | `createAppRoot(App, config)` | Bootstrap app with Google OAuth provider |
126
+ | `createAuthProvider(api)` | Create auth context provider for your API |
127
+ | `useAuth()` | Hook to access auth state and actions |
128
+
129
+ ### API Exports
130
+
131
+ | Export | Description |
132
+ |--------|-------------|
133
+ | `BaseApi` | Extensible API client with token management |
134
+ | `createAuthApi(baseUrl)` | Create pre-configured auth API instance |
135
+ | `getStoredUser()` | Get user from localStorage |
136
+ | `getStoredToken()` | Get token from localStorage |
137
+ | `isAuthenticated()` | Check if user is logged in |
138
+
139
+ ---
140
+
141
+ ## πŸ”§ Development
142
+
143
+ ```bash
144
+ # Install dependencies
145
+ npm install
146
+
147
+ # Type check
148
+ npm run typecheck
149
+
150
+ # Build library
151
+ npm run build
152
+
153
+ # Outputs:
154
+ # dist/index.js - ESM bundle
155
+ # dist/index.cjs - CommonJS bundle
156
+ # dist/index.d.ts - TypeScript declarations
157
+ ```
158
+
159
+ ---
160
+
161
+ ## πŸ“‹ Peer Dependencies
162
+
163
+ This package requires these dependencies in your app:
164
+
165
+ ```json
166
+ {
167
+ "react": "^18.0.0 || ^19.0.0",
168
+ "react-dom": "^18.0.0 || ^19.0.0",
169
+ "react-router-dom": "^6.0.0 || ^7.0.0",
170
+ "@react-oauth/google": "^0.12.0"
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ## πŸ—οΈ Architecture for New Template Apps
177
+
178
+ When creating a new template app that uses frontblok-auth:
179
+
180
+ 1. **Install the package**
181
+ ```bash
182
+ npm install @rationalbloks/frontblok-auth
183
+ ```
184
+
185
+ 2. **Create your own styling** (theme/, styles/)
186
+ 3. **Create your own components** (style them yourself)
187
+ 4. **Create your own API service** (extend BaseApi)
188
+ 5. **Use createAuthProvider** with your API
189
+ 6. **Bootstrap with createAppRoot**
190
+
191
+ ---
192
+
193
+ ## πŸ“ License
194
+
195
+ MIT Β© RationalBloks Team
@@ -0,0 +1,119 @@
1
+ import type { User, ApiKey, ApiKeyCreateResponse, AuthResponse } from './types';
2
+ /**
3
+ * BaseApi - Universal HTTP client with authentication.
4
+ *
5
+ * Extend this class to add your application-specific API methods.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * class MyAppApi extends BaseApi {
10
+ * constructor() {
11
+ * super(import.meta.env.VITE_API_URL || 'https://api.myapp.com');
12
+ * }
13
+ *
14
+ * async getProducts() {
15
+ * return this.request<Product[]>('/api/products/');
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+ export declare class BaseApi {
21
+ protected token: string | null;
22
+ protected refreshToken: string | null;
23
+ protected isRefreshing: boolean;
24
+ protected refreshPromise: Promise<boolean> | null;
25
+ protected proactiveRefreshTimer: ReturnType<typeof setTimeout> | null;
26
+ protected refreshCheckInterval: ReturnType<typeof setInterval> | null;
27
+ protected tokenRefreshPromise: Promise<void> | null;
28
+ protected readonly apiBaseUrl: string;
29
+ constructor(apiBaseUrl: string);
30
+ /**
31
+ * Sync tokens across browser tabs when localStorage changes.
32
+ * Prevents "stale refresh token" issue where Tab A rotates the token
33
+ * but Tab B still has the old (now invalid) refresh token in memory.
34
+ */
35
+ private setupStorageListener;
36
+ /**
37
+ * Start interval-based token check (runs every 60 seconds).
38
+ * More reliable than setTimeout which browsers throttle in background tabs.
39
+ */
40
+ private startRefreshCheckInterval;
41
+ /**
42
+ * Handle tab visibility changes - check token when user returns to tab.
43
+ */
44
+ private setupVisibilityHandler;
45
+ /**
46
+ * Check token expiry and refresh if needed.
47
+ */
48
+ protected checkAndRefreshToken(): Promise<void>;
49
+ /**
50
+ * Parse JWT to get expiration time.
51
+ */
52
+ protected getTokenExpiry(token: string): number | null;
53
+ /**
54
+ * Schedule proactive refresh 5 minutes before token expires.
55
+ */
56
+ protected scheduleProactiveRefresh(): void;
57
+ /**
58
+ * Load tokens from localStorage on initialization.
59
+ */
60
+ protected loadTokens(): void;
61
+ /**
62
+ * Wait for any pending token refresh to complete before making API calls.
63
+ */
64
+ ensureTokenReady(): Promise<void>;
65
+ /**
66
+ * Refresh the access token using the refresh token.
67
+ */
68
+ refreshAccessToken(): Promise<boolean>;
69
+ /**
70
+ * Make an authenticated HTTP request.
71
+ * Handles token refresh, 401 retry, and rate limiting automatically.
72
+ *
73
+ * This method is public so apps can make custom API calls without extending BaseApi.
74
+ *
75
+ * @example
76
+ * const authApi = createAuthApi(API_URL);
77
+ * const data = await authApi.request<MyType>('/api/my-endpoint/', { method: 'POST', body: JSON.stringify(payload) });
78
+ */
79
+ request<T>(endpoint: string, options?: RequestInit, isRetry?: boolean, retryCount?: number): Promise<T>;
80
+ login(email: string, password: string): Promise<AuthResponse>;
81
+ register(email: string, password: string, firstName: string, lastName: string): Promise<AuthResponse>;
82
+ getMe(): Promise<{
83
+ user: User;
84
+ }>;
85
+ getCurrentUser(): Promise<User>;
86
+ deleteAccount(password: string, confirmText: string): Promise<{
87
+ message: string;
88
+ note: string;
89
+ }>;
90
+ logout(): Promise<void>;
91
+ logoutAllDevices(): Promise<void>;
92
+ protected clearAuth(): void;
93
+ listApiKeys(): Promise<{
94
+ api_keys: ApiKey[];
95
+ total: number;
96
+ }>;
97
+ createApiKey(data: {
98
+ name: string;
99
+ scopes?: string;
100
+ expires_in_days?: number;
101
+ rate_limit_per_minute?: number;
102
+ }): Promise<ApiKeyCreateResponse>;
103
+ revokeApiKey(keyId: string): Promise<{
104
+ message: string;
105
+ key_prefix: string;
106
+ revoked_at: string;
107
+ }>;
108
+ }
109
+ /**
110
+ * Creates an auth API client instance.
111
+ * Preferred over class inheritance - simpler and cleaner.
112
+ *
113
+ * @param apiBaseUrl - The backend API base URL
114
+ * @returns A BaseApi instance with all auth methods
115
+ */
116
+ export declare function createAuthApi(apiBaseUrl: string): BaseApi;
117
+ export declare const getStoredUser: () => User | null;
118
+ export declare const getStoredToken: () => string | null;
119
+ export declare const isAuthenticated: () => boolean;
@@ -0,0 +1,2 @@
1
+ export { BaseApi, createAuthApi, getStoredUser, getStoredToken, isAuthenticated } from './client';
2
+ export type { User, ApiKey, ApiKeyCreateResponse, AuthResponse } from './types';
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Authenticated user interface.
3
+ * Represents the user data returned from authentication endpoints.
4
+ */
5
+ export interface User {
6
+ id: string;
7
+ email: string;
8
+ first_name: string;
9
+ last_name: string;
10
+ full_name: string;
11
+ role: string;
12
+ is_active: boolean;
13
+ is_admin: boolean;
14
+ created_at: string;
15
+ last_login?: string;
16
+ oauth_provider?: string | null;
17
+ has_google_linked?: boolean;
18
+ }
19
+ /**
20
+ * API Key interface for MCP servers and external integrations.
21
+ */
22
+ export interface ApiKey {
23
+ id: string;
24
+ name: string;
25
+ key_prefix: string;
26
+ scopes: string;
27
+ rate_limit_per_minute: number;
28
+ expires_at: string | null;
29
+ last_used_at: string | null;
30
+ created_at: string;
31
+ }
32
+ /**
33
+ * Response from creating a new API key.
34
+ * Includes the full key (only shown once).
35
+ */
36
+ export interface ApiKeyCreateResponse {
37
+ api_key: string;
38
+ id: string;
39
+ name: string;
40
+ key_prefix: string;
41
+ scopes: string;
42
+ rate_limit_per_minute: number;
43
+ expires_at: string | null;
44
+ created_at: string;
45
+ message: string;
46
+ }
47
+ /**
48
+ * Login/Register response with tokens and user data.
49
+ */
50
+ export interface AuthResponse {
51
+ access_token: string;
52
+ refresh_token?: string;
53
+ user: User;
54
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ export interface AuthConfig {
3
+ /** Google OAuth Client ID (optional - only needed if using Google Sign-In) */
4
+ googleClientId?: string;
5
+ }
6
+ /**
7
+ * Creates and renders the app root with authentication providers.
8
+ * This is the universal way to bootstrap a React app with auth.
9
+ */
10
+ export declare function createAppRoot(App: React.ComponentType, config?: AuthConfig): void;
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import type { User } from '../api';
3
+ import type { BaseApi } from '../api/client';
4
+ export interface AuthState {
5
+ user: User | null;
6
+ isLoading: boolean;
7
+ isAuthenticated: boolean;
8
+ error: string | null;
9
+ }
10
+ export interface AuthActions {
11
+ login: (email: string, password: string) => Promise<boolean>;
12
+ register: (email: string, password: string, firstName: string, lastName: string) => Promise<boolean>;
13
+ logout: () => void;
14
+ clearError: () => void;
15
+ refreshUser: () => Promise<void>;
16
+ }
17
+ export type AuthContextType = AuthState & AuthActions;
18
+ /**
19
+ * Hook to access authentication state and actions.
20
+ * Must be used within an AuthProvider.
21
+ */
22
+ export declare const useAuth: () => AuthContextType;
23
+ /**
24
+ * Creates an AuthProvider component that uses the specified API instance.
25
+ * This allows the universal auth context to work with any API that extends BaseApi.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * // In your app's auth setup:
30
+ * import { createAuthProvider } from '@/core/auth';
31
+ * import { myAppApi } from '@/services/myAppApi';
32
+ *
33
+ * export const MyAppAuthProvider = createAuthProvider(myAppApi);
34
+ * ```
35
+ */
36
+ export declare function createAuthProvider(api: BaseApi): React.FC<{
37
+ children: React.ReactNode;
38
+ }>;
@@ -0,0 +1,4 @@
1
+ export { useAuth, createAuthProvider } from './AuthContext';
2
+ export type { AuthState, AuthActions, AuthContextType } from './AuthContext';
3
+ export { createAppRoot } from './AppProvider';
4
+ export type { AuthConfig } from './AppProvider';