Add the ability to provide a GitHub token

To test install the GitHub PR extension and start code-server with GITHUB_TOKEN
or set github-auth in the config file.  The extension should be authenticated.

Index: code-server/lib/vscode/src/vs/platform/credentials/node/credentialsMainService.ts
===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/credentials/node/credentialsMainService.ts
+++ code-server/lib/vscode/src/vs/platform/credentials/node/credentialsMainService.ts
@@ -5,9 +5,18 @@
 
 import { InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials';
 import { ILogService } from 'vs/platform/log/common/log';
-import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
+import { IServerEnvironmentService } from 'vs/server/node/serverEnvironmentService';
 import { IProductService } from 'vs/platform/product/common/productService';
 import { BaseCredentialsMainService, KeytarModule } from 'vs/platform/credentials/common/credentialsMainService';
+import { generateUuid } from 'vs/base/common/uuid';
+import { equals as arrayEquals } from 'vs/base/common/arrays';
+
+interface IToken {
+	accessToken: string
+	account?: { label: string }
+	id: string
+	scopes: string[]
+}
 
 export class CredentialsWebMainService extends BaseCredentialsMainService {
 	// Since we fallback to the in-memory credentials provider, we do not need to surface any Keytar load errors
@@ -16,10 +25,15 @@ export class CredentialsWebMainService e
 
 	constructor(
 		@ILogService logService: ILogService,
-		@INativeEnvironmentService private readonly environmentMainService: INativeEnvironmentService,
+		@IServerEnvironmentService private readonly environmentMainService: IServerEnvironmentService,
 		@IProductService private readonly productService: IProductService,
 	) {
 		super(logService);
+		if (this.environmentMainService.args["github-auth"]) {
+			this.storeGitHubToken(this.environmentMainService.args["github-auth"]).catch((error) => {
+				this.logService.error('Failed to store provided GitHub token', error)
+			})
+		}
 	}
 
 	// If the credentials service is running on the server, we add a suffix -server to differentiate from the location that the
@@ -48,4 +62,59 @@ export class CredentialsWebMainService e
 		}
 		return this._keytarCache;
 	}
+
+	private async storeGitHubToken(githubToken: string): Promise<void> {
+		const extensionId = 'vscode.github-authentication';
+		const service = `${await this.getSecretStoragePrefix()}${extensionId}`;
+		const account = 'github.auth';
+		const scopes = [['read:user', 'user:email', 'repo']]
+
+		// Oddly the scopes need to match exactly so we cannot just have one token
+		// with all the scopes, instead we have to duplicate the token for each
+		// expected set of scopes.
+		const tokens: IToken[] = scopes.map((scopes) => ({
+			id: generateUuid(),
+			scopes: scopes.sort(), // Sort for comparing later.
+			accessToken: githubToken,
+		}));
+
+		const raw = await this.getPassword(service, account)
+
+		let existing: {
+			content: IToken[]
+		} | undefined;
+
+		if (raw) {
+			try {
+				const json = JSON.parse(raw);
+				json.content = JSON.parse(json.content);
+				existing = json;
+			} catch (error) {
+				this.logService.error('Failed to parse existing GitHub credentials', error)
+			}
+		}
+
+		// Keep tokens for account and scope combinations we do not have in case
+		// there is an extension that uses scopes we have not accounted for (in
+		// these cases the user will need to manually authenticate the extension
+		// through the UI) or the user has tokens for other accounts.
+		if (existing?.content) {
+			existing.content = existing.content.filter((existingToken) => {
+				const scopes = existingToken.scopes.sort();
+				return !(tokens.find((token) => {
+					return arrayEquals(scopes, token.scopes)
+						&& token.account?.label === existingToken.account?.label;
+				}))
+			})
+		}
+
+		return this.setPassword(service, account, JSON.stringify({
+			extensionId,
+			...(existing || {}),
+			content: JSON.stringify([
+				...tokens,
+				...(existing?.content || []),
+			])
+		}));
+	}
 }
