Skip to content

Commit

Permalink
Silent auth provider pop up, improve auth exp
Browse files Browse the repository at this point in the history
  • Loading branch information
abeatrix committed Oct 19, 2022
1 parent 4098d83 commit 8f65c35
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 65 deletions.
9 changes: 3 additions & 6 deletions client/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,6 @@
"category": "Sourcegraph",
"title": "Remove Repository from Sourcegraph File System",
"icon": "$(trash)"
},
{
"command": "sourcegraph.auth",
"category": "Sourcegraph",
"title": "Log in to Sourcegraph"
}
],
"authentication": [
Expand Down Expand Up @@ -232,7 +227,6 @@
"package": "yarn run -T ts-node ./scripts/package.ts",
"prebuild": "yarn build-inline-extensions",
"prewatch": "yarn build-inline-extensions",
"vscode:prepublish": "yarn build-inline-extensions && yarn build",
"build-inline-extensions": "node scripts/build-inline-extensions",
"task:gulp": "yarn run -T cross-env NODE_OPTIONS=\"--max_old_space_size=8192\" gulp",
"build:esbuild": "NODE_ENV=development yarn task:gulp esbuild",
Expand All @@ -252,5 +246,8 @@
"release:minor": "VSCE_RELEASE_TYPE=minor yarn run -T ts-node ./scripts/release.ts",
"release:patch": "VSCE_RELEASE_TYPE=patch yarn run -T ts-node ./scripts/release.ts",
"release:pre": "VSCE_RELEASE_TYPE=prerelease yarn run -T ts-node ./scripts/release.ts"
},
"devDependencies": {
"vsce": "^2.7.0"
}
}
3 changes: 2 additions & 1 deletion client/vscode/scripts/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import fs from 'fs'
const originalPackageJson = fs.readFileSync('package.json').toString()

try {
childProcess.execSync('yarn build-inline-extensions && yarn build', { stdio: 'inherit' })
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const packageJson: any = JSON.parse(originalPackageJson)
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
packageJson.name = 'sourcegraph'
fs.writeFileSync('package.json', JSON.stringify(packageJson))

childProcess.execSync('yarn vsce package --yarn --allow-star-activation -o dist', { stdio: 'inherit' })
childProcess.execSync('vsce package --yarn --allow-star-activation -o dist', { stdio: 'inherit' })
} finally {
fs.writeFileSync('package.json', originalPackageJson)
}
9 changes: 5 additions & 4 deletions client/vscode/scripts/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ const tokens = {
// Assume this is for testing purpose if tokens are not found
const hasTokens = tokens.vscode !== undefined && tokens.openvsx !== undefined
const commands = {
vscode_info: 'yarn vsce show sourcegraph.sourcegraph --json',
vscode_info: 'vsce show sourcegraph.sourcegraph --json',
// To publish to VS Code Marketplace
vscode_publish: `yarn vsce publish ${isPreRelease} --pat $VSCODE_MARKETPLACE_TOKEN --yarn --allow-star-activation`,
vscode_publish: `vsce publish ${isPreRelease} --pat $VSCODE_MARKETPLACE_TOKEN --yarn --allow-star-activation`,
// To package the extension without publishing
vscode_package: `yarn vsce package ${isPreRelease} --yarn --allow-star-activation`,
vscode_package: `vsce package ${isPreRelease} --yarn --allow-star-activation`,
// To publish to the open-vsx registry
openvsx_publish: 'yarn npx --yes ovsx publish --yarn -p $VSCODE_OPENVSX_TOKEN',
openvsx_publish: 'npx --yes ovsx publish --yarn -p $VSCODE_OPENVSX_TOKEN',
}
// Publish the extension with the correct extension name "sourcegraph"
try {
childProcess.execSync('yarn build-inline-extensions && yarn build', { stdio: 'inherit' })
// Get the latest release version nubmer of the last release from VS Code Marketplace using the vsce cli tool
const response = childProcess.execSync(commands.vscode_info).toString()
/*
Expand Down
3 changes: 1 addition & 2 deletions client/vscode/src/backend/authenticatedUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@ export function observeAuthenticatedUser(secretStorage: vscode.SecretStorage): O
throw new Error('Not an authenticated user')
}
})
.catch(async error => {
.catch(error => {
console.error('core auth error', error)
await secretStorage.delete(scretTokenKey)
// TODO surface error?
authenticatedUsers.next(null)
})
Expand Down
30 changes: 7 additions & 23 deletions client/vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ import { openSourcegraphUriCommand } from './file-system/commands'
import { initializeSourcegraphFileSystem } from './file-system/initialize'
import { SourcegraphUri } from './file-system/SourcegraphUri'
import { Event } from './graphql-operations'
import { accessTokenSetting } from './settings/accessTokenSetting'
import { endpointRequestHeadersSetting, endpointSetting, setEndpoint } from './settings/endpointSetting'
import { accessTokenSetting, processOldToken } from './settings/accessTokenSetting'
import { endpointRequestHeadersSetting, endpointSetting } from './settings/endpointSetting'
import { invalidateContextOnSettingsChange } from './settings/invalidation'
import { LocalStorageService, SELECTED_SEARCH_CONTEXT_SPEC_KEY } from './settings/LocalStorageService'
import { watchUninstall } from './settings/uninstall'
import { createVSCEStateMachine, VSCEQueryState } from './state'
import { focusSearchPanel, openSourcegraphLinks, registerWebviews, copySourcegraphLinks } from './webview/commands'
import { scretTokenKey, SourcegraphAuthProvider } from './webview/platform/AuthProvider'
import { scretTokenKey, SourcegraphAuthActions, SourcegraphAuthProvider } from './webview/platform/AuthProvider'
/**
* See CONTRIBUTING docs for the Architecture Diagram
*/
Expand All @@ -41,6 +41,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
new SourcegraphAuthProvider(secretStorage)
)
)
await processOldToken(secretStorage)
const initialInstanceURL = endpointSetting()
const initialAccessToken = await secretStorage.get(scretTokenKey)
const createIfNone = initialAccessToken ? { createIfNone: true } : { createIfNone: false }
Expand Down Expand Up @@ -71,20 +72,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
sourcegraphURL: `${initialInstanceURL}/.api`,
session,
})
async function login(newtoken: string, newuri: string): Promise<void> {
try {
await secretStorage.store(scretTokenKey, newtoken)
await vscode.authentication.getSession(endpointSetting(), [], { forceNewSession: true })
await setEndpoint(newuri)
stateMachine.emit({ type: 'sourcegraph_url_change' })
} catch (error) {
console.error(error)
}
}
async function logout(): Promise<void> {
await secretStorage.delete(scretTokenKey)
stateMachine.emit({ type: 'sourcegraph_url_change' })
}
const authActions = new SourcegraphAuthActions(secretStorage)
const extensionCoreAPI: ExtensionCoreAPI = {
panelInitialized: panelId => initializedPanelIDs.next(panelId),
observeState: () => proxySubscribable(stateMachine.observeState()),
Expand All @@ -100,8 +88,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
openLink: uri => openSourcegraphLinks(uri),
copyLink: uri => copySourcegraphLinks(uri),
getAccessToken: accessTokenSetting(context.secrets),
removeAccessToken: () => logout(),
setEndpointUri: (accessToken, uri) => login(accessToken, uri),
removeAccessToken: () => authActions.logout(),
setEndpointUri: (accessToken, uri) => authActions.login(accessToken, uri),
reloadWindow: () => vscode.commands.executeCommand('workbench.action.reloadWindow'),
focusSearchPanel,
streamSearch,
Expand Down Expand Up @@ -131,8 +119,4 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
initializeCodeSharingCommands(context, eventSourceType, localStorageService)
// Watch for uninstall to log uninstall event
watchUninstall(eventSourceType, localStorageService)

// Add Sourcegraph to workspace recommendations (disabled for now as it was reported to violate
// VS Code's UX guidelines for notifications: https://code.visualstudio.com/api/ux-guidelines/notifications)
// recommendSourcegraph(localStorageService).catch(() => {})
}
13 changes: 13 additions & 0 deletions client/vscode/src/settings/accessTokenSetting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ import { scretTokenKey } from '../webview/platform/AuthProvider'
import { endpointHostnameSetting, endpointProtocolSetting } from './endpointSetting'
import { readConfiguration } from './readConfiguration'

