Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.15% covered (success)
96.15%
25 / 26
85.71% covered (success)
85.71%
6 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Microsoft
96.15% covered (success)
96.15%
25 / 26
85.71% covered (success)
85.71%
6 / 7
11
0.00% covered (danger)
0.00%
0 / 1
 getAuthorizeUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAccessTokenUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getScope
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __construct
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getUserProfile
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
4
 getLogoSource
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getThemeColor
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Miniframe\SocialLogin\Provider;
4
5use Miniframe\Core\Config;
6use Miniframe\Core\Request;
7use Miniframe\SocialLogin\Model\User;
8
9class Microsoft extends AbstractOAuth2Provider
10{
11    /**
12     * Returns the Authorize URL
13     *
14     * @return string
15     */
16    protected function getAuthorizeUrl(): string
17    {
18        return 'https://login.microsoftonline.com/' . $this->tenant . '/oauth2/v2.0/authorize';
19    }
20
21    /**
22     * Returns the Access Token URL
23     *
24     * @return string
25     */
26    protected function getAccessTokenUrl(): string
27    {
28        return 'https://login.microsoftonline.com/' . $this->tenant . '/oauth2/v2.0/token';
29    }
30
31    /**
32     * The tenant ID. Valid values are common, organizations, consumers, and tenant identifiers.
33     *
34     * @var string
35     *
36     * @see https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow
37     */
38    protected $tenant = 'common';
39
40    /**
41     * Returns the requested scope
42     *
43     * @return string|null
44     */
45    protected function getScope(): ?string
46    {
47        return 'User.Read';
48    }
49
50    /**
51     * Creates a new Microsoft OAuth 2.0 provider
52     *
53     * @param Request $request Reference to the Request object.
54     * @param Config  $config  Reference to the Config object.
55     */
56    public function __construct(Request $request, Config $config)
57    {
58        parent::__construct($request, $config);
59        if ($config->has('sociallogin-microsoft', 'tenant')) {
60            $this->tenant = $config->get('sociallogin-microsoft', 'tenant');
61        }
62    }
63
64    /**
65     * Returns the user profile
66     *
67     * @param array $accessToken The access token.
68     *
69     * @return User
70     */
71    protected function getUserProfile(array $accessToken): User
72    {
73        $userData = $this->curlRequest('https://graph.microsoft.com/v1.0/me', 'GET', [], [
74            'Authorization: Bearer ' . $accessToken['access_token'],
75        ]);
76
77        // When fetching the avatar fails, fall back to Gravatar
78        $avatar = 'https://s.gravatar.com/avatar/' . md5(strtolower(trim($userData['userPrincipalName'])))
79            . '?s=80&d=identicon';
80
81        foreach (
82            [
83                // For business accounts (Office 365), the avatar can be located here:
84                'https://graph.microsoft.com/v1.0/me/photo/$value',
85                // For personal accounts (Hotmail, etc.), the avatar can be located here:
86                'https://graph.microsoft.com/beta/me/photo/$value'
87            ] as $url
88        ) {
89            try {
90                $avatarBlob = $this->curlRequest($url, 'GET', [], [
91                    'Authorization: Bearer ' . $accessToken['access_token'],
92                ], true);
93                $avatar = 'data:' . $avatarBlob['contentType'] . ';base64,'
94                    . base64_encode($avatarBlob['responseBody']);
95                break;
96            } catch (\RuntimeException $exception) {
97                if ($exception->getMessage() == 'HTTP #404 error') {
98                    continue;
99                }
100                throw $exception;
101            }
102        }
103
104        return new User(
105            $userData['id'],
106            $userData['userPrincipalName'],
107            $userData['displayName'],
108            $avatar,
109            static::class,
110            ['accessToken' => $accessToken, 'userData' => $userData]
111        );
112    }
113
114    /**
115     * Returns the image source for the logo of this provider.
116     *
117     * @return string
118     */
119    public static function getLogoSource(): string
120    {
121        return 'data:image/svg+xml;base64,'
122            . base64_encode(file_get_contents(__DIR__ . '/../../templates/logos/Microsoft.svg'));
123    }
124
125    /**
126     * Returns the theme color for this provider.
127     *
128     * @return string
129     */
130    public static function getThemeColor(): string
131    {
132        return 'rgb(0, 103, 184)';
133    }
134}