diff --git a/src/controllers/facebook.controller.ts b/src/controllers/facebook.controller.ts new file mode 100644 index 0000000..a790781 --- /dev/null +++ b/src/controllers/facebook.controller.ts @@ -0,0 +1,45 @@ +import BaseController from 'common/base.controller' +import {ZError} from 'common/ZError' +import { role, router } from 'decorators/router' +import {Account, PlatEnum} from 'modules/Account'; +import {FACEBOOK_APP_ID, fetchUserInfo, verifyFbUserAccessToken} from 'providers/facebook.provider'; + +class FacebookController extends BaseController { + @role('anon') + @router('post /wallet/login/facebook') + async checkFacebookJwt(req, res) { + let { code } = req.params; + if (!code) { + throw new ZError(10, 'params mismatch'); + } + const result = await verifyFbUserAccessToken(code); + if (!!result.error) { + throw new ZError(10, `${result.error?.message} (${result.error?.code})`) + } + const { data } = result; + if (!data) { + throw new ZError(11, 'no data from facebook'); + } + if (data.app_id !== FACEBOOK_APP_ID) { + throw new ZError(12, 'app id mismatch'); + } + if (!data.is_valid) { + throw new ZError(13, 'access_token not valid'); + } + const infoRes = await fetchUserInfo(code); + if (!!infoRes.error) { + throw new ZError(13, `${infoRes.error?.message} (${infoRes.error.code})`) + } + const openId = infoRes.id || data.user_id; + let user: any = {} + let now = Date.now() / 1000 + user.accessToken = code + user.accessTokenExpire = result.data['expires_at'] + user.scope = data['scopes'] + user.nickname = infoRes['name'] + user.email = infoRes['email'] + let account = await Account.insertOrUpdate({ plat: PlatEnum.FACEBOOK, openId }, user) + const ztoken = await res.jwtSign({ id: account.id }) + return { token: ztoken } + } +} diff --git a/src/modules/Account.ts b/src/modules/Account.ts index cda7bab..a2d549b 100644 --- a/src/modules/Account.ts +++ b/src/modules/Account.ts @@ -1,4 +1,4 @@ -import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose' +import { getModelForClass, index, modelOptions, mongoose, prop, Severity } from '@typegoose/typegoose' import { dbconn } from 'decorators/dbconn' import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses' import { BaseModule } from './Base' @@ -7,12 +7,14 @@ export enum PlatEnum { GOOGLE = 0, APPLE = 1, TIKTOK = 2, + FACEBOOK = 3, + TWITTER = 4, } interface AccountClass extends Base, TimeStamps {} @dbconn() @index({ plat: 1, openId: 1 }, { unique: true }) -@modelOptions({ schemaOptions: { collection: 'account', timestamps: true } }) +@modelOptions({ schemaOptions: { collection: 'account', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) class AccountClass extends BaseModule { @prop({ enum: PlatEnum, default: PlatEnum.GOOGLE }) public plat!: PlatEnum @@ -46,8 +48,8 @@ class AccountClass extends BaseModule { public refreshToken?: string @prop() public refreshTokenExpire?: number - @prop() - public scope?: string + @prop({type: mongoose.Schema.Types.Mixed}) + public scope?: any } export const Account = getModelForClass(AccountClass, { existingConnection: AccountClass.db }) diff --git a/src/providers/facebook.provider.ts b/src/providers/facebook.provider.ts new file mode 100644 index 0000000..259ca1b --- /dev/null +++ b/src/providers/facebook.provider.ts @@ -0,0 +1,19 @@ +import {NetClient} from "net/NetClient"; + +const FACEBOOK_API_HOST = 'https://graph.facebook.com' +export const FACEBOOK_APP_ID = '1204701000119770'; +const FACEBOOK_APP_SECRET = '5a1deba64b30c7326f497fc52691207f'; + +export async function getAppAccessToken() { + const url = `${FACEBOOK_API_HOST}/oauth/access_token?client_id=${FACEBOOK_APP_ID}&clent_secret=${FACEBOOK_APP_SECRET}&grant_type=client_credentials`; + return new NetClient().httpGet(url); +} +export async function verifyFbUserAccessToken(accessToken: string){ + const url = `${FACEBOOK_API_HOST}/debug_token?input_token=${accessToken}&access_token=GG|${FACEBOOK_APP_ID}|${FACEBOOK_APP_SECRET}`; + return new NetClient().httpGet(url); +} + +export async function fetchUserInfo(accessToken: string) { + const url = `${FACEBOOK_API_HOST}/me?fields=["email","id", "name"]&access_token=${accessToken}`; + return new NetClient().httpGet(url); +}