Skip to content

Commit aaa462c

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

File tree

10 files changed

+82
-53
lines changed

10 files changed

+82
-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.2.2",
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(accessToken.user.id.toString());
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.2.2",
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: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ 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 { handleExpressResponse, handleExpressError } from "@jmondi/oauth2-server/dist/adapters/express";
1213
import * as express from "express";
1314
import { inject, injectable } from "inversify";
15+
import { URL } from "url";
1416
import { Config } from "../config";
1517
import { clientRepository, createAuthorizationServer } from "./oauth-authorization-server";
1618

@@ -58,10 +60,33 @@ export class OAuthController {
5860
const rt = req.query.redirect_uri?.toString();
5961
if (!rt || !rt.startsWith("http://127.0.0.1:")) {
6062
log.error(`/oauth/authorize: invalid returnTo URL: "${rt}"`);
63+
}
64+
65+
const client = await clientRepository.getByIdentifier(clientID);
66+
if (client) {
67+
if (typeof req.query.redirect_uri !== "string") {
68+
log.error(req.query.redirect_uri ? "Missing redirect URI" : "Invalid format of redirect URI");
69+
res.sendStatus(400);
70+
return false;
71+
}
72+
73+
const normalizedRedirectUri = new URL(req.query.redirect_uri);
74+
normalizedRedirectUri.search = "";
75+
76+
if (!client.redirectUris.some((u) => new URL(u).toString() === normalizedRedirectUri.toString())) {
77+
log.error(`/oauth/authorize: invalid returnTo URL: "${req.query.redirect_uri}"`);
78+
res.sendStatus(400);
79+
return false;
80+
}
81+
} else {
82+
log.error(`/oauth/authorize unknown client id: "${clientID}"`);
6183
res.sendStatus(400);
6284
return false;
6385
}
64-
res.redirect(`${rt}/?approved=no`);
86+
87+
const redirectUri = new URL(req.query.redirect_uri);
88+
redirectUri.searchParams.append("approved", "no");
89+
res.redirect(redirectUri.toString());
6590
return false;
6691
} else if (wasApproved == "yes") {
6792
const additionalData = (user.additionalData = user.additionalData || {});
@@ -133,53 +158,23 @@ export class OAuthController {
133158

134159
// Return the HTTP redirect response
135160
const oauthResponse = await authorizationServer.completeAuthorizationRequest(authRequest);
136-
return handleResponse(req, res, oauthResponse);
161+
return handleExpressResponse(res, oauthResponse);
137162
} catch (e) {
138-
handleError(e, res);
163+
handleExpressError(e, res);
139164
}
140165
});
141166

142167
router.post("/oauth/token", async (req: express.Request, res: express.Response) => {
143168
const response = new OAuthResponse(res);
144169
try {
145170
const oauthResponse = await authorizationServer.respondToAccessTokenRequest(req, response);
146-
return handleResponse(req, res, oauthResponse);
171+
return handleExpressResponse(res, oauthResponse);
147172
} catch (e) {
148-
handleError(e, res);
173+
handleExpressError(e, res);
149174
return;
150175
}
151176
});
152177

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-
183178
return router;
184179
}
185180
}

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.2.2":
1731+
version "2.2.2"
1732+
resolved "https://registry.yarnpkg.com/@jmondi/oauth2-server/-/oauth2-server-2.2.2.tgz#e99b6edcd068c1a58423e2e8e94eeb0acb048b39"
1733+
integrity sha512-U9038EvDQJwc6SUxGjNfP1nhcyIzUuo4MLDBjWEp4ieuoyMYFBlMtmUbXcIEvvlwWQSXneUm7+TcTBcNHKrE8w==
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)