@hed-hog/core 0.0.355 → 0.0.358

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.
Files changed (287) hide show
  1. package/dist/ai/ai.service.d.ts +3 -12
  2. package/dist/ai/ai.service.d.ts.map +1 -1
  3. package/dist/ai/ai.service.js +34 -125
  4. package/dist/ai/ai.service.js.map +1 -1
  5. package/dist/auth/guards/auth.guard.d.ts +6 -1
  6. package/dist/auth/guards/auth.guard.d.ts.map +1 -1
  7. package/dist/auth/guards/auth.guard.js +33 -2
  8. package/dist/auth/guards/auth.guard.js.map +1 -1
  9. package/dist/core.module.d.ts.map +1 -1
  10. package/dist/core.module.js +3 -3
  11. package/dist/core.module.js.map +1 -1
  12. package/dist/file/file.controller.d.ts +4 -2
  13. package/dist/file/file.controller.d.ts.map +1 -1
  14. package/dist/file/file.controller.js +44 -38
  15. package/dist/file/file.controller.js.map +1 -1
  16. package/dist/file/file.service.d.ts +2 -2
  17. package/dist/file/file.service.d.ts.map +1 -1
  18. package/dist/file/file.service.js +58 -89
  19. package/dist/file/file.service.js.map +1 -1
  20. package/dist/file/provider/azure.provider.d.ts.map +1 -1
  21. package/dist/file/provider/azure.provider.js +10 -15
  22. package/dist/file/provider/azure.provider.js.map +1 -1
  23. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  24. package/dist/file/provider/gcs.provider.js +8 -9
  25. package/dist/file/provider/gcs.provider.js.map +1 -1
  26. package/dist/file/provider/local.provider.d.ts.map +1 -1
  27. package/dist/file/provider/local.provider.js +11 -10
  28. package/dist/file/provider/local.provider.js.map +1 -1
  29. package/dist/file/provider/provider.enum.d.ts +2 -1
  30. package/dist/file/provider/provider.enum.d.ts.map +1 -1
  31. package/dist/file/provider/provider.enum.js +1 -0
  32. package/dist/file/provider/provider.enum.js.map +1 -1
  33. package/dist/file/provider/provider.factory.d.ts +1 -1
  34. package/dist/file/provider/provider.factory.d.ts.map +1 -1
  35. package/dist/file/provider/provider.factory.js +18 -5
  36. package/dist/file/provider/provider.factory.js.map +1 -1
  37. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  38. package/dist/file/provider/s3.provider.js +4 -20
  39. package/dist/file/provider/s3.provider.js.map +1 -1
  40. package/dist/file/provider/spaces.provider.d.ts +27 -0
  41. package/dist/file/provider/spaces.provider.d.ts.map +1 -0
  42. package/dist/file/provider/spaces.provider.js +203 -0
  43. package/dist/file/provider/spaces.provider.js.map +1 -0
  44. package/dist/index.d.ts +4 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +4 -3
  47. package/dist/index.js.map +1 -1
  48. package/dist/integration-profile/dto/create.dto.d.ts +9 -0
  49. package/dist/integration-profile/dto/create.dto.d.ts.map +1 -0
  50. package/dist/integration-profile/dto/create.dto.js +54 -0
  51. package/dist/integration-profile/dto/create.dto.js.map +1 -0
  52. package/dist/integration-profile/dto/export.dto.d.ts +5 -0
  53. package/dist/integration-profile/dto/export.dto.d.ts.map +1 -0
  54. package/dist/integration-profile/dto/export.dto.js +29 -0
  55. package/dist/integration-profile/dto/export.dto.js.map +1 -0
  56. package/dist/integration-profile/dto/import-confirm.dto.d.ts +17 -0
  57. package/dist/integration-profile/dto/import-confirm.dto.d.ts.map +1 -0
  58. package/dist/integration-profile/dto/import-confirm.dto.js +77 -0
  59. package/dist/integration-profile/dto/import-confirm.dto.js.map +1 -0
  60. package/dist/integration-profile/dto/update.dto.d.ts.map +1 -0
  61. package/dist/integration-profile/dto/update.dto.js.map +1 -0
  62. package/dist/integration-profile/integration-profile.controller.d.ts +142 -0
  63. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -0
  64. package/dist/integration-profile/integration-profile.controller.js +166 -0
  65. package/dist/integration-profile/integration-profile.controller.js.map +1 -0
  66. package/dist/integration-profile/integration-profile.module.d.ts +3 -0
  67. package/dist/integration-profile/integration-profile.module.d.ts.map +1 -0
  68. package/dist/{mail-profile/mail-profile.module.js → integration-profile/integration-profile.module.js} +11 -13
  69. package/dist/integration-profile/integration-profile.module.js.map +1 -0
  70. package/dist/integration-profile/integration-profile.service.d.ts +160 -0
  71. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -0
  72. package/dist/integration-profile/integration-profile.service.js +609 -0
  73. package/dist/integration-profile/integration-profile.service.js.map +1 -0
  74. package/dist/integration-profile/integration-profile.utils.d.ts +30 -0
  75. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -0
  76. package/dist/integration-profile/integration-profile.utils.js +156 -0
  77. package/dist/integration-profile/integration-profile.utils.js.map +1 -0
  78. package/dist/mail/mail.module.d.ts.map +1 -1
  79. package/dist/mail/mail.module.js +36 -23
  80. package/dist/mail/mail.module.js.map +1 -1
  81. package/dist/mail/mail.service.d.ts +4 -2
  82. package/dist/mail/mail.service.d.ts.map +1 -1
  83. package/dist/mail/mail.service.js +33 -51
  84. package/dist/mail/mail.service.js.map +1 -1
  85. package/dist/mcp/guards/mcp-auth.guard.js +1 -1
  86. package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
  87. package/dist/mcp/mcp-api-key.controller.d.ts +2 -0
  88. package/dist/mcp/mcp-api-key.controller.d.ts.map +1 -1
  89. package/dist/mcp/mcp-api-key.controller.js +1 -1
  90. package/dist/mcp/mcp-api-key.controller.js.map +1 -1
  91. package/dist/mcp/mcp-api-key.service.d.ts +2 -1
  92. package/dist/mcp/mcp-api-key.service.d.ts.map +1 -1
  93. package/dist/mcp/mcp-api-key.service.js +6 -2
  94. package/dist/mcp/mcp-api-key.service.js.map +1 -1
  95. package/dist/mcp/mcp.module.d.ts.map +1 -1
  96. package/dist/mcp/mcp.module.js +3 -5
  97. package/dist/mcp/mcp.module.js.map +1 -1
  98. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  99. package/dist/mcp-chat/mcp-chat.service.js +17 -16
  100. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  101. package/dist/oauth/interfaces/OAuthProvider.d.ts +1 -1
  102. package/dist/oauth/interfaces/OAuthProvider.d.ts.map +1 -1
  103. package/dist/oauth/oauth.controller.d.ts +4 -1
  104. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  105. package/dist/oauth/oauth.controller.js +31 -5
  106. package/dist/oauth/oauth.controller.js.map +1 -1
  107. package/dist/oauth/oauth.service.d.ts +12 -1
  108. package/dist/oauth/oauth.service.d.ts.map +1 -1
  109. package/dist/oauth/oauth.service.js +125 -23
  110. package/dist/oauth/oauth.service.js.map +1 -1
  111. package/dist/oauth/providers/abstract.provider.d.ts +1 -1
  112. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  113. package/dist/oauth/providers/facebook.provider.d.ts +5 -2
  114. package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
  115. package/dist/oauth/providers/facebook.provider.js +34 -19
  116. package/dist/oauth/providers/facebook.provider.js.map +1 -1
  117. package/dist/oauth/providers/github.provider.d.ts +6 -2
  118. package/dist/oauth/providers/github.provider.d.ts.map +1 -1
  119. package/dist/oauth/providers/github.provider.js +43 -20
  120. package/dist/oauth/providers/github.provider.js.map +1 -1
  121. package/dist/oauth/providers/google.provider.d.ts +5 -2
  122. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  123. package/dist/oauth/providers/google.provider.js +34 -19
  124. package/dist/oauth/providers/google.provider.js.map +1 -1
  125. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +5 -2
  126. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  127. package/dist/oauth/providers/microsoft-entra-id.provider.js +43 -40
  128. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  129. package/dist/oauth/providers/microsoft.provider.d.ts +5 -2
  130. package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
  131. package/dist/oauth/providers/microsoft.provider.js +34 -19
  132. package/dist/oauth/providers/microsoft.provider.js.map +1 -1
  133. package/dist/profile/profile.controller.js +1 -1
  134. package/dist/profile/profile.controller.js.map +1 -1
  135. package/dist/security/security.service.d.ts +2 -0
  136. package/dist/security/security.service.d.ts.map +1 -1
  137. package/dist/security/security.service.js +21 -0
  138. package/dist/security/security.service.js.map +1 -1
  139. package/dist/setting/setting.service.d.ts +26 -1
  140. package/dist/setting/setting.service.d.ts.map +1 -1
  141. package/dist/setting/setting.service.js +240 -7
  142. package/dist/setting/setting.service.js.map +1 -1
  143. package/dist/user/user.controller.d.ts +2 -1
  144. package/dist/user/user.controller.d.ts.map +1 -1
  145. package/dist/user/user.controller.js +5 -4
  146. package/dist/user/user.controller.js.map +1 -1
  147. package/dist/user/user.service.d.ts +4 -2
  148. package/dist/user/user.service.d.ts.map +1 -1
  149. package/dist/user/user.service.js +19 -4
  150. package/dist/user/user.service.js.map +1 -1
  151. package/dist/webhook-integration/dto/webhook-action.dto.d.ts +17 -2
  152. package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -1
  153. package/dist/webhook-integration/dto/webhook-action.dto.js +93 -2
  154. package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -1
  155. package/dist/webhook-integration/event-webhook.controller.d.ts +1 -0
  156. package/dist/webhook-integration/event-webhook.controller.d.ts.map +1 -1
  157. package/dist/webhook-integration/event-webhook.controller.js +10 -0
  158. package/dist/webhook-integration/event-webhook.controller.js.map +1 -1
  159. package/dist/webhook-integration/event-webhook.service.d.ts +2 -0
  160. package/dist/webhook-integration/event-webhook.service.d.ts.map +1 -1
  161. package/dist/webhook-integration/event-webhook.service.js +30 -0
  162. package/dist/webhook-integration/event-webhook.service.js.map +1 -1
  163. package/dist/webhook-integration/webhook-command.registry.d.ts +15 -0
  164. package/dist/webhook-integration/webhook-command.registry.d.ts.map +1 -0
  165. package/dist/webhook-integration/webhook-command.registry.js +36 -0
  166. package/dist/webhook-integration/webhook-command.registry.js.map +1 -0
  167. package/dist/webhook-integration/webhook-integration.controller.d.ts +20 -0
  168. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  169. package/dist/webhook-integration/webhook-integration.controller.js +48 -0
  170. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  171. package/dist/webhook-integration/webhook-integration.module.d.ts.map +1 -1
  172. package/dist/webhook-integration/webhook-integration.module.js +5 -0
  173. package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
  174. package/dist/webhook-integration/webhook-integration.service.d.ts +27 -2
  175. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  176. package/dist/webhook-integration/webhook-integration.service.js +377 -50
  177. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  178. package/hedhog/data/file_provider.yaml +9 -1
  179. package/hedhog/data/integration_provider.yaml +148 -0
  180. package/hedhog/data/integration_type.yaml +29 -0
  181. package/hedhog/data/menu.yaml +6 -21
  182. package/hedhog/data/role.yaml +8 -0
  183. package/hedhog/data/route.yaml +73 -373
  184. package/hedhog/data/setting_group.yaml +209 -365
  185. package/hedhog/data/setting_subgroup.yaml +21 -13
  186. package/hedhog/frontend/app/account/components/connected-accounts.tsx.ejs +25 -20
  187. package/hedhog/frontend/app/account/components/tokens.tsx.ejs +421 -0
  188. package/hedhog/frontend/app/account/layout.tsx.ejs +6 -3
  189. package/hedhog/frontend/app/account/mcp-tokens/page.tsx.ejs +2 -2
  190. package/hedhog/frontend/app/account/tokens/page.tsx.ejs +5 -0
  191. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +938 -225
  192. package/hedhog/frontend/app/configurations/layout.tsx.ejs +1 -1
  193. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +3 -1
  194. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +1830 -0
  195. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +829 -128
  196. package/hedhog/frontend/app/mail/profiles/page.tsx.ejs +1 -820
  197. package/hedhog/frontend/app/setting-list/page.tsx.ejs +229 -395
  198. package/hedhog/frontend/app/users/permissions.tsx.ejs +79 -2
  199. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +7 -2
  200. package/hedhog/frontend/messages/en.json +239 -3
  201. package/hedhog/frontend/messages/pt.json +239 -3
  202. package/hedhog/table/file.yaml +1 -1
  203. package/hedhog/table/integration_profile.yaml +34 -0
  204. package/hedhog/table/integration_provider.yaml +23 -0
  205. package/hedhog/table/integration_type.yaml +19 -0
  206. package/hedhog/table/mcp_api_key.yaml +4 -0
  207. package/hedhog/table/webhook_action.yaml +141 -85
  208. package/package.json +4 -3
  209. package/src/ai/ai.service.ts +35 -153
  210. package/src/auth/guards/auth.guard.ts +32 -0
  211. package/src/core.module.ts +3 -3
  212. package/src/file/file.controller.ts +51 -42
  213. package/src/file/file.service.ts +67 -104
  214. package/src/file/provider/azure.provider.ts +14 -28
  215. package/src/file/provider/gcs.provider.ts +9 -9
  216. package/src/file/provider/local.provider.ts +10 -13
  217. package/src/file/provider/provider.enum.ts +1 -0
  218. package/src/file/provider/provider.factory.ts +21 -6
  219. package/src/file/provider/s3.provider.ts +4 -20
  220. package/src/file/provider/spaces.provider.ts +246 -0
  221. package/src/index.ts +6 -3
  222. package/src/integration-profile/dto/create.dto.ts +44 -0
  223. package/src/integration-profile/dto/export.dto.ts +15 -0
  224. package/src/integration-profile/dto/import-confirm.dto.ts +61 -0
  225. package/src/integration-profile/integration-profile.controller.ts +131 -0
  226. package/src/integration-profile/integration-profile.module.ts +16 -0
  227. package/src/integration-profile/integration-profile.service.ts +793 -0
  228. package/src/integration-profile/integration-profile.utils.ts +173 -0
  229. package/src/mail/mail.module.ts +41 -22
  230. package/src/mail/mail.service.ts +40 -64
  231. package/src/mcp/guards/mcp-auth.guard.ts +1 -1
  232. package/src/mcp/mcp-api-key.controller.ts +2 -2
  233. package/src/mcp/mcp-api-key.service.ts +6 -2
  234. package/src/mcp/mcp.module.ts +3 -5
  235. package/src/mcp-chat/mcp-chat.service.ts +23 -20
  236. package/src/oauth/interfaces/OAuthProvider.ts +1 -1
  237. package/src/oauth/oauth.controller.ts +30 -2
  238. package/src/oauth/oauth.service.ts +194 -35
  239. package/src/oauth/providers/abstract.provider.ts +1 -1
  240. package/src/oauth/providers/facebook.provider.ts +36 -20
  241. package/src/oauth/providers/github.provider.ts +46 -23
  242. package/src/oauth/providers/google.provider.ts +36 -20
  243. package/src/oauth/providers/microsoft-entra-id.provider.ts +42 -46
  244. package/src/oauth/providers/microsoft.provider.ts +36 -21
  245. package/src/profile/profile.controller.ts +2 -2
  246. package/src/security/security.service.ts +34 -1
  247. package/src/setting/setting.service.ts +307 -9
  248. package/src/user/user.controller.ts +4 -1
  249. package/src/user/user.service.ts +20 -2
  250. package/src/webhook-integration/dto/webhook-action.dto.ts +85 -4
  251. package/src/webhook-integration/event-webhook.controller.ts +55 -50
  252. package/src/webhook-integration/event-webhook.service.ts +422 -384
  253. package/src/webhook-integration/webhook-command.registry.ts +42 -0
  254. package/src/webhook-integration/webhook-integration.controller.ts +25 -0
  255. package/src/webhook-integration/webhook-integration.module.ts +50 -45
  256. package/src/webhook-integration/webhook-integration.service.ts +444 -53
  257. package/dist/mail-profile/dto/create.dto.d.ts +0 -23
  258. package/dist/mail-profile/dto/create.dto.d.ts.map +0 -1
  259. package/dist/mail-profile/dto/create.dto.js +0 -139
  260. package/dist/mail-profile/dto/create.dto.js.map +0 -1
  261. package/dist/mail-profile/dto/update.dto.d.ts.map +0 -1
  262. package/dist/mail-profile/dto/update.dto.js.map +0 -1
  263. package/dist/mail-profile/mail-profile.controller.d.ts +0 -94
  264. package/dist/mail-profile/mail-profile.controller.d.ts.map +0 -1
  265. package/dist/mail-profile/mail-profile.controller.js +0 -95
  266. package/dist/mail-profile/mail-profile.controller.js.map +0 -1
  267. package/dist/mail-profile/mail-profile.module.d.ts +0 -3
  268. package/dist/mail-profile/mail-profile.module.d.ts.map +0 -1
  269. package/dist/mail-profile/mail-profile.module.js.map +0 -1
  270. package/dist/mail-profile/mail-profile.service.d.ts +0 -109
  271. package/dist/mail-profile/mail-profile.service.d.ts.map +0 -1
  272. package/dist/mail-profile/mail-profile.service.js +0 -278
  273. package/dist/mail-profile/mail-profile.service.js.map +0 -1
  274. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +0 -32
  275. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +0 -1
  276. package/dist/mcp/tools/core-mail-profile.mcp-tools.js +0 -178
  277. package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +0 -1
  278. package/hedhog/data/mail_profile.yaml +0 -18
  279. package/hedhog/table/mail_profile.yaml +0 -75
  280. package/src/mail-profile/dto/create.dto.ts +0 -145
  281. package/src/mail-profile/mail-profile.controller.ts +0 -56
  282. package/src/mail-profile/mail-profile.module.ts +0 -18
  283. package/src/mail-profile/mail-profile.service.ts +0 -308
  284. package/src/mcp/tools/core-mail-profile.mcp-tools.ts +0 -128
  285. /package/dist/{mail-profile → integration-profile}/dto/update.dto.d.ts +0 -0
  286. /package/dist/{mail-profile → integration-profile}/dto/update.dto.js +0 -0
  287. /package/src/{mail-profile → integration-profile}/dto/update.dto.ts +0 -0
