@koopjs/auth-direct-file
Advanced tools
Comparing version 3.0.0-alpha.0 to 3.0.0
@@ -5,2 +5,13 @@ # Change Log | ||
## 3.0.0 | ||
### Changed | ||
* eliminate authenticationSpecification method | ||
### Added | ||
* allow authenticate method to "refresh" a token | ||
* allow token to arrive on header, query, body | ||
### Fixed | ||
* strip 'Bearer ' string from any authorization header | ||
## 2.0.3 | ||
@@ -7,0 +18,0 @@ ### Fixed |
{ | ||
"name": "@koopjs/auth-direct-file", | ||
"version": "3.0.0-alpha.0", | ||
"version": "3.0.0", | ||
"description": "Module for implementing a direct authentication pattern with file-based user-store in Koop", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -5,34 +5,47 @@ # Koop-Auth-Direct-File | ||
[![npm][npm-image]][npm-url] | ||
[![travis][travis-image]][travis-url] | ||
[![Greenkeeper badge][greenkeeper-image]][greenkeeper-url] | ||
## Authentication pattern | ||
## Usage | ||
The authentication module implemented here uses a *direct authentication* pattern; it receives user credentials (username/password) from a client and authenticates those credentials against an identity/user-store. Requests with valid credentials are issued an access-token (a string of encoded-data); The access token is encoded with the use of a secret known only to the Koop server. The access-token expires and becomes invalid after a certain period (default of 60 minutes). | ||
To use this plugin, you need to create a JSON file store. This should be an array of objects with properties `username` and `password`. See [user-store.json.example](user-store.json.example) as an example. Note, that this is a terrible way to store credentials! This plugin is really just meant as an example of how to design a Koop authorization plugin and not meant for any production environment. | ||
![get access token](https://gist.githubusercontent.com/rgwozdz/e44f3686abe40360532fbcc6dccf225d/raw/9768df32fc62e99ce7383c124cab8efdf45b1e18/koop-direct-auth-access-token.png) | ||
This module exports an initialization function that must be supplied with a secret-key and the path to the file that is the user-store. It will return a plugin object that can be registered with Koop. | ||
The issued access-token should be attached to all subsequent service requests by the client. When the server receives a request, it will check for the presence of an access-token and reject any requests that are missing such token. If the token is present, the server attempts to decode it with its stored secret. Failure to decode results in a request rejection. Once decoded, the server checks the token's expiration-date and rejects any token with a date that is out of range. If the token is not expired, the request for the desired resource proceeds. | ||
```javascript | ||
const authPlugin = require('@koopjs/auth-direct-file')('your-secret-key', `path/to/user-store.json`, { tokenExpirationMinutes: 300 }) | ||
koop.register(auth) | ||
``` | ||
![enter image description here](https://gist.githubusercontent.com/rgwozdz/e44f3686abe40360532fbcc6dccf225d/raw/9768df32fc62e99ce7383c124cab8efdf45b1e18/koop-direct-auth-resources.png) | ||
### Registration order | ||
## Example of Koop authentication implementation | ||
For an auth-plugin to secure the data served by a provider, it must be registered _before_ the provider. Any providers registered _after_ the auth-plugin will not have the plugin's authorization code applied. | ||
The [server.js](./server.js) file provides an example of securing a provider's resources. Start by requiring the authentication module. Pass it a secret and the file path of your user-store. | ||
```javascript | ||
const Koop = require('@koopjs/koop-core'); | ||
const providerOne = require('koop-provider-one'); | ||
const providerTwo = require('koop-provider-two'); | ||
const authPlugin = require('@koopjs/auth-direct-file')('your-secret-key', `path/to/user-store.json`); | ||
let auth = require('@koopjs/auth-direct-file')('pass-in-your-secret', `${__dirname}/user-store.json`) | ||
koop.register(auth) | ||
const koop = new Koop(); | ||
Then require and register your providers. | ||
// providerOne, registered before auth-plugin, will be secured by auth-plugin methods | ||
koop.register(providerOne); | ||
koop.register(authPlugin); | ||
// providerTwo, registered after auth-plugin, will NOT be secured | ||
koop.register(providerTwo); | ||
const provider = require('./') | ||
koop.register(provider) | ||
``` | ||
The authentication module will configure and add its `authorize`, `authenticate`, and `authenticationSpecification` functions to the provider's model prototype. Output services will leverage these functions to secure the service endpoints and properly route requests to authenticate. | ||
The underlying reason that order matter is because an auth-plugin add its `authorize` and `authenticate` functions to all _registered_ providers. (Note that providers may implement their own `authorize` and `authenticate` prototype methods, and if they do these will be used preferentially.) Output services will leverage these functions to secure the service endpoints and properly route requests to authenticate. | ||
Finally, create a JSON file store. This should be an array of objects with properties `username` and `password`. Set an environment variable `USER_STORE` with the path of the file relative to the root of the repository (e.g, `USER_STORE=./user-store.json`) | ||
## Authentication API | ||
## API | ||
### (secret, options) ⇒ <code>Object</code> | ||
### Module initialization function | ||
`(secret, userStoreFilePath, options) ⇒ Object` | ||
* configure the authentication module with secret use for token encoding/decoding | ||
@@ -46,60 +59,25 @@ | ||
| options.tokenExpirationMinutes | <code>integer</code> | minutes until token expires (default 60) | | ||
| options.useHttp | <code>boolean</code> | pass the `useHttp` boolean flag as part of the authenticationSpecification function result| | ||
## Special considerations for use with [koop-output-geoservices](https://github.com/koopjs/koop-output-geoservices) | ||
[koop-output-geoservices](https://github.com/koopjs/koop-output-geoservices) assumes that token-services occur over HTTPS. For development purposes you may wish to allow authentication to occur of HTTP. This can be done two different ways. You can add the `useHttp` option when configuring the module, which will be passed on in the result of `authenticationSpecification()` calls. | ||
let auth = require('@koopjs/auth-direct-file')('pass-in-your-secret', `${__dirname}/user-store.json`, { useHttp: true }) | ||
koop.register(auth) | ||
## Notes on use with ArcGIS Online and ArcGIS Portal | ||
This authorization plugin has been tested with ArcGIS Online and ArcGIS Enterprise. Note that these ArcGIS products appear to block or be impaired by SSH-tunneling tools like `ngrok`, so if you are trying to test a local Koop instance against Online or Enterprise, it may not work as otherwise expected. Generally, you'll have to do a "real" deployment to actually test authorization against these products. | ||
Alternatively, you can set an environment variable `KOOP_AUTH_HTTP=true`. Either of these approaches inform [koop-output-geoservices](https://github.com/koopjs/koop-output-geoservices) to use `http` as the protocol of the `tokenServicesUrl`. | ||
For enterprise versions of Portal earlier than 10.6, you may need to [import the root of your certificate into Portal's trust store](http://enterprise.arcgis.com/en/portal/10.5/administer/linux/import-a-certificate-into-the-portal.htm). We have observed the inability to store credentials for a secured Koop service on Portal instances that have not yet imported the root SSL certificate (of the Koop instance) into the trust-store. | ||
## Notes on use with ArcGIS Online and ArcGIS Portal | ||
This authorization plugin has been tested with ArcGIS Online and ArcGIS Portal. For versions of Portal earlier than 10.6, you may need to [import the root of your certificate into Portal's trust store](http://enterprise.arcgis.com/en/portal/10.5/administer/linux/import-a-certificate-into-the-portal.htm). We have observed the inability to store credentials for a secured Koop service on Portal instances that have not yet imported the root SSL certificate (of the Koop instance) into the trust-store. | ||
[npm-image]: https://img.shields.io/npm/v/@koopjs/auth-direct-file.svg?style=flat-square | ||
[npm-url]: https://www.npmjs.com/package/@koopjs/auth-direct-file | ||
[travis-image]: https://travis-ci.org/koopjs/koop-auth-direct-file.svg?style=flat-square | ||
[travis-url]: https://travis-ci.org/koopjs/koop-auth-direct-file | ||
[greenkeeper-image]: https://badges.greenkeeper.io/koopjs/koop-auth-direct-file.svg | ||
[greenkeeper-url]: https://greenkeeper.io/ | ||
## How to setup an app: Step by step guide | ||
On this guide we will show you how to setup a new app with a secured provider. We will be using [Koop-CLI](https://github.com/koopjs/koop-cli). | ||
## General notes on the authentication pattern used here | ||
``` | ||
# Install Koop-CLI if needed | ||
npm install -g @koopjs/cli | ||
The authentication module implemented here uses a *direct authentication* pattern; it receives user credentials (username/password) from a client and authenticates those credentials against an identity/user-store. Requests with valid credentials are issued an access-token (a string of encoded-data); The access token is encoded with the use of a secret known only to the Koop server. The access-token expires and becomes invalid after a certain period (default of 60 minutes). | ||
# Create a new Koop app | ||
koop new app demo-app | ||
cd demo-app | ||
![get access token](https://gist.githubusercontent.com/rgwozdz/e44f3686abe40360532fbcc6dccf225d/raw/9768df32fc62e99ce7383c124cab8efdf45b1e18/koop-direct-auth-access-token.png) | ||
# Add this Auth plugin | ||
koop add provider @koopjs/auth-direct-file | ||
In addition to directly accepting username and password, the `authenticate` method will also accept a valid token. If receivied, a new token (with new expiry time) is issued and returned. | ||
# Create a file with all valid credentials | ||
echo \[\\n\\t{ \"username\": \"admin\", \"password\": \"admin\" }\\n\] > src/user-store.json | ||
The issued access-token should be attached to all subsequent service requests by the client. When the server receives a request, it will check for the presence of an access-token and reject any requests that are missing such token. If the token is present, the server attempts to decode it with its stored secret. Failure to decode results in a request rejection. Once decoded, the server checks the token's expiration-date and rejects any token with a date that is out of range. If the token is not expired, the request for the desired resource proceeds. | ||
# Open src/plugins.js and replace: | ||
# This line: const authDirectFile = require('@koopjs/auth-direct-file') | ||
# For this line: const authDirectFile = require('@koopjs/auth-direct-file')('123456', `${__dirname}/user-store.json`, { useHttp: true }); | ||
![enter image description here](https://gist.githubusercontent.com/rgwozdz/e44f3686abe40360532fbcc6dccf225d/raw/9768df32fc62e99ce7383c124cab8efdf45b1e18/koop-direct-auth-resources.png) | ||
# Install any provider | ||
koop add provider koop-provider-carto | ||
# Run the koop server | ||
koop serve | ||
# Try to access server (it will fail <- require an access token) | ||
http://localhost:8080/koop-provider-carto/rest/services/common-data/twitter_t3chfest_reduced/FeatureServer/0 | ||
# Generate token using any of the credentials you placed at src/user-store.json | ||
http://localhost:8080/koop-provider-carto/tokens?username=rich&password=rich | ||
# Now retry to access service but adding the generated token | ||
http://localhost:8080/koop-provider-carto/rest/services/common-data/twitter_t3chfest_reduced/FeatureServer/0?token=GENERATED_TOKEN | ||
# That's it! you are done! | ||
``` |
@@ -5,3 +5,2 @@ const fs = require('fs'); | ||
const { validateCredentials } = require('./validate-credentials'); | ||
const errorPrefix = 'Auth plugin: '; | ||
const TOKEN_EXPIRATION_MINUTES = 60; | ||
@@ -15,3 +14,3 @@ | ||
.default(TOKEN_EXPIRATION_MINUTES), | ||
}); | ||
}).unknown(); | ||
@@ -33,3 +32,3 @@ let _tokenExpirationMinutes; | ||
if (!result) { | ||
throw new Error(`${errorPrefix}${userStoreFilePath} not found`); | ||
throw new Error(`${userStoreFilePath} not found`); | ||
} | ||
@@ -45,3 +44,3 @@ _secret = secret; | ||
if (error) { | ||
throw new Error(`${errorPrefix}${error.details[0].message}`); | ||
throw new Error(error.details[0].message); | ||
} | ||
@@ -55,2 +54,4 @@ | ||
authorize, | ||
name: 'file-based-auth-store', | ||
version: require('../package.json').version | ||
}; | ||
@@ -105,3 +106,6 @@ } | ||
async function authorize(req) { | ||
let token = req.query?.token || req.headers['authorization']; | ||
const { query = {}, body = {} } = req; | ||
const headerToken = req.headers['authorization'] ?? req.headers['authorization'].replace(/^Bearer /, ''); | ||
const params = { ...query, ...body }; | ||
const token = headerToken || params.token; | ||
@@ -108,0 +112,0 @@ if (!token) { |
@@ -30,3 +30,3 @@ const helpers = require('./validate-credentials'); | ||
expect(error.message).toMatch( | ||
/^Auth plugin: .+userz-store.json not found$/, | ||
/userz-store.json not found$/, | ||
); | ||
@@ -33,0 +33,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
479
0
58928
82