aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/auth_plugins/StudipAuthOAuth2.php
blob: aa9077633e8625ce3ac7c23b4983f96cd26d48e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<?php
use League\OAuth2\Client\Provider\GenericProvider;

/**
 * StudipAuthOAuth2.php - Stud.IP authentication using OAuth2
 *
 * @copyright 2024 Jan-Hendrik Willms <tleilax@gmail.com>
 * @license GPL2 or any later version
 * @since Stud.IP 6.0
 */
final class StudipAuthOAuth2 extends StudipAuthSSO
{
    protected string $client_id;
    protected string $client_secret;
    protected string $redirect_uri;

    protected string $url_authorize;
    protected string $url_access_token;
    protected string $url_resource_owner_details;

    private GenericProvider $oauth2_provider;

    private ?array $user_data = null;

    public function __construct($config = [])
    {
        parent::__construct($config);

        if (!isset($this->plugin_fullname)) {
            $this->plugin_fullname = _('OAuth2');
        }

        if (Request::option('sso') === $this->plugin_name) {
            $options = [
                'clientId' => $this->client_id,
                'clientSecret' => $this->client_secret,
                'redirectUri' => $this->redirect_uri,
                'urlAuthorize' => $this->url_authorize,
                'urlAccessToken' => $this->url_access_token,
                'urlResourceOwnerDetails' => $this->url_resource_owner_details,
            ];

            if (Config::get()->getValue('HTTP_PROXY')) {
                $options['proxy'] = Config::get()->getValue('HTTP_PROXY');
                $options['verify'] = false;
            }

            $this->oauth2_provider = new GenericProvider($options);
        }
    }

    public function getUser()
    {
        return $this->getUserData($this->getUsernameKey());
    }

    public function verifyUsername($username)
    {
        if (isset($this->user_data)) {
            return parent::verifyUsername($this->getUser());
        }

        if (!Request::get('code')) {
            $authorizationUrl = $this->oauth2_provider->getAuthorizationUrl(['scope' => 'profile email']);

            $_SESSION[self::class] = [
                'state' => $this->oauth2_provider->getState(),
                'redirect' => Request::url(),
            ];

            page_close();
            header('Location: ' . $authorizationUrl);
            die;
        } elseif (
            !Request::get('state')
            || empty($_SESSION[self::class]['state'])
            || Request::get('state') !== $_SESSION[self::class]['state']
        ) {
            if (isset($_SESSION[self::class])) {
                unset($_SESSION[self::class]);
            }
        } else {
            $accessToken = $this->oauth2_provider->getAccessToken('authorization_code', [
                'code' => Request::get('code'),
            ]);

            $resourceOwner = $this->oauth2_provider->getResourceOwner($accessToken);

            $this->user_data = $resourceOwner->toArray();

            return parent::verifyUsername($this->getUser());
        }

        return null;
    }

    /**
     * Callback that can be used in user_data_mapping array.
     */
    public function getUserData(string $key): ?string
    {
        return $this->user_data[$key];
    }

    /**
     * Returns the key used to store the username from user_data_mapping if
     * present. Defaults to 'nickname'.
     */
    private function getUsernameKey(): string
    {
        return $this->user_data_mapping['map_args']['auth_user_md5.username'] ?? 'nickname';
    }
}