@wopr-network/platform-core 1.50.0 → 1.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,13 +46,66 @@ export async function getUserCreator() {
46
46
  }
47
47
  return _userCreatorPromise;
48
48
  }
49
+ /**
50
+ * Fetch the primary verified email from GitHub's /user/emails API.
51
+ * GitHub returns null for profile.email when the user's email is private.
52
+ * The user:email scope grants access to this endpoint.
53
+ */
54
+ async function fetchGitHubPrimaryEmail(accessToken) {
55
+ try {
56
+ const res = await fetch("https://api.github.com/user/emails", {
57
+ headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/vnd.github+json" },
58
+ });
59
+ if (!res.ok)
60
+ return null;
61
+ const emails = (await res.json());
62
+ const primary = emails.find((e) => e.primary && e.verified);
63
+ return primary?.email ?? emails.find((e) => e.verified)?.email ?? null;
64
+ }
65
+ catch (error) {
66
+ logger.error("Failed to fetch GitHub primary email from /user/emails:", error);
67
+ return null;
68
+ }
69
+ }
49
70
  /** Resolve OAuth providers from config or env vars. */
50
71
  function resolveSocialProviders(cfg) {
51
72
  if (cfg.socialProviders)
52
73
  return cfg.socialProviders;
53
74
  return {
54
75
  ...(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET
55
- ? { github: { clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET } }
76
+ ? {
77
+ github: {
78
+ clientId: process.env.GITHUB_CLIENT_ID,
79
+ clientSecret: process.env.GITHUB_CLIENT_SECRET,
80
+ getUserInfo: async (token) => {
81
+ const accessToken = token.accessToken;
82
+ if (!accessToken)
83
+ return null;
84
+ const res = await fetch("https://api.github.com/user", {
85
+ headers: { Authorization: `Bearer ${accessToken}` },
86
+ });
87
+ if (!res.ok)
88
+ return null;
89
+ const profile = (await res.json());
90
+ let email = profile.email;
91
+ if (!email) {
92
+ email = await fetchGitHubPrimaryEmail(accessToken);
93
+ }
94
+ if (!email)
95
+ return null;
96
+ return {
97
+ user: {
98
+ id: String(profile.id),
99
+ name: profile.name || profile.login,
100
+ email,
101
+ image: profile.avatar_url,
102
+ emailVerified: true,
103
+ },
104
+ data: profile,
105
+ };
106
+ },
107
+ },
108
+ }
56
109
  : {}),
57
110
  ...(process.env.DISCORD_CLIENT_ID && process.env.DISCORD_CLIENT_SECRET
58
111
  ? { discord: { clientId: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET } }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wopr-network/platform-core",
3
- "version": "1.50.0",
3
+ "version": "1.50.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -116,12 +116,61 @@ export async function getUserCreator(): Promise<IUserCreator> {
116
116
  return _userCreatorPromise;
117
117
  }
118
118
 
119
+ /**
120
+ * Fetch the primary verified email from GitHub's /user/emails API.
121
+ * GitHub returns null for profile.email when the user's email is private.
122
+ * The user:email scope grants access to this endpoint.
123
+ */
124
+ async function fetchGitHubPrimaryEmail(accessToken: string): Promise<string | null> {
125
+ try {
126
+ const res = await fetch("https://api.github.com/user/emails", {
127
+ headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/vnd.github+json" },
128
+ });
129
+ if (!res.ok) return null;
130
+ const emails = (await res.json()) as { email: string; primary: boolean; verified: boolean }[];
131
+ const primary = emails.find((e) => e.primary && e.verified);
132
+ return primary?.email ?? emails.find((e) => e.verified)?.email ?? null;
133
+ } catch (error) {
134
+ logger.error("Failed to fetch GitHub primary email from /user/emails:", error);
135
+ return null;
136
+ }
137
+ }
138
+
119
139
  /** Resolve OAuth providers from config or env vars. */
120
140
  function resolveSocialProviders(cfg: BetterAuthConfig): BetterAuthOptions["socialProviders"] {
121
141
  if (cfg.socialProviders) return cfg.socialProviders;
122
142
  return {
123
143
  ...(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET
124
- ? { github: { clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET } }
144
+ ? {
145
+ github: {
146
+ clientId: process.env.GITHUB_CLIENT_ID,
147
+ clientSecret: process.env.GITHUB_CLIENT_SECRET,
148
+ getUserInfo: async (token) => {
149
+ const accessToken = token.accessToken;
150
+ if (!accessToken) return null;
151
+ const res = await fetch("https://api.github.com/user", {
152
+ headers: { Authorization: `Bearer ${accessToken}` },
153
+ });
154
+ if (!res.ok) return null;
155
+ const profile = (await res.json()) as Record<string, unknown>;
156
+ let email = profile.email as string | null;
157
+ if (!email) {
158
+ email = await fetchGitHubPrimaryEmail(accessToken);
159
+ }
160
+ if (!email) return null;
161
+ return {
162
+ user: {
163
+ id: String(profile.id),
164
+ name: (profile.name as string) || (profile.login as string),
165
+ email,
166
+ image: profile.avatar_url as string,
167
+ emailVerified: true,
168
+ },
169
+ data: profile,
170
+ };
171
+ },
172
+ },
173
+ }
125
174
  : {}),
126
175
  ...(process.env.DISCORD_CLIENT_ID && process.env.DISCORD_CLIENT_SECRET
127
176
  ? { discord: { clientId: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET } }