@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": "
|
|
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": "
|
|
89
|
+
"gitHead": "259ffc8b9203f57385bc42f151ba72202f743006"
|
|
90
90
|
}
|
package/src/graphql/schema.ts
CHANGED
package/src/handlers/config.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
package/src/handlers/datalad.ts
CHANGED
|
@@ -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
|
|
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
|
|
42
|
-
name: profile
|
|
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
|
-
|
|
23
|
+
reject({
|
|
24
24
|
message:
|
|
25
25
|
"An unexpected ORCID login failure occurred, please try again later.",
|
|
26
26
|
})
|
|
27
27
|
}
|
|
28
|
-
|
|
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 (
|
|
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,
|