Docs / Authentication and Secrets
Authentication and Secrets
Separate platform CLI authorization from app-user login and protect sessions and secret keys.
Platform authorization and app login
A CLI connection authorizes a developer to manage a project. It does not automatically authenticate visitors or grant store and administrator roles inside the app. Connect customer authentication to a verified identity provider used by the app.
The sample adds a server-session layer after identity-provider verification. Do not skip OAuth state, PKCE or token validation, and never issue a session from a browser-supplied user ID alone.
Session validation middleware
Add the app_sessions schema and AppEnv type from the Hono example. Store only an HMAC hash of the session token in the database.
import { createMiddleware } from 'hono/factory';
import { getCookie } from 'hono/cookie';
import type { AppEnv } from '../types';
export async function sessionHash(token: string, salt: string) {
const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(salt),
{ name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
const hash = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(token));
return Array.from(new Uint8Array(hash), b => b.toString(16).padStart(2, '0')).join('');
}
export const requireSession = createMiddleware<AppEnv>(async (c, next) => {
const token = getCookie(c, '__Host-app_session');
if (!token || !/^[a-f0-9]{64}$/.test(token)) return c.json({ error: 'login_required' }, 401);
const identity = await c.env.DB.prepare(
'SELECT user_id,session_hash FROM app_sessions WHERE session_hash=? AND expires_at>?'
).bind(await sessionHash(token, c.env.SESSION_HASH_SALT), Date.now())
.first<{ user_id: string; session_hash: string }>();
if (!identity) return c.json({ error: 'login_required' }, 401);
// 쿠키 인증을 사용하는 브라우저 변경 요청은 같은 Origin만 허용합니다.
if (!['GET', 'HEAD'].includes(c.req.method) &&
c.req.header('Origin') !== new URL(c.req.url).origin)
return c.json({ error: 'origin_not_allowed' }, 403);
c.set('identity', identity);
c.header('Cache-Control', 'private, no-store');
await next();
});joripspace secret generate --project PROJECT --name SESSION_HASH_SALTUse a generated project Secret for SESSION_HASH_SALT. Keep a separate local value in a Git-ignored .dev.vars file. A valid session does not grant access to every resource, so constrain each query by the user’s membership and role.
Issue a session after identity verification
// OAuth 등 본인 확인에 Success한 서버 처리에서만 호출합니다.
// User가 보낸 userId를 그대로 전달하는 공개 엔드포인트를 만들지 마세요.
import { setCookie } from 'hono/cookie';
import { sessionHash } from './middleware/session';
import type { Context } from 'hono';
import type { AppEnv } from './types';
export async function issueSession(c: Context<AppEnv>, verifiedUserId: string) {
if (!/^[a-zA-Z0-9_-]{1,80}$/.test(verifiedUserId)) throw new Error('Invalid internal user ID');
const bytes = crypto.getRandomValues(new Uint8Array(32));
const token = Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
const maxAge = 3600; // 앱의 세션 정책 예시
await c.env.DB.prepare('INSERT INTO app_sessions(session_hash,user_id,expires_at) VALUES(?,?,?)')
.bind(await sessionHash(token, c.env.SESSION_HASH_SALT), verifiedUserId, Date.now() + maxAge * 1000).run();
setCookie(c, '__Host-app_session', token, {
httpOnly: true, secure: true, sameSite: 'Lax', path: '/', maxAge,
});
// 응답 JSON·로그·URL에는 token을 포함하지 않습니다.
}Call this function only after a trusted login provider has verified the user. It is not a replacement for an OAuth or password-login implementation. Send the raw session in an HttpOnly, Secure, SameSite=Lax cookie.
Log out with a same-origin POST that deletes the session_hash row and expires the cookie with the same name and Path. Revoke related realtime access and define what happens to existing sessions when secrets rotate.
Secrets and API security
Store payment-gateway and mail-provider keys as project Secrets. CLI credentials, user sessions and provider Secrets serve different purposes. Never place Secrets in source, browser bundles, public repositories or logs.
- Validate authorization, input shape and request size on the server.
- Protect cookie-authenticated mutations against CSRF and avoid unnecessary CORS access.
- Escape HTML and use textContent for browser output.
- Encrypt sensitive data such as email, phone numbers and payment keys, or maintain a search-safe hash.
- Log status and error IDs, not cookies, full request bodies or complete provider responses.