Skip to content

Commit 4045bfe

Browse files
committed
feat: add WP-CLI commands and plugin README
Register CLI commands under `wp workos` namespace for scripting, bulk operations, and diagnostics: - `wp workos status` — plugin config and health - `wp workos user` — get, list, link, unlink, sync, import - `wp workos org` — list, get, sync, members, add/remove-member - `wp workos sync` — bulk push, pull, import, orgs with progress bars and dry-run Add comprehensive README.md documenting all plugin features, configuration, authentication flows, webhooks, REST API auth, database schema, WP-CLI reference, and development setup.
1 parent 5e066f5 commit 4045bfe

7 files changed

Lines changed: 1697 additions & 0 deletions

File tree

‎README.md‎

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
# WorkOS Identity
2+
3+
Enterprise identity management for WordPress powered by [WorkOS](https://workos.com). SSO, directory sync, MFA, and user management.
4+
5+
**Requires PHP:** 7.4+
6+
**Requires WordPress:** 5.9+
7+
**License:** GPL-2.0-or-later
8+
9+
## Features
10+
11+
- **Single Sign-On (SSO)** via WorkOS AuthKit — redirect or headless login modes
12+
- **Directory Sync (SCIM)** — automatic user provisioning and deprovisioning from your identity provider
13+
- **Role Mapping** — map WorkOS organization roles to WordPress roles
14+
- **Organization Management** — local caching of WorkOS organizations with multisite support
15+
- **Webhook Processing** — real-time sync of user, organization, and directory events
16+
- **REST API Authentication** — Bearer token auth for headless/decoupled WordPress
17+
- **Audit Logging** — forward WordPress events (login, logout, post changes, user changes) to WorkOS Audit Logs
18+
- **WP-CLI Commands** — full CLI access for scripting, bulk operations, and diagnostics
19+
20+
## Installation
21+
22+
1. Upload the plugin to `wp-content/plugins/workos/`.
23+
2. Run `composer install` in the plugin directory.
24+
3. Activate the plugin in WordPress admin.
25+
4. Navigate to **Settings > WorkOS** to configure.
26+
27+
## Configuration
28+
29+
### Admin UI
30+
31+
Go to **Settings > WorkOS** in the WordPress admin. The settings page has three tabs:
32+
33+
| Tab | Contents |
34+
|---|---|
35+
| **Settings** | API Key, Client ID, Environment ID, webhook secret, login mode, password fallback, audit logging toggle |
36+
| **Organization** | Select or create a WorkOS organization |
37+
| **Users** | Deprovision action, content reassignment, role mapping table |
38+
39+
The plugin supports two environments — **Production** and **Staging** — with separate credentials for each. An admin bar badge shows which environment is active.
40+
41+
### wp-config.php Constants
42+
43+
All credentials can be set via constants in `wp-config.php`, which take precedence over database values:
44+
45+
```php
46+
// Generic (used for any environment)
47+
define( 'WORKOS_API_KEY', 'sk_live_...' );
48+
define( 'WORKOS_CLIENT_ID', 'client_...' );
49+
define( 'WORKOS_WEBHOOK_SECRET', 'whsec_...' );
50+
define( 'WORKOS_ORGANIZATION_ID', 'org_...' );
51+
define( 'WORKOS_ENVIRONMENT_ID', 'environment_...' );
52+
define( 'WORKOS_ENVIRONMENT', 'production' ); // Lock active environment
53+
54+
// Per-environment (takes priority over generic)
55+
define( 'WORKOS_PRODUCTION_API_KEY', 'sk_live_...' );
56+
define( 'WORKOS_STAGING_API_KEY', 'sk_test_...' );
57+
```
58+
59+
## Authentication
60+
61+
### Redirect Mode (Default)
62+
63+
Users visiting `wp-login.php` are redirected to WorkOS AuthKit. After authentication, WorkOS redirects back to `/workos/callback` where the plugin exchanges the authorization code for user data and tokens.
64+
65+
### Headless Mode
66+
67+
The plugin intercepts WordPress's `authenticate` filter and validates credentials directly against the WorkOS API using email and password. This mode is useful for custom login forms.
68+
69+
### Password Fallback
70+
71+
When enabled, WordPress native password authentication remains available alongside WorkOS. Password reset and registration forms fall back to WordPress defaults.
72+
73+
## Webhooks
74+
75+
Configure your WorkOS dashboard to send webhooks to:
76+
77+
```
78+
https://yoursite.com/wp-json/workos/v1/webhook
79+
```
80+
81+
The plugin processes these event types:
82+
83+
| Category | Events |
84+
|---|---|
85+
| **Users** | `user.created`, `user.updated`, `user.deleted` |
86+
| **Directory Sync** | `dsync.user.created`, `dsync.user.updated`, `dsync.user.deleted`, `dsync.group.user_added`, `dsync.group.user_removed` |
87+
| **Organizations** | `organization.created`, `organization.updated` |
88+
| **Memberships** | `organization_membership.created`, `organization_membership.updated`, `organization_membership.deleted` |
89+
| **Connections** | `connection.activated`, `connection.deactivated`, `connection.deleted` |
90+
| **Auth** | `authentication.email_verification_succeeded` |
91+
92+
All events are verified against the webhook signing secret before processing.
93+
94+
## REST API Authentication
95+
96+
The plugin adds Bearer token authentication to the WordPress REST API. Send the WorkOS access token in the `Authorization` header:
97+
98+
```
99+
Authorization: Bearer <workos_access_token>
100+
```
101+
102+
The token is verified using WorkOS JWKS and mapped to a WordPress user via their linked WorkOS ID.
103+
104+
## Database Tables
105+
106+
The plugin creates three custom tables on activation:
107+
108+
| Table | Purpose |
109+
|---|---|
110+
| `{prefix}_workos_organizations` | Cached WorkOS organization data (name, slug, domains) |
111+
| `{prefix}_workos_org_memberships` | User-to-organization memberships with roles |
112+
| `{prefix}_workos_org_sites` | Organization-to-site mapping (multisite) |
113+
114+
User linking is stored in standard WordPress usermeta (`_workos_user_id`, `_workos_org_id`, `_workos_last_synced_at`, `_workos_deactivated`).
115+
116+
## WP-CLI Commands
117+
118+
All commands are registered under the `wp workos` namespace.
119+
120+
### Status
121+
122+
```bash
123+
# Show plugin configuration and health
124+
wp workos status
125+
126+
# Output as JSON
127+
wp workos status --format=json
128+
```
129+
130+
Displays: environment, API key (masked), client ID, organization ID, environment ID, enabled status, login mode, database version, and plugin version. The `source` column shows whether each value comes from a `constant` or the `database`.
131+
132+
---
133+
134+
### User Management
135+
136+
```bash
137+
# Get a user with WorkOS metadata
138+
wp workos user get <id> [--by=<id|email|workos_id>] [--format=<format>]
139+
140+
# List users with WorkOS link status
141+
wp workos user list [--linked] [--unlinked] [--role=<role>] [--format=<format>] [--fields=<fields>]
142+
143+
# Get user IDs for piping to other commands
144+
wp workos user list --unlinked --format=ids
145+
146+
# Link a WP user to a WorkOS user (validates via API)
147+
wp workos user link <wp_user_id> <workos_user_id>
148+
149+
# Remove WorkOS link from a user
150+
wp workos user unlink <wp_user_id> [--yes]
151+
152+
# Sync a single user: push to WorkOS (default) or pull from WorkOS
153+
wp workos user sync <wp_user_id> [--direction=<push|pull>]
154+
155+
# Import a single WorkOS user into WordPress
156+
wp workos user import <workos_user_id> [--porcelain] [--yes]
157+
```
158+
159+
**Examples:**
160+
161+
```bash
162+
# Find a user by email and show their WorkOS metadata
163+
wp workos user get admin@example.com --by=email
164+
165+
# Look up a user by their WorkOS ID
166+
wp workos user get user_01HXYZ --by=workos_id --format=json
167+
168+
# List all users that haven't been synced to WorkOS yet
169+
wp workos user list --unlinked --role=subscriber
170+
171+
# Pull latest profile data from WorkOS for a linked user
172+
wp workos user sync 42 --direction=pull
173+
174+
# Import a WorkOS user and get just the WP user ID
175+
wp workos user import user_01HXYZ --porcelain --yes
176+
```
177+
178+
---
179+
180+
### Organization Management
181+
182+
```bash
183+
# List local organizations
184+
wp workos org list [--source=<local|remote>] [--format=<format>]
185+
186+
# Get a single organization
187+
wp workos org get <id> [--by=<id|workos_id|remote>] [--format=<format>]
188+
189+
# Sync an organization from WorkOS API to local database
190+
wp workos org sync <workos_org_id>
191+
192+
# List organization members
193+
wp workos org members <id> [--by=<id|workos_id>] [--format=<format>]
194+
195+
# Add a user to a local organization
196+
wp workos org add-member <org_id> <user_id> [--role=<role>]
197+
198+
# Remove a user from a local organization
199+
wp workos org remove-member <org_id> <user_id> [--yes]
200+
```
201+
202+
**Examples:**
203+
204+
```bash
205+
# List organizations from the WorkOS API
206+
wp workos org list --source=remote --format=json
207+
208+
# Fetch an org directly from WorkOS without needing a local record
209+
wp workos org get org_01HXYZ --by=remote
210+
211+
# Sync an organization and view its members
212+
wp workos org sync org_01HXYZ
213+
wp workos org members org_01HXYZ --by=workos_id
214+
215+
# Add a user to an org as admin
216+
wp workos org add-member 1 42 --role=admin
217+
```
218+
219+
---
220+
221+
### Bulk Sync Operations
222+
223+
All bulk commands support `--dry-run` to preview changes, `--yes` to skip confirmation, `--limit` to cap the number of items processed, and display progress bars during execution.
224+
225+
```bash
226+
# Push all unlinked WP users to WorkOS
227+
wp workos sync push [--role=<role>] [--limit=<n>] [--dry-run] [--yes]
228+
229+
# Re-sync all linked users from WorkOS
230+
wp workos sync pull [--limit=<n>] [--dry-run] [--yes]
231+
232+
# Import WorkOS users into WordPress
233+
wp workos sync import [--organization_id=<id>] [--limit=<n>] [--dry-run] [--yes]
234+
235+
# Import all organizations from WorkOS
236+
wp workos sync orgs [--limit=<n>] [--dry-run] [--yes]
237+
```
238+
239+
**Examples:**
240+
241+
```bash
242+
# Preview what a full user push would do
243+
wp workos sync push --dry-run
244+
245+
# Push only subscribers, 50 at a time
246+
wp workos sync push --role=subscriber --limit=50 --yes
247+
248+
# Re-sync all linked users from WorkOS
249+
wp workos sync pull --yes
250+
251+
# Import the first 10 WorkOS users from a specific organization
252+
wp workos sync import --organization_id=org_01HXYZ --limit=10 --yes
253+
254+
# Import all organizations
255+
wp workos sync orgs --yes
256+
```
257+
258+
**Output format:** All bulk commands report a summary on completion:
259+
260+
```
261+
Success: 45 synced, 2 failed, 3 skipped.
262+
```
263+
264+
Non-fatal errors are shown as warnings during execution and counted in the summary.
265+
266+
---
267+
268+
### Common Options
269+
270+
All commands that display data support these output formats:
271+
272+
| Flag | Format |
273+
|---|---|
274+
| `--format=table` | ASCII table (default) |
275+
| `--format=json` | JSON array |
276+
| `--format=yaml` | YAML |
277+
| `--format=csv` | CSV |
278+
| `--format=ids` | Space-separated IDs (user list only) |
279+
280+
## Development
281+
282+
### Requirements
283+
284+
- PHP 7.4+
285+
- Composer
286+
- [slic](https://github.com/developer-starter/slic) (for running tests)
287+
288+
### Setup
289+
290+
```bash
291+
composer install
292+
```
293+
294+
### Running Tests
295+
296+
```bash
297+
# Using slic (Docker-based)
298+
cd wp-content/plugins
299+
slic here
300+
slic use workos
301+
slic run wpunit
302+
303+
# Using Composer
304+
composer test:wpunit
305+
```
306+
307+
### Code Standards
308+
309+
```bash
310+
# Check
311+
composer lint
312+
313+
# Auto-fix
314+
composer lint:fix
315+
```
316+
317+
### Architecture
318+
319+
The plugin uses a DI container (di52) with a feature-controller pattern:
320+
321+
```
322+
workos.php # Entry point
323+
src/WorkOS/Plugin.php # Bootstrap, container init
324+
src/WorkOS/Controller.php # Main controller, registers feature controllers
325+
src/WorkOS/Config.php # Centralized config with constant overrides
326+
src/WorkOS/
327+
Admin/Controller.php # Settings UI, user list enhancements
328+
Auth/Controller.php # Login, registration, password reset
329+
Sync/Controller.php # UserSync, RoleMapper, DirectorySync, AuditLog
330+
REST/Controller.php # Bearer token authentication
331+
Webhook/Controller.php # Webhook receiver
332+
Organization/Controller.php # Organization management
333+
CLI/Controller.php # WP-CLI commands
334+
```
335+
336+
Each controller extends `Contracts\Controller` and implements `isActive()` for conditional activation (e.g., `Admin\Controller` only activates in `is_admin()`, `CLI\Controller` only activates under `WP_CLI`).

‎src/WorkOS/CLI/Controller.php‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/**
3+
* CLI Controller
4+
*
5+
* @package WorkOS\CLI
6+
*/
7+
8+
namespace WorkOS\CLI;
9+
10+
use WorkOS\Contracts\Controller as BaseController;
11+
12+
/**
13+
* Registers WP-CLI commands when running under `wp`.
14+
*/
15+
class Controller extends BaseController {
16+
17+
/**
18+
* Only active when WP-CLI is available.
19+
*
20+
* @return bool
21+
*/
22+
public function isActive(): bool {
23+
return defined( 'WP_CLI' ) && WP_CLI;
24+
}
25+
26+
/**
27+
* Register CLI commands.
28+
*
29+
* @return void
30+
*/
31+
protected function doRegister(): void {
32+
\WP_CLI::add_command( 'workos status', StatusCommand::class );
33+
\WP_CLI::add_command( 'workos user', UserCommand::class );
34+
\WP_CLI::add_command( 'workos org', OrgCommand::class );
35+
\WP_CLI::add_command( 'workos sync', SyncCommand::class );
36+
}
37+
38+
/**
39+
* Unregister.
40+
*
41+
* @return void
42+
*/
43+
protected function doUnregister(): void {
44+
}
45+
}

0 commit comments

Comments
 (0)