UNPKG

@velas/account-agent

Version:

sdk

321 lines (252 loc) 10.5 kB
import urljoin from 'url-join'; import qs from 'qs'; import assert from '../helper/assert'; import Storage from '../helper/storage'; import KeyStorage from '../helper/key-storage'; import use from '../middleware/use'; import checkJWT from '../middleware/check-jwt'; import checkScopes from '../middleware/check-scopes'; import checkClient from '../middleware/check-client'; import initInteraction from '../middleware/init-interaction'; import deleteInteraction from '../middleware/delete-interaction'; import checkRedirectUri from '../middleware/check-redirect-uri'; import saveInteraction from '../middleware/save-interaction'; import checkPolicy from '../middleware/check-policy'; import processSubmission from '../middleware/process-submission'; import bindSession from '../middleware/bind-session'; import bindSessions from '../middleware/bind-sessions'; import bindScopes from '../middleware/bind-scopes'; import AuthorizationCode from '../middleware/authorization-code'; import RedirectUri from '../middleware/redirect-uri'; import removeExpiredItems from '../middleware/remove-expired-items'; import tokenController from '../controllers/token'; import transactionController from '../controllers/transaction'; import userinfoController from '../controllers/userinfo'; import exchangeController from '../controllers/exchange'; import storageAccessController from '../controllers/storageAccess'; import Client from '../Client'; import getSession from '../helper/session'; import ScopesConstructor from '../helper/scopes'; /** * Creates a new Agent API * @constructor * @param {Object} options */ function Agent(options) { assert.check( options, { type: 'object', message: 'options parameter is not valid' }, { StorageHandler: { type: 'function', message: 'StorageHandler option is required' }, KeyStorageHandler: { type: 'function', message: 'KeyStorageHandler option is required' }, client_host: { type: 'string', message: 'client_host option is required' }, client_provider: { type: 'object', message: 'client_provider option is required' }, client_account_contract: { type: 'string', message: 'client_account_contract option is required' }, backend_payer_public_key: { type: 'string', message: 'backend_payer_public_key option is required' }, }, ); this.baseOptions = options; this.baseOptions.issuer = 'agent'; const storage = new Storage(this.baseOptions); const keyStorage = new KeyStorage(this.baseOptions); const client = new Client(this.baseOptions, keyStorage); this.provider = { Session: getSession(storage, keyStorage), storage, keyStorage, client, sc: new ScopesConstructor({ account_contract: options.client_account_contract }), }; }; async function methodHandler(method, ctx) { try { if (!['token', 'transaction', 'userinfo', 'exchange', 'storage_access' ].includes(method)) throw new Error('method option is wrong'); if (method === 'token') { await use([ checkJWT ], ctx); return await tokenController(ctx); } else if(method === 'transaction') { return await transactionController(ctx); } else if(method === 'userinfo') { return await userinfoController(ctx); } else if(method === 'exchange') { return await exchangeController(ctx); } else if(method === 'storage_access') { return await storageAccessController(ctx); }; } catch(e) { return { error: 'invalid_request', description: e.message, }; }; }; const eventHendler = async function(event) { const { origin, data: { method, state, token, data }} = event; if (!state || method === 'custom') return; event.source.postMessage({ state, stage: 'pending'}, origin); const ctx = { params: { token, method, data, origin }, provider: this.provider, }; event.source.postMessage({ state, stage: 'done', ...await methodHandler(method, ctx)}, origin); }; Agent.prototype.process = async function(method, { token, data }) { assert.check(method, { type: 'string', message: 'options method is not valid' }); assert.check(token, { type: 'string', message: 'options token is not valid' }); const ctx = { params: { token, method, data }, provider: this.provider, }; return await methodHandler(method, ctx); }; Agent.prototype.addEventListener = function() { var eventListener = (eventHendler).bind(this); if ( window.addEventListener ) { window.addEventListener("message", eventListener, false) } else { window.attachEvent("onmessage", eventListener, false) }; // IE 8; }; Agent.prototype.interaction = async function(options) { assert.check( options, { type: 'object', message: 'options parameter is not valid' }, { id: { type: 'string', message: 'id option is required' }}, ); const foundInteraction = await this.provider.storage.getItem(`${'Interaction'}:${options.id}`); assert.check(foundInteraction, { type: 'object', message: 'interaction not found' }); return { interaction: foundInteraction }; }; Agent.prototype.authorize = async function(options) { assert.check( options, { type: 'object', message: 'options parameter is not valid' }, { token: { type: 'string', message: 'token option is required' }}, ); const ctx = { params: { token: options.token }, provider: this.provider, }; await use([ checkJWT, checkScopes, initInteraction, removeExpiredItems, bindSessions, checkClient, checkRedirectUri, checkPolicy, bindScopes, saveInteraction, ], ctx); if (ctx.interaction.redirect) { return { redirect: ctx.interaction.redirect }; }; return { interaction: ctx.interaction }; }; Agent.prototype.endSession = async function(interaction_id, session_id) { assert.check(interaction_id,{ type: 'string', message: 'interaction_id option is required in endSession' }); assert.check(session_id,{ type: 'string', message: 'session_id option is required in endSession' }); const foundInteraction = await this.provider.storage.getItem(`${'Interaction'}:${interaction_id}`); assert.check(foundInteraction, { type: 'object', message: 'interaction not found' }); const session = await this.provider.Session.findById(session_id); if (session) await session.destroy(); const ctx = { result: {}, interaction: foundInteraction, provider: this.provider, }; await use([ bindSession, processSubmission, bindSessions, checkPolicy, bindScopes, saveInteraction, ], ctx); if (ctx.interaction.stage === 'completed') { await use([ AuthorizationCode, RedirectUri, deleteInteraction, ], ctx); return { redirect: ctx.redirect }; }; return { interaction: ctx.interaction }; }; Agent.prototype.logout = async function(interaction_id, session_id, client_id) { assert.check(interaction_id,{ type: 'string', message: 'interaction_id option is required in logout' }); assert.check(session_id,{ type: 'string', message: 'session_id option is required in logout' }); assert.check(client_id,{ type: 'string', message: 'client_id option is required in logout' }); const foundInteraction = await this.provider.storage.getItem(`${'Interaction'}:${interaction_id}`); assert.check(foundInteraction, { type: 'object', message: 'interaction not found' }); const session = await this.provider.Session.findById(session_id); if (session) await session.logout(client_id); const ctx = { result: {}, interaction: foundInteraction, provider: this.provider, }; await use([ bindSession, processSubmission, bindSessions, checkPolicy, bindScopes, saveInteraction, ], ctx); if (ctx.interaction.stage === 'completed') { await use([ AuthorizationCode, RedirectUri, deleteInteraction, ], ctx); return { redirect: ctx.redirect }; }; return { interaction: ctx.interaction }; }; Agent.prototype.cancelInteraction = async function(id) { assert.check(id, { type: 'string', message: 'id option is required' }); const foundInteraction = await this.provider.storage.getItem(`${'Interaction'}:${id}`); assert.check(foundInteraction, { type: 'object', message: 'interaction not found' }); let redirect = { error: 'access_denied', description: 'canceled by user', state: foundInteraction.params.state || undefined, } if (foundInteraction.params.mode === 'redirect') { redirect = urljoin(foundInteraction.params.redirect_uri, '', '?' + qs.stringify(redirect)); } this.provider.storage.removeItem(`${'Interaction'}:${id}`); return { redirect }; }; Agent.prototype.finishInteraction = async function(id, result) { assert.check( result, { type: 'object', message: 'result parameter is not valid in finishInteraction' }, { mergeWithLastSubmission: { type: 'boolean', message: 'mergeWithLastSubmission option is required in finishInteraction' }}, ); assert.check(id,{ type: 'string', message: 'id option is required in finishInteraction' }); const foundInteraction = await this.provider.storage.getItem(`${'Interaction'}:${id}`); assert.check(foundInteraction, { type: 'object', message: 'interaction not found' }); const ctx = { result, interaction: foundInteraction, provider: this.provider, }; await use([ bindSession, processSubmission, bindSessions, checkPolicy, bindScopes, saveInteraction, ], ctx); if (ctx.interaction.stage === 'completed') { await use([ AuthorizationCode, RedirectUri, deleteInteraction, ], ctx); return { redirect: ctx.redirect }; }; return { interaction: ctx.interaction }; } export default Agent;