Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 87 additions & 2 deletions account.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,48 @@
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 { 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 {DidMailto.EmailAddress} [email]
* @param {object} [options]
* @param {boolean} [options.github]
*/
export const login = async (email) => loginWithClient(email, await getClient())
export const login = async (email, options) => {
if (email) {
await loginWithClient(email, await getClient())
} else if (options?.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
Expand Down Expand Up @@ -43,6 +74,60 @@ export const loginWithClient = async (email, client) => {
}
}

/**
* @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)
}
}

/**
*
*/
Expand Down
3 changes: 2 additions & 1 deletion bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ cli
.example('up path/to/files')

cli
.command('login <email>')
.command('login [email]')
.example('login user@example.com')
.describe(
'Authenticate this agent with your email address to gain access to all capabilities that have been delegated to it.'
)
.option('--github', 'Use GitHub to authenticate. GitHub developer accounts automatically gain access to a trial plan.', false)
.action(Account.login)

cli
Expand Down
Loading
Loading