Skip to content

Commit 71c83cb

Browse files
akosyakovfiliptronicek
authored andcommitted
[server] add vscode(-insiders) ouath2 clients
1 parent 3ecb0f7 commit 71c83cb

File tree

10 files changed

+78
-53
lines changed

10 files changed

+78
-53
lines changed

components/gitpod-db/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
],
2525
"dependencies": {
2626
"@gitpod/gitpod-protocol": "0.1.5",
27-
"@jmondi/oauth2-server": "^1.1.0",
27+
"@jmondi/oauth2-server": "^2.1.0",
2828
"mysql": "^2.18.1",
2929
"reflect-metadata": "^0.1.13",
3030
"the-big-username-blacklist": "^1.5.2",

components/gitpod-db/src/typeorm/auth-code-repository-db.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class AuthCodeRepositoryDB implements OAuthAuthCodeRepository {
3333
return (await this.getEntityManager()).getRepository<DBOAuthAuthCodeEntry>(DBOAuthAuthCodeEntry);
3434
}
3535

36-
public async getByIdentifier(authCodeCode: string): Promise<OAuthAuthCode> {
36+
public async getByIdentifier(authCodeCode: string): Promise<DBOAuthAuthCodeEntry> {
3737
const authCodeRepo = await this.getOauthAuthCodeRepo();
3838
let authCodes = await authCodeRepo.find({ code: authCodeCode });
3939
authCodes = authCodes.filter((te) => new Date(te.expiresAt).getTime() > Date.now());
@@ -54,7 +54,7 @@ export class AuthCodeRepositoryDB implements OAuthAuthCodeRepository {
5454
scopes: scopes,
5555
};
5656
}
57-
public async persist(authCode: OAuthAuthCode): Promise<void> {
57+
public async persist(authCode: DBOAuthAuthCodeEntry): Promise<void> {
5858
const authCodeRepo = await this.getOauthAuthCodeRepo();
5959
authCodeRepo.save(authCode);
6060
}

components/gitpod-db/src/typeorm/entity/db-oauth-auth-code.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License-AGPL.txt in the project root for license information.
55
*/
66

7-
import { OAuthAuthCode, OAuthClient, OAuthScope } from "@jmondi/oauth2-server";
7+
import { CodeChallengeMethod, OAuthAuthCode, OAuthClient, OAuthScope } from "@jmondi/oauth2-server";
88
import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
99
import { Transformer } from "../transformer";
1010
import { DBUser } from "./db-user";
@@ -38,7 +38,7 @@ export class DBOAuthAuthCodeEntry implements OAuthAuthCode {
3838
type: "varchar",
3939
length: 10,
4040
})
41-
codeChallengeMethod: string;
41+
codeChallengeMethod: CodeChallengeMethod
4242

4343
@Column({
4444
type: "timestamp",

components/gitpod-db/src/typeorm/user-db-impl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ export class TypeORMUserDBImpl implements UserDB {
486486
} else {
487487
var user: MaybeUser;
488488
if (accessToken.user) {
489-
user = await this.findUserById(accessToken.user.id);
489+
user = await this.findUserById(String(accessToken.user.id))
490490
}
491491
dbToken = {
492492
tokenHash,

components/proxy/conf/Caddyfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ https://{$GITPOD_DOMAIN} {
210210
@codesync path /code-sync*
211211
handle @codesync {
212212
gitpod.cors_origin {
213-
base_domain {$GITPOD_DOMAIN}
213+
any_domain true
214214
}
215215

216216
import compression

components/proxy/plugins/corsorigin/cors_origin.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func init() {
2727

2828
// CorsOrigin implements an HTTP handler that generates a valid CORS Origin value
2929
type CorsOrigin struct {
30+
AnyDomain bool `json:"any_domain,omitempty"`
3031
BaseDomain string `json:"base_domain,omitempty"`
3132
Debug bool `json:"debug,omitempty"`
3233
}
@@ -50,8 +51,14 @@ var (
5051

5152
// ServeHTTP implements caddyhttp.MiddlewareHandler.
5253
func (m CorsOrigin) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
54+
var allowedOrigins []string
55+
if m.AnyDomain {
56+
allowedOrigins = []string{"*"}
57+
} else {
58+
allowedOrigins = []string{"*." + m.BaseDomain}
59+
}
5360
c := cors.New(cors.Options{
54-
AllowedOrigins: []string{"*." + m.BaseDomain},
61+
AllowedOrigins: allowedOrigins,
5562
AllowedMethods: allowedMethods,
5663
AllowedHeaders: allowedHeaders,
5764
ExposedHeaders: exposeHeaders,
@@ -84,6 +91,13 @@ func (m *CorsOrigin) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
8491
}
8592

8693
switch key {
94+
case "any_domain":
95+
b, err := strconv.ParseBool(value)
96+
if err != nil {
97+
return d.Errf("invalid boolean value for subdirective any_domain '%s'", value)
98+
}
99+
100+
m.AnyDomain = b
87101
case "base_domain":
88102
m.BaseDomain = value
89103
case "debug":
@@ -98,7 +112,7 @@ func (m *CorsOrigin) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
98112
}
99113
}
100114

101-
if m.BaseDomain == "" {
115+
if !m.AnyDomain && m.BaseDomain == "" {
102116
return fmt.Errorf("Please configure the base_domain subdirective")
103117
}
104118

components/server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"@gitpod/ws-manager": "0.1.5",
4040
"@google-cloud/storage": "^5.6.0",
4141
"@improbable-eng/grpc-web-node-http-transport": "^0.14.0",
42-
"@jmondi/oauth2-server": "^1.1.0",
42+
"@jmondi/oauth2-server": "^2.1.0",
4343
"@octokit/rest": "18.6.1",
4444
"@probot/get-private-key": "^1.1.1",
4545
"amqplib": "^0.8.0",

components/server/src/oauth-server/db.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,30 @@ const jetBrainsGateway: OAuthClient = {
5757
],
5858
};
5959

60+
function createVSCodeClient(protocol: 'vscode' | 'vscode-insiders'): OAuthClient {
61+
return {
62+
id: protocol + '-' + 'gitpod',
63+
name: `VS Code${protocol === 'vscode-insiders' ? ' Insiders' : ''}: Gitpod extension`,
64+
redirectUris: [protocol + '://gitpod.gitpod-desktop/complete-gitpod-auth'],
65+
allowedGrants: ['authorization_code'],
66+
scopes: [
67+
{ name: "function:getGitpodTokenScopes" },
68+
{ name: "function:getLoggedInUser" },
69+
{ name: "function:accessCodeSyncStorage" },
70+
{ name: "resource:default" }
71+
],
72+
}
73+
}
74+
75+
const vscode = createVSCodeClient('vscode');
76+
const vscodeInsiders = createVSCodeClient('vscode-insiders');
77+
6078
export const inMemoryDatabase: InMemory = {
6179
clients: {
6280
[localClient.id]: localClient,
6381
[jetBrainsGateway.id]: jetBrainsGateway,
82+
[vscode.id]: vscode,
83+
[vscodeInsiders.id]: vscodeInsiders
6484
},
6585
tokens: {},
6686
scopes: {},

components/server/src/oauth-server/oauth-controller.ts

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ import { AuthCodeRepositoryDB } from "@gitpod/gitpod-db/lib/typeorm/auth-code-re
88
import { UserDB } from "@gitpod/gitpod-db/lib/user-db";
99
import { User } from "@gitpod/gitpod-protocol";
1010
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
11-
import { OAuthException, OAuthRequest, OAuthResponse } from "@jmondi/oauth2-server";
11+
import { OAuthRequest, OAuthResponse } from "@jmondi/oauth2-server";
12+
import {
13+
handleExpressResponse,
14+
handleExpressError,
15+
} from "@jmondi/oauth2-server/dist/adapters/express"
1216
import * as express from "express";
1317
import { inject, injectable } from "inversify";
18+
import { URL } from 'url';
1419
import { Config } from "../config";
1520
import { clientRepository, createAuthorizationServer } from "./oauth-authorization-server";
1621

@@ -58,10 +63,26 @@ export class OAuthController {
5863
const rt = req.query.redirect_uri?.toString();
5964
if (!rt || !rt.startsWith("http://127.0.0.1:")) {
6065
log.error(`/oauth/authorize: invalid returnTo URL: "${rt}"`);
66+
}
67+
68+
const client = await clientRepository.getByIdentifier(clientID);
69+
if (client) {
70+
const normalizedRedirectUri = new URL(req.query.redirect_uri);
71+
normalizedRedirectUri.search = '';
72+
73+
if (!client.redirectUris.some(u => new URL(u).toString() === normalizedRedirectUri.toString())) {
74+
log.error(`/oauth/authorize: invalid returnTo URL: "${req.query.redirect_uri}"`)
75+
res.sendStatus(400);
76+
return false;
77+
}
78+
} else {
79+
log.error(`/oauth/authorize unknown client id: "${clientID}"`)
6180
res.sendStatus(400);
6281
return false;
6382
}
64-
res.redirect(`${rt}/?approved=no`);
83+
const redirectUri = new URL(req.query.redirect_uri);
84+
redirectUri.searchParams.append('approved', 'no');
85+
res.redirect(redirectUri.toString());
6586
return false;
6687
} else if (wasApproved == "yes") {
6788
const additionalData = (user.additionalData = user.additionalData || {});
@@ -133,53 +154,23 @@ export class OAuthController {
133154

134155
// Return the HTTP redirect response
135156
const oauthResponse = await authorizationServer.completeAuthorizationRequest(authRequest);
136-
return handleResponse(req, res, oauthResponse);
157+
return handleExpressResponse(res, oauthResponse);
137158
} catch (e) {
138-
handleError(e, res);
159+
handleExpressError(e, res);
139160
}
140161
});
141162

142163
router.post("/oauth/token", async (req: express.Request, res: express.Response) => {
143164
const response = new OAuthResponse(res);
144165
try {
145166
const oauthResponse = await authorizationServer.respondToAccessTokenRequest(req, response);
146-
return handleResponse(req, res, oauthResponse);
167+
return handleExpressResponse(res, oauthResponse);
147168
} catch (e) {
148-
handleError(e, res);
169+
handleExpressError(e, res);
149170
return;
150171
}
151172
});
152173

153-
function handleError(e: Error | undefined, res: express.Response) {
154-
if (e instanceof OAuthException) {
155-
res.status(e.status);
156-
res.send({
157-
status: e.status,
158-
message: e.message,
159-
stack: e.stack,
160-
});
161-
return;
162-
}
163-
// Generic error
164-
res.status(500);
165-
res.send({
166-
err: e,
167-
});
168-
}
169-
170-
function handleResponse(req: express.Request, res: express.Response, response: OAuthResponse) {
171-
if (response.status === 302) {
172-
if (!response.headers.location) {
173-
throw new Error("missing redirect location");
174-
}
175-
res.set(response.headers);
176-
res.redirect(response.headers.location);
177-
} else {
178-
res.set(response.headers);
179-
res.status(response.status).send(response.body);
180-
}
181-
}
182-
183174
return router;
184175
}
185176
}

yarn.lock

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,10 +1727,10 @@
17271727
"@types/yargs" "^16.0.0"
17281728
chalk "^4.0.0"
17291729

1730-
"@jmondi/oauth2-server@^1.1.0":
1731-
version "1.1.1"
1732-
resolved "https://registry.yarnpkg.com/@jmondi/oauth2-server/-/oauth2-server-1.1.1.tgz#cb2530c17e2c7db3cc632bb68ec0f38a54c7ae1c"
1733-
integrity sha512-my3776n6TDsJQJ+nONG52VNgTQ7veH9lo4kb/AAWt9Rko6VBuMxOb/KxcYdkDrpOznJ036+tVveuhY7zSJjGYg==
1730+
"@jmondi/oauth2-server@^2.1.0":
1731+
version "2.1.0"
1732+
resolved "https://registry.yarnpkg.com/@jmondi/oauth2-server/-/oauth2-server-2.1.0.tgz#ffa10dd8b9c5c8b480824bf0ecf104d5d00ec4a7"
1733+
integrity sha512-R6zxiKCC0MyAk3M9rV8gM0bDqvXNZgiDLTriefkfsZIXZoVw52W2X8usf5Y8qSwnmdP4u3ijXbb2fSUAcSbDdA==
17341734
dependencies:
17351735
jsonwebtoken "^8.5.1"
17361736
ms "^2.1.3"
@@ -12035,9 +12035,9 @@ mz@^2.4.0:
1203512035
thenify-all "^1.0.0"
1203612036

1203712037
nan@^2.12.1, nan@^2.13.2:
12038-
version "2.15.0"
12039-
resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
12040-
integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
12038+
version "2.14.1"
12039+
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
12040+
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
1204112041

1204212042
nanoid@^3.1.30:
1204312043
version "3.1.30"

0 commit comments

Comments
 (0)