Shared sign-in
auth.hasanat.dev is a shared sign-in service for every site served on hasanat.dev domains. A user signs in once with a Google or GitHub account, and every Hasanat site recognises them.
How it works
Section titled “How it works”- After sign-in, the service sets the
authjs.session-tokencookie on the.hasanat.devdomain. Browsers send it to every site onhttps://*.hasanat.dev. - The cookie is encrypted with a secret that only the auth service holds, so your app never decodes it. Your app asks the service instead: it forwards the incoming
cookieheader tohttps://auth.hasanat.dev/api/auth/sessionand receives the session JSON, ornullwhen nobody is signed in. - The cookie is
httpOnly,Secure, andSameSite=Lax, so page scripts cannot read it. - Sessions expire after 30 days without use, and every session check extends them.
Endpoints
Section titled “Endpoints”- Sign-in page:
https://auth.hasanat.dev/api/auth/signin?callbackUrl=<url-encoded return URL> - Session:
https://auth.hasanat.dev/api/auth/session - Sign-out:
https://auth.hasanat.dev/api/auth/signout - User account:
https://auth.hasanat.dev/account
The service accepts return URLs on https://hasanat.dev, single-level subdomains such as https://name.hasanat.dev, and http://localhost. Any other return URL sends the user back to auth.hasanat.dev itself. The sign-in page is in English today.
Session shape
Section titled “Session shape”{ "user": { "id": "…", "name": "…", "email": "…", "image": "…", "provider": "google", "githubUsername": null, "role": "user" }, "expires": "…"}idis a stable user identifier. Key your app’s data to it.providerisgoogleorgithub.rolecomes from the user’s record in the auth service, defaults touser, and is read at sign-in. It is not a Hasanat project role such as lead or moderator. Keep your app’s own permissions in its database.
In the Hasanat starter
Section titled “In the Hasanat starter”The starter is already wired to the service. src/middleware.ts asks the auth service for the session and puts the user in Astro.locals.user, or null.
---// A page that requires sign-inimport { requireAuth } from "@/lib/auth";
const redirect = requireAuth(Astro.locals.user, Astro.url.href);if (redirect) return redirect;
const user = Astro.locals.user; // { id, name, email, image, role }---// Sign-in and sign-out links, and the admin checkimport { getSignInUrl, getSignOutUrl, isAdmin } from "@/lib/auth";
const signIn = getSignInUrl(Astro.url.href);const signOut = getSignOutUrl();const admin = isAdmin(Astro.locals.user); // role === "admin"// A protected API routeimport type { APIRoute } from "astro";
export const GET: APIRoute = async ({ locals }) => { if (!locals.user) return new Response("Unauthorized", { status: 401 }); return Response.json({ id: locals.user.id });};From the browser
Section titled “From the browser”Pages served on a hasanat.dev subdomain can ask for the session from the browser, because the auth service accepts credentialed requests from hasanat.dev origins:
const res = await fetch("https://auth.hasanat.dev/api/auth/session", { credentials: "include",});const session = await res.json(); // null when signed outOn your machine
Section titled “On your machine”Shared sign-in does not work on localhost: the cookie belongs to hasanat.dev domains, so it is never sent to your machine. In development, the starter points its sign-in links to http://localhost:8788, a local copy of the auth service that most builders do not run, because its repository is private.
So Astro.locals.user stays null locally. Build the public pages first, and try sign-in after deploying to a hasanat.dev subdomain.
Apps outside hasanat.dev domains
Section titled “Apps outside hasanat.dev domains”There is no OAuth flow for external apps today. auth.hasanat.dev/oauth/authorize and /oauth/token do not exist, and no client IDs or secrets are issued. The commented HASANAT_CLIENT_ID and HASANAT_CLIENT_SECRET lines in the starter’s .env.example are not used.
To use Hasanat accounts in an external app:
- Serve it on a hasanat.dev subdomain if you can, and shared sign-in works. See deployment and subdomains.
- Otherwise, request “sign-in for an external app” from the platform dashboard, and a maintainer will look at your case with you.
Data and privacy
Section titled “Data and privacy”Your app receives the user’s ID, name, email, and image from the auth service. The starter’s data policy sets the rules:
- Use the ID to associate data in your app, and show the user’s name and image.
- Do not store the email or image in your database unless the app needs it and you disclose it.
- Never share user data with third parties, sell it, or use it for advertising.
- Include a short privacy notice users can reach, even a footer link.
- Don’t ask for
AUTH_SECRETor copy it into your app. Your app does not need it, and it is never sent in any request. - Don’t store passwords, tokens, or the session cookie itself.
- Don’t try to decode the session cookie yourself: ask the auth service.
- Don’t add another sign-in provider, such as Firebase Auth or Clerk, to starter apps.