<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kamal Ud Din</title>
    <description>The latest articles on DEV Community by Kamal Ud Din (@kamal_dev).</description>
    <link>https://dev.to/kamal_dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1450764%2F85f5cbc6-c9e5-4f17-a4ff-b39b3db4eeb7.jpg</url>
      <title>DEV Community: Kamal Ud Din</title>
      <link>https://dev.to/kamal_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kamal_dev"/>
    <language>en</language>
    <item>
      <title>API permissions : Unable to retrieve Microsoft Ads account and Merchant Center data using Microsoft Azure Active Directory</title>
      <dc:creator>Kamal Ud Din</dc:creator>
      <pubDate>Fri, 26 Apr 2024 11:41:45 +0000</pubDate>
      <link>https://dev.to/kamal_dev/laravel-socialite-unable-to-retrieve-microsoft-ads-account-and-merchant-center-data-using-microsoft-azure-active-directory-580i</link>
      <guid>https://dev.to/kamal_dev/laravel-socialite-unable-to-retrieve-microsoft-ads-account-and-merchant-center-data-using-microsoft-azure-active-directory-580i</guid>
      <description>&lt;p&gt;0&lt;/p&gt;

&lt;p&gt;I'm working on a Laravel project using Socialite to authenticate and log in users with their Microsoft accounts. I've created an application in the Azure portal with the "Supported account types: All Microsoft account users" setting. The authentication process is working fine, and I can successfully fetch basic user details such as ID, name, and email. However, I'm unable to retrieve the user's Ads account and Merchant Center information using Microsoft Azure Active Directory. I've checked the Azure portal, but I cannot find the necessary API permissions/scopes, such as &lt;code&gt;ads.manage&lt;/code&gt;. I would like to know how to enable these permissions and properly configure my Azure application to fetch the required data.&lt;/p&gt;

