2017 © Pedro Pelรกez
 

library oauth2-resource

Ridibooks OAuth2

image

ridibooks/oauth2-resource

Ridibooks OAuth2

  • Tuesday, April 3, 2018
  • by ridibooks
  • Repository
  • 7 Watchers
  • 0 Stars
  • 1 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 1 Forks
  • 2 Open issues
  • 4 Versions
  • 0 % Grown

The README.md

ridibooks/oauth2

Build Status, (*1)

์†Œ๊ฐœ

  • OAuth2 ํด๋ผ์ด์–ธํŠธ์™€ ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•œ PHP ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
  • Ridi ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ(๋‚ด๋ถ€ ์„œ๋น„์Šค๊ฐ„์˜ SSO)์— ๋”ฐ๋ผ ์ž‘์„ฑ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • JWK Caching ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. psr-6์˜ ๊ตฌํ˜„์ฒด๋ฅผ JwtTokenValidator์— ์ฃผ์ž…ํ•˜๋ฉด ์บ์‹ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Requirements

  • PHP 7.2 or higher
  • php7.2-gmp web-token decryption ๋ชจ๋“ˆ์„ ์œ„ํ•ด์„œ๋Š” php7.2-gmp ๋ฅผ os ๋‚ด์— ์„ค์น˜ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํด๋ผ์ด์–ธํŠธ๋“ค์˜ OS ํ˜น์€ ๋„์ปค ์ด๋ฏธ์ง€ ๋‚ด์— ๊ผญ ์„ค์น˜ํ•ด์ฃผ์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ฐธ๊ณ  PR
  • silex/silex v1.3.x (optional)
  • symfony/symfony v4.x.x (optional)
  • guzzlehttp/guzzle (optional)

Installation

composer require ridibooks/oauth2

Usage

JwtTokenValidator Without Caching

use Ridibooks\OAuth2\Authorization\Validator\JwtTokenValidator;

$access_token = '...';

try {
    $jwk_url = $this->configs['jwk_url'];
    $validator = new JwtTokenValidator($jwk_url);
    $validator->validateToken($access_token);
} catch (AuthorizationException $e) {
    // handle exception
}

JwtTokenValidator With Caching

use Ridibooks\OAuth2\Authorization\Validator\JwtTokenValidator;

$access_token = '...';

try {
    $jwk_url = $this->configs['jwk_url'];
    $cache_item_pool = new FilesystemAdapter(); // [psr-6](https://www.php-fig.org/psr/psr-6/) Implementation Adaptor
    $validator = new JwtTokenValidator($jwk_url, $cache_item_pool);
    $validator->validateToken($access_token);
} catch (AuthorizationException $e) {
    // handle exception
}

ScopeChecker

$required = ['write', 'read'];
if (ScopeChecker::every($required, $granted)) {
    // pass
}

Granter

$client_info = new ClientInfo('client_id', 'client_secret', ['scope'], 'redirect_uri');
$auth_server_info = new AuthorizationServerInfo('authorization_url', 'token_url');

$granter = new Granter($client_info, $auth_server_info);
$authorization_url = $granter->authorize();
// Redirect to `$authorization_url`

Usage: with Silex Provider

OAuth2ServiceProvider๋ฅผ Silex ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋“ฑ๋ก(register)ํ•ด ์‚ฌ์šฉํ•œ๋‹ค., (*2)

Services

  • OAuth2ProviderKeyConstant::GRANTER
    • authorize(string $state, string $redirect_uri = null, array $scope = null): string: /authorize๋ฅผ ์œ„ํ•œ URL์„ ๋ฐ˜ํ™˜
  • OAuth2ProviderKeyConstant::AUTHORIZER
    • autorize(Request $request): JwtToken: access_token ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ›„ JwtToken ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
  • OAuth2ProviderKeyConstant::MIDDLEWARE
    • authorize(OAuth2ExceptionHandlerInterface $exception_handler = null, UserProviderInterface $user_provider = null, array $required_scopes = []): ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋ฐ˜ํ™˜

Example: OAuth2ProviderKeyConstant::MIDDLEWARE Service

use Ridibooks\OAuth2\Silex\Constant\OAuth2ProviderKeyConstant as KeyConstant;
use Ridibooks\OAuth2\Silex\Handler\LoginRequiredExceptionHandler;
use Ridibooks\OAuth2\Silex\Provider\OAuth2ServiceProvider;
use Ridibooks\OAuth2\Authorization\Validator\JwtTokenValidator;
use Example\UserProvder;

// `OAuth2ServiceProvider` ๋“ฑ๋ก
$app->register(new OAuth2ServiceProvider(), [
    KeyConstant::CLIENT_ID => 'example-client-id',
    KeyConstant::CLIENT_SECRET => 'example-client-secret',
    KeyConstant::JWT_VALIDATOR => new JwtTokenValidator($jwk_url)
]);

