https://github.com/ynohat/akamai-g2o
Connect/Express/Restify middleware implementing Akamai signature header authentication (G2O).
https://github.com/ynohat/akamai-g2o
akamai authentication
Last synced: 8 months ago
JSON representation
Connect/Express/Restify middleware implementing Akamai signature header authentication (G2O).
- Host: GitHub
- URL: https://github.com/ynohat/akamai-g2o
- Owner: ynohat
- License: mit
- Created: 2017-02-01T19:21:26.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2018-09-07T07:43:25.000Z (about 7 years ago)
- Last Synced: 2025-02-16T19:03:34.210Z (8 months ago)
- Topics: akamai, authentication
- Language: JavaScript
- Homepage:
- Size: 25.4 KB
- Stars: 2
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
Awesome Lists containing this project
README
[](https://travis-ci.org/ynohat/akamai-g2o)
> The contributions to this library are infrequent because it's features are stable and not expected to evolve much. Support for evolving NodeJS and web framework versions will be updated on a best effort basis, please reach out with pull requests or issues if something seems wrong.
# Akamai G2O for Node JS
G2O (Ghost to Origin, aka Signature Header Authentication) is an Akamai protocol that provides request authentication
between Akamai caching proxies and the origin.
This library provides a simple middleware function that provides compliant G2O validation for applications. The following
NodeJS server frameworks are supported:
- vanilla NodeJS HTTP server
- Express 4
- Restify 4
See the list of supported NodeJS versions in [the travis config file](.travis.yml).
## How the protocol works
From the documentation:
> This feature configures edge servers to include two special headers in requests to the origin server. One of these
> headers contains basic information about the specific request. The second header contains similar information encrypted
> with a shared secret. This allows the origin server to perform several levels of authentication to ensure that the request
> is coming directly from an edge server without tampering.
## What this library does not support
The reference documentation specifies that the `X-Akamai-G2O-Auth-Data` header should be stored to prevent replay attacks.
This is not supported (yet), because the library does not make assumptions about its environment and the storage engines
that may be available. It does however provide a way of extending the validation mechanism. See the `extraCheck` option
for more details.
## How to use this library
First, install it:
```
npm install --save akamai-g2o
```
It is assumed that you are familiar with G2O concepts, which aren't covered in depth here.
This simple example sets up an application to use G2O with default parameters.
```javascript
const app = require("express")();
const g2o = require("akamai-g2o");
app.use(g2o({
key: {
"nonce1": "s3cr3tk3y",
"nonce2": "s3cr3tk3y2"
}
}));
// ... setup routes and listen
```
Requests that fail authentication will result in a 403 response.
Requests that pass authentication will be extended with a `g2o` attribute with the following structure:
```javascript
{
data: {}, // see the Data object description section for details
signature: "base64 encoded signature generated by the middleware",
authenticated: true|false,
message: "present only when authenticated is false"
}
```
See the options section for more advanced configuration.
## Options
### key
Mandatory; `Object` or `Function`.
When providing an `Object`, it should map nonces to keys (see the official G2O documentation for more information about nonces).
It is also possible to provide a `Function` for more flexibility. It should have the following signature:
```javascript
function (req, data, callback)
```
`req` is the request object, typically an instance of `http.IncomingMessage`.
`data` is an `Object` representing the data in the `dataString`. It is described in more detail in the [Data object description](#data-object-description).
`callback` takes an `Error` or `null` as its first parameter and the key as its second.
### strict
*** NO LONGER SUPPORTED ***
If you want to implement not strict logic see: [onUnauthenticated](#onUnauthenticated).
### dataHeader
Optional; `String` (default: "X-Akamai-G2O-Auth-Data")
By default, Ghost sends the request data in the `X-Akamai-G2O-Auth-Data` header. Setting this option instructs the middleware
to retrieve a different header.
### signHeader
Optional; `String` (default: "X-Akamai-G2O-Auth-Sign")
By default, Ghost sends the request signature in the `X-Akamai-G2O-Auth-Sign` header. Setting this option instructs the middleware
to retrieve a different header.
### signString
Optional; `Function` (default: `req => req.url`)
By default, Ghost signs the request URL as represented in the status line of the request (the URL path).
Ghost can be configured to use any part or combination of parts of the request, in which case you
should provide a `Function` with the following signature:
```javascript
function (req) => String
```
### checkTime
Optional; `Boolean` (default: `true`)
If `true`, the request will fail if the request time is more than 30s distant from the current server time.
### timeWindow
Optional; `Number` (default: 30)
Number of seconds before or after which the difference between the server and request times will trigger an
authentication error when `checkTime` is `true`.
### extraCheck
Optional; `Function` (default: null)
If provided, the function will be called after all checks have been completed. The signature should be:
```javascript
function (req, callback)
```
An example implementation that prevents replay attacks might look like this:
```javascript
var g2o = require("akamai-g2o");
var app = require("express")();
var previousAuthDataValues = {};
app.use(g2o({
key: {
"nonce1": "s3cr3tk3y",
"nonce2": "s3cr3tk3y2"
},
extraCheck: function (req, callback) {
if (req.g2o.data.raw in previousAuthDataValues) {
callback(new Error("replayed request"));
} else {
callback();
}
}
}));
```
### onUnauthenticated
Optional; `Function` (default:
```javascript
function (req, res, next) {
var statusCode = 403;
if (typeof res.status === "function") {
// Express has a status() helper function
res.status(statusCode);
} else {
res.statusCode = statusCode;
}
res.end();
}
```
)
If provided, the function will be called after all checks have been completed and found to be unauthenticated.
An unauthenticated g2o data will have a message property, that is the failure reason. The signature should be:
```javascript
function (req, res, next)
```
An example implementation that:
- logs out the failed request
- uses a different status code
- only fails request if strict
```javascript
var g2o = require("akamai-g2o");
var app = require("express")();
var strict = true; // strict controlled outside, but maybe some config.
app.use(g2o({
key: {
"nonce1": "s3cr3tk3y",
"nonce2": "s3cr3tk3y2",
},
onUnauthenticated: function (req, res, next) {
var g2oResponse = Object.assign(
{},
{
strict: strict,
clientIp: req.ip, // for if there are no g2o header fallback to server known ip.
forwardAddresses: req.forwardAddresses,
uri: req.originalUrl, // note this is for express 4.0, else it is req.url
},
req.g2o
);
console.log('g2o unauthenticated', g2oResponse); // logging
if (strict) { // only fail request if strict
res.status(407).end(); // different status code
} else {
next();
}
}
}));
```
## Data object description
Ghost is required to send a header containing authentication information which is used to generate the signature. This library parses
the contents of the header and makes it available as an `Object` with the following structure:
```javascript
{
// the value of the raw header
raw: "1, 1.2.3.4, 2.3.4.5, 123456789, 42314563, nonce1",
// specifies the hashing function to use
version: int(1..5),
// edge server IP address
edgeIp: String,
// client IP address
clientIp: String,
// request time, as a Date object
time: Date,
// request unique identifier
uniqueId: String,
// nonce referencing the key to use
nonce: String
}
```
## Contributing
Don't break the unit tests, be as clean as you can, be nice.
```javascript
npm run -s test
```