&lt;p&gt;`&amp;lt;?php&lt;/p&gt;

&lt;p&gt;namespace SocialiteProviders\Microsoft;&lt;/p&gt;

&lt;p&gt;use Illuminate\Support\Arr;&lt;br&gt;
use GuzzleHttp\RequestOptions;&lt;br&gt;
use Illuminate\Support\Facades\Log;&lt;br&gt;
use GuzzleHttp\Exception\ClientException;&lt;br&gt;
use SocialiteProviders\Manager\OAuth2\AbstractProvider;&lt;br&gt;
use SocialiteProviders\Microsoft\MicrosoftUser as User;&lt;/p&gt;

&lt;p&gt;class Provider extends AbstractProvider&lt;br&gt;
{&lt;br&gt;
    public const IDENTIFIER = 'MICROSOFT';&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected const DEFAULT_FIELDS_USER = ['id', 'displayName', 'userPrincipalName'];
protected const DEFAULT_FIELDS_ADS_ACCOUNTS = ['id', 'name', 'customerId'];
protected const DEFAULT_FIELDS_MERCHANT_ACCOUNTS = ['id', 'name'];

protected $scopes = [
    'https://graph.microsoft.com/User.Read',
    'https://ads.microsoft.com/msads.manage',
    'offline_access',
];

protected function getAuthUrl($state)
{
    return $this-&amp;gt;buildAuthUrlFromBase(
        'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
        $state
    );
}

protected function getTokenUrl()
{
    return 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
}

protected function getUserByToken($token)
{
    $user = $this-&amp;gt;getUserData($token, 'https://graph.microsoft.com/v1.0/me', self::DEFAULT_FIELDS_USER);

    if (!$user) {
        return [];
    }

    try {
        $adsAccounts = $this-&amp;gt;getUserData($token, 'https://ads.microsoft.com/api/v13/accounts', self::DEFAULT_FIELDS_ADS_ACCOUNTS);
    } catch (ClientException $e) {
        Log::error('Failed to fetch ads accounts', ['exception' =&amp;gt; $e, 'user' =&amp;gt; $user]);
        $adsAccounts = [];
    }

    try {
        $merchantAccounts = $this-&amp;gt;getUserData($token, 'https://marketing.microsoft.com/rest/v1/merchantcenters', self::DEFAULT_FIELDS_MERCHANT_ACCOUNTS);
    } catch (ClientException $e) {
        Log::error('Failed to fetch merchant accounts', ['exception' =&amp;gt; $e, 'user' =&amp;gt; $user]);
        $merchantAccounts = [];
    }

    $user['adsAccounts'] = $adsAccounts;
    $user['merchantAccounts'] = $merchantAccounts;

    return $user;
}

protected function mapUserToObject(array $user)
{
    return (new User())-&amp;gt;setRaw($user)-&amp;gt;map([
        'id' =&amp;gt; $user['id'],
        'nickname' =&amp;gt; null,
        'name' =&amp;gt; $user['displayName'],
        'email' =&amp;gt; $user['userPrincipalName'],
        'avatar' =&amp;gt; Arr::get($user, 'avatar'),
        'adsAccounts' =&amp;gt; Arr::get($user, 'adsAccounts'),
        'merchantAccounts' =&amp;gt; Arr::get($user, 'merchantAccounts'),
        'tenant' =&amp;gt; Arr::get($user, 'tenant'),
    ]);
}

protected function getTokenFields($code)
{
    return array_merge(parent::getTokenFields($code), [
        'scope' =&amp;gt; $this-&amp;gt;formatScopes($this-&amp;gt;getScopes(), $this-&amp;gt;scopeSeparator),
    ]);
}

public static function additionalConfigKeys()
{
    return ['tenant', 'include_tenant_info', 'include_avatar', 'include_avatar_size', 'fields', 'tenant_fields'];
}

protected function getUserData($token, $url, $fields)
{
    $response = $this-&amp;gt;getHttpClient()-&amp;gt;get($url, [
        RequestOptions::HEADERS =&amp;gt; [
            'Accept' =&amp;gt; 'application/json',
            'Authorization' =&amp;gt; 'Bearer ' . $token,
        ],
        RequestOptions::QUERY =&amp;gt; [
            '$select' =&amp;gt; implode(',', $fields),
        ],
    ]);

    $data = json_decode((string) $response-&amp;gt;getBody(), true);

    if (!$data) {
        return [];
    }

    return $data;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'microsoft' =&amp;gt; [
'client_id' =&amp;gt; env('MICROSOFT_CLIENT_ID'),
'client_secret' =&amp;gt; env('MICROSOFT_CLIENT_SECRET'),
'tenant_id' =&amp;gt; env('MICROSOFT_TENANT_ID'),
'redirect' =&amp;gt; env('MICROSOFT_REDIRECT_URI'),
'tenant' =&amp;gt; 'common',
'include_tenant_info' =&amp;gt; true,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;],&lt;/p&gt;

&lt;p&gt;MICROSOFT_CLIENT_ID=*****&lt;br&gt;
MICROSOFT_CLIENT_SECRET=***&lt;br&gt;
MICROSOFT_TENANT_ID=**&lt;br&gt;
MICROSOFT_REDIRECT_URI=&lt;a href="https://infinitemsfeed.com/microsoft/auth"&gt;https://infinitemsfeed.com/microsoft/auth&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public function redirectToMicrosoft()
{
    return Socialite::driver('microsoft')-&amp;gt;redirect();
}

public function handleMicrosoftCallback()
{
    // return "abcdefghi";
    $user = Socialite::driver('microsoft')-&amp;gt;user();

    return $user;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;const handleMicrosoftLogin = () =&amp;gt; {&lt;br&gt;
  window.open("&lt;a href="https://infinitemsfeed.com/auth/microsoft?token="&gt;https://infinitemsfeed.com/auth/microsoft?token=&lt;/a&gt;" + window.sessionToken, "_blank")&lt;br&gt;
}`&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sw08ot587wyd4oom14y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sw08ot587wyd4oom14y.png" alt="Image of azure portal permission dashboard" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm facing issues with setting up API permissions or scopes&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>microsoft</category>
      <category>microsoftads</category>
      <category>laravel</category>
    </item>
  </channel>
</rss>
