-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathaccount.js
162 lines (143 loc) · 4.91 KB
/
account.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import open from 'open'
import { confirm } from '@inquirer/prompts'
import * as Account from '@web3-storage/w3up-client/account'
import * as Result from '@web3-storage/w3up-client/result'
import * as DidMailto from '@web3-storage/did-mailto'
import { authorize } from '@web3-storage/capabilities/access'
import { base64url } from 'multiformats/bases/base64'
import { select } from '@inquirer/prompts'
import { getClient } from './lib.js'
import ora from 'ora'
/**
* @typedef {Awaited<ReturnType<Account.login>>['ok']&{}} View
*/
export const OAuthProviderGitHub = 'github'
const OAuthProviders = /** @type {const} */ ([OAuthProviderGitHub])
/** @type {Record<import('@web3-storage/w3up-client/types').DID, string>} */
const GitHubOauthClientIDs = {
'did:web:web3.storage': 'Ov23li0xr95ocCkZiwaD',
'did:web:staging.web3.storage': 'Ov23liDKQB1ePrcGy5HI',
}
/** @param {import('@web3-storage/w3up-client/types').DID} serviceID */
const getGithubOAuthClientID = serviceID => {
const id = process.env.GITHUB_OAUTH_CLIENT_ID || GitHubOauthClientIDs[serviceID]
if (!id) throw new Error(`missing OAuth client ID for: ${serviceID}`)
return id
}
/**
* @param {DidMailto.EmailAddress} [email]
* @param {object} [options]
* @param {boolean} [options.github]
*/
export const login = async (email, options) => {
let method
if (email) {
method = 'email'
} else if (options?.github) {
method = 'github'
} else {
method = await select({
message: 'How do you want to login?',
choices: [
{ name: 'Via Email', value: 'email' },
{ name: 'Via GitHub', value: 'github' },
],
})
}
if (method === 'email' && email) {
await loginWithClient(email, await getClient())
} else if (method === 'github') {
await oauthLoginWithClient(OAuthProviderGitHub, await getClient())
} else {
console.error('Error: please provide email address or specify flag for alternate login method')
process.exit(1)
}
}
/**
* @param {DidMailto.EmailAddress} email
* @param {import('@web3-storage/w3up-client').Client} client
* @returns {Promise<View>}
*/
export const loginWithClient = async (email, client) => {
/** @type {import('ora').Ora|undefined} */
let spinner
const timeout = setTimeout(() => {
spinner = ora(
`🔗 please click the link sent to ${email} to authorize this agent`
).start()
}, 1000)
try {
const account = Result.try(await Account.login(client, email))
Result.try(await account.save())
if (spinner) spinner.stop()
console.log(`⁂ Agent was authorized by ${account.did()}`)
return account
} catch (err) {
if (spinner) spinner.stop()
console.error(err)
process.exit(1)
} finally {
clearTimeout(timeout)
}
}
/**
* @param {(typeof OAuthProviders)[number]} provider OAuth provider
* @param {import('@web3-storage/w3up-client').Client} client
*/
export const oauthLoginWithClient = async (provider, client) => {
if (provider != OAuthProviderGitHub) {
console.error(`Error: unknown OAuth provider: ${provider}`)
process.exit(1)
}
/** @type {import('ora').Ora|undefined} */
let spinner
try {
// create access/authorize request
const request = await authorize.delegate({
audience: client.agent.connection.id,
issuer: client.agent.issuer,
// agent that should be granted access
with: client.agent.did(),
// capabilities requested (account access)
nb: { att: [{ can: '*' }] }
})
const archive = await request.archive()
if (archive.error) {
throw new Error('archiving access authorize delegation', { cause: archive.error })
}
const clientID = getGithubOAuthClientID(client.agent.connection.id.did())
const state = base64url.encode(archive.ok)
const loginURL = `https://github.com/login/oauth/authorize?scope=read:user,user:email&client_id=${clientID}&state=${state}`
if (await confirm({ message: 'Open the GitHub login URL in your default browser?' })) {
spinner = ora('Waiting for GitHub authorization to be completed in browser...').start()
await open(loginURL)
} else {
spinner = ora(`Click the link to authenticate with GitHub: ${loginURL}`).start()
}
const expiration = Math.floor(Date.now() / 1000) + (60 * 15)
const account = Result.unwrap(await Account.externalLogin(client, { request: request.cid, expiration }))
Result.unwrap(await account.save())
if (spinner) spinner.stop()
console.log(`⁂ Agent was authorized by ${account.did()}`)
return account
} catch (err) {
if (spinner) spinner.stop()
console.error(err)
process.exit(1)
}
}
/**
*
*/
export const list = async () => {
const client = await getClient()
const accounts = Object.values(Account.list(client))
for (const account of accounts) {
console.log(account.did())
}
if (accounts.length === 0) {
console.log(
'⁂ Agent has not been authorized yet. Try `w3 login` to authorize this agent with your account.'
)
}
}