// ๋ฏธ๋“ค์›จ์–ด ๋“ฑ๋ก
$app->get('/auth-required', [$this, 'authRequiredApi'])
    ->before($app[KeyConstant::MIDDLEWARE]->authorize(new LoginRequiredExceptionHandler(), new UserProvider());

public function authRequiredApi(Application $app)
{
    // ์‚ฌ์šฉ์ž ์ถ”์ถœ
    $user = $app[KeyConstant::USER];
    ...
}

Example: OAuth2ProviderKeyConstant::AUTHORIZER Service

use Ridibooks\OAuth2\Authorization\Authorizer;
use Ridibooks\OAuth2\Authorization\Exception\AuthorizationException;
use Ridibooks\OAuth2\Silex\Constant\OAuth2ProviderKeyConstant;
use Ridibooks\OAuth2\Silex\Provider\OAuth2ServiceProvider;
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Ridibooks\OAuth2\Authorization\Validator\JwtTokenValidator;

...

// `OAuth2ServiceProvider` ๋“ฑ๋ก
$app->register(new OAuth2ServiceProvider(), [
    KeyConstant::CLIENT_ID => 'example-client-id',
    KeyConstant::CLIENT_SECRET => 'example-client-secret',
    KeyConstant::JWT_VALIDATOR => new JwtTokenValidator($jwk_url)
]);

...

$app->get('/', function (Application $app, Request $request) {
    /** @var Authorizer $authorizer */
    $authorizer = $app[OAuth2ProviderKeyConstant::AUTHORIZER];
    try {
        $token = $authorizer->authorize($request);
        return $token->getSubject();
    } catch (AuthorizationException $e) {
        // handle authorization error ...
    }
});

Usage: with Symfony Bundle

Services

  • Granter()
    • OAuth2ServiceProvider::getGranter()
    • Granter::authorize(string $state, string $redirect_uri = null, array $scope = null): string: /authorize๋ฅผ ์œ„ํ•œ URL์„ ๋ฐ˜ํ™˜
  • Authorizer()
    • OAuth2ServiceProvider::getAuthorizer()
    • Authorizer::autorize(Request $request): JwtToken: access_token ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ›„ JwtToken ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
  • OAuth2Middleware
    • OAuth2ServiceProvider::getMiddleware()
    • OAuth2ServiceProvider ์ƒ์„ฑ ์‹œ, Symfony Event Subscriber๋กœ ๋“ฑ๋ก

Example: OAuth2Middleware Service

Configuration

  • ์„ค์ • ์˜ˆ์‹œ๋Š” tests/Symfony์—์„œ ์‚ดํŽด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
1. OAuth2ServiceProviderBundle ๋“ฑ๋ก
# example: <project_root>/config/bundles.php

return [
    ...,
    Ridibooks\OAuth2\Symfony\OAuth2ServiceProviderBundle::class => ['all' => true]
];
2. Parameter ๋ฐ Service ์„ค์ •
  • '%env(VARIABLE)%'์„ ์ด์šฉํ•ด environment variable์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Required
    • client_id
    • client_secret
    • authorize_url
    • token_url
    • jwk_url
    • user_info_url
    • token_cookie_domain
    • default_exception_handler
  • optional
    • client_default_scope
    • client_default_redirect_uri
    • default_user_provider
    • cache_item_pool # psr-6์˜ ๊ตฌํ˜„์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๋ฉด Jwk ์š”์ฒญ ์‹œ ์บ์‹ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
# example: <project_root>/config/packages/o_auth2_service_provider.yml

o_auth2_service_provider:
  client_id: '%env(CLIENT_ID)%'
  client_secret: '%env(CLIENT_SECRET)%'
  authorize_url: https://account.dev.ridi.io/ridi/authorize/
  token_url: https://account.dev.ridi.io/oauth2/token/
  jwk_url: https://account.dev.ridi.io/oauth2/keys/public
  user_info_url: https://account.dev.ridi.io/accounts/me/
  token_cookie_domain: .ridi.io

  default_exception_handler: Ridibooks\OAuth2\Example\DefaultExceptionHandler
  cache_item_pool: Symfony\Component\Cache\Adapter\FilesystemAdapter
# example: <project_root>/config/services.yml

services:
  Ridibooks\OAuth2\Example\ExampleController:
    class: Ridibooks\OAuth2\Example\ExampleController
    autowire: true
    autoconfigure: true
    public: false
    arguments:
      - '@oauth2_service_provider'
3. Controller ์„ค์ •
namespace Ridibooks\OAuth2\Example;

use Ridibooks\OAuth2\Symfony\Annotation\OAuth2;
use Ridibooks\OAuth2\Symfony\Provider\OAuth2ServiceProvider;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class ExampleController extends Controller
{
    /** @var OAuth2ServiceProvider */
    private $oauth2_service_provider;

    /**
     * @param OAuth2ServiceProvider $oauth2_service_provider
     */
    public function __construct(OAuth2ServiceProvider $oauth2_service_provider)
    {
        $this->oauth2_service_provider = $oauth2_service_provider;
    }

    /**
     * @Route("/oauth2", methods={"GET"})
     * @OAuth2()
     *
     * @param Request $request
     * @return Response
     */
    public function normal(Request $request): Response
    {
        $user = $this->oauth2_service_provider->getMiddleware()->getUser();

        return new JsonResponse([
            'u_idx' => $user->getUidx(),
            'u_id' => $user->getUid()
        ]);
    }
}

OAuth2 Exception Handler ์„ค์ •

  • Exception Handler๋Š” OAuth2 ๊ณผ์ • ์ค‘, ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ Exception ์ƒํ™ฉ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • Application Controller์—์„œ default_exception_handler ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ง€์ •ํ•œ Exception Handler๊ฐ€ ์•„๋‹Œ ๋ณ„๋„์˜ Exception Handler๋ฅผ ์ด์šฉํ•˜๋ ค๋Š” ๊ฒฝ์šฐ, ์•„๋ž˜ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.
    • Ridibooks\OAuth2\Symfony\Handler\OAuth2ExceptionHandlerInterface๋ฅผ implementํ•œ Exception Handler๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
      • example: Ridibooks\Test\OAuth2\Symfony\TestExceptionHandler
    • Application Controller์˜ @OAuth2 Annotation์—์„œ exception_handler ์†์„ฑ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
      • example: @OAuth2(exception_handler="Ridibooks\Test\OAuth2\Symfony\TestExceptionHandler")

Custom User Provider ์„ค์ •

  • User Provider๋Š” ์ธ์ฆ ์ดํ›„, User ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • default_user_provider ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ, ๊ธฐ๋ณธ์ ์œผ๋กœ Ridibooks\OAuth2\Symfony\Provider\DefaultUserProvider๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค.
  • Application Controller์—์„œ default_user_provider ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ง€์ •ํ•œ User Provider๊ฐ€ ์•„๋‹Œ ๋ณ„๋„์˜ User Provider๋ฅผ ์ด์šฉํ•˜๋ ค๋Š” ๊ฒฝ์šฐ, ์•„๋ž˜ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.
    • Ridibooks\OAuth2\Symfony\Provider\UserProviderInterface๋ฅผ implementํ•œ User Provider๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    • Application Controller์˜ @OAuth2 Annotation์—์„œ user_provider ์†์„ฑ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
namespace Ridibooks\OAuth2\Example;

use Ridibooks\OAuth2\Authorization\Token\JwtToken;
use Ridibooks\OAuth2\Symfony\Provider\OAuth2ServiceProvider;
use Ridibooks\OAuth2\Symfony\Provider\UserProviderInterface;
use Symfony\Component\HttpFoundation\Request;

class CustomUserProvider implement UserProviderInterface
{
    /**
     * @param JwtToken $token
     * @param Request $request
     * @param OAuth2ServiceProvider $oauth2_service_provider
     * @return User
     */
    public function getUser(JwtToken $token, Request $request, OAuth2ServiceProvider $oauth2_service_provider): User
    {
        ...
    }
}
namespace Ridibooks\OAuth2\Example;

use Ridibooks\OAuth2\Symfony\Annotation\OAuth2;
use Ridibooks\OAuth2\Symfony\Provider\OAuth2ServiceProvider;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class ExampleController extends Controller
{
    /**
     * @Route("/oauth2", methods={"GET"})
     * @OAuth2(user_provider="Ridibooks\OAuth2\Example\CustomUserProvider")
     *
     * @param Request $request
     * @return Response
     */
    public function normal(Request $request): Response
    {
        ...
    }
}

Cache Item Pool ์„ค์ •

  • Cache Item Pool ์€ Jwk ๋ฅผ ์บ์‹ฑํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

์œ ์˜ํ•  ์ 

  • Jwk Multi signatures ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ค์ง ์ฒซ ๋ฒˆ์งธ ์ธ๋ฑ์Šค์˜ ์‹œ๊ทธ๋‹ˆ์ณ๋ฅผ ๊ฐ€์ ธ์™€์„œ decode ํ•ฉ๋‹ˆ๋‹ค.
  • Jwk Cache File ์˜ TTL(Time To Live)๋Š” 5๋ถ„ ์ž…๋‹ˆ๋‹ค.

The Versions

03/04 2018

dev-master

9999999-dev

Ridibooks OAuth2

  Sources   Download

MIT

The Requires

 

The Development Requires

02/04 2018

dev-kyungmi/feature/symfony

dev-kyungmi/feature/symfony

Ridibooks OAuth2 Resource

  Sources   Download

MIT

The Requires

 

The Development Requires

29/03 2018

dev-feature/symfony

dev-feature/symfony

Ridibooks OAuth2 Resource

  Sources   Download

MIT

The Requires

 

The Development Requires

20/03 2018

0.0.1

0.0.1.0

Ridibooks OAuth2 Resource

  Sources   Download

MIT

The Requires

 

The Development Requires