Documentation

Server

IndieAuth Server

A PSR-7-compatible implementation of the request-handling logic for IndieAuth authorization endpoints and token endpoints.

Typical minimal usage looks something like this:

// Somewhere in your app set-up code:
$server = new Taproot\IndieAuth\Server([
  // A secret key, >= 64 characters long.
  'secret' => YOUR_APP_INDIEAUTH_SECRET,

  // A path to store token data, or an object implementing TokenStorageInterface.
  'tokenStorage' => '/../data/auth_tokens/',

  // An authentication callback function, which either returns data about the current user,
  // or redirects to/implements an authentication flow.
  'authenticationHandler' => function (ServerRequestInterface $request, string $authenticationRedirect, ?string $normalizedMeUrl) {
    // If the request is authenticated, return an array with a `me` key containing the
    // canonical URL of the currently logged-in user.
    if ($userUrl = getLoggedInUserUrl($request)) {
      return ['me' => $userUrl];
    }

    // Otherwise, redirect the user to a login page, ensuring that they will be redirected
    // back to the IndieAuth flow with query parameters intact once logged in.
    return new Response('302', ['Location' => 'https://example.com/login?next=' . urlencode($authenticationRedirect)]);
  }
]);

// In your authorization endpoint route:
return $server->handleAuthorizationEndpointRequest($request);

// In your token endpoint route:
return $server->handleTokenEndpointRequest($request);

// In another route (e.g. a micropub route), to authenticate the request:
// (assuming $bearerToken is a token parsed from an “Authorization: Bearer XXXXXX” header
// or access_token property from a request body)
if ($accessToken = $server->getTokenStorage()->getAccessToken($bearerToken)) {
  // Request is authenticated as $accessToken['me'], and is allowed to
  // act according to the scopes listed in $accessToken['scope'].
  $scopes = explode(' ', $accessToken['scope']);
}

Refer to the __construct documentation for further configuration options, and to the documentation for both handling methods for further documentation about them.

Tags
link
https://indieauth.spec.indieweb.org/
link
https://www.rfc-editor.org/rfc/rfc6749.html#section-5.2
link
https://github.com/indieweb/indieauth-client-php
link
https://github.com/Zegnat/php-mindee/blob/development/index.php

Table of Contents

APPROVE_ACTION_KEY  = 'taproot_indieauth_action'
The form data key used for identifying a request as an authorization (consent screen) form submissions.
APPROVE_ACTION_VALUE  = 'approve'
The form data value used for identifying a request as an authorization (consent screen) form submissions.
DEFAULT_CSRF_KEY  = 'taproot_indieauth_server_csrf'
The key used to store the CSRF token everywhere it’s used: Request parameters, Request body, and Cookies.
HANDLE_AUTHENTICATION_REQUEST  = 'authenticationHandler'
HANDLE_NON_INDIEAUTH_REQUEST  = 'handleNonIndieAuthRequestCallback'
HASH_QUERY_STRING_KEY  = 'taproot_indieauth_server_hash'
The query string parameter key used for storing the hash used for validating authorization request parameters.
$authorizationForm  : AuthorizationFormInterface
$csrfMiddleware  : MiddlewareInterface
$exceptionTemplatePath  : string
$handleAuthenticationRequestCallback  : callable
$handleNonIndieAuthRequest  : callable
$httpGetWithEffectiveUrl  : callable
$logger  : LoggerInterface
$requirePkce  : bool
$secret  : string
$tokenStorage  : TokenStorageInterface
__construct()  : self
Constructor
getTokenStorage()  : TokenStorageInterface
handleAuthorizationEndpointRequest()  : ResponseInterface
Handle Authorization Endpoint Request
handleTokenEndpointRequest()  : ResponseInterface
Handle Token Endpoint Request
handleException()  : ResponseInterface
Handle Exception

Constants

APPROVE_ACTION_KEY

The form data key used for identifying a request as an authorization (consent screen) form submissions.

public mixed APPROVE_ACTION_KEY = 'taproot_indieauth_action'

APPROVE_ACTION_VALUE

The form data value used for identifying a request as an authorization (consent screen) form submissions.

public mixed APPROVE_ACTION_VALUE = 'approve'

