Skip to content

Microsoft OAuth Service #105

Closed
Closed

Description

@nicojmb

Hi all,

I've added a Microsoft OAuth service and i would like to share with community.

I take this opportunity to request that it be made configurable whether or not new users can be registered

Here are de code :)

config file: App\Config\ShieldOAuthConfig.php

<?php
public array $oauthConfigs = [
    'microsoft' => [
        'client_id'     => 'Get it from Microsoft Entra ID',
        'client_secret' => 'Get it from Microsoft Entra ID',
        'allow_login' => true,
    ],
];

library file: App\Libraries\ShieldOAuth\MicrosoftOAuth.php

<?php

declare(strict_types=1);

namespace App\Libraries\ShieldOAuth;

use Datamweb\ShieldOAuth\Libraries\Basic\AbstractOAuth;
use Exception;
use stdClass;

class MicrosoftOAuth extends AbstractOAuth
{
    private static $API_CODE_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize';
    private static $API_TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/token';
    private static $API_USER_INFO_URL = 'https://graph.microsoft.com/v1.0/me';
    private static $APPLICATION_NAME = 'ShieldOAuth';

    protected string $token;
    protected $client;
    protected $config;
    protected string $client_id;
    protected string $client_secret;
    protected string $callback_url;

    public function __construct(string $token = '')
    {
        $this->token  = $token;
        $this->client = \Config\Services::curlrequest();
        $this->config = new \Config\ShieldOAuthConfig();
        $this->callback_url = base_url('oauth/' . $this->config->call_back_route);
        $this->client_id = env('ShieldOAuthConfig.microsoft.client_id', $this->config->oauthConfigs['microsoft']['client_id']);
        $this->client_secret = env('ShieldOAuthConfig.microsoft.client_secret', $this->config->oauthConfigs['microsoft']['client_secret']);
    }
    public function makeGoLink(string $state): string
    {
        try
        {
            if (empty($this->client_id))
            {
                throw new Exception('Microsoft Tenant ID is empty,');
            }

            if (empty($this->client_id))
            {
                throw new Exception('Microsoft Client ID is empty,');
            }

            if (empty($this->client_secret))
            {
                throw new Exception('Microsoft Secret is empty,');
            }

            $query = http_build_query([
                'client_id' => $this->client_id,
                'response_type' => 'code',
                'redirect_uri' => $this->callback_url,
                'response_mode' => 'query',
                'approval_prompt' => 'auto',
                'scope' => 'User.Read profile openid email',
                'state' => $state
            ]);

            $microsoftURL = self::$API_CODE_URL . '?' . $query;

            return $microsoftURL;
        }
        catch (\Throwable $e)
        {
            die($e->getMessage());
        }
    }

    public function fetchAccessTokenWithAuthCode(array $allGet): void
    {
        $client = \Config\Services::curlrequest();

        try
        {
            $response = $client->request('POST', self::$API_TOKEN_URL, [
                'form_params' => [
                    'client_id'     => $this->client_id,
                    'client_secret' => $this->client_secret,
                    'grant_type'    => 'authorization_code',
                    'redirect_uri'  => $this->callback_url,
                    'code'          => $allGet['code']
                ],
                'http_errors' => false,
            ]);

            $response = json_decode($response->getBody());

            if (!empty($response->error))
            {
                throw new Exception($response->error);
            }

            $this->setToken($response->access_token);
        }
        catch (Exception $e)
        {
            die($e->getMessage());
        }
    }

    public function fetchUserInfoWithToken(): object
    {
        try
        {
            $response = $this->client->request('GET', self::$API_USER_INFO_URL, [
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->getToken(),
                ],
                'http_errors' => false,
            ]);
        }
        catch (Exception $e)
        {
            die($e->getMessage());
        }

        $response = json_decode($response->getBody());

        $userInfo = new stdClass();
        $userInfo->username = $response->userPrincipalName;
        $userInfo->email = $response->mail;
        $userInfo->first_name = $response->givenName;
        $userInfo->last_name = $response->surname;
        $userInfo->avatar = '';

        return $userInfo;
    }

    public function setColumnsName(string $nameOfProcess, object $userInfo): array
    {
        if ($nameOfProcess === 'syncingUserInfo')
        {
            $usersColumnsName = [
                $this->config->usersColumnsName['first_name'] => $userInfo->first_name,
                $this->config->usersColumnsName['last_name']  => $userInfo->last_name,
                //$this->config->usersColumnsName['avatar']  => $userInfo->last_name, // No get avantar from request
            ];
        }

        if ($nameOfProcess === 'newUser')
        {

            die('Auto-register users is disabled;');

            // Commented because i not use register new users

            // $usersColumnsName = [
            //     'username'                                    => $userInfo->username,
            //     'email'                                           => $userInfo->email,
            //     'password'                                    => random_string('crypto', 32),
            //     'active'                                          => TRUE,
            //     $this->config->usersColumnsName['first_name'] => $userInfo->first_name,
            //     $this->config->usersColumnsName['last_name']  => $userInfo->last_name,
            //     $this->config->usersColumnsName['avatar']     => $userInfo->avatar,
            // ];
        }

        return $usersColumnsName;
    }
}

Activity

  1. datamweb commented on Apr 7, 2024

    @datamweb
    Owner

    @nicojmb Thank you for sharing this driver. I know people have been looking for this.
    I will move this to the discussion section.

  2. Repository owner locked and limited conversation to collaborators on Apr 7, 2024
  3. converted this issue into a discussion #106 on Apr 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions