@openneuro/server 4.25.0 → 4.25.1-orcid-demo.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openneuro/server",
3
- "version": "4.25.0",
3
+ "version": "4.25.1-orcid-demo.0",
4
4
  "description": "Core service for the OpenNeuro platform.",
5
5
  "license": "MIT",
6
6
  "main": "src/server.js",
@@ -21,7 +21,7 @@
21
21
  "@elastic/elasticsearch": "8.13.1",
22
22
  "@graphql-tools/schema": "^10.0.0",
23
23
  "@keyv/redis": "^2.7.0",
24
- "@openneuro/search": "^4.25.0",
24
+ "@openneuro/search": "^4.25.1-orcid-demo.0",
25
25
  "@passport-next/passport-google-oauth2": "^1.0.0",
26
26
  "@sentry/node": "^4.5.3",
27
27
  "base64url": "^3.0.0",
@@ -52,7 +52,7 @@
52
52
  "passport-google-oauth20": "^1.0.0",
53
53
  "passport-jwt": "^4.0.0",
54
54
  "passport-oauth2-refresh": "^2.0.0",
55
- "passport-orcid": "^0.0.3",
55
+ "passport-orcid": "0.0.4",
56
56
  "react": "^18.2.0",
57
57
  "react-dom": "^18.2.0",
58
58
  "redlock": "^4.0.0",
@@ -86,5 +86,5 @@
86
86
  "publishConfig": {
87
87
  "access": "public"
88
88
  },
89
- "gitHead": "294a30606b3851318666efca9f987ccf8af8c5e7"
89
+ "gitHead": "259ffc8b9203f57385bc42f151ba72202f743006"
90
90
  }
@@ -317,8 +317,8 @@ export const typeDefs = `
317
317
  created: DateTime!
318
318
  modified: DateTime
319
319
  lastSeen: DateTime
320
- email: String!
321
- name: String!
320
+ email: String
321
+ name: String
322
322
  admin: Boolean
323
323
  blocked: Boolean
324
324
  }
@@ -9,8 +9,7 @@ const config = {
9
9
  GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
10
10
  GLOBUS_CLIENT_ID: process.env.GLOBUS_CLIENT_ID,
11
11
  ORCID_CLIENT_ID: process.env.ORCID_CLIENT_ID,
12
- ORCID_URI: process.env.ORCID_URI,
13
- ORCID_REDIRECT_URI: process.env.ORCID_REDIRECT_URI,
12
+ ORCID_API_ENDPOINT: process.env.ORCID_API_ENDPOINT,
14
13
  GOOGLE_TRACKING_IDS: process.env.GOOGLE_TRACKING_IDS,
15
14
  ENVIRONMENT: process.env.ENVIRONMENT,
16
15
  SUPPORT_URL: process.env.FRESH_DESK_URL,
@@ -39,9 +39,11 @@ export const getFile = async (req, res) => {
39
39
  }
40
40
  } catch (err) {
41
41
  // ConnectTimeoutError is Node/Undici and TimeoutError is the standard DOMException name
42
+ // "fetch failed" can mean the connection failed at setup (network is unavailable)
42
43
  if (
43
44
  err?.cause?.name === "ConnectTimeoutError" ||
44
- err?.name === "TimeoutError"
45
+ err?.name === "TimeoutError" ||
46
+ err?.name === "TypeError" && err?.message.includes("fetch failed")
45
47
  ) {
46
48
  // Unreachable backend, forward this error
47
49
  // Usually this is the service restarting due to node migrations or upgrades
@@ -0,0 +1,57 @@
1
+ import { vi } from "vitest"
2
+ vi.mock("../../../config.ts")
3
+ import { loadProfile } from "../passport"
4
+
5
+ describe("passport.loadProfile()", () => {
6
+ it("handles example orcid profile", () => {
7
+ const profile = {
8
+ provider: "orcid",
9
+ providerId: "0000-0000-0000-0000",
10
+ orcid: "0000-0000-0000-0000",
11
+ info: {
12
+ email: "test@example.com",
13
+ name: "Test User",
14
+ },
15
+ }
16
+ const result = loadProfile(profile)
17
+ if (result instanceof Error) {
18
+ expect(result).not.toBeInstanceOf(Error)
19
+ } else {
20
+ expect(result.email).toEqual(profile.info.email)
21
+ expect(result.name).toEqual(profile.info.name)
22
+ }
23
+ })
24
+ it("handles an ORCID without preferred name or email", () => {
25
+ const profile = {
26
+ provider: "orcid",
27
+ providerId: "0000-0000-0000-0000",
28
+ orcid: "0000-0000-0000-0000",
29
+ info: {},
30
+ }
31
+ const result = loadProfile(profile)
32
+ if (result instanceof Error) {
33
+ expect(result).not.toBeInstanceOf(Error)
34
+ } else {
35
+ expect(result.email).toBeUndefined()
36
+ expect(result.name).toEqual("Anonymous User")
37
+ }
38
+ })
39
+ it("handles an example Google account", () => {
40
+ const profile = {
41
+ provider: "google",
42
+ providerId: "1234-5678-1011",
43
+ displayName: "Test User",
44
+ emails: [
45
+ { value: "test@example.com", verified: false },
46
+ { value: "test2@example.com", verified: true },
47
+ ],
48
+ }
49
+ const result = loadProfile(profile)
50
+ if (result instanceof Error) {
51
+ expect(result).not.toBeInstanceOf(Error)
52
+ } else {
53
+ expect(result.email).toEqual("test2@example.com")
54
+ expect(result.name).toEqual("Test User")
55
+ }
56
+ })
57
+ })
@@ -23,7 +23,7 @@ interface OauthProfile {
23
23
  refresh?: string
24
24
  }
25
25
 
26
- const loadProfile = (profile): OauthProfile | Error => {
26
+ export const loadProfile = (profile): OauthProfile | Error => {
27
27
  if (profile.provider === PROVIDERS.GOOGLE) {
28
28
  // Get the account email from Google profile
29
29
  const primaryEmail = profile.emails
@@ -31,15 +31,15 @@ const loadProfile = (profile): OauthProfile | Error => {
31
31
  .shift()
32
32
  return {
33
33
  email: primaryEmail.value,
34
- name: profile.displayName,
34
+ name: profile?.displayName || "Anonymous User",
35
35
  provider: profile.provider,
36
36
  providerId: profile.id,
37
37
  refresh: undefined,
38
38
  }
39
39
  } else if (profile.provider === PROVIDERS.ORCID) {
40
40
  return {
41
- email: profile.info.email,
42
- name: profile.info.name,
41
+ email: profile?.info?.email,
42
+ name: profile?.info?.name || "Anonymous User",
43
43
  provider: profile.provider,
44
44
  providerId: profile.orcid,
45
45
  orcid: profile.orcid,
@@ -161,6 +161,7 @@ export const setupPassportAuth = () => {
161
161
  config.auth.orcid.apiURI.includes("sandbox"),
162
162
  clientID: config.auth.orcid.clientID,
163
163
  clientSecret: config.auth.orcid.clientSecret,
164
+ scope: "/read-limited",
164
165
  callbackURL: `${config.url + config.apiPrefix}auth/orcid/callback`,
165
166
  },
166
167
  verifyORCIDUser,
package/src/libs/orcid.ts CHANGED
@@ -20,22 +20,12 @@ export default {
20
20
  },
21
21
  (err, res) => {
22
22
  if (err) {
23
- return reject({
23
+ reject({
24
24
  message:
25
25
  "An unexpected ORCID login failure occurred, please try again later.",
26
26
  })
27
27
  }
28
- let doc
29
- // Catch issues with parsing this response
30
- try {
31
- doc = new xmldoc.XmlDocument(res.body)
32
- } catch (err) {
33
- return reject({
34
- type: "config",
35
- message:
36
- "ORCID auth response invalid, most likely this is a misconfigured ORCID_API_ENDPOINT value",
37
- })
38
- }
28
+ const doc = new xmldoc.XmlDocument(res.body)
39
29
  let name = doc.valueWithPath(
40
30
  "person:person.person:name.personal-details:credit-name",
41
31
  )
@@ -49,32 +39,14 @@ export default {
49
39
  "person:person.email:emails.email:email.email:email",
50
40
  )
51
41
 
52
- if (!name) {
53
- if (!firstname) {
54
- return reject({
55
- type: "given",
56
- message:
57
- "Your ORCID account does not have a given name, or it is not public. Please fix your account before continuing.",
58
- })
59
- } else if (!lastname) {
60
- return reject({
61
- type: "family",
62
- message:
63
- "Your ORCID account does not have a family name, or it is not public. Please fix your account before continuing.",
64
- })
65
- } else {
42
+ if (!name && firstname && lastname) {
43
+ if (firstname && lastname) {
66
44
  name = `${firstname} ${lastname}`
45
+ } else {
46
+ name = lastname || firstname
67
47
  }
68
48
  }
69
49
 
70
- if (!email) {
71
- return reject({
72
- type: "email",
73
- message:
74
- "Your ORCID account does not have an e-mail, or your e-mail is not public. Please fix your account before continuing.",
75
- })
76
- }
77
-
78
50
  resolve({
79
51
  name,
80
52
  email,