Auth Action Bridge – astrojs-wp-integration
Overview
Section titled “Overview”The package ships a JWT-first Astro auth bridge for WordPress.
It is built on top of fluent-wp-client and is meant to be the central place where Astro resolves one authenticated WordPress client or client config per request.
What It Gives You
Section titled “What It Gives You”createWordPressAuthBridge(config)for login, cookie handling, and request auth resolutionloginActionfor username/password -> JWT logingetClient(context)for a ready-to-useWordPressClientresolveUser(context)andisAuthenticated(context)for middleware checksgetClientConfig(context)when you need raw client config instead of the client instance
The bridge is client-first, uses web-standard runtime APIs, and does not expose a legacy raw JWT action-auth helper.
It uses the published client’s loginWithJwt() and users().me() helpers instead of duplicating request handling.
resolveUser() and isAuthenticated() only describe request user state.
If you want getClient() or getClientConfig() to fall back to bridge-level static credentials, opt in with { allowStaticAuthFallback: true }.
WordPress Requirement
Section titled “WordPress Requirement”Enable the JWT Authentication for WP REST API plugin and set JWT_AUTH_SECRET_KEY in WordPress.
Basic Setup
Section titled “Basic Setup”import { createWordPressAuthBridge } from 'wp-astrojs-integration';
export const wordPressAuthBridge = createWordPressAuthBridge({ baseUrl: import.meta.env.WP_URL, cookieName: 'wp_user_session',});Middleware
Section titled “Middleware”Use the bridge as the single place that resolves the current request into one authenticated client.
import { defineMiddleware } from 'astro:middleware';import { wordPressAuthBridge } from './lib/auth/bridge';
export const onRequest = defineMiddleware(async (context, next) => { if (context.url.pathname === '/login') { return next(); }
const wp = await wordPressAuthBridge.getClient(context);
if (!wp) { return Response.redirect(new URL('/login', context.url), 302); }
try { context.locals.user = await wp.users().me(); context.locals.wp = wp; } catch { wordPressAuthBridge.clearAuthentication(context.cookies); return Response.redirect(new URL('/login', context.url), 302); }
return next();});Action Factories with the Bridge Client
Section titled “Action Factories with the Bridge Client”Action factories use the bridge client directly, so you do not need to rebuild baseUrl, auth, or authHeaders for each action.
import { createCreatePostAction, createUpdatePostAction, createDeletePostAction, pageSchema,} from 'wp-astrojs-integration';import { wordPressAuthBridge } from '../lib/auth/bridge';
export const server = { login: wordPressAuthBridge.loginAction,
createPost: createCreatePostAction(wordPressAuthBridge.getClient), updatePost: createUpdatePostAction(wordPressAuthBridge.getClient), deletePost: createDeletePostAction(wordPressAuthBridge.getClient),
createPage: createCreatePostAction(wordPressAuthBridge.getClient, { resource: 'pages', responseSchema: pageSchema, }),};If you prefer static credentials, build one client yourself and pass it directly:
import { WordPressClient } from 'fluent-wp-client';
const wp = new WordPressClient({ baseUrl: import.meta.env.WP_URL, auth: { username: import.meta.env.WP_USERNAME, password: import.meta.env.WP_APP_PASSWORD, },});
export const server = { createPost: createCreatePostAction(wp),};Low-Level Escape Hatch
Section titled “Low-Level Escape Hatch”If you need to create the client yourself, the bridge can also return the resolved config.
import { WordPressClient } from 'fluent-wp-client';
const clientConfig = await wordPressAuthBridge.getClientConfig(Astro);
if (!clientConfig) { return Astro.redirect('/login');}
const wp = new WordPressClient(clientConfig);Optional Static Fallback
Section titled “Optional Static Fallback”If the same bridge also has static auth, authHeader, or authHeaders, keep request-user checks on resolveUser() / isAuthenticated().
Only opt into static fallback when you explicitly want one service-capable client.
const wp = await wordPressAuthBridge.getClient(Astro, { allowStaticAuthFallback: true,});
if (!wp) { return Astro.redirect('/login');}Custom Auth Resolution
Section titled “Custom Auth Resolution”The bridge can also resolve auth from request headers or other request-scoped sources.
import { createAuthResolver } from 'fluent-wp-client';import { createWordPressAuthBridge } from 'wp-astrojs-integration';import type { ActionAPIContext } from 'astro:actions';
export const bridge = createWordPressAuthBridge({ baseUrl: import.meta.env.WP_URL, authResolver: createAuthResolver<ActionAPIContext>((context) => { return context.request.headers.get('authorization'); }),});Login Payload Schema
Section titled “Login Payload Schema”import { z } from 'astro/zod';import { wordPressLoginInputSchema } from 'wp-astrojs-integration';
type LoginPayload = z.input<typeof wordPressLoginInputSchema>;