Update api key header

Use more common X-Api-Key header with no prefix

Also updates Unauthorized response to include a json error body
and simplifies working with new API key via curl and the vscode
rest client

Signed-off-by: James Taylor <jamest@uk.ibm.com>
This commit is contained in:
James Taylor 2021-07-27 15:35:40 +01:00
parent c3a34ef559
commit 05f7026e58
5 changed files with 145 additions and 98 deletions

View file

@ -48,52 +48,60 @@ npm run start:dev
## REST API ## REST API
If everything went well, you can now make basic asset transfer REST calls! For example... If everything went well, you can now make basic asset transfer REST calls!
The examples below require a `SAMPLE_APIKEY` environment variable which must be set to an API key from the `.env` file created above.
For example, to use the ORG1_APIKEY...
```
SAMPLE_APIKEY=$(grep ORG1_APIKEY .env | cut -d '=' -f 2-)
```
### Get all assets... ### Get all assets...
```shell ```shell
curl http://localhost:3000/api/assets curl --header "X-Api-Key: ${SAMPLE_APIKEY}" http://localhost:3000/api/assets
``` ```
### Check whether an asset exists... ### Check whether an asset exists...
```shell ```shell
curl --include --request OPTIONS http://localhost:3000/api/assets/asset7 curl --include --header "X-Api-Key: ${SAMPLE_APIKEY}" --request OPTIONS http://localhost:3000/api/assets/asset7
``` ```
### Create an asset... ### Create an asset...
```shell ```shell
curl --include --header "Content-Type: application/json" --header "api-key:Api-Key <apikeyfororg>" --request POST --data '{"id":"asset7","color":"red","size":42,"owner":"Jean","appraisedValue":101}' http://localhost:3000/api/assets curl --include --header "Content-Type: application/json" --header "X-Api-Key: ${SAMPLE_APIKEY}" --request POST --data '{"id":"asset7","color":"red","size":42,"owner":"Jean","appraisedValue":101}' http://localhost:3000/api/assets
``` ```
### Read transaction status... ### Read transaction status...
```shell ```shell
curl --header "api-key:Api-Key <apikeyfororg>" http://localhost:3000/api/transactions/__transaction_id__ curl --header "X-Api-Key: ${SAMPLE_APIKEY}" http://localhost:3000/api/transactions/__transaction_id__
``` ```
### Read an asset... ### Read an asset...
```shell ```shell
curl --header "api-key:Api-Key <apikeyfororg>" http://localhost:3000/api/assets/asset7 curl --header "X-Api-Key: ${SAMPLE_APIKEY}" http://localhost:3000/api/assets/asset7
``` ```
### Update an asset... ### Update an asset...
```shell ```shell
curl --include --header "Content-Type: application/json" --header "api-key:Api-Key <apikeyfororg>" --request PUT --data '{"id":"asset7","color":"red","size":11,"owner":"Jean","appraisedValue":101}' http://localhost:3000/api/assets/asset7 curl --include --header "Content-Type: application/json" --header "X-Api-Key: ${SAMPLE_APIKEY}" --request PUT --data '{"id":"asset7","color":"red","size":11,"owner":"Jean","appraisedValue":101}' http://localhost:3000/api/assets/asset7
``` ```
### Transfer an asset... ### Transfer an asset...
```shell ```shell
curl --include --header "Content-Type: application/json" --header "api-key:Api-Key <apikeyfororg>" --request PATCH --data '[{"op":"replace","path":"/owner","value":"Ashleigh"}]' http://localhost:3000/api/assets/asset7 curl --include --header "Content-Type: application/json" --header "X-Api-Key: ${SAMPLE_APIKEY}" --request PATCH --data '[{"op":"replace","path":"/owner","value":"Ashleigh"}]' http://localhost:3000/api/assets/asset7
``` ```
### Delete an asset... ### Delete an asset...
```shell ```shell
curl --include --header "api-key:Api-Key <apikeyfororg>" --request DELETE http://localhost:3000/api/assets/asset7 curl --include --header "X-Api-Key: ${SAMPLE_APIKEY}" --request DELETE http://localhost:3000/api/assets/asset7
``` ```

View file

@ -0,0 +1,84 @@
// Demo file for use with REST Client for Visual Studio Code
// See https://github.com/Huachao/vscode-restclient
//
// Edit the values below to match your environment if required
@hostname = localhost
@port = {{$dotenv PORT}}
@baseUrl = http://{{hostname}}:{{port}}
@apiUrl = {{baseUrl}}/api
@api-key = {{$dotenv ORG1_APIKEY}}
### Check the server is ready
GET {{baseUrl}}/ready HTTP/1.1
### Check the server is still live
GET {{baseUrl}}/live HTTP/1.1
### Get all assets
GET {{apiUrl}}/assets HTTP/1.1
X-Api-Key: {{api-key}}
### Check if asset exists
OPTIONS {{apiUrl}}/assets/asset7 HTTP/1.1
X-Api-Key: {{api-key}}
### Create asset
POST {{apiUrl}}/assets HTTP/1.1
content-type: application/json
X-Api-Key: {{api-key}}
{
"id": "asset7",
"color": "red",
"size": 42,
"owner": "Jean",
"appraisedValue": 101
}
### Read transaction status
GET {{apiUrl}}/transactions/__transaction_id__ HTTP/1.1
X-Api-Key: {{api-key}}
### Read asset
GET {{apiUrl}}/assets/asset7 HTTP/1.1
X-Api-Key: {{api-key}}
### Update asset
PUT {{apiUrl}}/assets/asset7 HTTP/1.1
content-type: application/json
X-Api-Key: {{api-key}}
{
"id": "asset7",
"color": "red",
"size": 11,
"owner": "Jean",
"appraisedValue": 101
}
### Transfer asset
PATCH {{apiUrl}}/assets/asset7 HTTP/1.1
content-type: application/json
X-Api-Key: {{api-key}}
[
{
"op": "replace",
"path": "/owner",
"value": "Ashleigh"
}
]
### Delete asset
DELETE {{apiUrl}}/assets/asset7 HTTP/1.1
X-Api-Key: {{api-key}}

