-
Notifications
You must be signed in to change notification settings - Fork 3
/
Provider.php
143 lines (119 loc) · 3.99 KB
/
Provider.php
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<?php
namespace SocialiteProviders\FranceConnect;
use GuzzleHttp\RequestOptions;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Laravel\Socialite\Two\InvalidStateException;
use SocialiteProviders\Manager\OAuth2\AbstractProvider;
class Provider extends AbstractProvider
{
/**
* API URLs.
*/
public const PROD_BASE_URL = 'https://app.franceconnect.gouv.fr/api/v1';
public const TEST_BASE_URL = 'https://fcp.integ01.dev-franceconnect.fr/api/v1';
public const IDENTIFIER = 'FRANCECONNECT';
protected $scopes = [
'openid',
'given_name',
'family_name',
'gender',
'birthplace',
'birthcountry',
'email',
'preferred_username',
];
protected $scopeSeparator = ' ';
/**
* Return API Base URL.
*
* @return string
*/
protected function getBaseUrl()
{
return config('app.env') === 'production' ? self::PROD_BASE_URL : self::TEST_BASE_URL;
}
public static function additionalConfigKeys(): array
{
return ['logout_redirect'];
}
protected function getAuthUrl($state): string
{
//It is used to prevent replay attacks
$this->parameters['nonce'] = Str::random(20);
return $this->buildAuthUrlFromBase($this->getBaseUrl().'/authorize', $state);
}
protected function getTokenUrl(): string
{
return $this->getBaseUrl().'/token';
}
/**
* {@inheritdoc}
*/
public function getAccessTokenResponse($code)
{
$response = $this->getHttpClient()->post($this->getBaseUrl().'/token', [
RequestOptions::HEADERS => ['Authorization' => 'Basic '.base64_encode($this->clientId.':'.$this->clientSecret)],
RequestOptions::FORM_PARAMS => $this->getTokenFields($code),
]);
return json_decode((string) $response->getBody(), true);
}
/**
* {@inheritdoc}
*/
public function user()
{
if ($this->hasInvalidState()) {
throw new InvalidStateException;
}
$response = $this->getAccessTokenResponse($this->getCode());
$user = $this->mapUserToObject($this->getUserByToken(
$token = Arr::get($response, 'access_token')
));
//store tokenId session for logout url generation
$this->request->session()->put('fc_token_id', Arr::get($response, 'id_token'));
return $user->setTokenId(Arr::get($response, 'id_token'))
->setToken($token)
->setRefreshToken(Arr::get($response, 'refresh_token'))
->setExpiresIn(Arr::get($response, 'expires_in'));
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get($this->getBaseUrl().'/userinfo', [
RequestOptions::HEADERS => [
'Authorization' => 'Bearer '.$token,
],
]);
return json_decode((string) $response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['sub'],
'given_name' => $user['given_name'],
'family_name' => $user['family_name'],
'gender' => $user['gender'],
'birthplace' => $user['birthplace'],
'birthcountry' => $user['birthcountry'],
'email' => $user['email'],
'preferred_username' => $user['preferred_username'],
]);
}
/**
* Generate logout URL for redirection to FranceConnect.
*/
public function generateLogoutURL()
{
$params = [
'post_logout_redirect_uri' => $this->getConfig('logout_redirect'),
'id_token_hint' => $this->request->session()->get('fc_token_id'),
];
return $this->getBaseUrl().'/logout?'.http_build_query($params);
}
}