Skip to content

Instantly share code, notes, and snippets.

@johnpancoast
Last active August 20, 2019 08:22
Show Gist options
  • Save johnpancoast/359bad0255cb50ccd6ab13e4ac18e4e8 to your computer and use it in GitHub Desktop.
Save johnpancoast/359bad0255cb50ccd6ab13e4ac18e4e8 to your computer and use it in GitHub Desktop.
Example of how to use FOS OAuth Server Bundle without a client secret
<?php
/**
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
namespace Your\Namespace\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use OAuth2\OAuth2;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use FOS\OAuthServerBundle\Controller\TokenController as FOSTokenController;
use Symfony\Component\Routing\RouterInterface;
/**
* Token controller to override FOS OAuth's controller
*
* This token controller mimics FOS OAuth Server Bundles except that it allows
* the client (app) to pass a null client secret.
*
* This is just one example of way to accomplish this while still using FOS
* OAuth Server Bundle. I'm sure there's another, possibly better, way of
* doing this but it works.
*
* Background:
* OAuth requires a client id and secret along with username and password for the
* "password" (resource owner credentials) grant type. However, mobile apps have a
* problem where a client secret cannot be securely shipped with them without the
* secret being available to everyone, thus eliminating the security that the
* client authentication provides. The oauth spec brings up these points but it does
* not make any recommendations aside from saying that you must authenticate clients.
* At the moment, the FOS OAuth Server Bundle has only one means of registering
* a client. You manually create it on the server. This is common for a lot of
* oauth libs, however, this means that there's currently no way to securely
* authenticate mobile clients because the client registration happens on the
* server without client interaction and the secret cannot be shipped securely
* with the client (app).
*
* In addition to this, the FOS OAuth Server Bundle requires the client secret to
* be passed (null secret not allowed) and judging by the OAuth spec, this
* seems like a good idea. However, without some other way to securely register and
* validate clients, this means that people creating oauth servers for mobile apps
* are left wondering what to do. Some people recommend just not passing the secret
* at all but the oauth spec says otherwise, for good reason.
*
* That said, if you've read the oauth spec and you're aware of the security
* implications of having no client authentication and you still want to use
* the fos oauth server bundle, you can do something like the example below.
*
* read the spec first:
* https://tools.ietf.org/html/rfc6749
*
* Usage:
*
* 1.) Install/setup the fos oauth server bundle like normal.
* 2.) Add this controller to your codebase.
* 3.) Define this controller as a service.
*
* app/config/services.yml
*
* your_service_name:
* class: Your\Namespace\TokenController
* arguments:
* - "@fos_oauth_server.server"
* - "@router"
* - "@=container.hasParameter('oauth_clients') ? parameter('oauth_clients') : null"
*
* 4.) Define the fos oauth token route and point to your controller.
*
* app/config/routing.yml
*
* fos_oauth_server_token:
* path: "/oauth/v2/token"
* defaults: { _controller: "your_service_name:tokenAction" }
* methods: [GET|POST]
*
* 5.) Create your oauth client and note the id and secret.
* 6.) Add the client id and secret to the `oauth_clients` values in your
* parameters as an array (where client id is key and secret is value).
*
* app/config/parameters.yml
*
* parameters:
* ...
* oauth_clients:
* clientid: clientsecret
*/
class TokenController extends FOSTokenController
{
/**
* @var array
*/
private $oauthClients = [];
/**
* @var RouterInterface
*/
private $router;
/**
* Constructor
*
* Overrides and calls parent::__construct($server) but adds $oauthClients
* that are available.
*
* @param OAuth2 $server
* @param RouterInterface $router
* @param array $oauthClients
*/
public function __construct(
OAuth2 $server,
RouterInterface $router,
array $oauthClients = []
)
{
$this->router = $router;
$this->oauthClients = $oauthClients;
parent::__construct($server);
}
/**
* Overrides parent::tokenAction() but allows no client secret.
*
* {@inheritdoc}
*/
public function tokenAction(Request $request)
{
$clientId = $request->get('client_id');
$clientSecret = $request->get('client_secret')
? $request->get('client_secret')
: isset($this->oauthClients[$clientId]) ? $this->oauthClients[$clientId] : null
;
return parent::tokenAction(
Request::create(
$this->router->generate('fos_oauth_server_token'),
'POST',
[
'client_id' => $clientId,
'client_secret' => $clientSecret,
'username' => $request->get('username'),
'password' => $request->get('password'),
'grant_type' => $request->get('grant_type'),
]
)
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment