@oneidentity/safeguard 1.0.5 → 8.0.0-pre36948

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 CHANGED
@@ -1,279 +1,408 @@
1
- # safeguard.js
2
- One Identity Safeguard JavaScript SDK
3
-
4
- -----------
5
-
6
- <p align="center">
7
- <i>Check out our <a href="samples">sample projects</a> to get started with your own custom integration to Safeguard!</i>
8
- </p>
9
-
10
- -----------
11
-
12
- ## Support
13
-
14
- One Identity open source projects are supported through [One Identity GitHub issues](https://github.com/OneIdentity/safeguard.js/issues) and the [One Identity Community](https://www.oneidentity.com/community/). This includes all scripts, plugins, SDKs, modules, code snippets or other solutions. For assistance with any One Identity GitHub project, please raise a new Issue on the [One Identity GitHub project](https://github.com/OneIdentity/safeguard.js/issues) page. You may also visit the [One Identity Community](https://www.oneidentity.com/community/) to ask questions. Requests for assistance made through official One Identity Support will be referred back to GitHub and the One Identity Community forums where those requests can benefit all users.
15
-
16
- If you would like to add to safeguard.js? See the [developer guide](https://github.com/OneIdentity/safeguard.js/blob/master/src/README.md).
17
-
18
- ## Introduction
19
-
20
- All functionality in Safeguard is available via the Safeguard API. There is
21
- nothing that can be done in the Safeguard UI that cannot also be performed
22
- using the Safeguard API programmatically.
23
-
24
- safeguard.js is provided to facilitate calling the Safeguard API from JavaScript.
25
- It is meant to remove the complexity of dealing with authentication via
26
- Safeguard's embedded secure token service (STS). The basic usage is to call
27
- `connect()` to establish a connection to Safeguard, then you can call
28
- `invoke()` multiple times using the same authenticated connection.
29
-
30
- safeguard.js also provides an easy way to call Safeguard A2A from JavaScript. The A2A service requires client certificate authentication for retrieving passwords for application integration. When Safeguard A2A is properly configured, specified passwords can be retrieved with a single method call without requiring access request workflow approvals. Safeguard A2A is protected by API keys and IP restrictions in addition to client certificate authentication.
31
-
32
- safeguard.js includes an SDK for listening to Safeguard's powerful, real-time event notification system. Safeguard provides role-based event notifications via SignalR to subscribed clients. If a Safeguard user is an Asset Administrator events related to the creation, modification, or deletion of Assets and Asset Accounts will be sent to that user. When used with a certificate user, this provides an opportunity for reacting programmatically to any data modification in Safeguard. Events are also supported for access request workflow and for A2A password changes.
33
-
34
- ## Installation
35
-
36
- This javascript module is published to the [npm registry](https://www.npmjs.com/package/@oneidentity/safeguard) to make it as easy as possible to install.
37
-
38
- ```Bash
39
- > npm install @oneidentity/safeguard
40
- ```
41
-
42
- ## Getting Started
43
-
44
- A simple code example for calling the Safeguard API for authentication through the standard Safeguard STS login page:
45
-
46
- ```JavaScript
47
- // Browser Example
48
- SafeguardJs.connectRsts('safeguard.sample.corp', `${window.location.protocol}//${window.location.host}${window.location.pathname}`)
49
- .then((connection) => {
50
- connection.invoke(SafeguardJs.Services.CORE, SafeguardJs.HttpMethods.GET, 'v3/Me')
51
- .then((results) => {
52
- console.log(results);
53
- });
54
- });
55
- ```
56
-
57
- For Node.JS, password authentication is available:
58
-
59
- ```JavaScript
60
- // Node.JS Example
61
- SafeguardJs.addCAFromFile('ssl/ca.pem');
62
- let connection = await SafeguardJs.connectPassword('safeguard.sample.corp', 'myuser', 'mypassword');
63
- ```
64
-
65
- Password authentication to an external provider is as follows:
66
-
67
- ```JavaScript
68
- // Node.JS Example
69
- SafeguardJs.addCAFromFile('ssl/ca.pem');
70
- let connection = await SafeguardJs.connectPassword('safeguard.sample.corp', 'myuser', 'mypassword', 'myexternalprovider');
71
- ```
72
-
73
- Client certificate authentication is also available. This can be done either using a PFX certificate file or a PEM and KEY.
74
-
75
- ```JavaScript
76
- // Node.JS Example
77
- SafeguardJs.addCAFromFile('ssl/ca.pem');
78
- let connection = await SafeguardJs.connectCertificateFromFiles('safeguard.sample.corp', 'ssl/certificateuser.pem', 'ssl/certificateuser.key', null, 'password');
79
- ```
80
-
81
- ```JavaScript
82
- // Node.JS Example
83
- SafeguardJs.addCAFromFile('ssl/ca.pem');
84
- let connection = await SafeguardJs.connectCertificateFromFiles('safeguard.sample.corp', null, null, 'ssl/certificateuser.pfx', 'password');
85
- ```
86
-
87
- Client certificate authentication to an external provider is also available. This can again be done either using a PFX certificate file or a PEM and KEY.
88
-
89
- ```JavaScript
90
- // Node.JS Example
91
- SafeguardJs.addCAFromFile('ssl/ca.pem');
92
- SafeguardJs.addCAFromFile('ssl/ca2.pem'); // additional CA for external provider
93
- let connection = await SafeguardJs.connectCertificateFromFiles('safeguard.sample.corp', 'ssl/certificateuser.pem', 'ssl/certificateuser.key', null, 'password', 'myexternalprovider');
94
- ```
95
-
96
- ```JavaScript
97
- // Node.JS Example
98
- SafeguardJs.addCAFromFile('ssl/ca.pem');
99
- SafeguardJs.addCAFromFile('ssl/ca2.pem'); // additional CA for external provider
100
- let connection = await SafeguardJs.connectCertificateFromFiles('safeguard.sample.corp', null, null, 'ssl/certificateuser.pfx', 'password', 'myexternalprovider');
101
- ```
102
-
103
- A connection can also be made anonymously
104
-
105
- ```JavaScript
106
- // Browser Example
107
- let connection = SafeguardJs.connectAnonymous('safeguard.sample.corp');
108
- ```
109
-
110
- ```JavaScript
111
- // Node.JS Example
112
- let localStorage = new require('src/LocalStorage').LocalStorage;
113
- SafeguardJs.addCAFromFile('ca.pem');
114
- let connection = SafeguardJs.connectAnonymous('safeguard.sample.corp', null, localStorage);
115
- ```
116
-
117
- Authentication is also possible using an existing Safeguard API token:
118
-
119
- ```JavaScript
120
- // Browser Example
121
- let apiToken = GetTokenSomehow();
122
- let connection = SafeguardJs.connectAnonymous('safeguard.sample.corp');
123
- SafeguardJs.Storage.setUserToken(apiToken);
124
- ```
125
-
126
- ```JavaScript
127
- // Node.JS Example
128
- let apiToken = GetTokenSomehow();
129
- let localStorage = new require('src/LocalStorage').LocalStorage;
130
-
131
- let connection = SafeguardJs.connectAnonymous('safeguard.sample.corp', null, localStorage);
132
- SafeguardJs.Storage.setUserToken(apiToken);
133
- ```
134
-
135
- Two-factor authentication can only be performed through `connectRsts()`, so that the secure token service can use the browser agent to redirect you to multiple authentication providers.
136
-
137
- Most of the examples above use the default SafeguardJs.Storage locations. For RSTS and anonymous connections, SessionStorage is used to persist authentication information. For password and certificate authentication, LocalStorage is used, which is an in memory storage. By writing a new SafeguardJs storage class, these authentication values can be stored elsewhere. Further information can be found <a href="src/SessionStorage.js">here</a> for session storage and <a href="src/LocalStorage.js">here</a> for local storage.
138
-
139
- ## Getting Started With A2A
140
-
141
- Once you have configured your A2A registration in Safeguard you can retrieve an A2A password or private key using a certificate and api key.
142
-
143
- To retrieve a password via A2A:
144
-
145
- ```JavaScript
146
- // Node.JS Example
147
- SafeguardJs.addCAFromFile('ssl/ca.pem');
148
- let password = await SafeguardJs.a2aGetCredentialFromFiles('safeguard.sample.corp', 'myapikey', SafeguardJs.A2ATypes.PASSWORD, null, 'ssl/certificateuser.pem', 'ssl/certificateuser.key', 'password');
149
- ```
150
-
151
- To retrieve a private key via A2A:
152
-
153
- ```JavaScript
154
- // Node.JS Example
155
- SafeguardJs.addCAFromFile('ssl/ca.pem');
156
- let privateKey = await SafeguardJs.a2aGetCredentialFromFiles('safeguard.sample.corp', 'myapikey', SafeguardJs.A2ATypes.PRIVATEKEY, SafeguardJs.SshKeyFormats.OPENSSH, 'ssl/certificateuser.pem', 'ssl/certificateuser.key', 'password');
157
- ```
158
-
159
- ## About the Safeguard API
160
-
161
- The Safeguard API is a REST-based Web API. Safeguard API endpoints are called
162
- using HTTP operators and JSON (or XML) requests and responses. The Safeguard API
163
- is documented using Swagger. You may use Swagger UI to call the API directly or
164
- to read the documentation about URLs, parameters, and payloads.
165
-
166
- To access the Swagger UI use a browser to navigate to:
167
- `https://<address>/service/<service>/swagger`
168
-
169
- - `<address>` = Safeguard network address
170
- - `<service>` = Safeguard service to use
171
-
172
- The Safeguard API is made up of multiple services: core, appliance, notification,
173
- and a2a.
174
-
175
- |Service|Description|
176
- |-|-|
177
- |core|Most product functionality is found here. All cluster-wide operations: access request workflow, asset management, policy management, etc.|
178
- |appliance|Appliance specific operations, such as setting IP address, maintenance, backups, support bundles, appliance management|
179
- |notification|Anonymous, unauthenticated operations. This service is available even when the appliance isn't fully online|
180
- |a2a|Application integration specific operations. Fetching passwords, making access requests on behalf of users, etc.|
181
-
182
- Each of these services provides a separate Swagger endpoint.
183
-
184
- You may use the `Authorize` button at the top of the screen to get an API token
185
- to call the Safeguard API directly using Swagger.
186
-
187
- ### Examples
188
-
189
- Most functionality is in the core service as mentioned above. The notification service
190
- provides read-only information for status, etc.
191
-
192
- #### Anonymous Call for Safeguard Status
193
-
194
- Sample can be found <a href="samples\Browser\Promises\AnonymousExample">here</a>.
195
-
196
- ```JavaScript
197
- // Browser Example
198
- SafeguardJs.connectAnonymous('safeguard.sample.corp', saveConnectionCallback)
199
- .then((connection) => {
200
- connection.invoke(SafeguardJs.Services.NOTIFICATION, SafeguardJs.HttpMethods.GET, 'v3/Status')
201
- .then((results) => {
202
- console.log(results);
203
- });
204
- });
205
- ```
206
-
207
- Sample can be found <a href="samples\Node.JS\anonymousExample.js">here</a>.
208
-
209
- ```JavaScript
210
- // Node.JS Example
211
- let localStorage = new require('src/LocalStorage').LocalStorage;
212
- SafeguardJs.addCAFromFile(caFile);
213
- let connection = await SafeguardJs.connectAnonymous(hostName, null, localStorage);
214
- let result = await connection.invoke(SafeguardJs.Services.NOTIFICATION, SafeguardJs.HttpMethods.GET, 'v3/Status');
215
- console.log(result);
216
- ```
217
-
218
- #### Get remaining access token lifetime
219
-
220
- Sample can be found <a href="samples\Node.JS\passwordExample.js">here</a>.
221
-
222
- ```JavaScript
223
- // Node.JS Example
224
- SafeguardJs.addCAFromFile('ssl/ca.pem');
225
- let connection = await SafeguardJs.connectPassword('safeguard.sample.corp', 'myuser', 'mypassword');
226
- let result = await connection.getAccessTokenLifetimeRemaining();
227
- console.log(result);
228
- ```
229
-
230
- #### Register for SignalR events
231
-
232
- Sample can be found <a href="samples\Browser\Promises\SignalRExample">here</a>.
233
-
234
- ```JavaScript
235
- // Browser Example
236
- function callback(ev) {
237
- console.log(`Received SignalR event: ${ev.Message}`);
238
- }
239
-
240
- SafeguardJs.connectRsts('safeguard.sample.corp', `${window.location.protocol}//${window.location.host}${window.location.pathname}`)
241
- .then((connection) => {
242
- connection.registerSignalR(logCallback);
243
- });
244
- ```
245
-
246
- Sample can be found <a href="samples\Node.JS\signalRExample.js">here</a>.
247
-
248
- ```JavaScript
249
- // Node.JS Example
250
- function callback(ev) {
251
- console.log(`Received SignalR event: ${ev.Message}`);
252
- }
253
-
254
- SafeguardJs.addCAFromFile('ssl/ca.pem');
255
- let connection = await SafeguardJs.connectPassword('safeguard.sample.corp', 'myuser', 'mypassword');
256
- await connection.registerSignalR(callback);
257
- ```
258
-
259
- #### Create a New User and Set the Password
260
-
261
- Sample can be found <a href="samples\Browser\Promises\NewUserExample">here</a>.
262
-
263
- ```JavaScript
264
- // Browser Example
265
- let user = {
266
- 'PrimaryAuthenticationProviderId': -1,
267
- 'UserName': 'MyNewUser'
268
- };
269
- let password = 'MyNewUser123';
270
-
271
- SafeguardJs.connectRsts('safeguard.sample.corp', `${window.location.protocol}//${window.location.host}${window.location.pathname}`)
272
- .then((connection) => {
273
- connection.invoke(SafeguardJs.Services.CORE, SafeguardJs.HttpMethods.POST, 'v3/Users', user)
274
- .then((results) => {
275
- let newUser = JSON.parse(results);
276
- connection.invoke(SafeguardJs.Services.CORE, SafeguardJs.HttpMethods.PUT, `v3/Users/${newUser.Id}/Password`, `"${password}"`);
277
- });
278
- });
279
- ```
1
+ [![npm](https://img.shields.io/npm/v/@oneidentity/safeguard.svg)](https://www.npmjs.com/package/@oneidentity/safeguard)
2
+ [![license](https://img.shields.io/github/license/OneIdentity/safeguard.js.svg)](https://github.com/OneIdentity/safeguard.js/blob/main/LICENSE)
3
+
4
+ # safeguard.js
5
+
6
+ One Identity Safeguard JavaScript/TypeScript SDK
7
+
8
+ -----------
9
+
10
+ <p align="center">
11
+ <i>Check out our <a href="samples">sample projects</a> to get started with your own custom integration to Safeguard!</i>
12
+ </p>
13
+
14
+ -----------
15
+
16
+ ## Support
17
+
18
+ One Identity open source projects are supported through [One Identity GitHub issues](https://github.com/OneIdentity/safeguard.js/issues) and the [One Identity Community](https://www.oneidentity.com/community/). This includes all scripts, plugins, SDKs, modules, code snippets or other solutions. For assistance with any One Identity GitHub project, please raise a new Issue on the [One Identity GitHub project](https://github.com/OneIdentity/safeguard.js/issues) page. You may also visit the [One Identity Community](https://www.oneidentity.com/community/) to ask questions. Requests for assistance made through official One Identity Support will be referred back to GitHub and the One Identity Community forums where those requests can benefit all users.
19
+
20
+ ## Introduction
21
+
22
+ All functionality in Safeguard is available via the Safeguard API. There is
23
+ nothing that can be done in the Safeguard UI that cannot also be performed
24
+ using the Safeguard API programmatically.
25
+
26
+ safeguard.js is provided to facilitate calling the Safeguard API from
27
+ JavaScript and TypeScript. It is meant to remove the complexity of dealing
28
+ with authentication via Safeguard's embedded secure token service (STS). The
29
+ basic usage is to create a `SafeguardClient` with your chosen authentication
30
+ strategy, call `connect()`, then call API methods using the same authenticated
31
+ client.
32
+
33
+ safeguard.js also provides an easy way to call Safeguard A2A from JavaScript.
34
+ The A2A service requires client certificate authentication for retrieving
35
+ passwords for application integration. When Safeguard A2A is properly
36
+ configured, specified passwords can be retrieved with a single method call
37
+ without requiring access request workflow approvals.
38
+
39
+ safeguard.js includes an SDK for listening to Safeguard's powerful, real-time
40
+ event notification system. Safeguard provides role-based event notifications
41
+ via SignalR to subscribed clients. The `PersistentSafeguardEventListener`
42
+ provides automatic reconnection with token refresh for long-running listeners.
43
+
44
+ ### Features
45
+
46
+ - **TypeScript-first** with full type declarations
47
+ - **Dual ESM/CJS** — works with `import` and `require()`
48
+ - **Node.js and Browser** support
49
+ - **Multiple auth strategies** — Password, Certificate, PKCE (browser), PKCE Non-Interactive (headless), Token, Anonymous
50
+ - **A2A client** — retrieve/set passwords, SSH keys, API key secrets, broker access requests
51
+ - **SignalR events** — one-shot and persistent event listeners with auto-reconnect
52
+ - **Secure by default** — TLS verification enabled, no secrets in memory longer than needed
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ npm install @oneidentity/safeguard
58
+ ```
59
+
60
+ Requires Node.js 20 or later.
61
+
62
+ ## Getting Started
63
+
64
+ ### Password Authentication (Node.js)
65
+
66
+ ```typescript
67
+ import { SafeguardClient, PasswordAuth, Service } from '@oneidentity/safeguard';
68
+
69
+ const client = new SafeguardClient('safeguard.sample.corp', {
70
+ auth: new PasswordAuth({
71
+ username: 'Admin',
72
+ password: 'Admin123',
73
+ provider: 'Local',
74
+ }),
75
+ });
76
+
77
+ await client.connect();
78
+
79
+ const me = await client.get(Service.CORE, 'v4/Me');
80
+ console.log(me);
81
+
82
+ await client.disconnect();
83
+ ```
84
+
85
+ ### PKCE Authentication (Browser)
86
+
87
+ ```typescript
88
+ import { SafeguardClient, PkceAuth, handlePkceCallback, Service } from '@oneidentity/safeguard';
89
+
90
+ // On your callback page, call this first:
91
+ handlePkceCallback();
92
+
93
+ // On your main page:
94
+ const client = new SafeguardClient('safeguard.sample.corp', {
95
+ auth: new PkceAuth({ redirectUri: window.location.href }),
96
+ });
97
+
98
+ await client.connect(); // Redirects to Safeguard login if no stored tokens
99
+
100
+ const me = await client.get(Service.CORE, 'v4/Me');
101
+ console.log(me);
102
+ ```
103
+
104
+ ### PKCE Non-Interactive (Headless Automation)
105
+
106
+ For automated scenarios where no browser is available (CI/CD, scripts):
107
+
108
+ ```typescript
109
+ import { SafeguardClient, PkceNonInteractiveAuth, Service } from '@oneidentity/safeguard';
110
+
111
+ const client = new SafeguardClient('safeguard.sample.corp', {
112
+ auth: new PkceNonInteractiveAuth({
113
+ username: 'Admin',
114
+ password: 'Admin123',
115
+ provider: 'Local',
116
+ }),
117
+ });
118
+
119
+ await client.connect();
120
+ const me = await client.get(Service.CORE, 'v4/Me');
121
+ await client.disconnect();
122
+ ```
123
+
124
+ ### Client Certificate Authentication
125
+
126
+ ```typescript
127
+ import { SafeguardClient, CertificateAuth, Service } from '@oneidentity/safeguard';
128
+
129
+ const client = new SafeguardClient('safeguard.sample.corp', {
130
+ auth: new CertificateAuth({
131
+ certFile: '/path/to/client.pem',
132
+ keyFile: '/path/to/client.key',
133
+ passphrase: 'optional-key-passphrase',
134
+ }),
135
+ });
136
+
137
+ await client.connect();
138
+ const me = await client.get(Service.CORE, 'v4/Me');
139
+ await client.disconnect();
140
+ ```
141
+
142
+ ### Anonymous Access
143
+
144
+ ```typescript
145
+ import { SafeguardClient, AnonymousAuth, Service } from '@oneidentity/safeguard';
146
+
147
+ const client = new SafeguardClient('safeguard.sample.corp', {
148
+ auth: new AnonymousAuth(),
149
+ });
150
+
151
+ await client.connect();
152
+ const status = await client.get(Service.NOTIFICATION, 'v4/Status');
153
+ console.log(status);
154
+ ```
155
+
156
+ ### Pre-existing API Token
157
+
158
+ ```typescript
159
+ import { SafeguardClient, TokenAuth, Service } from '@oneidentity/safeguard';
160
+
161
+ const client = new SafeguardClient('safeguard.sample.corp', {
162
+ auth: new TokenAuth({ accessToken: 'your-token-here' }),
163
+ });
164
+
165
+ await client.connect();
166
+ const me = await client.get(Service.CORE, 'v4/Me');
167
+ ```
168
+
169
+ ## Calling the API
170
+
171
+ The client provides typed HTTP methods:
172
+
173
+ ```typescript
174
+ // GET
175
+ const users = await client.get(Service.CORE, 'v4/Users');
176
+
177
+ // GET with query parameters
178
+ const filtered = await client.get(Service.CORE, 'v4/Users', {
179
+ query: { filter: "Name eq 'Admin'", fields: 'Id,Name' },
180
+ });
181
+
182
+ // POST (create)
183
+ const newUser = await client.post(Service.CORE, 'v4/Users', {
184
+ json: { Name: 'newuser', PrimaryAuthenticationProvider: { Id: -1 } },
185
+ });
186
+
187
+ // PUT (update)
188
+ await client.put(Service.CORE, `v4/Users/${newUser.Id}/Password`, {
189
+ json: 'NewPassword123',
190
+ });
191
+
192
+ // DELETE
193
+ await client.delete(Service.CORE, `v4/Users/${newUser.Id}`);
194
+ ```
195
+
196
+ ### Services
197
+
198
+ | Service | Description |
199
+ |---------|-------------|
200
+ | `Service.CORE` | Most product functionality — access requests, asset management, policy, users |
201
+ | `Service.APPLIANCE` | Appliance-specific operations — IP address, maintenance, backups |
202
+ | `Service.NOTIFICATION` | Anonymous/unauthenticated — status, availability |
203
+ | `Service.A2A` | Application integration — credential retrieval, access request brokering |
204
+
205
+ ## A2A (Application to Application)
206
+
207
+ ```typescript
208
+ import { A2AClient, CertificateAuth } from '@oneidentity/safeguard';
209
+
210
+ const a2a = new A2AClient('safeguard.sample.corp', {
211
+ auth: new CertificateAuth({
212
+ certFile: 'client.pem',
213
+ keyFile: 'client.key',
214
+ }),
215
+ });
216
+
217
+ // Retrieve a password
218
+ const password = await a2a.retrievePassword(apiKey);
219
+
220
+ // Retrieve an SSH private key
221
+ const sshKey = await a2a.retrievePrivateKey(apiKey);
222
+
223
+ // Set a password (write-back)
224
+ await a2a.setPassword(apiKey, 'NewPassword123');
225
+
226
+ // Discover retrievable accounts
227
+ const accounts = await a2a.getRetrievableAccounts();
228
+ ```
229
+
230
+ ## Event Listeners (SignalR)
231
+
232
+ Real-time event support requires the optional `@microsoft/signalr` peer dependency:
233
+
234
+ ```bash
235
+ npm install @microsoft/signalr
236
+ ```
237
+
238
+ Event classes are imported from the `@oneidentity/safeguard/events` subpath:
239
+
240
+ ```typescript
241
+ import { SafeguardEventListener } from '@oneidentity/safeguard/events';
242
+ import { PasswordAuth, NodeHttpClient, MemoryStorage } from '@oneidentity/safeguard';
243
+ import * as signalR from '@microsoft/signalr';
244
+ ```
245
+
246
+ ### One-Shot Listener
247
+
248
+ ```typescript
249
+ // Authenticate and build SignalR connection
250
+ const auth = new PasswordAuth({ username: 'Admin', password: 'Admin123', provider: 'Local' });
251
+ const httpClient = new NodeHttpClient();
252
+ const storage = new MemoryStorage();
253
+ const tokenSet = await auth.authenticate('safeguard.sample.corp', httpClient, storage);
254
+
255
+ const connection = new signalR.HubConnectionBuilder()
256
+ .withUrl(`https://safeguard.sample.corp/service/event/signalr`, {
257
+ accessTokenFactory: () => tokenSet.accessToken.expose(),
258
+ })
259
+ .withAutomaticReconnect()
260
+ .build();
261
+
262
+ const listener = new SafeguardEventListener(connection);
263
+
264
+ listener.on('NotifyEventAsync', (event) => {
265
+ console.log('Event received:', event);
266
+ });
267
+
268
+ await listener.start();
269
+ ```
270
+
271
+ ### Persistent Listener (Auto-Reconnect)
272
+
273
+ For long-running processes that need to survive network interruptions and
274
+ token expiration:
275
+
276
+ ```typescript
277
+ import { PersistentSafeguardEventListener } from '@oneidentity/safeguard/events';
278
+
279
+ const listener = new PersistentSafeguardEventListener(
280
+ connection, auth, 'safeguard.sample.corp', httpClient, storage,
281
+ );
282
+
283
+ listener.onStateChange((state) => {
284
+ console.log('State:', state);
285
+ });
286
+
287
+ listener.on('NotifyEventAsync', (event) => {
288
+ console.log('Event:', event);
289
+ });
290
+
291
+ await listener.start();
292
+ ```
293
+
294
+ The persistent listener automatically checks token lifetime and refreshes
295
+ credentials before they expire (with a 60-second safety margin).
296
+
297
+ ## Security
298
+
299
+ ### Token Storage (Browser)
300
+
301
+ The SDK stores access tokens **in memory only**. Tokens do not survive a page
302
+ refresh, which is the secure default for single-page applications without a
303
+ backend-for-frontend (BFF).
304
+
305
+ If your application requires persistence across page reloads, you may
306
+ explicitly store the token yourself — but be aware this exposes the token to
307
+ cross-site scripting (XSS) attacks:
308
+
309
+ ```typescript
310
+ await client.connect();
311
+ // ⚠️ Customer explicitly accepts the XSS risk:
312
+ sessionStorage.setItem('my_token', client.accessToken.expose());
313
+ ```
314
+
315
+ For high-security environments, prefer in-memory tokens with re-authentication
316
+ on refresh, or implement a BFF pattern where tokens never reach the browser.
317
+
318
+ ### SecretValue
319
+
320
+ Credentials and access tokens are wrapped in `SecretValue`, a class that
321
+ redacts content from `toString()`, `toJSON()`, and `console.log()` output.
322
+ This prevents accidental logging of secrets.
323
+
324
+ To retrieve the raw string value (e.g., for HTTP headers or storage), call
325
+ `.expose()`:
326
+
327
+ ```typescript
328
+ const tokenSet = await auth.authenticate(host, httpClient, storage);
329
+ const raw: string = tokenSet.accessToken.expose(); // explicit opt-in
330
+ ```
331
+
332
+ ### TLS Verification
333
+
334
+ TLS certificate verification is **enabled by default** and should never be
335
+ disabled in production. For development environments with self-signed
336
+ certificates:
337
+
338
+ ```typescript
339
+ // Development only — never use in production
340
+ const client = new SafeguardClient('dev-appliance.local', {
341
+ auth: new PasswordAuth({ ... }),
342
+ verify: false,
343
+ });
344
+ ```
345
+
346
+ For production with a custom CA:
347
+
348
+ ```typescript
349
+ const client = new SafeguardClient('safeguard.corp', { auth });
350
+ client.setHttpClient(new NodeHttpClient({ ca: '/path/to/ca-bundle.pem' }));
351
+ ```
352
+
353
+ ### Host Validation
354
+
355
+ The `SafeguardClient` constructor validates the `host` parameter to prevent
356
+ injection attacks. Only bare hostnames or IP addresses are accepted — URLs,
357
+ paths, ports, query strings, and whitespace are rejected with a
358
+ `ConfigurationError`.
359
+
360
+ ### Token Lifetime
361
+
362
+ `client.getAccessTokenLifetimeRemaining()` decodes the JWT `exp` claim
363
+ client-side. This is a convenience for scheduling token refresh — never use it
364
+ as an authorization decision. The server is the sole authority on token
365
+ validity.
366
+
367
+ ## About the Safeguard API
368
+
369
+ The Safeguard API is a REST-based Web API. Safeguard API endpoints are called
370
+ using HTTP operators and JSON requests and responses. The Safeguard API is
371
+ documented using Swagger. You may use Swagger UI to call the API directly or
372
+ to read the documentation about URLs, parameters, and payloads.
373
+
374
+ To access the Swagger UI use a browser to navigate to:
375
+ `https://<address>/service/<service>/swagger`
376
+
377
+ - `<address>` = Safeguard network address
378
+ - `<service>` = Safeguard service to use
379
+
380
+ To access the Swagger OpenAPI specification:
381
+ `https://<address>/service/<service>/swagger/v4/swagger.json`
382
+
383
+ ### Query Parameters
384
+
385
+ | Parameter | Example | Description |
386
+ |-----------|---------|-------------|
387
+ | `filter` | `filter=Name eq 'Admin'` | Filter results |
388
+ | `fields` | `fields=Id,Name` | Select specific properties |
389
+ | `orderby` | `orderby=Name` or `orderby=-Name` | Sort ascending/descending |
390
+ | `page` | `page=0` | Page number (0-based) |
391
+ | `limit` | `limit=100` | Results per page |
392
+ | `count` | `count=true` | Include total count |
393
+ | `q` | `q=admin` | Full-text search |
394
+
395
+ ## Migration from v7.x
396
+
397
+ See [MIGRATION.md](MIGRATION.md) for a complete guide to upgrading from the
398
+ legacy JavaScript API to the v8.0 TypeScript SDK.
399
+
400
+ ## Related Projects
401
+
402
+ | Project | Language | Description |
403
+ |---------|----------|-------------|
404
+ | [SafeguardDotNet](https://github.com/OneIdentity/SafeguardDotNet) | C# | .NET SDK |
405
+ | [PySafeguard](https://github.com/OneIdentity/PySafeguard) | Python | Python SDK |
406
+ | [SafeguardJava](https://github.com/OneIdentity/SafeguardJava) | Java | Java SDK |
407
+ | [safeguard-ps](https://github.com/OneIdentity/safeguard-ps) | PowerShell | PowerShell module |
408
+ | [safeguard-bash](https://github.com/OneIdentity/safeguard-bash) | Bash | Bash utilities |