1<?php
2
3namespace dokuwiki\plugin\oauthkeycloak;
4
5use dokuwiki\plugin\oauth\Service\AbstractOAuth2Base;
6use OAuth\Common\Http\Uri\Uri;
7
8/**
9 * Custom Service for Keycloak oAuth
10 */
11class Keycloak extends AbstractOAuth2Base
12{
13    /**
14     * Defined scopes are listed here:
15     * @link https://www.keycloak.org/docs/latest/server_admin/#_client_scopes
16     */
17    const SCOPE_OPENID    = 'openid';
18
19    /**
20     * Endpoints are listed here:
21     * @link https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#authorization-server-metadata
22     */
23    const ENDPOINT_AUTH     = 'authorization_endpoint';
24    const ENDPOINT_TOKEN    = 'token_endpoint';
25    const ENDPOINT_USERINFO = 'userinfo_endpoint';
26    /**
27     * This endpoint is used for backchannel logout and documented here
28     * @link https://www.keycloak.org/docs/latest/server_admin/#con-basic-settings_server_administration_guide
29     */
30    const ENDPOINT_LOGOUT   = 'end_session_endpoint';
31
32    protected $discovery;
33
34    /**
35     * Return URI of discovered endpoint
36     *
37     * @return string
38     */
39    public function getEndpoint(string $endpoint)
40    {
41        if (!isset($this->discovery)) {
42            $plugin = plugin_load('helper', 'oauthkeycloak');
43            $json = file_get_contents($plugin->getConf('openidurl'));
44            if (!$json) return '';
45            $this->discovery = json_decode($json, true);
46        }
47        if (!isset($this->discovery[$endpoint])) return '';
48        return $this->discovery[$endpoint];
49    }
50
51    /** @inheritdoc */
52    public function getAuthorizationEndpoint()
53    {
54        return new Uri($this->getEndpoint(self::ENDPOINT_AUTH));
55    }
56
57    /** @inheritdoc */
58    public function getAccessTokenEndpoint()
59    {
60        return new Uri($this->getEndpoint(self::ENDPOINT_TOKEN));
61    }
62
63    /** @inheritdoc */
64    protected function getAuthorizationMethod()
65    {
66        return static::AUTHORIZATION_METHOD_HEADER_BEARER;
67    }
68
69    /**
70     * Logout from Keycloak
71     *
72     * @return void
73     * @throws \OAuth\Common\Exception\Exception
74     */
75    public function logout()
76    {
77        $token = $this->getStorage()->retrieveAccessToken($this->service());
78        $refreshToken = $token->getRefreshToken();
79
80        if (!$refreshToken) {
81            return;
82        }
83
84        $parameters = [
85            'client_id' => $this->credentials->getConsumerId(),
86            'client_secret' => $this->credentials->getConsumerSecret(),
87            'refresh_token' => $refreshToken,
88        ];
89
90        $this->httpClient->retrieveResponse(
91            new Uri($this->getEndpoint(self::ENDPOINT_LOGOUT)),
92            $parameters,
93            $this->getExtraOAuthHeaders()
94        );
95    }
96}
97