Skip to content

Auth Action Bridge – astrojs-wp-integration

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.

  • createWordPressAuthBridge(config) for login, cookie handling, and request auth resolution
  • loginAction for username/password -> JWT login
  • getClient(context) for a ready-to-use WordPressClient
  • resolveUser(context) and isAuthenticated(context) for middleware checks
  • getClientConfig(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 }.

Enable the JWT Authentication for WP REST API plugin and set JWT_AUTH_SECRET_KEY in WordPress.

src/lib/auth/bridge.ts
import { createWordPressAuthBridge } from 'wp-astrojs-integration';
export const wordPressAuthBridge = createWordPressAuthBridge({
baseUrl: import.meta.env.WP_URL,
cookieName: 'wp_user_session',
});

Use the bridge as the single place that resolves the current request into one authenticated client.

src/middleware.ts
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 use the bridge client directly, so you do not need to rebuild baseUrl, auth, or authHeaders for each action.

src/actions/index.ts
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),
};

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);

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');
}

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');
}),
});
import { z } from 'astro/zod';
import { wordPressLoginInputSchema } from 'wp-astrojs-integration';
type LoginPayload = z.input<typeof wordPressLoginInputSchema>;
  • TypeScript88%
  • PHP11%
  • Astro1%