GithubHelp home page GithubHelp logo

eljam / guzzle-jwt-middleware Goto Github PK

View Code? Open in Web Editor NEW
29.0 1.0 13.0 42 KB

Guzzle Jwt middleware

License: MIT License

PHP 100.00%
jwt php json guzzlehttp guzzle-middleware middleware bearer

guzzle-jwt-middleware's Introduction

Guzzle Jwt middleware

Build Status Code Quality Code Coverage SensioLabsInsight Latest Unstable Version Latest Stable Version Downloads license

Introduction

Works great with LexikJWTAuthenticationBundle

Installation

composer require eljam/guzzle-jwt-middleware

Usage

<?php

use Eljam\GuzzleJwt\JwtMiddleware;
use Eljam\GuzzleJwt\Manager\JwtManager;
use Eljam\GuzzleJwt\Strategy\Auth\QueryAuthStrategy;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;

require_once 'vendor/autoload.php';

//Create your auth strategy
$authStrategy = new QueryAuthStrategy(['username' => 'admin', 'password' => 'admin']);

//Optionnal: create your persistence strategy
$persistenceStrategy = null;

$baseUri = 'http://api.example.org/';

// Create authClient
$authClient = new Client(['base_uri' => $baseUri]);

//Create the JwtManager
$jwtManager = new JwtManager(
    $authClient,
    $authStrategy,
    $persistenceStrategy,
    [
        'token_url' => '/api/token',
    ]
);

// Create a HandlerStack
$stack = HandlerStack::create();

// Add middleware
$stack->push(new JwtMiddleware($jwtManager));

$client = new Client(['handler' => $stack, 'base_uri' => $baseUri]);

try {
    $response = $client->get('/api/ping');
    echo($response->getBody());
} catch (TransferException $e) {
    echo $e->getMessage();
}

//response
//{"data":"pong"}

Auth Strategies

QueryAuthStrategy

$authStrategy = new QueryAuthStrategy(
    [
        'username' => 'admin',
        'password' => 'admin',
        'query_fields' => ['username', 'password'],
    ]
);

FormAuthStrategy

$authStrategy = new FormAuthStrategy(
    [
        'username' => 'admin',
        'password' => 'admin',
        'form_fields' => ['username', 'password'],
    ]
);

HttpBasicAuthStrategy

$authStrategy = new HttpBasicAuthStrategy(
    [
        'username' => 'admin',
        'password' => 'password',
    ]
);

JsonAuthStrategy

$authStrategy = new JsonAuthStrategy(
    [
        'username' => 'admin',
        'password' => 'admin',
        'json_fields' => ['username', 'password'],
    ]
);

Persistence

To avoid requesting a token everytime php runs, you can pass to JwtManager an implementation of TokenPersistenceInterface. By default NullTokenPersistence will be used.

Simpe cache adapter (PSR-16)

If you have any PSR-16 compatible cache, you can use it as a persistence handler:

<?php

use Eljam\GuzzleJwt\Persistence\SimpleCacheTokenPersistence;
use Psr\SimpleCache\CacheInterface;

/**
 * @var CacheInterface
 */
$psr16cache;

$persistenceStrategy = new SimpleCacheTokenPersistence($psr16cache);

Optionnally you can specify the TTL and cache key used:

<?php

use Eljam\GuzzleJwt\Persistence\SimpleCacheTokenPersistence;
use Psr\SimpleCache\CacheInterface;

/**
 * @var CacheInterface
 */
$psr16cache;

$ttl = 1800;
$cacheKey = 'myUniqueKey';

$persistenceStrategy = new SimpleCacheTokenPersistence($psr16cache, $ttl, $cacheKey);

Custom persistence

You may create you own persistence handler by implementing the TokenPersistenceInterface:

namespace App\Jwt\Persistence;

use Eljam\GuzzleJwt\Persistence\TokenPersistenceInterface;

class MyCustomPersistence implements TokenPersistenceInterface
{
    /**
     * Save the token data.
     *
     * @param JwtToken $token
     */
    public function saveToken(JwtToken $token)
    {
        // Use APCu, Redis or whatever fits your needs.
        return;
    }

    /**
     * Retrieve the token from storage and return it.
     * Return null if nothing is stored.
     *
     * @return JwtToken Restored token
     */
    public function restoreToken()
    {
        return null;
    }

    /**
     * Delete the saved token data.
     */
    public function deleteToken()
    {
        return;
    }

    /**
     * Returns true if a token exists (although it may not be valid)
     *
     * @return bool
     */
    public function hasToken()
    {
        return false;
    }
}

Token key

Property accessor

With the property accessor you can point to a node in your json.

Json Example:

{
    "status": "success",
    "message": "Login successful",
    "payload": {
        "token": "1453720507"
    },
    "expires_in": 3600
}

Library configuration:

$jwtManager = new JwtManager(
    $authClient,
    $authStrategy,
    $persistenceStrategy,
    [
        'token_url'  => '/api/token',
        'token_key'  => 'payload.token',
        'expire_key' => 'expires_in'
    ]
);

Default behavior

By default this library assumes your json response has a key token, something like this:

{
    token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9..."
}

but now you can change the token_key in the JwtManager options:

$jwtManager = new JwtManager(
    $authClient,
    $authStrategy,
    $persistenceStrategy,
    [
        'token_url' => '/api/token',
        'token_key' => 'access_token',
    ]
);

Authorization Header Type

Some endpoints use different Authorization header types (Bearer, JWT, etc...).

The default is Bearer, but another type can be supplied in the middleware:

$stack->push(new JwtMiddleware($jwtManager, 'JWT'));

Cached token

To avoid too many calls between multiple request, there is a cache system.

Json example:

{
    token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9...",
    expires_in: "3600"
}
$jwtManager = new JwtManager(
    $authClient,
    $authStrategy,
    $persistenceStrategy,
    [
        'token_url' => '/api/token',
        'token_key' => 'access_token',
        'expire_key' => 'expires_in', # default is expires_in if not set
    ]
);

The bundle natively supports the exp field in the JWT payload.

guzzle-jwt-middleware's People

Stargazers

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

Watchers

 avatar

guzzle-jwt-middleware's Issues

Token sometimes re-used too close to expiration.

Hi,

I'm running into an issue that a token is re-used very close to the expiration of the token, which then cause a not-authorized response from the API.

I believe this is caused by the following line of code:

$expiration = new \DateTime('now + ' . $expiresIn . ' seconds');

or you could argue that this should be handled in the validation part:

return (new \DateTime()) < $this->expiration;

In my case I have solved this for now by adding a small safety margin:

--- a/src/Manager/JwtManager.php	2022-11-23 10:01:15.148041661 +0100
+++ b/src/Manager/JwtManager.php	2022-11-23 10:00:33.247774501 +0100
@@ -132,7 +132,7 @@
         }

         if ($expiresIn) {
-            $expiration = new \DateTime('now + ' . $expiresIn . ' seconds');
+            $expiration = new \DateTime('now + ' . ((int)($expiresIn * 0.9)) . ' seconds');
         } elseif (count($jwtParts = explode('.', $tokenValue)) === 3
             && is_array($payload = json_decode(base64_decode($jwtParts[1]), true))
             // https://tools.ietf.org/html/rfc7519.html#section-4.1.4

Perhaps you could think of a more elegant solution.

Rebrand package

As far as I can read, this package is a more generic Bearer token middleware :).

Maybe this should be stated more clear in the readme?

Not work ...

Sorry for the title, i have not inspiration.
I try to debug but i don't understand.

I write some beautiful class for use it but that not work. And i try to look like your exemple on readme for test if I forget something but that not work.

PS : the class have inheriance with Client of Guzzle

(into a class i have)
      $authStrategy = new FormAuthStrategy([
            'username' => $this->username,
            'password' => $this->password
        ]);

        //Create the JwtManager
        $jwtManager = new JwtManager($this, $authStrategy, [
            'token_url' => 'app_dev.php/api/post/en_GB/login_check'
        ]);

        // Create a HandlerStack
        $stack = HandlerStack::create();
        $stack->push(new JwtMiddleware($jwtManager));

        $client = new Client(['handler' => $stack, 'base_uri' => 'http://local.dev']);
        try {
            $locale = 'en_GB';

            $authorInfo = [
                'firstname' => 'test',
                'lastname' => 'test',
                'email' => 'test'
            ];

            $response = $client->post('app_dev.php/api/post/'.$locale.'/author/post.json', [
                'form_params' => [
                    'api_author' => $authorInfo
                ]
            ]);

            echo($response->getBody());
        } catch (TransferException $e) {
            echo $e->getMessage();
        }

