Skip to content

Commit d3b065a

Browse files
Merge pull request #2 from PrestaShopCorp/feat/get-session-logout-url
feat: get session logout url
2 parents 30cb923 + c6a6f6b commit d3b065a

File tree

5 files changed

+304
-5
lines changed

5 files changed

+304
-5
lines changed

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,46 @@ if (!empty($_GET['error'])) {
9494

9595
For more information see the PHP League's general usage examples.
9696

97+
## Logout flow
98+
99+
Going beyond the scope of this library we provide a helper function `getLogoutUrl` to logout from your oauth2 session.
100+
101+
The only required parameter is `id_token_int` here, you can optionally provide `post_logout_redirect_uri` to override the one from the constructor.
102+
103+
Also don't forget to provide `postLogoutCallbackUri` at construction time if you plan to use it.
104+
105+
```php
106+
$prestaShopProvider = new \PrestaShop\OAuth2\Client\Provider\PrestaShop([
107+
'clientId' => 'yourClientId', // The client ID assigned to you by PrestaShop
108+
'clientSecret' => 'yourClientSecret', // The client password assigned to you by PrestaShop
109+
'redirectUri' => 'yourClientRedirectUri', // The URL responding to the code flow implemented here
110+
'postLogoutCallbackUri' => 'yourLogoutCallbackUri', // Logout url whitelisted among the ones defined with your client
111+
// Optional parameters
112+
'uiLocales' => ['fr-FR', 'en'],
113+
'acrValues' => ['prompt:create'], // In that specific case we change the default prompt to the "register" page
114+
]);
115+
116+
if (isset($_GET['oauth2Callback')) {
117+
// your logout code
118+
session_destroy();
119+
120+
} else {
121+
/** @var \League\OAuth2\Client\Token\AccessToken $accessToken */
122+
$accessToken = $_SESSION['accessToken'];
123+
124+
// The only required parameter is "id_token_int" here,
125+
// you can optionally provide "post_logout_redirect_uri" to override the one from the constructor.
126+
$logoutUrl = $prestaShopProvider->getLogoutUrl([
127+
'id_token_hint' => $accessToken->getValues()['id_token'],
128+
// (Optionnal here) Logout url whitelisted among the ones defined with your client
129+
// 'post_logout_redirect_uri' => 'https://my-logout-url/?oauth2Callback',
130+
]);
131+
132+
header('Location: ' . $logoutUrl);
133+
exit;
134+
}
135+
```
136+
97137
## Testing
98138

99139
``` bash

composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,9 @@
3737
"psr-4": {
3838
"PrestaShop\\OAuth2\\Client\\Test\\": "tests/src/"
3939
}
40+
},
41+
"scripts": {
42+
"phpunit": "./vendor/bin/phpunit --coverage-text",
43+
"php-cs-fixer": "./vendor/bin/php-cs-fixer fix --config .php_cs.dist.php --diff"
4044
}
4145
}

src/Provider/LogoutTrait.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace PrestaShop\OAuth2\Client\Provider;
4+
5+
trait LogoutTrait
6+
{
7+
/**
8+
* @var string
9+
*/
10+
protected $postLogoutCallbackUri;
11+
12+
/**
13+
* @return string
14+
*/
15+
public function getBaseSessionLogoutUrl(): string
16+
{
17+
return 'https://oauth.prestashop.com/oauth2/sessions/logout';
18+
}
19+
20+
/**
21+
* Builds the session logout URL.
22+
*
23+
* @param array $options
24+
*
25+
* @return string Logout URL
26+
*
27+
* @throws \Exception
28+
*/
29+
public function getLogoutUrl(array $options = []): string
30+
{
31+
$base = $this->getBaseSessionLogoutUrl();
32+
$params = $this->getLogoutParameters($options);
33+
$query = $this->getLogoutQuery($params);
34+
35+
return $this->appendQuery($base, $query);
36+
}
37+
38+
/**
39+
* @param array $options
40+
*
41+
* @return string[]
42+
*
43+
* @throws \Exception
44+
*/
45+
protected function getLogoutParameters(array $options): array
46+
{
47+
if (empty($options['id_token_hint'])) {
48+
// $options['id_token_hint'] = $this->getSessionAccessToken()->getValues()['id_token'];
49+
throw new \Exception('Missing id_token_hint required parameter');
50+
}
51+
52+
if (empty($options['post_logout_redirect_uri'])) {
53+
if (!empty($this->postLogoutCallbackUri)) {
54+
$options['post_logout_redirect_uri'] = $this->postLogoutCallbackUri;
55+
} else {
56+
throw new \Exception('Missing post_logout_redirect_uri required parameter');
57+
}
58+
}
59+
60+
return $options;
61+
}
62+
63+
/**
64+
* Builds the logout URL's query string.
65+
*
66+
* @param array $params Query parameters
67+
*
68+
* @return string Query string
69+
*/
70+
protected function getLogoutQuery(array $params): string
71+
{
72+
return $this->buildQueryString($params);
73+
}
74+
}

src/Provider/PrestaShop.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
class PrestaShop extends AbstractProvider
3030
{
3131
use BearerAuthorizationTrait;
32+
use LogoutTrait;
3233

3334
/**
3435
* @var string If set, will be sent as the "prompt" parameter
@@ -54,7 +55,7 @@ class PrestaShop extends AbstractProvider
5455
/**
5556
* @return string
5657
*/
57-
public function getBaseAuthorizationUrl()
58+
public function getBaseAuthorizationUrl(): string
5859
{
5960
return 'https://oauth.prestashop.com/oauth2/auth';
6061
}
@@ -64,7 +65,7 @@ public function getBaseAuthorizationUrl()
6465
*
6566
* @return string
6667
*/
67-
public function getBaseAccessTokenUrl(array $params)
68+
public function getBaseAccessTokenUrl(array $params): string
6869
{
6970
return 'https://oauth.prestashop.com/oauth2/token';
7071
}
@@ -74,7 +75,7 @@ public function getBaseAccessTokenUrl(array $params)
7475
*
7576
* @return string
7677
*/
77-
public function getResourceOwnerDetailsUrl(AccessToken $token)
78+
public function getResourceOwnerDetailsUrl(AccessToken $token): string
7879
{
7980
return 'https://oauth.prestashop.com/userinfo';
8081
}
@@ -84,7 +85,7 @@ public function getResourceOwnerDetailsUrl(AccessToken $token)
8485
*
8586
* @return string[]
8687
*/
87-
protected function getAuthorizationParameters(array $options)
88+
protected function getAuthorizationParameters(array $options): array
8889
{
8990
if (empty($options['prompt']) && $this->prompt) {
9091
$options['prompt'] = $this->prompt;
@@ -106,7 +107,7 @@ protected function getAuthorizationParameters(array $options)
106107
/**
107108
* @return string[]
108109
*/
109-
public function getDefaultScopes()
110+
public function getDefaultScopes(): array
110111
{
111112
return ['openid', 'offline_access'];
112113
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
<?php
2+
3+
namespace PrestaShop\OAuth2\Client\Test\Provider;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use PrestaShop\OAuth2\Client\Provider\PrestaShop;
7+
8+
class LogoutTraitTest extends TestCase
9+
{
10+
/**
11+
* @var PrestaShop
12+
*/
13+
private $provider;
14+
15+
protected function setUp(): void
16+
{
17+
$this->provider = new PrestaShop([
18+
'clientId' => 'test-client',
19+
'clientSecret' => 'secret',
20+
'redirectUri' => 'https://test-client-redirect.net',
21+
'postLogoutCallbackUri' => 'https://test-client-redirect.net/logout?oauth2Callback',
22+
'uiLocales' => ['fr-CA', 'en'],
23+
'acrValues' => ['prompt:login'],
24+
]);
25+
}
26+
27+
/**
28+
* @test
29+
*/
30+
public function itShouldGenerateLogoutUrl(): void
31+
{
32+
$idToken = 'someRandomIdToken';
33+
34+
$url = $this->provider->getLogoutUrl([
35+
'id_token_hint' => $idToken,
36+
]);
37+
$uri = parse_url($url);
38+
$query = [];
39+
40+
if (\is_array($uri) && isset($uri['query'])) {
41+
parse_str($uri['query'], $query);
42+
}
43+
44+
$this->assertEquals($idToken, $query['id_token_hint']);
45+
$this->assertEquals('https://test-client-redirect.net/logout?oauth2Callback', $query['post_logout_redirect_uri']);
46+
// $this->assertEquals('fr-CA en', $query['ui_locales']);
47+
}
48+
49+
/**
50+
* @test
51+
*/
52+
public function itShouldGenerateLogoutUrlWithOptionalParameters(): void
53+
{
54+
$idToken = 'someRandomIdToken';
55+
$postLogoutRedirectUri = 'https://overriden-post-logout-uri.net';
56+
57+
$url = $this->provider->getLogoutUrl([
58+
'id_token_hint' => $idToken,
59+
'post_logout_redirect_uri' => $postLogoutRedirectUri,
60+
]);
61+
$uri = parse_url($url);
62+
$query = [];
63+
64+
if (\is_array($uri) && isset($uri['query'])) {
65+
parse_str($uri['query'], $query);
66+
}
67+
68+
$this->assertEquals($idToken, $query['id_token_hint']);
69+
$this->assertEquals($postLogoutRedirectUri, $query['post_logout_redirect_uri']);
70+
// $this->assertEquals('fr-CA en', $query['ui_locales']);
71+
}
72+
73+
/**
74+
* @test
75+
*/
76+
public function itShouldGenerateLogoutUrlWithOptionalOnlyParameters(): void
77+
{
78+
$idToken = 'someRandomIdToken';
79+
$postLogoutRedirectUri = 'https://overriden-post-logout-uri.net';
80+
81+
$this->provider = new PrestaShop([
82+
'clientId' => 'test-client',
83+
'clientSecret' => 'secret',
84+
'redirectUri' => 'https://test-client-redirect.net',
85+
// 'postLogoutCallbackUri' => 'https://test-client-redirect.net/logout?oauth2Callback',
86+
'uiLocales' => ['fr-CA', 'en'],
87+
'acrValues' => ['prompt:login'],
88+
]);
89+
90+
$url = $this->provider->getLogoutUrl([
91+
'id_token_hint' => $idToken,
92+
'post_logout_redirect_uri' => $postLogoutRedirectUri,
93+
]);
94+
$uri = parse_url($url);
95+
$query = [];
96+
97+
if (\is_array($uri) && isset($uri['query'])) {
98+
parse_str($uri['query'], $query);
99+
}
100+
101+
$this->assertEquals($idToken, $query['id_token_hint']);
102+
$this->assertEquals($postLogoutRedirectUri, $query['post_logout_redirect_uri']);
103+
// $this->assertEquals('fr-CA en', $query['ui_locales']);
104+
}
105+
106+
/**
107+
* @test
108+
*/
109+
public function itShouldGetBaseSessionLogoutUrl(): void
110+
{
111+
$url = $this->provider->getBaseSessionLogoutUrl();
112+
$uri = parse_url($url);
113+
114+
$path = '';
115+
if (\is_array($uri) && isset($uri['path'])) {
116+
$path = $uri['path'];
117+
}
118+
119+
$this->assertEquals('/oauth2/sessions/logout', $path);
120+
}
121+
122+
/**
123+
* @test
124+
*/
125+
public function itShouldGetLogoutUrl(): void
126+
{
127+
$idToken = 'someRandomIdToken';
128+
129+
$url = $this->provider->getLogoutUrl([
130+
'id_token_hint' => $idToken,
131+
]);
132+
$uri = parse_url($url);
133+
134+
$path = '';
135+
if (\is_array($uri) && isset($uri['path'])) {
136+
$path = $uri['path'];
137+
}
138+
139+
$this->assertEquals('/oauth2/sessions/logout', $path);
140+
}
141+
142+
/**
143+
* @test
144+
*/
145+
public function itShouldThrowExceptionWhenIdTokenIsMissing(): void
146+
{
147+
$this->expectException(\Exception::class);
148+
149+
$this->expectExceptionMessage('Missing id_token_hint required parameter');
150+
151+
$this->provider->getLogoutUrl([
152+
// 'id_token_hint' => $idToken
153+
]);
154+
}
155+
156+
/**
157+
* @test
158+
*/
159+
public function itShouldThrowExceptionWhenPostLogoutCallbackUriIsMissing(): void
160+
{
161+
$idToken = 'someRandomIdToken';
162+
163+
$this->provider = new PrestaShop([
164+
'clientId' => 'test-client',
165+
'clientSecret' => 'secret',
166+
'redirectUri' => 'https://test-client-redirect.net',
167+
// 'postLogoutCallbackUri' => 'https://test-client-redirect.net/logout?oauth2Callback',
168+
'uiLocales' => ['fr-CA', 'en'],
169+
'acrValues' => ['prompt:login'],
170+
]);
171+
172+
$this->expectException(\Exception::class);
173+
174+
$this->expectExceptionMessage('Missing post_logout_redirect_uri required parameter');
175+
176+
$this->provider->getLogoutUrl([
177+
'id_token_hint' => $idToken,
178+
]);
179+
}
180+
}

0 commit comments

Comments
 (0)