auth0-ext-compilers
Advanced tools
Comparing version 4.0.0 to 5.0.0
'use strict'; | ||
const Errors = require('./errors'); | ||
module.exports = { | ||
respondWithError, | ||
wrap, | ||
@@ -11,2 +10,44 @@ }; | ||
function respondWithError(error, res) { | ||
if (!(error instanceof Error)) { | ||
error = new Error(error.message || String(error) || 'Unknown error') | ||
} | ||
if (!error.statusCode) { | ||
error.statusCode = 500; | ||
} | ||
const statusCode = error.statusCode; | ||
const headers = { | ||
'Content-Type': 'application/json', | ||
}; | ||
const payload = { | ||
message: error.message, | ||
statusCode: error.statusCode, | ||
} | ||
['code', 'errno', 'error', 'error_description', 'data'] | ||
.forEach(key => { | ||
if (error[key]) payload[key] = error[key]; | ||
}); | ||
if (error.statusCode === 500 && error.stack) { | ||
payload.stack = error.stack; | ||
} | ||
let json; | ||
try { | ||
json = JSON.stringify(payload); | ||
} catch (e) { | ||
const error = new Error('Error serializing error: ' + e.message); | ||
error.statusCode = 500; | ||
return respondWithError(error, res); | ||
} | ||
res.writeHead(statusCode, headers); | ||
res.end(json); | ||
} | ||
function wrap(webtaskFn, payloadAdapter) { | ||
@@ -26,40 +67,33 @@ if (!payloadAdapter) { | ||
function buildResponse(error /*, arg1, arg2, ...*/) { | ||
if (error) { | ||
return respondWithError(error, res); | ||
} | ||
const response = { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
} | ||
if (error) { | ||
response.statusCode = error.statusCode || 500; | ||
response.data = { | ||
message: error.message || 'Unkown error', | ||
code: error.code || 'unknown_error', | ||
statusCode: error.statusCode || 500, | ||
stack: error.stack, | ||
}; | ||
} else { | ||
headers: { }, | ||
// Marshall the non-error callback arguments into the wire format | ||
// that the extension <--> auth0-server protocol expects | ||
response.data = payloadAdapter.apply(null, Array.prototype.slice.call(arguments, 1)); | ||
data: payloadAdapter.apply(null, Array.prototype.slice.call(arguments, 1)), | ||
} | ||
return respond(response); | ||
} | ||
// Currently the respond function assumes json as the only format that | ||
// will be sent over the wire. In the future we could inspect the request | ||
// and do applicable content negotiation. | ||
function respond(response) { | ||
// Currently the respond function assumes json as the only format that | ||
// will be sent over the wire. In the future we could inspect the request | ||
// and do applicable content negotiation. | ||
let json; | ||
try { | ||
const body = JSON.stringify(response.data); | ||
res.writeHead(response.statusCode, response.headers); | ||
res.end(body); | ||
json = JSON.stringify(response.data); | ||
} catch (e) { | ||
return buildResponse(Errors.badImplementation('Error when JSON serializing the result of the extension point')); | ||
return respondWithError(new Error('Error when JSON serializing the result of the extension point'), res); | ||
} | ||
response.headers['Content-Type'] = 'application/json'; | ||
res.writeHead(response.statusCode, response.headers); | ||
res.end(json); | ||
return; | ||
} | ||
} | ||
} |
'use strict'; | ||
const Errors = require('./errors'); | ||
module.exports = { | ||
@@ -16,5 +14,9 @@ is_authorized, | ||
if (match && match[1] === ctx.secrets['auth0-extension-secret']) return cb(); | ||
return cb(Errors.unauthorized()); | ||
const error = new Error('Unauthorized extensibility point'); | ||
error.statusCode = 401; | ||
return cb(error); | ||
} | ||
return cb(); | ||
} |
@@ -5,11 +5,22 @@ 'use strict'; | ||
const Authz = require('../authorization'); | ||
const Errors = require('../errors'); | ||
module.exports = authorize_and_process_body; | ||
module.exports = clientCredentialsExchange; | ||
function authorize_and_process_body(options, cb) { | ||
function clientCredentialsExchange(options, cb) { | ||
options.nodejsCompiler(options.script, function (error, func) { | ||
if (error) return cb(error); | ||
if (error) { | ||
// Return a wrapped webtask function that will generate an error | ||
// so that we have a consistent pathway to error reporting | ||
const webtaskFn = (webtaskContext, cb) => { | ||
error.error_description = error.message; | ||
error.message = 'Unable to compile the extensibility code as javascript'; | ||
return cb(error); | ||
}; | ||
return cb(null, Adapter.wrap(webtaskFn)); | ||
} | ||
@@ -21,13 +32,13 @@ | ||
function handler (webtaskContext, cb) { | ||
Authz.is_authorized(webtaskContext, err => { | ||
if (err) return cb(err); | ||
Authz.is_authorized(webtaskContext, error => { | ||
if (error) return cb(error); | ||
if (typeof webtaskContext.body !== 'object') | ||
return cb(Errors.badRequest()); | ||
return cb(new Error('Body received by extensibility point is not an object')); | ||
if (typeof webtaskContext.body.client !== 'object') | ||
return cb(Errors.badRequest()); | ||
return cb(new Error('Body .client received by extensibility point is not an object')); | ||
if (webtaskContext.body.scope && !Array.isArray(webtaskContext.body.scope)) | ||
return cb(Errors.badRequest()); | ||
return cb(new Error('Body .scope received by extensibility point is neither empty nor an array')); | ||
if (typeof webtaskContext.body.audience !== 'string') | ||
return cb(Errors.badRequest()); | ||
return cb(new Error('Body .audience received by extensibility point is not a string')); | ||
@@ -34,0 +45,0 @@ const context = typeof webtaskContext.body.context === 'object' |
@@ -5,3 +5,2 @@ 'use strict'; | ||
const Authz = require('../authorization'); | ||
const Errors = require('../errors'); | ||
@@ -23,13 +22,5 @@ | ||
function rawHandler(ctx, req, res) { | ||
Authz.is_authorized(ctx, err => { | ||
if (err) { | ||
res.writeHead(err.statusCode || 500, { 'Content-Type': 'application/json' }); | ||
res.end(JSON.stringify({ | ||
message: err.message || 'Unkown error', | ||
code: err.code || 'unknown_error', | ||
statusCode: err.statusCode || 500, | ||
stack: err.stack, | ||
})); | ||
return; | ||
Authz.is_authorized(ctx, error => { | ||
if (error) { | ||
return Adapter.respondWithError(error, res); | ||
} | ||
@@ -36,0 +27,0 @@ |
@@ -8,3 +8,3 @@ { | ||
}, | ||
"version": "4.0.0", | ||
"version": "5.0.0", | ||
"description": "Webtask compilers for Auth0 platform extensibility points", | ||
@@ -11,0 +11,0 @@ "engines": { |
@@ -11,6 +11,8 @@ This repository contains [webtask compilers](https://webtask.io/docs/webtask-compilers) that enable custom programming models for Auth0 platform extensibility points. | ||
cat > custom_claims.js <<EOF | ||
module.exports = function (ctx, cb) { | ||
ctx.scope.push('foo'); | ||
ctx['https://example.com/foo'] = 'bar'; | ||
cb(null, ctx); | ||
module.exports = function(client, scope, audience, context, cb) { | ||
var access_token = {}; | ||
access_token['https://foo.com/claim'] = 'bar'; | ||
access_token.scope = scope; | ||
access_token.scope.push('extra'); | ||
cb(null, access_token); | ||
}; | ||
@@ -29,5 +31,5 @@ EOF | ||
## What is Auth0 extension | ||
## What is an Auth0 extension | ||
Auth0 extension is a webtask created in the Auth0 tenant's webtask container and associated with specific metadata properties as outlined in the table below. | ||
An Auth0 extension is a webtask created in the Auth0 tenant's webtask container and associated with specific metadata properties as outlined in the table below. | ||
@@ -46,3 +48,3 @@ | Name | Required? | Value | | ||
Auth0 extensions are executed by issuing an HTTP POST request to the webtask URL from Auth0 runtime. To ensure only Auth0 runtime and/or a specific Auth0 tenant can issue such requests, the requests use a secret-based authorization mechanism. If an extension webtask has been created with `auth0-extension-secret` secret parameter, the value of that parameter MUST equal to the value of the `Authorization: Bearer {secret}` header of the HTTP POST request. To allow Auth0 runtime to add the necessary header to the webtask request it is making, the same secret value is stored in the `auth0-extension-secret` metadata property. This setup can be achieved with the following: | ||
Auth0 extensions are executed by issuing an HTTP POST request to the webtask URL from the Auth0 runtime. To ensure that only the Auth0 runtime and/or a specific Auth0 tenant can issue such requests, the requests use a secret-based authorization mechanism. If an extension webtask has been created with the `auth0-extension-secret` secret parameter, the value of that parameter MUST equal to the value of the `Authorization: Bearer {secret}` header of the HTTP POST request. To allow the Auth0 runtime to add the necessary header to the webtask request it is making, the same secret value is stored in the `auth0-extension-secret` metadata property. This setup can be achieved with the following: | ||
@@ -120,8 +122,43 @@ ```bash | ||
@param {string} audience - token's audience claim | ||
@param {object} context - additional authorization context | ||
@param {object} context.webtask - the raw webtask context object | ||
@param {function} cb - function (error, accessTokenClaims) | ||
*/ | ||
module.exports = function (client, scope, audience, cb) { | ||
// add any namespaced claims directly to ctx | ||
module.exports = function (client, scope, audience, context cb) { | ||
// call the callback with an error to signal authorization failure | ||
// or with a mapping of claims to values (including scopes). | ||
cb(null, { claim: 'value' }); // return error or a mapping of access token claims | ||
}; | ||
``` | ||
### The *generic* programming model for all extensibility points | ||
A generic compiler is provided (`auth0-ext-compilers/generic`) that does not adhere to any extension-specific programming model. Instead, this compiler is a light facade on top of the 2ary and 3ary [webtask programming models](https://webtask.io/docs/model). The compiler provides authorization of the incoming webtask request and then invokes the supplied function. | ||
#### 2ary *generic* extension | ||
```javascript | ||
module.exports = function(ctx, cb) { | ||
var scope = ctx.body.scope; | ||
var access_token = {}; | ||
access_token['https://foo.com/claim'] = 'bar'; | ||
access_token.scope = scope; | ||
access_token.scope.push('extra'); | ||
cb(null, access_token); | ||
}; | ||
``` | ||
#### 3ary *generic* extension | ||
```javascript | ||
module.exports = function(ctx, req, res) { | ||
var scope = ctx.body.scope; | ||
var access_token = {}; | ||
access_token['https://foo.com/claim'] = 'bar'; | ||
access_token.scope = scope; | ||
access_token.scope.push('extra'); | ||
res.writeHead(200, { 'Content-Type': 'application/json' }); | ||
res.end(JSON.stringify(access_token)); | ||
}; | ||
``` |
@@ -91,3 +91,3 @@ 'use strict'; | ||
Assert.ok(error); | ||
Assert.equal(error.statusCode, 401); | ||
Assert.equal(error.status, 401); | ||
Assert.equal(data, undefined); | ||
@@ -111,3 +111,3 @@ done(); | ||
Assert.ok(error); | ||
Assert.equal(error.statusCode, 401); | ||
Assert.equal(error.status, 401); | ||
Assert.equal(data, undefined); | ||
@@ -259,3 +259,3 @@ done(); | ||
Assert.ok(error); | ||
Assert.equal(error.statusCode, 400); | ||
Assert.equal(error.status, 400); | ||
Assert.equal(data, undefined); | ||
@@ -278,3 +278,3 @@ done(); | ||
Assert.ok(error); | ||
Assert.equal(error.statusCode, 400); | ||
Assert.equal(error.status, 400); | ||
Assert.equal(data, undefined); | ||
@@ -297,3 +297,3 @@ done(); | ||
Assert.ok(error); | ||
Assert.equal(error.statusCode, 400); | ||
Assert.equal(error.status, 400); | ||
Assert.equal(data, undefined); | ||
@@ -316,3 +316,3 @@ done(); | ||
Assert.ok(error); | ||
Assert.equal(error.statusCode, 400); | ||
Assert.equal(error.status, 400); | ||
Assert.equal(data, undefined); | ||
@@ -336,3 +336,3 @@ done(); | ||
Assert.ok(error); | ||
Assert.equal(error.statusCode, 401); | ||
Assert.equal(error.status, 401); | ||
Assert.equal(data, undefined); | ||
@@ -356,3 +356,3 @@ done(); | ||
Assert.ok(error); | ||
Assert.equal(error.statusCode, 401); | ||
Assert.equal(error.status, 401); | ||
Assert.equal(data, undefined); | ||
@@ -369,2 +369,3 @@ done(); | ||
try { | ||
// For brevity ;-) | ||
var factory = eval('(function (module) {' + script + '})'); | ||
@@ -396,6 +397,11 @@ var m = { exports: {} }; | ||
error.code = payload.code; | ||
error.statusCode = payload.statusCode; | ||
error.stack = payload.stack; | ||
error.title = payload.title; | ||
error.status = payload.status; | ||
error.detail = payload.detail; | ||
for (let key in payload) { | ||
if (!error[key]) { | ||
error[key] = payload[key]; | ||
} | ||
} | ||
return cb(error); | ||
@@ -402,0 +408,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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
31536
518
161
1
10