@cloudbase/node-sdk
Version:
tencent cloud base server sdk for node.js
241 lines (212 loc) • 6.24 kB
text/typescript
import jwt from 'jsonwebtoken'
import { E } from '../utils/utils'
import { ERROR } from '../const/code'
import { CloudBase } from '../cloudbase'
import { SYMBOL_CURRENT_ENV, SYMBOL_DEFAULT_ENV } from '../const/symbol'
import * as tcbapicaller from '../utils/tcbapirequester'
import * as tcbopenapicommonrequester from '../utils/tcbopenapicommonrequester'
import {
IContextParam,
ICustomReqOpts, IUserInfoQuery, IGetAuthContextResult, IGetUserInfoResult, IGetEndUserInfoResult
} from '../../types'
const checkCustomUserIdRegex = /^[a-zA-Z0-9_\-#@~=*(){}[\]:.,<>+]{4,32}$/
function validateUid(uid: string): void {
if (typeof uid !== 'string') {
throw E({ ...ERROR.INVALID_PARAM, message: 'uid must be a string' })
}
if (!checkCustomUserIdRegex.test(uid)) {
throw E({ ...ERROR.INVALID_PARAM, message: `Invalid uid: "${uid}"` })
}
}
export class Auth {
private readonly cloudbase: CloudBase
public constructor(cloudbase: CloudBase) {
this.cloudbase = cloudbase
}
public async getAuthContext(context: IContextParam): Promise<IGetAuthContextResult> {
const {
TCB_UUID, LOGINTYPE, QQ_OPENID, QQ_APPID
} = CloudBase.getCloudbaseContext(context)
const result: any = {
uid: TCB_UUID,
loginType: LOGINTYPE
}
if (LOGINTYPE === 'QQ-MINI') {
result.appId = QQ_APPID
result.openId = QQ_OPENID
}
return result
}
public getClientIP(): string {
const { TCB_SOURCE_IP } = CloudBase.getCloudbaseContext()
return TCB_SOURCE_IP || ''
}
public getUserInfo(): IGetUserInfoResult {
const {
WX_OPENID,
WX_APPID,
TCB_UUID,
TCB_CUSTOM_USER_ID,
TCB_ISANONYMOUS_USER
} = CloudBase.getCloudbaseContext()
return {
openId: WX_OPENID || '',
appId: WX_APPID || '',
uid: TCB_UUID || '',
customUserId: TCB_CUSTOM_USER_ID || '',
isAnonymous: TCB_ISANONYMOUS_USER === 'true'
}
}
public async getEndUserInfo(uid?: string, opts?: ICustomReqOpts): Promise<IGetEndUserInfoResult> {
const {
WX_OPENID,
WX_APPID,
TCB_UUID,
TCB_CUSTOM_USER_ID,
TCB_ISANONYMOUS_USER
} = CloudBase.getCloudbaseContext()
const defaultUserInfo = {
openId: WX_OPENID || '',
appId: WX_APPID || '',
uid: TCB_UUID || '',
customUserId: TCB_CUSTOM_USER_ID || '',
isAnonymous: TCB_ISANONYMOUS_USER === 'true'
}
if (uid === undefined) {
return await Promise.resolve({
userInfo: defaultUserInfo
})
}
validateUid(uid)
return await tcbapicaller.request({
config: this.cloudbase.config,
params: {
action: 'auth.getUserInfoForAdmin',
uuid: uid
},
method: 'post',
opts,
headers: {
'content-type': 'application/json'
}
}).then(result => {
if (result.code) {
return result
}
return {
userInfo: {
...defaultUserInfo,
...result.data
},
requestId: result.requestId
}
})
}
public async queryUserInfo(query: IUserInfoQuery, opts?: ICustomReqOpts): Promise<any> {
const { uid, platform, platformId } = query
return await tcbapicaller.request({
config: this.cloudbase.config,
params: {
action: 'auth.getUserInfoForAdmin',
uuid: uid,
platform,
platformId
},
method: 'post',
opts,
headers: {
'content-type': 'application/json'
}
}).then(result => {
if (result.code) {
return result
}
return {
userInfo: {
...result.data
},
requestId: result.requestId
}
})
}
public async getClientCredential(opts?: ICustomReqOpts): Promise<any> {
// 如果有 accessKey 直接返回 accessKey,不用再去换取 token
if (this.cloudbase.config.accessKey) {
return this.cloudbase.config.accessKey
}
return await tcbopenapicommonrequester.request({
config: this.cloudbase.config,
method: 'POST',
opts,
headers: {
'content-type': 'application/json'
},
path: '/auth/v1/token/clientCredential',
data: {
grant_type: 'client_credentials'
}
}).then(result => {
return result.body
})
}
public createTicket(uid: string, options: any = {}): string {
validateUid(uid)
const timestamp = new Date().getTime()
const { TCB_ENV, SCF_NAMESPACE } = CloudBase.getCloudbaseContext()
const { credentials } = this.cloudbase.config
/* eslint-disable-next-line */
const { env_id } = credentials
let { envName } = this.cloudbase.config
if (!envName) {
throw E({ ...ERROR.INVALID_PARAM, message: 'no env in config' })
}
// 检查 credentials 是否包含 env
if (!env_id) {
throw E({
...ERROR.INVALID_PARAM,
message:
'当前私钥未包含env_id 信息, 请前往腾讯云云开发控制台,获取自定义登录最新私钥'
})
}
// 使用symbol时替换为环境变量内的env
if (envName === SYMBOL_CURRENT_ENV) {
envName = TCB_ENV || SCF_NAMESPACE
} else if (envName === SYMBOL_DEFAULT_ENV) {
// nothing to do
}
// 检查 credentials env 和 init 指定 env 是否一致
if (env_id && env_id !== envName) {
throw E({
...ERROR.INVALID_PARAM,
message: '当前私钥所属环境与 init 指定环境不一致!'
})
}
if (!Reflect.has(options, 'allowInsecureKeySizes')) {
options.allowInsecureKeySizes = true
}
const {
refresh = 3600 * 1000,
expire = timestamp + 7 * 24 * 60 * 60 * 1000
} = options
const token = jwt.sign(
{
alg: 'RS256',
env: envName,
iat: timestamp,
exp: timestamp + 10 * 60 * 1000, // ticket十分钟有效
uid,
refresh,
expire
},
credentials.private_key,
{
allowInsecureKeySizes: options.allowInsecureKeySizes === true,
algorithm: 'RS256'
}
)
return credentials.private_key_id + '/@@/' + token
}
}
export function auth(cloudbase: CloudBase): Auth {
return new Auth(cloudbase)
}