DEFAULT_CSRF_KEY

The key used to store the CSRF token everywhere it’s used: Request parameters, Request body, and Cookies.

public mixed DEFAULT_CSRF_KEY = 'taproot_indieauth_server_csrf'

HANDLE_AUTHENTICATION_REQUEST

public mixed HANDLE_AUTHENTICATION_REQUEST = 'authenticationHandler'

HANDLE_NON_INDIEAUTH_REQUEST

public mixed HANDLE_NON_INDIEAUTH_REQUEST = 'handleNonIndieAuthRequestCallback'

HASH_QUERY_STRING_KEY

The query string parameter key used for storing the hash used for validating authorization request parameters.

public mixed HASH_QUERY_STRING_KEY = 'taproot_indieauth_server_hash'

Properties

$csrfMiddleware

protected MiddlewareInterface $csrfMiddleware

$exceptionTemplatePath

protected string $exceptionTemplatePath

$handleAuthenticationRequestCallback

protected callable $handleAuthenticationRequestCallback

$handleNonIndieAuthRequest

protected callable $handleNonIndieAuthRequest

$httpGetWithEffectiveUrl

protected callable $httpGetWithEffectiveUrl

$logger

protected LoggerInterface $logger

$requirePkce

protected bool $requirePkce

$secret

protected string $secret

Methods

__construct()

Constructor

public __construct(array<string|int, mixed> $config) : self

Server instances are configured by passing a config array to the constructor.

The following keys are required:

  • authenticationHandler: a callable with the signature function (ServerRequestInterface $request, string $authenticationRedirect, ?string $normalizedMeUrl): array|ResponseInterface. This function is called on IndieAuth authorization requests, after validating the query parameters.

    It should check to see if $request is authenticated, then:

    • If it is authenticated, return an array which MUST have a me key, mapping to the canonical URL of the currently logged-in user. It may additionally have a profile key. These keys will be stored in the authorization code and sent to the client, if successful.
    • If it is not authenticated, either present or redirect to an authentication flow. This flow MUST redirect the logged-in used back to $authenticationRedirect.

    If the request has a valid me parameter, the canonicalized version of it is passed as $normalizedMeUrl. Otherwise, this parameter is null. This parameter can optionally be used as a suggestion for which user to log in as in a multi-user authentication flow, but should NOT be considered valid data.

    If redirecting to an existing authentication flow, this callable can usually be implemented as a closure. The callable may also implement its own authentication logic. For an example, see Callback\SingleUserPasswordAuthenticationCallback.

  • secret: A cryptographically random string with a minimum length of 64 characters. Used to hash and subsequently verify request query parameters which get passed around.

  • tokenStorage: Either an object implementing Storage\TokenStorageInterface, or a string path, which will be passed to Storage\FilesystemJsonStorage. This object handles persisting authorization codes and access tokens, as well as implementation-specific parts of the exchange process which are out of the scope of the Server class (e.g. lifetimes and expiry). Refer to the Storage\TokenStorageInterface documentation for more details.

The following keys may be required depending on which packages you have installed:

  • httpGetWithEffectiveUrl: must be a callable with the following signature: function (string $url): array [ResponseInterface $response, string $effectiveUrl], where $effectiveUrl is the final URL after following any redirects (unfortunately, neither the PSR-7 Response nor the PSR-18 Client interfaces offer a standard way of getting this very important data, hence the unusual return signature). If guzzlehttp/guzzle is installed, this parameter will be created automatically. Otherwise, the user must provide their own callable.