Result:
I have a good call to login api, I have a success with a token (see with some dump into class)
But for the call to post.json, i have not the Authorization Header ! and i have 401 (see into profiler of distant call)

I add some dump() into Guzzle code for understand, and I see the token once for the call but I don't understand how Guzzle work. Too many callback with Guzzle and Promise.

root@qanda:/var/www/qanda# composer show | grep guzzle
Do not run Composer as root/super user! See https://getcomposer.org/root for details
eljam/guzzle-jwt-middleware              v0.4.0              A jwt authentication middleware for guzzle 6
guzzle/guzzle                            v3.8.1              Guzzle is a PHP HTTP client library and framework for building RESTful web service clients
guzzlehttp/guzzle                        6.2.2               Guzzle is a PHP HTTP client library
guzzlehttp/promises                      1.3.0               Guzzle promises library
guzzlehttp/psr7                          1.3.1               PSR-7 message implementation
kevinrob/guzzle-cache-middleware         v1.4.3              A HTTP/1.1 Cache for Guzzle 6. It's a simple Middleware to be added in the HandlerStack. (RFC 7234)

Have you test the POST call ? do you have any idea ?

Thanks

Token on json response

In JWTManger, it's possible to change the token_key on parameters.

This library considered that the token has returned on the first level of the json response, but I need to treat a different response.
For example,the botpress response for connexion API :

{
    "status": "success",
    "message": "Login successful",
    "payload": {
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImJvdHByZXNzQGN5YmVybWFsdmVpbGxhbmNlLmdvdXYuZnIiLCJzdHJhdGVneSI6ImRlZmF1bHQiLCJpc1N1cGVyQWRtaW4iOnRydWUsImlhdCI6MTU2MzI3NzEzMSwiZXhwIjoxNTYzMjk4NzMxLCJhdWQiOiJjb2xsYWJvcmF0b3JzIn0.mYwBOjgLxlVduNuxzDUnIzMCE4oAQ5TMLLhK56XDdO4"
    }
}

How I can configure JWTManager to treat this connexion response? I think we need to do a pull request to change l.118 & l.129

Renewal of tokens didn't work.

The isValid function was never called, cause the token was fetched outside the closure for the middleware:

this fixed it for me:
`

public function __invoke(callable $handler)

{
    $manager = $this->jwtManager;
    // echo $token; 
    // exit; 
    return function (
        RequestInterface $request,
        array $options
    ) use (
        $handler,
        $manager
    ) {
        $token = $manager->getJwtToken()->getToken();
        return $handler($request->withHeader(
            'Authorization',
            sprintf(self::AUTH_BEARER, $token)
        ), $options);
    };
}

`

SimpleCacheTokenPersistence use a PSR-6 method instead of the PSR-16 delete

The delete method is incorrect and will throw with a strict PSR-6 implementation:
https://github.com/eljam/guzzle-jwt-middleware/blob/master/src/Persistence/SimpleCacheTokenPersistence.php#L64

This isn't catched by the unit tests because symfony is aliasing some PSR-16 methods for compatibility
https://github.com/symfony/cache/blob/4.4/Simple/AbstractCache.php#L35

Also symfony/cache has dropped PSR-16 support in their master branch and will only focus on PSR-6 interface.

What needs to be done:

  • fix the adapter
  • fix the unit test to remove symfony/cache and use a strict PSR-16 mocked cache instead

Nice to have:

  • also provide a PSR-6 adapter

I'll try to do a PR in a few days

Doesn't support simple-cache 2.0 and 3.0

Support is locked to 1.0 so use of this package blocks support for 2.0 and 3.0. This doesn't currently seem to be a problem but as other packages move forward with 8.x type support it could begin to be a problem.

The only blocker for supporting either seems to be:
kodus/mock-cache#2

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.