SPQRInc
SPQRInc

Reputation: 198

Log in on 3rd party website using Laravel Passport

I need to solve the following problem: I created two applications using Laravel.

Now I would like to provide a login method for users of Application B. All users that are using Application B are existing in Application A. So a user logging in on Application B is using the credentials that are saved in Application A. All changes that are made to the user in Application A should be visible on Application B.

Now I wonder how I could do that. Any idea how to solve this? I can imagine using API-calls with

        $client = $this->client;
        try {
            return $client->request('POST',
                'https://'.config('server').'/oauth/token', [
                    'headers'     => [
                        'cache-control' => 'no-cache',
                        'Content-Type'  => 'application/x-www-form-urlencoded',
                    ],
                    'form_params' => [
                        'client_id'     => ...,
                        'client_secret' => ...,
                        'grant_type'    => 'password',
                        'username'      => $users-username,
                        'password'      => $users-password,
                    ],
                ]);
        } catch (BadResponseException $e) {
            return $e->getResponse();
        }
    }

but I don't get, whether I need to create a client_id and client_secret for each user on Application A. If this is the case, I'd need to store these information somewhere so I'd end up creating a User model on Application B itself.

My questions are:

Upvotes: 4

Views: 3309

Answers (3)

sebdesign
sebdesign

Reputation: 838

The most elegant way would be to use Laravel Socialite in your Application B to act as an OAuth client for Application A which acts as an OAuth server (using Laravel Passport).

  1. On Application A, you need to create a client on Passport for your Application B:
php artisan passport:client --password

First, you will be asked Which user ID should the client be assigned to?. Press enter as you don't want to restrict the client to a specific user.

Then, you will be asked to provide a name for your client, your should enter something like Application B.

At last, you will be asked for the redirect URL of you client. You should enter https://test.org/oauth/callback, which is the URL that Socialite will use to redirect the users back after they are authenticated.

If you need to test this on your machine, enter your local domain, e.g. https://application-b.test/oauth/callback.

Once the client is created, keep the Client ID and Client secret as you will need them later.

You will also need an API endpoint to provide the details for the authenticated user, .e.g. https://test.example.org/user.

  1. On Application B, after installing Laravel Socialite, you need to set the credentials for the Application A in your services.php configuration file. For example:
'passport' => [
    'client_id' => env('PASSPORT_CLIENT_ID'),
    'client_secret' => env('PASSPORT_CLIENT_SECRET'),
    'redirect' => 'oauth/callback',
],

In your .env file, set the client ID and secret for the Passport client you created in Application A:

PASSPORT_CLIENT_ID=...
PASSPORT_CLIENT_SECRET=...
  1. Now you need to extend Socialite with a custom provider for Application A.

Create a PassportProvider class in the app/Socialite directory (or anywhere inside app if you prefer).

<?php

namespace App\Socialite;

use Laravel\Socialite\Two\AbstractProvider;
use Laravel\Socialite\Two\ProviderInterface;
use Laravel\Socialite\Two\User;
use Illuminate\Support\Arr;

class PassportProvider extends AbstractProvider implements ProviderInterface
{
    /**
     * {@inheritdoc}
     */
    protected function getAuthUrl($state)
    {
        return $this->buildAuthUrlFromBase('https://test.example.org/oauth/authorize', $state);
    }

    /**
     * {@inheritdoc}
     */
    protected function getTokenUrl()
    {
        return 'https://test.example.org/oauth/token';
    }

    /**
     * {@inheritdoc}
     */
    protected function getUserByToken($token)
    {
        $response = $this->getHttpClient()->get(
            'https://test.example.org/user',
            $this->getRequestOptions($token)
        );

        return json_decode($response->getBody(), true);
    }

    /**
     * {@inheritdoc}
     */
    protected function mapUserToObject(array $user)
    {
        return (new User)->setRaw($user)->map([
            'id' => $user['id'],
            'name' => Arr::get($user, 'name'),
            'email' => Arr::get($user, 'email'),
        ]);
    }

    /**
     * Get the default options for an HTTP request.
     *
     * @param string $token
     * @return array
     */
    protected function getRequestOptions($token)
    {
        return [
            'headers' => [
                'Accept' => 'application/json',
                'Authorization' => 'token '.$token,
            ],
        ];
    }
}

Next, in your ApplicationServiceProvider (or in a separate service provider), add the following:

use App\Socialite\PassportProvider;
use Illuminate\Support\Facades\URL;
use Laravel\Socialite\Facades\Socialite;

public function boot()
{
    Socialite::extend('passport', function (function ($app) {
        $config = $app['config']['services.passport'];

        return new PassportProvider(
            $app['request'],
            $config['client_id'],
            $config['client_secret'],
            URL::to($config['redirect'])
        );
    });
}

Now you are ready to use your custom Socialite provider in your login controller on your client application:

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Laravel\Socialite\Facades\Socialite;

class LoginController extends Controller
{
    /**
     * Redirect the user to the GitHub authentication page.
     *
     * @return \Illuminate\Http\RedirectResponse
     */
    public function redirectToProvider()
    {
        return Socialite::driver('passport')->redirect();
    }

    /**
     * Obtain the user information from GitHub.
     *
     * @return \Illuminate\Http\Response
     */
    public function handleProviderCallback()
    {
        $user = Socialite::driver('passport')->user();
    }
}

Don't forget to register the login routes in your routes/web.php file:

Route::get('oauth/redirect', 'Auth\LoginController@redirectToProvider');
Route::get('oauth/callback', 'Auth\LoginController@handleProviderCallback');

Upvotes: 9

parth patel
parth patel

Reputation: 141

there is two solutions

1. If you just need Application A database details than create multiple database connection in your Application B

https://fideloper.com/laravel-multiple-database-connections

2. If you want to access Application A endpoint than keep same files storage/oauth-public.key, storage/oauth-private.key for your both Application so Application A can easily Authenticate Application B authorisation token.

Upvotes: 0

Anas Bakro
Anas Bakro

Reputation: 1409

A. You would only create one client_id and client_secret for Application B
B. Use Passport as you wrote above
C. You can, but you need to set it up manually which I'm not sure is a good idea, a better one would be to redirect it to Application A UI (In case of web) or directly contact Application A (in case of mobile) and let it handle it by itself.

Upvotes: 0

Related Questions