prividium 0.0.2-beta → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/bin/cli.js +2 -0
- package/dist/cli/base-cli.js +9 -0
- package/dist/cli/commands/config.js +40 -0
- package/dist/cli/commands/proxy.js +71 -0
- package/dist/cli/index.js +12 -0
- package/dist/cli/server/config-file.js +58 -0
- package/dist/cli/server/connection-workflow.js +93 -0
- package/dist/cli/server/server.js +80 -0
- package/dist/cli/static/callback.html +70 -0
- package/dist/cli/static/start.html +33 -0
- package/dist/{index.d.ts → sdk/index.d.ts} +1 -1
- package/dist/{popup-auth.d.ts → sdk/popup-auth.d.ts} +7 -2
- package/dist/{popup-auth.js → sdk/popup-auth.js} +5 -5
- package/dist/{prividium-chain.js → sdk/prividium-chain.js} +0 -1
- package/dist/{types.d.ts → sdk/types.d.ts} +2 -7
- package/dist/tsconfig.cli.tsbuildinfo +1 -0
- package/dist/tsconfig.sdk.tsbuildinfo +1 -0
- package/package.json +22 -6
- /package/dist/{index.js → sdk/index.js} +0 -0
- /package/dist/{prividium-chain.d.ts → sdk/prividium-chain.d.ts} +0 -0
- /package/dist/{storage.d.ts → sdk/storage.d.ts} +0 -0
- /package/dist/{storage.js → sdk/storage.js} +0 -0
- /package/dist/{token-utils.d.ts → sdk/token-utils.d.ts} +0 -0
- /package/dist/{token-utils.js → sdk/token-utils.js} +0 -0
- /package/dist/{types.js → sdk/types.js} +0 -0
package/README.md
CHANGED
|
@@ -168,6 +168,34 @@ page at the `redirectUrl` you configured:
|
|
|
168
168
|
|
|
169
169
|
- The callback page must be hosted on the same origin as your main application that initiates the auth flow.
|
|
170
170
|
|
|
171
|
+
## OAuth Scopes
|
|
172
|
+
|
|
173
|
+
The SDK supports requesting specific OAuth scopes during authorization to ensure users meet certain requirements:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { createPrividiumChain, type OauthScope } from 'prividium';
|
|
177
|
+
|
|
178
|
+
const prividium = createPrividiumChain({
|
|
179
|
+
clientId: 'your-client-id',
|
|
180
|
+
chain: prividiumChain,
|
|
181
|
+
rpcUrl: 'https://rpc.prividium.io',
|
|
182
|
+
authBaseUrl: 'https://auth.prividium.io',
|
|
183
|
+
redirectUrl: window.location.origin + '/auth/callback',
|
|
184
|
+
scope: ['wallet:required', 'network:required'], // Request specific scopes
|
|
185
|
+
onAuthExpiry: () => {
|
|
186
|
+
console.log('Authentication expired');
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Available Scopes
|
|
192
|
+
|
|
193
|
+
- **`wallet:required`** - Ensures the user has at least one wallet address associated with their account
|
|
194
|
+
- **`network:required`** - Ensures the user has a wallet connected with the correct chain configuration
|
|
195
|
+
|
|
196
|
+
When scopes are specified, the authorization flow will validate that the user meets all requirements. If requirements
|
|
197
|
+
are not met, the user panel will guide them through the necessary setup steps before completing authentication.
|
|
198
|
+
|
|
171
199
|
## API Reference
|
|
172
200
|
|
|
173
201
|
### `createPrividiumChain(config)`
|
|
@@ -183,6 +211,7 @@ interface PrividiumConfig {
|
|
|
183
211
|
rpcUrl: string; // Private RPC endpoint URL
|
|
184
212
|
authBaseUrl: string; // Authorization service base URL
|
|
185
213
|
redirectUrl: string; // OAuth redirect URL
|
|
214
|
+
scope?: OauthScope[]; // Optional OAuth scopes to request (optional)
|
|
186
215
|
storage?: Storage; // Custom storage implementation (optional)
|
|
187
216
|
onAuthExpiry?: () => void; // Called when authentication expires (optional)
|
|
188
217
|
}
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import yargs from 'yargs';
|
|
2
|
+
export const BASE_CLI = yargs(process.argv.slice(2))
|
|
3
|
+
.scriptName('prividium-cli')
|
|
4
|
+
.option('configPath', {
|
|
5
|
+
alias: ['c', 'config-path', 'config'],
|
|
6
|
+
description: 'Path for config file. By default config file is stored under user personal folder',
|
|
7
|
+
type: 'string',
|
|
8
|
+
demandOption: false
|
|
9
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ConfigFile } from '../server/config-file.js';
|
|
2
|
+
function clearConfig(path) {
|
|
3
|
+
const file = new ConfigFile(path);
|
|
4
|
+
file.remove();
|
|
5
|
+
}
|
|
6
|
+
function configPath(path) {
|
|
7
|
+
const file = new ConfigFile(path);
|
|
8
|
+
file.printPath();
|
|
9
|
+
}
|
|
10
|
+
function printConfig(path) {
|
|
11
|
+
const file = new ConfigFile(path);
|
|
12
|
+
file.print();
|
|
13
|
+
}
|
|
14
|
+
function updateConfig(urls, path) {
|
|
15
|
+
const file = new ConfigFile(path);
|
|
16
|
+
file.write(urls);
|
|
17
|
+
}
|
|
18
|
+
export const addConfig = (cli) => {
|
|
19
|
+
return cli.command('config', 'Manipulate local configuration file', (yargs) => yargs
|
|
20
|
+
.command('clear', 'Clears current configuration file', (yargs) => yargs, (args) => clearConfig(args.configPath))
|
|
21
|
+
.command('path', 'Shows local config file path', (yargs) => yargs, (args) => configPath(args.configPath))
|
|
22
|
+
.command('print', 'Prints current configuration', (yargs) => yargs, (args) => printConfig(args.configPath))
|
|
23
|
+
.command('set', 'Updates config', (yargs) => yargs
|
|
24
|
+
.option('rpcUrl', {
|
|
25
|
+
alias: ['rpc-url', 'r'],
|
|
26
|
+
description: 'Specifies target Prividium rpc url. These takes precedence over config file and env variable.',
|
|
27
|
+
demandOption: true,
|
|
28
|
+
type: 'string'
|
|
29
|
+
})
|
|
30
|
+
.option('userPanelUrl', {
|
|
31
|
+
alias: ['user-panel-url', 'u'],
|
|
32
|
+
description: 'Specifies url used to log in into the Prividium network. Takes precedence over config file and env variable',
|
|
33
|
+
type: 'string',
|
|
34
|
+
demandOption: true
|
|
35
|
+
}), (args) => updateConfig({
|
|
36
|
+
prividiumRpcUrl: args.rpcUrl,
|
|
37
|
+
userPanelUrl: args.userPanelUrl
|
|
38
|
+
}, args.configPath))
|
|
39
|
+
.demandCommand());
|
|
40
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { buildServer } from '../server/server.js';
|
|
2
|
+
import { CreationWorkflow } from '../server/connection-workflow.js';
|
|
3
|
+
import { ConfigFile } from '../server/config-file.js';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
const envSchema = z.object({
|
|
6
|
+
PRIVIDIUM_RPC_URL: z.string().optional(),
|
|
7
|
+
USER_PANEL_URL: z.string().optional()
|
|
8
|
+
});
|
|
9
|
+
async function startServer(opts) {
|
|
10
|
+
const config = new ConfigFile(opts.configPath);
|
|
11
|
+
const workflow = new CreationWorkflow(config);
|
|
12
|
+
workflow.start();
|
|
13
|
+
const configConfig = config.read();
|
|
14
|
+
const env = envSchema.parse(process.env);
|
|
15
|
+
const givenRpcUrl = opts.rpcUrl ?? env.PRIVIDIUM_RPC_URL ?? configConfig.prividiumRpcUrl;
|
|
16
|
+
const givenUserPanelUrl = opts.userPanelUrl ?? env.USER_PANEL_URL ?? configConfig.userPanelUrl;
|
|
17
|
+
const { prividiumRpcUrl, userPanelUrl } = await workflow.gatherData(givenRpcUrl, givenUserPanelUrl);
|
|
18
|
+
const app = buildServer({
|
|
19
|
+
prividiumRpcUrl,
|
|
20
|
+
userPanelUrl,
|
|
21
|
+
async onSubmit() {
|
|
22
|
+
await workflow.onSubmit();
|
|
23
|
+
},
|
|
24
|
+
onCall(methodName) {
|
|
25
|
+
workflow.onMessage(methodName);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const serverUrl = await app.listen({
|
|
29
|
+
port: 24101,
|
|
30
|
+
host: '127.0.0.1'
|
|
31
|
+
});
|
|
32
|
+
await workflow.waitForAuthentication(serverUrl);
|
|
33
|
+
}
|
|
34
|
+
export const addProxy = (cli) => {
|
|
35
|
+
return cli.command('proxy', 'Starts authenticated rpc proxy server', (yargs) => yargs
|
|
36
|
+
.option('rpcUrl', {
|
|
37
|
+
alias: ['rpc-url', 'r'],
|
|
38
|
+
description: 'Specifies target Prividium rpc url. These takes precedence over config file and env variable.',
|
|
39
|
+
demandOption: false,
|
|
40
|
+
type: 'string'
|
|
41
|
+
})
|
|
42
|
+
.option('userPanelUrl', {
|
|
43
|
+
alias: ['user-panel-url', 'u'],
|
|
44
|
+
description: 'Specifies url used to log in into the Prividium network. Takes precedence over config file and env variable',
|
|
45
|
+
type: 'string'
|
|
46
|
+
})
|
|
47
|
+
.option('configPath', {
|
|
48
|
+
alias: ['c', 'config-path', 'config'],
|
|
49
|
+
description: 'Path for config file. By default config file is stored under user personal folder',
|
|
50
|
+
type: 'string',
|
|
51
|
+
demandOption: false
|
|
52
|
+
})
|
|
53
|
+
.option('port', {
|
|
54
|
+
alias: ['p'],
|
|
55
|
+
description: 'Port used for local proxy. This has to match with the port configured in your Prividium network.',
|
|
56
|
+
default: 24101,
|
|
57
|
+
type: 'number'
|
|
58
|
+
})
|
|
59
|
+
.option('host', {
|
|
60
|
+
alias: 'h',
|
|
61
|
+
description: 'Host used for local server. By default traffic from outside localhost is disabled.',
|
|
62
|
+
default: '127.0.0.1',
|
|
63
|
+
type: 'string'
|
|
64
|
+
}), (args) => startServer({
|
|
65
|
+
rpcUrl: args.rpcUrl,
|
|
66
|
+
userPanelUrl: args.userPanelUrl,
|
|
67
|
+
configPath: args.configPath,
|
|
68
|
+
port: args.port,
|
|
69
|
+
host: args.host
|
|
70
|
+
}));
|
|
71
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BASE_CLI } from './base-cli.js';
|
|
2
|
+
import { addProxy } from './commands/proxy.js';
|
|
3
|
+
import { addConfig } from './commands/config.js';
|
|
4
|
+
const actions = [addProxy, addConfig];
|
|
5
|
+
const baseCli = actions.reduce((argv, applyAction) => {
|
|
6
|
+
return applyAction(argv);
|
|
7
|
+
}, BASE_CLI);
|
|
8
|
+
const cli = baseCli.help().strict().demandCommand();
|
|
9
|
+
cli.parseAsync().catch((e) => {
|
|
10
|
+
console.error(e);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import appDirs from 'appdirsjs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { log } from '@clack/prompts';
|
|
6
|
+
import color from 'kleur';
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access
|
|
8
|
+
const appDirsFn = appDirs.default;
|
|
9
|
+
const dirs = appDirsFn({ appName: 'prividium-proxy' });
|
|
10
|
+
const urlConfigSchema = z.object({
|
|
11
|
+
prividiumRpcUrl: z.string(),
|
|
12
|
+
userPanelUrl: z.string()
|
|
13
|
+
});
|
|
14
|
+
export class ConfigFile {
|
|
15
|
+
filePath;
|
|
16
|
+
constructor(filePath) {
|
|
17
|
+
if (filePath) {
|
|
18
|
+
this.filePath = filePath;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this.filePath = path.join(dirs.config, 'config.json');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
read() {
|
|
25
|
+
if (!existsSync(this.filePath)) {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
const data = readFileSync(this.filePath).toString();
|
|
30
|
+
try {
|
|
31
|
+
return urlConfigSchema.parse(JSON.parse(data));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
rmSync(this.filePath);
|
|
35
|
+
log.warn('Corrupted configuration file');
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
write(param) {
|
|
41
|
+
const dir = path.parse(this.filePath).dir;
|
|
42
|
+
mkdirSync(dir, { recursive: true });
|
|
43
|
+
writeFileSync(this.filePath, JSON.stringify(param));
|
|
44
|
+
}
|
|
45
|
+
remove() {
|
|
46
|
+
if (existsSync(this.filePath)) {
|
|
47
|
+
rmSync(this.filePath);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
print() {
|
|
51
|
+
const { prividiumRpcUrl, userPanelUrl } = this.read();
|
|
52
|
+
console.log(`${color.bold('Prividium rpc url')}: ${prividiumRpcUrl}`);
|
|
53
|
+
console.log(`${color.bold('Log in url')}: ${userPanelUrl}`);
|
|
54
|
+
}
|
|
55
|
+
printPath() {
|
|
56
|
+
console.log(this.filePath);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { confirm, intro, log, tasks, text } from '@clack/prompts';
|
|
2
|
+
import color from 'kleur';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { setTimeout } from 'timers/promises';
|
|
5
|
+
const HEADER = ` .__ .__ .___.__ .__ .__
|
|
6
|
+
_____________|__|__ _|__| __| _/|__|__ __ _____ ____ | | |__|
|
|
7
|
+
\\____ \\_ __ \\ \\ \\/ / |/ __ | | | | \\/ \\ ______ _/ ___\\| | | |
|
|
8
|
+
| |_> > | \\/ |\\ /| / /_/ | | | | / Y Y \\ /_____/ \\ \\___| |_| |
|
|
9
|
+
| __/|__| |__| \\_/ |__\\____ | |__|____/|__|_| / \\___ >____/__|
|
|
10
|
+
|__| \\/ \\/ \\/ `;
|
|
11
|
+
export class CreationWorkflow {
|
|
12
|
+
config;
|
|
13
|
+
submitCallback;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.submitCallback = undefined;
|
|
17
|
+
}
|
|
18
|
+
start() {
|
|
19
|
+
console.log(color.blue(HEADER));
|
|
20
|
+
console.log('');
|
|
21
|
+
intro('Starting prividium proxy');
|
|
22
|
+
}
|
|
23
|
+
async gatherData(maybePrividiumRpcUrl, maybeUserPanelUrl) {
|
|
24
|
+
const { url: prividiumRpcUrl, prompted: prompted1 } = await this.askForUrl(maybePrividiumRpcUrl, 'prividium rpc');
|
|
25
|
+
const { url: userPanelUrl, prompted: prompted2 } = await this.askForUrl(maybeUserPanelUrl, 'user panel');
|
|
26
|
+
if (prompted1 || prompted2) {
|
|
27
|
+
const confirmation = await confirm({
|
|
28
|
+
message: 'Do wou want to store this config for future usages?'
|
|
29
|
+
});
|
|
30
|
+
if (confirmation) {
|
|
31
|
+
this.config.write({ prividiumRpcUrl, userPanelUrl });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
prividiumRpcUrl,
|
|
36
|
+
userPanelUrl
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async askForUrl(maybeUrl, name) {
|
|
40
|
+
if (maybeUrl !== undefined) {
|
|
41
|
+
const res = z.url().safeParse(maybeUrl);
|
|
42
|
+
if (!res.success) {
|
|
43
|
+
log.error(`Invalidad ${name} url provided: ${maybeUrl}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
log.info(`Using ${name} url: ${color.bold(maybeUrl)}`);
|
|
47
|
+
return { url: maybeUrl, prompted: false };
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const url = await text({
|
|
51
|
+
message: `Please insert your ${name} url`,
|
|
52
|
+
validate(value) {
|
|
53
|
+
const parsed = z.url().safeParse(value);
|
|
54
|
+
if (!parsed.success) {
|
|
55
|
+
return 'Please provide a valid url';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (typeof url === 'symbol') {
|
|
60
|
+
log.warn('Canceled by the user');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
return { url, prompted: true };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async waitForAuthentication(url) {
|
|
67
|
+
log.info(`Please log in: ${url}`);
|
|
68
|
+
await tasks([
|
|
69
|
+
{
|
|
70
|
+
task: async () => {
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
this.submitCallback = resolve;
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
title: 'Waiting for authentication'
|
|
76
|
+
}
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
79
|
+
async onSubmit() {
|
|
80
|
+
if (this.submitCallback === undefined) {
|
|
81
|
+
throw new Error('Missing submit callback.');
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
this.submitCallback('Authentication successful!');
|
|
85
|
+
await setTimeout(100);
|
|
86
|
+
}
|
|
87
|
+
log.info(`Your proxy rpc is ready! 🚀: \n\n${color.bold('http://127.0.0.1:24101/rpc')}\n`);
|
|
88
|
+
log.info('Waiting for logs...');
|
|
89
|
+
}
|
|
90
|
+
onMessage(msg) {
|
|
91
|
+
log.message(msg);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import Fastify from 'fastify';
|
|
2
|
+
import { validatorCompiler } from 'fastify-type-provider-zod';
|
|
3
|
+
import fastifyStatic from '@fastify/static';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { fastifyHttpProxy } from '@fastify/http-proxy';
|
|
7
|
+
import { randomBytes } from 'node:crypto';
|
|
8
|
+
function fastifyApp() {
|
|
9
|
+
return Fastify().withTypeProvider();
|
|
10
|
+
}
|
|
11
|
+
function randomStateString() {
|
|
12
|
+
return randomBytes(20).toString('hex');
|
|
13
|
+
}
|
|
14
|
+
export function buildServer(config) {
|
|
15
|
+
const app = fastifyApp();
|
|
16
|
+
app.setValidatorCompiler(validatorCompiler);
|
|
17
|
+
const state = randomStateString();
|
|
18
|
+
let jwt = '';
|
|
19
|
+
app.register(fastifyStatic, { root: path.join(import.meta.dirname, '..', 'static') });
|
|
20
|
+
app.get('/health', async (_req, reply) => {
|
|
21
|
+
return reply.send('ok');
|
|
22
|
+
});
|
|
23
|
+
app.get('/', async (_req, reply) => {
|
|
24
|
+
reply.header('Cache-Control', 'no-cache');
|
|
25
|
+
return reply.sendFile(path.join('start.html'));
|
|
26
|
+
});
|
|
27
|
+
app.get('/redirect-uri', async (_req, reply) => {
|
|
28
|
+
const url = new URL('/auth/authorize', config.userPanelUrl);
|
|
29
|
+
url.searchParams.set('client_id', 'proxy-cli');
|
|
30
|
+
url.searchParams.set('redirect_uri', `http://localhost:24101/callback`);
|
|
31
|
+
url.searchParams.set('state', state);
|
|
32
|
+
url.searchParams.set('response_type', 'token');
|
|
33
|
+
return reply.send(url.toString());
|
|
34
|
+
});
|
|
35
|
+
app.get('/callback', async (_req, reply) => {
|
|
36
|
+
reply.header('Cache-Control', 'no-cache');
|
|
37
|
+
return reply.sendFile(path.join('callback.html'));
|
|
38
|
+
});
|
|
39
|
+
app.post('/submit', {
|
|
40
|
+
schema: {
|
|
41
|
+
body: z.object({
|
|
42
|
+
token: z.string(),
|
|
43
|
+
state: z.string()
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
}, async (req, reply) => {
|
|
47
|
+
if (req.body.state !== state) {
|
|
48
|
+
throw new Error('invalid state received');
|
|
49
|
+
}
|
|
50
|
+
jwt = req.body.token;
|
|
51
|
+
await config.onSubmit();
|
|
52
|
+
return reply.send('ok');
|
|
53
|
+
});
|
|
54
|
+
app.register(fastifyHttpProxy, {
|
|
55
|
+
upstream: config.prividiumRpcUrl,
|
|
56
|
+
prefix: '/rpc',
|
|
57
|
+
rewritePrefix: '/rpc',
|
|
58
|
+
routes: ['/'],
|
|
59
|
+
preValidation: (request, _reply, done) => {
|
|
60
|
+
const parser = z.object({ method: z.string() });
|
|
61
|
+
const { method } = parser.parse(request.body);
|
|
62
|
+
config.onCall(method);
|
|
63
|
+
done();
|
|
64
|
+
},
|
|
65
|
+
preHandler: (_req, reply, done) => {
|
|
66
|
+
if (jwt === '') {
|
|
67
|
+
reply.status(500).send('please authenticate first.');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
return done();
|
|
71
|
+
},
|
|
72
|
+
replyOptions: {
|
|
73
|
+
rewriteRequestHeaders: (_originalReq, headers) => ({
|
|
74
|
+
...headers,
|
|
75
|
+
authorization: `Bearer ${jwt}`
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return app;
|
|
80
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Prividium</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
</head>
|
|
9
|
+
<body
|
|
10
|
+
class="min-h-screen bg-gradient-to-br from-slate-50 via-white to-blue-50 text-slate-800 flex items-center justify-center p-4"
|
|
11
|
+
>
|
|
12
|
+
<div
|
|
13
|
+
class="text-center bg-white/80 backdrop-blur-md py-10 px-6 shadow-xl rounded-2xl border border-slate-200 sm:px-12 w-full max-w-md"
|
|
14
|
+
>
|
|
15
|
+
<h1
|
|
16
|
+
id="main-title"
|
|
17
|
+
class="text-2xl md:text-3xl font-bold bg-gradient-to-r from-blue-700 to-blue-900 bg-clip-text text-transparent"
|
|
18
|
+
>
|
|
19
|
+
Finalizing Prividium sign-in...
|
|
20
|
+
</h1>
|
|
21
|
+
<div class="mt-4">
|
|
22
|
+
<svg
|
|
23
|
+
class="animate-spin h-8 w-8 text-blue-700 mx-auto"
|
|
24
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
25
|
+
fill="none"
|
|
26
|
+
viewBox="0 0 24 24"
|
|
27
|
+
>
|
|
28
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
29
|
+
<path
|
|
30
|
+
class="opacity-75"
|
|
31
|
+
fill="currentColor"
|
|
32
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
33
|
+
></path>
|
|
34
|
+
</svg>
|
|
35
|
+
</div>
|
|
36
|
+
<div id="proxy-url" style="display: none" class="mt-6 text-blue-700 text-center">
|
|
37
|
+
<img src="https://www.zksync.io/faq/faq-brackets.svg" alt="Success" class="h-12 w-auto mx-auto mb-3" />
|
|
38
|
+
<span>You can now access your Prividium RPC proxy at: </span>
|
|
39
|
+
<span class="font-bold">http://127.0.0.1:24101/rpc</span>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<script>
|
|
43
|
+
const titleElem = document.getElementById('main-title');
|
|
44
|
+
const hashPath = window.location.hash;
|
|
45
|
+
const params = new URLSearchParams(hashPath.replace(/^#/, ''));
|
|
46
|
+
const maybeToken = params.get('token');
|
|
47
|
+
const maybeState = params.get('state');
|
|
48
|
+
|
|
49
|
+
if (!maybeToken || !maybeToken) {
|
|
50
|
+
console.error('Missing stuff');
|
|
51
|
+
} else {
|
|
52
|
+
fetch('/submit', {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
'content-type': 'application/json'
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
token: maybeToken,
|
|
59
|
+
state: maybeState
|
|
60
|
+
})
|
|
61
|
+
}).then(() => {
|
|
62
|
+
titleElem.innerHTML = 'Done! This window can be closed now.';
|
|
63
|
+
document.querySelector('svg').style.display = 'none';
|
|
64
|
+
const urlElem = document.getElementById('proxy-url');
|
|
65
|
+
urlElem.style.display = 'block';
|
|
66
|
+
}, console.error);
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
69
|
+
</body>
|
|
70
|
+
</html>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Prividium</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
</head>
|
|
9
|
+
<body class="bg-gray-900 text-white flex items-center justify-center h-screen">
|
|
10
|
+
<div class="text-center">
|
|
11
|
+
<h1 id="main-title" class="text-2xl font-bold">Redirecting...</h1>
|
|
12
|
+
<div class="mt-4">
|
|
13
|
+
<svg class="animate-spin h-8 w-8 text-white mx-auto" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
14
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
15
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
16
|
+
</svg>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<script>
|
|
20
|
+
const hashPath = window.location.hash;
|
|
21
|
+
const params = new URLSearchParams(hashPath.replace(/^#/, ''));
|
|
22
|
+
|
|
23
|
+
fetch('/redirect-uri').then(
|
|
24
|
+
(r) => {
|
|
25
|
+
return r.text();
|
|
26
|
+
},
|
|
27
|
+
console.error
|
|
28
|
+
).then(url => {
|
|
29
|
+
window.location.assign(url);
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
@@ -3,4 +3,4 @@ export type { PrividiumConfig, PrividiumChain, PopupOptions, Storage, TokenData,
|
|
|
3
3
|
export { AUTH_ERRORS, STORAGE_KEYS } from './types.js';
|
|
4
4
|
export { LocalStorage, TokenManager } from './storage.js';
|
|
5
5
|
export { parseToken, isTokenExpired, generateRandomState } from './token-utils.js';
|
|
6
|
-
export { PopupAuth, handleAuthCallback, type AuthCallbackMessage } from './popup-auth.js';
|
|
6
|
+
export { PopupAuth, handleAuthCallback, type AuthCallbackMessage, type OauthScope } from './popup-auth.js';
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { type PopupOptions } from './types.js';
|
|
2
2
|
import { type TokenManager } from './storage.js';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* OAuth scopes that can be requested during authorization
|
|
5
|
+
*
|
|
6
|
+
* - `wallet:required`: Requires user to have at least one wallet associated
|
|
7
|
+
* - `network:required`: Requires user to have wallet connected with correct chain
|
|
8
|
+
*/
|
|
9
|
+
export type OauthScope = 'wallet:required' | 'network:required';
|
|
4
10
|
export interface PopupAuthConfig {
|
|
5
11
|
authBaseUrl: string;
|
|
6
12
|
clientId: string;
|
|
7
13
|
redirectUri: string;
|
|
8
14
|
tokenManager: TokenManager;
|
|
9
15
|
onAuthExpiry?: () => void;
|
|
10
|
-
scope?: OauthScope[];
|
|
11
16
|
}
|
|
12
17
|
export declare class PopupAuth {
|
|
13
18
|
private config;
|
|
@@ -6,10 +6,10 @@ export class PopupAuth {
|
|
|
6
6
|
this.config = config;
|
|
7
7
|
}
|
|
8
8
|
async authorize(options = {}) {
|
|
9
|
-
const { popupSize = { w:
|
|
9
|
+
const { popupSize = { w: 600, h: 800 }, scopes = [] } = options;
|
|
10
10
|
const state = generateRandomState();
|
|
11
11
|
this.config.tokenManager.setState(state);
|
|
12
|
-
const authUrl = this.buildAuthUrl(state);
|
|
12
|
+
const authUrl = this.buildAuthUrl(state, scopes);
|
|
13
13
|
const popup = this.openPopup(authUrl, popupSize);
|
|
14
14
|
return new Promise((resolve, reject) => {
|
|
15
15
|
const TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
@@ -108,14 +108,14 @@ export class PopupAuth {
|
|
|
108
108
|
}, TIMEOUT_MS);
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
-
buildAuthUrl(state) {
|
|
111
|
+
buildAuthUrl(state, scopes) {
|
|
112
112
|
const url = new URL('/auth/authorize', this.config.authBaseUrl);
|
|
113
113
|
url.searchParams.set('client_id', this.config.clientId);
|
|
114
114
|
url.searchParams.set('redirect_uri', this.config.redirectUri);
|
|
115
115
|
url.searchParams.set('state', state);
|
|
116
116
|
url.searchParams.set('response_type', 'token');
|
|
117
|
-
if (
|
|
118
|
-
for (const scope of
|
|
117
|
+
if (scopes?.length) {
|
|
118
|
+
for (const scope of scopes) {
|
|
119
119
|
url.searchParams.append('scope', scope);
|
|
120
120
|
}
|
|
121
121
|
}
|
|
@@ -12,7 +12,6 @@ export interface PrividiumConfig {
|
|
|
12
12
|
authBaseUrl: string;
|
|
13
13
|
redirectUrl: string;
|
|
14
14
|
permissionsApiBaseUrl: string;
|
|
15
|
-
scope?: OauthScope[];
|
|
16
15
|
storage?: Storage;
|
|
17
16
|
onAuthExpiry?: () => void;
|
|
18
17
|
}
|
|
@@ -50,12 +49,7 @@ export interface EnableWalletTokenResponse {
|
|
|
50
49
|
export interface PrividiumChain {
|
|
51
50
|
chain: Chain;
|
|
52
51
|
transport: Transport;
|
|
53
|
-
authorize(opts?:
|
|
54
|
-
popupSize?: {
|
|
55
|
-
w: number;
|
|
56
|
-
h: number;
|
|
57
|
-
};
|
|
58
|
-
}): Promise<string>;
|
|
52
|
+
authorize(opts?: PopupOptions): Promise<string>;
|
|
59
53
|
unauthorize(): void;
|
|
60
54
|
isAuthorized(): boolean;
|
|
61
55
|
getAuthHeaders(): Record<string, string> | null;
|
|
@@ -77,6 +71,7 @@ export interface PopupOptions {
|
|
|
77
71
|
w: number;
|
|
78
72
|
h: number;
|
|
79
73
|
};
|
|
74
|
+
scopes?: OauthScope[];
|
|
80
75
|
}
|
|
81
76
|
export declare const AUTH_ERRORS: {
|
|
82
77
|
readonly INVALID_STATE: "Invalid state parameter";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../cli/base-cli.ts","../cli/index.ts","../cli/commands/config.ts","../cli/commands/proxy.ts","../cli/server/config-file.ts","../cli/server/connection-workflow.ts","../cli/server/server.ts"],"version":"5.8.3"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/index.ts","../src/popup-auth.ts","../src/prividium-chain.ts","../src/storage.ts","../src/token-utils.ts","../src/types.ts"],"version":"5.8.3"}
|
package/package.json
CHANGED
|
@@ -1,26 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prividium",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"bin": {
|
|
5
|
+
"prividium": "./bin/cli.js"
|
|
6
|
+
},
|
|
4
7
|
"exports": {
|
|
5
8
|
".": {
|
|
6
|
-
"import": "./dist/index.js",
|
|
7
|
-
"types": "./dist/index.d.ts"
|
|
9
|
+
"import": "./dist/sdk/index.js",
|
|
10
|
+
"types": "./dist/sdk/index.d.ts"
|
|
8
11
|
}
|
|
9
12
|
},
|
|
10
13
|
"files": [
|
|
11
14
|
"dist"
|
|
12
15
|
],
|
|
13
16
|
"scripts": {
|
|
14
|
-
"build": "tsc",
|
|
17
|
+
"build": "tsc -b && yarn copy-static-files",
|
|
18
|
+
"copy-static-files": "cp -r cli/static dist/cli",
|
|
19
|
+
"dev": "tsc -b --watch",
|
|
15
20
|
"lint": "eslint . --ignore-path ../../.gitignore --max-warnings 0",
|
|
16
21
|
"lint:fix": "eslint . --fix --ignore-path ../../.gitignore",
|
|
17
22
|
"test": "vitest run",
|
|
18
23
|
"test:watch": "vitest",
|
|
19
|
-
"typecheck": "tsc --noEmit",
|
|
24
|
+
"typecheck": "tsc -b --noEmit",
|
|
25
|
+
"cli": "tsx cli/index.ts",
|
|
20
26
|
"typecheck:test": "tsc --project tsconfig.test.json --noEmit"
|
|
21
27
|
},
|
|
22
28
|
"dependencies": {
|
|
23
|
-
"
|
|
29
|
+
"@clack/prompts": "^0.11.0",
|
|
30
|
+
"@fastify/http-proxy": "^11.3.0",
|
|
31
|
+
"@fastify/static": "^8.3.0",
|
|
32
|
+
"appdirsjs": "^1.2.7",
|
|
33
|
+
"fastify": "^5.0.0",
|
|
34
|
+
"fastify-type-provider-zod": "^6.1.0",
|
|
35
|
+
"kleur": "^4.1.5",
|
|
36
|
+
"yargs": "^18.0.0",
|
|
37
|
+
"zod": "^4.1.12"
|
|
24
38
|
},
|
|
25
39
|
"peerDependencies": {
|
|
26
40
|
"viem": ">=2.0.0"
|
|
@@ -28,8 +42,10 @@
|
|
|
28
42
|
"devDependencies": {
|
|
29
43
|
"@repo/eslint-config": "workspace:*",
|
|
30
44
|
"@types/node": "^22.8.6",
|
|
45
|
+
"@types/yargs": "^17.0.34",
|
|
31
46
|
"eslint": "^8",
|
|
32
47
|
"jsdom": "^25.0.0",
|
|
48
|
+
"tsx": "^4.20.6",
|
|
33
49
|
"typescript": "^5.8.3",
|
|
34
50
|
"vitest": "^3.2.4"
|
|
35
51
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|