@armco/iam-client 0.1.1

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,181 @@
1
+ # @armco/iam-client
2
+
3
+ Browser/SPA client for IAM - OIDC/OAuth2 authentication with PKCE.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @armco/iam-client
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### Vanilla JavaScript/TypeScript
14
+
15
+ ```typescript
16
+ import { createIAMClient } from '@armco/iam-client';
17
+
18
+ const iam = createIAMClient({
19
+ issuer: 'http://localhost:5000',
20
+ clientId: 'my-app',
21
+ redirectUri: 'http://localhost:3000/callback',
22
+ scopes: ['openid', 'profile', 'email', 'offline_access'],
23
+ });
24
+
25
+ // Login
26
+ document.getElementById('login-btn').onclick = () => iam.login();
27
+
28
+ // Signup
29
+ document.getElementById('signup-btn').onclick = () => iam.signup();
30
+
31
+ // Handle callback (on /callback page)
32
+ const result = await iam.handleCallback();
33
+ if (result.success) {
34
+ console.log('Logged in:', result.user);
35
+ } else {
36
+ console.error('Login failed:', result.error);
37
+ }
38
+
39
+ // Get current user
40
+ const user = iam.getUser();
41
+
42
+ // Get access token (auto-refreshes if needed)
43
+ const token = await iam.getAccessToken();
44
+
45
+ // Logout
46
+ await iam.logout();
47
+ ```
48
+
49
+ ### React
50
+
51
+ ```tsx
52
+ import { IAMProvider, useAuth } from '@armco/iam-client/react';
53
+
54
+ // Wrap your app
55
+ function App() {
56
+ return (
57
+ <IAMProvider
58
+ issuer="http://localhost:5000"
59
+ clientId="my-app"
60
+ redirectUri="http://localhost:3000/callback"
61
+ onLoginSuccess={(result) => {
62
+ console.log('Logged in:', result.user);
63
+ // Navigate to dashboard
64
+ }}
65
+ >
66
+ <Router>
67
+ <Routes>
68
+ <Route path="/" element={<Home />} />
69
+ <Route path="/callback" element={<Callback />} />
70
+ <Route path="/dashboard" element={<Dashboard />} />
71
+ </Routes>
72
+ </Router>
73
+ </StuffleIAMProvider>
74
+ );
75
+ }
76
+
77
+ // Use the hook
78
+ function Home() {
79
+ const { isAuthenticated, isLoading, user, login, signup, logout } = useAuth();
80
+
81
+ if (isLoading) return <div>Loading...</div>;
82
+
83
+ if (!isAuthenticated) {
84
+ return (
85
+ <div>
86
+ <button onClick={() => login()}>Login</button>
87
+ <button onClick={() => signup()}>Sign Up</button>
88
+ </div>
89
+ );
90
+ }
91
+
92
+ return (
93
+ <div>
94
+ <p>Welcome, {user?.name || user?.email}</p>
95
+ <button onClick={() => logout()}>Logout</button>
96
+ </div>
97
+ );
98
+ }
99
+
100
+ // Callback page (auto-handled by provider)
101
+ function Callback() {
102
+ const { isLoading, error } = useAuth();
103
+
104
+ if (isLoading) return <div>Processing login...</div>;
105
+ if (error) return <div>Error: {error.message}</div>;
106
+
107
+ return <Navigate to="/dashboard" />;
108
+ }
109
+ ```
110
+
111
+ ## Configuration
112
+
113
+ ```typescript
114
+ interface StuffleIAMConfig {
115
+ /** IAM server base URL */
116
+ issuer: string;
117
+ /** OAuth2 client ID */
118
+ clientId: string;
119
+ /** Redirect URI after login */
120
+ redirectUri: string;
121
+ /** Requested scopes (default: openid profile email) */
122
+ scopes?: string[];
123
+ /** Post-logout redirect URI */
124
+ postLogoutRedirectUri?: string;
125
+ /** Enable PKCE (default: true) */
126
+ usePkce?: boolean;
127
+ /** Storage type (default: sessionStorage) */
128
+ storage?: 'localStorage' | 'sessionStorage' | 'memory';
129
+ /** Auto-refresh tokens (default: true) */
130
+ autoRefresh?: boolean;
131
+ /** Seconds before expiry to refresh (default: 60) */
132
+ refreshThreshold?: number;
133
+ }
134
+ ```
135
+
136
+ ## React Hooks
137
+
138
+ | Hook | Description |
139
+ |------|-------------|
140
+ | `useAuth()` | Full auth context (user, login, logout, etc.) |
141
+ | `useUser()` | Current user object |
142
+ | `useIsAuthenticated()` | Boolean auth status |
143
+ | `useAccessToken()` | Function to get access token |
144
+ | `useHasRole(roles)` | Check if user has role(s) |
145
+
146
+ ## Protected Routes (HOC)
147
+
148
+ ```tsx
149
+ import { withAuth } from '@armco/iam-client/react';
150
+
151
+ const ProtectedPage = withAuth(MyComponent, {
152
+ LoadingComponent: () => <div>Loading...</div>,
153
+ UnauthorizedComponent: () => <div>Please login</div>,
154
+ roles: ['admin'], // Optional: require specific roles
155
+ });
156
+ ```
157
+
158
+ ## API Reference
159
+
160
+ ### StuffleIAMClient Methods
161
+
162
+ | Method | Description |
163
+ |--------|-------------|
164
+ | `login(options?)` | Start login flow |
165
+ | `signup(options?)` | Start signup flow |
166
+ | `logout(options?)` | End session |
167
+ | `handleCallback(url?)` | Process OAuth callback |
168
+ | `getUser()` | Get current user from ID token |
169
+ | `getAccessToken()` | Get access token (refreshes if needed) |
170
+ | `isAuthenticated()` | Check if user is logged in |
171
+ | `refreshToken()` | Manually refresh tokens |
172
+ | `subscribe(listener)` | Subscribe to auth state changes |
173
+
174
+ ## Development
175
+
176
+ ```bash
177
+ cd packages/sdk-js
178
+ npm install
179
+ npm run build
180
+ npm run dev # Watch mode
181
+ ```
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Stuffle IAM SDK Types
3
+ */
4
+ interface StuffleIAMConfig {
5
+ /** IAM server base URL (e.g., http://localhost:5000) */
6
+ issuer: string;
7
+ /** OAuth2 client ID */
8
+ clientId: string;
9
+ /** Redirect URI after login */
10
+ redirectUri: string;
11
+ /** Requested scopes (default: openid profile email) */
12
+ scopes?: string[];
13
+ /** Post-logout redirect URI */
14
+ postLogoutRedirectUri?: string;
15
+ /** Enable PKCE (default: true, recommended for SPAs) */
16
+ usePkce?: boolean;
17
+ /** Storage type for tokens (default: sessionStorage) */
18
+ storage?: 'localStorage' | 'sessionStorage' | 'memory';
19
+ /** Auto-refresh tokens before expiry (default: true) */
20
+ autoRefresh?: boolean;
21
+ /** Seconds before expiry to trigger refresh (default: 60) */
22
+ refreshThreshold?: number;
23
+ }
24
+ interface TokenResponse {
25
+ access_token: string;
26
+ token_type: string;
27
+ expires_in: number;
28
+ refresh_token?: string;
29
+ id_token?: string;
30
+ scope?: string;
31
+ }
32
+ interface User {
33
+ sub: string;
34
+ email?: string;
35
+ email_verified?: boolean;
36
+ name?: string;
37
+ given_name?: string;
38
+ family_name?: string;
39
+ picture?: string;
40
+ username?: string;
41
+ roles?: string[];
42
+ tenantId?: string;
43
+ [key: string]: unknown;
44
+ }
45
+ interface AuthState {
46
+ isAuthenticated: boolean;
47
+ isLoading: boolean;
48
+ user: User | null;
49
+ accessToken: string | null;
50
+ idToken: string | null;
51
+ error: Error | null;
52
+ }
53
+ interface LoginOptions {
54
+ /** Additional scopes to request */
55
+ scopes?: string[];
56
+ /** State parameter (auto-generated if not provided) */
57
+ state?: string;
58
+ /** Nonce for ID token validation */
59
+ nonce?: string;
60
+ /** Redirect to signup instead of login */
61
+ signup?: boolean;
62
+ /** Prompt parameter (none, login, consent, select_account) */
63
+ prompt?: 'none' | 'login' | 'consent' | 'select_account';
64
+ /** Login hint (email or username) */
65
+ loginHint?: string;
66
+ }
67
+ interface LogoutOptions {
68
+ /** Post-logout redirect URI */
69
+ returnTo?: string;
70
+ /** Include id_token_hint in logout request */
71
+ idTokenHint?: string;
72
+ }
73
+ interface CallbackResult {
74
+ success: boolean;
75
+ user?: User;
76
+ accessToken?: string;
77
+ idToken?: string;
78
+ refreshToken?: string;
79
+ error?: string;
80
+ errorDescription?: string;
81
+ }
82
+ interface OIDCDiscovery {
83
+ issuer: string;
84
+ authorization_endpoint: string;
85
+ token_endpoint: string;
86
+ userinfo_endpoint: string;
87
+ jwks_uri: string;
88
+ end_session_endpoint?: string;
89
+ registration_endpoint?: string;
90
+ scopes_supported: string[];
91
+ response_types_supported: string[];
92
+ grant_types_supported: string[];
93
+ token_endpoint_auth_methods_supported: string[];
94
+ code_challenge_methods_supported?: string[];
95
+ }
96
+
97
+ /**
98
+ * Stuffle IAM Client
99
+ *
100
+ * OIDC/OAuth2 client for browser-based applications.
101
+ * Supports authorization code flow with PKCE.
102
+ */
103
+
104
+ declare class StuffleIAMClient {
105
+ private config;
106
+ private storage;
107
+ private discovery;
108
+ private refreshTimer;
109
+ private listeners;
110
+ constructor(config: StuffleIAMConfig);
111
+ /**
112
+ * Fetch OIDC discovery document
113
+ */
114
+ getDiscovery(): Promise<OIDCDiscovery>;
115
+ /**
116
+ * Start login flow - redirects to authorization endpoint
117
+ */
118
+ login(options?: LoginOptions): Promise<void>;
119
+ /**
120
+ * Alias for login({ signup: true })
121
+ */
122
+ signup(options?: Omit<LoginOptions, 'signup'>): Promise<void>;
123
+ /**
124
+ * Handle callback from authorization server
125
+ */
126
+ handleCallback(url?: string): Promise<CallbackResult>;
127
+ /**
128
+ * Exchange authorization code for tokens
129
+ */
130
+ private exchangeCode;
131
+ /**
132
+ * Refresh access token using refresh token
133
+ */
134
+ refreshToken(): Promise<TokenResponse | null>;
135
+ /**
136
+ * Logout - end session
137
+ */
138
+ logout(options?: LogoutOptions): Promise<void>;
139
+ /**
140
+ * Get current access token (refreshes if needed)
141
+ */
142
+ getAccessToken(): Promise<string | null>;
143
+ /**
144
+ * Get current user from stored ID token
145
+ */
146
+ getUser(): User | null;
147
+ /**
148
+ * Fetch user info from userinfo endpoint
149
+ */
150
+ fetchUserInfo(accessToken?: string): Promise<User | null>;
151
+ /**
152
+ * Check if user is authenticated
153
+ */
154
+ isAuthenticated(): boolean;
155
+ /**
156
+ * Get current auth state
157
+ */
158
+ getAuthState(): AuthState;
159
+ /**
160
+ * Subscribe to auth state changes
161
+ */
162
+ subscribe(listener: (state: AuthState) => void): () => void;
163
+ /**
164
+ * Store tokens in storage
165
+ */
166
+ private storeTokens;
167
+ /**
168
+ * Clear all stored tokens
169
+ */
170
+ private clearTokens;
171
+ /**
172
+ * Setup auto-refresh timer
173
+ */
174
+ private setupAutoRefresh;
175
+ /**
176
+ * Notify all listeners of state change
177
+ */
178
+ private notifyListeners;
179
+ }
180
+ /**
181
+ * Create a new Stuffle IAM client instance
182
+ */
183
+ declare function createStuffleIAMClient(config: StuffleIAMConfig): StuffleIAMClient;
184
+
185
+ export { type AuthState as A, type CallbackResult as C, type LoginOptions as L, type OIDCDiscovery as O, StuffleIAMClient as S, type TokenResponse as T, type User as U, type StuffleIAMConfig as a, type LogoutOptions as b, createStuffleIAMClient as c };
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Stuffle IAM SDK Types
3
+ */
4
+ interface StuffleIAMConfig {
5
+ /** IAM server base URL (e.g., http://localhost:5000) */
6
+ issuer: string;
7
+ /** OAuth2 client ID */
8
+ clientId: string;
9
+ /** Redirect URI after login */
10
+ redirectUri: string;
11
+ /** Requested scopes (default: openid profile email) */
12
+ scopes?: string[];
13
+ /** Post-logout redirect URI */
14
+ postLogoutRedirectUri?: string;
15
+ /** Enable PKCE (default: true, recommended for SPAs) */
16
+ usePkce?: boolean;
17
+ /** Storage type for tokens (default: sessionStorage) */
18
+ storage?: 'localStorage' | 'sessionStorage' | 'memory';
19
+ /** Auto-refresh tokens before expiry (default: true) */
20
+ autoRefresh?: boolean;
21
+ /** Seconds before expiry to trigger refresh (default: 60) */
22
+ refreshThreshold?: number;
23
+ }
24
+ interface TokenResponse {
25
+ access_token: string;
26
+ token_type: string;
27
+ expires_in: number;
28
+ refresh_token?: string;
29
+ id_token?: string;
30
+ scope?: string;
31
+ }
32
+ interface User {
33
+ sub: string;
34
+ email?: string;
35
+ email_verified?: boolean;
36
+ name?: string;
37
+ given_name?: string;
38
+ family_name?: string;
39
+ picture?: string;
40
+ username?: string;
41
+ roles?: string[];
42
+ tenantId?: string;
43
+ [key: string]: unknown;
44
+ }
45
+ interface AuthState {
46
+ isAuthenticated: boolean;
47
+ isLoading: boolean;
48
+ user: User | null;
49
+ accessToken: string | null;
50
+ idToken: string | null;
51
+ error: Error | null;
52
+ }
53
+ interface LoginOptions {
54
+ /** Additional scopes to request */
55
+ scopes?: string[];
56
+ /** State parameter (auto-generated if not provided) */
57
+ state?: string;
58
+ /** Nonce for ID token validation */
59
+ nonce?: string;
60
+ /** Redirect to signup instead of login */
61
+ signup?: boolean;
62
+ /** Prompt parameter (none, login, consent, select_account) */
63
+ prompt?: 'none' | 'login' | 'consent' | 'select_account';
64
+ /** Login hint (email or username) */
65
+ loginHint?: string;
66
+ }
67
+ interface LogoutOptions {
68
+ /** Post-logout redirect URI */
69
+ returnTo?: string;
70
+ /** Include id_token_hint in logout request */
71
+ idTokenHint?: string;
72
+ }
73
+ interface CallbackResult {
74
+ success: boolean;
75
+ user?: User;
76
+ accessToken?: string;
77
+ idToken?: string;
78
+ refreshToken?: string;
79
+ error?: string;
80
+ errorDescription?: string;
81
+ }
82
+ interface OIDCDiscovery {
83
+ issuer: string;
84
+ authorization_endpoint: string;
85
+ token_endpoint: string;
86
+ userinfo_endpoint: string;
87
+ jwks_uri: string;
88
+ end_session_endpoint?: string;
89
+ registration_endpoint?: string;
90
+ scopes_supported: string[];
91
+ response_types_supported: string[];
92
+ grant_types_supported: string[];
93
+ token_endpoint_auth_methods_supported: string[];
94
+ code_challenge_methods_supported?: string[];
95
+ }
96
+
97
+ /**
98
+ * Stuffle IAM Client
99
+ *
100
+ * OIDC/OAuth2 client for browser-based applications.
101
+ * Supports authorization code flow with PKCE.
102
+ */
103
+
104
+ declare class StuffleIAMClient {
105
+ private config;
106
+ private storage;
107
+ private discovery;
108
+ private refreshTimer;
109
+ private listeners;
110
+ constructor(config: StuffleIAMConfig);
111
+ /**
112
+ * Fetch OIDC discovery document
113
+ */
114
+ getDiscovery(): Promise<OIDCDiscovery>;
115
+ /**
116
+ * Start login flow - redirects to authorization endpoint
117
+ */
118
+ login(options?: LoginOptions): Promise<void>;
119
+ /**
120
+ * Alias for login({ signup: true })
121
+ */
122
+ signup(options?: Omit<LoginOptions, 'signup'>): Promise<void>;
123
+ /**
124
+ * Handle callback from authorization server
125
+ */
126
+ handleCallback(url?: string): Promise<CallbackResult>;
127
+ /**
128
+ * Exchange authorization code for tokens
129
+ */
130
+ private exchangeCode;
131
+ /**
132
+ * Refresh access token using refresh token
133
+ */
134
+ refreshToken(): Promise<TokenResponse | null>;
135
+ /**
136
+ * Logout - end session
137
+ */
138
+ logout(options?: LogoutOptions): Promise<void>;
139
+ /**
140
+ * Get current access token (refreshes if needed)
141
+ */
142
+ getAccessToken(): Promise<string | null>;
143
+ /**
144
+ * Get current user from stored ID token
145
+ */
146
+ getUser(): User | null;
147
+ /**
148
+ * Fetch user info from userinfo endpoint
149
+ */
150
+ fetchUserInfo(accessToken?: string): Promise<User | null>;
151
+ /**
152
+ * Check if user is authenticated
153
+ */
154
+ isAuthenticated(): boolean;
155
+ /**
156
+ * Get current auth state
157
+ */
158
+ getAuthState(): AuthState;
159
+ /**
160
+ * Subscribe to auth state changes
161
+ */
162
+ subscribe(listener: (state: AuthState) => void): () => void;
163
+ /**
164
+ * Store tokens in storage
165
+ */
166
+ private storeTokens;
167
+ /**
168
+ * Clear all stored tokens
169
+ */
170
+ private clearTokens;
171
+ /**
172
+ * Setup auto-refresh timer
173
+ */
174
+ private setupAutoRefresh;
175
+ /**
176
+ * Notify all listeners of state change
177
+ */
178
+ private notifyListeners;
179
+ }
180
+ /**
181
+ * Create a new Stuffle IAM client instance
182
+ */
183
+ declare function createStuffleIAMClient(config: StuffleIAMConfig): StuffleIAMClient;
184
+
185
+ export { type AuthState as A, type CallbackResult as C, type LoginOptions as L, type OIDCDiscovery as O, StuffleIAMClient as S, type TokenResponse as T, type User as U, type StuffleIAMConfig as a, type LogoutOptions as b, createStuffleIAMClient as c };
@@ -0,0 +1,41 @@
1
+ export { A as AuthState, C as CallbackResult, L as LoginOptions, b as LogoutOptions, O as OIDCDiscovery, S as StuffleIAMClient, a as StuffleIAMConfig, T as TokenResponse, U as User, c as createStuffleIAMClient } from './client-CTKWBZ26.mjs';
2
+
3
+ /**
4
+ * Utility functions for PKCE and crypto operations
5
+ */
6
+ /**
7
+ * Generate a random string for state/nonce
8
+ */
9
+ declare function generateRandomString(length?: number): string;
10
+ /**
11
+ * Generate PKCE code verifier (43-128 characters)
12
+ */
13
+ declare function generateCodeVerifier(): string;
14
+ /**
15
+ * Generate PKCE code challenge from verifier
16
+ */
17
+ declare function generateCodeChallenge(verifier: string): Promise<string>;
18
+ /**
19
+ * Decode JWT payload (without verification)
20
+ */
21
+ declare function decodeJwtPayload<T = Record<string, unknown>>(token: string): T | null;
22
+ /**
23
+ * Check if token is expired
24
+ */
25
+ declare function isTokenExpired(token: string, thresholdSeconds?: number): boolean;
26
+
27
+ /**
28
+ * Token storage abstraction
29
+ */
30
+ interface TokenStorage {
31
+ get(key: string): string | null;
32
+ set(key: string, value: string): void;
33
+ remove(key: string): void;
34
+ clear(): void;
35
+ }
36
+ /**
37
+ * Get storage adapter based on type
38
+ */
39
+ declare function getStorage(type: 'localStorage' | 'sessionStorage' | 'memory'): TokenStorage;
40
+
41
+ export { type TokenStorage, decodeJwtPayload, generateCodeChallenge, generateCodeVerifier, generateRandomString, getStorage, isTokenExpired };
@@ -0,0 +1,41 @@
1
+ export { A as AuthState, C as CallbackResult, L as LoginOptions, b as LogoutOptions, O as OIDCDiscovery, S as StuffleIAMClient, a as StuffleIAMConfig, T as TokenResponse, U as User, c as createStuffleIAMClient } from './client-CTKWBZ26.js';
2
+
3
+ /**
4
+ * Utility functions for PKCE and crypto operations
5
+ */
6
+ /**
7
+ * Generate a random string for state/nonce
8
+ */
9
+ declare function generateRandomString(length?: number): string;
10
+ /**
11
+ * Generate PKCE code verifier (43-128 characters)
12
+ */
13
+ declare function generateCodeVerifier(): string;
14
+ /**
15
+ * Generate PKCE code challenge from verifier
16
+ */
17
+ declare function generateCodeChallenge(verifier: string): Promise<string>;
18
+ /**
19
+ * Decode JWT payload (without verification)
20
+ */
21
+ declare function decodeJwtPayload<T = Record<string, unknown>>(token: string): T | null;
22
+ /**
23
+ * Check if token is expired
24
+ */
25
+ declare function isTokenExpired(token: string, thresholdSeconds?: number): boolean;
26
+
27
+ /**
28
+ * Token storage abstraction
29
+ */
30
+ interface TokenStorage {
31
+ get(key: string): string | null;
32
+ set(key: string, value: string): void;
33
+ remove(key: string): void;
34
+ clear(): void;
35
+ }
36
+ /**
37
+ * Get storage adapter based on type
38
+ */
39
+ declare function getStorage(type: 'localStorage' | 'sessionStorage' | 'memory'): TokenStorage;
40
+
41
+ export { type TokenStorage, decodeJwtPayload, generateCodeChallenge, generateCodeVerifier, generateRandomString, getStorage, isTokenExpired };