@drmhse/sso-sdk 0.2.9 → 0.3.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/dist/index.js CHANGED
@@ -147,7 +147,7 @@ var HttpClient = class {
147
147
  signal: controller.signal
148
148
  });
149
149
  clearTimeout(timeoutId);
150
- if (response.status === 401 && this.sessionManager && !options._retry && !path.includes("/auth/login")) {
150
+ if (response.status === 401 && this.sessionManager && !options._retry && !path.includes("/auth/login") && !path.includes("/auth/refresh")) {
151
151
  try {
152
152
  const newToken = await this.sessionManager.refreshSession();
153
153
  return this.request(path, {
@@ -731,6 +731,23 @@ var AuthModule = class {
731
731
  const response = await this.http.post("/api/auth/register", payload);
732
732
  return response.data;
733
733
  }
734
+ /**
735
+ * Verify an email address using the token from the verification email.
736
+ *
737
+ * @param token Verification token
738
+ * @returns HTML success page string
739
+ *
740
+ * @example
741
+ * ```typescript
742
+ * const html = await sso.auth.verifyEmail('token-from-email');
743
+ * ```
744
+ */
745
+ async verifyEmail(token) {
746
+ const response = await this.http.get("/auth/verify-email", {
747
+ params: { token }
748
+ });
749
+ return response.data;
750
+ }
734
751
  /**
735
752
  * Login with email and password.
736
753
  * Automatically persists the session and configures the client.
@@ -1490,6 +1507,43 @@ var WebhooksModule = class {
1490
1507
  var OrganizationsModule = class {
1491
1508
  constructor(http) {
1492
1509
  this.http = http;
1510
+ /**
1511
+ * SCIM token management methods
1512
+ */
1513
+ this.scim = {
1514
+ /**
1515
+ * Create a new SCIM token.
1516
+ * The token is only returned once upon creation.
1517
+ */
1518
+ createToken: async (orgSlug, payload) => {
1519
+ const response = await this.http.post(
1520
+ `/api/organizations/${orgSlug}/scim-tokens`,
1521
+ payload
1522
+ );
1523
+ return response.data;
1524
+ },
1525
+ /**
1526
+ * List all SCIM tokens.
1527
+ */
1528
+ listTokens: async (orgSlug) => {
1529
+ const response = await this.http.get(
1530
+ `/api/organizations/${orgSlug}/scim-tokens`
1531
+ );
1532
+ return response.data;
1533
+ },
1534
+ /**
1535
+ * Revoke a SCIM token.
1536
+ */
1537
+ revokeToken: async (orgSlug, tokenId) => {
1538
+ await this.http.post(`/api/organizations/${orgSlug}/scim-tokens/${tokenId}/revoke`);
1539
+ },
1540
+ /**
1541
+ * Delete a SCIM token.
1542
+ */
1543
+ deleteToken: async (orgSlug, tokenId) => {
1544
+ await this.http.delete(`/api/organizations/${orgSlug}/scim-tokens/${tokenId}`);
1545
+ }
1546
+ };
1493
1547
  /**
1494
1548
  * Member management methods
1495
1549
  */
@@ -1513,6 +1567,24 @@ var OrganizationsModule = class {
1513
1567
  );
1514
1568
  return response.data;
1515
1569
  },
1570
+ /**
1571
+ * Add a member to the organization (Invite + Accept).
1572
+ * This is a convenience method that creates an invitation and immediately accepts it.
1573
+ * Useful for testing and admin operations.
1574
+ *
1575
+ * @param orgSlug Organization slug
1576
+ * @param payload Member details (email, role)
1577
+ * @returns The created invitation
1578
+ */
1579
+ add: async (orgSlug, payload) => {
1580
+ const response = await this.http.post(
1581
+ `/api/organizations/${orgSlug}/invitations`,
1582
+ payload
1583
+ );
1584
+ const invitation = response.data;
1585
+ await this.http.post("/api/invitations/accept", { token: invitation.token });
1586
+ return invitation;
1587
+ },
1516
1588
  /**
1517
1589
  * Update a member's role.
1518
1590
  * Requires 'owner' role.
@@ -2554,6 +2626,26 @@ var ServicesModule = class {
2554
2626
  );
2555
2627
  return response.data;
2556
2628
  },
2629
+ /**
2630
+ * Initiate an IdP-initiated SAML login.
2631
+ * Returns an HTML page with an auto-submitting form that POSTs the SAML assertion to the Service Provider.
2632
+ *
2633
+ * @param orgSlug Organization slug
2634
+ * @param serviceSlug Service slug
2635
+ * @returns HTML page with auto-submitting form
2636
+ *
2637
+ * @example
2638
+ * ```typescript
2639
+ * const html = await sso.services.saml.initiateLogin('acme-corp', 'salesforce');
2640
+ * document.body.innerHTML = html; // Auto-submits
2641
+ * ```
2642
+ */
2643
+ initiateLogin: async (orgSlug, serviceSlug) => {
2644
+ const response = await this.http.get(
2645
+ `/api/organizations/${orgSlug}/services/${serviceSlug}/saml/login`
2646
+ );
2647
+ return response.data;
2648
+ },
2557
2649
  /**
2558
2650
  * Generate a new SAML signing certificate for the IdP.
2559
2651
  * Requires 'owner' or 'admin' role.
@@ -2807,7 +2899,7 @@ var InvitationsModule = class {
2807
2899
  * @example
2808
2900
  * ```typescript
2809
2901
  * const invitation = await sso.invitations.create('acme-corp', {
2810
- * invitee_email: 'newuser@example.com',
2902
+ * email: 'newuser@example.com',
2811
2903
  * role: 'member'
2812
2904
  * });
2813
2905
  * ```
@@ -3343,6 +3435,78 @@ var ServiceApiModule = class {
3343
3435
  constructor(http) {
3344
3436
  this.http = http;
3345
3437
  }
3438
+ /**
3439
+ * List all users for the service
3440
+ * Requires 'read:users' permission on the API key
3441
+ *
3442
+ * @param params Optional pagination parameters
3443
+ * @returns List of users with total count
3444
+ */
3445
+ async listUsers(params) {
3446
+ const queryParams = new URLSearchParams();
3447
+ if (params?.limit) queryParams.set("limit", params.limit.toString());
3448
+ if (params?.offset) queryParams.set("offset", params.offset.toString());
3449
+ const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
3450
+ const response = await this.http.get(`/api/service/users${query}`);
3451
+ return response.data;
3452
+ }
3453
+ /**
3454
+ * Get a specific user by ID
3455
+ * Requires 'read:users' permission on the API key
3456
+ *
3457
+ * @param userId User ID to retrieve
3458
+ * @returns User details
3459
+ */
3460
+ async getUser(userId) {
3461
+ const response = await this.http.get(`/api/service/users/${userId}`);
3462
+ return response.data;
3463
+ }
3464
+ /**
3465
+ * List all subscriptions for the service
3466
+ * Requires 'read:subscriptions' permission on the API key
3467
+ *
3468
+ * @param params Optional pagination parameters
3469
+ * @returns List of subscriptions with total count
3470
+ */
3471
+ async listSubscriptions(params) {
3472
+ const queryParams = new URLSearchParams();
3473
+ if (params?.limit) queryParams.set("limit", params.limit.toString());
3474
+ if (params?.offset) queryParams.set("offset", params.offset.toString());
3475
+ const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
3476
+ const response = await this.http.get(`/api/service/subscriptions${query}`);
3477
+ return response.data;
3478
+ }
3479
+ /**
3480
+ * Get subscription for a specific user
3481
+ * Requires 'read:subscriptions' permission on the API key
3482
+ *
3483
+ * @param userId User ID whose subscription to retrieve
3484
+ * @returns User's subscription
3485
+ */
3486
+ async getSubscription(userId) {
3487
+ const response = await this.http.get(`/api/service/subscriptions/${userId}`);
3488
+ return response.data;
3489
+ }
3490
+ /**
3491
+ * Get analytics for the service
3492
+ * Requires 'read:analytics' permission on the API key
3493
+ *
3494
+ * @returns Service analytics data
3495
+ */
3496
+ async getAnalytics() {
3497
+ const response = await this.http.get("/api/service/analytics");
3498
+ return response.data;
3499
+ }
3500
+ /**
3501
+ * Get service information
3502
+ * Requires 'read:service' permission on the API key
3503
+ *
3504
+ * @returns Service information
3505
+ */
3506
+ async getServiceInfo() {
3507
+ const response = await this.http.get("/api/service/info");
3508
+ return response.data;
3509
+ }
3346
3510
  /**
3347
3511
  * Create a new user
3348
3512
  * Requires 'write:users' permission on the API key
@@ -3766,15 +3930,40 @@ var PasskeysModule = class {
3766
3930
  * }
3767
3931
  * ```
3768
3932
  */
3933
+ /**
3934
+ * Start the passkey registration ceremony.
3935
+ * returns the options required to create credentials in the browser.
3936
+ */
3937
+ async registerStart(displayName) {
3938
+ const response = await this.http.post(
3939
+ "/api/auth/passkeys/register/start",
3940
+ { name: displayName }
3941
+ );
3942
+ return response.data;
3943
+ }
3944
+ /**
3945
+ * Finish the passkey registration ceremony.
3946
+ * Verifies the credential created by the browser.
3947
+ */
3948
+ async registerFinish(challengeId, credential) {
3949
+ const response = await this.http.post(
3950
+ "/api/auth/passkeys/register/finish",
3951
+ {
3952
+ challenge_id: challengeId,
3953
+ credential
3954
+ }
3955
+ );
3956
+ return response.data;
3957
+ }
3958
+ /**
3959
+ * Register a new passkey for the authenticated user
3960
+ * ...
3961
+ */
3769
3962
  async register(displayName) {
3770
3963
  if (!this.isSupported()) {
3771
3964
  throw new Error("WebAuthn is not supported in this browser");
3772
3965
  }
3773
- const startResponse = await this.http.post(
3774
- "/auth/passkeys/register/start",
3775
- { name: displayName }
3776
- );
3777
- const startData = startResponse.data;
3966
+ const startData = await this.registerStart(displayName);
3778
3967
  const createOptions = {
3779
3968
  publicKey: {
3780
3969
  ...startData.options,
@@ -3808,14 +3997,8 @@ var PasskeysModule = class {
3808
3997
  clientExtensionResults: credential.getClientExtensionResults(),
3809
3998
  type: credential.type
3810
3999
  };
3811
- const finishResponse = await this.http.post(
3812
- "/auth/passkeys/register/finish",
3813
- {
3814
- challenge_id: startData.challenge_id,
3815
- credential: credentialJSON
3816
- }
3817
- );
3818
- return finishResponse.data.passkey_id;
4000
+ const finishResponse = await this.registerFinish(startData.challenge_id, credentialJSON);
4001
+ return finishResponse.passkey_id;
3819
4002
  }
3820
4003
  /**
3821
4004
  * Authenticate with a passkey and obtain a JWT token
@@ -3839,16 +4022,40 @@ var PasskeysModule = class {
3839
4022
  * }
3840
4023
  * ```
3841
4024
  */
4025
+ /**
4026
+ * Start the passkey authentication ceremony.
4027
+ * Returns the options required to get credentials from the browser.
4028
+ */
4029
+ async authenticateStart(email) {
4030
+ const response = await this.http.post(
4031
+ "/api/auth/passkeys/authenticate/start",
4032
+ { email }
4033
+ );
4034
+ return response.data;
4035
+ }
4036
+ /**
4037
+ * Finish the passkey authentication ceremony.
4038
+ * Verifies the assertion returned by the browser.
4039
+ */
4040
+ async authenticateFinish(challengeId, credential) {
4041
+ const response = await this.http.post(
4042
+ "/api/auth/passkeys/authenticate/finish",
4043
+ {
4044
+ challenge_id: challengeId,
4045
+ credential
4046
+ }
4047
+ );
4048
+ return response.data;
4049
+ }
4050
+ /**
4051
+ * Authenticate with a passkey and obtain a JWT token
4052
+ * ...
4053
+ */
3842
4054
  async login(email) {
3843
4055
  if (!this.isSupported()) {
3844
4056
  throw new Error("WebAuthn is not supported in this browser");
3845
4057
  }
3846
- const startRequest = { email };
3847
- const startResponse = await this.http.post(
3848
- "/auth/passkeys/authenticate/start",
3849
- startRequest
3850
- );
3851
- const startData = startResponse.data;
4058
+ const startData = await this.authenticateStart(email);
3852
4059
  const getOptions = {
3853
4060
  publicKey: {
3854
4061
  ...startData.options,
@@ -3879,14 +4086,7 @@ var PasskeysModule = class {
3879
4086
  clientExtensionResults: credential.getClientExtensionResults(),
3880
4087
  type: credential.type
3881
4088
  };
3882
- const finishResponse = await this.http.post(
3883
- "/auth/passkeys/authenticate/finish",
3884
- {
3885
- challenge_id: startData.challenge_id,
3886
- credential: credentialJSON
3887
- }
3888
- );
3889
- return finishResponse.data;
4089
+ return this.authenticateFinish(startData.challenge_id, credentialJSON);
3890
4090
  }
3891
4091
  /**
3892
4092
  * Convert Base64URL string to Uint8Array
@@ -4059,6 +4259,10 @@ var SsoClient = class {
4059
4259
  * Sets the JWT for all subsequent authenticated requests.
4060
4260
  * Pass null to clear the token.
4061
4261
  *
4262
+ * NOTE: For OAuth callback flows, prefer using setSession() which properly
4263
+ * updates the SessionManager. This method updates both the HTTP headers
4264
+ * AND the SessionManager for backward compatibility.
4265
+ *
4062
4266
  * @param token The JWT string, or null to clear
4063
4267
  *
4064
4268
  * @example
@@ -4073,10 +4277,30 @@ var SsoClient = class {
4073
4277
  setAuthToken(token) {
4074
4278
  if (token) {
4075
4279
  this.http.defaults.headers.common["Authorization"] = `Bearer ${token}`;
4280
+ this.session.setSession({ access_token: token });
4076
4281
  } else {
4077
4282
  delete this.http.defaults.headers.common["Authorization"];
4283
+ this.session.clearSession();
4078
4284
  }
4079
4285
  }
4286
+ /**
4287
+ * Sets the session tokens for OAuth callback flows.
4288
+ * This properly updates the SessionManager and persists tokens to storage.
4289
+ *
4290
+ * @param tokens Object containing access_token and optionally refresh_token
4291
+ *
4292
+ * @example
4293
+ * ```typescript
4294
+ * // After OAuth callback
4295
+ * await sso.setSession({
4296
+ * access_token: accessToken,
4297
+ * refresh_token: refreshToken
4298
+ * });
4299
+ * ```
4300
+ */
4301
+ async setSession(tokens) {
4302
+ await this.session.setSession(tokens);
4303
+ }
4080
4304
  /**
4081
4305
  * Sets the API key for service-to-service authentication.
4082
4306
  * Pass null to clear the API key.