ridibooks / oauth2
Ridibooks OAuth2
Installs: 26 035
Dependents: 0
Suggesters: 0
Security: 0
Stars: 3
Watchers: 7
Forks: 1
Open Issues: 2
Requires
Requires (Dev)
- mockery/mockery: ^1.3
- phpunit/phpunit: ^7
Suggests
- guzzlehttp/guzzle: Required when using `Ridibooks\OAuth2\Silex\Provider\DefaultUserProvider`or `Ridibooks\OAuth2\Symfony\Provider\DefaultUserProvider`
- silex/silex: Required when using `Ridibooks\OAuth2\Silex\Provider\OAuth2ServiceProvider`(compatible with 1.3.x versions)
- symfony/symfony: Required when using `Ridibooks\OAuth2\Symfony\OAuth2ServiceProviderBundle`(compatible with 4.x.x versions)
README
소개
- OAuth2 클라이언트와 리소스 서버를 구축하기 위한 PHP 라이브러리입니다.
- Ridi 스타일 가이드(내부 서비스간의 SSO)에 따라 작성 되었습니다.
- JWK Caching 를 선택적으로 지원합니다. psr-6의 구현체를 JwtTokenValidator에 주입하면 캐싱 기능을 사용할 수 있습니다.
Requirements
PHP 7.2
or higherphp7.2-gmp
web-token decryption 모듈을 위해서는 php7.2-gmp 를 os 내에 설치해줘야 합니다. 따라서 이 라이브러리 클라이언트들의 OS 혹은 도커 이미지 내에 꼭 설치해주시길 바랍니다. 참고 PRsilex/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
)해 사용한다.
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
- example:
- Application Controller의
@OAuth2
Annotation에서exception_handler
속성을 지정합니다.- example:
@OAuth2(exception_handler="Ridibooks\Test\OAuth2\Symfony\TestExceptionHandler")
- example:
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분 입니다.