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
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
$authorizationForm
protected
AuthorizationFormInterface
$authorizationForm
$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
$tokenStorage
protected
TokenStorageInterface
$tokenStorage
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 signaturefunction (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 aprofile
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
. - If it is authenticated, return an array which MUST have a
-
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 implementingStorage\TokenStorageInterface
, or a string path, which will be passed toStorage\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 theStorage\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). Ifguzzlehttp/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 ofAuthorizationFormInterface
. Defaults toDefaultAuthorizationForm
. Refer to that implementation if you wish to replace the consent screen/scope choosing/authorization form. -
csrfMiddleware
: an instance ofMiddlewareInterface
, which will be used to CSRF-protect the user-facing authorization flow. By default an instance ofDoubleSubmitCookieCsrfMiddleware
. 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. Returningnull
will result in a standardinvalid_request
error being returned. -
logger
: An instance ofLoggerInterface
. Will be used for internal logging, and will also be set as the logger for any objects passed in config which implementLoggerAwareInterface
. -
requirePKCE
: bool, default true. Setting this tofalse
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 totrue
, these old authorization codes will no longer be redeemable.
Parameters
- $config : array<string|int, mixed>
-
An array of configuration variables
Return values
self —getTokenStorage()
public
getTokenStorage() : TokenStorageInterface
Return values
TokenStorageInterface —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