@sentio/sdk 1.26.4 → 1.27.1

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/src/cli/cli.ts CHANGED
@@ -10,9 +10,9 @@ import { finalizeHost, FinalizeProjectName, SentioProjectConfig } from './config
10
10
  import { uploadFile } from './upload'
11
11
  import chalk from 'chalk'
12
12
  import { buildProcessor } from './build'
13
- import { runLogin } from './commands/run-login'
14
13
  import { runCreate } from './commands/run-create'
15
14
  import { runVersion } from './commands/run-version'
15
+ import { runLogin } from './commands/run-login'
16
16
 
17
17
  const mainDefinitions = [{ name: 'command', defaultOption: true }]
18
18
  const mainOptions = commandLineArgs(mainDefinitions, {
@@ -159,12 +159,12 @@ function usage() {
159
159
  header: 'Usage',
160
160
  content: [
161
161
  'sentio <command> --help\t\tshow detail usage of specific command',
162
- 'sentio login --api-key=xx\t\tsave credential to local',
162
+ 'sentio login\t\t\t\tlogin to sentio',
163
163
  'sentio create\t\t\t\tcreate a template project',
164
164
  'sentio upload\t\t\t\tbuild and upload processor to sentio',
165
165
  'sentio gen\t\t\t\tgenerate abi',
166
166
  'sentio build\t\t\t\tgenerate abi and build',
167
- 'sentio version\t\t\t\tcurrent cli version',
167
+ 'sentio version\t\t\tcurrent cli version',
168
168
  ],
169
169
  },
170
170
  ])
@@ -0,0 +1,120 @@
1
+ import express from 'express'
2
+ import { getAuthConfig, getFinalizedHost } from '../config'
3
+ import url, { URL } from 'url'
4
+ import fetch from 'node-fetch'
5
+ import { getCliVersion } from '../utils'
6
+ import { WriteKey } from '../key'
7
+ import chalk from 'chalk'
8
+ import http from 'http'
9
+ import os from 'os'
10
+ import * as crypto from 'crypto'
11
+
12
+ interface AuthParams {
13
+ serverPort: number
14
+ sentioHost: string
15
+ codeVerifier: string
16
+ }
17
+
18
+ const app = express()
19
+
20
+ let server: http.Server
21
+ let authParams: AuthParams
22
+
23
+ export function startServer(params: AuthParams) {
24
+ authParams = params
25
+ server = app.listen(params.serverPort)
26
+ }
27
+
28
+ app.get('/callback', async (req, res) => {
29
+ res.end('login success, please go back to CLI to continue')
30
+ const host = authParams.sentioHost
31
+ const code = req.query.code
32
+ if (!code || (code as string).length == 0) {
33
+ return
34
+ }
35
+
36
+ // exchange token
37
+ const tokenResRaw = await getToken(host, code as string)
38
+ if (!tokenResRaw.ok) {
39
+ console.error(chalk.red('request token error, code:', tokenResRaw.status, tokenResRaw.statusText))
40
+ return
41
+ }
42
+ const tokenRes = await tokenResRaw.json()
43
+ const accessToken = tokenRes['access_token']
44
+
45
+ // check if the account is ready
46
+ const realHost = getFinalizedHost(host)
47
+ const userResRaw = await getUser(realHost, accessToken)
48
+ if (!userResRaw.ok) {
49
+ if (userResRaw.status == 401) {
50
+ console.error(chalk.red('please sign up on sentio first'))
51
+ } else {
52
+ console.error(chalk.red('get user error, code:', userResRaw.status, userResRaw.statusText))
53
+ }
54
+ return
55
+ }
56
+ const userRes = await userResRaw.json()
57
+ if (!userRes.emailVerified) {
58
+ console.error(chalk.red('please verify your email first'))
59
+ return
60
+ }
61
+
62
+ // create API key
63
+ const apiKeyName = `${os.hostname()}-${crypto.randomBytes(4).toString('hex')}`
64
+ const createApiKeyResRaw = await createApiKey(realHost, apiKeyName, 'sdk_generated', accessToken)
65
+ if (!createApiKeyResRaw.ok) {
66
+ console.error(chalk.red('create api key error, code:', createApiKeyResRaw.status, createApiKeyResRaw.statusText))
67
+ return
68
+ }
69
+ const createApiKeyRes = await createApiKeyResRaw.json()
70
+ const apiKey = createApiKeyRes['key']
71
+ WriteKey(realHost, apiKey)
72
+ console.log(chalk.green('login success, new API key: ' + apiKey))
73
+
74
+ server.close()
75
+ })
76
+
77
+ async function getToken(host: string, code: string) {
78
+ const authConf = getAuthConfig(host)
79
+ const params = new url.URLSearchParams({
80
+ grant_type: 'authorization_code',
81
+ client_id: authConf.clientId,
82
+ code_verifier: authParams.codeVerifier,
83
+ code: code,
84
+ redirect_uri: `http://localhost:${authParams.serverPort}/callback`,
85
+ })
86
+ return fetch(`${authConf.domain}/oauth/token`, {
87
+ method: 'POST',
88
+ headers: {
89
+ 'Content-Type': 'application/x-www-form-urlencoded',
90
+ },
91
+ body: params.toString(),
92
+ })
93
+ }
94
+
95
+ async function createApiKey(host: string, name: string, source: string, accessToken: string) {
96
+ const createApiKeyUrl = new URL('/api/v1/keys', host)
97
+ return fetch(createApiKeyUrl, {
98
+ method: 'POST',
99
+ headers: {
100
+ Authorization: 'Bearer ' + accessToken,
101
+ version: getCliVersion(),
102
+ },
103
+ body: JSON.stringify({
104
+ name: name,
105
+ scopes: ['write:project'],
106
+ source: source,
107
+ }),
108
+ })
109
+ }
110
+
111
+ async function getUser(host: string, accessToken: string) {
112
+ const getUserUrl = new URL('/api/v1/users', host)
113
+ return fetch(getUserUrl, {
114
+ method: 'GET',
115
+ headers: {
116
+ Authorization: 'Bearer ' + accessToken,
117
+ version: getCliVersion(),
118
+ },
119
+ })
120
+ }
@@ -1,10 +1,14 @@
1
1
  import commandLineArgs from 'command-line-args'
