Auth namespace
Reference for registration, login, token rotation, verification, password reset, magic links, and OAuth.
A
Jboard.auth creates and manages candidate or employer sessions for one board. It uses a short-lived bearer access token and an opaque, single-use refresh token. The SDK never navigates and never performs a hidden refresh after a 401—your application owns those decisions.
Session result
Registration, login, refresh, magic-link consumption, and OAuth exchange resolve a BoardAuthSession:
| Field | Type | Meaning |
|---|---|---|
object | 'board_auth_session' | Stable result discriminator |
accessToken | string | Bearer JWT, valid for one hour |
refreshToken | string | Single-use token, valid for 30 days until used or revoked |
expiresAt | number | Access-token expiry in epoch milliseconds |
boardUser | BoardUser | The authenticated user on this board |
Storage and persistence
Methods that return a session also write its access and refresh tokens to the client’s configured auth.storage before resolving.
- Browser clients default to
memory; choosesessionorlocalexplicitly if the session must survive longer. - Server clients default to
nostore. Keep the returned pair in an application-owned httpOnly cookie and pass authorization per call. refreshreplaces both stored tokens with the rotated pair.logoutclears the access and refresh tokens only after server-side revocation succeeds.
See Authentication and session ownership before using a shared server client.
Registration and password login
| Method and signature | Returns | Behavior |
|---|---|---|
board.auth.register(body: RegisterBody, options?: FetchOptions) | Promise<BoardAuthSession> | Creates a candidate or employer using the emailpass method and persists the returned session. |
board.auth.login(body: LoginBody, options?: FetchOptions) | Promise<BoardAuthSession> | Authenticates an email/password pair and persists the returned session. |
RegisterBody requires role, method: 'emailpass', email, password, and displayName. Passwords must contain at least eight characters.
12345678910const session = await board.auth.register({role: 'candidate',method: 'emailpass',email: 'ada@example.com',password: 'correct-horse-battery-staple',displayName: 'Ada Lovelace',});console.log(session.boardUser.email);console.log(new Date(session.expiresAt));
The SDK returns the session; it does not redirect to an account page or decide whether an unverified user can proceed.
Refresh and logout
| Method and signature | Returns | Behavior |
|---|---|---|
board.auth.refresh(body?: RefreshBody, options?: FetchOptions) | Promise<BoardAuthSession> | Rotates the refresh token, persists the replacement pair, and returns it. |
board.auth.logout(body?: LogoutBody, options?: FetchOptions) | Promise<void> | Revokes the refresh token and clears the configured access/refresh storage after the 204 response. |
When body is omitted, both methods read the refresh token from SDK storage. A nostore caller must pass it explicitly:
1234567const rotated = await board.auth.refresh({refreshToken: session.refreshToken,});await board.auth.logout({refreshToken: rotated.refreshToken,});
If neither the body nor storage supplies a refresh token, the SDK throws a plain Error before sending a request. It is not a BoardApiError because no API response exists.
Refresh tokens are single-use. Coordinate concurrent refreshes with one shared in-flight promise or createSessionRefresher from @cavuno/board/server. Do not retry a refresh that returned 401.
Logout is idempotent on the server, including for an unknown refresh token. If the request fails because of a network or server error, the SDK deliberately retains local storage—the token might still be live server-side. Your application can retry revocation or explicitly choose a local-only sign-out.
Email verification
| Method and signature | Returns | Authentication |
|---|---|---|
board.auth.verifyEmail(body: VerifyEmailBody, options?: FetchOptions) | Promise<void> | The email-link token authorizes the request. |
board.auth.verifyEmailWithCode(body: { code: string }, options?: FetchOptions) | Promise<void> | Requires the signed-in user’s bearer token. |
board.auth.resendVerification(options?: FetchOptions) | Promise<void> | Requires the signed-in user’s bearer token. |
verifyEmail consumes the token included in the email link. verifyEmailWithCode consumes the six-digit code sent to the current user. Both resolve after a 204 response.
123456await board.auth.verifyEmail({ token: tokenFromEmailLink });await board.auth.verifyEmailWithCode({ code: '123456' },{ headers: { authorization: `Bearer ${accessToken}` } },);
resendVerification sends a fresh code and link. It resolves as a no-op when the current user is already verified.
Password reset
| Method and signature | Returns | Behavior |
|---|---|---|
board.auth.forgotPassword(body: ForgotPasswordBody, options?: FetchOptions) | Promise<void> | Accepts an email and always resolves 204 when the request is accepted, whether or not the account exists. |
board.auth.resetPassword(body: ResetPasswordBody, options?: FetchOptions) | Promise<void> | Consumes a single-use reset token, sets a password of at least eight characters, and invalidates existing sessions. |
The uniform forgot-password response prevents the endpoint from becoming an account-existence oracle.
123456await board.auth.forgotPassword({ email: 'ada@example.com' });await board.auth.resetPassword({token: resetTokenFromEmail,password: 'a-new-strong-password',});
After a successful reset, send the user through login again. Do not assume a previously stored access token remains valid.
Magic links
| Method and signature | Returns | Behavior |
|---|---|---|
board.auth.requestMagicLink(body: RequestMagicLinkBody, options?: FetchOptions) | Promise<void> | Sends a passwordless sign-in link and resolves 204. |
board.auth.consumeMagicLink(body: ConsumeMagicLinkBody, options?: FetchOptions) | Promise<BoardAuthSession> | Exchanges the email token once and persists the returned session. |
RequestMagicLinkBody accepts an email plus an optional same-origin returnTo path.
12345678await board.auth.requestMagicLink({email: 'ada@example.com',returnTo: '/account',});const session = await board.auth.consumeMagicLink({token: tokenFromEmailLink,});
The SDK does not navigate to returnTo; the host application owns the callback route and the post-exchange redirect.
OAuth
| Method and signature | Returns | Behavior |
|---|---|---|
board.auth.getOAuthAuthorizationUrl(provider: OAuthProvider, query?: OAuthAuthorizationQuery, options?: FetchOptions) | Promise<OAuthAuthorizationUrl> | Returns an authorization URL for 'google' or 'linkedin'. |
board.auth.exchangeOAuth(body: OAuthExchangeBody, options?: FetchOptions) | Promise<BoardAuthSession> | Exchanges the callback’s one-time token and persists the session. |
12345678910const { authorizeUrl } = await board.auth.getOAuthAuthorizationUrl('google', {returnTo: '/account',});window.location.assign(authorizeUrl);// In the application’s callback route:const session = await board.auth.exchangeOAuth({token: callbackToken,});
The URL method does not change browser location. Validate the callback route’s own redirect input instead of navigating to an arbitrary query parameter.
Fetch options and errors
Every method accepts FetchOptions in its final position. Standard fields such as signal and headers, plus framework fetch extensions, pass through to fetch.
Every non-2xx API response throws BoardApiError. Relevant codes include board_auth_email_taken, board_auth_invalid_credentials, board_auth_invalid_token, board_auth_registration_disabled, and board_auth_token_expired. Codes are additive within v1, so preserve a default error branch.
Use isUnauthorized for an expired or invalid session, but do not confuse it with isBoardPasswordRequired, which represents the separate board-password wall. Log error.requestId when present and never log either token.
Verify an auth implementation
- Confirm the selected storage receives both tokens after login and receives the rotated pair after refresh.
- Confirm two simultaneous expired-session requests produce one refresh attempt within the process.
- Confirm invalid and reused email, magic-link, reset, and OAuth tokens fail without being retried.
- Confirm logout clears local tokens only after revocation succeeds.
- On SSR, confirm access and refresh tokens never appear in HTML, serialized props, URLs, public caches, or browser JavaScript.