Authentication API
The @instroc/auth package handles sign-up, sign-in, sessions, and everything around them. Sessions live in secure HttpOnly cookies and refresh automatically, so your components only ever deal with hooks and plain method calls. This page documents the full useAuth surface, the helper hooks and components, and the error and rate-limit behavior you should design around.
useAuth
useAuth() is the main hook. It returns the current state plus every auth method:
const {
user, // AuthUser | null
session,
loading,
initializing,
error, // string | null
login, // login({ email, password })
signup, // signup({ email, password, ...profile fields })
logout, // logout()
signInWithOAuth, // signInWithOAuth(provider)
verifyOTP, // verifyOTP(email, code)
resendOTP, // resendOTP(email)
updateProfile, // updateProfile(data)
refreshSession,
forgotPassword, // forgotPassword(email)
resetPassword, // resetPassword(token, newPassword)
authConfig,
} = useAuth();
Methods take positional parameters exactly as shown and throw an AuthError on failure. AuthError has a .status property with the HTTP status code.
Signing up
signup() resolves to a result with a status field that tells you what to do next: "authenticated" (the user is signed in), "needs_verification" (an OTP code was emailed), or "needs_approval".
async function handleSignup(email: string, password: string) {
try {
const result = await signup({ email, password });
if (result.status === "authenticated") {
// signed in, go to the app
} else if (result.status === "needs_verification") {
// show the OTP code form
} else if (result.status === "needs_approval") {
// tell the user their account is pending approval
}
} catch (e) {
// e is an AuthError with .status
}
}
Email verification with OTP
When verification is enabled (it is off by default), new users receive a one-time code by email. Confirm it with verifyOTP(email, code) and offer a resend button backed by resendOTP(email). If an unverified user tries to log in, the login call fails with a 403 carrying the error code email_not_verified; route them back to the code form rather than showing a generic failure.
Forgot and reset password
The flow is two calls. forgotPassword(email) sends the reset email, and the link in it lands the user on your reset page with a token, where you call resetPassword(token, newPassword). Both throw AuthError on failure.
Updating the profile
updateProfile(data) updates the signed-in user's profile fields, such as display_name and avatar_url. The AuthUser shape is:
{
id: string;
email: string;
email_verified: boolean;
display_name: string;
avatar_url: string;
metadata: object;
created_at: string;
is_owner?: boolean;
}
OAuth sign-in
signInWithOAuth(provider) starts an OAuth redirect. Google and GitHub are available today, fully managed by the platform with no configuration, and more providers are coming. For most apps you should render the ready-made component instead of wiring buttons yourself:
import { OAuthButtons } from "@instroc/auth";
<OAuthButtons />
<OAuthButtons /> renders one button per provider enabled in your project and handles the redirect. Props let you customize it: providers (restrict which to show), labels, icons, renderButton (full render control), and buttonClassName, among styling options.
Route guards
@instroc/auth/router exports two wrapper components. RequireAuth renders its children only for signed-in users and redirects everyone else to the login page. RedirectIfAuthed does the opposite, which is what you want around login and signup pages.
import { RequireAuth, RedirectIfAuthed } from "@instroc/auth/router";
<RequireAuth>
<AccountPage />
</RequireAuth>
Other hooks
useUser()returns just theAuthUseror null.useSession()returns the current session.useAuthRequired()for imperative checks that the visitor is signed in.useIsOwner()is true when the signed-in user is the app owner, handy for admin-only pages.useIsWorkspaceMember()is true for members of your Instroc workspace.
useIsWorkspaceMember and useIsOwner gate UI only. Real protection comes from each table's security rules; never rely on a hidden button to protect data.
Form hooks
For each auth form there is a hook that packages the submit handler and its state. All of them share the shape {submit, loading, error, setError, clearError}:
useLoginFormuseSignupFormuseOtpForm, which addsresend,resending, andresendCooldownuseForgotPasswordForm, which addssubmitted,resendCooldown, andresetuseResetPasswordForm
authConfig
authConfig (returned by useAuth) describes what is enabled for this project, so your UI can adapt without hardcoding:
{
emailAuthEnabled: boolean;
googleAuthEnabled: boolean;
githubAuthEnabled: boolean;
microsoftAuthEnabled: boolean;
facebookAuthEnabled: boolean;
allowSignup: boolean;
requireEmailVerification: boolean;
}
Only Google and GitHub can be switched on today; the other provider flags exist for providers that are coming later.
Rate limits
Login attempts are limited to 20 per IP and 5 per email address every 15 minutes, and signups to 5 per IP per hour. Design your error handling to show a "try again later" message when a 429 comes back rather than retrying in a loop.
To manage which sign-in methods are on and see your app's users, use the Authentication and Users panels described in Backend authentication.