@@ -1,5 +1,7 @@
1
+ import { PrismaService } from '@hed-hog/api-prisma';
1
2
  import { HttpService } from '@nestjs/axios';
2
- import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
3
+ import { BadRequestException, forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
4
+ import { buildOAuthConfigFromIntegration } from '../../integration-profile/integration-profile.utils';
3
5
  import { SettingService } from '../../setting/setting.service';
4
6
  import { BaseOAuthProvider } from './abstract.provider';
5
7
 
@@ -11,6 +13,7 @@ export class MicrosoftEntraIdProvider extends BaseOAuthProvider {
11
13
  http: HttpService,
12
14
  @Inject(forwardRef(() => SettingService))
13
15
  private readonly setting: SettingService,
16
+ private readonly prisma: PrismaService,
14
17
  ) {
15
18
  super(http);
16
19
  }
@@ -32,39 +35,42 @@ export class MicrosoftEntraIdProvider extends BaseOAuthProvider {
32
35
  return 'MICROSOFT-ENTRA-ID';
33
36
  }
34
37
 
35
- async getAuthUrl(callbackPath: string) {
38
+ private async loadConfig() {
36
39
  const settings = await this.setting.getSettingValues([
37
- 'microsoft_entra_id_client_id',
38
- 'microsoft_entra_id_client_secret',
39
- 'microsoft_entra_id_scopes',
40
- 'microsoft_entra_id_tenant_id',
40
+ 'oauth-microsoft-entra-id-profile-id',
41
41
  'url',
42
42
  ]);
43
+ const profileId = Number(settings['oauth-microsoft-entra-id-profile-id']);
44
+ if (!profileId) throw new BadRequestException('Microsoft Entra ID OAuth profile not configured.');
43
45
 
44
- if (
45
- !settings['microsoft_entra_id_client_id'] ||
46
- !settings['microsoft_entra_id_client_secret'] ||
47
- !settings['microsoft_entra_id_scopes'] ||
48
- !settings['microsoft_entra_id_tenant_id'] ||
49
- !settings['url']
50
- ) {
51
- this.logger.error(
52
- 'Microsoft Entra ID OAuth is missing required settings. Verify client id, client secret, scopes, tenant id, and url.',
53
- );
54
- throw new Error('Microsoft Entra ID OAuth is not configured properly. Please configure microsoft_entra_id_client_id, microsoft_entra_id_client_secret, microsoft_entra_id_scopes, microsoft_entra_id_tenant_id, and url settings.');
46
+ const profile = await this.prisma.integration_profile.findUnique({
47
+ where: { id: profileId },
48
+ include: { integration_provider: { select: { slug: true } } },
49
+ });
50
+ if (!profile) throw new BadRequestException('Microsoft Entra ID OAuth profile not found.');
51
+
52
+ const { clientId, clientSecret, tenantId } = buildOAuthConfigFromIntegration(
53
+ profile.integration_provider.slug,
54
+ profile.config,
55
+ );
56
+ if (!tenantId) {
57
+ throw new BadRequestException('Microsoft Entra ID OAuth profile is missing tenant_id.');
55
58
  }
59
+ return { clientId, clientSecret, tenantId, baseUrl: String(settings['url'] ?? '') };
60
+ }
56
61
 
57
- const tenantId = settings['microsoft_entra_id_tenant_id'];
58
- const redirectURI = this.buildRedirectUri(settings['url'], callbackPath);
59
- const baseScopes = Array.isArray(settings['microsoft_entra_id_scopes'])
60
- ? settings['microsoft_entra_id_scopes']
61
- : [];
62
+ async getAuthUrl(callbackPath: string) {
63
+ const { clientId, tenantId, baseUrl } = await this.loadConfig();
64
+ const settings = await this.setting.getSettingValues(['oauth-microsoft-entra-id-scopes']);
65
+ const baseScopes: string[] = settings['oauth-microsoft-entra-id-scopes'] ?? ['openid', 'profile', 'email'];
62
66
  const scopes = baseScopes.includes('offline_access')
63
67
  ? baseScopes
64
68
  : [...baseScopes, 'offline_access'];
69
+
70
+ const redirectURI = this.buildRedirectUri(baseUrl, callbackPath);
65
71
  const isConnectFlow = callbackPath.includes('/connect');
66
72
  const params = new URLSearchParams({
67
- client_id: settings['microsoft_entra_id_client_id'],
73
+ client_id: clientId,
68
74
  redirect_uri: redirectURI,
69
75
  response_type: 'code',
70
76
  scope: scopes.join(' '),
@@ -75,43 +81,33 @@ export class MicrosoftEntraIdProvider extends BaseOAuthProvider {
75
81
  `OAuth auth URL requested: provider=MICROSOFT-ENTRA-ID redirectUri=${redirectURI} hasOfflineAccess=${scopes.includes('offline_access') ? 'yes' : 'no'} connectFlow=${isConnectFlow ? 'yes' : 'no'} tenantConfigured=${tenantId ? 'yes' : 'no'}`,
76
82
  );
77
83
 
78
- // Force consent only for explicit account connect, not for routine login.
79
84
  if (isConnectFlow) {
80
85
  params.set('prompt', 'consent');
81
86
  }
82
87
 
83
- const authUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize?${params.toString()}`;
84
- return authUrl;
88
+ return `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize?${params.toString()}`;
85
89
  }
86
90
 
87
- async getProfile(code: string, type: string): Promise<any> {
88
- let redirectUri = '';
91
+ async getProfile(code: string, type: string, redirectUri?: string): Promise<any> {
92
+ let resolvedRedirectUri = '';
89
93
 
90
94
  try {
91
- const settings = await this.setting.getSettingValues([
92
- 'microsoft_entra_id_client_id',
93
- 'microsoft_entra_id_client_secret',
94
- 'microsoft_entra_id_scopes',
95
- 'microsoft_entra_id_tenant_id',
96
- 'url',
97
- ]);
98
- const tenantId = settings['microsoft_entra_id_tenant_id'];
99
- const scopes = settings['microsoft_entra_id_scopes'] ?? [];
100
- redirectUri = this.buildRedirectUri(
101
- settings['url'],
102
- `/callback/microsoft-entra-id/${type}`,
103
- );
95
+ const { clientId, clientSecret, tenantId, baseUrl } = await this.loadConfig();
96
+ const settings = await this.setting.getSettingValues(['oauth-microsoft-entra-id-scopes']);
97
+ const scopes: string[] = settings['oauth-microsoft-entra-id-scopes'] ?? [];
98
+ resolvedRedirectUri =
99
+ redirectUri?.trim() || this.buildRedirectUri(baseUrl, `/callback/microsoft-entra-id/${type}`);
104
100
 
105
101
  this.logger.log(
106
- `OAuth token exchange started: provider=MICROSOFT-ENTRA-ID type=${type} redirectUri=${redirectUri} hasCode=${code ? 'yes' : 'no'} codeLength=${code?.length ?? 0}`,
102
+ `OAuth token exchange started: provider=MICROSOFT-ENTRA-ID type=${type} redirectUri=${resolvedRedirectUri} hasCode=${code ? 'yes' : 'no'} codeLength=${code?.length ?? 0}`,
107
103
  );
108
104
 
109
105
  const token = await this.fetchToken({
110
106
  code,
111
107
  url: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
112
- clientId: settings['microsoft_entra_id_client_id'],
113
- clientSecret: settings['microsoft_entra_id_client_secret'],
114
- redirectUri,
108
+ clientId,
109
+ clientSecret,
110
+ redirectUri: resolvedRedirectUri,
115
111
  });
116
112
 
117
113
  const profile = await this.fetchProfile(
@@ -141,7 +137,7 @@ export class MicrosoftEntraIdProvider extends BaseOAuthProvider {
141
137
  const mappedError = this.mapProviderError('Microsoft Entra ID', error);
142
138
 
143
139
  this.logger.warn(
144
- `OAuth callback failed: provider=MICROSOFT-ENTRA-ID status=${mappedError.status ?? 'unknown'} error=${mappedError.providerError ?? 'unknown'} providerCode=${mappedError.providerErrorCode ?? 'n/a'} reason=${mappedError.reason} redirectUri=${redirectUri || 'unresolved'}`,
140
+ `OAuth callback failed: provider=MICROSOFT-ENTRA-ID status=${mappedError.status ?? 'unknown'} error=${mappedError.providerError ?? 'unknown'} providerCode=${mappedError.providerErrorCode ?? 'n/a'} reason=${mappedError.reason} redirectUri=${resolvedRedirectUri || 'unresolved'}`,
145
141
  );
146
142
 
147
143
  throw mappedError;
@@ -1,16 +1,18 @@
1
+ import { PrismaService } from '@hed-hog/api-prisma';
1
2
  import { HttpService } from '@nestjs/axios';
2
- import { forwardRef, Inject, Injectable } from '@nestjs/common';
3
+ import { BadRequestException, forwardRef, Inject, Injectable } from '@nestjs/common';
4
+ import { buildOAuthConfigFromIntegration } from '../../integration-profile/integration-profile.utils';
3
5
  import { SettingService } from '../../setting/setting.service';
4
6
  import { BaseOAuthProvider } from './abstract.provider';
5
7
 
6
8
  @Injectable()
7
- export class MicrosoftProvider
8
- extends BaseOAuthProvider {
9
+ export class MicrosoftProvider extends BaseOAuthProvider {
9
10
 
10
11
  constructor(
11
12
  http: HttpService,
12
13
  @Inject(forwardRef(() => SettingService))
13
14
  private readonly setting: SettingService,
15
+ private readonly prisma: PrismaService,
14
16
  ) {
15
17
  super(http);
16
18
  }
@@ -19,19 +21,35 @@ export class MicrosoftProvider
19
21
  return 'MICROSOFT';
20
22
  }
21
23
 
22
- async getAuthUrl(callbackPath: string) {
23
-
24
+ private async loadConfig() {
24
25
  const settings = await this.setting.getSettingValues([
25
- 'microsoft_client_id',
26
- 'microsoft_client_secret',
27
- 'microsoft_scopes',
26
+ 'oauth-microsoft-profile-id',
28
27
  'url',
29
28
  ]);
29
+ const profileId = Number(settings['oauth-microsoft-profile-id']);
30
+ if (!profileId) throw new BadRequestException('Microsoft OAuth profile not configured.');
31
+
32
+ const profile = await this.prisma.integration_profile.findUnique({
33
+ where: { id: profileId },
34
+ include: { integration_provider: { select: { slug: true } } },
35
+ });
36
+ if (!profile) throw new BadRequestException('Microsoft OAuth profile not found.');
37
+
38
+ const { clientId, clientSecret, tenantId } = buildOAuthConfigFromIntegration(
39
+ profile.integration_provider.slug,
40
+ profile.config,
41
+ );
42
+ return { clientId, clientSecret, tenantId, baseUrl: String(settings['url'] ?? '') };
43
+ }
44
+
45
+ async getAuthUrl(callbackPath: string) {
46
+ const { clientId, baseUrl } = await this.loadConfig();
47
+ const settings = await this.setting.getSettingValues(['oauth-microsoft-scopes']);
48
+ const scopes: string[] = settings['oauth-microsoft-scopes'] ?? ['openid', 'profile', 'email'];
49
+ const redirectURI = new URL(callbackPath, baseUrl).toString();
30
50
 
31
- const redirectURI = new URL(callbackPath, settings['url']).toString();
32
- const scopes = settings['microsoft_scopes']
33
51
  const params = new URLSearchParams({
34
- client_id: settings['microsoft_client_id'],
52
+ client_id: clientId,
35
53
  redirect_uri: redirectURI,
36
54
  response_type: 'code',
37
55
  scope: scopes.join(' '),
@@ -42,21 +60,18 @@ export class MicrosoftProvider
42
60
  return `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${params}`;
43
61
  }
44
62
 
45
- async getProfile(code: string, type: string): Promise<any> {
63
+ async getProfile(code: string, type: string, redirectUri?: string): Promise<any> {
64
+ const { clientId, clientSecret, baseUrl } = await this.loadConfig();
46
65
 
47
- const settings = await this.setting.getSettingValues([
48
- 'microsoft_client_id',
49
- 'microsoft_client_secret',
50
- 'microsoft_scopes',
51
- 'url',
52
- ]);
66
+ const resolvedRedirectUri =
67
+ redirectUri?.trim() || `${baseUrl}/callback/microsoft/${type}`;
53
68
 
54
69
  const token = await this.fetchToken({
55
70
  code,
56
71
  url: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
57
- clientId: settings['microsoft_client_id'],
58
- clientSecret: settings['microsoft_client_secret'],
59
- redirectUri: `${settings['url']}/callback/microsoft/${type}`,
72
+ clientId,
73
+ clientSecret,
74
+ redirectUri: resolvedRedirectUri,
60
75
  });
61
76
 
62
77
  const profile = await this.fetchProfile(
@@ -1,4 +1,4 @@
1
- import { Role, User } from '@hed-hog/api';
1
+ import { NoRole, User } from '@hed-hog/api';
2
2
  import { Locale } from '@hed-hog/api-locale';
3
3
  import { Body, Controller, Delete, Get, Headers, Ip, Param, ParseIntPipe, Post, Put, UploadedFile, UseInterceptors } from '@nestjs/common';
4
4
  import { FileInterceptor } from '@nestjs/platform-express';
@@ -19,7 +19,7 @@ import { VerifyMfaTotpDto } from './dto/verify-mfa-totp.dto';
19
19
  import { VerifyWebAuthnRegistrationDto } from './dto/verify-webauthn-registration.dto';
20
20
  import { VerifyWebAuthnDto } from './dto/verify-webauthn.dto';
21
21
  import { ProfileService } from './profile.service';
22
- @Role()
22
+ @NoRole()
23
23
  @Controller('profile')
24
24
  export class ProfileController {
25
25
 
@@ -2,7 +2,15 @@ import { getLocaleText } from '@hed-hog/api-locale';
2
2
  import { BadRequestException, Injectable } from '@nestjs/common';
3
3
  import { ConfigService } from '@nestjs/config';
4
4
  import * as argon2 from 'argon2';
5
- import { createCipheriv, createDecipheriv, createHash, randomBytes, scryptSync } from 'crypto';
5
+ import {
6
+ createCipheriv,
7
+ createDecipheriv,
8
+ createHash,
9
+ createHmac,
10
+ randomBytes,
11
+ scryptSync,
12
+ timingSafeEqual,
13
+ } from 'crypto';
6
14
 
7
15
  @Injectable()
8
16
  export class SecurityService {
@@ -31,6 +39,31 @@ export class SecurityService {
31
39
  return createHash('sha256').update(value).digest('base64url');
32
40
  }
33
41
 
42
+ signHmacSha256(value: string, secret?: string): string {
43
+ const signingSecret = secret || this.getJwtSecret();
44
+ return createHmac('sha256', signingSecret).update(value).digest('base64url');
45
+ }
46
+
47
+ verifyHmacSha256(value: string, signature: string, secret?: string): boolean {
48
+ if (!signature) {
49
+ return false;
50
+ }
51
+
52
+ try {
53
+ const expected = this.signHmacSha256(value, secret);
54
+ const expectedBuffer = Buffer.from(expected);
55
+ const providedBuffer = Buffer.from(signature);
56
+
57
+ if (expectedBuffer.length !== providedBuffer.length) {
58
+ return false;
59
+ }
60
+
61
+ return timingSafeEqual(expectedBuffer, providedBuffer);
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+
34
67
  hashArgon2(value: string, pepper?: string): Promise<string> {
35
68
  return argon2.hash(this.getValueWithPepper(value, pepper));
36
69
  }