콘텐츠로 이동

Scalekit & Astro

Scalekit은 B2B 및 AI 애플리케이션을 위해 구축된 인증 플랫폼입니다. 소셜 로그인, 엔터프라이즈 SSO, 매직 링크 등을 제공합니다 — 전체 OAuth 2.0 / OIDC 흐름을 관리하므로 로그인 UI를 구축할 필요 없이 토큰과 사용자 프로필을 얻을 수 있습니다. 단일 Scalekit 환경이 여러 애플리케이션(예: app.yourcompany.comdocs.yourcompany.com)을 지원하므로, 사용자가 한 번 인증하면 모든 속성에서 동일한 세션을 공유합니다.

  • Scalekit 계정과 환경이 필요합니다. 없다면 scalekit.com에 무료로 가입하여 새 환경을 생성할 수 있습니다.
  • 요청 시 렌더링을 위해 output: 'server'가 활성화된 Astro 프로젝트가 필요합니다.
  • Scalekit 환경의 자격 증명이 필요합니다. Scalekit 대시보드 Settings > API Credentials 섹션에서 찾을 수 있습니다.
    • SCALEKIT_ENVIRONMENT_URL: Scalekit 환경의 URL입니다.
    • SCALEKIT_CLIENT_ID: Scalekit 클라이언트 ID입니다.
    • SCALEKIT_CLIENT_SECRET: Scalekit 클라이언트 비밀입니다.
    • SCALEKIT_REDIRECT_URI: 로그인 후 Scalekit가 리디렉션할 콜백 URL입니다 (예: http://localhost:4321/api/auth/callback). Scalekit 대시보드의 Settings > Redirects에서 등록하세요.

Astro 프로젝트에 Scalekit 자격 증명을 추가하려면 .env 파일에 다음을 추가하세요.

.env
SCALEKIT_ENVIRONMENT_URL=YOUR_SCALEKIT_ENVIRONMENT_URL
SCALEKIT_CLIENT_ID=YOUR_SCALEKIT_CLIENT_ID
SCALEKIT_CLIENT_SECRET=YOUR_SCALEKIT_CLIENT_SECRET
SCALEKIT_REDIRECT_URI=http://localhost:4321/api/auth/callback

이제 이러한 환경 변수를 프로젝트에서 사용할 수 있습니다.

환경 변수에 IntelliSense를 사용하려면 src/ 디렉터리의 env.d.ts 파일을 수정하거나 생성하여 다음을 추가하세요.

src/env.d.ts
/// <reference types="astro/client" />
interface ImportMetaEnv {
readonly SCALEKIT_ENVIRONMENT_URL: string;
readonly SCALEKIT_CLIENT_ID: string;
readonly SCALEKIT_CLIENT_SECRET: string;
readonly SCALEKIT_REDIRECT_URI: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
declare namespace App {
interface Locals {
user?: {
sub: string;
email?: string;
name?: string;
};
}
}
Astro의 환경 변수.env 파일에 대해 자세히 알아보세요.

프로젝트에 다음 파일이 포함되어야 합니다.

  • 디렉터리src/
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

Scalekit에 연결하려면 프로젝트에 @scalekit-sdk/node를 설치하세요.

터미널 창
npm install @scalekit-sdk/node

그 다음, src/ 디렉터리에 lib 폴더를 만들고 Scalekit 클라이언트 파일을 추가하세요.

src/lib/scalekit.ts
import { ScalekitClient } from "@scalekit-sdk/node";
export const scalekit = new ScalekitClient(
import.meta.env.SCALEKIT_ENVIRONMENT_URL,
import.meta.env.SCALEKIT_CLIENT_ID,
import.meta.env.SCALEKIT_CLIENT_SECRET,
);
export const REDIRECT_URI =
import.meta.env.SCALEKIT_REDIRECT_URI ?? "http://localhost:4321/api/auth/callback";

프로젝트에 다음 파일이 포함되어야 합니다.

  • 디렉터리src/
    • 디렉터리lib/
      • scalekit.ts
    • env.d.ts
  • .env
  • package.json

Scalekit은 표준 OAuth 2.0 / OIDC 리디렉션 흐름을 통해 인증을 처리합니다. 앱에서 사용자를 Scalekit으로 보내면, 사용자가 로그인하고 Scalekit이 권한 코드와 함께 콜백 URL로 리디렉션합니다. 그 다음 해당 권한 코드를 토큰으로 교환합니다. 이 가이드에서는 클라이언트 비밀과 함께 권한 코드 흐름을 사용하며, 이는 서버 측 렌더링 애플리케이션에 적합합니다.

프로젝트에 인증을 추가하려면 세 개의 서버 엔드포인트를 생성해야 합니다.

  • GET /api/auth/login: 사용자를 Scalekit에서 로그인하도록 리디렉션합니다.
  • GET /api/auth/callback: 권한 코드를 토큰으로 교환하고 세션 쿠키를 설정합니다.
  • GET /api/auth/logout: 세션 쿠키를 지우고 Scalekit 세션을 종료합니다.

프로젝트의 src/pages/api/auth/ 디렉터리에 이러한 엔드포인트를 생성하세요. 프로젝트에 다음 새 파일이 포함되어야 합니다.

  • 디렉터리src/
    • 디렉터리lib/
      • scalekit.ts
    • 디렉터리pages/
      • 디렉터리api/
        • 디렉터리auth/
          • login.ts
          • callback.ts
          • logout.ts
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

login.ts은 권한 URL을 생성하고 사용자를 Scalekit에서 로그인하도록 리디렉션합니다.

src/pages/api/auth/login.ts
import type { APIRoute } from "astro";
import { scalekit, REDIRECT_URI } from "../../../lib/scalekit";
export const GET: APIRoute = async () => {
const url = scalekit.getAuthorizationUrl(REDIRECT_URI, {
scopes: ["openid", "profile", "email", "offline_access"],
});
return Response.redirect(url);
};

callback.ts는 Scalekit에서 권한 코드를 수신하고, 토큰으로 교환한 후 HttpOnly 쿠키에 저장합니다.

src/pages/api/auth/callback.ts
import type { APIRoute } from "astro";
import { scalekit, REDIRECT_URI } from "../../../lib/scalekit";
export const GET: APIRoute = async ({ request, cookies }) => {
const url = new URL(request.url);
const code = url.searchParams.get("code");
if (!code) {
return Response.redirect(new URL("/", request.url).origin);
}
const { user, idToken, accessToken, refreshToken } =
await scalekit.authenticateWithCode(code, REDIRECT_URI);
const secure = url.protocol === "https:";
const cookieOptions = { httpOnly: true, path: "/", sameSite: "lax" as const, secure };
cookies.set("sk-id-token", idToken, cookieOptions);
cookies.set("sk-access-token", accessToken, cookieOptions);
cookies.set("sk-refresh-token", refreshToken, cookieOptions);
return Response.redirect(new URL("/", request.url).origin);
};

logout.ts는 세션 쿠키를 지우고 Scalekit 로그아웃 엔드포인트로 리디렉션하여 Scalekit 세션을 종료합니다.

src/pages/api/auth/logout.ts
import type { APIRoute } from "astro";
import { scalekit } from "../../../lib/scalekit";
export const GET: APIRoute = async ({ request, cookies }) => {
const idToken = cookies.get("sk-id-token")?.value;
cookies.delete("sk-id-token", { path: "/" });
cookies.delete("sk-access-token", { path: "/" });
cookies.delete("sk-refresh-token", { path: "/" });
const logoutUrl = scalekit.getLogoutUrl({
idTokenHint: idToken,
postLogoutRedirectUri: new URL("/", request.url).origin,
});
return Response.redirect(logoutUrl);
};

모든 요청에서 액세스 토큰을 검증하고 Astro.locals.user에 인증된 사용자 프로필을 채우려면 src/middleware.ts 파일을 생성하세요. 액세스 토큰이 만료되면 미들웨어가 리프레시 토큰을 사용하여 자동으로 갱신합니다.

src/middleware.ts
import { defineMiddleware } from "astro:middleware";
import type { IdTokenClaim } from "@scalekit-sdk/node";
import { scalekit } from "./lib/scalekit";
export const onRequest = defineMiddleware(async (context, next) => {
const accessToken = context.cookies.get("sk-access-token")?.value;
if (accessToken) {
try {
const claims = await scalekit.validateToken<IdTokenClaim>(accessToken);
context.locals.user = {
sub: claims.sub,
email: claims.email,
name: claims.name,
};
} catch {
// 액세스 토큰이 유효하지 않거나 만료됨 — 갱신 시도
const refreshToken = context.cookies.get("sk-refresh-token")?.value;
if (refreshToken) {
try {
const { accessToken: newToken } =
await scalekit.refreshAccessToken(refreshToken);
const secure = new URL(context.request.url).protocol === "https:";
context.cookies.set("sk-access-token", newToken, {
httpOnly: true,
path: "/",
sameSite: "lax",
secure,
});
const claims = await scalekit.validateToken<IdTokenClaim>(newToken);
context.locals.user = {
sub: claims.sub,
email: claims.email,
name: claims.name,
};
} catch {
// 갱신 실패 — 세션 쿠키 지우기
context.cookies.delete("sk-id-token", { path: "/" });
context.cookies.delete("sk-access-token", { path: "/" });
context.cookies.delete("sk-refresh-token", { path: "/" });
}
}
}
}
return next();
});

이제 미들웨어가 Astro.locals.user를 채우므로, 인증 상태에 따라 다른 콘텐츠를 표시하는 페이지를 생성할 수 있습니다.

dashboard.astro는 인증된 사용자만 액세스할 수 있는 페이지입니다. 미들웨어에서 설정된 Astro.locals에서 사용자를 읽고, 사용자가 로그인하지 않았으면 홈 페이지로 리디렉션합니다.

src/pages/dashboard.astro
---
import Layout from "../layouts/Layout.astro";
const user = Astro.locals.user;
if (!user) {
return Astro.redirect("/");
}
---
<Layout title="Dashboard">
<h1>Welcome, {user.name ?? user.email}</h1>
<p>You are signed in.</p>
<a href="/api/auth/logout">Sign out</a>
</Layout>

더 많은 백엔드 서비스 가이드

기여하기 커뮤니티 후원하기