Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 88 additions & 6 deletions plugins/wasmer.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
<?

<?php

/**
* Execute a GraphQL query against the Wasmer registry
*
* This function sends a GraphQL query to the Wasmer backend API to fetch
* database credentials and metadata. It's used by the magic login system
* to authenticate and retrieve database connection information.
*
* @param string $registry The GraphQL endpoint URL (e.g., https://registry.wasmer.io/graphql)
* @param string $query The GraphQL query string
* @param array $variables Variables to be used in the GraphQL query
* @param string|null $authToken Optional Bearer token for authentication (the magic login token)
* @return array|null The decoded JSON response, or NULL on error
*/
function wasmer_graphql_query($registry, $query, $variables, $authToken = NULL)
{
// Prepare the payload
Expand Down Expand Up @@ -36,20 +49,69 @@ function wasmer_graphql_query($registry, $query, $variables, $authToken = NULL)
return $responseData;
}

/**
* AdminerWasmer Plugin
*
* This plugin implements the "magic login" feature for Adminer, which allows
* users to authenticate and connect to databases using a temporary token
* provided by the Wasmer backend.
*
* MAGIC LOGIN FLOW:
* =================
* 1. User accesses URL with format: ?magiclogin=<token>&dbid=<database_id>
* 2. The token (starting with "wott_") is a backend authentication token from Wasmer
* 3. Plugin makes GraphQL query to WASMER_GRAPHQL_URL with token as Bearer auth
* 4. GraphQL API validates token and returns database credentials if authorized
* 5. Plugin validates database belongs to WASMER_APP_ID (if configured)
* 6. Database credentials are automatically injected into Adminer's login form
* 7. User is logged into the database automatically without manual credentials
*
* REQUIRED ENVIRONMENT VARIABLES:
* ===============================
* - WASMER_GRAPHQL_URL: URL to Wasmer's GraphQL API (e.g., https://registry.wasmer.io/graphql)
* - WASMER_APP_ID (optional): Restricts access to databases from a specific app ID for security
*
* URL PARAMETERS:
* ===============
* - magiclogin: The authentication token (e.g., wott_IC5J7NJ3AYCBI2FXRRKDB37SBR5NS3RI)
* - dbid: The database ID to connect to
*
* TROUBLESHOOTING:
* ================
* If magic login redirects to login form instead of admin page:
* - Verify WASMER_GRAPHQL_URL environment variable is set correctly
* - Check that the token is valid and not expired
* - Ensure the user has access to the requested database
* - Verify WASMER_APP_ID matches the database's app (if set)
* - Check for CORS errors in browser console
*/
class AdminerWasmer
{
private $wasmerAppId;

function __construct()
{
// Load the app ID from environment to restrict database access to a specific app
$this->wasmerAppId = getenv("WASMER_APP_ID");

// Check if this is a magic login request
// Magic login requires: magiclogin token, dbid parameter, and NO username (to avoid conflicts)
if (isset($_GET["magiclogin"]) && isset($_GET["dbid"]) && !isset($_GET["username"])) {
// Get the Wasmer GraphQL API URL from environment
$url = getenv("WASMER_GRAPHQL_URL");
if (!$url) {
die('Error while doing Magic Login: WASMER_GRAPHQL_URL environment variable is not set.');
}

// Extract the magic login token from the URL parameter
// This token (e.g., wott_IC5J7NJ3AYCBI2FXRRKDB37SBR5NS3RI) is a backend authentication token
// It will be used as a Bearer token when querying the Wasmer GraphQL API
$authToken = $_GET["magiclogin"];

// GraphQL query to fetch database credentials
// The token is validated on the backend, and if authorized, returns:
// - Database connection details (host, port, username, name, password)
// - App information (id, adminUrl) for validation and navigation
$query = <<<'GRAPHQL'
query ($dbid: ID!) {
node(id: $dbid) {
Expand All @@ -71,31 +133,44 @@ function __construct()
$variables = [
"dbid" => $_GET["dbid"]
];

// Execute the GraphQL query with the magic login token
$responseData = wasmer_graphql_query($url, $query, $variables, $authToken);
// Check if the GraphQL query was successful
if (!$responseData) {
die('Error while doing Magic Login: Error occurred while fetching the database credentials.');
}

// Extract node data
// Extract node data from the GraphQL response
$nodeData = isset($responseData['data']['node']) ? $responseData['data']['node'] : null;

// Check if node data exists
// Validate that the database was found and user has access
// The backend validates the token and only returns data if authorized
if ($nodeData === null) {
die('Error while doing Magic Login: Database with the provided id couldn\'t be found, or you don\'t have access to it.');
}

// Security check: If WASMER_APP_ID is configured, ensure the database
// belongs to this specific app. This prevents access to databases from other apps.
if ($this->wasmerAppId && $this->wasmerAppId !== '') {
if ($nodeData["app"]["id"] !== $this->wasmerAppId) {
die('Error while doing Magic Login: Database app doesn\'t match.');
}
}

// Store the admin URL in a cookie so we can display a "Go to Dashboard" link
$adminUrl = $nodeData["app"]["adminUrl"];
setcookie("wasmer_admin_url", $adminUrl, time() + 3600, "/");

// Build the database server address (host:port format)
$server = $nodeData["host"];
if ($nodeData["port"]) {
$server .= ":" . $nodeData["port"];
}

// Inject the database credentials into POST data
// This simulates a manual login form submission, triggering Adminer's authentication
// The user is automatically logged in without seeing the login form
$_POST["auth"] = array(
"driver" => "server",
"server" => $server,
Expand All @@ -117,14 +192,21 @@ function head()
echo '<link rel="stylesheet" href="static/wasmer.css">';
}

/**
* Add custom navigation link to return to Wasmer dashboard
*
* If a magic login was performed, the admin URL is stored in a cookie.
* This function adds a navigation link at the top of Adminer to easily
* return to the Wasmer app dashboard.
*/
function navigation($missing)
{
if (isset($_COOKIE["wasmer_admin_url"])) {
$adminUrl = $_COOKIE["wasmer_admin_url"];
$escapedAdminUrl = htmlspecialchars($adminUrl);
if ($escapedAdminUrl) {
?>
<a id="wasmer-dashboard-link" href="<? echo "$escapedAdminUrl"; ?>">
<a id="wasmer-dashboard-link" href="<?php echo $escapedAdminUrl; ?>">
<svg viewBox="0 0 29 34" height="1em" width="1em">
<g clip-path="url(#prefix__clip0_1268_12249)">
<path d="M0 12.3582C0 10.4725 0 9.52973 0.507307 9.23683C1.01461 8.94394 1.83111 9.41534 3.46411 10.3581L10.784 14.5843C12.417 15.5271 13.2335 15.9985 13.7408 16.8771C14.2481 17.7558 14.2481 18.6986 14.2481 20.5843V29.0364C14.2481 30.9221 14.2481 31.8649 13.7408 32.1578C13.2335 32.4507 12.417 31.9793 10.784 31.0365L3.4641 26.8103C1.83111 25.8675 1.01461 25.3961 0.507307 24.5175C0 23.6388 0 22.696 0 20.8103V12.3582Z"></path>
Expand All @@ -141,7 +223,7 @@ function navigation($missing)
Wasmer App Dashboard
</a>

<?
<?php
}
}
}
Expand Down
76 changes: 75 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Customizations for [Adminer](https://www.adminer.org), the best database managem
- SQL autocomplete plugin
- plugin that dumps to PHP code
- plugin that saves menu scroll position
- plugin that allows magic login with Wasmer (requires `WASMER_GRAPHQL_URL` environment variable)
- plugin that allows magic login with Wasmer (see [Magic Login](#magic-login) section below)

Testing
-------
Expand Down Expand Up @@ -46,6 +46,80 @@ Autologin
If you want to login automatically (use that for non-public environment only, e.g. localhost), you can do that by setting environment variables `ADMINER_SERVER`, `ADMINER_USERNAME` and `ADMINER_PASSWORD`. If you want to be automatically redirected to https, set `ADMINER_HTTPS_REDIRECT` environment variable to `true`.


Magic Login
-----------

The magic login feature allows users to authenticate and connect to databases using a temporary token from the Wasmer backend, without manually entering credentials.

### How It Works

1. **User receives a magic login URL** from the Wasmer platform with the format:
```
https://your-adminer.wasmer.dev/?magiclogin=<token>&dbid=<database_id>
```
Example:
```
https://wordpress-865zz.wasmer.dev/?magiclogin=wott_IC5J7NJ3AYCBI2FXRRKDB37SBR5NS3RI&dbid=db_abc123
```

2. **The token is validated** by querying the Wasmer GraphQL API:
- The token (starting with `wott_`) is a backend authentication token from Wasmer
- Adminer sends a GraphQL query to `WASMER_GRAPHQL_URL` with the token as Bearer authentication
- The Wasmer backend validates the token and returns database credentials if the user is authorized

3. **Database credentials are retrieved** from the GraphQL response:
- Host and port
- Username and password
- Database name
- App metadata (for validation and navigation)

4. **Automatic login** is performed:
- The credentials are automatically injected into Adminer's authentication system
- User is logged into the database without seeing the login form
- A "Go to Wasmer App Dashboard" link is added to the navigation

### Required Environment Variables

- **`WASMER_GRAPHQL_URL`** (required): URL to Wasmer's GraphQL API
- Example: `https://registry.wasmer.io/graphql`
- This is where the magic login token is validated

- **`WASMER_APP_ID`** (optional): Restricts database access to a specific app ID
- When set, only databases belonging to this app can be accessed
- Provides additional security layer
- Example: `app_xyz789`

### Troubleshooting

If the magic login link redirects to the login form instead of the admin page:

1. **Check environment variables**: Ensure `WASMER_GRAPHQL_URL` is set correctly
```bash
echo $WASMER_GRAPHQL_URL
```

2. **Verify token validity**: The `wott_*` token may be expired or invalid
- Tokens are temporary and have an expiration time
- Request a new magic login link

3. **Check authorization**: The user may not have access to the requested database
- Verify the user has permissions for the database ID in the Wasmer platform

4. **Validate app matching**: If `WASMER_APP_ID` is set, ensure it matches the database's app
- The database must belong to the configured app

5. **Check for CORS errors**: Look in the browser console for CORS-related errors
- This may indicate domain configuration issues

### Security Notes

- The magic login token (`wott_*`) is validated by the Wasmer backend, not stored locally.
- Token validation happens server-side via the GraphQL API.
- Tokens are temporary and expire after a certain time period.
- When `WASMER_APP_ID` is configured, only databases from that specific app can be accessed.
- The admin URL is stored in a cookie for 1 hour to enable navigation back to the dashboard.


Login page
----------

Expand Down