@sentio/sdk 1.26.4 → 1.27.1

Sign up to get free protection for your applications and to get access to all the features.
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
  }