Skip to content

Commit

Permalink
fix: Auth tokens for service
Browse files Browse the repository at this point in the history
  • Loading branch information
matvp91 committed Nov 7, 2024
1 parent 7f0b01e commit d5558b9
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 45 deletions.
69 changes: 44 additions & 25 deletions packages/api/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,53 @@ import { Elysia, t } from "elysia";
import { env } from "./env";
import { DeliberateError } from "./errors";

export const authUserJwt = jwt({
name: "authUserJwt",
type Authed =
| {
type: "user";
id: number;
}
| { type: "service" };

interface AuthOptions {
user?: boolean;
service?: boolean;
}

export const jwtUser = jwt({
name: "jwtUser",
schema: t.Object({
id: t.Number(),
}),
secret: env.SUPER_SECRET,
});

export const authUser = new Elysia()
.use(bearer())
.use(authUserJwt)
.derive({ as: "scoped" }, async ({ bearer, authUserJwt }) => {
if (!bearer) {
throw new DeliberateError({ type: "ERR_UNAUTHORIZED" });
}
const token = await authUserJwt.verify(bearer);
if (!token) {
throw new DeliberateError({ type: "ERR_USER_INVALID_TOKEN" });
}
return { user: token };
});

export const authService = new Elysia().derive(
{ as: "scoped" },
async ({ headers }) => {
if (headers["x-api-key"] !== env.SUPER_SECRET) {
throw new DeliberateError({ type: "ERR_UNAUTHORIZED" });
}
return { service: {} };
},
);
export function auth(options: AuthOptions = {}) {
return new Elysia()
.use(bearer())
.use(jwtUser)
.derive({ as: "scoped" }, async ({ headers, bearer, jwtUser }) => {
let authed: Authed | undefined;

if (options.user) {
const token = await jwtUser.verify(bearer);
if (token) {
authed = {
type: "user",
id: token.id,
};
}
}

if (options.service) {
if (headers["x-api-key"] === env.SUPER_SECRET) {
authed = { type: "service" };
}
}

if (!authed) {
throw new DeliberateError({ type: "ERR_UNAUTHORIZED" });
}

return { authed };
});
}
5 changes: 1 addition & 4 deletions packages/api/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import type { ValidationError } from "elysia";

interface ValidationField {
path: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
value: unknown;
message: string;
}

Expand All @@ -16,7 +15,6 @@ interface ApiErrorCode {
ERR_UNAUTHORIZED: never;
ERR_NOT_FOUND: never;
ERR_USER_INVALID_CREDENTIALS: never;
ERR_USER_INVALID_TOKEN: never;
ERR_STORAGE_NO_FILE_PREVIEW: never;
}

Expand All @@ -25,7 +23,6 @@ const statusMap: Record<keyof ApiErrorCode, number> = {
ERR_VALIDATION: 403,
ERR_NOT_FOUND: 404,
ERR_UNAUTHORIZED: 401,
ERR_USER_INVALID_TOKEN: 401,
ERR_USER_INVALID_CREDENTIALS: 401,
ERR_STORAGE_NO_FILE_PREVIEW: 400,
};
Expand Down
3 changes: 3 additions & 0 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const app = new Elysia()
.use(
swagger({
scalarVersion: "1.25.50",
scalarConfig: {
defaultOpenAllTags: true,
},
documentation: {
info: {
title: "Superstreamer API",
Expand Down
5 changes: 2 additions & 3 deletions packages/api/src/routes/assets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Elysia, t } from "elysia";
import { authService, authUser } from "../auth";
import { auth } from "../auth";
import {
assetsFilterSchema,
getAssets,
Expand All @@ -8,8 +8,7 @@ import {
import { AssetSchema } from "../types";

export const assets = new Elysia()
.use(authUser)
.use(authService)
.use(auth({ user: true, service: true }))
.get(
"/assets",
async ({ query }) => {
Expand Down
5 changes: 2 additions & 3 deletions packages/api/src/routes/jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import {
} from "bolt";
import { AudioCodec, VideoCodec } from "bolt";
import { Elysia, t } from "elysia";
import { authService, authUser } from "../auth";
import { auth } from "../auth";
import { DeliberateError } from "../errors";
import { getJob, getJobLogs, getJobs } from "../repositories/jobs";
import { JobSchema } from "../types";

export const jobs = new Elysia()
.use(authUser)
.use(authService)
.use(auth({ user: true, service: true }))
.post(
"/transcode",
async ({ body }) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/routes/storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Elysia, t } from "elysia";
import { authUser } from "../auth";
import { auth } from "../auth";
import { DeliberateError } from "../errors";
import { StorageFileSchema, StorageFolderSchema } from "../types";
import {
Expand All @@ -9,7 +9,7 @@ import {
} from "../utils/s3";

export const storage = new Elysia()
.use(authUser)
.use(auth({ user: true }))
.get(
"/storage/folder",
async ({ query }) => {
Expand Down
8 changes: 4 additions & 4 deletions packages/api/src/routes/token.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Elysia, t } from "elysia";
import { authUserJwt } from "../auth";
import { jwtUser } from "../auth";
import { DeliberateError } from "../errors";
import { getUserIdByCredentials } from "../repositories/users";

export const token = new Elysia().use(authUserJwt).post(
export const token = new Elysia().use(jwtUser).post(
"/token",
async ({ authUserJwt, body }) => {
async ({ jwtUser, body }) => {
const id = await getUserIdByCredentials(body.username, body.password);
if (id === null) {
throw new DeliberateError({ type: "ERR_USER_INVALID_CREDENTIALS" });
}
return await authUserJwt.sign({
return await jwtUser.sign({
id,
});
},
Expand Down
11 changes: 7 additions & 4 deletions packages/api/src/routes/user.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Elysia } from "elysia";
import { authUser } from "../auth";
import { auth } from "../auth";
import { getUser } from "../repositories/users";
import { UserSchema } from "../types";

export const user = new Elysia().use(authUser).get(
export const user = new Elysia().use(auth({ user: true })).get(
"/user",
async ({ user }) => {
return await getUser(user.id);
async ({ authed }) => {
if (authed.type !== "user") {
throw new Error("Invalid authed");
}
return await getUser(authed.id);
},
{
detail: {
Expand Down

0 comments on commit d5558b9

Please sign in to comment.