// IMPORTANT: Call this function only once when extention is first activated
export async function processOldToken(secretStorage: vscode.SecretStorage): Promise<void> {
// Process the token that lives in user configuration
// Move them to secrets and then remove them by setting it as undefined
const storageToken = await secretStorage.get(scretTokenKey)
const oldToken = vscode.workspace.getConfiguration().get<string>('sourcegraph.accessToken') || ''
if (!storageToken && oldToken.length > 8) {
await secretStorage.store(scretTokenKey, oldToken)
await removeOldAccessTokenSetting()
}
return
}

export async function accessTokenSetting(secretStorage: vscode.SecretStorage): Promise<string> {
const currentToken = await secretStorage.get(scretTokenKey)
return currentToken || ''
Expand Down
9 changes: 7 additions & 2 deletions client/vscode/src/settings/endpointSetting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ export function endpointSetting(): string {
return removeEndingSlash(url)
}

export async function setEndpoint(newEndpoint: string | undefined): Promise<void> {
export async function setEndpoint(newEndpoint: string): Promise<void> {
const newEndpointURL = newEndpoint ? removeEndingSlash(newEndpoint) : 'https://sourcegraph.com'
await readConfiguration().update('url', newEndpointURL, vscode.ConfigurationTarget.Global)
const currentEndpointHostname = new URL(endpointSetting()).hostname
const newEndpointHostname = new URL(newEndpointURL).hostname
if (currentEndpointHostname !== newEndpointHostname) {
await readConfiguration().update('url', newEndpointURL)
}
return
}

export function endpointHostnameSetting(): string {
Expand Down
4 changes: 3 additions & 1 deletion client/vscode/src/settings/recommendations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* Disabled due to violation of the VS Code's UX guidelines for notifications
* To be revaluated in the future
* To be revaluated in the future: https://code.visualstudio.com/api/ux-guidelines/notifications
* This functions add Sourcegraph to workspace recommendations if haven't already
* eg: recommendSourcegraph(localStorageService).catch(() => {})
*/

import * as vscode from 'vscode'
Expand Down
13 changes: 0 additions & 13 deletions client/vscode/src/webview/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,6 @@ export function registerWebviews({
})
)

context.subscriptions.push(
vscode.commands.registerCommand('sourcegraph.auth', async (token: string, uri?: string) => {
// Get our PAT session.
await context.secrets.store(scretTokenKey, token)
const session = await vscode.authentication.getSession(uri || endpointSetting(), [], {
forceNewSession: true,
})
if (session) {
await vscode.window.showInformationMessage('Logged in sucessfully')
}
})
)

// Update `EventSource` Authorization header on access token / headers change.
// It will also be changed when the token has been changed --handled by Auth Provider
context.subscriptions.push(
Expand Down
34 changes: 23 additions & 11 deletions client/vscode/src/webview/platform/AuthProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import {
AuthenticationProvider,
AuthenticationProviderAuthenticationSessionsChangeEvent,
AuthenticationSession,
commands,
Disposable,
Event,
EventEmitter,
SecretStorage,
workspace,
} from 'vscode'

import polyfillEventSource from '@sourcegraph/shared/src/polyfills/vendor/eventSource'

import { removeOldAccessTokenSetting } from '../../settings/accessTokenSetting'
import { endpointRequestHeadersSetting, endpointSetting } from '../../settings/endpointSetting'
import { endpointRequestHeadersSetting, endpointSetting, setEndpoint } from '../../settings/endpointSetting'

export const scretTokenKey = 'SOURCEGRAPH_AUTH'

Expand Down Expand Up @@ -126,13 +125,26 @@ export class SourcegraphAuthProvider implements AuthenticationProvider, Disposab
}
}

// Call this function only once when extention is first activated
export async function processOldToken(secretStorage: SecretStorage): Promise<void> {
// Process the token that lives in user configuration
// Move them to secrets and then remove them by setting it as undefined
const oldToken = workspace.getConfiguration().get<string>('sourcegraph.accessToken') || ''
if (oldToken) {
await secretStorage.store(scretTokenKey, oldToken)
await removeOldAccessTokenSetting()
export class SourcegraphAuthActions {
private currentEndpoint = endpointSetting()

constructor(private readonly secretStorage: SecretStorage) {}

public async login(newtoken: string, newuri: string): Promise<void> {
try {
await this.secretStorage.store(scretTokenKey, newtoken)
if (this.currentEndpoint !== newuri) {
await setEndpoint(newuri)
}
return
} catch (error) {
console.error(error)
}
}

public async logout(): Promise<void> {
await this.secretStorage.delete(scretTokenKey)
await commands.executeCommand('workbench.action.reloadWindow')
return
}
}
13 changes: 11 additions & 2 deletions client/vscode/src/webview/sidebars/auth/AuthSidebarView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const AuthSidebarView: React.FunctionComponent<React.PropsWithChildren<Au
const [hostname, setHostname] = useState(instanceHostname)
const [accessToken, setAccessToken] = useState<string | undefined>('initial')
const [endpointUrl, setEndpointUrl] = useState(instanceURL)
const sourcegraphDotCom = 'https://www.sourcegraph.com'
const isSourcegraphDotCom = useMemo(() => {
const hostname = new URL(instanceURL).hostname
if (hostname === 'sourcegraph.com' || hostname === 'www.sourcegraph.com') {
Expand Down Expand Up @@ -90,6 +91,13 @@ export const AuthSidebarView: React.FunctionComponent<React.PropsWithChildren<Au
setEndpointUrl(event.target.value)
}, [])

const onInstanceTypeChange = useCallback(() => {
setUsePrivateInstance(!usePrivateInstance)
if (!usePrivateInstance) {
setEndpointUrl(sourcegraphDotCom)
}
}, [usePrivateInstance])

const validateAccessToken: React.FormEventHandler<HTMLFormElement> = (event): void => {
event.preventDefault()
if (state !== 'validating' && accessToken) {
Expand All @@ -111,9 +119,10 @@ export const AuthSidebarView: React.FunctionComponent<React.PropsWithChildren<Au
.toPromise()
currentAuthStateResult
.then(async ({ data }) => {
await extensionCoreAPI.setEndpointUri(accessToken, endpointUrl)
if (data?.currentUser) {
await extensionCoreAPI.setEndpointUri(accessToken, endpointUrl)
setState('success')
return
}
setState('failure')
return
Expand Down Expand Up @@ -270,7 +279,7 @@ export const AuthSidebarView: React.FunctionComponent<React.PropsWithChildren<Au
</Alert>
)}
<Text className="my-0">
<VSCodeLink onClick={() => setUsePrivateInstance(!usePrivateInstance)}>
<VSCodeLink onClick={() => onInstanceTypeChange()}>
{!usePrivateInstance ? 'Need to connect to a private instance?' : 'Not a private instance user?'}
</VSCodeLink>
</Text>
Expand Down
2 changes: 2 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6097,6 +6097,8 @@ __metadata:
"@sourcegraph/vscode@workspace:client/vscode":
version: 0.0.0-use.local
resolution: "@sourcegraph/vscode@workspace:client/vscode"
dependencies:
vsce: ^2.7.0
languageName: unknown
linkType: soft

Expand Down

0 comments on commit 8f65c35

Please sign in to comment.