Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions src/content/docs/ko/guides/authentication.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -149,24 +149,39 @@ const session = await auth.api.getSession({
<p>{session.user?.name}</p>
```

미들웨어와 `auth` 객체를 사용하여 경로를 보호할 수도 있습니다. 다음 예시는 로그인한 대시보드 경로에 액세스하려는 사용자가 인증되었는지 확인하고 인증되지 않은 경우 홈 페이지로 리디렉션합니다.
`auth` 객체를 사용하여 경로를 보호할 수도 있습니다. 다음 예시는 [Astro의 고급 라우팅](/ko/guides/routing/#고급-라우팅)과 [Hono](https://hono.dev/)를 사용하여 `/dashboard` 아래의 모든 경로에 인증된 세션을 요구하고, 그렇지 않으면 홈 페이지로 리디렉션합니다:

```ts title="src/middleware.ts"
import { auth } from "../../../auth"; // Better Auth 인스턴스를 가져옵니다.
import { defineMiddleware } from "astro:middleware";

export const onRequest = defineMiddleware(async (context, next) => {
const isAuthed = await auth.api
.getSession({
headers: context.request.headers,
})
if (context.url.pathname === "/dashboard" && !isAuthed) {
return context.redirect("/");
```ts title="src/fetch.ts"
import { Hono } from "hono";
import { astro } from "astro/hono";
import { auth } from "../auth"; // Better Auth 인스턴스를 가져옵니다.

const app = new Hono();

// /dashboard 아래의 모든 경로를 보호합니다.
app.use("/dashboard", requireAuth);
app.use("/dashboard/*", requireAuth);

// 그 외의 모든 요청에는 Astro의 내장 파이프라인을 실행합니다.
app.use(astro());

export default app;

async function requireAuth(c, next) {
const session = await auth.api.getSession({ headers: c.req.raw.headers });
if (!session) {
return c.redirect("/");
}
return next();
});
}
```

:::caution
미들웨어가 확인하는 공개 pathname이 Astro가 내부적으로 매칭하는 라우트와 항상 동일한 것은 아닙니다. 구성된 `base`, URL 인코딩, 중복 슬래시로 인해 두 값이 달라질 수 있습니다. 공격자는 이 차이를 악용하여 검사에서 인식하지 못하는 pathname으로 보호된 라우트에 접근할 수 있습니다.

`context.url.pathname`을 문자열(예: `context.url.pathname === "/dashboard"` 또는 `context.url.pathname.startsWith("/dashboard")`)과 비교하여 요청을 승인하지 마세요. 대신 라우터에 경로 매칭을 맡겨 접근을 제한하세요.
:::

### 다음 단계

- [Better Auth + Astro 가이드](https://www.better-auth.com/docs/integrations/astro)
Expand Down
51 changes: 38 additions & 13 deletions src/content/docs/ko/guides/routing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -648,9 +648,9 @@ export default defineConfig({

#### `astro()`를 사용하여 전체 파이프라인 실행하기

Astro의 기본 라우팅 동작을 유지하면서 그 전후에 사용자 지정 로직이 필요한 경우 [`astro()`](/ko/reference/modules/astro-fetch/#astro)를 사용하세요. 이 접근 방식은 기본 파이프라인 순서를 유지하며 한 곳에서 전처리 및 후처리 로직을 추가할 수 있게 해줍니다. 인증 가드 추가, 요청 로깅, 사용자 지정 헤더 설정 등 많은 사용 사례에서 `astro()`만으로 충분합니다.
Astro의 기본 라우팅 동작을 유지하면서 그 전후에 사용자 지정 로직이 필요한 경우 [`astro()`](/ko/reference/modules/astro-fetch/#astro)를 사용하세요. 이 접근 방식은 기본 파이프라인 순서를 유지하며 한 곳에서 전처리 및 후처리 로직을 추가할 수 있게 해줍니다. 요청 로깅, 사용자 지정 헤더 설정 등 많은 사용 사례에서 `astro()`만으로 충분합니다.

다음 예시는 Astro 파이프라인을 실행하기 전에 사용자가 대시보드에 액세스할 수 있는지 확인하고, Astro 실행이 완료된 후 응답에 사용자 지정 헤더를 추가합니다:
다음 예시는 Astro 파이프라인을 실행하기 전에 수신되는 각 요청을 기록하고, Astro 실행이 완료된 후 응답에 사용자 지정 헤더를 추가합니다:

```ts title="src/fetch.ts"
import { FetchState, astro } from 'astro/fetch';
Expand All @@ -659,17 +659,8 @@ export default {
async fetch(request: Request): Promise<Response> {
const state = new FetchState(request);

// 사용자 지정 전처리 로직 (Astro 핸들러 실행 전)
const url = new URL(request.url);
if (url.pathname.startsWith('/dashboard')) {
const cookie = request.headers.get('cookie') ?? '';
if (!cookie.includes('session=')) {
return new Response(null, {
status: 302,
headers: { Location: '/login' },
});
}
}
// 사용자 지정 전처리 로직 (모든 Astro 핸들러 실행 전)
console.log(`${request.method} ${new URL(request.url).pathname}`);

const response = await astro(state);

Expand All @@ -680,6 +671,12 @@ export default {
};
```

:::caution
수신 요청의 pathname이 Astro가 내부적으로 매칭하는 라우트와 항상 동일한 것은 아니므로, `url.pathname.startsWith('/dashboard')`와 같은 검사는 우회될 수 있습니다.

요청 pathname을 문자열과 비교하여 요청을 승인하는 데 전처리를 사용하지 마세요. 라우트를 보호하려면 [Hono](#hono와-함께-사용하기)와 같이 경로 매칭을 담당하는 라우터로 라우트를 매칭하세요.
:::

#### 개별 핸들러 조합하기

파이프라인 실행 순서를 더 세밀하게 제어하거나 특정 기능을 제외하고 싶을 때, [`astro/fetch`](/ko/reference/modules/astro-fetch/)에서 제공하는 개별 핸들러 함수를 조합할 수 있습니다. 각 핸들러는 일치하는 라우트, 쿠키, 세션 등 요청별 데이터를 추적하는 [`FetchState` 객체](/ko/reference/modules/astro-fetch/#fetchstate)를 기반으로 동작합니다. 핸들러를 원하는 순서로 호출하고 단계 사이에 사용자 지정 로직을 삽입할 수 있습니다.
Expand Down Expand Up @@ -733,3 +730,31 @@ app.use(i18n());

export default app;
```

보호할 Hono 라우트에 인증 검사를 등록하여 보호된 라우트를 지킬 수도 있습니다:

```ts title="src/fetch.ts"
import { Hono } from 'hono';
import { actions, middleware, pages, i18n } from 'astro/hono';
import { isLoggedIn } from './lib/auth';

const app = new Hono();

// /dashboard 아래의 모든 경로를 보호합니다.
app.use('/dashboard', requireAuth);
app.use('/dashboard/*', requireAuth);

app.use(actions());
app.use(middleware());
app.use(pages());
app.use(i18n());

export default app;

async function requireAuth(c, next) {
if (!(await isLoggedIn(c.req.raw))) {
return c.redirect('/login');
}
return next();
}
```
Loading