Skip to content

Instantly share code, notes, and snippets.

@kseta
Last active December 13, 2016 07:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kseta/e1a30deba12abf25bcde5ec123d67f89 to your computer and use it in GitHub Desktop.
Save kseta/e1a30deba12abf25bcde5ec123d67f89 to your computer and use it in GitHub Desktop.
Symfony で FOSOAuthServerBundle のトークン認証が必要な WEB API のデモを作ってみた ref: http://qiita.com/kseta/items/fc00fc1676985455356a
<?php
namespace AppBundle\Entity;
use FOS\OAuthServerBundle\Entity\AccessToken as BaseAccessToken;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class AccessToken extends BaseAccessToken
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="Client")
* @ORM\JoinColumn(nullable=false)
*/
protected $client;
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
*/
protected $user;
}
...
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new AppBundle\AppBundle(),
// 追加
new FOS\OAuthServerBundle\FOSOAuthServerBundle(),
new FOS\RestBundle\FOSRestBundle(),
new FOS\UserBundle\FOSUserBundle(),
new JMS\SerializerBundle\JMSSerializerBundle(),
);
...
}
<?php
namespace AppBundle\Entity;
use FOS\OAuthServerBundle\Entity\AuthCode as BaseAuthCode;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class AuthCode extends BaseAuthCode
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="Client")
* @ORM\JoinColumn(nullable=false)
*/
protected $client;
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
*/
protected $user;
}
<?php
namespace AppBundle\Entity;
use FOS\OAuthServerBundle\Entity\Client as BaseClient;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Client extends BaseClient
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
fos_oauth_server:
db_driver: orm # Drivers available: orm, mongodb, or propel
client_class: AppBundle\Entity\Client
access_token_class: AppBundle\Entity\AccessToken
refresh_token_class: AppBundle\Entity\RefreshToken
auth_code_class: AppBundle\Entity\AuthCode
service:
user_provider: fos_user.user_provider.username
<?php
namespace AppBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CreateOAuthClientCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('app:oauth-client:create')
->setDescription('Create OAuth Client.')
->addArgument('redirectUri', InputArgument::REQUIRED, 'The redirect uri')
->addArgument('grantType', InputArgument::REQUIRED, 'The grant type')
->setHelp(<<<EOT
The <info>app:oauth:create</info> command creates a OAuth Client:
<info>php app/console app:oauth:create</info>
This interactive shell will ask you for an client name, a redirect uri and then a grant type.
EOT
);
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$redirectUri = $input->getArgument('redirectUri');
$grantType = $input->getArgument('grantType');
$clientManager = $this->getContainer()->get('fos_oauth_server.client_manager.default');
$client = $clientManager->createClient();
$client->setRedirectUris([$redirectUri]);
$client->setAllowedGrantTypes([$grantType]);
$clientManager->updateClient($client);
$output->writeln(sprintf('Created OAuth Client'));
return;
}
protected function interact(InputInterface $input, OutputInterface $output)
{
if (!$input->getArgument('redirectUri')) {
$redirectUri = $this->getHelper('dialog')->askAndValidate(
$output,
'Please choose an Redirect Uri:',
function($redirectUri) {
if (empty($redirectUri)) {
throw new \Exception('Redirect Uri can not be empty');
}
return $redirectUri;
}
);
$input->setArgument('redirectUri', $redirectUri);
}
if (!$input->getArgument('grantType')) {
$grantType = $this->getHelper('dialog')->askAndValidate(
$output,
'Please choose a Grant Type:',
function($grantType) {
if (empty($grantType)) {
throw new \Exception('Grant Type can not be empty');
}
return $grantType;
}
);
$input->setArgument('grantType', $grantType);
}
}
}
$ symfony new symfony-oauth-sever 2.8
$ cd symfony-oauth-server
$ composer require friendsofsymfony/rest-bundle 1.3.*
$ composer require friendsofsymfony/user-bundle
$ composer require jms/serializer-bundle
$ php app/console server:start
$ http GET http://localhost:8000/app_dev.php/api/welcome
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
[
"welcome!"
]
$ composer require friendsofsymfony/oauth-server-bundle
$ php app/console doctrine:schema:update --force
+--------------------------------+
| Tables_in_symfony-oauth-server |
+--------------------------------+
| access_token |
| auth_code |
| client |
| fos_user |
| refresh_token |
+--------------------------------+
5 rows in set (0.00 sec)
$ http GET http://localhost:8000/app_dev.php/api/welcome
HTTP/1.1 401 Unauthorized
Cache-Control: no-store, private
Content-Type: application/json
Host: localhost:8000
Pragma: no-cache
WWW-Authenticate: Bearer realm="Service", error="access_denied", error_description="OAuth2 authentication required"
{
"error": "access_denied",
"error_description": "OAuth2 authentication required"
}
$ php app/console app:oauth-client:create
Please choose an Redirect Uri:http://example.com
Please choose a Grant Types (separate multiple grant type with a space):password refresh_token
Created OAuth Client
mysql> select * from client;
+----+----------------------------------------------------+--------------------------------------+----------------------------------------------------+---------------------------+
| id | random_id | redirect_uris | secret | allowed_grant_types |
+----+----------------------------------------------------+--------------------------------------+----------------------------------------------------+---------------------------+
| 1 | 1avth1a6t18kc4coc4o48wkogsww0c0o4oko0oc4wsgc4848c4 | a:1:{i:0;s:18:"http://example.com";} | 18u7clil6ww008480ko4occkkw80s4k4w0o0w8wgko44wwgkg0 | a:2:{i:0;s:8:"password";i:1;s:13:"token_refresh";} |
+----+----------------------------------------------------+--------------------------------------+----------------------------------------------------+---------------------------+
1 row in set (0.00 sec)
$ php app/console router:debug |grep token
fos_oauth_server_token GET|POST ANY ANY /oauth/v2/token
$ http POST http://localhost:8000/app_dev.php/oauth/v2/token \
grant_type=password \
username=admin \
password=adminpass \
client_id=1_1avth1a6t18kc4coc4o48wkogsww0c0o4oko0oc4wsgc4848c4 \
client_secret=18u7clil6ww008480ko4occkkw80s4k4w0o0w8wgko44wwgkg0
HTTP/1.1 200 OK
Cache-Control: no-store, private
Content-Type: application/json
Pragma: no-cache
{
"access_token": "OWRiNWU5OTJlMGIzOGE1OTRiNjQ2MGZkM2EwYTM1YWFjNjdjYzc3NWEwYjMxN2VlOWNjY2E4ZDE0ZDRlOTdjZg",
"expires_in": 3600,
"refresh_token": "MmMwODYwYjg1Y2VmZGQ4NTExYmQwOTE1NTg4YzhiOTY4N2EzZTcyZmI2MGI1MTVjMjA1NTMxMTE0Zjc0Zjg4MQ",
"scope": null,
"token_type": "bearer"
}
mysql> select * from access_token;
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
| id | client_id | user_id | token | expires_at | scope |
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
| 1 | 1 | 1 | OWRiNWU5OTJlMGIzOGE1OTRiNjQ2MGZkM2EwYTM1YWFjNjdjYzc3NWEwYjMxN2VlOWNjY2E4ZDE0ZDRlOTdjZg | 1481609172 | NULL |
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
1 row in set (0.00 sec)
mysql> select * from refresh_token;
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
| id | client_id | user_id | token | expires_at | scope |
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
| 1 | 1 | 1 | MmMwODYwYjg1Y2VmZGQ4NTExYmQwOTE1NTg4YzhiOTY4N2EzZTcyZmI2MGI1MTVjMjA1NTMxMTE0Zjc0Zjg4MQ | 1482815172 | NULL |
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
1 row in set (0.00 sec)
$ http GET http://localhost:8000/app_dev.php/api/welcome \
"Authorization:Bearer OWRiNWU5OTJlMGIzOGE1OTRiNjQ2MGZkM2EwYTM1YWFjNjdjYzc3NWEwYjMxN2VlOWNjY2E4ZDE0ZDRlOTdjZg"
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
[
"welcome!"
]
$ http GET http://localhost:8000/app_dev.php/api/welcome \
"Authorization:Bearer OWRiNWU5OTJlMGIzOGE1OTRiNjQ2MGZkM2EwYTM1YWFjNjdjYzc3NWEwYjMxN2VlOWNjY2E4ZDE0ZDRlOTdjZg"
HTTP/1.1 401 Unauthorized
Cache-Control: no-store, private
Content-Type: application/json
Pragma: no-cache
WWW-Authenticate: Bearer realm="Service", error="invalid_grant", error_description="The access token provided has expired."
{
"error": "invalid_grant",
"error_description": "The access token provided has expired."
}
$ http POST http://localhost:8000/app_dev.php/oauth/v2/token \
grant_type=refresh_token \
refresh_token=MmMwODYwYjg1Y2VmZGQ4NTExYmQwOTE1NTg4YzhiOTY4N2EzZTcyZmI2MGI1MTVjMjA1NTMxMTE0Zjc0Zjg4MQ \ client_id=1_1avth1a6t18kc4coc4o48wkogsww0c0o4oko0oc4wsgc4848c4 \
client_secret=18u7clil6ww008480ko4occkkw80s4k4w0o0w8wgko44wwgkg0
HTTP/1.1 200 OK
Cache-Control: no-store, private
Content-Type: application/json
Pragma: no-cache
{
"access_token": "MjhlZWNlMTFmNTA1ZmIwMWJkMzc0NDMyYmZmZWNkZDE1ZWZiNzYwM2I5MWU2MmNlMmY4YzQ1ZWJmZjYzMzQxMw",
"expires_in": 3600,
"refresh_token": "ZGU0YmQzYzA4MDY2MWI5NTU4YTE3NjU0NmUzN2I3MmM0MzEyZjMyNmMxZjhjOTkwZjY3YjRkNWM2YjE5NTU5Yg",
"scope": null,
"token_type": "bearer"
}
$ http GET http://localhost:8000/app_dev.php/api/welcome \
"Authorization:Bearer MjhlZWNlMTFmNTA1ZmIwMWJkMzc0NDMyYmZmZWNkZDE1ZWZiNzYwM2I5MWU2MmNlMmY4YzQ1ZWJmZjYzMzQxMw"
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
[
"welcome!"
]
mysql> select * from access_token;
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
| id | client_id | user_id | token | expires_at | scope |
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
| 1 | 1 | 1 | OWRiNWU5OTJlMGIzOGE1OTRiNjQ2MGZkM2EwYTM1YWFjNjdjYzc3NWEwYjMxN2VlOWNjY2E4ZDE0ZDRlOTdjZg | 1481609172 | NULL |
| 2 | 1 | 1 | MjhlZWNlMTFmNTA1ZmIwMWJkMzc0NDMyYmZmZWNkZDE1ZWZiNzYwM2I5MWU2MmNlMmY4YzQ1ZWJmZjYzMzQxMw | 1481613560 | NULL |
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
2 rows in set (0.00 sec)
$ php app/console fos:oauth-server:clean
Removed 1 items from Access token storage.
Removed 0 items from Refresh token storage.
Removed 0 items from Auth code storage.
mysql> select * from access_token;
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
| id | client_id | user_id | token | expires_at | scope |
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
| 2 | 1 | 1 | MjhlZWNlMTFmNTA1ZmIwMWJkMzc0NDMyYmZmZWNkZDE1ZWZiNzYwM2I5MWU2MmNlMmY4YzQ1ZWJmZjYzMzQxMw | 1481613560 | NULL |
+----+-----------+---------+----------------------------------------------------------------------------------------+------------+-------+
1 row in set (0.00 sec)
$ php app/console doctrine:database:create
$ php app/console doctrine:schema:update --force
mysql> use symfony-oauth-server;
mysql> show tables;
+--------------------------------+
| Tables_in_symfony-oauth-server |
+--------------------------------+
| fos_user |
+--------------------------------+
1 rows in set (0.00 sec)
$ php app/console fos:user:create
Please choose a username:admin
Please choose an email:admin@example.com
Please choose a password:adminpass
Created user admin
mysql> select * from fos_user;
+----+----------+--------------------+-------------------+-------------------+---------+---------------------------------+--------------------------------------------------------------+------------+--------+---------+------------+--------------------+-----------------------+--------+---------------------+-----------------------+
| id | username | username_canonical | email | email_canonical | enabled | salt | password | last_login | locked | expired | expires_at | confirmation_token | password_requested_at | roles | credentials_expired | credentials_expire_at |
+----+----------+--------------------+-------------------+-------------------+---------+---------------------------------+--------------------------------------------------------------+------------+--------+---------+------------+--------------------+-----------------------+--------+---------------------+-----------------------+
| 1 | admin | admin | admin@example.com | admin@example.com | 1 | kjflxygblxcwccgkg4o4ooggkckgggw | $2y$13$kjflxygblxcwccgkg4o4oe34L7Y6Iorvaz4V5Sr9lg3nbSnHn.b.i | NULL | 0 | 0 | NULL | NULL | NULL | a:0:{} | 0 | NULL |
+----+----------+--------------------+-------------------+-------------------+---------+---------------------------------+--------------------------------------------------------------+------------+--------+---------+------------+--------------------+-----------------------+--------+---------------------+-----------------------+
1 row in set (0.01 sec)
<?php
namespace AppBundle\Entity;
use FOS\OAuthServerBundle\Entity\RefreshToken as BaseRefreshToken;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class RefreshToken extends BaseRefreshToken
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="Client")
* @ORM\JoinColumn(nullable=false)
*/
protected $client;
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
*/
protected $user;
}
fos_oauth_server_token:
resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"
fos_oauth_server_authorize:
resource: "@FOSOAuthServerBundle/Resources/config/routing/authorize.xml"
# To get started with security, check out the documentation:
# http://symfony.com/doc/current/security.html
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
# http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
in_memory:
memory: ~
firewalls:
oauth_token:
pattern: ^/oauth/v2/token
security: false
api:
pattern: ^/api
fos_oauth: true
stateless: true
anonymous: false # can be omitted as its default value
access_control:
- { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }
<?php
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
// your own logic
}
}
<?php
namespace AppBundle\Controller;
use FOS\RestBundle\Controller\Annotations\RouteResource;
use FOS\RestBundle\Controller\FOSRestController;
/**
* @RouteResource("welcome")
*/
class WelcomeController extends FOSRestController
{
public function getAction()
{
return ["welcome!"];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment