Developer Information

Basic Authentication Flows

Login Flow

skinparam backgroundColor #EEE
skinparam handwritten false
skinparam sequence {
    ArrowColor DarkGreen
    ArrowFontSize 18
    ParticipantBorderColor ForestGreen
    ParticipantFontSize 20
    ParticipantBackgroundColor TECHNOLOGY
}

autonumber

"TYPO3 Login" -> "OAuth2 App": Authentication Request
"OAuth2 App" -> "TYPO3 Login": Authentication Response: Callback URL
"TYPO3 Login" -> "TYPO3 Login": Validate Session State
"TYPO3 Login" -> "OAuth2 App": Fetch Access Token
"OAuth2 App" -> "TYPO3 Login": Respond with AccessToken and ResourceOwner
"TYPO3 Login" -> "TYPO3 Login": Match by ResourceOwner ID & Provider ID
"TYPO3 Login" -> "TYPO3 Login": (optional) Evaluate MFA
"TYPO3 Login" -> "TYPO3 Backend": Return found user and login

Registering new Provider for User

skinparam backgroundColor #EEE
skinparam handwritten false
skinparam sequence {
    ArrowColor DarkGreen
    ArrowFontSize 18
    ParticipantBorderColor ForestGreen
    ParticipantFontSize 20
    ParticipantBackgroundColor TECHNOLOGY
    NoteBackgroundColor ForestGreen
    NoteBorderColor ForestGreen
    NoteShadowing false
}

autonumber

"TYPO3 Backend" -> "OAuth2 Popup": Open Popup for Authentication
"OAuth2 Popup" -> "OAuth2 Provider": Redirect for Application Registration
note left of "OAuth2 Provider"
    User enters their provider credentials
    and grants access.
end note
"OAuth2 Provider" -> "OAuth2 Popup": Callback with code & state
"OAuth2 Popup" -> "TYPO3 Backend": Post message with code & state for evaluation
"TYPO3 Backend" -> "TYPO3 Backend": Fetch access token and resource owner
"TYPO3 Backend" -> "TYPO3 Backend": Store provider id and user id in database
@enduml

Creating Users

This extension does not provide the possibility to create users on the fly itself. Its purpose is to provide OAuth2 authentication only. To allow users to register directly via OAuth, the extension comes with a PSR-14 event that can be used to create the users.

As user creation and their respective access rights is most likely specific to your custom domain and provider, you should implement this individually. Here is an overview of how you can achieve that:

Create the class

Create a class that will listen to the event and create a user:

class UserCreationListener {
   public function __invoke(UserLookupEvent $event): ?array {
      if ($event->getProviderId !== 'github') {
         // make sure you only react to "your" provider
         return null;
      }

      // get the current user record from the event - in case another listener
      // already provided data or the user existed in TYPO3
      // if it is null, the user does not exist yet.
      $userRecord = $event->getUserRecord() ?? [];

      // fetch the properties you want from the resource owner
      if ($event->getResourceOwner() instanceof GithubResourceOwner) {
         $resourceOwner = $event->getResourceOwner();

         // IMPORTANT: implement a check if the resource owner is allowed to access
         // this TYPO3 - for example by Github Organization. Otherwise _all_
         // Github users will be created and can log in.
         if (!$this->yourServiceClass->checkStuff($event)) {
            return null;
         }

         // depending on the specific resource owner, there are different sub-properties available
         $userRecord['email'] = $resourceOwner['email'];
         $userRecord['username'] = $resourceOwner['username'];
         // ...
      }

      // persist or update the $userRecord row in the be_users table
      // and enrich $userRecord with row data (TYPO3 record row with uid, TCA
      // control fields etc)
      // make sure to persist the oauth2 connection, too
      // @see \Waldhacker\Oauth2Client\Repository\BackendUserRepository::persistIdentityForUser
      $userRecord = $this->yourBackendRepository->addOrUpdate($userRecord);

      // set the user record for use in further authentication handling
      $event->setUserRecord($userRecord);
   }
}

Register Event Listener

In your Configuration/Services.yaml add a listener for the event:

services:
  MyCompany\MyPackage\EventListener\UserCreationListener:
    tags:
      - name: event.listener
        identifier: 'myListener'
        event: \Waldhacker\Oauth2Client\Events\UserLookupEvent

Note

We plan on developing further extensions for generic user creation in the future as add-ons to this one, however, user creation will not become a part of this extension.

API Methods

To make it easier for you to work with your chosen provider, you can use the Oauth2ProviderManager to create an instance of your provider and use that to fetch access tokens or query the API.

use \Waldhacker\Oauth2Client\Service\Oauth2ProviderManager;

class YourServiceClass {

   private AbstractProvider $provider;

   public function __construct(Oauth2ProviderManager $manager) {
      $this->provider = $manager->createProvider('github', $yourCallbackUrl);
   }

   public function checkStuff(UserLookupEvent $event): bool {
      // you can get code and state properties from the event
      $state = $event->getState();
      $code = $event->getCode();
      if (!isset($_SESSION['oauth2-state']) || $_SESSION['oauth2-state'] !== $state) {
         return null;
      }
      // ...
      $accessToken = $this->provider->getAccessToken(
             'authorization_code',
             [
                 'code' => $code,
             ]
         );
      if ($accessToken instanceof AccessToken) {
         $request = $this->getAuthenticatedRequest(AbstractProvider::METHOD_GET, $url, $token);
         $response = $this->getParsedResponse($request);
      }
      // ...
      return false;
   }
}

Other Use Cases

The event shown above is only triggered once a resource owner (user) has been successfully authenticated against the external service. Besides user creation, the event could for example additionally be used for updating user details on login to keep data in sync.