The following keys are optional:

  • authorizationForm: an instance of AuthorizationFormInterface. Defaults to DefaultAuthorizationForm. Refer to that implementation if you wish to replace the consent screen/scope choosing/authorization form.
  • csrfMiddleware: an instance of MiddlewareInterface, which will be used to CSRF-protect the user-facing authorization flow. By default an instance of DoubleSubmitCookieCsrfMiddleware. Refer to that implementation if you want to replace it with your own middleware — you will likely have to either make sure your middleware sets the same request attribute, or alter your templates accordingly.
  • exceptionTemplatePath: string, path to a template which will be used for displaying user-facing errors. Defaults to ../templates/default_exception_response.html.php, refer to that if you wish to write your own template.
  • handleNonIndieAuthRequestCallback: A callback with the following signature: function (ServerRequestInterface $request): ?ResponseInterface which will be called if the authorization endpoint gets a request which is not identified as an IndieAuth request or authorization form submission request. You could use this to handle various requests e.g. client-side requests made by your authentication or authorization pages, if it’s not convenient to put them elsewhere. Returning null will result in a standard invalid_request error being returned.
  • logger: An instance of LoggerInterface. Will be used for internal logging, and will also be set as the logger for any objects passed in config which implement LoggerAwareInterface.
  • requirePKCE: bool, default true. Setting this to false allows requests which don’t provide PKCE parameters (code_challenge, code_challenge_method, code_verifier), under the following conditions:
    • If any of the PKCE parameters are present in an authorization code request, all must be present and valid.
    • If an authorization code request lacks PKCE parameters, the created auth code can only be exchanged by an exchange request without parameters.
    • If authorization codes are stored without PKCE parameters, and then requirePKCE is set to true, these old authorization codes will no longer be redeemable.
Parameters
$config : array<string|int, mixed>

An array of configuration variables

Return values
self

handleAuthorizationEndpointRequest()

Handle Authorization Endpoint Request

public handleAuthorizationEndpointRequest(ServerRequestInterface $request) : ResponseInterface

This method handles all requests to your authorization endpoint, passing execution off to other callbacks when necessary. The logical flow can be summarised as follows:

  • If this request an auth code exchange for profile information, validate the request and return a response or error response.
  • Otherwise, proceed, wrapping all execution in CSRF-protection middleware.
  • Validate the request’s indieauth authorization code request parameters, returning an error response if any are missing or invalid.
  • Call the authentication callback
    • If the callback returned an instance of ResponseInterface, the user is not currently logged in. Return the Response, which will presumably start an authentication flow.
    • Otherwise, the callback returned information about the currently logged-in user. Continue.
  • If this request is an authorization form submission, validate the data, store and authorization code and return a redirect response to the client redirect_uri with code data. On an error, return an appropriate error response.
  • Otherwise, fetch the client_id, parse app data if present, validate the redirect_uri and present the authorization form/consent screen to the user.
  • If none of the above apply, try calling the non-indieauth request handler. If it returns a Response, return that, otherwise return an error response.

This route should NOT be wrapped in additional CSRF-protection, due to the need to handle API POST requests from the client. Make sure you call it from a route which is excluded from any CSRF-protection you might be using. To customise the CSRF protection used internally, refer to the __construct config array documentation for the csrfMiddleware key.

Most user-facing errors are thrown as instances of IndieAuthException, which are passed off to handleException to be turned into an instance of ResponseInterface. If you want to customise error behaviour, one way to do so is to subclass Server and override that method.

Parameters
$request : ServerRequestInterface
Return values
ResponseInterface

handleTokenEndpointRequest()

Handle Token Endpoint Request

public handleTokenEndpointRequest(ServerRequestInterface $request) : ResponseInterface

Handles requests to the IndieAuth token endpoint. The logical flow can be summarised as follows:

  • Check that the request is a code redeeming request. Return an error if not.
  • Ensure that all required parameters are present. Return an error if not.
  • Attempt to exchange the code parameter for an access token. Return an error if it fails.
  • Make sure the client_id and redirect_uri request parameters match those stored in the auth code. If not, revoke the access token and return an error.
  • Make sure the provided code_verifier hashes to the code_challenge stored in the auth code. If not, revoke the access token and return an error.
  • Make sure the granted scope stored in the auth code is not empty. If it is, revoke the access token and return an error.
  • Otherwise, return a success response containing information about the issued access token.

This method must NOT be CSRF-protected as it accepts external requests from client apps.

Parameters
$request : ServerRequestInterface
Return values
ResponseInterface

handleException()

Handle Exception

protected handleException(IndieAuthException $exception) : ResponseInterface

Turns an instance of IndieAuthException into an appropriate instance of ResponseInterface.

Parameters
$exception : IndieAuthException
Return values
ResponseInterface

Search results