authmi 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/next.js CHANGED
@@ -4452,49 +4452,65 @@ var next_exports = {};
4452
4452
  __export2(next_exports, {
4453
4453
  AuthMiClient: () => AuthMiClient,
4454
4454
  AuthMiError: () => AuthMiError,
4455
- LocalStorageTokenStorage: () => LocalStorageTokenStorage,
4456
- MemoryTokenStorage: () => MemoryTokenStorage,
4457
4455
  ScopeError: () => ScopeError,
4458
4456
  createAuthMiMiddleware: () => createAuthMiMiddleware,
4459
- validateScopes: () => validateScopes,
4460
- withScope: () => withScope
4457
+ validateScopes: () => validateScopes
4461
4458
  });
4462
4459
  module.exports = __toCommonJS(next_exports);
4463
4460
  var import_server = __toESM(require_server());
4464
4461
 
4465
- // src/types.ts
4466
- var AuthMiError = class extends Error {
4467
- constructor(message, code, statusCode) {
4468
- super(message);
4469
- this.code = code;
4470
- this.statusCode = statusCode;
4471
- this.name = "AuthMiError";
4472
- }
4473
- };
4474
- var ScopeError = class extends AuthMiError {
4475
- constructor(message, requiredScopes, providedScopes) {
4476
- super(message, "INSUFFICIENT_SCOPES", 403);
4477
- this.requiredScopes = requiredScopes;
4478
- this.providedScopes = providedScopes;
4479
- this.name = "ScopeError";
4462
+ // src/config.ts
4463
+ function resolveConfig(provider) {
4464
+ const raw = typeof provider === "function" ? provider() : provider;
4465
+ return {
4466
+ baseUrl: raw.baseUrl,
4467
+ apiKey: raw.apiKey ?? "",
4468
+ serviceId: raw.serviceId ?? "",
4469
+ clientId: raw.clientId ?? "",
4470
+ clientSecret: raw.clientSecret ?? "",
4471
+ defaultTokenExpiry: raw.defaultTokenExpiry ?? 24
4472
+ };
4473
+ }
4474
+
4475
+ // src/storage/localStorage.ts
4476
+ var STORAGE_KEY = "authmi_token";
4477
+ var LocalStorageTokenStorage = class {
4478
+ canAccess() {
4479
+ if (typeof window === "undefined") return false;
4480
+ try {
4481
+ const probe = "__authmi_storage_test__";
4482
+ localStorage.setItem(probe, "1");
4483
+ localStorage.removeItem(probe);
4484
+ return true;
4485
+ } catch {
4486
+ return false;
4487
+ }
4480
4488
  }
4481
- };
4482
- var _LocalStorageTokenStorage = class _LocalStorageTokenStorage {
4483
4489
  getToken() {
4484
- if (typeof window === "undefined") return null;
4485
- return localStorage.getItem(_LocalStorageTokenStorage.KEY);
4490
+ if (!this.canAccess()) return null;
4491
+ try {
4492
+ return localStorage.getItem(STORAGE_KEY);
4493
+ } catch {
4494
+ return null;
4495
+ }
4486
4496
  }
4487
4497
  setToken(token) {
4488
- if (typeof window === "undefined") return;
4489
- localStorage.setItem(_LocalStorageTokenStorage.KEY, token);
4498
+ if (!this.canAccess()) return;
4499
+ try {
4500
+ localStorage.setItem(STORAGE_KEY, token);
4501
+ } catch {
4502
+ }
4490
4503
  }
4491
4504
  removeToken() {
4492
- if (typeof window === "undefined") return;
4493
- localStorage.removeItem(_LocalStorageTokenStorage.KEY);
4505
+ if (!this.canAccess()) return;
4506
+ try {
4507
+ localStorage.removeItem(STORAGE_KEY);
4508
+ } catch {
4509
+ }
4494
4510
  }
4495
4511
  };
4496
- _LocalStorageTokenStorage.KEY = "authmi_token";
4497
- var LocalStorageTokenStorage = _LocalStorageTokenStorage;
4512
+
4513
+ // src/storage/memory.ts
4498
4514
  var MemoryTokenStorage = class {
4499
4515
  constructor() {
4500
4516
  this.token = null;
@@ -4510,51 +4526,56 @@ var MemoryTokenStorage = class {
4510
4526
  }
4511
4527
  };
4512
4528
 
4529
+ // src/types.ts
4530
+ var AuthMiError = class extends Error {
4531
+ constructor(message, code, statusCode) {
4532
+ super(message);
4533
+ this.code = code;
4534
+ this.statusCode = statusCode;
4535
+ this.name = "AuthMiError";
4536
+ }
4537
+ };
4538
+ var ScopeError = class extends AuthMiError {
4539
+ constructor(message, requiredScopes, providedScopes) {
4540
+ super(message, "INSUFFICIENT_SCOPES", 403);
4541
+ this.requiredScopes = requiredScopes;
4542
+ this.providedScopes = providedScopes;
4543
+ this.name = "ScopeError";
4544
+ }
4545
+ };
4546
+
4513
4547
  // src/client.ts
4514
4548
  var AuthMiClient = class {
4515
- constructor(config, tokenStorage) {
4516
- this.config = {
4517
- apiKey: "",
4518
- serviceId: "",
4519
- clientId: "",
4520
- clientSecret: "",
4521
- defaultTokenExpiry: 24,
4522
- ...config
4523
- };
4524
- this.tokenStorage = tokenStorage || new LocalStorageTokenStorage();
4549
+ constructor(config, options) {
4550
+ this.configProvider = config;
4551
+ this.storage = options?.storage ?? (typeof window !== "undefined" ? new LocalStorageTokenStorage() : new MemoryTokenStorage());
4552
+ this.cache = options?.cache || null;
4525
4553
  }
4526
- /** Build Basic auth header from clientId:clientSecret */
4527
- basicAuthHeader() {
4528
- if (this.config.clientId && this.config.clientSecret) {
4529
- const encoded = btoa(`${this.config.clientId}:${this.config.clientSecret}`);
4530
- return `Basic ${encoded}`;
4531
- }
4532
- return null;
4554
+ /** Resolve current config (supports lazy evaluation) */
4555
+ get config() {
4556
+ return resolveConfig(this.configProvider);
4533
4557
  }
4534
- // ============ HTTP Utilities ============
4535
- async request(endpoint, options = {}, requireAuth = false) {
4536
- const url = `${this.config.baseUrl}${endpoint}`;
4558
+ // ============ HTTP plumbing ============
4559
+ async request(endpoint, options = {}, requireUserToken = false, requireServiceAuth = true) {
4560
+ const cfg = this.config;
4561
+ const url = `${cfg.baseUrl}${endpoint}`;
4537
4562
  const headers = {
4538
4563
  "Content-Type": "application/json",
4539
4564
  ...options.headers || {}
4540
4565
  };
4541
- const basic = this.basicAuthHeader();
4542
- if (basic && !requireAuth && !headers["Authorization"]) {
4543
- headers["Authorization"] = basic;
4544
- } else if (this.config.apiKey && !requireAuth && !headers["Authorization"]) {
4545
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
4546
- }
4547
- if (requireAuth) {
4548
- const token = this.getAccessToken();
4549
- if (token && !headers["Authorization"]) {
4550
- headers["Authorization"] = `Bearer ${token}`;
4566
+ if (!headers["Authorization"]) {
4567
+ if (requireUserToken) {
4568
+ const token = await this.storage.getToken();
4569
+ if (token) headers["Authorization"] = `Bearer ${token}`;
4570
+ } else if (requireServiceAuth && cfg.clientId && cfg.clientSecret) {
4571
+ headers["Authorization"] = `Basic ${btoa(`${cfg.clientId}:${cfg.clientSecret}`)}`;
4572
+ headers["X-Service-ID"] = cfg.serviceId || cfg.clientId;
4573
+ } else if (requireServiceAuth && cfg.apiKey) {
4574
+ headers["Authorization"] = `Bearer ${cfg.apiKey}`;
4551
4575
  }
4552
4576
  }
4553
4577
  try {
4554
- const response = await fetch(url, {
4555
- ...options,
4556
- headers
4557
- });
4578
+ const response = await fetch(url, { ...options, headers });
4558
4579
  const data = await response.json();
4559
4580
  if (!response.ok) {
4560
4581
  if (response.status === 403 && data.required && data.provided) {
@@ -4572,9 +4593,7 @@ var AuthMiClient = class {
4572
4593
  }
4573
4594
  return data;
4574
4595
  } catch (error) {
4575
- if (error instanceof AuthMiError) {
4576
- throw error;
4577
- }
4596
+ if (error instanceof AuthMiError) throw error;
4578
4597
  throw new AuthMiError(
4579
4598
  error instanceof Error ? error.message : "Network error",
4580
4599
  "NETWORK_ERROR"
@@ -4582,384 +4601,497 @@ var AuthMiClient = class {
4582
4601
  }
4583
4602
  }
4584
4603
  // ============ Configuration ============
4585
- /**
4586
- * Update the client's service configuration
4587
- */
4588
4604
  configure(config) {
4589
- this.config = { ...this.config, ...config };
4605
+ const current = resolveConfig(this.configProvider);
4606
+ this.configProvider = { ...current, ...config };
4590
4607
  }
4591
- /**
4592
- * Get current configuration (without sensitive data)
4593
- */
4594
4608
  getConfig() {
4595
- const { apiKey: _, clientSecret: __, ...rest } = this.config;
4609
+ const { apiKey: _, clientSecret: __, ...rest } = resolveConfig(this.configProvider);
4596
4610
  return rest;
4597
4611
  }
4598
4612
  // ============ Token Management ============
4599
- /**
4600
- * Get the stored access token
4601
- */
4602
- getAccessToken() {
4603
- return this.tokenStorage.getToken();
4604
- }
4605
- /**
4606
- * Store an access token
4607
- */
4613
+ async getAccessToken() {
4614
+ return this.storage.getToken();
4615
+ }
4608
4616
  setAccessToken(token) {
4609
- this.tokenStorage.setToken(token);
4617
+ this.storage.setToken(token);
4610
4618
  }
4611
- /**
4612
- * Remove the stored access token (logout)
4613
- */
4614
4619
  clearAccessToken() {
4615
- this.tokenStorage.removeToken();
4616
- }
4617
- /**
4618
- * Check if user is logged in
4619
- */
4620
- isLoggedIn() {
4621
- return !!this.getAccessToken();
4622
- }
4623
- // ============ Scope Validation ============
4624
- /**
4625
- * Introspect a token or API key and validate required scopes
4626
- */
4627
- async introspect(request) {
4628
- return this.request("/api/v1/introspect", {
4629
- method: "POST",
4630
- body: JSON.stringify(request)
4631
- });
4632
- }
4633
- /**
4634
- * Validate the current token with optional scope checking
4635
- */
4636
- async validateWithScopes(requiredScopes) {
4637
- const token = this.getAccessToken();
4638
- if (!token) {
4639
- return { valid: false, error: "No token provided" };
4640
- }
4641
- if (!this.config.apiKey && !this.config.clientSecret) {
4642
- return { valid: false, error: "Service credentials not configured" };
4643
- }
4644
- if (!this.config.serviceId) {
4645
- return { valid: false, error: "Service ID not configured in AuthMiConfig" };
4646
- }
4647
- try {
4648
- const result = await this.introspect({
4649
- serviceId: this.config.serviceId,
4650
- token,
4651
- requiredScopes
4652
- });
4653
- return {
4654
- valid: result.valid,
4655
- type: result.type === "bearer" ? "token" : "api_key",
4656
- scopes: result.scopes,
4657
- userId: result.userId,
4658
- email: result.email,
4659
- error: result.error
4660
- };
4661
- } catch (error) {
4662
- if (error instanceof ScopeError) {
4663
- return {
4664
- valid: false,
4665
- error: error.message,
4666
- scopes: error.providedScopes
4667
- };
4668
- }
4669
- if (error instanceof AuthMiError && error.statusCode === 401) {
4670
- return { valid: false, error: "Invalid or expired token" };
4671
- }
4672
- throw error;
4673
- }
4674
- }
4675
- /**
4676
- * Check if the current user has all the required scopes
4677
- * Throws ScopeError if scopes are insufficient
4678
- */
4679
- async requireScopes(...requiredScopes) {
4680
- const result = await this.validateWithScopes(requiredScopes);
4681
- if (!result.valid) {
4682
- throw new AuthMiError(
4683
- result.error || "Authentication required",
4684
- "UNAUTHORIZED",
4685
- 401
4686
- );
4687
- }
4688
- const hasScopes = requiredScopes.every(
4689
- (scope) => result.scopes?.includes(scope)
4690
- );
4691
- if (!hasScopes) {
4692
- throw new ScopeError(
4693
- `Required scopes: ${requiredScopes.join(", ")}`,
4694
- requiredScopes,
4695
- result.scopes || []
4696
- );
4697
- }
4698
- return result;
4699
- }
4700
- /**
4701
- * Execute a function only if the user has the required scopes
4702
- */
4703
- async withScope(scopes, fn) {
4704
- await this.requireScopes(...scopes);
4705
- return fn();
4706
- }
4707
- // ============ OAuth ============
4708
- /**
4709
- * Initiate OAuth login flow and return the authorization URL
4710
- */
4711
- async initiateOAuth(provider, redirectUri) {
4712
- if (!this.config.apiKey && !this.config.clientSecret) {
4713
- throw new AuthMiError(
4714
- "API key or client credentials required for OAuth. Configure the client first.",
4715
- "MISSING_CREDENTIALS"
4716
- );
4717
- }
4718
- if (!this.config.serviceId) {
4719
- throw new AuthMiError(
4720
- "serviceId required for OAuth. Configure the client first.",
4721
- "MISSING_SERVICE_ID"
4722
- );
4723
- }
4724
- const response = await this.request(
4725
- "/api/v1/auth/oauth/initiate",
4726
- {
4727
- method: "POST",
4728
- body: JSON.stringify({ provider, redirectUri, client_id: this.config.serviceId })
4729
- }
4730
- );
4731
- return response.url;
4620
+ this.storage.removeToken();
4732
4621
  }
4733
- /**
4734
- * Handle OAuth callback URL and store the access token
4735
- * Returns null if no OAuth params are present in the URL
4736
- */
4737
- handleOAuthCallback(url) {
4738
- const parsedUrl = typeof url === "string" ? new URL(url) : url;
4739
- const token = parsedUrl.searchParams.get("token");
4740
- const userId = parsedUrl.searchParams.get("user_id");
4741
- const email = parsedUrl.searchParams.get("email");
4742
- if (!token || !userId || !email) {
4743
- return null;
4744
- }
4745
- this.setAccessToken(token);
4746
- return { token, userId, email };
4622
+ async isLoggedIn() {
4623
+ const token = await this.storage.getToken();
4624
+ return !!token;
4747
4625
  }
4748
- /**
4749
- * Check if the current URL contains OAuth callback params
4750
- */
4751
- isOAuthCallback(url) {
4752
- const parsedUrl = typeof url === "string" ? new URL(url) : url;
4753
- return parsedUrl.searchParams.has("token") && parsedUrl.searchParams.has("user_id") && parsedUrl.searchParams.has("email");
4626
+ // ============ Auth Endpoints ============
4627
+ async signup(credentials) {
4628
+ const response = await this.request("/api/v1/auth/signup", {
4629
+ method: "POST",
4630
+ body: JSON.stringify(credentials)
4631
+ }, false, false);
4632
+ return this.mapAuthToken(response);
4754
4633
  }
4755
- // ============ Authentication ============
4756
- /**
4757
- * Login a user and store the access token
4758
- */
4759
4634
  async login(credentials) {
4760
- if (!this.config.apiKey && !this.config.clientSecret) {
4761
- throw new AuthMiError(
4762
- "API key or client credentials required for login. Configure the client first.",
4763
- "MISSING_CREDENTIALS"
4764
- );
4765
- }
4635
+ const cfg = this.config;
4766
4636
  const response = await this.request("/api/v1/auth/login", {
4767
4637
  method: "POST",
4768
4638
  body: JSON.stringify({
4769
4639
  email: credentials.email,
4770
4640
  password: credentials.password,
4771
- client_id: this.config.serviceId,
4772
- expires_in_hours: credentials.expiresInHours || this.config.defaultTokenExpiry,
4641
+ client_id: cfg.serviceId,
4642
+ expires_in_hours: credentials.expiresInHours || cfg.defaultTokenExpiry,
4773
4643
  scopes: credentials.scopes
4774
4644
  })
4775
- });
4776
- const token = {
4777
- accessToken: response.access_token,
4778
- tokenType: response.token_type,
4779
- expiresIn: response.expires_in,
4780
- expiresAt: response.expires_at,
4781
- userId: response.user_id,
4782
- email: response.email,
4783
- scopes: response.scopes
4784
- };
4645
+ }, false, false);
4646
+ const token = this.mapAuthToken(response);
4785
4647
  this.setAccessToken(token.accessToken);
4786
4648
  return token;
4787
4649
  }
4788
- /**
4789
- * Logout the current user
4790
- */
4791
4650
  async logout() {
4792
- const token = this.getAccessToken();
4651
+ const token = await this.storage.getToken();
4793
4652
  if (token) {
4794
4653
  try {
4795
- await this.request("/api/v1/auth/logout", {
4796
- method: "POST"
4797
- }, true);
4798
- } catch (error) {
4654
+ await this.request("/api/v1/auth/logout", { method: "POST" }, true);
4655
+ } catch {
4799
4656
  }
4800
4657
  }
4801
4658
  this.clearAccessToken();
4802
4659
  }
4803
- /**
4804
- * Validate the current token or an API key
4805
- * @deprecated Use validateWithScopes instead
4806
- */
4807
- async validate(_credential) {
4808
- return this.validateWithScopes();
4809
- }
4810
- /**
4811
- * Refresh the current token
4812
- */
4813
4660
  async refreshToken() {
4814
- const response = await this.request("/api/v1/auth/refresh", {
4815
- method: "POST"
4816
- }, true);
4661
+ const response = await this.request("/api/v1/auth/refresh", { method: "POST" }, true);
4817
4662
  const token = {
4818
4663
  accessToken: response.access_token,
4819
- tokenType: response.token_type,
4664
+ tokenType: "Bearer",
4820
4665
  expiresIn: response.expires_in,
4821
4666
  expiresAt: response.expires_at,
4822
4667
  userId: "",
4823
- // Refresh doesn't return user info
4824
4668
  email: ""
4825
4669
  };
4826
4670
  this.setAccessToken(token.accessToken);
4827
4671
  return token;
4828
4672
  }
4829
- // ============ User Management ============
4830
- /**
4831
- * Create a new user (requires admin API key)
4832
- */
4833
- async createUser(request) {
4834
- const response = await this.request("/api/v1/users/create", {
4673
+ async getMe() {
4674
+ const response = await this.request("/api/v1/auth/me", {}, true);
4675
+ return { userId: response.user_id, email: response.email, name: response.name, createdAt: response.created_at };
4676
+ }
4677
+ async forgotPassword(request) {
4678
+ return this.request("/api/v1/auth/forgot-password", {
4835
4679
  method: "POST",
4836
- body: JSON.stringify(request)
4837
- });
4680
+ body: JSON.stringify({ email: request.email, client_id: this.config.serviceId })
4681
+ }, false, false);
4682
+ }
4683
+ async resetPassword(request) {
4684
+ return this.request("/api/v1/auth/reset-password", {
4685
+ method: "POST",
4686
+ body: JSON.stringify({ token: request.token, new_password: request.newPassword })
4687
+ }, false, false);
4688
+ }
4689
+ async verifyEmail(request) {
4690
+ return this.request("/api/v1/auth/verify-email", {
4691
+ method: "POST",
4692
+ body: JSON.stringify({ token: request.token })
4693
+ }, false, false);
4694
+ }
4695
+ async resendVerification(request) {
4696
+ return this.request("/api/v1/auth/resend-verification", {
4697
+ method: "POST",
4698
+ body: JSON.stringify({ email: request.email, client_id: this.config.serviceId })
4699
+ }, false, false);
4700
+ }
4701
+ async deactivateAccount(password) {
4702
+ await this.request("/api/v1/auth/deactivate", {
4703
+ method: "POST",
4704
+ body: JSON.stringify({ password })
4705
+ }, true, false);
4706
+ }
4707
+ mapAuthToken(raw) {
4838
4708
  return {
4839
- userId: response.user_id,
4840
- email: response.email,
4841
- createdAt: response.created_at
4709
+ accessToken: raw.access_token,
4710
+ tokenType: raw.token_type,
4711
+ expiresIn: raw.expires_in,
4712
+ expiresAt: raw.expires_at,
4713
+ userId: raw.user_id,
4714
+ email: raw.email,
4715
+ emailVerified: raw.email_verified,
4716
+ scopes: raw.scopes
4842
4717
  };
4843
4718
  }
4844
- /**
4845
- * List all users for the service
4846
- */
4719
+ // ============ OAuth ============
4720
+ async initiateOAuth(provider, redirectUri) {
4721
+ const cfg = this.config;
4722
+ if (!cfg.clientId && !cfg.clientSecret && !cfg.apiKey) {
4723
+ throw new AuthMiError("Credentials required for OAuth", "MISSING_CREDENTIALS");
4724
+ }
4725
+ const response = await this.request("/api/v1/auth/oauth/initiate", {
4726
+ method: "POST",
4727
+ body: JSON.stringify({ provider, redirectUri, client_id: cfg.serviceId || cfg.clientId })
4728
+ }, false, false);
4729
+ return response.url;
4730
+ }
4731
+ handleOAuthCallback(url) {
4732
+ const parsed = typeof url === "string" ? new URL(url) : url;
4733
+ const token = parsed.searchParams.get("token");
4734
+ const userId = parsed.searchParams.get("user_id");
4735
+ const email = parsed.searchParams.get("email");
4736
+ if (!token || !userId || !email) return null;
4737
+ this.setAccessToken(token);
4738
+ return { token, userId, email };
4739
+ }
4740
+ isOAuthCallback(url) {
4741
+ const parsed = typeof url === "string" ? new URL(url) : url;
4742
+ return !!(parsed.searchParams.has("token") && parsed.searchParams.has("user_id") && parsed.searchParams.has("email"));
4743
+ }
4744
+ // ============ SAML ============
4745
+ async initiateSaml(request) {
4746
+ return this.request("/api/v1/auth/saml/initiate", {
4747
+ method: "POST",
4748
+ body: JSON.stringify({ email: request.email, redirect_uri: request.redirectUri })
4749
+ }, false, false);
4750
+ }
4751
+ async parseSamlMetadata(xml) {
4752
+ return this.request("/api/v1/auth/saml/parse-metadata", {
4753
+ method: "POST",
4754
+ body: JSON.stringify({ metadata_xml: xml })
4755
+ }, false, false);
4756
+ }
4757
+ async listSamlConnections() {
4758
+ const response = await this.request("/api/v1/auth/saml/connections");
4759
+ return response.connections;
4760
+ }
4761
+ async createSamlConnection(request) {
4762
+ return this.request("/api/v1/auth/saml/connections", {
4763
+ method: "POST",
4764
+ body: JSON.stringify(request)
4765
+ });
4766
+ }
4767
+ async deleteSamlConnection(id) {
4768
+ await this.request(`/api/v1/auth/saml/connections/${encodeURIComponent(id)}`, {
4769
+ method: "DELETE"
4770
+ });
4771
+ }
4772
+ // ============ 2FA ============
4773
+ async setup2fa() {
4774
+ return this.request("/api/v1/auth/2fa/setup", { method: "POST" }, true, false);
4775
+ }
4776
+ async enable2fa(code) {
4777
+ return this.request("/api/v1/auth/2fa/enable", {
4778
+ method: "POST",
4779
+ body: JSON.stringify({ code })
4780
+ }, true, false);
4781
+ }
4782
+ async disable2fa(password) {
4783
+ await this.request("/api/v1/auth/2fa/disable", {
4784
+ method: "POST",
4785
+ body: JSON.stringify({ password })
4786
+ }, true, false);
4787
+ }
4788
+ async regenerateBackupCodes() {
4789
+ return this.request("/api/v1/auth/2fa/backup-codes", { method: "POST" }, true, false);
4790
+ }
4791
+ async challenge2fa(request) {
4792
+ return this.request("/api/v1/auth/2fa/challenge", {
4793
+ method: "POST",
4794
+ body: JSON.stringify(request)
4795
+ }, false, false);
4796
+ }
4797
+ // ============ Users ============
4798
+ async createUser(request) {
4799
+ const response = await this.request(
4800
+ "/api/v1/users/create",
4801
+ {
4802
+ method: "POST",
4803
+ body: JSON.stringify(request)
4804
+ }
4805
+ );
4806
+ return { userId: response.user_id, email: response.email, name: response.name, createdAt: response.created_at };
4807
+ }
4847
4808
  async listUsers() {
4848
4809
  const response = await this.request("/api/v1/users/list");
4849
4810
  return response.users.map((u) => ({
4850
4811
  userId: u.user_id,
4851
4812
  email: u.email,
4813
+ name: u.name,
4852
4814
  createdAt: u.created_at
4853
4815
  }));
4854
4816
  }
4855
- /**
4856
- * Delete a user
4857
- */
4817
+ async getUser(userId) {
4818
+ const response = await this.request(
4819
+ `/api/v1/users/${encodeURIComponent(userId)}`
4820
+ );
4821
+ return { userId: response.user_id, email: response.email, name: response.name, createdAt: response.created_at };
4822
+ }
4858
4823
  async deleteUser(userId) {
4859
4824
  await this.request("/api/v1/users/delete", {
4860
4825
  method: "POST",
4861
4826
  body: JSON.stringify({ user_id: userId })
4862
4827
  });
4863
4828
  }
4864
- /**
4865
- * Update user password
4866
- */
4829
+ async deleteUserById(userId) {
4830
+ await this.request(`/api/v1/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
4831
+ }
4867
4832
  async updatePassword(userId, oldPassword, newPassword) {
4868
4833
  await this.request("/api/v1/users/password", {
4869
4834
  method: "POST",
4870
- body: JSON.stringify({
4871
- user_id: userId,
4872
- old_password: oldPassword,
4873
- new_password: newPassword
4874
- })
4835
+ body: JSON.stringify({ user_id: userId, old_password: oldPassword, new_password: newPassword })
4875
4836
  });
4876
4837
  }
4877
- // ============ Scope Management ============
4878
- /**
4879
- * List custom scopes for the service
4880
- */
4881
- async listScopes() {
4882
- const response = await this.request("/api/v1/scopes/list");
4883
- return response.scopes;
4838
+ // ============ Roles ============
4839
+ async createRole(request) {
4840
+ const response = await this.request(
4841
+ "/api/v1/users/roles/create",
4842
+ { method: "POST", body: JSON.stringify(request) }
4843
+ );
4844
+ return { roleId: response.role_id, name: response.name, description: response.description, createdAt: response.created_at };
4884
4845
  }
4885
- /**
4886
- * Create a custom scope
4887
- */
4888
- async createScope(request) {
4889
- const response = await this.request("/api/v1/scopes/create", {
4846
+ async listRoles() {
4847
+ const response = await this.request("/api/v1/users/roles/list");
4848
+ return response.roles.map((r) => ({
4849
+ roleId: r.role_id,
4850
+ name: r.name,
4851
+ description: r.description,
4852
+ createdAt: r.created_at
4853
+ }));
4854
+ }
4855
+ async deleteRole(roleId) {
4856
+ await this.request(`/api/v1/users/roles/${encodeURIComponent(roleId)}`, { method: "DELETE" });
4857
+ }
4858
+ async assignRole(userId, roleId) {
4859
+ await this.request("/api/v1/users/roles/assign", {
4890
4860
  method: "POST",
4891
- body: JSON.stringify(request)
4861
+ body: JSON.stringify({ user_id: userId, role_id: roleId })
4892
4862
  });
4893
- return {
4894
- scopeId: response.scope_id,
4895
- name: response.name,
4896
- key: response.key,
4897
- description: response.description,
4898
- createdAt: response.created_at
4899
- };
4900
4863
  }
4901
- /**
4902
- * Delete a custom scope
4903
- */
4904
- async deleteScope(scopeId) {
4905
- await this.request("/api/v1/scopes/delete", {
4864
+ async unassignRole(userId, roleId) {
4865
+ await this.request("/api/v1/users/roles/unassign", {
4906
4866
  method: "POST",
4907
- body: JSON.stringify({ scope_id: scopeId })
4867
+ body: JSON.stringify({ user_id: userId, role_id: roleId })
4908
4868
  });
4909
4869
  }
4910
- // ============ API Key Management ============
4911
- /**
4912
- * List all API keys for the service
4913
- */
4870
+ async getUserRoles(userId) {
4871
+ const response = await this.request(
4872
+ `/api/v1/users/${encodeURIComponent(userId)}/roles`
4873
+ );
4874
+ return response.roles.map((r) => ({
4875
+ roleId: r.role_id,
4876
+ name: r.name,
4877
+ description: r.description,
4878
+ createdAt: r.created_at
4879
+ }));
4880
+ }
4881
+ // ============ API Keys ============
4914
4882
  async listApiKeys() {
4915
4883
  const response = await this.request("/api/v1/keys/list");
4916
4884
  return response.keys.map((k) => ({
4917
4885
  keyId: k.keyId,
4918
4886
  name: k.name,
4919
4887
  scope: k.scope,
4920
- scopes: [],
4888
+ scopes: k.scopes,
4921
4889
  keyType: k.keyType,
4890
+ tpsLimit: k.tpsLimit,
4922
4891
  revoked: k.revoked,
4923
- createdAt: k.createdAt,
4924
- tpsLimit: k.tpsLimit
4892
+ createdAt: k.createdAt
4925
4893
  }));
4926
4894
  }
4927
- /**
4928
- * Create a new API key with optional custom scopes
4929
- */
4930
4895
  async createApiKey(request) {
4931
- const response = await this.request("/api/v1/keys/create", {
4896
+ const response = await this.request(
4897
+ "/api/v1/keys/create",
4898
+ {
4899
+ method: "POST",
4900
+ body: JSON.stringify({
4901
+ user_id: request.userId,
4902
+ name: request.name,
4903
+ scope: request.scope || "service",
4904
+ scopes: request.scopes,
4905
+ key_type: request.keyType || "service",
4906
+ tps_limit: request.tpsLimit || 100
4907
+ })
4908
+ }
4909
+ );
4910
+ return { keyId: response.key_id, apiKey: response.api_key, name: response.name, scope: response.scope, scopes: response.scopes };
4911
+ }
4912
+ async revokeApiKey(keyId) {
4913
+ await this.request("/api/v1/keys/revoke", {
4914
+ method: "POST",
4915
+ body: JSON.stringify({ key_id: keyId })
4916
+ });
4917
+ }
4918
+ // ============ Scopes ============
4919
+ async listScopes() {
4920
+ const response = await this.request("/api/v1/scopes/list");
4921
+ return response.scopes;
4922
+ }
4923
+ async createScope(request) {
4924
+ const response = await this.request(
4925
+ "/api/v1/scopes/create",
4926
+ { method: "POST", body: JSON.stringify(request) }
4927
+ );
4928
+ return { scopeId: response.scope_id, name: response.name, key: response.key, description: response.description, createdAt: response.created_at };
4929
+ }
4930
+ async deleteScope(scopeId) {
4931
+ await this.request("/api/v1/scopes/delete", {
4932
+ method: "POST",
4933
+ body: JSON.stringify({ scope_id: scopeId })
4934
+ });
4935
+ }
4936
+ // ============ Introspection & Validation ============
4937
+ async introspect(request) {
4938
+ if (this.cache && request.token) {
4939
+ const cached = this.cache.get(request.token);
4940
+ if (cached) return cached;
4941
+ }
4942
+ const result = await this.request("/api/v1/introspect", {
4932
4943
  method: "POST",
4933
4944
  body: JSON.stringify({
4934
- user_id: request.userId,
4935
- name: request.name,
4936
- scope: request.scope || "service",
4937
- scopes: request.scopes,
4938
- key_type: request.keyType || "service",
4939
- tps_limit: request.tpsLimit || 100
4945
+ serviceId: request.serviceId,
4946
+ token: request.token,
4947
+ apiKey: request.apiKey,
4948
+ requiredScopes: request.requiredScopes
4940
4949
  })
4941
4950
  });
4942
- return {
4943
- keyId: response.key_id,
4944
- apiKey: response.api_key,
4945
- name: response.name,
4946
- scope: response.scope,
4947
- scopes: response.scopes
4948
- };
4951
+ if (this.cache && request.token) {
4952
+ this.cache.set(request.token, result);
4953
+ }
4954
+ return result;
4949
4955
  }
4950
- /**
4951
- * Revoke an API key
4952
- */
4953
- async revokeApiKey(keyId) {
4954
- await this.request("/api/v1/keys/revoke", {
4956
+ async validate() {
4957
+ const cfg = this.config;
4958
+ const token = await this.storage.getToken();
4959
+ if (!token) return { valid: false, error: "No token stored" };
4960
+ if (!cfg.serviceId) return { valid: false, error: "Service ID not configured" };
4961
+ if (!cfg.apiKey && !cfg.clientSecret) return { valid: false, error: "Service credentials not configured" };
4962
+ try {
4963
+ const result = await this.introspect({ serviceId: cfg.serviceId, token });
4964
+ return {
4965
+ valid: result.valid,
4966
+ type: result.type === "bearer" ? "token" : "api_key",
4967
+ scopes: result.scopes,
4968
+ userId: result.userId,
4969
+ email: result.email,
4970
+ error: result.error
4971
+ };
4972
+ } catch (error) {
4973
+ if (error instanceof ScopeError) {
4974
+ return { valid: false, error: error.message, scopes: error.providedScopes };
4975
+ }
4976
+ if (error instanceof AuthMiError && error.statusCode === 401) {
4977
+ return { valid: false, error: "Invalid or expired token" };
4978
+ }
4979
+ throw error;
4980
+ }
4981
+ }
4982
+ async validateWithScopes(requiredScopes) {
4983
+ const cfg = this.config;
4984
+ const token = await this.storage.getToken();
4985
+ if (!token) return { valid: false, error: "No token provided" };
4986
+ if (!cfg.apiKey && !cfg.clientSecret) return { valid: false, error: "Service credentials not configured" };
4987
+ if (!cfg.serviceId) return { valid: false, error: "Service ID not configured" };
4988
+ try {
4989
+ const result = await this.introspect({ serviceId: cfg.serviceId, token, requiredScopes });
4990
+ return {
4991
+ valid: result.valid,
4992
+ type: result.type === "bearer" ? "token" : "api_key",
4993
+ scopes: result.scopes,
4994
+ userId: result.userId,
4995
+ email: result.email,
4996
+ error: result.error
4997
+ };
4998
+ } catch (error) {
4999
+ if (error instanceof ScopeError) {
5000
+ return { valid: false, error: error.message, scopes: error.providedScopes };
5001
+ }
5002
+ if (error instanceof AuthMiError && error.statusCode === 401) {
5003
+ return { valid: false, error: "Invalid or expired token" };
5004
+ }
5005
+ throw error;
5006
+ }
5007
+ }
5008
+ async requireScopes(...requiredScopes) {
5009
+ const result = await this.validateWithScopes(requiredScopes);
5010
+ if (!result.valid) {
5011
+ throw new AuthMiError(result.error || "Authentication required", "UNAUTHORIZED", 401);
5012
+ }
5013
+ const hasScopes = requiredScopes.every((s) => result.scopes?.includes(s));
5014
+ if (!hasScopes) {
5015
+ throw new ScopeError(`Required: ${requiredScopes.join(", ")}`, requiredScopes, result.scopes || []);
5016
+ }
5017
+ return result;
5018
+ }
5019
+ async withScope(scopes, fn) {
5020
+ await this.requireScopes(...scopes);
5021
+ return fn();
5022
+ }
5023
+ // ============ Webhooks ============
5024
+ async listWebhooks() {
5025
+ const response = await this.request("/api/v1/webhooks/list");
5026
+ return response.webhooks;
5027
+ }
5028
+ async registerWebhook(request) {
5029
+ const response = await this.request(
5030
+ "/api/v1/webhooks/register",
5031
+ { method: "POST", body: JSON.stringify(request) }
5032
+ );
5033
+ return { webhookId: response.webhook_id, url: response.url, events: response.events, active: response.active, createdAt: response.created_at };
5034
+ }
5035
+ async deleteWebhook(webhookId) {
5036
+ await this.request("/api/v1/webhooks/delete", {
4955
5037
  method: "POST",
4956
- body: JSON.stringify({ key_id: keyId })
5038
+ body: JSON.stringify({ webhook_id: webhookId })
5039
+ });
5040
+ }
5041
+ // ============ Billing ============
5042
+ async listPlans() {
5043
+ const response = await this.request("/api/v1/billing/plans");
5044
+ return response.plans;
5045
+ }
5046
+ async getSubscription() {
5047
+ return this.request("/api/v1/billing/subscription");
5048
+ }
5049
+ async createCheckout(priceId) {
5050
+ return this.request("/api/v1/billing/checkout", {
5051
+ method: "POST",
5052
+ body: JSON.stringify({ price_id: priceId })
5053
+ });
5054
+ }
5055
+ async createPortalSession() {
5056
+ return this.request("/api/v1/billing/portal", { method: "POST" });
5057
+ }
5058
+ // ============ Audit ============
5059
+ async getAuditLogs(query) {
5060
+ const params = new URLSearchParams();
5061
+ if (query?.limit) params.set("limit", String(query.limit));
5062
+ if (query?.offset) params.set("offset", String(query.offset));
5063
+ if (query?.actorId) params.set("actor_id", query.actorId);
5064
+ if (query?.action) params.set("action", query.action);
5065
+ if (query?.resourceType) params.set("resource_type", query.resourceType);
5066
+ if (query?.startDate) params.set("start_date", query.startDate);
5067
+ if (query?.endDate) params.set("end_date", query.endDate);
5068
+ const qs = params.toString();
5069
+ const response = await this.request(
5070
+ `/api/v1/audit-logs${qs ? `?${qs}` : ""}`
5071
+ );
5072
+ return response.logs;
5073
+ }
5074
+ // ============ Services ============
5075
+ async getService(serviceId) {
5076
+ return this.request(`/api/v1/services/${encodeURIComponent(serviceId)}`);
5077
+ }
5078
+ async updateService(serviceId, data) {
5079
+ return this.request(`/api/v1/services/${encodeURIComponent(serviceId)}`, {
5080
+ method: "POST",
5081
+ body: JSON.stringify(data)
4957
5082
  });
4958
5083
  }
4959
- // ============ Service Management ============
4960
- /**
4961
- * Onboard a new service (requires master admin key)
4962
- */
5084
+ async rotateClientSecret(serviceId) {
5085
+ return this.request(
5086
+ `/api/v1/services/${encodeURIComponent(serviceId)}/rotate-secret`,
5087
+ { method: "POST" }
5088
+ );
5089
+ }
5090
+ // ============ JWKS ============
5091
+ async getJwks(serviceId) {
5092
+ return this.request(`/.well-known/jwks.json?service_id=${encodeURIComponent(serviceId)}`);
5093
+ }
5094
+ // ============ Onboarding (admin only) ============
4963
5095
  async onboardService(request, masterAdminKey) {
4964
5096
  const response = await this.request("/api/v1/services/onboard", {
4965
5097
  method: "POST",
@@ -4980,9 +5112,6 @@ var AuthMiClient = class {
4980
5112
  createdAt: response.created_at
4981
5113
  };
4982
5114
  }
4983
- /**
4984
- * Delete a service (requires master admin key)
4985
- */
4986
5115
  async deleteService(serviceId, masterAdminKey) {
4987
5116
  await this.request("/api/v1/services/deboard", {
4988
5117
  method: "POST",
@@ -4992,45 +5121,9 @@ var AuthMiClient = class {
4992
5121
  body: JSON.stringify({ service_id: serviceId })
4993
5122
  });
4994
5123
  }
4995
- // ============ Webhook Management ============
4996
- /**
4997
- * List webhooks
4998
- */
4999
- async listWebhooks() {
5000
- const response = await this.request(
5001
- "/api/v1/webhooks/list"
5002
- );
5003
- return response.webhooks;
5004
- }
5005
- /**
5006
- * Register a webhook
5007
- */
5008
- async registerWebhook(request) {
5009
- const response = await this.request("/api/v1/webhooks/register", {
5010
- method: "POST",
5011
- body: JSON.stringify(request)
5012
- });
5013
- return {
5014
- webhookId: response.webhook_id,
5015
- url: response.url,
5016
- events: response.events,
5017
- active: response.active,
5018
- createdAt: response.created_at
5019
- };
5020
- }
5021
- /**
5022
- * Delete a webhook
5023
- */
5024
- async deleteWebhook(webhookId) {
5025
- await this.request("/api/v1/webhooks/delete", {
5026
- method: "POST",
5027
- body: JSON.stringify({ webhook_id: webhookId })
5028
- });
5029
- }
5030
5124
  };
5031
5125
 
5032
5126
  // src/next.tsx
5033
- var import_jsx_runtime = require("react/jsx-runtime");
5034
5127
  function createAuthMiMiddleware(options) {
5035
5128
  return async function middleware(request) {
5036
5129
  const {
@@ -5048,10 +5141,7 @@ function createAuthMiMiddleware(options) {
5048
5141
  }
5049
5142
  const token = request.cookies.get("authmi_token")?.value || request.headers.get("Authorization")?.replace("Bearer ", "");
5050
5143
  if (!token) {
5051
- if (protectedPaths.some((p) => pathname.startsWith(p))) {
5052
- return import_server.NextResponse.redirect(new URL(loginPath, request.url));
5053
- }
5054
- if (Object.keys(scopedPaths).some((p) => pathname.startsWith(p))) {
5144
+ if (protectedPaths.some((p) => pathname.startsWith(p)) || Object.keys(scopedPaths).some((p) => pathname.startsWith(p))) {
5055
5145
  return import_server.NextResponse.redirect(new URL(loginPath, request.url));
5056
5146
  }
5057
5147
  return import_server.NextResponse.next();
@@ -5083,9 +5173,7 @@ function createAuthMiMiddleware(options) {
5083
5173
  requestHeaders.set("x-user-email", introspectResult.email || "");
5084
5174
  requestHeaders.set("x-user-scopes", JSON.stringify(introspectResult.scopes || []));
5085
5175
  return import_server.NextResponse.next({
5086
- request: {
5087
- headers: requestHeaders
5088
- }
5176
+ request: { headers: requestHeaders }
5089
5177
  });
5090
5178
  } catch (error) {
5091
5179
  console.error("Auth middleware error:", error);
@@ -5096,11 +5184,6 @@ function createAuthMiMiddleware(options) {
5096
5184
  }
5097
5185
  };
5098
5186
  }
5099
- function withScope(Component, _requiredScopes, _config) {
5100
- return function WithScopeWrapper(props) {
5101
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props });
5102
- };
5103
- }
5104
5187
  async function validateScopes(request, options) {
5105
5188
  const { baseUrl, apiKey, requiredScopes, serviceId } = options;
5106
5189
  const authHeader = request.headers.get("Authorization");
@@ -5109,12 +5192,9 @@ async function validateScopes(request, options) {
5109
5192
  return { valid: false, error: "No token provided", status: 401 };
5110
5193
  }
5111
5194
  const client = new AuthMiClient({ baseUrl, apiKey, serviceId });
5112
- if (!serviceId) {
5113
- return { valid: false, error: "Service ID not configured", status: 500 };
5114
- }
5115
5195
  try {
5116
5196
  const result = await client.introspect({
5117
- serviceId: options.serviceId,
5197
+ serviceId,
5118
5198
  token,
5119
5199
  requiredScopes
5120
5200
  });
@@ -5139,10 +5219,7 @@ async function validateScopes(request, options) {
5139
5219
  0 && (module.exports = {
5140
5220
  AuthMiClient,
5141
5221
  AuthMiError,
5142
- LocalStorageTokenStorage,
5143
- MemoryTokenStorage,
5144
5222
  ScopeError,
5145
5223
  createAuthMiMiddleware,
5146
- validateScopes,
5147
- withScope
5224
+ validateScopes
5148
5225
  });