@rebasepro/server-core 0.0.1-canary.4d4fb3e → 0.0.1-canary.ca2cb6e

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 (176) hide show
  1. package/dist/common/src/collections/CollectionRegistry.d.ts +8 -0
  2. package/dist/common/src/util/entities.d.ts +22 -0
  3. package/dist/common/src/util/relations.d.ts +14 -4
  4. package/dist/common/src/util/resolutions.d.ts +1 -1
  5. package/dist/index-DXVBFp5V.js +37 -0
  6. package/dist/index-DXVBFp5V.js.map +1 -0
  7. package/dist/index.es.js +49931 -55
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/index.umd.js +28475 -34569
  10. package/dist/index.umd.js.map +1 -1
  11. package/dist/server-core/src/api/openapi-generator.d.ts +15 -1
  12. package/dist/server-core/src/api/types.d.ts +1 -1
  13. package/dist/server-core/src/auth/admin-routes.d.ts +10 -1
  14. package/dist/server-core/src/auth/apple-oauth.d.ts +30 -0
  15. package/dist/server-core/src/auth/bitbucket-oauth.d.ts +11 -0
  16. package/dist/server-core/src/auth/discord-oauth.d.ts +14 -0
  17. package/dist/server-core/src/auth/facebook-oauth.d.ts +14 -0
  18. package/dist/server-core/src/auth/github-oauth.d.ts +15 -0
  19. package/dist/server-core/src/auth/gitlab-oauth.d.ts +13 -0
  20. package/dist/server-core/src/auth/google-oauth.d.ts +5 -11
  21. package/dist/server-core/src/auth/index.d.ts +13 -2
  22. package/dist/server-core/src/auth/interfaces.d.ts +45 -6
  23. package/dist/server-core/src/auth/jwt.d.ts +1 -0
  24. package/dist/server-core/src/auth/linkedin-oauth.d.ts +18 -0
  25. package/dist/server-core/src/auth/microsoft-oauth.d.ts +16 -0
  26. package/dist/server-core/src/auth/middleware.d.ts +36 -11
  27. package/dist/server-core/src/auth/rate-limiter.d.ts +2 -2
  28. package/dist/server-core/src/auth/routes.d.ts +13 -3
  29. package/dist/server-core/src/auth/slack-oauth.d.ts +12 -0
  30. package/dist/server-core/src/auth/spotify-oauth.d.ts +12 -0
  31. package/dist/server-core/src/auth/twitter-oauth.d.ts +18 -0
  32. package/dist/server-core/src/cron/cron-loader.d.ts +17 -0
  33. package/dist/server-core/src/cron/cron-routes.d.ts +14 -0
  34. package/dist/server-core/src/cron/cron-scheduler.d.ts +61 -0
  35. package/dist/server-core/src/cron/cron-store.d.ts +32 -0
  36. package/dist/server-core/src/cron/index.d.ts +6 -0
  37. package/dist/server-core/src/email/index.d.ts +2 -2
  38. package/dist/server-core/src/email/templates.d.ts +9 -0
  39. package/dist/server-core/src/email/types.d.ts +20 -23
  40. package/dist/server-core/src/history/history-routes.d.ts +2 -2
  41. package/dist/server-core/src/index.d.ts +5 -0
  42. package/dist/server-core/src/init.d.ts +112 -2
  43. package/dist/server-core/src/singleton.d.ts +35 -0
  44. package/dist/server-core/src/storage/LocalStorageController.d.ts +9 -9
  45. package/dist/server-core/src/storage/S3StorageController.d.ts +8 -8
  46. package/dist/server-core/src/storage/index.d.ts +18 -11
  47. package/dist/server-core/src/storage/routes.d.ts +3 -3
  48. package/dist/server-core/src/storage/types.d.ts +28 -16
  49. package/dist/server-core/src/utils/dev-port.d.ts +35 -0
  50. package/dist/server-core/src/utils/logger.d.ts +31 -0
  51. package/dist/server-core/src/utils/request-logger.d.ts +19 -0
  52. package/dist/types/src/controllers/auth.d.ts +2 -0
  53. package/dist/types/src/controllers/client.d.ts +119 -7
  54. package/dist/types/src/controllers/collection_registry.d.ts +4 -3
  55. package/dist/types/src/controllers/customization_controller.d.ts +7 -1
  56. package/dist/types/src/controllers/data.d.ts +34 -7
  57. package/dist/types/src/controllers/data_driver.d.ts +20 -28
  58. package/dist/types/src/controllers/database_admin.d.ts +2 -2
  59. package/dist/types/src/controllers/email.d.ts +34 -0
  60. package/dist/types/src/controllers/index.d.ts +1 -0
  61. package/dist/types/src/controllers/local_config_persistence.d.ts +4 -4
  62. package/dist/types/src/controllers/navigation.d.ts +5 -5
  63. package/dist/types/src/controllers/registry.d.ts +6 -3
  64. package/dist/types/src/controllers/side_entity_controller.d.ts +7 -6
  65. package/dist/types/src/controllers/storage.d.ts +24 -26
  66. package/dist/types/src/rebase_context.d.ts +8 -4
  67. package/dist/types/src/types/backend.d.ts +4 -1
  68. package/dist/types/src/types/builders.d.ts +5 -4
  69. package/dist/types/src/types/chips.d.ts +1 -1
  70. package/dist/types/src/types/collections.d.ts +169 -125
  71. package/dist/types/src/types/cron.d.ts +102 -0
  72. package/dist/types/src/types/data_source.d.ts +1 -1
  73. package/dist/types/src/types/entity_actions.d.ts +8 -8
  74. package/dist/types/src/types/entity_callbacks.d.ts +15 -15
  75. package/dist/types/src/types/entity_link_builder.d.ts +1 -1
  76. package/dist/types/src/types/entity_overrides.d.ts +2 -1
  77. package/dist/types/src/types/entity_views.d.ts +8 -8
  78. package/dist/types/src/types/export_import.d.ts +3 -3
  79. package/dist/types/src/types/index.d.ts +1 -0
  80. package/dist/types/src/types/plugins.d.ts +72 -18
  81. package/dist/types/src/types/properties.d.ts +118 -33
  82. package/dist/types/src/types/relations.d.ts +1 -1
  83. package/dist/types/src/types/slots.d.ts +30 -6
  84. package/dist/types/src/types/translations.d.ts +44 -0
  85. package/dist/types/src/types/user_management_delegate.d.ts +1 -0
  86. package/package.json +84 -84
  87. package/scratch.ts +6 -5
  88. package/src/api/ast-schema-editor.ts +11 -11
  89. package/src/api/collections_for_test/callbacks_test_collection.ts +5 -2
  90. package/src/api/errors.ts +38 -14
  91. package/src/api/graphql/graphql-schema-generator.ts +5 -3
  92. package/src/api/openapi-generator.ts +613 -58
  93. package/src/api/rest/api-generator.ts +17 -11
  94. package/src/api/rest/query-parser.ts +1 -1
  95. package/src/api/schema-editor-routes.ts +2 -0
  96. package/src/api/server.ts +8 -5
  97. package/src/api/types.ts +1 -1
  98. package/src/auth/admin-routes.ts +58 -17
  99. package/src/auth/apple-oauth.ts +130 -0
  100. package/src/auth/bitbucket-oauth.ts +82 -0
  101. package/src/auth/discord-oauth.ts +83 -0
  102. package/src/auth/facebook-oauth.ts +72 -0
  103. package/src/auth/github-oauth.ts +110 -0
  104. package/src/auth/gitlab-oauth.ts +70 -0
  105. package/src/auth/google-oauth.ts +34 -46
  106. package/src/auth/index.ts +15 -2
  107. package/src/auth/interfaces.ts +54 -7
  108. package/src/auth/jwt.ts +22 -5
  109. package/src/auth/linkedin-oauth.ts +81 -0
  110. package/src/auth/microsoft-oauth.ts +88 -0
  111. package/src/auth/middleware.ts +194 -45
  112. package/src/auth/password.ts +3 -1
  113. package/src/auth/rate-limiter.ts +4 -4
  114. package/src/auth/routes.ts +197 -139
  115. package/src/auth/slack-oauth.ts +71 -0
  116. package/src/auth/spotify-oauth.ts +67 -0
  117. package/src/auth/twitter-oauth.ts +120 -0
  118. package/src/collections/loader.ts +6 -6
  119. package/src/cron/cron-loader.ts +89 -0
  120. package/src/cron/cron-routes.test.ts +265 -0
  121. package/src/cron/cron-routes.ts +85 -0
  122. package/src/cron/cron-scheduler.test.ts +421 -0
  123. package/src/cron/cron-scheduler.ts +413 -0
  124. package/src/cron/cron-store.ts +163 -0
  125. package/src/cron/index.ts +6 -0
  126. package/src/db/interfaces.ts +2 -2
  127. package/src/email/index.ts +3 -2
  128. package/src/email/smtp-email-service.ts +5 -2
  129. package/src/email/templates.ts +93 -6
  130. package/src/email/types.ts +17 -24
  131. package/src/functions/function-loader.ts +34 -6
  132. package/src/functions/function-routes.ts +2 -2
  133. package/src/history/history-routes.ts +4 -3
  134. package/src/index.ts +10 -0
  135. package/src/init.ts +475 -57
  136. package/src/services/driver-registry.ts +1 -1
  137. package/src/singleton.test.ts +28 -0
  138. package/src/singleton.ts +70 -0
  139. package/src/storage/LocalStorageController.ts +50 -53
  140. package/src/storage/S3StorageController.ts +49 -46
  141. package/src/storage/index.ts +27 -16
  142. package/src/storage/routes.ts +76 -59
  143. package/src/storage/storage-registry.ts +5 -5
  144. package/src/storage/types.ts +44 -32
  145. package/src/types/index.ts +4 -4
  146. package/src/utils/dev-port.ts +176 -0
  147. package/src/utils/logger.ts +143 -0
  148. package/src/utils/logging.ts +5 -2
  149. package/src/utils/request-logger.ts +66 -0
  150. package/test/admin-routes.test.ts +114 -65
  151. package/test/api-generator.test.ts +73 -30
  152. package/test/ast-schema-editor.test.ts +5 -3
  153. package/test/auth-middleware-hono.test.ts +268 -33
  154. package/test/auth-routes.test.ts +325 -146
  155. package/test/driver-registry.test.ts +4 -2
  156. package/test/error-propagation.test.ts +226 -0
  157. package/test/errors.test.ts +12 -7
  158. package/test/jwt-security.test.ts +18 -9
  159. package/test/jwt.test.ts +26 -13
  160. package/test/middleware.test.ts +13 -8
  161. package/test/query-parser.test.ts +13 -8
  162. package/test/rate-limiter.test.ts +7 -7
  163. package/test/safe-compare.test.ts +66 -0
  164. package/test/singleton.test.ts +59 -0
  165. package/test/storage-local.test.ts +36 -43
  166. package/test/storage-registry.test.ts +38 -36
  167. package/test/storage-routes.test.ts +9 -5
  168. package/test/storage-s3.test.ts +51 -48
  169. package/test.ts +6 -0
  170. package/tsconfig.json +1 -1
  171. package/vite.config.ts +7 -5
  172. package/dist/index-BeMqpmfQ.js +0 -239
  173. package/dist/index-BeMqpmfQ.js.map +0 -1
  174. package/dist/index-bl4J3lNb.js +0 -55823
  175. package/dist/index-bl4J3lNb.js.map +0 -1
  176. package/vite.config.ts.timestamp-1775065397568-8a853255edf6e.mjs +0 -46
