Fresh — фреймворк для Deno, ориентированный на серверный рендеринг,
островную архитектуру и минимальное использование клиентского
JavaScript. Интеграция OAuth 2.0 в Fresh-приложениях опирается на
стандартные HTTP-механизмы, Web API Deno и строгую модель модулей без
node_modules. Это упрощает контроль зависимостей, но
требует точного понимания протокола OAuth 2.0 и жизненного цикла
запросов.
OAuth 2.0 используется для делегированной авторизации: приложение получает ограниченный доступ к ресурсам пользователя через сторонний провайдер (Google, GitHub, Microsoft и др.), не работая напрямую с его учетными данными.
Классическая схема OAuth 2.0 включает следующие роли:
В Fresh-приложении ключевые точки интеграции:
/login,
/oauth/callback);Для серверных приложений Fresh используется Authorization Code Flow. Он безопасен, поддерживает refresh-токены и не требует хранения секретов на клиенте.
Основные шаги:
authorization_code в callback-маршрут.access_token.В Fresh маршруты определяются файловой структурой. Начало OAuth-процесса обычно реализуется в серверном обработчике:
// routes/login.ts
import { Handlers } from "$fresh/server.ts";
export const handler: Handlers = {
GET() {
const params = new URLSearchParams({
client_id: Deno.env.get("OAUTH_CLIENT_ID")!,
redirect_uri: "https://example.com/oauth/callback",
response_type: "code",
scope: "openid profile email",
state: crypto.randomUUID(),
});
return Response.redirect(
`https://provider.com/oauth/authorize?${params}`,
302,
);
},
};
Ключевые моменты:
state используется для защиты от CSRF.redirect_uri должен совпадать с настройками у
провайдера.Callback-маршрут принимает code и state,
проверяет корректность и запрашивает токен:
// routes/oauth/callback.ts
import { Handlers } from "$fresh/server.ts";
export const handler: Handlers = {
async GET(req) {
const url = new URL(req.url);
const code = url.searchParams.get("code");
const tokenResponse = await fetch("https://provider.com/oauth/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "authorization_code",
code: code!,
redirect_uri: "https://example.com/oauth/callback",
client_id: Deno.env.get("OAUTH_CLIENT_ID")!,
client_secret: Deno.env.get("OAUTH_CLIENT_SECRET")!,
}),
});
const tokens = await tokenResponse.json();
// сохранение токенов
return new Response(null, {
status: 302,
headers: { Location: "/" },
});
},
};
Особенности Fresh и Deno:
fetch.Deno.env.Fresh не навязывает механизм сессий. Возможные подходы:
headers.append(
"Set-Cookie",
`access_token=${token}; HttpOnly; Secure; Path=/`,
);
Нативное хранилище для Fresh:
const kv = await Deno.openKv();
await kv.set(["session", sessionId], tokens);
Fresh поддерживает middleware на уровне маршрутов:
// routes/_middleware.ts
import { MiddlewareHandlerContext } from "$fresh/server.ts";
export async function handler(
req: Request,
ctx: MiddlewareHandlerContext,
) {
const cookie = req.headers.get("cookie");
if (!cookie?.includes("access_token")) {
return Response.redirect("/login", 302);
}
return await ctx.next();
}
Такой middleware автоматически защищает все маршруты ниже по иерархии.
После получения access_token выполняется запрос к API
провайдера:
const userResponse = await fetch("https://provider.com/userinfo", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const profile = await userResponse.json();
Данные профиля могут быть:
Если провайдер поддерживает refresh-токены:
await fetch("https://provider.com/oauth/token", {
method: "POST",
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token,
client_id,
client_secret,
}),
});
Обновление выполняется сервером, прозрачно для пользователя.
Обязательные меры:
state параметра;client_secret только на сервере;scope;Fresh, за счёт отсутствия клиентской логики по умолчанию, снижает риск утечки токенов через XSS.
OAuth-логика полностью серверная. Острова (islands/)
используются только для UI-состояний:
Авторизация не требует гидратации страницы, что сохраняет ключевое преимущество Fresh — минимальный JavaScript на клиенте.
Интеграция нескольких OAuth-провайдеров достигается абстракцией конфигурации:
type ProviderConfig = {
authorizeUrl: string;
tokenUrl: string;
scope: string;
};
Маршруты Fresh легко масштабируются под Google, GitHub, Discord и корпоративные OAuth-серверы без изменения архитектуры.
OAuth 2.0 органично вписывается в философию Fresh:
Интеграция остаётся прозрачной, расширяемой и полностью соответствующей стандартам веб-платформы.