UNPKG

@xrengine/server-core

Version:

Shared components for XREngine server

246 lines (224 loc) 6.67 kB
import { Paginated, Params } from '@feathersjs/feathers' import { SequelizeServiceOptions, Service } from 'feathers-sequelize' import { random } from 'lodash' import { Sequelize } from 'sequelize' import { v1 as uuidv1 } from 'uuid' import { isDev } from '@xrengine/common/src/config' import { IdentityProviderInterface } from '@xrengine/common/src/dbmodels/IdentityProvider' import { AvatarInterface } from '@xrengine/common/src/interfaces/AvatarInterface' import { UserInterface } from '@xrengine/common/src/interfaces/User' import { Application } from '../../../declarations' import appConfig from '../../appconfig' import { scopeTypeSeed } from '../../scope/scope-type/scope-type.seed' import getFreeInviteCode from '../../util/get-free-invite-code' import { UserParams } from '../user/user.class' interface IdentityProviderParams extends UserParams { bot?: boolean } /** * A class for identity-provider service */ export class IdentityProvider<T = IdentityProviderInterface> extends Service<T> { public app: Application public docs: any constructor(options: Partial<SequelizeServiceOptions>, app: Application) { super(options) this.app = app } /** * A method used to create accessToken * * @param data which contains token and type * @param params * @returns accessToken */ async create(data: any, params: IdentityProviderParams = {}): Promise<T & { accessToken?: string }> { let { token, type, password } = data let user let authResult if (params.authentication) { authResult = await (this.app.service('authentication') as any).strategies.jwt.authenticate( { accessToken: params.authentication.accessToken }, {} ) if (authResult[appConfig.authentication.entity]?.userId) { user = await this.app.service('user').get(authResult[appConfig.authentication.entity]?.userId) } } if ( (!user || !user.scopes || !user.scopes.find((scope) => scope.type === 'admin:admin')) && params.provider && type !== 'password' && type !== 'email' && type !== 'sms' ) type = 'guest' //Non-password/magiclink create requests must always be for guests let userId = data.userId || (authResult ? authResult[appConfig.authentication.entity]?.userId : null) let identityProvider: any switch (type) { case 'email': identityProvider = { token, type } break case 'sms': identityProvider = { token, type } break case 'password': identityProvider = { token, password, type } break case 'github': identityProvider = { token: token, type } break case 'facebook': identityProvider = { token: token, type } break case 'google': identityProvider = { token: token, type } break case 'twitter': identityProvider = { token: token, type } break case 'linkedin': identityProvider = { token: token, type } break case 'discord': identityProvider = { token: token, type } break case 'guest': identityProvider = { token: token, type: type } break case 'auth0': break } // if userId is not defined, then generate userId if (!userId) { userId = uuidv1() } const sequelizeClient: Sequelize = this.app.get('sequelizeClient') const userService = this.app.service('user') const User = sequelizeClient.model('user') // check if there is a user with userId let foundUser try { foundUser = await userService.get(userId) } catch (err) {} if (foundUser != null) { // if there is the user with userId, then we add the identity provider to the user return (await super.create( { ...data, ...identityProvider, userId }, params )) as T & { accessToken?: string } } // create with user association params.sequelize = { include: [User] } const code = await getFreeInviteCode(this.app) // if there is no user with userId, then we create a user and a identity provider. const adminCount = await this.app.service('user').Model.count({ include: [ { model: this.app.service('scope').Model, where: { type: 'admin:admin' } } ] }) const avatars = (await this.app .service('avatar') .find({ isInternal: true, query: { $limit: 1000 } })) as Paginated<AvatarInterface> let isGuest = type === 'guest' if (adminCount === 0) { // in dev mode make the first guest an admin // otherwise make the first logged in user an admin if (isDev || !isGuest) { type = 'admin' } } let result try { result = await super.create( { ...data, ...identityProvider, user: { id: userId, isGuest, inviteCode: type === 'guest' ? null : code, avatarId: avatars.data[random(avatars.data.length - 1)].id } }, params ) } catch (err) { console.error(err) await this.app.service('user').remove(userId) throw err } // DRC if (type === 'guest') { if (appConfig.scopes.guest.length) { const data = appConfig.scopes.guest.map((el) => { return { type: el, userId } }) await this.app.service('scope').create(data) } result.accessToken = await this.app .service('authentication') .createAccessToken({}, { subject: result.id.toString() }) } else if (isDev && type === 'admin') { // in dev mode, add all scopes to the first user made an admin const data = scopeTypeSeed.templates.map(({ type }) => { return { userId, type } }) await this.app.service('scope').create(data) result.accessToken = await this.app .service('authentication') .createAccessToken({}, { subject: result.id.toString() }) } return result } async find(params?: UserParams): Promise<T[] | Paginated<T>> { const loggedInUser = params!.user as UserInterface if (params!.provider) params!.query!.userId = loggedInUser.id return super.find(params) } }