2
2
  import commandLineUsage from 'command-line-usage'
3
- import { getFinalizedHost } from '../config'
3
+ import { getAuthConfig, getFinalizedHost } from '../config'
4
+ import { startServer } from './login-server'
5
+ import url, { URL } from 'url'
6
+ import * as crypto from 'crypto'
4
7
  import chalk from 'chalk'
5
- import https from 'https'
6
- import http from 'http'
7
8
  import { WriteKey } from '../key'
9
+ import fetch from 'node-fetch'
10
+
11
+ const port = 20000
8
12
 
9
13
  export function runLogin(argv: string[]) {
10
14
  const optionDefinitions = [
@@ -15,23 +19,23 @@ export function runLogin(argv: string[]) {
15
19
  description: 'Display this usage guide.',
16
20
  },
17
21
  {
18
- name: 'api-key',
22
+ name: 'host',
23
+ description: '(Optional) Override Sentio Host name',
19
24
  type: String,
20
- description: '(Required) Your API key',
21
25
  },
22
26
  {
23
- name: 'host',
24
- description: '(Optional) Override Sentio Host name',
27
+ name: 'api-key',
25
28
  type: String,
29
+ description: '(Optional) Your API key',
26
30
  },
27
31
  ]
28
32
  const options = commandLineArgs(optionDefinitions, { argv })
29
33
 
30
- if (options.help || !options['api-key']) {
34
+ if (options.help) {
31
35
  const usage = commandLineUsage([
32
36
  {
33
37
  header: 'Login to Sentio',
34
- content: 'sentio login --api-key=<key>',
38
+ content: 'sentio login',
35
39
  },
36
40
  {
37
41
  header: 'Options',
@@ -39,32 +43,59 @@ export function runLogin(argv: string[]) {
39
43
  },
40
44
  ])
41
45
  console.log(usage)
42
- } else {
46
+ } else if (options['api-key']) {
43
47
  const host = getFinalizedHost(options.host)
44
48
  console.log(chalk.blue('login to ' + host))
45
- const url = new URL(host)
46
- const reqOptions = {
47
- hostname: url.hostname,
48
- port: url.port,
49
- path: '/api/v1/processors/check_key',
50
- method: 'HEAD',
51
- headers: {
52
- 'api-key': options['api-key'],
53
- },
54
- }
55
- const h = url.protocol == 'https:' ? https : http
56
- const req = h.request(reqOptions, (res) => {
57
- if (res.statusCode == 200) {
58
- WriteKey(host, options['api-key'])
49
+ const apiKey = options['api-key']
50
+ checkKey(host, apiKey).then((res) => {
51
+ if (res.status == 200) {
52
+ WriteKey(host, apiKey)
59
53
  console.log(chalk.green('login success'))
60
54
  } else {
61
- console.error(chalk.red('login failed, code:', res.statusCode, res.statusMessage))
55
+ console.error(chalk.red('login failed, code:', res.status, res.statusText))
62
56
  }
63
57
  })
58
+ } else {
59
+ // https://auth0.com/docs/get-started/authentication-and-authorization-flow/call-your-api-using-the-authorization-code-flow-with-pkce
60
+ const verifier = base64URLEncode(crypto.randomBytes(32))
61
+ const challenge = base64URLEncode(sha256(verifier))
64
62
 
65
- req.on('error', (error) => {
66
- console.error(error)
63
+ const conf = getAuthConfig(options.host)
64
+ const authURL = new URL(conf.domain + `/authorize?`)
65
+ const params = new url.URLSearchParams({
66
+ response_type: 'code',
67
+ code_challenge: challenge,
68
+ code_challenge_method: 'S256',
69
+ client_id: conf.clientId,
70
+ redirect_uri: `http://localhost:${port}/callback`,
71
+ audience: conf.audience,
72
+ prompt: 'login',
73
+ })
74
+ authURL.search = params.toString()
75
+ console.log('Continue your authorization here: ' + authURL.toString())
76
+
77
+ startServer({
78
+ serverPort: port,
79
+ sentioHost: options.host,
80
+ codeVerifier: verifier,
67
81
  })
68
- req.end()
69
82
  }
70
83
  }
84
+
85
+ function base64URLEncode(str: Buffer) {
86
+ return str.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
87
+ }
88
+
89
+ function sha256(str: string) {
90
+ return crypto.createHash('sha256').update(str).digest()
91
+ }
92
+
93
+ async function checkKey(host: string, apiKey: string) {
94
+ const checkApiKeyUrl = new URL('/api/v1/processors/check_key', host)
95
+ return fetch(checkApiKeyUrl, {
96
+ method: 'HEAD',
97
+ headers: {
98
+ 'api-key': apiKey,
99
+ },
100
+ })
101
+ }
package/src/cli/config.ts CHANGED
@@ -22,6 +22,35 @@ export function getFinalizedHost(host: string): string {
22
22
  return host
23
23
  }
24
24
 
25
+ export function getAuthConfig(host: string): { domain: string; clientId: string; audience: string } {
26
+ let domain = '',
27
+ clientId = '',
28
+ audience = ''
29
+ switch (host) {
30
+ case 'local':
31
+ domain = 'https://sentio-dev.us.auth0.com'
32
+ clientId = 'qGDisObqQbcPeRA8k02POPZ2Df4KVCna'
33
+ audience = 'http://localhost:8080/v1'
34
+ break
35
+ case '':
36
+ case undefined:
37
+ case 'prod':
38
+ domain = 'https://auth.sentio.xyz'
39
+ clientId = 'xd80PeuvuZVHpBFh7yEdlSZdtE5mTpGe'
40
+ audience = 'https://app.sentio.xyz/api/v1'
41
+ break
42
+ case 'test':
43
+ case 'staging':
44
+ domain = 'https://auth.test.sentio.xyz'
45
+ clientId = 'qXVvovHaOE37SndxTZJxCKgZjw1axPax'
46
+ audience = 'https://test.sentio.xyz/api/v1'
47
+ break
48
+ default:
49
+ break
50
+ }
51
+ return { domain, clientId, audience }
52
+ }
53
+
25
54
  export function finalizeHost(config: SentioProjectConfig) {
26
55
  config.host = getFinalizedHost(config.host)
27
56
  }