Skip to content

Commit a63c62c

Browse files
committed
[server] add Unauthenticated decorator for public-api
1 parent cb32240 commit a63c62c

File tree

3 files changed

+68
-8
lines changed

3 files changed

+68
-8
lines changed

components/server/src/api/server.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { APITeamsService as TeamsServiceAPI } from "./teams";
4141
import { APIUserService as UserServiceAPI } from "./user";
4242
import { WorkspaceServiceAPI } from "./workspace-service-api";
4343
import { AuthProviderServiceAPI } from "./auth-provider-service-api";
44+
import { Unauthenticated } from "./unauthenticated";
4445

4546
decorate(injectable(), PublicAPIConverter);
4647

@@ -214,9 +215,21 @@ export class API {
214215

215216
const apply = async <T>(): Promise<T> => {
216217
const subjectId = await self.verify(context);
217-
await rateLimit(subjectId);
218-
context.user = await self.ensureFgaMigration(subjectId);
218+
const isAuthenticated = !!subjectId;
219+
const requiresAuthentication = !Unauthenticated.get(target, prop);
219220

221+
if (!isAuthenticated && requiresAuthentication) {
222+
throw new ConnectError("unauthenticated", Code.Unauthenticated);
223+
}
224+
225+
if (isAuthenticated) {
226+
await rateLimit(subjectId);
227+
context.user = await self.ensureFgaMigration(subjectId);
228+
}
229+
230+
// TODO(at) if unauthenticated, we still need to apply enforece a rate limit
231+
232+
// actually call the RPC handler
220233
return Reflect.apply(target[prop as any], target, args);
221234
};
222235
if (grpc_type === "unary" || grpc_type === "client_stream") {
@@ -250,14 +263,16 @@ export class API {
250263
};
251264
}
252265

253-
private async verify(context: HandlerContext): Promise<string> {
266+
private async verify(context: HandlerContext): Promise<string | undefined> {
254267
const cookieHeader = (context.requestHeader.get("cookie") || "") as string;
255-
const claims = await this.sessionHandler.verifyJWTCookie(cookieHeader);
256-
const subjectId = claims?.sub;
257-
if (!subjectId) {
258-
throw new ConnectError("unauthenticated", Code.Unauthenticated);
268+
try {
269+
const claims = await this.sessionHandler.verifyJWTCookie(cookieHeader);
270+
const subjectId = claims?.sub;
271+
return subjectId;
272+
} catch (error) {
273+
log.warn("Failed to authenticate user with JWT Session", error);
274+
return undefined;
259275
}
260-
return subjectId;
261276
}
262277

263278
private async ensureFgaMigration(userId: string): Promise<User> {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License.AGPL.txt in the project root for license information.
5+
*/
6+
7+
import * as chai from "chai";
8+
import { Unauthenticated } from "./unauthenticated";
9+
10+
const expect = chai.expect;
11+
12+
class Foo {
13+
@Unauthenticated()
14+
async fooUnauthenticated() {}
15+
16+
async foo() {}
17+
}
18+
19+
describe("Unauthenticated decorator", function () {
20+
const foo = new Foo();
21+
22+
it("function is decorated", function () {
23+
expect(Unauthenticated.get(foo, "fooUnauthenticated")).to.be.true;
24+
});
25+
it("function is not decorated", function () {
26+
expect(Unauthenticated.get(foo, "foo")).to.be.false;
27+
});
28+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License.AGPL.txt in the project root for license information.
5+
*/
6+
7+
const UNAUTHENTICATED_METADATA_KEY = Symbol("Unauthenticated");
8+
9+
export function Unauthenticated() {
10+
return Reflect.metadata(UNAUTHENTICATED_METADATA_KEY, true);
11+
}
12+
13+
export namespace Unauthenticated {
14+
export function get(target: Object, properyKey: string | symbol): boolean {
15+
return !!Reflect.getMetadata(UNAUTHENTICATED_METADATA_KEY, target, properyKey);
16+
}
17+
}

0 commit comments

Comments
 (0)