Skip to content

Commit ce5ad4b

Browse files
authored
feat: integreate OKTA authentication (#216)
1 parent ef8392f commit ce5ad4b

File tree

11 files changed

+458
-118
lines changed

11 files changed

+458
-118
lines changed

cli-typescript/package-lock.json

Lines changed: 111 additions & 60 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli-typescript/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"devDependencies": {
2727
"@esbuild-kit/esm-loader": "^2.6.5",
2828
"@types/eslint-plugin-prettier": "^3.1.3",
29+
"@types/express": "^5.0.2",
2930
"@types/node": "^22.13.16",
3031
"eslint": "^9.24.0",
3132
"eslint-config-prettier": "^10.1.1",
@@ -41,6 +42,8 @@
4142
"commander": "^13.1.0",
4243
"dotenv": "^16.4.7",
4344
"ethers": "^6.13.5",
45+
"express": "^5.1.0",
46+
"mkdirp": "^3.0.1",
4447
"ox": "^0.7.0",
4548
"viem": "^2.26.3"
4649
}

cli-typescript/src/cmds/createCommand.ts

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
import listProjectsAction from '../utils/cmdActions/listProjectsAction';
4242
import { getProjectStore } from '../utils/fileUtils';
4343
import fillProjectConfigAction from '../utils/cmdActions/fillProjectConfigAction';
44+
import { authenticate } from '../utils/auth';
4445

4546
export const getNewProjectCmdDescription = (defaultInfo?: string) => {
4647
defaultInfo =
@@ -55,10 +56,16 @@ export const getNewProjectCmdDescription = (defaultInfo?: string) => {
5556
`;
5657
};
5758

58-
const presets = async () => {
59+
const presets = async (cliCmd: string) => {
5960
try {
6061
console.log('Starting prestart tasks...');
6162

63+
console.log('Authenticating...');
64+
await authenticate();
65+
66+
// set cmd name globally
67+
process.env.MAGICDROP_CLI_CMD = cliCmd;
68+
6269
setBaseDir();
6370
} catch (error: any) {
6471
showError({ text: `An error occurred: ${error.message}` });
@@ -80,14 +87,32 @@ export const createEvmCommand = ({
8087
.description(`${platform.name} launchpad commands`)
8188
.aliases(commandAliases);
8289

83-
newCmd.hook('preAction', async () => {
90+
newCmd.hook('preAction', async (_, actionCommand) => {
8491
try {
85-
await presets();
92+
await presets(actionCommand.name());
8693
} catch (error: any) {
8794
showError({ text: `setup failed - ${error.message}` });
8895
}
8996
});
9097

98+
// subcommand hook; verify if the collection is supported on the platform
99+
newCmd.hook('preAction', (_, actionCommand) => {
100+
const symbol = actionCommand.args[0];
101+
102+
if (!SUBCOMMAND_EXCLUDE_LIST.includes(actionCommand.name()) && !!symbol) {
103+
const store = getProjectStore(symbol);
104+
store.read();
105+
106+
if (!platform.isChainIdSupported(store.data?.chainId ?? 0)) {
107+
showError({
108+
text: `collection '${symbol}' not supported on the ${platform.name} platform.`,
109+
});
110+
111+
process.exit(1);
112+
}
113+
}
114+
});
115+
91116
newCmd
92117
.command('new <symbol>')
93118
.aliases(['n', 'init'])
@@ -160,23 +185,6 @@ export const createEvmCommand = ({
160185
) => await deployAction(platform, symbol, params),
161186
);
162187

163-
// subcommand hook; verify if the collection is supported on the platform
164-
newCmd.hook('preAction', (_, actionCommand) => {
165-
const symbol = actionCommand.args[0];
166-
if (!SUBCOMMAND_EXCLUDE_LIST.includes(actionCommand.name()) && !!symbol) {
167-
const store = getProjectStore(symbol);
168-
store.read();
169-
170-
if (!platform.isChainIdSupported(store.data?.chainId ?? 0)) {
171-
showError({
172-
text: `collection '${symbol}' not supported on the ${platform.name} platform.`,
173-
});
174-
175-
process.exit(1);
176-
}
177-
}
178-
});
179-
180188
newCmd
181189
.command('configure-project <symbol>')
182190
.description(

cli-typescript/src/cmds/general.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,6 @@ export const createNewWalletCmd = () =>
4141
new Command('create-wallet')
4242
.command('create-wallet <symbol>')
4343
.description('create a new wallet for a collection')
44-
.addOption(
45-
getForceOption(
46-
`
47-
overwrite the existing wallet.json for the collection.
48-
Note: this will NOT delete the existing wallet in turnkey if a wallet with the same collection name already exists.
49-
Please reconcile manually in turnkey if you want to delete the existing wallet.
50-
`,
51-
false,
52-
),
53-
)
5444
.action(newWalletAction);
5545

5646
export const initContractCmd = () =>

cli-typescript/src/utils/ContractManager.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ export class ContractManager {
4040
public client: PublicClient;
4141
public rpcUrl: string;
4242
public chain: Chain;
43-
private meTurnkeyServiceClient: ReturnType<typeof getMETurnkeyServiceClient>;
4443

4544
constructor(
4645
public chainId: SUPPORTED_CHAINS,
@@ -50,7 +49,6 @@ export class ContractManager {
5049
this.symbol = this.symbol.toLowerCase();
5150
this.rpcUrl = rpcUrls[this.chainId];
5251
this.chain = getViemChainByChainId(this.chainId);
53-
this.meTurnkeyServiceClient = getMETurnkeyServiceClient();
5452

5553
// Initialize viem client
5654
this.client = createPublicClient({
@@ -115,7 +113,8 @@ export class ContractManager {
115113
value?: bigint;
116114
gasLimit?: bigint;
117115
}): Promise<Hex> {
118-
return this.meTurnkeyServiceClient.sendTransaction(this.symbol, {
116+
const meTurnkeyServiceClient = await getMETurnkeyServiceClient();
117+
return await meTurnkeyServiceClient.sendTransaction(this.symbol, {
119118
to,
120119
data,
121120
value,
@@ -361,7 +360,7 @@ export class ContractManager {
361360
const data = encodeFunctionData({
362361
abi: [ERC1155M_ABIS.setTransferable],
363362
functionName: ERC1155M_ABIS.setTransferable.name,
364-
args: [false],
363+
args: [!freeze],
365364
});
366365

367366
const txHash = await this.sendTransaction({
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import fs from 'fs';
2+
import * as okta from './okta';
3+
import { mkdirp } from 'mkdirp';
4+
5+
export const authenticate = async () => {
6+
const configDir = `${process.env.HOME}/.config/magicdrop2`;
7+
const configPath = `${configDir}/config.json`;
8+
9+
// make dir if it does not exist
10+
mkdirp.sync(configDir);
11+
12+
// Load credentials
13+
const config: IConfig = fs.existsSync(configPath)
14+
? JSON.parse(fs.readFileSync(configPath, 'utf-8'))
15+
: {
16+
accessToken: '',
17+
refreshToken: '',
18+
idToken: '',
19+
expires: '2025-05-31T00:00:00.000Z',
20+
meApiAdminKey: '',
21+
};
22+
23+
const setToken = (token: okta.IAuth['token']) => {
24+
config.accessToken = token.access_token;
25+
config.refreshToken = token.refresh_token;
26+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
27+
};
28+
29+
if (!config.accessToken || !config.refreshToken) {
30+
const { token } = await okta.authenticate();
31+
setToken(token);
32+
return;
33+
}
34+
35+
// Decode the access token and find expiry
36+
const decoded = decodeJWT(config.accessToken);
37+
const expiry = new Date(decoded.exp * 1000).getTime();
38+
const now = new Date().getTime();
39+
40+
if (now >= expiry) {
41+
// Authenticate if expired, and we do not have a valid refresh token
42+
if (!(await okta.validToken(config.refreshToken, 'refresh_token'))) {
43+
const { token } = await okta.authenticate();
44+
setToken(token);
45+
} else {
46+
// Refresh if expired, and we have a valid refresh token
47+
const token = await okta.refresh(config.refreshToken);
48+
if (token) setToken(token);
49+
}
50+
}
51+
52+
return {
53+
Authorization: `Bearer ${config.accessToken}`,
54+
};
55+
};
56+
57+
export const decodeJWT = (token: string) => {
58+
return JSON.parse(
59+
Buffer.from(token.split('.')[1], 'base64').toString(),
60+
) as DecodedJWT;
61+
};
62+
63+
export type DecodedJWT = {
64+
ver: number;
65+
jti: string;
66+
iss: string;
67+
aud: string;
68+
sub: string;
69+
iat: number;
70+
exp: number;
71+
cid: string;
72+
uid: string;
73+
scp: string[];
74+
auth_time: number;
75+
};
76+
77+
export type IConfig = {
78+
accessToken: string;
79+
refreshToken: string;
80+
idToken: string;
81+
expires: string;
82+
meApiAdminKey: string;
83+
xBypassBotKey: string;
84+
};

0 commit comments

Comments
 (0)