@@ -0,0 +1,72 @@
1
+ import type { OAuthProvider, OAuthProviderProfile } from "./interfaces";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Creates a Facebook / Meta OAuth Provider integration.
6
+ *
7
+ * Uses the authorization code flow to exchange a code for an access token,
8
+ * then fetches user profile from the Facebook Graph API.
9
+ */
10
+ export function createFacebookProvider(config: { clientId: string; clientSecret: string }): OAuthProvider<{ code: string; redirectUri: string }> {
11
+ return {
12
+ id: "facebook",
13
+ schema: z.object({
14
+ code: z.string().min(1, "Auth code is required"),
15
+ redirectUri: z.string().url("Valid redirect URI is required")
16
+ }),
17
+ verify: async (payload: { code: string; redirectUri: string }): Promise<OAuthProviderProfile | null> => {
18
+ try {
19
+ // Exchange code for access token
20
+ const tokenUrl = new URL("https://graph.facebook.com/v19.0/oauth/access_token");
21
+ tokenUrl.searchParams.set("client_id", config.clientId);
22
+ tokenUrl.searchParams.set("client_secret", config.clientSecret);
23
+ tokenUrl.searchParams.set("redirect_uri", payload.redirectUri);
24
+ tokenUrl.searchParams.set("code", payload.code);
25
+
26
+ const tokenResponse = await fetch(tokenUrl.toString());
27
+
28
+ if (!tokenResponse.ok) {
29
+ console.error("Failed to get Facebook access token:", await tokenResponse.text());
30
+ return null;
31
+ }
32
+
33
+ const tokenData = await tokenResponse.json() as { access_token: string };
34
+ const accessToken = tokenData.access_token;
35
+
36
+ // Fetch user profile with email and profile picture
37
+ const profileUrl = new URL("https://graph.facebook.com/v19.0/me");
38
+ profileUrl.searchParams.set("fields", "id,name,email,picture.type(large)");
39
+ profileUrl.searchParams.set("access_token", accessToken);
40
+
41
+ const profileResponse = await fetch(profileUrl.toString());
42
+
43
+ if (!profileResponse.ok) {
44
+ console.error("Failed to get Facebook user info:", await profileResponse.text());
45
+ return null;
46
+ }
47
+
48
+ const profileData = await profileResponse.json() as {
49
+ id: string;
50
+ name?: string;
51
+ email?: string;
52
+ picture?: { data?: { url?: string } };
53
+ };
54
+
55
+ if (!profileData.email) {
56
+ console.error("Facebook user has no email (email permission may not have been granted)");
57
+ return null;
58
+ }
59
+
60
+ return {
61
+ providerId: profileData.id,
62
+ email: profileData.email,
63
+ displayName: profileData.name || null,
64
+ photoUrl: profileData.picture?.data?.url || null
65
+ };
66
+ } catch (error) {
67
+ console.error("Facebook OAuth error:", error);
68
+ return null;
69
+ }
70
+ }
71
+ };
72
+ }
@@ -0,0 +1,110 @@
1
+ import type { OAuthProvider, OAuthProviderProfile } from "./interfaces";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Creates a GitHub OAuth Provider integration.
6
+ *
7
+ * Flow: Frontend receives an authorization `code` via the GitHub OAuth redirect.
8
+ * This provider exchanges the code for an access token, then fetches the user's
9
+ * profile and primary email from the GitHub API.
10
+ */
11
+ export function createGitHubProvider(config: { clientId: string; clientSecret: string }): OAuthProvider<{ code: string; redirectUri: string }> {
12
+ return {
13
+ id: "github",
14
+ schema: z.object({
15
+ code: z.string().min(1, "Auth code is required"),
16
+ redirectUri: z.string().url("Valid redirect URI is required")
17
+ }),
18
+ verify: async (payload: { code: string; redirectUri: string }): Promise<OAuthProviderProfile | null> => {
19
+ try {
20
+ // Exchange code for access token
21
+ const tokenResponse = await fetch("https://github.com/login/oauth/access_token", {
22
+ method: "POST",
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ "Accept": "application/json"
26
+ },
27
+ body: JSON.stringify({
28
+ client_id: config.clientId,
29
+ client_secret: config.clientSecret,
30
+ code: payload.code,
31
+ redirect_uri: payload.redirectUri
32
+ })
33
+ });
34
+
35
+ if (!tokenResponse.ok) {
36
+ console.error("Failed to get GitHub access token:", await tokenResponse.text());
37
+ return null;
38
+ }
39
+
40
+ const tokenData = await tokenResponse.json() as { access_token?: string; error?: string };
41
+ if (tokenData.error || !tokenData.access_token) {
42
+ console.error("GitHub token exchange error:", tokenData.error);
43
+ return null;
44
+ }
45
+
46
+ const accessToken = tokenData.access_token;
47
+
48
+ // Fetch user profile
49
+ const profileResponse = await fetch("https://api.github.com/user", {
50
+ headers: {
51
+ "Authorization": `Bearer ${accessToken}`,
52
+ "Accept": "application/vnd.github+json",
53
+ "User-Agent": "Rebase-Auth"
54
+ }
55
+ });
56
+
57
+ if (!profileResponse.ok) {
58
+ console.error("Failed to get GitHub user info:", await profileResponse.text());
59
+ return null;
60
+ }
61
+
62
+ const profileData = await profileResponse.json() as {
63
+ id: number;
64
+ login: string;
65
+ name?: string | null;
66
+ avatar_url?: string | null;
67
+ email?: string | null;
68
+ };
69
+
70
+ // GitHub may not return email in profile if it's private.
71
+ // Fetch from /user/emails endpoint instead.
72
+ let email = profileData.email;
73
+ if (!email) {
74
+ const emailsResponse = await fetch("https://api.github.com/user/emails", {
75
+ headers: {
76
+ "Authorization": `Bearer ${accessToken}`,
77
+ "Accept": "application/vnd.github+json",
78
+ "User-Agent": "Rebase-Auth"
79
+ }
80
+ });
81
+
82
+ if (emailsResponse.ok) {
83
+ const emails = await emailsResponse.json() as Array<{
84
+ email: string;
85
+ primary: boolean;
86
+ verified: boolean;
87
+ }>;
88
+ const primary = emails.find(e => e.primary && e.verified) || emails.find(e => e.verified);
89
+ email = primary?.email || null;
90
+ }
91
+ }
92
+
93
+ if (!email) {
94
+ console.error("GitHub user has no verified email");
95
+ return null;
96
+ }
97
+
98
+ return {
99
+ providerId: String(profileData.id),
100
+ email,
101
+ displayName: profileData.name || profileData.login || null,
102
+ photoUrl: profileData.avatar_url || null
103
+ };
104
+ } catch (error) {
105
+ console.error("GitHub OAuth error:", error);
106
+ return null;
107
+ }
108
+ }
109
+ };
110
+ }
@@ -0,0 +1,70 @@
1
+ import type { OAuthProvider, OAuthProviderProfile } from "./interfaces";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Creates a GitLab OAuth Provider integration.
6
+ * Works with both GitLab.com and self-hosted instances.
7
+ */
8
+ export function createGitLabProvider(config: {
9
+ clientId: string;
10
+ clientSecret: string;
11
+ baseUrl?: string;
12
+ }): OAuthProvider<{ code: string; redirectUri: string }> {
13
+ const gitlabUrl = (config.baseUrl || "https://gitlab.com").replace(/\/$/, "");
14
+
15
+ return {
16
+ id: "gitlab",
17
+ schema: z.object({
18
+ code: z.string().min(1, "Auth code is required"),
19
+ redirectUri: z.string().url("Valid redirect URI is required")
20
+ }),
21
+ verify: async (payload: { code: string; redirectUri: string }): Promise<OAuthProviderProfile | null> => {
22
+ try {
23
+ const tokenResponse = await fetch(`${gitlabUrl}/oauth/token`, {
24
+ method: "POST",
25
+ headers: { "Content-Type": "application/json" },
26
+ body: JSON.stringify({
27
+ client_id: config.clientId,
28
+ client_secret: config.clientSecret,
29
+ code: payload.code,
30
+ grant_type: "authorization_code",
31
+ redirect_uri: payload.redirectUri
32
+ })
33
+ });
34
+
35
+ if (!tokenResponse.ok) {
36
+ console.error("Failed to get GitLab access token:", await tokenResponse.text());
37
+ return null;
38
+ }
39
+
40
+ const tokenData = await tokenResponse.json() as { access_token: string };
41
+
42
+ const profileResponse = await fetch(`${gitlabUrl}/api/v4/user`, {
43
+ headers: { "Authorization": `Bearer ${tokenData.access_token}` }
44
+ });
45
+
46
+ if (!profileResponse.ok) {
47
+ console.error("Failed to get GitLab user info:", await profileResponse.text());
48
+ return null;
49
+ }
50
+
51
+ const p = await profileResponse.json() as {
52
+ id: number; username: string; name?: string;
53
+ email: string; avatar_url?: string | null;
54
+ };
55
+
56
+ if (!p.email) { console.error("GitLab user has no email"); return null; }
57
+
58
+ return {
59
+ providerId: String(p.id),
60
+ email: p.email,
61
+ displayName: p.name || p.username || null,
62
+ photoUrl: p.avatar_url || null
63
+ };
64
+ } catch (error) {
65
+ console.error("GitLab OAuth error:", error);
66
+ return null;
67
+ }
68
+ }
69
+ };
70
+ }
@@ -1,4 +1,6 @@
1
1
  import { OAuth2Client } from "google-auth-library/build/src/index.js";
