The Auth plugin is geared around OAuth 1 and OAuth 2 authentication workflows, so it's best to review how that works.
The Auth module takes care of steps 1, 2, 4, and 5. It will be up to your plugin to nominate a callback URL and trigger the Auth module to trigger the token request.
Let's run through an example from start to finish. We'll assume you're working on a site module, but this would also be applicable in a plugin.
Before we dive in, we'll need to create our own provider class for our plugin. This implements an Auth provider class. Let's use Facebook as an example.
Create a site-module/providers/Facebook.php
file with the following:
<?php
namespace modules\sitemodule\providers;
use verbb\auth\base\OAuthProvider;
use verbb\auth\providers\Facebook as FacebookProvider;
class Facebook extends OAuthProvider
{
// Public Methods
// =========================================================================
public function getOAuthProviderClass(): string
{
return FacebookProvider::class;
}
}
Here, we extend the OAuthProvider
class which abstracts some logic for dealing with OAuth providers. There's only one method we need to implement which is providing a Auth provider class to handle authentication.
The idea is that you'll add to this class with your own provider logic depending on your plugin.
In your plugin, you'll need to create two controller endpoints - one for initialising the request, the other to receive the request from the provider.
<?php
namespace modules\sitemodule\controllers;
use Craft;
use craft\web\Controller;
use yii\web\Response;
class AuthController extends Controller
{
// Properties
// =========================================================================
protected array|int|bool $allowAnonymous = ['login', 'callback'];
// Public Methods
// =========================================================================
public function beforeAction($action): bool
{
// Don't require CSRF validation for callback requests
if ($action->id === 'callback') {
$this->enableCsrfValidation = false;
}
return parent::beforeAction($action);
}
public function actionLogin(): Response
{
}
public function actionCallback(): Response
{
}
}
Here, we've generated the skeleton of a controller for our two endpoints. We have two anonymous routes:
/actions/site-module/auth/login
/actions/site-module/auth/callback
The endpoints can be whatever you like, this is just the example we're using.
When triggering the site-module/auth/login
controller action, we'll need to create the authorization URL for a provider, and redirect away to their site.
We can do this with the following:
use verbb\auth\Auth;
public function actionLogin(): Response
{
// Create the provider class with the redirectUri pointing to our `actionCallback` method
$provider = new \modules\socialmodule\providers\Facebook([
'clientId' => '••••••••••••••••••••••••••••',
'clientSecret' => '••••••••••••••••••••••••••••',
'redirectUri' => UrlHelper::actionUrl('site-module/auth/callback'),
'graphApiVersion' => 'v3.3',
]);
// Redirect to the provider platform to login and authorize
return Auth::$plugin->getOAuth()->connect('my-plugin-handle', $provider);
}
We construct a config variable (which follows the standard league/oauth2-client
syntax), fetch a provider of our choosing (Facebook for example), and generate a authorization URL. With that set, we then redirect away with connect()
.
Some providers need extra settings, like the Facebook OAuth provider needs the graphApiVersion
.
After the user logs in on the provider site, they'll be redirect back to our site, hitting the site-module/auth/callback
controller action.
Let's add some code to handle generating the token.
public function actionCallback(): Response
{
// Create the provider class with the redirectUri pointing to our `actionCallback` method
$provider = new \modules\socialmodule\providers\Facebook([
'clientId' => '••••••••••••••••••••••••••••',
'clientSecret' => '••••••••••••••••••••••••••••',
'redirectUri' => UrlHelper::actionUrl('social-module/auth/callback'),
'graphApiVersion' => 'v3.3',
]);
// Fetch the Token model from the provider
$token = Auth::$plugin->getOAuth()->callback('my-plugin-handle', $provider);
// Record a referene
$token->reference = 'some-reference';
// Save it to the database
Auth::$plugin->getTokens()->upsertToken($token);
// Redirect to somewhere
return $this->redirect('/module');
}
Here, we grab our provider, then call callback()
to use the authentication code returned from the provider to fetch an access token. We then call upsertToken()
to save this to the database, while also adding a reference
for our uses. upsertToken()
will either create a new token, or find an existing token with the same ownerHandle
, providerType
, tokenType
and reference
. We could call saveToken()
but that would likely cause duplicates every time we run this callback.
You can see this can be improved be having somewhere central to store the provider config - but that'll be up to you to implement in your plugin.
So what does Auth do to help with this overall process, rather than doing it youself?
redirect
to allow a redirect after hitting the callback endpoint.state
to validate the state returned by the authorization URL for CSRF protection.origin
to keep track of the referrer.request()
method to make API calls with a valid token.The OAuthProvider
class (or the OAuthProviderTrait
) contains useful logic for creating an OAuth-based provider. It handles normalizing logic between OAuth 1 and 2 providers.
The only requirement is to define a getOAuthProviderClass()
method that returns the class of a league-based provider.
clientId
and clientSecret
properties, with auto-env variable parsingredirectUri
to set the redirect URI.getOAuthProviderConfig()
to set the config used when creating the provider.getOAuthProvider()
to get the provider instance.getAuthorizationUrlOptions()
to set any URL params when creating the authorization URL.getAuthorizationUrl()
the authorization URL.getAccessToken()
to return the access token from an authorization request.getToken()
to define logic on how to retrieve a token from the database, once created.request()
to trigger an authenticated API request, based on the getToken()
token.