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