2
+ import type { OAuthProvider, OAuthProviderProfile } from "./interfaces";
3
+ import { z } from "zod";
2
4
 
3
5
  export interface GoogleUserInfo {
4
6
  googleId: string;
@@ -8,53 +10,39 @@ export interface GoogleUserInfo {
8
10
  emailVerified: boolean;
9
11
  }
10
12
 
11
- let googleClient: OAuth2Client | null = null;
12
- let configuredClientId: string | null = null;
13
-
14
- /**
15
- * Configure Google OAuth - call this during initialization
16
- */
17
- export function configureGoogleOAuth(clientId: string): void {
18
- configuredClientId = clientId;
19
- googleClient = new OAuth2Client(clientId);
20
- }
21
-
22
13
  /**
23
- * Verify a Google ID token and extract user information
24
- * @param idToken The ID token from Google Sign-In on the frontend
14
+ * Creates a Google OAuth Provider integration
25
15
  */
26
- export async function verifyGoogleIdToken(idToken: string): Promise<GoogleUserInfo | null> {
27
- if (!googleClient || !configuredClientId) {
28
- throw new Error("Google OAuth not configured. Call configureGoogleOAuth() first.");
29
- }
30
-
31
- try {
32
- const ticket = await googleClient.verifyIdToken({
33
- idToken,
34
- audience: configuredClientId
35
- });
36
-
37
- const payload = ticket.getPayload();
38
- if (!payload) {
39
- return null;
16
+ export function createGoogleProvider(clientId: string): OAuthProvider<{ idToken: string }> {
17
+ const googleClient = new OAuth2Client(clientId);
18
+
19
+ return {
20
+ id: "google",
21
+ schema: z.object({
22
+ idToken: z.string().min(1, "ID token is required")
23
+ }),
24
+ verify: async (payload: { idToken: string }): Promise<OAuthProviderProfile | null> => {
25
+ try {
26
+ const ticket = await googleClient.verifyIdToken({
27
+ idToken: payload.idToken,
28
+ audience: clientId
29
+ });
30
+
31
+ const content = ticket.getPayload();
32
+ if (!content) {
33
+ return null;
34
+ }
35
+
36
+ return {
37
+ providerId: content.sub,
38
+ email: content.email || "",
39
+ displayName: content.name || null,
40
+ photoUrl: content.picture || null
41
+ };
42
+ } catch (error) {
43
+ console.error("Failed to verify Google ID token:", error);
44
+ return null;
45
+ }
40
46
  }
41
-
42
- return {
43
- googleId: payload.sub,
44
- email: payload.email || "",
45
- displayName: payload.name || null,
46
- photoUrl: payload.picture || null,
47
- emailVerified: payload.email_verified || false
48
- };
49
- } catch (error) {
50
- console.error("Failed to verify Google ID token:", error);
51
- return null;
52
- }
53
- }
54
-
55
- /**
56
- * Check if Google OAuth is configured
57
- */
58
- export function isGoogleOAuthConfigured(): boolean {
59
- return googleClient !== null && configuredClientId !== null;
47
+ };
60
48
  }
package/src/auth/index.ts CHANGED
@@ -1,12 +1,25 @@
1
1
  // Auth module exports
2
+ export * from "./interfaces";
3
+
2
4
  export { configureJwt, generateAccessToken, verifyAccessToken, generateRefreshToken, hashRefreshToken, getRefreshTokenExpiry, getAccessTokenExpiry } from "./jwt";
3
5
  export type { JwtConfig, AccessTokenPayload } from "./jwt";
4
6
 
5
7
  export { hashPassword, verifyPassword, validatePasswordStrength } from "./password";
6
8
  export type { PasswordValidationResult } from "./password";
7
9
 
8
- export { configureGoogleOAuth, verifyGoogleIdToken, isGoogleOAuthConfigured } from "./google-oauth";
9
- export type { GoogleUserInfo } from "./google-oauth";
10
+ // OAuth Providers
11
+ export { createGoogleProvider } from "./google-oauth";
12
+ export { createLinkedinProvider } from "./linkedin-oauth";
13
+ export { createGitHubProvider } from "./github-oauth";
14
+ export { createMicrosoftProvider } from "./microsoft-oauth";
15
+ export { createAppleProvider } from "./apple-oauth";
16
+ export { createFacebookProvider } from "./facebook-oauth";
17
+ export { createTwitterProvider } from "./twitter-oauth";
18
+ export { createDiscordProvider } from "./discord-oauth";
19
+ export { createGitLabProvider } from "./gitlab-oauth";
20
+ export { createBitbucketProvider } from "./bitbucket-oauth";
21
+ export { createSlackProvider } from "./slack-oauth";
22
+ export { createSpotifyProvider } from "./spotify-oauth";
10
23
 
11
24
  export { requireAuth, requireAdmin, optionalAuth, extractUserFromToken, createAuthMiddleware } from "./middleware";
12
25
  export type { AuthMiddlewareOptions, AuthResult } from "./middleware";
@@ -1,6 +1,8 @@
1
+ import { z } from "zod";
2
+
1
3
  /**
2
4
  * Authentication Abstraction Interfaces
3
- *
5
+ *
4
6
  * These interfaces define the contracts for authentication-related operations.
5
7
  * Implementations can use different databases (PostgreSQL, MongoDB, etc.) to
6
8
  * store user, role, and token data.
@@ -15,8 +17,6 @@ export interface UserData {
15
17
  passwordHash?: string | null;
16
18
  displayName?: string | null;
17
19
  photoUrl?: string | null;
18
- provider: string;
19
- googleId?: string | null;
20
20
  emailVerified: boolean;
21
21
  emailVerificationToken?: string | null;
22
22
  emailVerificationSentAt?: Date | null;
@@ -32,11 +32,46 @@ export interface CreateUserData {
32
32
  passwordHash?: string;
33
33
  displayName?: string;
34
34
  photoUrl?: string;
35
- provider?: string;
36
- googleId?: string;
37
35
  emailVerified?: boolean;
38
36
  }
39
37
 
38
+ /**
39
+ * User Identity Data (OAuth accounts linked to user)
40
+ */
41
+ export interface UserIdentityData {
42
+ id: string;
43
+ userId: string;
44
+ provider: string;
45
+ providerId: string;
46
+ profileData?: Record<string, unknown> | null;
47
+ createdAt: Date;
48
+ updatedAt: Date;
49
+ }
50
+
51
+ /**
52
+ * Standardized profile data returned by an OAuth provider verification payload
53
+ */
54
+ export interface OAuthProviderProfile {
55
+ providerId: string;
56
+ email: string;
57
+ displayName?: string | null;
58
+ photoUrl?: string | null;
59
+ }
60
+
61
+ /**
62
+ * Pluggable OAuth Provider integration strategy
63
+ */
64
+ export interface OAuthProvider<T = unknown> {
65
+ /** The identifier of the provider (e.g. "github", "google") */
66
+ id: string;
67
+
68
+ /** Zod schema validating the expected request payload (e.g. { code: string }) */
69
+ schema: z.ZodSchema<T>;
70
+
71
+ /** Verify external tokens/codes and return a standardized user profile */
72
+ verify: (payload: T) => Promise<OAuthProviderProfile | null>;
73
+ }
74
+
40
75
  /**
41
76
  * Role data structure
42
77
  */
@@ -110,6 +145,8 @@ export interface ListUsersOptions {
110
145
  orderBy?: string;
111
146
  /** Sort direction (default "desc") */
112
147
  orderDir?: "asc" | "desc";
148
+ /** Filter by role ID */
149
+ roleId?: string;
113
150
  }
114
151
 
115
152
  /**
@@ -144,9 +181,19 @@ export interface UserRepository {
144
181
  getUserByEmail(email: string): Promise<UserData | null>;
145
182
 
146
183
  /**
147
- * Get a user by Google ID
184
+ * Get a user by an OAuth identity
185
+ */
186
+ getUserByIdentity(provider: string, providerId: string): Promise<UserData | null>;
187
+
188
+ /**
189
+ * Get all identities linked to a user
190
+ */
191
+ getUserIdentities(userId: string): Promise<UserIdentityData[]>;
192
+
193
+ /**
194
+ * Link a new OAuth identity to a user
148
195
  */
149
- getUserByGoogleId(googleId: string): Promise<UserData | null>;
196
+ linkUserIdentity(userId: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void>;
150
197
 
151
198
  /**
152
199
  * Update a user
package/src/auth/jwt.ts CHANGED
@@ -10,6 +10,7 @@ export interface JwtConfig {
10
10
  export interface AccessTokenPayload {
11
11
  userId: string;
12
12
  roles: string[];
13
+ uid?: string;
13
14
  }
14
15
 
15
16
  let jwtConfig: JwtConfig = {
@@ -30,10 +31,18 @@ export function configureJwt(config: JwtConfig): void {
30
31
  "jwt_secret",
31
32
  "your-secret",
32
33
  "your-super-secret-jwt-key-change-in-production",
34
+ "super-secret-jwt-key-change-in-production",
33
35
  "change-me",
34
36
  "changeme",
35
37
  "password",
36
- "test"
38
+ "test",
39
+ "mysecret",
40
+ "my-secret",
41
+ "my_secret",
42
+ "example-secret",
43
+ "please-change-me",
44
+ "replace-this-with-a-real-secret",
45
+ "default-secret"
37
46
  ]);
38
47
 
39
48
  if (!config.secret || config.secret.length < 32) {
@@ -64,7 +73,8 @@ export function generateAccessToken(userId: string, roles: string[]): string {
64
73
  throw new Error("JWT secret not configured. Call configureJwt() first.");
65
74
  }
66
75
 
67
- const payload: AccessTokenPayload = { userId, roles };
76
+ const payload: AccessTokenPayload = { userId,
77
+ roles };
68
78
 
69
79
  return jwt.sign(payload, jwtConfig.secret, {
70
80
  expiresIn: jwtConfig.accessExpiresIn as jwt.SignOptions["expiresIn"],
@@ -112,12 +122,19 @@ export function verifyAccessToken(token: string): AccessTokenPayload | null {
112
122
  }
113
123
 
114
124
  try {
115
- const decoded = jwt.verify(token, jwtConfig.secret, { algorithms: ["HS256"] }) as jwt.JwtPayload & AccessTokenPayload;
125
+ const decoded = jwt.verify(token, jwtConfig.secret, { algorithms: ["HS256"] }) as { userId?: string; uid?: string; sub?: string; roles?: string[] };
126
+ const id = decoded.userId || decoded.uid || decoded.sub;
127
+ if (!id) {
128
+ console.error("[JWT] Verification failed: missing id in payload", decoded);
129
+ return null;
130
+ }
131
+
116
132
  return {
117
- userId: decoded.userId,
118
- roles: decoded.roles
133
+ userId: id,
134
+ roles: decoded.roles || []
119
135
  };
120
136
  } catch (error) {
137
+ console.error("[JWT] Verification failed:", error, "Token start:", token.substring(0, 15));
121
138
  return null;
122
139
  }
123
140
  }
@@ -0,0 +1,81 @@
1
+ import type { OAuthProvider, OAuthProviderProfile } from "./interfaces";
2
+ import { z } from "zod";
3
+
4
+ export interface LinkedinUserInfo {
5
+ linkedinId: string;
6
+ email: string;
7
+ displayName: string | null;
8
+ photoUrl: string | null;
9
+ emailVerified: boolean;
10
+ }
11
+
12
+ /**
13
+ * Creates a LinkedIn OAuth Provider integration
14
+ */
15
+ export function createLinkedinProvider(config: { clientId: string, clientSecret: string }): OAuthProvider<{ code: string; redirectUri: string }> {
16
+ return {
17
+ id: "linkedin",
18
+ schema: z.object({
19
+ code: z.string().min(1, "Auth code is required"),
20
+ redirectUri: z.string().url("Valid redirect URI is required")
21
+ }),
22
+ verify: async (payload: { code: string; redirectUri: string }): Promise<OAuthProviderProfile | null> => {
23
+ try {
24
+ // Exchange code for access token
25
+ const tokenResponse = await fetch("https://www.linkedin.com/oauth/v2/accessToken", {
26
+ method: "POST",
27
+ headers: {
28
+ "Content-Type": "application/x-www-form-urlencoded"
29
+ },
30
+ body: new URLSearchParams({
31
+ grant_type: "authorization_code",
32
+ code: payload.code,
33
+ redirect_uri: payload.redirectUri,
34
+ client_id: config.clientId,
35
+ client_secret: config.clientSecret
36
+ })
37
+ });
38
+
39
+ if (!tokenResponse.ok) {
40
+ const errBody = await tokenResponse.text();
41
+ console.error("Failed to get LinkedIn access token:", errBody);
42
+ return null;
43
+ }
44
+
45
+ const tokenData = await tokenResponse.json() as { access_token: string; id_token?: string };
46
+ const accessToken = tokenData.access_token;
47
+
48
+ // Fetch User Info using OIDC userinfo endpoint
49
+ const profileResponse = await fetch("https://api.linkedin.com/v2/userinfo", {
50
+ headers: {
51
+ "Authorization": `Bearer ${accessToken}`
52
+ }
53
+ });
54
+
55
+ if (!profileResponse.ok) {
56
+ const errBody = await profileResponse.text();
57
+ console.error("Failed to get LinkedIn user info:", errBody);
58
+ return null;
59
+ }
60
+
61
+ const profileData = await profileResponse.json() as {
62
+ sub: string;
63
+ email: string;
64
+ name?: string;
65
+ picture?: string;
66
+ email_verified?: boolean;
67
+ };
68
+
69
+ return {
70
+ providerId: profileData.sub,
71
+ email: profileData.email,
72
+ displayName: profileData.name || null,
73
+ photoUrl: profileData.picture || null
74
+ };
75
+ } catch (error) {
76
+ console.error("LinkedIn OAuth error:", error);
77
+ return null;
78
+ }
79
+ }
80
+ };
81
+ }