@savant-realms/federated-auth-realm-sdk-ts-js 0.4.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 +120 -0
- package/dist/client.d.ts +45 -0
- package/dist/client.js +191 -0
- package/dist/discovery.d.ts +23 -0
- package/dist/discovery.js +32 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +21 -0
- package/dist/storage/index.d.ts +16 -0
- package/dist/storage/index.js +37 -0
- package/dist/types/index.d.ts +272 -0
- package/dist/types/index.js +38 -0
- package/dist/utils/jwt.d.ts +5 -0
- package/dist/utils/jwt.js +29 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Federated Auth Realm TS/JS SDK
|
|
2
|
+
|
|
3
|
+
A comprehensive TypeScript/JavaScript SDK for the Federated Auth Realm, enabling easy integration of multi-app authentication and authorization.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Built-in Session Management**: Handles `accessToken` and `refreshToken` automatically.
|
|
8
|
+
- **Axios Interceptors**: Automatically injects Bearer tokens and handles token refresh logic (401 response).
|
|
9
|
+
- **Universal Storage**: Support for `LocalStorage` (browser) and `MemoryStorage` (server-side).
|
|
10
|
+
- **Full API Coverage**: Methods for registration, login (email/Google/Facebook), profile management, and more.
|
|
11
|
+
- **SRWP Support**: Savant Realms Workspace Protocol discovery for realm-to-realm communication.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install federated-auth-realm-sdk-ts-js
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Basic Usage
|
|
20
|
+
|
|
21
|
+
### Initialization
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import {
|
|
25
|
+
FederatedAuthClient,
|
|
26
|
+
LocalStorageProvider,
|
|
27
|
+
} from 'federated-auth-realm-sdk-ts-js';
|
|
28
|
+
|
|
29
|
+
const client = new FederatedAuthClient({
|
|
30
|
+
baseURL: 'https://api.federatedauthrealm.com/v1',
|
|
31
|
+
appKey: 'your-app-key',
|
|
32
|
+
storage: new LocalStorageProvider(), // Optional, defaults to LocalStorageProvider
|
|
33
|
+
onSessionExpired: () => {
|
|
34
|
+
// Redirect to login page
|
|
35
|
+
window.location.href = '/login';
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Authentication
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// Login
|
|
44
|
+
const auth = await client.login({
|
|
45
|
+
email: 'user@example.com',
|
|
46
|
+
password: 'password',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Check if authenticated
|
|
50
|
+
const isAuthenticated = await client.isAuthenticated();
|
|
51
|
+
|
|
52
|
+
// Get current user
|
|
53
|
+
const user = await client.getUser();
|
|
54
|
+
|
|
55
|
+
// Logout
|
|
56
|
+
await client.logout();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Profile Management
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const profile = await client.getProfile();
|
|
63
|
+
|
|
64
|
+
await client.updateProfile({
|
|
65
|
+
name: 'Updated Name',
|
|
66
|
+
metadata: { theme: 'dark' },
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Token Verification (Server-side)
|
|
71
|
+
|
|
72
|
+
Back-end services can verify tokens without knowing the `JWT_SECRET` by using the realm's verification endpoint:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const result = await client.verifyToken('user-access-token');
|
|
76
|
+
|
|
77
|
+
if (result.valid) {
|
|
78
|
+
console.log('Token is valid for user:', result.user);
|
|
79
|
+
} else {
|
|
80
|
+
console.log('Token is invalid');
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### SRWP Discovery
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { SRWPDiscoveryClient } from 'federated-auth-realm-sdk-ts-js';
|
|
88
|
+
|
|
89
|
+
const discovery = new SRWPDiscoveryClient(
|
|
90
|
+
'https://api.federatedauthrealm.com/v1'
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const knownRealms = await discovery.getKnownRealms();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Advanced Storage
|
|
97
|
+
|
|
98
|
+
For server-side usage (Node.js), use `MemoryStorageProvider` or implement your own `StorageProvider`:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { MemoryStorageProvider } from 'federated-auth-realm-sdk-ts-js';
|
|
102
|
+
|
|
103
|
+
const client = new FederatedAuthClient({
|
|
104
|
+
baseURL: '...',
|
|
105
|
+
appKey: '...',
|
|
106
|
+
storage: new MemoryStorageProvider(),
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Development
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm install
|
|
114
|
+
npm run build
|
|
115
|
+
npm test
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AuthResponse, LoginRequest, RegisterRequest, GoogleAuthRequest, FacebookAuthRequest, ForgotPasswordRequest, ResetPasswordRequest, ChangePasswordRequest, RefreshTokenRequest, UpdateProfileRequest, VerifyTokenResponse, User } from './types';
|
|
2
|
+
import { StorageProvider } from './storage';
|
|
3
|
+
export interface FederatedAuthClientConfig {
|
|
4
|
+
baseURL: string;
|
|
5
|
+
appKey: string;
|
|
6
|
+
storage?: StorageProvider;
|
|
7
|
+
onSessionExpired?: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare class FederatedAuthClient {
|
|
10
|
+
private client;
|
|
11
|
+
private storage;
|
|
12
|
+
private appKey;
|
|
13
|
+
private onSessionExpired?;
|
|
14
|
+
private isRefreshing;
|
|
15
|
+
private refreshSubscribers;
|
|
16
|
+
constructor(config: FederatedAuthClientConfig);
|
|
17
|
+
private setupInterceptors;
|
|
18
|
+
private onRefreshed;
|
|
19
|
+
register(data: RegisterRequest): Promise<AuthResponse>;
|
|
20
|
+
login(data: LoginRequest): Promise<AuthResponse>;
|
|
21
|
+
loginWithGoogle(data: GoogleAuthRequest): Promise<AuthResponse>;
|
|
22
|
+
loginWithFacebook(data: FacebookAuthRequest): Promise<AuthResponse>;
|
|
23
|
+
forgotPassword(data: ForgotPasswordRequest): Promise<{
|
|
24
|
+
message: string;
|
|
25
|
+
}>;
|
|
26
|
+
resetPassword(data: ResetPasswordRequest): Promise<{
|
|
27
|
+
message: string;
|
|
28
|
+
}>;
|
|
29
|
+
changePassword(data: ChangePasswordRequest): Promise<{
|
|
30
|
+
message: string;
|
|
31
|
+
}>;
|
|
32
|
+
refreshToken(data: RefreshTokenRequest): Promise<AuthResponse>;
|
|
33
|
+
logout(): Promise<{
|
|
34
|
+
message: string;
|
|
35
|
+
}>;
|
|
36
|
+
logoutLocal(): Promise<void>;
|
|
37
|
+
getProfile(): Promise<User>;
|
|
38
|
+
updateProfile(data: UpdateProfileRequest): Promise<{
|
|
39
|
+
message: string;
|
|
40
|
+
}>;
|
|
41
|
+
verifyToken(token: string): Promise<VerifyTokenResponse>;
|
|
42
|
+
private setSession;
|
|
43
|
+
getUser(): Promise<User | null>;
|
|
44
|
+
isAuthenticated(): Promise<boolean>;
|
|
45
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FederatedAuthClient = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const storage_1 = require("./storage");
|
|
9
|
+
const jwt_1 = require("./utils/jwt");
|
|
10
|
+
class FederatedAuthClient {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.isRefreshing = false;
|
|
13
|
+
this.refreshSubscribers = [];
|
|
14
|
+
this.appKey = config.appKey;
|
|
15
|
+
this.storage = config.storage || new storage_1.LocalStorageProvider();
|
|
16
|
+
this.onSessionExpired = config.onSessionExpired;
|
|
17
|
+
this.client = axios_1.default.create({
|
|
18
|
+
baseURL: config.baseURL,
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
this.setupInterceptors();
|
|
24
|
+
}
|
|
25
|
+
setupInterceptors() {
|
|
26
|
+
this.client.interceptors.request.use(async (config) => {
|
|
27
|
+
const token = await this.storage.getItem('accessToken');
|
|
28
|
+
if (token) {
|
|
29
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
30
|
+
}
|
|
31
|
+
return config;
|
|
32
|
+
}, error => Promise.reject(error));
|
|
33
|
+
this.client.interceptors.response.use(response => response, async (error) => {
|
|
34
|
+
const originalRequest = error.config;
|
|
35
|
+
if (error.response?.status === 401 && !originalRequest._retry) {
|
|
36
|
+
if (originalRequest.url?.includes('/auth/login') ||
|
|
37
|
+
originalRequest.url?.includes('/auth/register') ||
|
|
38
|
+
originalRequest.url?.includes('/auth/refresh')) {
|
|
39
|
+
return Promise.reject(error);
|
|
40
|
+
}
|
|
41
|
+
if (this.isRefreshing) {
|
|
42
|
+
return new Promise(resolve => {
|
|
43
|
+
this.refreshSubscribers.push((token) => {
|
|
44
|
+
originalRequest.headers.Authorization = `Bearer ${token}`;
|
|
45
|
+
resolve(this.client(originalRequest));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
originalRequest._retry = true;
|
|
50
|
+
this.isRefreshing = true;
|
|
51
|
+
try {
|
|
52
|
+
const refreshToken = await this.storage.getItem('refreshToken');
|
|
53
|
+
if (!refreshToken) {
|
|
54
|
+
throw new Error('No refresh token available');
|
|
55
|
+
}
|
|
56
|
+
const response = await this.refreshToken({ refreshToken });
|
|
57
|
+
const { accessToken, refreshToken: newRefreshToken } = response;
|
|
58
|
+
await this.storage.setItem('accessToken', accessToken);
|
|
59
|
+
await this.storage.setItem('refreshToken', newRefreshToken);
|
|
60
|
+
this.onRefreshed(accessToken);
|
|
61
|
+
originalRequest.headers.Authorization = `Bearer ${accessToken}`;
|
|
62
|
+
return this.client(originalRequest);
|
|
63
|
+
}
|
|
64
|
+
catch (refreshError) {
|
|
65
|
+
await this.logoutLocal();
|
|
66
|
+
if (this.onSessionExpired) {
|
|
67
|
+
this.onSessionExpired();
|
|
68
|
+
}
|
|
69
|
+
return Promise.reject(refreshError);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
this.isRefreshing = false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return Promise.reject(error);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
onRefreshed(token) {
|
|
79
|
+
this.refreshSubscribers.forEach(callback => callback(token));
|
|
80
|
+
this.refreshSubscribers = [];
|
|
81
|
+
}
|
|
82
|
+
// Auth Methods
|
|
83
|
+
async register(data) {
|
|
84
|
+
const response = await this.client.post('/auth/register', {
|
|
85
|
+
...data,
|
|
86
|
+
appKey: this.appKey,
|
|
87
|
+
});
|
|
88
|
+
await this.setSession(response.data);
|
|
89
|
+
return response.data;
|
|
90
|
+
}
|
|
91
|
+
async login(data) {
|
|
92
|
+
const response = await this.client.post('/auth/login', {
|
|
93
|
+
...data,
|
|
94
|
+
appKey: this.appKey,
|
|
95
|
+
});
|
|
96
|
+
await this.setSession(response.data);
|
|
97
|
+
return response.data;
|
|
98
|
+
}
|
|
99
|
+
async loginWithGoogle(data) {
|
|
100
|
+
const response = await this.client.post('/auth/google', {
|
|
101
|
+
...data,
|
|
102
|
+
appKey: this.appKey,
|
|
103
|
+
});
|
|
104
|
+
await this.setSession(response.data);
|
|
105
|
+
return response.data;
|
|
106
|
+
}
|
|
107
|
+
async loginWithFacebook(data) {
|
|
108
|
+
const response = await this.client.post('/auth/facebook', {
|
|
109
|
+
...data,
|
|
110
|
+
appKey: this.appKey,
|
|
111
|
+
});
|
|
112
|
+
await this.setSession(response.data);
|
|
113
|
+
return response.data;
|
|
114
|
+
}
|
|
115
|
+
async forgotPassword(data) {
|
|
116
|
+
const response = await this.client.post('/auth/forgot-password', {
|
|
117
|
+
...data,
|
|
118
|
+
appKey: this.appKey,
|
|
119
|
+
});
|
|
120
|
+
return response.data;
|
|
121
|
+
}
|
|
122
|
+
async resetPassword(data) {
|
|
123
|
+
const response = await this.client.post('/auth/reset-password', data);
|
|
124
|
+
return response.data;
|
|
125
|
+
}
|
|
126
|
+
async changePassword(data) {
|
|
127
|
+
const response = await this.client.post('/auth/change-password', data);
|
|
128
|
+
return response.data;
|
|
129
|
+
}
|
|
130
|
+
async refreshToken(data) {
|
|
131
|
+
const response = await this.client.post('/auth/refresh', data);
|
|
132
|
+
return response.data;
|
|
133
|
+
}
|
|
134
|
+
async logout() {
|
|
135
|
+
try {
|
|
136
|
+
const response = await this.client.post('/auth/logout');
|
|
137
|
+
return response.data;
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
await this.logoutLocal();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async logoutLocal() {
|
|
144
|
+
await this.storage.removeItem('accessToken');
|
|
145
|
+
await this.storage.removeItem('refreshToken');
|
|
146
|
+
await this.storage.removeItem('user');
|
|
147
|
+
}
|
|
148
|
+
async getProfile() {
|
|
149
|
+
const response = await this.client.get('/auth/profile');
|
|
150
|
+
return response.data;
|
|
151
|
+
}
|
|
152
|
+
async updateProfile(data) {
|
|
153
|
+
const response = await this.client.put('/auth/profile', data);
|
|
154
|
+
return response.data;
|
|
155
|
+
}
|
|
156
|
+
async verifyToken(token) {
|
|
157
|
+
const response = await this.client.post('/auth/verify', { token });
|
|
158
|
+
return response.data;
|
|
159
|
+
}
|
|
160
|
+
// Session management
|
|
161
|
+
async setSession(auth) {
|
|
162
|
+
if (auth.accessToken) {
|
|
163
|
+
await this.storage.setItem('accessToken', auth.accessToken);
|
|
164
|
+
}
|
|
165
|
+
if (auth.refreshToken) {
|
|
166
|
+
await this.storage.setItem('refreshToken', auth.refreshToken);
|
|
167
|
+
}
|
|
168
|
+
if (auth.user) {
|
|
169
|
+
await this.storage.setItem('user', JSON.stringify(auth.user));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async getUser() {
|
|
173
|
+
const userStr = await this.storage.getItem('user');
|
|
174
|
+
if (userStr) {
|
|
175
|
+
try {
|
|
176
|
+
return JSON.parse(userStr);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
async isAuthenticated() {
|
|
185
|
+
const token = await this.storage.getItem('accessToken');
|
|
186
|
+
if (!token)
|
|
187
|
+
return false;
|
|
188
|
+
return !jwt_1.JwtUtils.isExpired(token);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.FederatedAuthClient = FederatedAuthClient;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface RealmInfo {
|
|
2
|
+
id: string;
|
|
3
|
+
url: string;
|
|
4
|
+
status: string;
|
|
5
|
+
lastSeen: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SRWPWhisperRequest {
|
|
8
|
+
sourceRealm: string;
|
|
9
|
+
sourceUrl: string;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
knownRealms: RealmInfo[];
|
|
12
|
+
}
|
|
13
|
+
export declare class SRWPDiscoveryClient {
|
|
14
|
+
private client;
|
|
15
|
+
constructor(baseURL: string);
|
|
16
|
+
whisper(data: SRWPWhisperRequest): Promise<{
|
|
17
|
+
message: string;
|
|
18
|
+
}>;
|
|
19
|
+
getKnownRealms(): Promise<RealmInfo[]>;
|
|
20
|
+
bootstrap(peerUrl: string): Promise<{
|
|
21
|
+
message: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SRWPDiscoveryClient = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
class SRWPDiscoveryClient {
|
|
9
|
+
constructor(baseURL) {
|
|
10
|
+
this.client = axios_1.default.create({
|
|
11
|
+
baseURL,
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async whisper(data) {
|
|
18
|
+
const response = await this.client.post('/whisper', data);
|
|
19
|
+
return response.data;
|
|
20
|
+
}
|
|
21
|
+
async getKnownRealms() {
|
|
22
|
+
const response = await this.client.get('/known');
|
|
23
|
+
return response.data;
|
|
24
|
+
}
|
|
25
|
+
async bootstrap(peerUrl) {
|
|
26
|
+
const response = await this.client.post('/bootstrap', {
|
|
27
|
+
peerUrl,
|
|
28
|
+
});
|
|
29
|
+
return response.data;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.SRWPDiscoveryClient = SRWPDiscoveryClient;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types"), exports);
|
|
18
|
+
__exportStar(require("./storage"), exports);
|
|
19
|
+
__exportStar(require("./client"), exports);
|
|
20
|
+
__exportStar(require("./discovery"), exports);
|
|
21
|
+
__exportStar(require("./utils/jwt"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface StorageProvider {
|
|
2
|
+
getItem(key: string): string | null | Promise<string | null>;
|
|
3
|
+
setItem(key: string, value: string): void | Promise<void>;
|
|
4
|
+
removeItem(key: string): void | Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare class LocalStorageProvider implements StorageProvider {
|
|
7
|
+
getItem(key: string): string | null;
|
|
8
|
+
setItem(key: string, value: string): void;
|
|
9
|
+
removeItem(key: string): void;
|
|
10
|
+
}
|
|
11
|
+
export declare class MemoryStorageProvider implements StorageProvider {
|
|
12
|
+
private storage;
|
|
13
|
+
getItem(key: string): string | null;
|
|
14
|
+
setItem(key: string, value: string): void;
|
|
15
|
+
removeItem(key: string): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryStorageProvider = exports.LocalStorageProvider = void 0;
|
|
4
|
+
class LocalStorageProvider {
|
|
5
|
+
getItem(key) {
|
|
6
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
7
|
+
return localStorage.getItem(key);
|
|
8
|
+
}
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
setItem(key, value) {
|
|
12
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
13
|
+
localStorage.setItem(key, value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
removeItem(key) {
|
|
17
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
18
|
+
localStorage.removeItem(key);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.LocalStorageProvider = LocalStorageProvider;
|
|
23
|
+
class MemoryStorageProvider {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.storage = new Map();
|
|
26
|
+
}
|
|
27
|
+
getItem(key) {
|
|
28
|
+
return this.storage.get(key) || null;
|
|
29
|
+
}
|
|
30
|
+
setItem(key, value) {
|
|
31
|
+
this.storage.set(key, value);
|
|
32
|
+
}
|
|
33
|
+
removeItem(key) {
|
|
34
|
+
this.storage.delete(key);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.MemoryStorageProvider = MemoryStorageProvider;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
export declare enum UserType {
|
|
2
|
+
SUPERADMIN = "SUPERADMIN",
|
|
3
|
+
MANAGER = "MANAGER",
|
|
4
|
+
EMPLOYEE = "EMPLOYEE",
|
|
5
|
+
CLIENT_USER = "CLIENT_USER",
|
|
6
|
+
GUEST_USER = "GUEST_USER"
|
|
7
|
+
}
|
|
8
|
+
export declare enum Gender {
|
|
9
|
+
MALE = "MALE",
|
|
10
|
+
FEMALE = "FEMALE",
|
|
11
|
+
OTHER = "OTHER"
|
|
12
|
+
}
|
|
13
|
+
export interface PhoneNumber {
|
|
14
|
+
internationalPrefix: string;
|
|
15
|
+
number: string;
|
|
16
|
+
fullNumber: string;
|
|
17
|
+
type?: 'MOBILE' | 'WORK_PHONE' | 'HOME_PHONE';
|
|
18
|
+
isPrimary: boolean;
|
|
19
|
+
isActive: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface Address {
|
|
22
|
+
inline: string;
|
|
23
|
+
structured: {
|
|
24
|
+
street: string | null;
|
|
25
|
+
secondaryStreet?: string | null;
|
|
26
|
+
civicNumber: string | null;
|
|
27
|
+
townCity: string;
|
|
28
|
+
provinceState: string;
|
|
29
|
+
provinceStateShortForm?: string;
|
|
30
|
+
postalCode: string;
|
|
31
|
+
country: string;
|
|
32
|
+
countryISO: string;
|
|
33
|
+
notes?: string;
|
|
34
|
+
};
|
|
35
|
+
type?: 'HOME' | 'WORK' | 'OTHER';
|
|
36
|
+
isPrimary: boolean | null;
|
|
37
|
+
isActive: boolean | null;
|
|
38
|
+
}
|
|
39
|
+
export interface Link {
|
|
40
|
+
url: string;
|
|
41
|
+
isPrimary: boolean;
|
|
42
|
+
isActive: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface ProfilePhoto {
|
|
45
|
+
url: string;
|
|
46
|
+
uploadDate: string;
|
|
47
|
+
exifData?: Record<string, unknown>;
|
|
48
|
+
isAvatar: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface EmergencyContact {
|
|
51
|
+
name: string;
|
|
52
|
+
surname: string;
|
|
53
|
+
email: string;
|
|
54
|
+
phone: PhoneNumber;
|
|
55
|
+
address?: Address;
|
|
56
|
+
relation: 'Legally Married Spouse' | 'Partner' | 'Child' | 'Parent' | 'Sibling' | 'Relative' | 'Friend' | 'Coworker' | 'Other';
|
|
57
|
+
isPrimary: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface PartialDateWithYear {
|
|
60
|
+
year: string;
|
|
61
|
+
month?: string;
|
|
62
|
+
day?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface Salary {
|
|
65
|
+
value: number;
|
|
66
|
+
currency: string;
|
|
67
|
+
dateFrom: PartialDateWithYear;
|
|
68
|
+
dateTo?: PartialDateWithYear;
|
|
69
|
+
}
|
|
70
|
+
export interface WorkFlexibleBenefit {
|
|
71
|
+
benefit: {
|
|
72
|
+
name: string;
|
|
73
|
+
notes?: string;
|
|
74
|
+
isActive: boolean;
|
|
75
|
+
};
|
|
76
|
+
benefitingFrom?: PartialDateWithYear;
|
|
77
|
+
benefitingTo?: PartialDateWithYear;
|
|
78
|
+
value?: string;
|
|
79
|
+
}
|
|
80
|
+
export interface Company {
|
|
81
|
+
id: string;
|
|
82
|
+
name: string;
|
|
83
|
+
headquarters?: Address;
|
|
84
|
+
yearOfFoundation: number;
|
|
85
|
+
typeOfCompany: 'Corporation' | 'LLC' | 'Partnership' | 'Sole Proprietorship' | 'Non-Profit' | 'Government' | 'Other';
|
|
86
|
+
industry: string;
|
|
87
|
+
rangeOfNumberOfEmployees: string;
|
|
88
|
+
website: Link;
|
|
89
|
+
email: string;
|
|
90
|
+
phoneNumber: PhoneNumber;
|
|
91
|
+
}
|
|
92
|
+
export interface Occupation {
|
|
93
|
+
title: string;
|
|
94
|
+
company?: Company;
|
|
95
|
+
startDate?: PartialDateWithYear;
|
|
96
|
+
endDate?: PartialDateWithYear;
|
|
97
|
+
role?: string;
|
|
98
|
+
workLocationType: 'Onsite' | 'Hybrid' | 'Remote';
|
|
99
|
+
workLocation?: Address;
|
|
100
|
+
contract?: 'Internal Hire' | 'Contractor/Freelancer';
|
|
101
|
+
timeEffort?: 'Full Time' | 'Part Time';
|
|
102
|
+
experienceLevel?: 'Junior' | 'Middle' | 'Senior';
|
|
103
|
+
managementLevel?: 'Simple Employee' | 'Middle Manager' | 'Senior Manager' | 'Officer' | 'Chief Officer' | 'CEO';
|
|
104
|
+
salaries?: Salary[];
|
|
105
|
+
benefits?: WorkFlexibleBenefit[];
|
|
106
|
+
}
|
|
107
|
+
export interface User {
|
|
108
|
+
id: string;
|
|
109
|
+
email?: string;
|
|
110
|
+
name?: string;
|
|
111
|
+
surname?: string;
|
|
112
|
+
userType: UserType;
|
|
113
|
+
gender?: Gender;
|
|
114
|
+
dateOfBirth?: string;
|
|
115
|
+
isEmailVerified: boolean;
|
|
116
|
+
isActive: boolean;
|
|
117
|
+
enabledApps: Application[];
|
|
118
|
+
permissions?: string[];
|
|
119
|
+
accessRequests?: unknown[];
|
|
120
|
+
phoneNumbers?: PhoneNumber[];
|
|
121
|
+
addresses?: Address[];
|
|
122
|
+
bio?: string;
|
|
123
|
+
profileLinks?: Link[];
|
|
124
|
+
occupations?: Occupation[];
|
|
125
|
+
profilePhotos?: ProfilePhoto[];
|
|
126
|
+
emergencyContacts?: EmergencyContact[];
|
|
127
|
+
metadata?: Record<string, unknown>;
|
|
128
|
+
createdAt?: string;
|
|
129
|
+
updatedAt?: string;
|
|
130
|
+
lastLoginAt?: string;
|
|
131
|
+
}
|
|
132
|
+
export declare enum ApplicationStatus {
|
|
133
|
+
ACTIVE = "ACTIVE",
|
|
134
|
+
TEMPORARILY_INACTIVE = "TEMPORARILY_INACTIVE",
|
|
135
|
+
SHADOW_DELETED = "SHADOW_DELETED"
|
|
136
|
+
}
|
|
137
|
+
export declare enum PlatformType {
|
|
138
|
+
API = "API",
|
|
139
|
+
UI = "UI",
|
|
140
|
+
DB = "DB",
|
|
141
|
+
SERVICE = "SERVICE",
|
|
142
|
+
OTHER = "OTHER"
|
|
143
|
+
}
|
|
144
|
+
export declare enum UIPlatformSubtype {
|
|
145
|
+
UI_WEB = "UI_WEB",
|
|
146
|
+
UI_MOBILE_ANDROID = "UI_MOBILE_ANDROID",
|
|
147
|
+
UI_MOBILE_IOS = "UI_MOBILE_IOS",
|
|
148
|
+
UI_OTHER = "UI_OTHER"
|
|
149
|
+
}
|
|
150
|
+
export interface PlatformUrl {
|
|
151
|
+
id: string;
|
|
152
|
+
value: string;
|
|
153
|
+
name: string;
|
|
154
|
+
description?: string;
|
|
155
|
+
isPrimary: boolean;
|
|
156
|
+
isRedirect: boolean;
|
|
157
|
+
isActive: boolean;
|
|
158
|
+
createdAt: string;
|
|
159
|
+
updatedAt: string;
|
|
160
|
+
}
|
|
161
|
+
export interface Platform {
|
|
162
|
+
id: string;
|
|
163
|
+
type: PlatformType;
|
|
164
|
+
name: string;
|
|
165
|
+
environment: 'development' | 'staging' | 'production';
|
|
166
|
+
subtype?: UIPlatformSubtype;
|
|
167
|
+
urls: PlatformUrl[];
|
|
168
|
+
isActive: boolean;
|
|
169
|
+
createdAt: string;
|
|
170
|
+
updatedAt: string;
|
|
171
|
+
}
|
|
172
|
+
export interface Application {
|
|
173
|
+
id: string;
|
|
174
|
+
appKey: string;
|
|
175
|
+
slug?: string;
|
|
176
|
+
name: string;
|
|
177
|
+
logo?: string;
|
|
178
|
+
description?: string;
|
|
179
|
+
status?: ApplicationStatus;
|
|
180
|
+
managerId?: string;
|
|
181
|
+
isPublic?: boolean;
|
|
182
|
+
platforms?: Platform[];
|
|
183
|
+
config?: Record<string, unknown>;
|
|
184
|
+
createdAt?: string;
|
|
185
|
+
updatedAt?: string;
|
|
186
|
+
}
|
|
187
|
+
export interface AuthResponse {
|
|
188
|
+
accessToken: string;
|
|
189
|
+
refreshToken: string;
|
|
190
|
+
expiresIn: number;
|
|
191
|
+
user?: User;
|
|
192
|
+
action?: 'account_created' | 'guest_user_transformed' | 'access_granted' | 'access_requested' | 'already_has_access' | 'account_exists';
|
|
193
|
+
message?: string;
|
|
194
|
+
}
|
|
195
|
+
export interface LoginRequest {
|
|
196
|
+
email?: string;
|
|
197
|
+
id?: string;
|
|
198
|
+
password?: string;
|
|
199
|
+
appKey: string;
|
|
200
|
+
rememberMe?: boolean;
|
|
201
|
+
}
|
|
202
|
+
export interface RegisterRequest {
|
|
203
|
+
email?: string;
|
|
204
|
+
password?: string;
|
|
205
|
+
name?: string;
|
|
206
|
+
surname?: string;
|
|
207
|
+
appKey: string;
|
|
208
|
+
gender?: Gender;
|
|
209
|
+
dateOfBirth?: string;
|
|
210
|
+
phoneNumbers?: PhoneNumber[];
|
|
211
|
+
addresses?: Address[];
|
|
212
|
+
bio?: string;
|
|
213
|
+
profileLinks?: Link[];
|
|
214
|
+
occupations?: Occupation[];
|
|
215
|
+
profilePhotos?: ProfilePhoto[];
|
|
216
|
+
emergencyContacts?: EmergencyContact[];
|
|
217
|
+
metadata?: Record<string, unknown>;
|
|
218
|
+
}
|
|
219
|
+
export interface GoogleAuthRequest {
|
|
220
|
+
googleToken: string;
|
|
221
|
+
appKey: string;
|
|
222
|
+
}
|
|
223
|
+
export interface FacebookAuthRequest {
|
|
224
|
+
facebookToken: string;
|
|
225
|
+
appKey: string;
|
|
226
|
+
}
|
|
227
|
+
export interface ForgotPasswordRequest {
|
|
228
|
+
email: string;
|
|
229
|
+
appKey?: string;
|
|
230
|
+
}
|
|
231
|
+
export interface ResetPasswordRequest {
|
|
232
|
+
token: string;
|
|
233
|
+
password?: string;
|
|
234
|
+
confirmPassword?: string;
|
|
235
|
+
}
|
|
236
|
+
export interface ChangePasswordRequest {
|
|
237
|
+
currentPassword?: string;
|
|
238
|
+
newPassword?: string;
|
|
239
|
+
confirmPassword?: string;
|
|
240
|
+
}
|
|
241
|
+
export interface RefreshTokenRequest {
|
|
242
|
+
refreshToken: string;
|
|
243
|
+
}
|
|
244
|
+
export interface UpdateProfileRequest {
|
|
245
|
+
name?: string;
|
|
246
|
+
surname?: string;
|
|
247
|
+
gender?: Gender;
|
|
248
|
+
dateOfBirth?: string;
|
|
249
|
+
phoneNumbers?: PhoneNumber[];
|
|
250
|
+
addresses?: Address[];
|
|
251
|
+
bio?: string;
|
|
252
|
+
profileLinks?: Link[];
|
|
253
|
+
occupations?: Occupation[];
|
|
254
|
+
profilePhotos?: ProfilePhoto[];
|
|
255
|
+
emergencyContacts?: EmergencyContact[];
|
|
256
|
+
metadata?: Record<string, unknown>;
|
|
257
|
+
}
|
|
258
|
+
export interface VerifyTokenRequest {
|
|
259
|
+
token: string;
|
|
260
|
+
}
|
|
261
|
+
export interface VerifyTokenResponse {
|
|
262
|
+
valid: boolean;
|
|
263
|
+
user: User | null;
|
|
264
|
+
}
|
|
265
|
+
export interface TokenPayload {
|
|
266
|
+
sub: string;
|
|
267
|
+
email: string;
|
|
268
|
+
userType: UserType;
|
|
269
|
+
appKey?: string;
|
|
270
|
+
iat: number;
|
|
271
|
+
exp: number;
|
|
272
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UIPlatformSubtype = exports.PlatformType = exports.ApplicationStatus = exports.Gender = exports.UserType = void 0;
|
|
4
|
+
var UserType;
|
|
5
|
+
(function (UserType) {
|
|
6
|
+
UserType["SUPERADMIN"] = "SUPERADMIN";
|
|
7
|
+
UserType["MANAGER"] = "MANAGER";
|
|
8
|
+
UserType["EMPLOYEE"] = "EMPLOYEE";
|
|
9
|
+
UserType["CLIENT_USER"] = "CLIENT_USER";
|
|
10
|
+
UserType["GUEST_USER"] = "GUEST_USER";
|
|
11
|
+
})(UserType || (exports.UserType = UserType = {}));
|
|
12
|
+
var Gender;
|
|
13
|
+
(function (Gender) {
|
|
14
|
+
Gender["MALE"] = "MALE";
|
|
15
|
+
Gender["FEMALE"] = "FEMALE";
|
|
16
|
+
Gender["OTHER"] = "OTHER";
|
|
17
|
+
})(Gender || (exports.Gender = Gender = {}));
|
|
18
|
+
var ApplicationStatus;
|
|
19
|
+
(function (ApplicationStatus) {
|
|
20
|
+
ApplicationStatus["ACTIVE"] = "ACTIVE";
|
|
21
|
+
ApplicationStatus["TEMPORARILY_INACTIVE"] = "TEMPORARILY_INACTIVE";
|
|
22
|
+
ApplicationStatus["SHADOW_DELETED"] = "SHADOW_DELETED";
|
|
23
|
+
})(ApplicationStatus || (exports.ApplicationStatus = ApplicationStatus = {}));
|
|
24
|
+
var PlatformType;
|
|
25
|
+
(function (PlatformType) {
|
|
26
|
+
PlatformType["API"] = "API";
|
|
27
|
+
PlatformType["UI"] = "UI";
|
|
28
|
+
PlatformType["DB"] = "DB";
|
|
29
|
+
PlatformType["SERVICE"] = "SERVICE";
|
|
30
|
+
PlatformType["OTHER"] = "OTHER";
|
|
31
|
+
})(PlatformType || (exports.PlatformType = PlatformType = {}));
|
|
32
|
+
var UIPlatformSubtype;
|
|
33
|
+
(function (UIPlatformSubtype) {
|
|
34
|
+
UIPlatformSubtype["UI_WEB"] = "UI_WEB";
|
|
35
|
+
UIPlatformSubtype["UI_MOBILE_ANDROID"] = "UI_MOBILE_ANDROID";
|
|
36
|
+
UIPlatformSubtype["UI_MOBILE_IOS"] = "UI_MOBILE_IOS";
|
|
37
|
+
UIPlatformSubtype["UI_OTHER"] = "UI_OTHER";
|
|
38
|
+
})(UIPlatformSubtype || (exports.UIPlatformSubtype = UIPlatformSubtype = {}));
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JwtUtils = void 0;
|
|
4
|
+
class JwtUtils {
|
|
5
|
+
static decode(token) {
|
|
6
|
+
try {
|
|
7
|
+
const base64Url = token.split('.')[1];
|
|
8
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
9
|
+
const jsonPayload = decodeURIComponent(atob(base64)
|
|
10
|
+
.split('')
|
|
11
|
+
.map(function (c) {
|
|
12
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
13
|
+
})
|
|
14
|
+
.join(''));
|
|
15
|
+
return JSON.parse(jsonPayload);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
static isExpired(token) {
|
|
22
|
+
const payload = this.decode(token);
|
|
23
|
+
if (!payload || !payload.exp)
|
|
24
|
+
return true;
|
|
25
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
26
|
+
return payload.exp < currentTime;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.JwtUtils = JwtUtils;
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@savant-realms/federated-auth-realm-sdk-ts-js",
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Federated Auth Realm - SDK Ts/Js",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"test:watch": "jest --watch",
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"deploy": "./scripts/deploy.sh",
|
|
19
|
+
"lint": "eslint src/**/*.ts",
|
|
20
|
+
"lint:fix": "eslint src/**/*.ts --fix",
|
|
21
|
+
"format": "prettier --write src/**/*.ts",
|
|
22
|
+
"format:check": "prettier --check src/**/*.ts",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"check-all": "npm run type-check && npm run lint && npm run format:check && npm run test",
|
|
25
|
+
"lint-staged": "lint-staged",
|
|
26
|
+
"prepare": "husky install"
|
|
27
|
+
},
|
|
28
|
+
"lint-staged": {
|
|
29
|
+
"*.ts": [
|
|
30
|
+
"eslint --fix",
|
|
31
|
+
"prettier --write"
|
|
32
|
+
],
|
|
33
|
+
"*.{json,md}": [
|
|
34
|
+
"prettier --write"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/savant-realms/federated-auth-realm-sdk-ts-js.git"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [],
|
|
42
|
+
"author": "",
|
|
43
|
+
"license": "ISC",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/savant-realms/federated-auth-realm-sdk-ts-js/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/savant-realms/federated-auth-realm-sdk-ts-js#readme",
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"axios": "^1.13.2",
|
|
50
|
+
"jsonwebtoken": "^9.0.3",
|
|
51
|
+
"zod": "^4.3.5"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@eslint/js": "^9.39.2",
|
|
55
|
+
"@types/jest": "^30.0.0",
|
|
56
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
57
|
+
"@types/node": "^25.0.3",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^8.52.0",
|
|
59
|
+
"@typescript-eslint/parser": "^8.52.0",
|
|
60
|
+
"eslint": "^9.39.2",
|
|
61
|
+
"eslint-config-prettier": "^10.1.8",
|
|
62
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
63
|
+
"globals": "^17.0.0",
|
|
64
|
+
"husky": "^9.1.7",
|
|
65
|
+
"jest": "^30.2.0",
|
|
66
|
+
"lint-staged": "^16.2.7",
|
|
67
|
+
"prettier": "^3.7.4",
|
|
68
|
+
"ts-jest": "^29.4.6",
|
|
69
|
+
"tsx": "^4.21.0",
|
|
70
|
+
"typescript": "^5.9.3"
|
|
71
|
+
}
|
|
72
|
+
}
|