Giter VIP home page Giter VIP logo

php-mintoken's Introduction

Mintoken

A minimal IndieAuth compatible Token Endpoint.

Several times I have been asked if there is a token endpoint available to be used together with Selfauth. A minimal solution that would just work for issuing access tokens that is self-hostable on a server with PHP.

I do not have a need for a token endpoint like this myself, thus developing one would go against my selfdogfooding principles. But because I have a general interest in the IndieAuth specification, here is an implementation anyway!

Setup

  1. Download the latest release from GitHub and extract the files.

  2. Create an SQLite database; these instructions assume the database is called tokens.db.

    You can do this from the command line:

    sqlite3 tokens.db < schema.sql

    If you prefer, create the SQLite database by your favourite means and use schema.sql to create the expected tables.

  3. Define trusted authorization endpoints in the settings table of the SQLite database. Mintoken will only check codes with these endpoints, and takes the me value they return as trusted without further verification.

    E.g. if we take the example setup for Selfauth, the endpoint https://example.com/auth/ should be whitelisted.

    From the command line:

    sqlite3 tokens.db 'INSERT INTO settings VALUES ("endpoint", "https://example.com/auth/");'
  4. Upload the SQLite database to a secure directory on your server. Make sure it is not publicly available to the web! This is very important for security reasons.

  5. Edit endpoint.php so line 5 defines the correct path to the SQLite database as the value for MINTOKEN_SQLITE_PATH.

    You should use the full path to tokens.db. For example, define('MINTOKEN_SQLITE_PATH', '../../tokens.db');

  6. Put endpoint.php anywhere on your server where it is available to the web. (This can be in the same folder as Selfauth, for simplicity.)

  7. Make the token endpoint discoverable. Either by defining a Link HTTP header, or adding the following to the <head> of the pages where you also link to your authorization_endpoint:

    <link rel="token_endpoint" href="https://example.com/auth/endpoint.php">

    (The href must point at your endpoint.php file.)

License

The BSD Zero Clause License (0BSD). Please see the LICENSE file for more information.

php-mintoken's People

Contributors

jeremycherfas avatar zegnat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-mintoken's Issues

Rename endpoint.php to index.php

It would be nice to be able to clone this repo into a new directory and have it accessible as the index of that directory. Then the <head> of the sites with which I use this could simply reference <link rel="token_endpoint" href="https://example.com/token/">, rather than <link rel="token_endpoint" href="https://example.com/token/endpoint.php">.

Explore possibility to be database agnostic

While there are a few reasons for why I used an SQLite database as the storage behind Mintoken, it may be possible to let the user setup any PDO connection they want. That way any storage compatible with PDO will be compatible with Mintoken.

A few things to note:

  1. This means the SQLs need to be as globally supported as possible. Is there some standard SQL dialect that should be aimed for?
  2. Are there possible security mismatches between different databases?

As far as 2 goes, look into what the security experts from Paragon are doing in EasyDB.

Solve timing attacks against the tokens

The token should consist of two parts:

  1. unique token identifier,
  2. random blob.

The first part can be used to query the token information from the database. If this part is faulty, nothing can be retrieved, and an error response can be sent. That is exactly as it is now.

That part might be vulnerable to timing attacks against SQLite’s WHERE clause. This means it would take slightly longer to deny a token starting with a known character, versus one without. (See this issue for a similar thing, and the links therein.) Over time the entire valid token can be found.

Mintoken can use the first part of the token to retrieve the second part of the token from the database. Then PHP can do a constant time comparison between the submitted second part and the second part from the database.

This means the second part is protecting us from timing attacks. Even if an attacker has used timing attacks to discover the complete first part of the token, the same attack can not be used against the second part. This protects the entire token against timing attacks.

How should a token endpoint respond to invalid requests?

Mintoken uses error codes taken from RFC 6750: OAuth 2.0 Bearer Token Usage, 3.1. Error Codes.

An HTTP 401 status code with the invalid_token error is used whenever a faulty bearer token is send as part of the IndieAuth Access Token Verification. Example:

php-mintoken/endpoint.php

Lines 196 to 198 in 998e1d3

header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Bearer, error="invalid_token", error_description="The access token is unknown"');
exit();

But when a faulty POST request is made, I am not sure which standard to follow. RFC 6750 also features invalid_request:

   invalid_request
         The request is missing a required parameter, includes an
         unsupported parameter or parameter value, repeats the same
         parameter, uses more than one method for including an access
         token, or is otherwise malformed.  The resource server SHOULD
         respond with the HTTP 400 (Bad Request) status code.

Does it make sense to send a HTTP 400 status code, and only put the error in the WWW-Authenticate? That doesn’t feel right. Should a token endpoint respond with a JSON body instead, following RFC 6749: OAuth 2.0, 5.2. Error Response? But then only parameter mistakes return JSON.

Store only hashed tokens

Currently all tokens are stored in the database as they are. This is fine as long as the database is securely stored in its entirety. But if the database was ever leaked, there is a window of opportunity for the tokens to get used before the owner revokes all of them.

By storing only hashed tokens an attacker would first need to bruteforce the hashes before being able to use any of the tokens. Which may proof to be impossible, or at least give the legitimate owner a safe window in which to revoke their tokens.

README clarifications

  • Create clear instructions for MINTOKEN_SQLITE_PATH, including an example.

    Possible example:

    define('MINTOKEN_SQLITE_PATH', '../../tokens.db');
  • Give a non-command line alternative for adding trusted endpoints to the settings table.

  • Make it clear that one instance of Mintoken can function for multiple websites and multiple authorization endpoints.

Absence of GET param seems to break Mintoken

I am opening this on behalf of Richard, who was at IndieWebCamp Düsseldorf this weekend. This is also the context: we didn't get Mintoken to work and we had minus 3 minutes until demo time.

The endpoint seemed to crash on an error: array_merge() doesn't accept null as a second argument. This means that in the code below, filter_input_array(INPUT_GET, ['me' ...]) does return null. (Line 278)

php-mintoken/endpoint.php

Lines 265 to 281 in 5f508fb

$request = array_merge(
filter_input_array(INPUT_POST, [
'grant_type' => [
'filter' => FILTER_VALIDATE_REGEXP,
'options' => ['regexp' => '@^authorization_code$@'],
],
'code' => [
'filter' => FILTER_VALIDATE_REGEXP,
'options' => ['regexp' => '@^[\x20-\x7E]+$@'],
],
'client_id' => FILTER_VALIDATE_URL,
'redirect_uri' => FILTER_VALIDATE_URL,
]),
filter_input_array(INPUT_GET, [
'me' => FILTER_VALIDATE_URL,
])
);

I cannot find a mention in the spec of the me param in either the POST-body nor the query string for the token request. But: Gimme-a-token does add the me, just in the POST-body, not the query string (which I think is the INPUT_GET but I am not sure how that works).

When we added a ?me= to the endpoint URL manually, it didn't seem to work either, but that's about all the debug-time we had left.

Can you shine any light on this? Where did me come from and why does Mintoken think it should come from GET?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.