2023-03-08 16:41:59 +01:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
exports = module.exports = {
|
|
|
|
|
getProvider,
|
|
|
|
|
getMiddleware
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const debug = require('debug')('box:oidc'),
|
|
|
|
|
fs = require('fs'),
|
|
|
|
|
path = require('path'),
|
|
|
|
|
paths = require('./paths.js'),
|
|
|
|
|
settings = require('./settings.js');
|
|
|
|
|
|
|
|
|
|
class CloudronAdapter {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Creates an instance of MyAdapter for an oidc-provider model.
|
|
|
|
|
*
|
|
|
|
|
* @constructor
|
|
|
|
|
* @param {string} name Name of the oidc-provider model. One of "Grant, "Session", "AccessToken",
|
|
|
|
|
* "AuthorizationCode", "RefreshToken", "ClientCredentials", "Client", "InitialAccessToken",
|
|
|
|
|
* "RegistrationAccessToken", "DeviceCode", "Interaction", "ReplayDetection",
|
|
|
|
|
* "BackchannelAuthenticationRequest", or "PushedAuthorizationRequest"
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
constructor(name) {
|
|
|
|
|
this.name = name;
|
|
|
|
|
this.fileStorePath = path.join(paths.PLATFORM_DATA_DIR, `oidc-${name}.json`);
|
|
|
|
|
|
|
|
|
|
debug(`Creating adapter for ${name} backed by ${this.fileStorePath}`);
|
|
|
|
|
|
|
|
|
|
let data = {};
|
|
|
|
|
try {
|
|
|
|
|
data = JSON.parse(fs.readFileSync(this.fileStorePath), 'utf8');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
debug(`filestore for adapter ${name} not found, start with new one`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.store = data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Update or Create an instance of an oidc-provider model.
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise} Promise fulfilled when the operation succeeded. Rejected with error when
|
|
|
|
|
* encountered.
|
|
|
|
|
* @param {string} id Identifier that oidc-provider will use to reference this model instance for
|
|
|
|
|
* future operations.
|
|
|
|
|
* @param {object} payload Object with all properties intended for storage.
|
|
|
|
|
* @param {integer} expiresIn Number of seconds intended for this model to be stored.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
async upsert(id, payload, expiresIn) {
|
|
|
|
|
debug(`[${this.name}] upsert id:${id} expiresIn:${expiresIn}`, payload);
|
|
|
|
|
|
2023-03-09 18:59:04 +01:00
|
|
|
this.store[id] = { id, expiresIn, payload, consumed: false };
|
2023-03-08 16:41:59 +01:00
|
|
|
fs.writeFileSync(this.fileStorePath, JSON.stringify(this.store), 'utf8');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Return previously stored instance of an oidc-provider model.
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise} Promise fulfilled with what was previously stored for the id (when found and
|
|
|
|
|
* not dropped yet due to expiration) or falsy value when not found anymore. Rejected with error
|
|
|
|
|
* when encountered.
|
|
|
|
|
* @param {string} id Identifier of oidc-provider model
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
async find(id) {
|
|
|
|
|
debug(`[${this.name}] find id:${id}`);
|
|
|
|
|
|
|
|
|
|
if (!this.store[id]) return false;
|
|
|
|
|
|
2023-03-09 18:59:04 +01:00
|
|
|
return this.store[id].payload;
|
2023-03-08 16:41:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Return previously stored instance of DeviceCode by the end-user entered user code. You only
|
|
|
|
|
* need this method for the deviceFlow feature
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise} Promise fulfilled with the stored device code object (when found and not
|
|
|
|
|
* dropped yet due to expiration) or falsy value when not found anymore. Rejected with error
|
|
|
|
|
* when encountered.
|
|
|
|
|
* @param {string} userCode the user_code value associated with a DeviceCode instance
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
async findByUserCode(userCode) {
|
|
|
|
|
debug(`[${this.name}] FIXME findByUserCode userCode:${userCode}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Return previously stored instance of Session by its uid reference property.
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise} Promise fulfilled with the stored session object (when found and not
|
|
|
|
|
* dropped yet due to expiration) or falsy value when not found anymore. Rejected with error
|
|
|
|
|
* when encountered.
|
|
|
|
|
* @param {string} uid the uid value associated with a Session instance
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
async findByUid(uid) {
|
|
|
|
|
debug(`[${this.name}] findByUid uid:${uid}`);
|
|
|
|
|
|
|
|
|
|
for (let d in this.store) {
|
2023-03-09 19:16:36 +01:00
|
|
|
if (this.store[d].payload.uid === uid) return this.store[d].payload;
|
2023-03-08 16:41:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Mark a stored oidc-provider model as consumed (not yet expired though!). Future finds for this
|
|
|
|
|
* id should be fulfilled with an object containing additional property named "consumed" with a
|
|
|
|
|
* truthy value (timestamp, date, boolean, etc).
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise} Promise fulfilled when the operation succeeded. Rejected with error when
|
|
|
|
|
* encountered.
|
|
|
|
|
* @param {string} id Identifier of oidc-provider model
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
async consume(id) {
|
2023-03-09 18:59:04 +01:00
|
|
|
debug(`[${this.name}] consume id:${id}`);
|
|
|
|
|
|
|
|
|
|
if (this.store[id]) this.store[id].consumed = true;
|
2023-03-08 16:41:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Destroy/Drop/Remove a stored oidc-provider model. Future finds for this id should be fulfilled
|
|
|
|
|
* with falsy values.
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise} Promise fulfilled when the operation succeeded. Rejected with error when
|
|
|
|
|
* encountered.
|
|
|
|
|
* @param {string} id Identifier of oidc-provider model
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
async destroy(id) {
|
|
|
|
|
debug(`[${this.name}] destroy id:${id}`);
|
|
|
|
|
|
|
|
|
|
delete this.store[id];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Destroy/Drop/Remove a stored oidc-provider model by its grantId property reference. Future
|
|
|
|
|
* finds for all tokens having this grantId value should be fulfilled with falsy values.
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise} Promise fulfilled when the operation succeeded. Rejected with error when
|
|
|
|
|
* encountered.
|
|
|
|
|
* @param {string} grantId the grantId value associated with a this model's instance
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
async revokeByGrantId(grantId) {
|
|
|
|
|
debug(`[${this.name}] revokeByGrantId grantId:${grantId}`);
|
|
|
|
|
|
|
|
|
|
for (let d in this.store) {
|
|
|
|
|
if (this.store[d].grantId === grantId) {
|
|
|
|
|
delete this.store[d];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-09 20:17:27 +01:00
|
|
|
async function getProvider(routePrefix) {
|
2023-03-08 16:41:59 +01:00
|
|
|
const { Provider } = await import('oidc-provider');
|
|
|
|
|
|
|
|
|
|
const configuration = {
|
2023-03-09 19:16:36 +01:00
|
|
|
async findAccount(ctx, id) {
|
|
|
|
|
debug(`findAccount ctx:${ctx} id:${id}`);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
accountId: id,
|
|
|
|
|
async claims(use, scope) { return { sub: id }; },
|
|
|
|
|
};
|
|
|
|
|
},
|
2023-03-08 16:41:59 +01:00
|
|
|
adapter: CloudronAdapter,
|
2023-03-09 20:17:27 +01:00
|
|
|
interactions: {
|
|
|
|
|
url: async function(ctx, interaction) {
|
|
|
|
|
return `${routePrefix}/interaction/${interaction.uid}`;
|
|
|
|
|
}
|
2023-03-10 16:07:45 +01:00
|
|
|
},
|
2023-03-08 16:41:59 +01:00
|
|
|
clients: [{
|
|
|
|
|
client_id: 'foo',
|
|
|
|
|
client_secret: 'bar',
|
|
|
|
|
redirect_uris: ['https://openidconnect.net/callback'],
|
|
|
|
|
}],
|
|
|
|
|
pkce: {
|
|
|
|
|
required: function pkceRequired(ctx, client) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const provider = new Provider(`https://${settings.dashboardFqdn()}`, configuration);
|
|
|
|
|
provider.proxy = true
|
|
|
|
|
|
|
|
|
|
return provider;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-09 20:17:27 +01:00
|
|
|
async function getMiddleware(routePrefix) {
|
|
|
|
|
const provider = await getProvider(routePrefix);
|
2023-03-08 16:41:59 +01:00
|
|
|
|
|
|
|
|
return provider.callback();
|
|
|
|
|
}
|