Ship auth, plans, and payments
without building any of it
Astapa gives you a complete backend for user authentication, subscription management, and local payment processing — all through a single API. Redirect users to our hosted login, read a signed JWT, and gate features by plan. That's it.
What you get
Hosted Authentication
Email/password, Google, and GitHub OAuth. We handle signup, login, email verification, and password resets.
Plan Management
Create subscription tiers in the dashboard. Assign plans to users. The JWT carries the plan — your app just reads it.
Local Payments
QRIS, GoPay, OVO, bank transfers via Xendit. Built for Southeast Asia from day one.
MCP Auth
JWT-based authentication for AI agents and MCP servers. client_credentials flow with scoped access.
How it works
Three steps. No auth infrastructure to build or maintain.
Create a project
Head to your dashboard and create a new project. You'll get a client_id and client_secret. Add your app's callback URL as a redirect URI.
Redirect to hosted login
Send your users to our hosted login page. They can sign up or sign in with email, Google, or GitHub. After authentication, we redirect them back to your app with an authorization code.
// Redirect user to Astapa's hosted login
const params = new URLSearchParams({
client_id: process.env.ASTAPA_CLIENT_ID,
redirect_uri: "https://yourapp.com/callback",
});
window.location.href = `https://astapa.com/auth/login?${params}`;Exchange the code for tokens
Your callback handler receives the authorization code. Exchange it for an access token (JWT) and a refresh token. The JWT contains the user's identity, email, and any custom claims like their subscription plan.
// Exchange authorization code for tokens
const res = await fetch("https://astapa.com/api/platform/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "authorization_code",
code: searchParams.get("code"),
client_id: process.env.ASTAPA_CLIENT_ID,
client_secret: process.env.ASTAPA_CLIENT_SECRET,
redirect_uri: "https://yourapp.com/callback",
}),
});
const { access_token, refresh_token, expires_in } = await res.json();
// access_token is a signed JWT (RS256) — verify it with our JWKS endpointWhat's in the JWT
Every access token is a signed JWT. Decode it and you'll find everything you need to identify the user and gate features — no extra API calls required.
{
"sub": "42",
"email": "user@example.com",
"email_verified": true,
"project_id": "proj_abc123",
"custom_claims": {
"plan": "pro",
"role": "admin",
"feature_flags": ["beta_dashboard", "export_csv"]
},
"iss": "https://astapa.com",
"aud": "your_client_id",
"exp": 1712345678,
"iat": 1712342078
}custom_claims.plan from the JWT and gate features in your app. No database queries, no extra API calls. When a user upgrades, set their new plan via the Claims API, refresh the token, and they're on the new plan instantly.Token lifecycle
Access tokens expire after 1 hour. Use the refresh token to get a new one without re-authenticating. This is typically handled transparently in your middleware.
POST /api/platform/token with grant_type=refresh_token. You get a fresh JWT with updated claims.POST /api/platform/revoke to invalidate all refresh tokens, then clear cookies. The user can't refresh anymore.Base URL
All API endpoints are relative to your Astapa deployment. If you're using the hosted version:
https://astapa.com/api/platform/...astapa.com with your deployment's origin. All paths stay the same.Sandbox mode
Every project starts in sandbox mode. Sandbox projects work exactly like production — same APIs, same JWTs, same flows — but with relaxed rate limits and test data isolation. When you're ready to go live, flip the toggle in your dashboard.
Ready to build?
Pick where you want to go next.