View file

@ -1,24 +1,61 @@
/*
* SPDX-License-Identifier: Apache-2.0
*/
import { logger } from './logger'; import { logger } from './logger';
import passport from 'passport';
import { NextFunction, Request, Response } from 'express';
import { HeaderAPIKeyStrategy } from 'passport-headerapikey'; import { HeaderAPIKeyStrategy } from 'passport-headerapikey';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import * as config from './config'; import * as config from './config';
const { UNAUTHORIZED } = StatusCodes;
export const fabricAPIKeyStrategy: HeaderAPIKeyStrategy = export const fabricAPIKeyStrategy: HeaderAPIKeyStrategy =
new HeaderAPIKeyStrategy( new HeaderAPIKeyStrategy(
{ header: 'api-key', prefix: 'Api-Key ' }, { header: 'X-API-Key', prefix: '' },
true, false,
function (apikey, done) { function (apikey, done) {
logger.debug({ apikey }, 'Checking X-API-Key');
const user: { org: string } = { const user: { org: string } = {
org: '', org: '',
}; };
if (apikey === config.org1ApiKey) { if (apikey === config.org1ApiKey) {
user.org = 'Org1'; user.org = 'Org1';
logger.info('Organisation set to Org1'); logger.debug('Organisation set to Org1');
done(null, user); done(null, user);
//todo //todo
//add org2 apikey check //add org2 apikey check
} else { } else {
logger.debug('APIKEY Mismatch'); logger.debug({ apikey }, 'No valid X-API-Key');
return done(null, false); return done(null, false);
} }
} }
); );
export const authenticateApiKey = (
req: Request,
res: Response,
next: NextFunction
): void => {
passport.authenticate(
'headerapikey',
{ session: false },
function (err, user, _info) {
if (err) return next(err);
if (!user)
return res.status(UNAUTHORIZED).json({
status: getReasonPhrase(UNAUTHORIZED),
reason: 'NO_VALID_APIKEY',
timestamp: new Date().toISOString(),
});
req.logIn(user, { session: false }, (err) => {
if (err) {
return next(err);
}
return next();
});
}
)(req, res, next);
};

View file

@ -22,7 +22,7 @@ const {
SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE,
} = StatusCodes; } = StatusCodes;
import { fabricAPIKeyStrategy } from './auth'; import { authenticateApiKey, fabricAPIKeyStrategy } from './auth';
import passport from 'passport'; import passport from 'passport';
export const createServer = async (): Promise<Application> => { export const createServer = async (): Promise<Application> => {
const app = express(); const app = express();
@ -98,16 +98,8 @@ export const createServer = async (): Promise<Application> => {
throw new Error('Example error'); throw new Error('Example error');
}); });
app.use( app.use('/api/assets', authenticateApiKey, assetsRouter);
'/api/assets', app.use('/api/transactions', authenticateApiKey, transactionsRouter);
passport.authenticate('headerapikey', { session: false }),
assetsRouter
);
app.use(
'/api/transactions',
passport.authenticate('headerapikey', { session: false }),
transactionsRouter
);
// For everything else // For everything else
app.use((_req, res) => app.use((_req, res) =>

View file

@ -1,74 +0,0 @@
// Demo file for use with REST Client for Visual Studio Code
// See https://github.com/Huachao/vscode-restclient
@hostname = localhost
@port = 3000
@baseUrl = http://{{hostname}}:{{port}}/api
//Get the apikey from .env file
@api-key= Api-Key 295069C9-ABF5-4D2A-A020-2FF9F4E8DF07
### Get all assets
GET {{baseUrl}}/assets HTTP/1.1
api-key: {{api-key}}
### Check if asset exists
OPTIONS {{baseUrl}}/assets/asset7 HTTP/1.1
api-key: {{api-key}}
### Create asset
POST {{baseUrl}}/assets HTTP/1.1
content-type: application/json
api-key: {{api-key}}
{
"id": "asset7",
"color": "red",
"size": 42,
"owner": "Jean",
"appraisedValue": 101
}
### Read transaction status
GET {{baseUrl}}/transactions/__transaction_id__ HTTP/1.1
api-key: {{api-key}}
### Read asset
GET {{baseUrl}}/assets/asset7 HTTP/1.1
api-key: {{api-key}}
### Update asset
PUT {{baseUrl}}/assets/asset7 HTTP/1.1
content-type: application/json
api-key: {{api-key}}
{
"id": "asset7",
"color": "red",
"size": 11,
"owner": "Jean",
"appraisedValue": 101
}
### Transfer asset
PATCH {{baseUrl}}/assets/asset7 HTTP/1.1
content-type: application/json
api-key: {{api-key}}
[
{
"op": "replace",
"path": "/owner",
"value": "Ashleigh"
}
]
### Delete asset
DELETE {{baseUrl}}/assets/asset7 HTTP/1.1
api-key: {{api-key}}