| 1 |
<?php |
| 2 |
/** |
| 3 |
* `templately/auth-login-with-google` — get the URL to connect this site to |
| 4 |
* a Templately account via Google sign-in (spec 041-mcp-abilities, Auth |
| 5 |
* Tools addendum). |
| 6 |
* |
| 7 |
* `Http::google_auth_url()` is a pure URL builder with no session/Options |
| 8 |
* dependency, so it is safe to call directly (no singleton-caching gotcha |
| 9 |
* applies here, unlike auth-status/auth-logout). Completing the sign-in |
| 10 |
* itself requires a real browser — an agent cannot finish an OAuth flow |
| 11 |
* headlessly, so this ability only starts it and hands back the URL. |
| 12 |
* |
| 13 |
* Manual key handoff, not auto-attribution: this call happens headlessly |
| 14 |
* (no browser cookie session), and the browser that later completes the |
| 15 |
* Google redirect may not be logged into wp-admin either — so there's no |
| 16 |
* ambient session to attribute a connection to automatically. Rather than |
| 17 |
* bridging attribution into that session (an earlier approach here — see |
| 18 |
* git history / MCP/CLAUDE.md), the flow instead marks this login as |
| 19 |
* MCP-initiated via a one-time, 5-minute transient keyed by a |
| 20 |
* self-generated token, and `MCP::intercept_mcp_oauth_callback()` |
| 21 |
* (registered on `init` at priority 5, in MCP.php) shows the resulting |
| 22 |
* `api_key` directly to the user on callback instead of letting |
| 23 |
* Plugin::google_login_handler() auto-consume it. The user copies that key |
| 24 |
* back to the agent, which then calls the existing |
| 25 |
* `templately/auth-login-with-api-key` ability in its own genuinely |
| 26 |
* authenticated MCP session — no attribution bridging needed there at all. |
| 27 |
* |
| 28 |
* The token travels via `google_auth_url()`'s `$redirect_to` param, NOT the |
| 29 |
* URL's own `state` param: templately-backend's own docs confirm `state` is |
| 30 |
* cached server-side only for the Google round-trip and never echoed back |
| 31 |
* to the site, while `site_url` (which `redirect_to` folds into) comes back |
| 32 |
* intact — confirmed live too. `MCP::intercept_mcp_oauth_callback()` reads |
| 33 |
* the token from `$_GET['redirect-to']` — that same key is read by |
| 34 |
* `Plugin::google_login_handler()` (priority 10) for its own unrelated |
| 35 |
* "reopen this editor path after login" purpose, but for an MCP-initiated |
| 36 |
* login that handler never runs at all (the interceptor exits first). |
| 37 |
* |
| 38 |
* @package Templately\Modules\McpAbilities\Abilities |
| 39 |
*/ |
| 40 |
|
| 41 |
namespace Templately\Modules\McpAbilities\Abilities; |
| 42 |
|
| 43 |
use Templately\Modules\McpAbilities\MCP; |
| 44 |
use Templately\Modules\McpCore\Registry\ToolDescriptor; |
| 45 |
use Templately\Modules\McpCore\Support\Permissions; |
| 46 |
use Templately\Utils\Http; |
| 47 |
|
| 48 |
class AuthLoginWithGoogleAbility { |
| 49 |
|
| 50 |
const ID = 'templately/auth-login-with-google'; |
| 51 |
|
| 52 |
public static function descriptor(): array { |
| 53 |
return [ |
| 54 |
'id' => self::ID, |
| 55 |
'label' => __( 'Start Templately Google Sign-In', 'templately' ), |
| 56 |
'description' => __( 'Get the URL to connect this site to a Templately account via Google sign-in. Completing sign-in requires opening the URL in a real browser.', 'templately' ), |
| 57 |
'input_schema' => [ |
| 58 |
'type' => 'object', |
| 59 |
'properties' => (object) [], |
| 60 |
'additionalProperties' => false, |
| 61 |
], |
| 62 |
'output_schema' => [ |
| 63 |
'type' => 'object', |
| 64 |
], |
| 65 |
'execute_callback' => [ self::class, 'execute' ], |
| 66 |
'permission_callback' => [ Permissions::class, 'can_use_abilities' ], |
| 67 |
'access_level' => ToolDescriptor::ACCESS_FULL, |
| 68 |
'annotations' => [ 'readonly' => true, 'destructive' => false, 'idempotent' => false ], |
| 69 |
]; |
| 70 |
} |
| 71 |
|
| 72 |
/** |
| 73 |
* @param array $input |
| 74 |
* @return array |
| 75 |
*/ |
| 76 |
public static function execute( array $input ): array { |
| 77 |
// Self-generated — guaranteed to already match MCP::is_valid_oauth_state()'s |
| 78 |
// format, so no extraction/validation round-trip is needed here. |
| 79 |
$token = wp_generate_password( 32, false ); |
| 80 |
|
| 81 |
$url = Http::get_instance()->google_auth_url( $token ); |
| 82 |
|
| 83 |
// Marker only — no user id needed. Attribution happens later, when |
| 84 |
// the user pastes the api_key back for an auth-login-with-api-key |
| 85 |
// call in this agent's own authenticated session. |
| 86 |
set_transient( |
| 87 |
MCP::OAUTH_TOKEN_TRANSIENT_PREFIX . $token, |
| 88 |
true, |
| 89 |
5 * MINUTE_IN_SECONDS |
| 90 |
); |
| 91 |
|
| 92 |
return [ |
| 93 |
'url' => $url, |
| 94 |
'instructions' => __( 'Open this URL in a browser and complete Google sign-in. Instead of connecting automatically, the page will show you an API key — copy it and give it to the agent, which will call templately-auth-login-with-api-key to finish connecting this site.', 'templately' ), |
| 95 |
]; |
| 96 |
} |
| 97 |
} |
| 98 |
|