Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/sof strategy #22

Merged
merged 5 commits into from Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 11 additions & 7 deletions FAQ.md
Expand Up @@ -30,9 +30,6 @@ You can configure which version you want to support here. The values should neve
- **logging** - Place to put logging configurations. Currently only level is allowed, but we intend to add more in the future. You can also customize the logger in `src/lib/logger` to add support for whatever you need.
- **auth** - Various configurations for authentication.
- **name** - Name of the strategy to use for passport
- **clientId** - Client Id needed for an introspection query. This will be populated by a CLIENT_ID environment variable.
- **clientSecret** - Client Secret needed for an introspection query. This will be populated by a CLIENT_SECRET environment variable.
- **introspectionUrl** - Introspection url
- **strategy** - Path to your strategy
- **passportOptions** - Options object passed directly into passport.

Expand All @@ -51,9 +48,16 @@ Authentication is implemented based on the [SMART App Authorization Guide](http:

The way it works is our server will parse the bearer token from headers and then send the token back to the introspection url along with the client id and client secret to validate the token. If the token is valid, the scopes will be used to validate access to all resources. For example, let's say Joe logs in to some app that uses this server as a backend. He authenticates with the application and then tries to load up a dashboard full of data. When the request comes to this server, we will take the token provided by the app, validate it, then check the scopes associated with it before returning any data. If Joe has enough scope to view the resources requested, they will be returned, otherwise, an insufficient scope error will be returned.

To set all this up in our server, you only need to define a few environment variables. However, you will need a valid auth server with an introspection endpoint available. This must be used to prevent someone from resigning a token after modifying it or some other man in the middle type attacks. We already wrote our own bearer strategy (`src/strategies/bearer.strategy`) that will utilize the introspection endpoint. You can use it, customize it, or add your own passport strategies (writing your own requires more customization). The three environment variables you need to define are `CLIENT_ID`, `CLIENT_SECRET`, and `INTROSPECTION_URL`.
To set all this up in our server, you only need to define a few environment variables. However, you will need a valid auth server with an introspection endpoint available. This must be used to prevent someone from resigning a token after modifying it or some other man in the middle type attacks. We already wrote our own bearer strategy, [https://github.com/Asymmetrik/phx-tools/tree/master/packages/sof-strategy](https://github.com/Asymmetrik/phx-tools/tree/master/packages/sof-strategy), that will utilize the introspection endpoint. You can use it, customize it, or add your own passport strategies (writing your own requires more customization). The three environment variables you need to define are `CLIENT_ID`, `CLIENT_SECRET`, and `INTROSPECTION_URL`.

That's it. Remember authentication is enabled by default and will throw an error if you attempt to query the graphql endpoint without setting those three environment variables. If you want to disable auth completely, just remove the name or strategy from `src/config.js` under the `SERVER_CONFIG.auth` property or remove the `configurePassport` call in `src/index.js`.
That's it. Remember authentication is enabled by default and will throw an error if you attempt to query the graphql endpoint without setting those three environment variables. If you want to disable auth completely, see below.

### Disabling Authentication
There are two things you will need to do to if you want to disable authentication. We are using [https://github.com/Asymmetrik/phx-tools/tree/master/packages/sof-strategy](https://github.com/Asymmetrik/phx-tools/tree/master/packages/sof-strategy) and [https://github.com/Asymmetrik/phx-tools/tree/master/packages/sof-graphql-invariant](https://github.com/Asymmetrik/phx-tools/tree/master/packages/sof-graphql-invariant).

To disable the strategy, just remove the name or strategy from `src/config.js` under the `SERVER_CONFIG.auth` property or remove the `configurePassport` call in `src/index.js`. This enables the authentication piece.

To disable the scope invariant, or the authorization piece, change the feature flags in `src/environment.js` for the environment you want to disable it in from true to false. For example, change `process.env.SOF_AUTHENTICATION = true;` to `process.env.SOF_AUTHENTICATION = false;`. This tells the scopeInvariantResolver to not do any scope checking, which only works if SMART is enabled.

## Resolvers
You can read a little bit about GraphQL resolvers on [graphql.org](https://graphql.org/learn/execution/#root-fields-resolvers). Resolvers are where you return data back for the API and it needs to be in the correct format. GraphQL will attempt to coerce data types being resolved when possible, but if you return an invalid property, it will throw an error. You can return an object, array of objects, promise, or an array or promises and GraphQL will just handle it. All of the resolvers are located in `src/resources/{version}/profiles/{profile_name}/resolver.js`.
Expand All @@ -62,7 +66,7 @@ All of the resolver's contain stubs that just need to be filled in, so all you n

```javascript
// In src/resources/3_0_1/profiles/patient/resolver.js
module.exports.patientResolver = function patientResolver (root, args, ctx, info) {
module.exports.getPatient = function getPatient (root, args, ctx, info) {
// This is not very realistic, but just giving you a simple idea of how
// this works
let id = args._id;
Expand Down Expand Up @@ -148,7 +152,7 @@ Finally, grab the connnection or client in your resolvers so you can start worki
// In src/resources/3_0_1/profiles/patient/resolver.js
const errorUtils = require('../../../../utils/error.utils');

module.exports.patientResolver = function patientResolver (root, args, ctx, info) {
module.exports.getPatient = function getPatient (root, args, ctx, info) {
let db = ctx.server.db;
let version = ctx.version;
let logger = ctx.server.logger;
Expand Down
3 changes: 2 additions & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "graphql-fhir",
"version": "1.0.1",
"version": "1.1.0",
"description": "A Javascript based GraphQL FHIR server",
"main": "index.js",
"author": "Robert-W <rwinterbottom@asymmetrik.com>>",
Expand All @@ -21,6 +21,7 @@
"dependencies": {
"@asymmetrik/fhir-gql-schema-utils": "^1.0.1",
"@asymmetrik/sof-graphql-invariant": "^1.0.2",
"@asymmetrik/sof-strategy": "^1.0.2",
"body-parser": "^1.18.3",
"compression": "^1.7.2",
"cross-env": "^5.2.0",
Expand Down
11 changes: 6 additions & 5 deletions src/config.js
@@ -1,4 +1,4 @@
const path = require('path');
const smartBearerStrategy = require('@asymmetrik/sof-strategy');

/**
* @name VERSION
Expand Down Expand Up @@ -27,10 +27,11 @@ const SERVER_CONFIG = {
// Auth configurations
auth: {
name: 'bearer',
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
introspectionUrl: process.env.INTROSPECTION_URL,
strategy: path.posix.resolve('src/strategies/bearer.strategy.js'),
strategy: smartBearerStrategy({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
introspectionUrl: process.env.INTROSPECTION_URL,
}),
passportOptions: {
session: false,
},
Expand Down
2 changes: 1 addition & 1 deletion src/lib/server.js
Expand Up @@ -101,7 +101,7 @@ class Server {
// to make it easy to determine if the server is using authentication
this.env.AUTHENTICATION = true;
// Add the strategy to passport
passport.use(require(auth.strategy));
passport.use(auth.strategy);
}
// return self for chaining
return this;
Expand Down
42 changes: 0 additions & 42 deletions src/middleware/deprecated.auth.middleware.js

This file was deleted.

38 changes: 0 additions & 38 deletions src/strategies/bearer.strategy.js

This file was deleted.

120 changes: 0 additions & 120 deletions src/strategies/bearer.strategy.test.js

This file was deleted.

36 changes: 32 additions & 4 deletions yarn.lock
Expand Up @@ -20,6 +20,14 @@
resolved "https://registry.yarnpkg.com/@asymmetrik/sof-scope-checker/-/sof-scope-checker-1.0.1.tgz#dd5a10088d015546c11c3160e786ed65ba9381d3"
integrity sha512-T6A24Llmrtpd46F6tfwU8Myd1ulUPKFCL3Tj6DZYLm+skQzrQ6/Bs9q3CeV7J3JX7IGw3pwh699Z78vuyDbUJQ==

"@asymmetrik/sof-strategy@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@asymmetrik/sof-strategy/-/sof-strategy-1.0.2.tgz#76de8d5bbd27becf50ccd4b75e417561907555a2"
integrity sha512-VWLvR/8FfdprqEdaH7gychQIfShom8GLtV9KixaX6SMp3iNxjDfOQ2CO7vFfJNHDTx4gPJcVaaN3iCY/nC4lGg==
dependencies:
passport-http-bearer "^1.0.1"
superagent "^4.1.0"

"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
Expand Down Expand Up @@ -1076,7 +1084,7 @@ cookie@0.3.1:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=

cookiejar@^2.1.0:
cookiejar@^2.1.0, cookiejar@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c"
integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==
Expand Down Expand Up @@ -1219,7 +1227,7 @@ debug@^3.1.0:
dependencies:
ms "^2.1.1"

debug@^4.0.1:
debug@^4.0.1, debug@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
Expand Down Expand Up @@ -1927,7 +1935,7 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=

form-data@^2.3.1, form-data@~2.3.2:
form-data@^2.3.1, form-data@^2.3.3, form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
Expand Down Expand Up @@ -3703,6 +3711,11 @@ mime@^1.4.1:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==

mime@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.0.tgz#e051fd881358585f3279df333fe694da0bcffdd6"
integrity sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==

mimic-fn@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
Expand Down Expand Up @@ -4428,7 +4441,7 @@ qs@6.5.2, qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==

qs@^6.5.1:
qs@^6.5.1, qs@^6.6.0:
version "6.6.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.6.0.tgz#a99c0f69a8d26bf7ef012f871cdabb0aee4424c2"
integrity sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA==
Expand Down Expand Up @@ -5207,6 +5220,21 @@ superagent@^3.8.3:
qs "^6.5.1"
readable-stream "^2.3.5"

superagent@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/superagent/-/superagent-4.1.0.tgz#c465c2de41df2b8d05c165cbe403e280790cdfd5"
integrity sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag==
dependencies:
component-emitter "^1.2.0"
cookiejar "^2.1.2"
debug "^4.1.0"
form-data "^2.3.3"
formidable "^1.2.0"
methods "^1.1.1"
mime "^2.4.0"
qs "^6.6.0"
readable-stream "^3.0.6"

supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
Expand Down