重构代码, 优化登录逻辑, 增加帐号绑定流程
This commit is contained in:
parent
15bb27eefa
commit
b6d58c4f5b
@ -45,4 +45,7 @@ HASH_SALT='iG4Rpsa)6U31$H#^T85$^^3'
|
|||||||
GAME_PAY_CB_URL=https://game2006api-test.kingsome.cn/webapp/index.php?c=Shop&a=buyGoodsDirect
|
GAME_PAY_CB_URL=https://game2006api-test.kingsome.cn/webapp/index.php?c=Shop&a=buyGoodsDirect
|
||||||
|
|
||||||
# client登录时,验证用户数据的private key
|
# client登录时,验证用户数据的private key
|
||||||
WALLET_CLIENT_SK='38d9baa24aaea6f87a1caa51f588b0c9578368a1cb00b1639eb9f450b6cada00'
|
WALLET_CLIENT_SK='38d9baa24aaea6f87a1caa51f588b0c9578368a1cb00b1639eb9f450b6cada00'
|
||||||
|
|
||||||
|
# 检查guest能否绑定平台账号
|
||||||
|
GAME_CHECK_RELATION_URL='https://game2006api-test.kingsome.cn/webapp/index.php?c=AccountVerify&a=canBind'
|
@ -1,9 +1,10 @@
|
|||||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||||
import { role, router } from 'decorators/router'
|
import { role, router } from 'decorators/router'
|
||||||
import { Account, PlatEnum } from 'modules/Account'
|
import { Account } from 'modules/Account'
|
||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
import { PlatApple } from 'plats/PlatApple'
|
import { PlatApple } from 'plats/PlatApple'
|
||||||
import { IPlat } from 'plats/IPlat'
|
import { IPlat } from 'plats/IPlat'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
|
|
||||||
const plat: IPlat = new PlatApple()
|
const plat: IPlat = new PlatApple()
|
||||||
class AppleController extends BaseController {
|
class AppleController extends BaseController {
|
||||||
@ -16,9 +17,13 @@ class AppleController extends BaseController {
|
|||||||
|
|
||||||
@role(ROLE_ANON)
|
@role(ROLE_ANON)
|
||||||
@router('post /wallet/login/apple')
|
@router('post /wallet/login/apple')
|
||||||
async checkGoogleJwt(req, res) {
|
async checkAppleJwt(req, res) {
|
||||||
logger.db('login', req)
|
logger.db('login', req)
|
||||||
const { openId, data } = await plat.verifyToken(req)
|
const { openId, data } = await plat.verifyToken(req)
|
||||||
|
const { api_platform } = req.headers
|
||||||
|
if (api_platform) {
|
||||||
|
data.platform = api_platform
|
||||||
|
}
|
||||||
let user = await Account.insertOrUpdate({ plat: PlatEnum.APPLE, openId }, data)
|
let user = await Account.insertOrUpdate({ plat: PlatEnum.APPLE, openId }, data)
|
||||||
const ztoken = await res.jwtSign({
|
const ztoken = await res.jwtSign({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
|
@ -1,41 +1,29 @@
|
|||||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||||
import { ZError } from 'common/ZError'
|
import { ZError } from 'common/ZError'
|
||||||
import { role, router } from 'decorators/router'
|
import { role, router } from 'decorators/router'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
import { Account, PlatEnum } from 'modules/Account'
|
import { Account } from 'modules/Account'
|
||||||
import * as wasm from 'rustwallet'
|
import { IPlat } from 'plats/IPlat'
|
||||||
import { isUUID } from 'utils/string.util'
|
import { PlatClient } from 'plats/PlatClient'
|
||||||
|
|
||||||
const CLIENT_SUFFIX = '_clientid'
|
const plat: IPlat = new PlatClient()
|
||||||
|
|
||||||
function checkClientId(clientId: string) {
|
|
||||||
if (!clientId) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!clientId.endsWith(CLIENT_SUFFIX)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
const id = clientId.slice(0, clientId.length - CLIENT_SUFFIX.length)
|
|
||||||
return isUUID(id)
|
|
||||||
}
|
|
||||||
class ClientController extends BaseController {
|
class ClientController extends BaseController {
|
||||||
@role(ROLE_ANON)
|
@role(ROLE_ANON)
|
||||||
@router('post /wallet/login/client')
|
@router('post /wallet/login/client')
|
||||||
async clientLogin(req, res) {
|
async clientLogin(req, res) {
|
||||||
const { code } = req.params
|
const { code } = req.params
|
||||||
const { api_platform } = req.headers
|
|
||||||
logger.db('login', req)
|
logger.db('login', req)
|
||||||
if (!code) {
|
if (!code) {
|
||||||
throw new ZError(11, 'param missing')
|
throw new ZError(11, 'param missing')
|
||||||
}
|
}
|
||||||
const sk = process.env.WALLET_CLIENT_SK
|
const { openId, data } = await plat.verifyToken(req)
|
||||||
let codeDecrypto = wasm.rdecrypt(sk, code)
|
const { api_platform } = req.headers
|
||||||
if (!checkClientId(codeDecrypto)) {
|
if (api_platform) {
|
||||||
throw new ZError(12, 'param invalid')
|
data.platform = api_platform
|
||||||
}
|
}
|
||||||
const openId = codeDecrypto.slice(0, codeDecrypto.length - CLIENT_SUFFIX.length)
|
|
||||||
logger.info('clientLogin', openId)
|
logger.info('clientLogin', openId)
|
||||||
let user = await Account.insertOrUpdate({ plat: PlatEnum.CLIENT, openId }, { platform: api_platform })
|
let user = await Account.insertOrUpdate({ plat: PlatEnum.CLIENT, openId }, data)
|
||||||
const ztoken = await res.jwtSign({
|
const ztoken = await res.jwtSign({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
openid: user.openId,
|
openid: user.openId,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||||
import { ZError } from 'common/ZError'
|
import { ZError } from 'common/ZError'
|
||||||
import { role, router } from 'decorators/router'
|
import { role, router } from 'decorators/router'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
import { Account, PlatEnum } from 'modules/Account'
|
import { Account } from 'modules/Account'
|
||||||
import { IPlat } from 'plats/IPlat'
|
import { IPlat } from 'plats/IPlat'
|
||||||
import { PlatFacebook } from 'plats/PlatFacebook'
|
import { PlatFacebook } from 'plats/PlatFacebook'
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||||
import { role, router } from 'decorators/router'
|
import { role, router } from 'decorators/router'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
|
|
||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
import { Account, PlatEnum } from 'modules/Account'
|
import { Account } from 'modules/Account'
|
||||||
import { IPlat } from 'plats/IPlat'
|
import { IPlat } from 'plats/IPlat'
|
||||||
import { PlatGoogle } from 'plats/PlatGoogle'
|
import { PlatGoogle } from 'plats/PlatGoogle'
|
||||||
|
|
||||||
|
@ -1,42 +1,120 @@
|
|||||||
import { ZError } from 'common/ZError'
|
import { ZError } from 'common/ZError'
|
||||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||||
import { role, router } from 'decorators/router'
|
import { role, router } from 'decorators/router'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
import { Account, PlatEnum } from 'modules/Account'
|
import { Account } from 'modules/Account'
|
||||||
|
import { UnionAccount } from 'modules/UnionAccount'
|
||||||
|
import { Wallet } from 'modules/Wallet'
|
||||||
import { IPlat } from 'plats/IPlat'
|
import { IPlat } from 'plats/IPlat'
|
||||||
import { PlatApple } from 'plats/PlatApple'
|
import { PlatApple } from 'plats/PlatApple'
|
||||||
|
import { PlatClient } from 'plats/PlatClient'
|
||||||
import { PlatFacebook } from 'plats/PlatFacebook'
|
import { PlatFacebook } from 'plats/PlatFacebook'
|
||||||
import { PlatGoogle } from 'plats/PlatGoogle'
|
import { PlatGoogle } from 'plats/PlatGoogle'
|
||||||
import { PlatTikTok } from 'plats/PlatTikTok'
|
import { PlatTikTok } from 'plats/PlatTikTok'
|
||||||
|
import { checkReleation } from 'service/game.svr'
|
||||||
|
|
||||||
const plats: Map<PlatEnum, IPlat> = new Map([
|
const plats: Map<PlatEnum, IPlat> = new Map([
|
||||||
[PlatEnum.GOOGLE, new PlatGoogle()],
|
[PlatEnum.GOOGLE, new PlatGoogle()],
|
||||||
[PlatEnum.APPLE, new PlatApple()],
|
[PlatEnum.APPLE, new PlatApple()],
|
||||||
[PlatEnum.FACEBOOK, new PlatFacebook()],
|
[PlatEnum.FACEBOOK, new PlatFacebook()],
|
||||||
[PlatEnum.TIKTOK, new PlatTikTok()],
|
[PlatEnum.TIKTOK, new PlatTikTok()],
|
||||||
|
[PlatEnum.CLIENT, new PlatClient()],
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// 如果客户端有传入account, 则说明该次登录是绑定账号
|
||||||
|
// 首先查找该账号是否已经绑定了其他账号
|
||||||
|
const parseBindAccount = async (account: string, channel: PlatEnum, user: any) => {
|
||||||
|
const uid = user.id
|
||||||
|
let unionAccount
|
||||||
|
const filterData: any = {}
|
||||||
|
filterData[`plats.${channel}`] = uid
|
||||||
|
if (account) {
|
||||||
|
// TODO:: check from game svr, verify account and check if plat account could bind
|
||||||
|
let checkResult: any = await checkReleation(account, channel, user.openId)
|
||||||
|
console.log(checkResult)
|
||||||
|
if (checkResult.errcode) {
|
||||||
|
throw new ZError(30, checkResult.errmsg)
|
||||||
|
}
|
||||||
|
unionAccount = await UnionAccount.findOne({ gameAccount: account })
|
||||||
|
if (unionAccount) {
|
||||||
|
let platInfo = unionAccount.plats.get(channel + '')
|
||||||
|
// 如果已经绑定, 且绑定的相同平台下不同的账号, 则抛出异常
|
||||||
|
// 如果未绑定, 那么查找平台账号是否已经绑定了其他账号
|
||||||
|
if (platInfo && platInfo !== uid) {
|
||||||
|
throw new ZError(21, 'account already bind')
|
||||||
|
} else if (!platInfo) {
|
||||||
|
// 检查pid是否已经绑定了其他账号
|
||||||
|
let unionAccount2 = await UnionAccount.findOne(filterData)
|
||||||
|
// 如果记录不存在, 那么将平台账号绑定至当前unionAccount
|
||||||
|
if (!unionAccount2) {
|
||||||
|
// 如果当前unionAccount已经设置了钱包账号, 且钱包账号不是当前账号, 那么检查当前账号是否开启了钱包
|
||||||
|
// 如果开启了钱包, 那么就不允许绑定
|
||||||
|
if (unionAccount.walletAccount && unionAccount.walletAccount !== uid) {
|
||||||
|
const wallet = await Wallet.findByAccount(uid)
|
||||||
|
if (wallet && wallet.address) {
|
||||||
|
throw new ZError(23, 'plat account already had wallet')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unionAccount.plats.set(channel + '', uid)
|
||||||
|
unionAccount.markModified('plats')
|
||||||
|
} else if (unionAccount2.gameAccount && unionAccount2.gameAccount === account) {
|
||||||
|
// 这种情况不用处理, 理论上是不可能出现的
|
||||||
|
} else {
|
||||||
|
throw new ZError(22, 'plat account already bind')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unionAccount = await UnionAccount.insertOrUpdate(filterData, {})
|
||||||
|
if (unionAccount.gameAccount && unionAccount.gameAccount !== account) {
|
||||||
|
throw new ZError(22, 'plat account already bind')
|
||||||
|
}
|
||||||
|
unionAccount.gameAccount = account
|
||||||
|
}
|
||||||
|
await unionAccount.save()
|
||||||
|
} else {
|
||||||
|
unionAccount = await UnionAccount.findOne(filterData)
|
||||||
|
}
|
||||||
|
let walletUser
|
||||||
|
// 如果统一账号存在, 但钱包账号不存在,那么就把当前绑定账号的信息写入钱包账号
|
||||||
|
if (unionAccount && !unionAccount.walletAccount) {
|
||||||
|
unionAccount.walletAccount = uid
|
||||||
|
walletUser = user
|
||||||
|
await unionAccount.save()
|
||||||
|
} else if (unionAccount && unionAccount.walletAccount) {
|
||||||
|
walletUser = await Account.findById(unionAccount.walletAccount)
|
||||||
|
} else {
|
||||||
|
walletUser = user
|
||||||
|
}
|
||||||
|
return { unionAccount, walletUser }
|
||||||
|
}
|
||||||
class LoginController extends BaseController {
|
class LoginController extends BaseController {
|
||||||
@role(ROLE_ANON)
|
@role(ROLE_ANON)
|
||||||
@router('post /wallet/login/general')
|
@router('post /wallet/login/general')
|
||||||
async generalLogin(req, res) {
|
async generalLogin(req, res) {
|
||||||
const { channel, account } = req.params
|
const { code, channel, account } = req.params
|
||||||
logger.db('login', req)
|
logger.db('login', req)
|
||||||
|
if (!code) {
|
||||||
|
throw new ZError(10, 'code not found')
|
||||||
|
}
|
||||||
const plat = plats.get(channel)
|
const plat = plats.get(channel)
|
||||||
if (!plat) {
|
if (!plat) {
|
||||||
throw new ZError(10, 'plat not found')
|
throw new ZError(11, 'plat not support')
|
||||||
}
|
}
|
||||||
const { openId, data } = await plat.verifyToken(req)
|
const { openId, data } = await plat.verifyToken(req)
|
||||||
const { api_platform } = req.headers
|
const { api_platform } = req.headers
|
||||||
if (api_platform) {
|
if (api_platform) {
|
||||||
data.platform = api_platform
|
data.platform = api_platform
|
||||||
}
|
}
|
||||||
let user = await Account.insertOrUpdate({ plat: channel, openId }, data)
|
const user = await Account.insertOrUpdate({ plat: channel, openId }, data)
|
||||||
|
const { unionAccount, walletUser } = await parseBindAccount(account, channel, user)
|
||||||
const ztoken = await res.jwtSign({
|
const ztoken = await res.jwtSign({
|
||||||
id: user.id,
|
id: walletUser.id,
|
||||||
openid: user.openId,
|
uid: unionAccount?.id || '',
|
||||||
version: user.accountVersion || 0,
|
gid: unionAccount?.gameAccount || '',
|
||||||
plat: user.plat,
|
openid: walletUser.openId,
|
||||||
|
version: walletUser.accountVersion || 0,
|
||||||
|
plat: walletUser.plat,
|
||||||
})
|
})
|
||||||
return { token: ztoken }
|
return { token: ztoken }
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||||
import { ZError } from 'common/ZError'
|
import { ZError } from 'common/ZError'
|
||||||
import { role, router } from 'decorators/router'
|
import { role, router } from 'decorators/router'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
import { Account, PlatEnum } from 'modules/Account'
|
import { Account } from 'modules/Account'
|
||||||
import { CodeRecord, CodeStatus, CodeType, DEFAULT_CODE, DEFAULT_EXPIRE_TIME } from 'modules/CodeRecord'
|
import { CodeRecord, CodeStatus, CodeType, DEFAULT_CODE, DEFAULT_EXPIRE_TIME } from 'modules/CodeRecord'
|
||||||
import {
|
import {
|
||||||
DEFAULT_REGIST_HTML,
|
DEFAULT_REGIST_HTML,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||||
import { ZError } from 'common/ZError'
|
import { ZError } from 'common/ZError'
|
||||||
import { role, router } from 'decorators/router'
|
import { role, router } from 'decorators/router'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
import { Account, PlatEnum } from 'modules/Account'
|
import { Account } from 'modules/Account'
|
||||||
import { IPlat } from 'plats/IPlat'
|
import { IPlat } from 'plats/IPlat'
|
||||||
import { PlatTikTok } from 'plats/PlatTikTok'
|
import { PlatTikTok } from 'plats/PlatTikTok'
|
||||||
import { fetchAccessToken, refreshAccessToken } from 'service/tiktok.svr'
|
import { fetchAccessToken, refreshAccessToken } from 'service/tiktok.svr'
|
||||||
|
11
src/enums/PlatEnum.ts
Normal file
11
src/enums/PlatEnum.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export enum PlatEnum {
|
||||||
|
GOOGLE = 0,
|
||||||
|
APPLE = 1,
|
||||||
|
TIKTOK = 2,
|
||||||
|
FACEBOOK = 3,
|
||||||
|
TWITTER = 4,
|
||||||
|
TELEGRAM = 5,
|
||||||
|
EMAIL = 6,
|
||||||
|
DISCORD = 7,
|
||||||
|
CLIENT = 10,
|
||||||
|
}
|
@ -3,18 +3,7 @@ import { dbconn } from 'decorators/dbconn'
|
|||||||
import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses'
|
import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses'
|
||||||
import { BaseModule } from './Base'
|
import { BaseModule } from './Base'
|
||||||
import { genRandomString, sha512 } from 'utils/security.util'
|
import { genRandomString, sha512 } from 'utils/security.util'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
export enum PlatEnum {
|
|
||||||
GOOGLE = 0,
|
|
||||||
APPLE = 1,
|
|
||||||
TIKTOK = 2,
|
|
||||||
FACEBOOK = 3,
|
|
||||||
TWITTER = 4,
|
|
||||||
TELEGRAM = 5,
|
|
||||||
EMAIL = 6,
|
|
||||||
DISCORD = 7,
|
|
||||||
CLIENT = 10,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成密码的salt和hash
|
* 生成密码的salt和hash
|
||||||
|
35
src/modules/UnionAccount.ts
Normal file
35
src/modules/UnionAccount.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { getModelForClass, index, modelOptions, mongoose, prop, ReturnModelType, Severity } from '@typegoose/typegoose'
|
||||||
|
import { dbconn } from 'decorators/dbconn'
|
||||||
|
import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses'
|
||||||
|
import { BaseModule } from './Base'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
|
/**
|
||||||
|
* 账号绑定表
|
||||||
|
*/
|
||||||
|
interface UnionAccountClass extends Base, TimeStamps {}
|
||||||
|
@dbconn()
|
||||||
|
@index({ gameAccount: 1 }, { unique: true, partialFilterExpression: { gameAccount: { $exists: true } } })
|
||||||
|
@index({ walletAccount: 1 }, { unique: true, partialFilterExpression: { walletAccount: { $exists: true } } })
|
||||||
|
@modelOptions({
|
||||||
|
schemaOptions: { collection: 'union_account', timestamps: true },
|
||||||
|
options: { allowMixed: Severity.ALLOW },
|
||||||
|
})
|
||||||
|
class UnionAccountClass extends BaseModule {
|
||||||
|
// 生成钱包所用账号
|
||||||
|
@prop()
|
||||||
|
public walletAccount?: string
|
||||||
|
// 绑定的guest账号
|
||||||
|
@prop()
|
||||||
|
public gameAccount?: string
|
||||||
|
|
||||||
|
@prop({ type: String })
|
||||||
|
public plats: Map<PlatEnum, string>
|
||||||
|
|
||||||
|
public static async findByPlat(this: ReturnModelType<typeof UnionAccountClass>, channel: PlatEnum, uid: string) {
|
||||||
|
const filterData: any = {}
|
||||||
|
filterData[`plats.${channel}`] = uid
|
||||||
|
return this.findOne(filterData).exec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UnionAccount = getModelForClass(UnionAccountClass, { existingConnection: UnionAccountClass.db })
|
@ -1,4 +1,4 @@
|
|||||||
import { getModelForClass, index, modelOptions, mongoose, prop, Severity } from '@typegoose/typegoose'
|
import { getModelForClass, index, modelOptions, mongoose, prop, ReturnModelType, Severity } from '@typegoose/typegoose'
|
||||||
import { dbconn } from 'decorators/dbconn'
|
import { dbconn } from 'decorators/dbconn'
|
||||||
import { BaseModule } from './Base'
|
import { BaseModule } from './Base'
|
||||||
|
|
||||||
@ -28,6 +28,10 @@ class WalletClass extends BaseModule {
|
|||||||
@prop({ required: true, default: true })
|
@prop({ required: true, default: true })
|
||||||
public nweRecord: boolean
|
public nweRecord: boolean
|
||||||
|
|
||||||
|
public static async findByAccount(this: ReturnModelType<typeof WalletClass>, account: string) {
|
||||||
|
return this.findOne({ account }).exec()
|
||||||
|
}
|
||||||
|
|
||||||
public toJson() {
|
public toJson() {
|
||||||
return {
|
return {
|
||||||
key: this.key,
|
key: this.key,
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { OAuth2Client } from 'google-auth-library'
|
|
||||||
import { IPlat } from './IPlat'
|
import { IPlat } from './IPlat'
|
||||||
import verifyAppleToken from 'verify-apple-id-token'
|
import verifyAppleToken from 'verify-apple-id-token'
|
||||||
import { ZError } from 'common/ZError'
|
|
||||||
|
|
||||||
const CLIENT_ID_DEBUG = 'com.jc.tebg'
|
const CLIENT_ID_DEBUG = 'com.jc.tebg'
|
||||||
const CLIENT_ID_RELEASE = 'com.cege.games.release'
|
const CLIENT_ID_RELEASE = 'com.cege.games.release'
|
||||||
|
31
src/plats/PlatClient.ts
Normal file
31
src/plats/PlatClient.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { IPlat } from './IPlat'
|
||||||
|
import verifyAppleToken from 'verify-apple-id-token'
|
||||||
|
import { ZError } from 'common/ZError'
|
||||||
|
import { isUUID } from 'utils/string.util'
|
||||||
|
import * as wasm from 'rustwallet'
|
||||||
|
|
||||||
|
const CLIENT_SUFFIX = '_clientid'
|
||||||
|
|
||||||
|
function checkClientId(clientId: string) {
|
||||||
|
if (!clientId) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!clientId.endsWith(CLIENT_SUFFIX)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const id = clientId.slice(0, clientId.length - CLIENT_SUFFIX.length)
|
||||||
|
return isUUID(id)
|
||||||
|
}
|
||||||
|
export class PlatClient implements IPlat {
|
||||||
|
async verifyToken(req: any): Promise<any> {
|
||||||
|
let { code, token } = req.params
|
||||||
|
code = code || token
|
||||||
|
const sk = process.env.WALLET_CLIENT_SK
|
||||||
|
let codeDecrypto = wasm.rdecrypt(sk, code)
|
||||||
|
if (!checkClientId(codeDecrypto)) {
|
||||||
|
throw new ZError(12, 'param invalid')
|
||||||
|
}
|
||||||
|
const openId = codeDecrypto.slice(0, codeDecrypto.length - CLIENT_SUFFIX.length)
|
||||||
|
return { openId, data: {} }
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import { OAuth2Client } from 'google-auth-library'
|
import { OAuth2Client } from 'google-auth-library'
|
||||||
import { IPlat } from './IPlat'
|
import { IPlat } from './IPlat'
|
||||||
import { ZError } from 'common/ZError'
|
import { ZError } from 'common/ZError'
|
||||||
|
import logger from 'logger/logger'
|
||||||
|
|
||||||
const GOOGLE_OAUTH_ISS = 'https://accounts.google.com'
|
const GOOGLE_OAUTH_ISS = 'https://accounts.google.com'
|
||||||
const GOOGLE_OAUTH_ISS1 = 'accounts.google.com'
|
const GOOGLE_OAUTH_ISS1 = 'accounts.google.com'
|
||||||
@ -13,34 +14,55 @@ export class PlatGoogle implements IPlat {
|
|||||||
async verifyToken(req: any): Promise<any> {
|
async verifyToken(req: any): Promise<any> {
|
||||||
let { code, token } = req.params
|
let { code, token } = req.params
|
||||||
code = code || token
|
code = code || token
|
||||||
const client = new OAuth2Client(CLIENT_ID)
|
|
||||||
const ticket = await client.verifyIdToken({
|
|
||||||
idToken: code,
|
|
||||||
audience: [CLIENT_ID, CLIENT_ID2, CLIENT_ID_IOS, IOS_TEST], // Specify the CLIENT_ID of the app that accesses the backend
|
|
||||||
// Or, if multiple clients access the backend:
|
|
||||||
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
|
|
||||||
})
|
|
||||||
const payload = ticket.getPayload()
|
|
||||||
if (!(payload.iss === GOOGLE_OAUTH_ISS || payload.iss === GOOGLE_OAUTH_ISS1)) {
|
|
||||||
throw new ZError(10, 'id token error')
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
payload.aud !== CLIENT_ID &&
|
|
||||||
payload.aud !== CLIENT_ID2 &&
|
|
||||||
payload.aud !== CLIENT_ID_IOS &&
|
|
||||||
payload.aud !== IOS_TEST
|
|
||||||
) {
|
|
||||||
throw new ZError(11, 'client id mismatch')
|
|
||||||
}
|
|
||||||
let data: any = {}
|
let data: any = {}
|
||||||
if (payload.email) data.email = payload.email
|
let openId
|
||||||
if (process.env.NODE_ENV !== 'development') {
|
const client = new OAuth2Client(CLIENT_ID)
|
||||||
if (payload.email_verified !== undefined) data.emailVerified = payload.email_verified
|
try {
|
||||||
|
const ticket = await client.verifyIdToken({
|
||||||
|
idToken: code,
|
||||||
|
audience: [CLIENT_ID, CLIENT_ID2, CLIENT_ID_IOS, IOS_TEST], // Specify the CLIENT_ID of the app that accesses the backend
|
||||||
|
// Or, if multiple clients access the backend:
|
||||||
|
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
|
||||||
|
})
|
||||||
|
const payload = ticket.getPayload()
|
||||||
|
if (!(payload.iss === GOOGLE_OAUTH_ISS || payload.iss === GOOGLE_OAUTH_ISS1)) {
|
||||||
|
throw new ZError(10, 'id token error')
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
payload.aud !== CLIENT_ID &&
|
||||||
|
payload.aud !== CLIENT_ID2 &&
|
||||||
|
payload.aud !== CLIENT_ID_IOS &&
|
||||||
|
payload.aud !== IOS_TEST
|
||||||
|
) {
|
||||||
|
throw new ZError(11, 'client id mismatch')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.email) data.email = payload.email
|
||||||
|
if (process.env.NODE_ENV !== 'development') {
|
||||||
|
if (payload.email_verified !== undefined) data.emailVerified = payload.email_verified
|
||||||
|
}
|
||||||
|
if (payload.locale) data.locale = payload.locale
|
||||||
|
if (payload.name) data.nickname = payload.name
|
||||||
|
if (payload.picture) data.avatar = payload.picture
|
||||||
|
openId = payload.sub
|
||||||
|
} catch (err) {
|
||||||
|
logger.log('error parse google id token', err)
|
||||||
|
try {
|
||||||
|
let info: any = await client.getTokenInfo(code)
|
||||||
|
console.log(info)
|
||||||
|
if (info.email) data.email = info.email
|
||||||
|
if (info.aud !== CLIENT_ID2) {
|
||||||
|
throw new ZError(11, 'client id mismatch')
|
||||||
|
}
|
||||||
|
if (process.env.NODE_ENV !== 'development') {
|
||||||
|
if (info.email_verified !== undefined) data.emailVerified = info.email_verified
|
||||||
|
}
|
||||||
|
if (info.name) data.nickname = info.name
|
||||||
|
openId = info.sub
|
||||||
|
} catch (e2) {
|
||||||
|
logger.log('error parse google access token', e2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (payload.locale) data.locale = payload.locale
|
|
||||||
if (payload.name) data.nickname = payload.name
|
|
||||||
if (payload.picture) data.avatar = payload.picture
|
|
||||||
const openId = payload.sub
|
|
||||||
return { openId, data }
|
return { openId, data }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import axios from 'axios'
|
|||||||
import { PayRecordClass } from 'modules/PayRecord'
|
import { PayRecordClass } from 'modules/PayRecord'
|
||||||
import { DocumentType } from '@typegoose/typegoose'
|
import { DocumentType } from '@typegoose/typegoose'
|
||||||
import { hmacsha256 } from 'utils/security.util'
|
import { hmacsha256 } from 'utils/security.util'
|
||||||
|
import { PlatEnum } from 'enums/PlatEnum'
|
||||||
|
|
||||||
export async function reportPayResult(data: DocumentType<PayRecordClass>) {
|
export async function reportPayResult(data: DocumentType<PayRecordClass>) {
|
||||||
let repData = {
|
let repData = {
|
||||||
@ -27,10 +28,10 @@ export async function reportPayResult(data: DocumentType<PayRecordClass>) {
|
|||||||
return axios(reqConfig)
|
return axios(reqConfig)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* TODO::向游戏服务查询guest账号和平台账号能否绑定
|
* 向游戏服务查询guest账号和平台账号能否绑定
|
||||||
*/
|
*/
|
||||||
export async function checkReleation() {
|
export async function checkReleation(gid: string, plat: PlatEnum, openId: string) {
|
||||||
let url = `${process.env.GAME_CHECK_RELATION_URL}`
|
let url = `${process.env.GAME_CHECK_RELATION_URL}&guest_account=${gid}&target_plat=${plat}&target_account=${openId}`
|
||||||
let reqConfig: any = {
|
let reqConfig: any = {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url,
|
url,
|
||||||
@ -38,5 +39,6 @@ export async function checkReleation() {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return axios(reqConfig)
|
let res = await axios(reqConfig)
|
||||||
|
return res.data
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user