fhirsmith 0.3.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/CHANGELOG.md +42 -0
- package/FHIRsmith.png +0 -0
- package/README.md +277 -0
- package/config-template.json +144 -0
- package/library/folder-setup.js +58 -0
- package/library/html-server.js +166 -0
- package/library/html.js +835 -0
- package/library/i18nsupport.js +259 -0
- package/library/languages.js +779 -0
- package/library/logger-telnet.js +205 -0
- package/library/logger.js +279 -0
- package/library/package-manager.js +876 -0
- package/library/utilities.js +196 -0
- package/library/version-utilities.js +1056 -0
- package/npmprojector/config-example.json +13 -0
- package/npmprojector/indexer.js +394 -0
- package/npmprojector/npmprojector.js +395 -0
- package/npmprojector/readme.md +174 -0
- package/npmprojector/watcher.js +335 -0
- package/package.json +119 -0
- package/packages/package-crawler.js +846 -0
- package/packages/packages-template.html +126 -0
- package/packages/packages.js +2838 -0
- package/passwords.ini +2 -0
- package/publisher/publisher-template.html +208 -0
- package/publisher/publisher.js +2167 -0
- package/publisher/task-draft.js +458 -0
- package/registry/api.js +735 -0
- package/registry/crawler.js +637 -0
- package/registry/model.js +513 -0
- package/registry/readme.md +243 -0
- package/registry/registry-data.json +121015 -0
- package/registry/registry-template.html +126 -0
- package/registry/registry.js +1395 -0
- package/registry/test-runner.js +237 -0
- package/root-template.html +124 -0
- package/server.js +524 -0
- package/shl/private-key.pem +5 -0
- package/shl/public-key.pem +18 -0
- package/shl/shl.js +1125 -0
- package/shl/vhl.js +69 -0
- package/static/FHIRsmith128.png +0 -0
- package/static/FHIRsmith16.png +0 -0
- package/static/FHIRsmith32.png +0 -0
- package/static/FHIRsmith64.png +0 -0
- package/static/assets/css/bootstrap-fhir.css +5302 -0
- package/static/assets/css/bootstrap-glyphicons.css +2 -0
- package/static/assets/css/bootstrap.css +4097 -0
- package/static/assets/css/jquery-ui.css +523 -0
- package/static/assets/css/jquery-ui.structure.css +863 -0
- package/static/assets/css/jquery-ui.structure.min.css +5 -0
- package/static/assets/css/jquery-ui.theme.css +439 -0
- package/static/assets/css/jquery-ui.theme.min.css +5 -0
- package/static/assets/css/jquery.ui.all.css +7 -0
- package/static/assets/css/modules.css +18 -0
- package/static/assets/css/project.css +367 -0
- package/static/assets/css/pygments-manni.css +66 -0
- package/static/assets/css/tags.css +74 -0
- package/static/assets/css/xml.css +2 -0
- package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
- package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
- package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
- package/static/assets/ico/favicon.ico +0 -0
- package/static/assets/ico/favicon.png +0 -0
- package/static/assets/images/fhir-logo-www.png +0 -0
- package/static/assets/images/fhir-logo.png +0 -0
- package/static/assets/images/hl7-logo.png +0 -0
- package/static/assets/images/logo_ansinew.jpg +0 -0
- package/static/assets/images/search.png +0 -0
- package/static/assets/images/stripe.png +0 -0
- package/static/assets/images/target.png +0 -0
- package/static/assets/images/tx-registry-root.gif +0 -0
- package/static/assets/images/tx-registry.png +0 -0
- package/static/assets/images/tx-server.png +0 -0
- package/static/assets/images/tx-version.png +0 -0
- package/static/assets/js/bootstrap.min.js +6 -0
- package/static/assets/js/fhir-gw.js +259 -0
- package/static/assets/js/fhir.js +2 -0
- package/static/assets/js/html5shiv.js +8 -0
- package/static/assets/js/jcookie.js +96 -0
- package/static/assets/js/jquery-ui.min.js +6 -0
- package/static/assets/js/jquery.js +10716 -0
- package/static/assets/js/jquery.min.js +2 -0
- package/static/assets/js/jquery.ui.core.js +314 -0
- package/static/assets/js/jquery.ui.draggable.js +825 -0
- package/static/assets/js/jquery.ui.mouse.js +162 -0
- package/static/assets/js/jquery.ui.resizable.js +842 -0
- package/static/assets/js/jquery.ui.widget.js +268 -0
- package/static/assets/js/json2.js +487 -0
- package/static/assets/js/jtip.js +97 -0
- package/static/assets/js/respond.min.js +6 -0
- package/static/assets/js/statuspage.js +70 -0
- package/static/assets/js/xml.js +2 -0
- package/static/dist/js/bootstrap.js +1964 -0
- package/static/favicon.png +0 -0
- package/static/fhir.css +626 -0
- package/static/icon-fhir-16.png +0 -0
- package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
- package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- package/static/images/ui-icons_222222_256x240.png +0 -0
- package/static/images/ui-icons_228ef1_256x240.png +0 -0
- package/static/images/ui-icons_ef8c08_256x240.png +0 -0
- package/static/images/ui-icons_ffd27a_256x240.png +0 -0
- package/static/images/ui-icons_ffffff_256x240.png +0 -0
- package/static/js/jquery.effects.blind.js +49 -0
- package/static/js/jquery.effects.bounce.js +78 -0
- package/static/js/jquery.effects.clip.js +54 -0
- package/static/js/jquery.effects.core.js +763 -0
- package/static/js/jquery.effects.drop.js +50 -0
- package/static/js/jquery.effects.explode.js +79 -0
- package/static/js/jquery.effects.fade.js +32 -0
- package/static/js/jquery.effects.fold.js +56 -0
- package/static/js/jquery.effects.highlight.js +50 -0
- package/static/js/jquery.effects.pulsate.js +51 -0
- package/static/js/jquery.effects.scale.js +178 -0
- package/static/js/jquery.effects.shake.js +57 -0
- package/static/js/jquery.effects.slide.js +50 -0
- package/static/js/jquery.effects.transfer.js +45 -0
- package/static/js/jquery.ui.accordion.js +611 -0
- package/static/js/jquery.ui.autocomplete.js +612 -0
- package/static/js/jquery.ui.button.js +416 -0
- package/static/js/jquery.ui.datepicker.js +1823 -0
- package/static/js/jquery.ui.dialog.js +878 -0
- package/static/js/jquery.ui.droppable.js +296 -0
- package/static/js/jquery.ui.position.js +252 -0
- package/static/js/jquery.ui.progressbar.js +109 -0
- package/static/js/jquery.ui.selectable.js +266 -0
- package/static/js/jquery.ui.slider.js +666 -0
- package/static/js/jquery.ui.sortable.js +1077 -0
- package/static/js/jquery.ui.tabs.js +758 -0
- package/stats.js +80 -0
- package/test-cache/vsac/vsac-valuesets.db +0 -0
- package/token/nginx_passport_setup.md +383 -0
- package/token/security_guide.md +294 -0
- package/token/token-template.html +330 -0
- package/token/token.js +1300 -0
- package/translations/Messages.properties +1510 -0
- package/translations/Messages_ar.properties +1399 -0
- package/translations/Messages_de.properties +836 -0
- package/translations/Messages_es.properties +737 -0
- package/translations/Messages_fr.properties +1 -0
- package/translations/Messages_ja.properties +893 -0
- package/translations/Messages_nl.properties +1357 -0
- package/translations/Messages_pt.properties +1302 -0
- package/translations/Messages_ru.properties +1 -0
- package/translations/Messages_uz.properties +1 -0
- package/translations/Messages_zh.properties +1 -0
- package/translations/rendering-phrases.properties +1128 -0
- package/translations/rendering-phrases_ar.properties +1091 -0
- package/translations/rendering-phrases_de.properties +6 -0
- package/translations/rendering-phrases_es.properties +6 -0
- package/translations/rendering-phrases_fr.properties +624 -0
- package/translations/rendering-phrases_ja.properties +21 -0
- package/translations/rendering-phrases_nl.properties +970 -0
- package/translations/rendering-phrases_pt.properties +1020 -0
- package/translations/rendering-phrases_ru.properties +1094 -0
- package/translations/rendering-phrases_uz.properties +1 -0
- package/translations/rendering-phrases_zh.properties +1 -0
- package/tx/README.md +418 -0
- package/tx/cm/cm-api.js +110 -0
- package/tx/cm/cm-database.js +735 -0
- package/tx/cm/cm-package.js +325 -0
- package/tx/cs/cs-api.js +789 -0
- package/tx/cs/cs-areacode.js +615 -0
- package/tx/cs/cs-country.js +1110 -0
- package/tx/cs/cs-cpt.js +785 -0
- package/tx/cs/cs-cs.js +1579 -0
- package/tx/cs/cs-currency.js +539 -0
- package/tx/cs/cs-db.js +1321 -0
- package/tx/cs/cs-hgvs.js +329 -0
- package/tx/cs/cs-lang.js +465 -0
- package/tx/cs/cs-loinc.js +1485 -0
- package/tx/cs/cs-mimetypes.js +238 -0
- package/tx/cs/cs-ndc.js +704 -0
- package/tx/cs/cs-omop.js +1025 -0
- package/tx/cs/cs-provider-api.js +43 -0
- package/tx/cs/cs-provider-list.js +37 -0
- package/tx/cs/cs-rxnorm.js +808 -0
- package/tx/cs/cs-snomed.js +1102 -0
- package/tx/cs/cs-ucum.js +514 -0
- package/tx/cs/cs-unii.js +271 -0
- package/tx/cs/cs-uri.js +218 -0
- package/tx/cs/cs-usstates.js +305 -0
- package/tx/dev.fhir.org.yml +14 -0
- package/tx/fixtures/test-cases-setup.json +18 -0
- package/tx/fixtures/test-cases.yml +16 -0
- package/tx/html/codesystem-operations.liquid +25 -0
- package/tx/html/home-metrics.liquid +247 -0
- package/tx/html/operations-form.liquid +148 -0
- package/tx/html/search-form.liquid +62 -0
- package/tx/html/tx-template.html +133 -0
- package/tx/html/valueset-operations.liquid +54 -0
- package/tx/importers/atc-to-fhir.js +316 -0
- package/tx/importers/import-loinc.module.js +1536 -0
- package/tx/importers/import-ndc.module.js +1088 -0
- package/tx/importers/import-rxnorm.module.js +898 -0
- package/tx/importers/import-sct.module.js +2457 -0
- package/tx/importers/import-unii.module.js +601 -0
- package/tx/importers/readme.md +453 -0
- package/tx/importers/subset-loinc.module.js +1081 -0
- package/tx/importers/subset-rxnorm.module.js +938 -0
- package/tx/importers/tx-import-base.js +351 -0
- package/tx/importers/tx-import-settings.js +310 -0
- package/tx/importers/tx-import.js +357 -0
- package/tx/library/canonical-resource.js +88 -0
- package/tx/library/capabilitystatement.js +292 -0
- package/tx/library/codesystem.js +774 -0
- package/tx/library/conceptmap.js +568 -0
- package/tx/library/designations.js +932 -0
- package/tx/library/errors.js +77 -0
- package/tx/library/extensions.js +117 -0
- package/tx/library/namingsystem.js +322 -0
- package/tx/library/operation-outcome.js +127 -0
- package/tx/library/parameters.js +105 -0
- package/tx/library/renderer.js +1559 -0
- package/tx/library/terminologycapabilities.js +418 -0
- package/tx/library/ucum-parsers.js +1029 -0
- package/tx/library/ucum-service.js +370 -0
- package/tx/library/ucum-types.js +1099 -0
- package/tx/library/valueset.js +543 -0
- package/tx/library.js +676 -0
- package/tx/ocl/cm-ocl.js +106 -0
- package/tx/ocl/cs-ocl.js +39 -0
- package/tx/ocl/vs-ocl.js +105 -0
- package/tx/operation-context.js +568 -0
- package/tx/params.js +613 -0
- package/tx/provider.js +403 -0
- package/tx/sct/ecl.js +1560 -0
- package/tx/sct/expressions.js +2077 -0
- package/tx/sct/structures.js +1396 -0
- package/tx/tx-html.js +1063 -0
- package/tx/tx.fhir.org.yml +39 -0
- package/tx/tx.js +927 -0
- package/tx/vs/vs-api.js +112 -0
- package/tx/vs/vs-database.js +786 -0
- package/tx/vs/vs-package.js +358 -0
- package/tx/vs/vs-vsac.js +366 -0
- package/tx/workers/batch-validate.js +129 -0
- package/tx/workers/batch.js +361 -0
- package/tx/workers/closure.js +32 -0
- package/tx/workers/expand.js +1845 -0
- package/tx/workers/lookup.js +407 -0
- package/tx/workers/metadata.js +467 -0
- package/tx/workers/operations.js +34 -0
- package/tx/workers/read.js +164 -0
- package/tx/workers/search.js +384 -0
- package/tx/workers/subsumes.js +334 -0
- package/tx/workers/translate.js +492 -0
- package/tx/workers/validate.js +2504 -0
- package/tx/workers/worker.js +904 -0
- package/tx/xml/capabilitystatement-xml.js +63 -0
- package/tx/xml/codesystem-xml.js +62 -0
- package/tx/xml/conceptmap-xml.js +65 -0
- package/tx/xml/namingsystem-xml.js +65 -0
- package/tx/xml/operationoutcome-xml.js +127 -0
- package/tx/xml/parameters-xml.js +312 -0
- package/tx/xml/terminologycapabilities-xml.js +64 -0
- package/tx/xml/valueset-xml.js +64 -0
- package/tx/xml/xml-base.js +603 -0
- package/vcl/vcl-parser.js +1098 -0
- package/vcl/vcl.js +253 -0
- package/windows-install.js +19 -0
- package/xig/xig-template.html +124 -0
- package/xig/xig.js +3049 -0
package/stats.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const { monitorEventLoopDelay } = require('perf_hooks');
|
|
2
|
+
const {cache} = require("express/lib/application");
|
|
3
|
+
|
|
4
|
+
class ServerStats {
|
|
5
|
+
started = false;
|
|
6
|
+
requestCount = 0;
|
|
7
|
+
requestTime = 0;
|
|
8
|
+
// Collect metrics every 10 minutes
|
|
9
|
+
intervalMs = 10 * 60 * 1000;
|
|
10
|
+
history = [];
|
|
11
|
+
requestCountSnapshot = 0;
|
|
12
|
+
startMem = 0;
|
|
13
|
+
startTime = Date.now();
|
|
14
|
+
timer;
|
|
15
|
+
cachingModules = [];
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
this.timer = setInterval(() => {
|
|
19
|
+
this.recordMetrics();
|
|
20
|
+
}, this.intervalMs);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
recordMetrics() {
|
|
24
|
+
if (this.started) {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
|
|
27
|
+
const currentMem = process.memoryUsage().heapUsed;
|
|
28
|
+
const requestsDelta = this.requestCount - this.requestCountSnapshot;
|
|
29
|
+
const requestsTat = requestsDelta > 0 ? this.requestTime / requestsDelta : 0;
|
|
30
|
+
const minutesSinceStart = this.history.length > 1
|
|
31
|
+
? this.intervalMs / 60000
|
|
32
|
+
: (now - this.startTime) / 60000;
|
|
33
|
+
const requestsPerMin = minutesSinceStart > 0 ? requestsDelta / minutesSinceStart : 0;
|
|
34
|
+
const elapsedMs = (now - this.lastTime);
|
|
35
|
+
const usage = process.cpuUsage(this.lastUsage);
|
|
36
|
+
const cpuMs = (usage.user + usage.system) / 1000; // microseconds → milliseconds
|
|
37
|
+
const percent = elapsedMs > 0 ? 100 * cpuMs / elapsedMs : 0;
|
|
38
|
+
const loopDelay = this.eventLoopMonitor.mean / 1e6;
|
|
39
|
+
let cacheCount = 0;
|
|
40
|
+
for (let m of this.cachingModules) {
|
|
41
|
+
cacheCount = cacheCount + m.cacheCount();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.history.push({time: now, mem: currentMem - this.startMem, rpm: requestsPerMin, tat: requestsTat, cpu: percent, block: loopDelay, cache : cacheCount});
|
|
45
|
+
|
|
46
|
+
this.eventLoopMonitor.reset();
|
|
47
|
+
this.requestCountSnapshot = this.requestCount;
|
|
48
|
+
this.requestTime = 0;
|
|
49
|
+
this.lastUsage = process.cpuUsage();
|
|
50
|
+
this.lastTime = now;
|
|
51
|
+
|
|
52
|
+
// Prune old data (keep 24 hours)
|
|
53
|
+
const cutoff = now - (24 * 60 * 60 * 1000); // 24 hours ago
|
|
54
|
+
this.history = this.history.filter(m => m.time > cutoff);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
markStarted() {
|
|
59
|
+
this.started = true;
|
|
60
|
+
this.startMem = process.memoryUsage().heapUsed;
|
|
61
|
+
this.startTime = Date.now();
|
|
62
|
+
this.lastUsage = process.cpuUsage();
|
|
63
|
+
this.lastTime = this.startTime;
|
|
64
|
+
this.eventLoopMonitor = monitorEventLoopDelay({ resolution: 20 });
|
|
65
|
+
this.eventLoopMonitor.enable();
|
|
66
|
+
this.recordMetrics();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
countRequest(name, tat) {
|
|
70
|
+
// we ignore name for now, but we might split the tat tracking up by name
|
|
71
|
+
// at some stage
|
|
72
|
+
this.requestCount++;
|
|
73
|
+
this.requestTime = this.requestTime + tat;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
finishStats() {
|
|
77
|
+
clearInterval(this.timer);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
module.exports = ServerStats;
|
|
Binary file
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# Nginx + Passport.js Configuration Guide
|
|
2
|
+
|
|
3
|
+
## Architecture Overview
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Client Browser
|
|
7
|
+
↓ HTTPS (443) / HTTP (80)
|
|
8
|
+
Nginx Reverse Proxy
|
|
9
|
+
↓ HTTP (3000) - Internal Network
|
|
10
|
+
Node.js App (Passport.js)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This setup works perfectly with Passport.js, but requires proper configuration to handle the proxy correctly.
|
|
14
|
+
|
|
15
|
+
## Required Configuration Changes
|
|
16
|
+
|
|
17
|
+
### 1. **Express Trust Proxy Configuration**
|
|
18
|
+
|
|
19
|
+
Add this to your main `server.js` before initializing routes:
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
// server.js - Add after creating the Express app
|
|
23
|
+
const app = express();
|
|
24
|
+
|
|
25
|
+
// Trust the nginx proxy - CRITICAL for proper IP handling
|
|
26
|
+
app.set('trust proxy', 1);
|
|
27
|
+
|
|
28
|
+
// Alternative: Trust specific proxy IP
|
|
29
|
+
// app.set('trust proxy', '127.0.0.1');
|
|
30
|
+
|
|
31
|
+
// Rest of your middleware...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. **Updated Token Module for Proxy Support**
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// In token.js, update session configuration
|
|
38
|
+
initializeSession() {
|
|
39
|
+
const sessionConfig = {
|
|
40
|
+
store: new SQLiteStore({
|
|
41
|
+
db: this.config.database || 'token.db',
|
|
42
|
+
table: 'sessions'
|
|
43
|
+
}),
|
|
44
|
+
secret: this.config.sessionSecret || crypto.randomBytes(64).toString('hex'),
|
|
45
|
+
name: 'fhir.token.sid',
|
|
46
|
+
resave: false,
|
|
47
|
+
saveUninitialized: false,
|
|
48
|
+
rolling: true,
|
|
49
|
+
cookie: {
|
|
50
|
+
// This is key - secure cookies when behind HTTPS proxy
|
|
51
|
+
secure: this.config.server?.httpsProxy || process.env.NODE_ENV === 'production',
|
|
52
|
+
httpOnly: true,
|
|
53
|
+
maxAge: 24 * 60 * 60 * 1000,
|
|
54
|
+
sameSite: 'lax'
|
|
55
|
+
},
|
|
56
|
+
proxy: true // Important for proxied environments
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
this.router.use(session(sessionConfig));
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. **Environment-Specific OAuth Configuration**
|
|
64
|
+
|
|
65
|
+
Update your `config.json` with environment-specific URLs:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"server": {
|
|
70
|
+
"port": 3000,
|
|
71
|
+
"httpsProxy": true,
|
|
72
|
+
"cors": {
|
|
73
|
+
"origin": true,
|
|
74
|
+
"credentials": true
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"modules": {
|
|
78
|
+
"token": {
|
|
79
|
+
"enabled": true,
|
|
80
|
+
"database": "/absolute/path/to/token.db",
|
|
81
|
+
"sessionSecret": "your-session-secret",
|
|
82
|
+
"oauth": {
|
|
83
|
+
"google": {
|
|
84
|
+
"clientId": "your-google-client-id",
|
|
85
|
+
"clientSecret": "your-google-client-secret",
|
|
86
|
+
"redirectUri": "https://local.fhir.org/token/auth/google/callback",
|
|
87
|
+
"scope": ["openid", "profile", "email"]
|
|
88
|
+
},
|
|
89
|
+
"facebook": {
|
|
90
|
+
"clientId": "your-facebook-app-id",
|
|
91
|
+
"clientSecret": "your-facebook-app-secret",
|
|
92
|
+
"redirectUri": "https://local.fhir.org/token/auth/facebook/callback",
|
|
93
|
+
"scope": ["email"]
|
|
94
|
+
},
|
|
95
|
+
"github": {
|
|
96
|
+
"clientId": "your-github-client-id",
|
|
97
|
+
"clientSecret": "your-github-client-secret",
|
|
98
|
+
"redirectUri": "https://local.fhir.org/token/auth/github/callback",
|
|
99
|
+
"scope": ["user:email"]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 4. **Production Configuration**
|
|
108
|
+
|
|
109
|
+
For production, update OAuth redirect URIs to use `tokens.fhir.org`:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"oauth": {
|
|
114
|
+
"google": {
|
|
115
|
+
"redirectUri": "https://tokens.fhir.org/token/auth/google/callback"
|
|
116
|
+
},
|
|
117
|
+
"facebook": {
|
|
118
|
+
"redirectUri": "https://tokens.fhir.org/token/auth/facebook/callback"
|
|
119
|
+
},
|
|
120
|
+
"github": {
|
|
121
|
+
"redirectUri": "https://tokens.fhir.org/token/auth/github/callback"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Nginx Configuration
|
|
128
|
+
|
|
129
|
+
### Development (local.fhir.org)
|
|
130
|
+
|
|
131
|
+
```nginx
|
|
132
|
+
server {
|
|
133
|
+
listen 80;
|
|
134
|
+
server_name local.fhir.org;
|
|
135
|
+
return 301 https://$server_name$request_uri;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
server {
|
|
139
|
+
listen 443 ssl http2;
|
|
140
|
+
server_name local.fhir.org;
|
|
141
|
+
|
|
142
|
+
# SSL configuration for local development
|
|
143
|
+
ssl_certificate /path/to/local.fhir.org.crt;
|
|
144
|
+
ssl_certificate_key /path/to/local.fhir.org.key;
|
|
145
|
+
|
|
146
|
+
# Security headers
|
|
147
|
+
add_header X-Frame-Options SAMEORIGIN;
|
|
148
|
+
add_header X-Content-Type-Options nosniff;
|
|
149
|
+
add_header X-XSS-Protection "1; mode=block";
|
|
150
|
+
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
151
|
+
|
|
152
|
+
location / {
|
|
153
|
+
proxy_pass http://127.0.0.1:3000;
|
|
154
|
+
proxy_http_version 1.1;
|
|
155
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
156
|
+
proxy_set_header Connection 'upgrade';
|
|
157
|
+
proxy_set_header Host $host;
|
|
158
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
159
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
160
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
161
|
+
proxy_set_header X-Forwarded-Host $host;
|
|
162
|
+
proxy_set_header X-Forwarded-Port $server_port;
|
|
163
|
+
proxy_cache_bypass $http_upgrade;
|
|
164
|
+
|
|
165
|
+
# Important for OAuth callbacks
|
|
166
|
+
proxy_redirect http://127.0.0.1:3000 https://local.fhir.org;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Production (tokens.fhir.org)
|
|
172
|
+
|
|
173
|
+
```nginx
|
|
174
|
+
server {
|
|
175
|
+
listen 80;
|
|
176
|
+
server_name tokens.fhir.org;
|
|
177
|
+
return 301 https://$server_name$request_uri;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
server {
|
|
181
|
+
listen 443 ssl http2;
|
|
182
|
+
server_name tokens.fhir.org;
|
|
183
|
+
|
|
184
|
+
# SSL configuration
|
|
185
|
+
ssl_certificate /path/to/tokens.fhir.org.crt;
|
|
186
|
+
ssl_certificate_key /path/to/tokens.fhir.org.key;
|
|
187
|
+
|
|
188
|
+
# Enhanced security headers for production
|
|
189
|
+
add_header X-Frame-Options DENY;
|
|
190
|
+
add_header X-Content-Type-Options nosniff;
|
|
191
|
+
add_header X-XSS-Protection "1; mode=block";
|
|
192
|
+
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
|
193
|
+
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'";
|
|
194
|
+
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
|
195
|
+
|
|
196
|
+
# Rate limiting
|
|
197
|
+
limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;
|
|
198
|
+
limit_req zone=auth burst=10 nodelay;
|
|
199
|
+
|
|
200
|
+
location / {
|
|
201
|
+
proxy_pass http://127.0.0.1:3000;
|
|
202
|
+
proxy_http_version 1.1;
|
|
203
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
204
|
+
proxy_set_header Connection 'upgrade';
|
|
205
|
+
proxy_set_header Host $host;
|
|
206
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
207
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
208
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
209
|
+
proxy_set_header X-Forwarded-Host $host;
|
|
210
|
+
proxy_set_header X-Forwarded-Port $server_port;
|
|
211
|
+
proxy_cache_bypass $http_upgrade;
|
|
212
|
+
|
|
213
|
+
# Timeouts
|
|
214
|
+
proxy_connect_timeout 60s;
|
|
215
|
+
proxy_send_timeout 60s;
|
|
216
|
+
proxy_read_timeout 60s;
|
|
217
|
+
|
|
218
|
+
proxy_redirect http://127.0.0.1:3000 https://tokens.fhir.org;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
# Optional: Serve static files directly from nginx
|
|
222
|
+
location /assets/ {
|
|
223
|
+
alias /path/to/app/static/assets/;
|
|
224
|
+
expires 1y;
|
|
225
|
+
add_header Cache-Control "public, immutable";
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## OAuth Provider Registration
|
|
231
|
+
|
|
232
|
+
### Development URLs
|
|
233
|
+
When registering your app with OAuth providers for development:
|
|
234
|
+
|
|
235
|
+
**Google Cloud Console:**
|
|
236
|
+
- Authorized JavaScript origins: `https://local.fhir.org`
|
|
237
|
+
- Authorized redirect URIs: `https://local.fhir.org/token/auth/google/callback`
|
|
238
|
+
|
|
239
|
+
**Facebook for Developers:**
|
|
240
|
+
- Valid OAuth Redirect URIs: `https://local.fhir.org/token/auth/facebook/callback`
|
|
241
|
+
- App Domains: `local.fhir.org`
|
|
242
|
+
|
|
243
|
+
**GitHub OAuth Apps:**
|
|
244
|
+
- Authorization callback URL: `https://local.fhir.org/token/auth/github/callback`
|
|
245
|
+
|
|
246
|
+
### Production URLs
|
|
247
|
+
For production, register separate apps or add additional URLs:
|
|
248
|
+
|
|
249
|
+
- Google: `https://tokens.fhir.org/token/auth/google/callback`
|
|
250
|
+
- Facebook: `https://tokens.fhir.org/token/auth/facebook/callback`
|
|
251
|
+
- GitHub: `https://tokens.fhir.org/token/auth/github/callback`
|
|
252
|
+
|
|
253
|
+
## Environment Configuration
|
|
254
|
+
|
|
255
|
+
### Development Environment Variables
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# .env.development
|
|
259
|
+
NODE_ENV=development
|
|
260
|
+
PORT=3000
|
|
261
|
+
SESSION_SECRET=your-dev-session-secret
|
|
262
|
+
BEHIND_PROXY=true
|
|
263
|
+
|
|
264
|
+
# OAuth Development
|
|
265
|
+
GOOGLE_CLIENT_ID=your-dev-google-client-id
|
|
266
|
+
GOOGLE_CLIENT_SECRET=your-dev-google-client-secret
|
|
267
|
+
FACEBOOK_CLIENT_ID=your-dev-facebook-client-id
|
|
268
|
+
FACEBOOK_CLIENT_SECRET=your-dev-facebook-client-secret
|
|
269
|
+
GITHUB_CLIENT_ID=your-dev-github-client-id
|
|
270
|
+
GITHUB_CLIENT_SECRET=your-dev-github-client-secret
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Production Environment Variables
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# .env.production
|
|
277
|
+
NODE_ENV=production
|
|
278
|
+
PORT=3000
|
|
279
|
+
SESSION_SECRET=your-production-session-secret-64-chars-minimum
|
|
280
|
+
BEHIND_PROXY=true
|
|
281
|
+
|
|
282
|
+
# OAuth Production
|
|
283
|
+
GOOGLE_CLIENT_ID=your-prod-google-client-id
|
|
284
|
+
GOOGLE_CLIENT_SECRET=your-prod-google-client-secret
|
|
285
|
+
FACEBOOK_CLIENT_ID=your-prod-facebook-client-id
|
|
286
|
+
FACEBOOK_CLIENT_SECRET=your-prod-facebook-client-secret
|
|
287
|
+
GITHUB_CLIENT_ID=your-prod-github-client-id
|
|
288
|
+
GITHUB_CLIENT_SECRET=your-prod-github-client-secret
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Configuration Loading
|
|
292
|
+
|
|
293
|
+
Update your server.js to handle environment-specific configs:
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
// server.js - Enhanced configuration loading
|
|
297
|
+
let config;
|
|
298
|
+
try {
|
|
299
|
+
const configFile = process.env.NODE_ENV === 'production' ? 'config.production.json' : 'config.json';
|
|
300
|
+
const configPath = path.join(__dirname, configFile);
|
|
301
|
+
const configData = fs.readFileSync(configPath, 'utf8');
|
|
302
|
+
config = JSON.parse(configData);
|
|
303
|
+
|
|
304
|
+
// Override with environment variables if present
|
|
305
|
+
if (process.env.SESSION_SECRET) {
|
|
306
|
+
config.modules.token.sessionSecret = process.env.SESSION_SECRET;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// OAuth environment overrides
|
|
310
|
+
const oauthOverrides = {
|
|
311
|
+
google: {
|
|
312
|
+
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
313
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET
|
|
314
|
+
},
|
|
315
|
+
facebook: {
|
|
316
|
+
clientId: process.env.FACEBOOK_CLIENT_ID,
|
|
317
|
+
clientSecret: process.env.FACEBOOK_CLIENT_SECRET
|
|
318
|
+
},
|
|
319
|
+
github: {
|
|
320
|
+
clientId: process.env.GITHUB_CLIENT_ID,
|
|
321
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
Object.keys(oauthOverrides).forEach(provider => {
|
|
326
|
+
if (config.modules.token.oauth[provider] && oauthOverrides[provider].clientId) {
|
|
327
|
+
config.modules.token.oauth[provider] = {
|
|
328
|
+
...config.modules.token.oauth[provider],
|
|
329
|
+
...oauthOverrides[provider]
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
serverLog.info(`Loaded ${configFile}. Active modules = ${Object.keys(config.modules).filter(mod => config.modules[mod].enabled).join(', ')}`);
|
|
335
|
+
} catch (error) {
|
|
336
|
+
serverLog.error('Failed to load configuration:', error.message);
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Testing the Setup
|
|
342
|
+
|
|
343
|
+
### 1. **Local Development Test**
|
|
344
|
+
```bash
|
|
345
|
+
# Start your Node.js server
|
|
346
|
+
npm run dev
|
|
347
|
+
|
|
348
|
+
# Test the OAuth flow
|
|
349
|
+
curl -I https://local.fhir.org/token/auth/google
|
|
350
|
+
|
|
351
|
+
# Should return 302 redirect to Google OAuth
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 2. **Production Deployment Test**
|
|
355
|
+
```bash
|
|
356
|
+
# Test OAuth endpoints
|
|
357
|
+
curl -I https://tokens.fhir.org/token/auth/google
|
|
358
|
+
curl -I https://tokens.fhir.org/token/auth/facebook
|
|
359
|
+
curl -I https://tokens.fhir.org/token/auth/github
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Troubleshooting Common Issues
|
|
363
|
+
|
|
364
|
+
### 1. **"Invalid Redirect URI" Error**
|
|
365
|
+
- Ensure OAuth app registration URLs exactly match your callback URLs
|
|
366
|
+
- Check for trailing slashes in URLs
|
|
367
|
+
- Verify HTTPS vs HTTP protocol
|
|
368
|
+
|
|
369
|
+
### 2. **Session Issues Behind Proxy**
|
|
370
|
+
- Ensure `app.set('trust proxy', 1)` is set
|
|
371
|
+
- Verify nginx proxy headers are correctly configured
|
|
372
|
+
- Check that `secure: true` is set for cookies in production
|
|
373
|
+
|
|
374
|
+
### 3. **CSRF Token Errors**
|
|
375
|
+
- Verify nginx is passing all headers correctly
|
|
376
|
+
- Ensure `X-Forwarded-Proto` header is set to `https`
|
|
377
|
+
- Check that `sameSite` cookie setting is appropriate
|
|
378
|
+
|
|
379
|
+
### 4. **IP Address Logging Issues**
|
|
380
|
+
- If you see 127.0.0.1 instead of real IPs, check `trust proxy` setting
|
|
381
|
+
- Verify `X-Real-IP` and `X-Forwarded-For` headers in nginx config
|
|
382
|
+
|
|
383
|
+
This setup provides a robust, production-ready OAuth implementation behind nginx proxy that works seamlessly with Passport.js!
|