Skip to content

Commit

Permalink
feat: 🎸 update passport
Browse files Browse the repository at this point in the history
  • Loading branch information
divisey committed Mar 27, 2023
1 parent d1be9e9 commit b39dfff
Show file tree
Hide file tree
Showing 13 changed files with 216 additions and 92 deletions.
11 changes: 2 additions & 9 deletions packages/mvm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,10 @@ export default class MVM extends EventEmitter {
}

public async getAssets() {
const tokens = await bridge.getTokenList(this.account);

const tokens = (await bridge.getTokenList(this.account)) || [];
const assets = await Promise.all(
tokens?.map(async (token) => {
const assetId = await this.contractOpt?.getAssetIdByContractAddress(
token.contractAddress
);

if (!assetId) return null;

const asset = await this.cache.getAsset(assetId);
const asset = await this.cache.getAsset(token.mixinAssetId);
const balance = fmtBalance(token.balance);

return { ...asset, balance };
Expand Down
1 change: 1 addition & 0 deletions packages/mvm/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ export interface Token {
name: string;
symbol: string;
type: string;
mixinAssetId: string;
}
2 changes: 0 additions & 2 deletions packages/passport/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
"types": "index.d.ts",
"dependencies": {
"@foxone/fennec-dapp": "^0.5.4",
"@foxone/mixin-api": "0.1.30",
"@foxone/mvm": "0.1.30",
"@foxone/utils": "^0.2.6"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/passport/src/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { isMVM, State } from "./index";
export default function (state: State) {
return async (id: string) => {
if (state.channel === "mixin") {
return await state.mixin.endpoints.getAsset(id);
return await state.mixin.getAsset(id);
}

if (state.channel === "fennec") {
return await state.fennec.ctx?.wallet.getAsset(id);
}

if (isMVM(state.channel)) {
return await state.mvm.getAsset(id);
return await state.mvm?.getAsset(id);
}

return null;
Expand Down
4 changes: 2 additions & 2 deletions packages/passport/src/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { isMVM, State } from "./index";
export default function (state: State) {
return async () => {
if (state.channel === "mixin") {
return await state.mixin.endpoints.getAssets();
return await state.mixin.getAssets();
}

if (state.channel === "fennec") {
return (await state.fennec.ctx?.wallet.getAssets()) ?? [];
}

if (isMVM(state.channel)) {
return await state.mvm.getAssets();
return await state.mvm?.getAssets();
}

return [];
Expand Down
111 changes: 79 additions & 32 deletions packages/passport/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,58 +11,105 @@ export default function (
) {
const connectFennec = async () => {
await state.fennec.connect(options.origin);
state.token =

const mixinToken =
(await state.fennec.ctx?.wallet.signToken({
payload: options.JWTPayload || {}
})) ?? "";
})) || "";

if (options.customizeToken) {
const resp = await options.hooks?.onDistributeToken?.({
token: mixinToken,
type: "mixin_token"
});

state.token = resp?.token ?? "";
} else {
state.token = mixinToken;
}
};

const connectMVM = async (type, reject) => {
await state.mvm.connenct(type);
await state.mvm?.connenct(type);

let params: SignMessageParams = {};
if (options.signMessage) {
let params: SignMessageParams = {};

if (options.beforeSignMessage) {
params = await options.beforeSignMessage();
}
if (options.hooks?.beforeSignMessage) {
params = await options.hooks.beforeSignMessage();
}

let resp: any = await state.mvm.signMessage(params);
const signedData: any = await state.mvm?.signMessage(params);

if (options.afterSignMessage) {
resp = await options.afterSignMessage(resp);
if (options.hooks?.onDistributeToken) {
const resp = await options.hooks?.onDistributeToken?.({
message: signedData?.message ?? "",
signature: signedData?.signature ?? "",
type: "signed_message"
});

state.token = resp?.token ?? "";
} else {
reject(
"Need onDistributeToken hook to process signed message to token"
);
}
} else {
reject("Need afterSignMessage hook to process signed message to token");
const mixinToken = state.mvm?.getAuthToken() ?? "";

if (options.customizeToken) {
const resp = await options.hooks?.onDistributeToken?.({
token: mixinToken,
type: "mixin_token"
});

state.token = resp?.token ?? "";
} else {
state.token = mixinToken;
}
}
};

return resp;
const connectMixin = async (data, reject) => {
if (data.token) {
const mixinToken = data.token;

if (options.customizeToken) {
const resp = await options.hooks?.onDistributeToken?.({
token: mixinToken,
type: "mixin_token"
});

state.token = resp?.token ?? "";
state.mixin_token = mixinToken;
} else {
state.token = mixinToken;
}
} else {
if (options.hooks?.onDistributeToken) {
const resp = await options.hooks.onDistributeToken({
code: data.code,
type: "mixin_code"
});

state.token = resp.token;
state.mixin_token = resp.mixin_token;
} else {
reject("Need onDistributeToken hook to process code to tokens");
}
}
};

const handleAuth = async (data, resolve, reject) => {
state.channel = data.type;

if (state.channel === "fennec") {
await connectFennec();
resolve({ channel: state.channel, token: state.token });
}

if (isMVM(state.channel)) {
await connectMVM(state.channel, reject);
resolve({ channel: state.channel, token: state.token });
}
if (state.channel === "fennec") await connectFennec();

if (state.channel === "mixin") {
if (data.token) {
state.token = data.token;
} else {
if (!options.getTokenByCode) {
return reject("No auth actions provided");
}
if (isMVM(state.channel)) await connectMVM(state.channel, reject);

state.token = await options.getTokenByCode(data.code);
}
if (state.channel === "mixin") await connectMixin(data, reject);

resolve({ channel: state.channel, token: state.token });
}
resolve({ channel: state.channel, token: state.token });
};

return (): Promise<AuthData> => {
Expand Down
5 changes: 5 additions & 0 deletions packages/passport/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export {};

declare global {
const MVM: any;
}
2 changes: 1 addition & 1 deletion packages/passport/src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function (state: State) {
symbol: string;
}) => {
if (isMVM(state.channel)) {
state.mvm.watchAsset(params);
state.mvm?.watchAsset(params);
}
}
};
Expand Down
89 changes: 63 additions & 26 deletions packages/passport/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { VueConstructor } from "vue/types/umd";

import Fennec from "@foxone/fennec-dapp";
import MixinAPI from "@foxone/mixin-api";
import MVM from "@foxone/mvm";
import MixinAPI from "./mixin-apis";
import createAuthAction from "./auth";
import createAssetsAction from "./assets";
import createAssetAction from "./asset";
Expand All @@ -21,17 +20,46 @@ export interface SignMessageParams {
resources?: Array<string>;
}

// ATTENTION: in order to get Mixin OAuth Token correnctly
// remember set pkce and scope correnctly in @foxone/UIKit options
export interface PassportOptions {
origin: string;
config: any;
JWTPayload?: any;
beforeSignMessage?: () => Promise<SignMessageParams>;
afterSignMessage?: (params: {
message: string;
signature: string;
}) => Promise<string>;
onDisconnect?: () => void;
getTokenByCode?: (code: string) => Promise<string>;
chainId?: string; // mvm chain select
infuraId?: string; // need by mvm walletconnect
JWTPayload?: any; // need by fennec signToken

// if customizeToken = false:
// mvm and fennec channel will return access token for https://api.mixin.one/me
// developer can save this token to access Mixin Messenger backend
// ATTENTION: /me token has a short expire time (about one day)
// token will be refreshed everytime sync function executed
// mixin oauth channel will return Mixin OAuth Token

// if customizeToken = true:
// developer should provide hooks for exchange token or auth code or signed message to customizeToken token
// developer should both token and mixin_token for Mixin OAuth in order to access mixin assets
// token will NOT be refershed in sync function
customizeToken: boolean;

// if signMessage = false
// mvm will use /me token as auth type

// if signMessage = true
// mvm connect will ask user to sign message
// developer should provider hooks to verfiy signature and distribute custom token
signMessage: boolean;

hooks: {
beforeSignMessage?: () => Promise<SignMessageParams>;
onDistributeToken?: (params: {
type: "mixin_token" | "signed_message" | "mixin_code";
code?: string;
token?: string;
message?: string;
signature?: string;
}) => Promise<{ token: string; mixin_token?: string }>;
afterDisconnect?: () => void;
};
}

export type Channel =
Expand All @@ -47,7 +75,7 @@ export type PassportMethods = {
fennec: Fennec;
getAssets: ReturnType<typeof createAssetsAction>;
getAsset: ReturnType<typeof createAssetAction>;
mvm: MVM;
mvm: any | null;
payment: ReturnType<typeof createPaymentAction>;
sync: ReturnType<typeof createSyncAction>;
helper: ReturnType<typeof createHelper>;
Expand All @@ -57,7 +85,8 @@ export type State = {
channel: Channel;
fennec: Fennec;
mixin: MixinAPI;
mvm: MVM;
mvm: any | null;
mixin_token?: string;
token: string;
};

Expand All @@ -80,22 +109,30 @@ function install(Vue: VueConstructor, options: PassportOptions) {
channel: "",
fennec: new Fennec(),
mixin: new MixinAPI(),
mvm: new MVM(options.config),
mixin_token: "",
mvm: null,
token: ""
};

state.mixin.provider.instance.interceptors.request.use((config) => {
config.headers = {
...config.headers,
Authorization: `Bearer ${state.token}`
};

return config;
});

state.mvm.on("disconnect", () => {
options.onDisconnect?.();
});
state.mixin.use((configs) => ({
...configs,
headers: {
...configs.headers,
Authorization: `Bearer ${
options.customizeToken ? state.mixin_token : state.token
}`
}
}));

if (typeof MVM !== "undefined") {
state.mvm = new MVM({
chainId: options.chainId,
infuraId: options.infuraId
});
state.mvm.on("disconnect", () => {
options.hooks.afterDisconnect?.();
});
}

Vue.prototype.$passport = {
auth: createAuthAction(Vue, options, state),
Expand Down
44 changes: 44 additions & 0 deletions packages/passport/src/mixin-apis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
export default class MixinAPI {
token = "";

baseUrl = "https://api.mixin.one";

intercepts: any[] = [];

use(intercept) {
this.intercepts.push(intercept);
}

async request(url: string, method = "GET") {
let options = {
headers: {},
method
};

options = this.intercepts.reduce(
(merged, intercept) => intercept(merged),
options
);

const resp = await fetch(this.baseUrl + url, options);
const json = await resp.json();

return json.data;
}

getAsset(id: string) {
return this.request(`/asset?id=${id}`);
}

getAssets() {
return this.request("/assets");
}

codes(code: string) {
return this.request(`/codes/${code}`);
}

getProfile() {
return this.request("/me");
}
}

0 comments on commit b39dfff

Please sign in to comment.