22
33namespace SocialiteProviders \Apple ;
44
5+ use Carbon \CarbonImmutable ;
56use DateInterval ;
67use Firebase \JWT \JWK ;
78use GuzzleHttp \Client ;
1314use Laravel \Socialite \Two \InvalidStateException ;
1415use Lcobucci \Clock \SystemClock ;
1516use Lcobucci \JWT \Configuration ;
16- use Lcobucci \JWT \Signer \Rsa \Sha256 ;
17+ use Lcobucci \JWT \Encoding \JoseEncoder ;
18+ use Lcobucci \JWT \Exception ;
19+ use Lcobucci \JWT \Signer ;
20+ use Lcobucci \JWT \Signer \Ecdsa \Sha256 as EcdsaSha256 ;
21+ use Lcobucci \JWT \Signer \Key \InMemory ;
22+ use Lcobucci \JWT \Signer \Rsa \Sha256 as RsaSha256 ;
23+ use Lcobucci \JWT \Token \Parser ;
1724use Lcobucci \JWT \Validation \Constraint \IssuedBy ;
1825use Lcobucci \JWT \Validation \Constraint \LooseValidAt ;
1926use Lcobucci \JWT \Validation \Constraint \SignedWith ;
20- use Lcobucci \JWT \Validation \RequiredConstraintsViolated ;
27+ use Lcobucci \JWT \Validation \Validator ;
2128use Psr \Http \Message \ResponseInterface ;
2229use SocialiteProviders \Manager \OAuth2 \AbstractProvider ;
2330use SocialiteProviders \Manager \OAuth2 \User ;
@@ -26,7 +33,7 @@ class Provider extends AbstractProvider
2633{
2734 public const IDENTIFIER = 'APPLE ' ;
2835
29- public const URL = 'https://appleid.apple.com ' ;
36+ private const URL = 'https://appleid.apple.com ' ;
3037
3138 protected $ scopes = [
3239 'name ' ,
@@ -41,18 +48,11 @@ class Provider extends AbstractProvider
4148 protected $ scopeSeparator = ' ' ;
4249
4350 /**
44- * JWT Configuration.
51+ * JWT Configuration for Apple Authentication Token .
4552 *
4653 * @var ?Configuration
4754 */
48- protected $ jwtConfig = null ;
49-
50- /**
51- * Private Key.
52- *
53- * @var string
54- */
55- protected $ privateKey = '' ;
55+ protected ?Configuration $ jwtConfig = null ;
5656
5757 /**
5858 * {@inheritdoc}
@@ -114,43 +114,53 @@ protected function getUserByToken($token)
114114
115115 protected function getClientSecret ()
116116 {
117- if (!$ this ->jwtConfig ) {
118- $ this ->getJwtConfig (); // Generate Client Secret from private key if not set.
117+ if (!empty ($ this ->privateKey )) {
118+ $ this ->clientSecret = $ this ->generateApplePrivateTokenString ();
119+ config ()->set ('services.apple.client_secret ' , $ this ->clientSecret );
119120 }
120121
121122 return $ this ->clientSecret ;
122123 }
123124
124- protected function getJwtConfig ()
125+ protected function createJwtConfig (): void
125126 {
126- if (!$ this ->jwtConfig ) {
127+ if (!$ this ->jwtConfig instanceof Configuration ) {
127128 $ private_key_path = $ this ->getConfig ('private_key ' , '' );
128129 $ private_key_passphrase = $ this ->getConfig ('passphrase ' , '' );
129- $ signer = $ this ->getConfig ('signer ' , '' );
130+ $ signerClassName = $ this ->getConfig ('signer ' , '' );
130131
131- if (empty ($ signer ) || !class_exists ($ signer )) {
132- $ signer = ! empty ( $ private_key_path ) ? \ Lcobucci \ JWT \ Signer \ Ecdsa \Sha256::class : AppleSignerNone ::class;
132+ if (empty ($ signerClassName ) || !class_exists ($ signerClassName ) || ! is_a ( $ signerClassName , Signer::class, true )) {
133+ $ signerClassName = EcdsaSha256 ::class;
133134 }
134135
135136 if (!empty ($ private_key_path ) && file_exists ($ private_key_path )) {
136- $ this -> privateKey = file_get_contents ($ private_key_path );
137+ $ key = InMemory:: file ($ private_key_path, $ private_key_passphrase );
137138 } else {
138- $ this -> privateKey = $ private_key_path; // Support for plain text private keys
139+ $ key = InMemory:: plainText ( $ private_key_path, $ private_key_passphrase );
139140 }
140141
141142 $ this ->jwtConfig = Configuration::forSymmetricSigner (
142- new $ signer (),
143- AppleSignerInMemory:: plainText ( $ this -> privateKey , $ private_key_passphrase )
143+ new $ signerClassName (),
144+ $ key
144145 );
145-
146- if (!empty ($ this ->privateKey )) {
147- $ appleToken = new AppleToken ($ this ->getJwtConfig ());
148- $ this ->clientSecret = $ appleToken ->generate ();
149- config ()->set ('services.apple.client_secret ' , $ this ->clientSecret );
150- }
151146 }
147+ }
152148
153- return $ this ->jwtConfig ;
149+ private function generateApplePrivateTokenString (): string
150+ {
151+ $ now = CarbonImmutable::now ();
152+ $ this ->createJwtConfig ();
153+
154+ $ token = $ this ->jwtConfig ->builder ()
155+ ->issuedBy (config ('services.apple.team_id ' ))
156+ ->issuedAt ($ now )
157+ ->expiresAt ($ now ->addHour ())
158+ ->permittedFor (Provider::URL )
159+ ->relatedTo (config ('services.apple.client_id ' ))
160+ ->withHeader ('kid ' , config ('services.apple.key_id ' ))
161+ ->getToken ($ this ->jwtConfig ->signer (), $ this ->jwtConfig ->signingKey ());
162+
163+ return $ token ->toString ();
154164 }
155165
156166 /**
@@ -179,7 +189,11 @@ public function userByIdentityToken(string $token): User
179189 */
180190 public function checkToken ($ jwt )
181191 {
182- $ token = $ this ->getJwtConfig ()->parser ()->parse ($ jwt );
192+ try {
193+ $ token = (new Parser (new JoseEncoder ()))->parse ($ jwt );
194+ } catch (Exception $ e ) {
195+ throw new InvalidStateException ($ e ->getMessage ());
196+ }
183197
184198 $ data = Cache::remember ('socialite:Apple-JWKSet ' , 5 * 60 , function () {
185199 $ response = (new Client )->get (self ::URL .'/auth/keys ' );
@@ -190,25 +204,23 @@ public function checkToken($jwt)
190204 $ publicKeys = JWK ::parseKeySet ($ data );
191205 $ kid = $ token ->headers ()->get ('kid ' );
192206
193- if (isset ($ publicKeys [$ kid ])) {
194- $ publicKey = openssl_pkey_get_details ($ publicKeys [$ kid ]->getKeyMaterial ());
207+ if (!isset ($ publicKeys [$ kid ])) {
208+ throw new InvalidStateException ('Invalid JWT Signature ' );
209+ }
210+
211+ $ publicKey = openssl_pkey_get_details ($ publicKeys [$ kid ]->getKeyMaterial ());
212+ try {
195213 $ constraints = [
196- new SignedWith (new Sha256 , AppleSignerInMemory ::plainText ($ publicKey ['key ' ])),
214+ new SignedWith (new RsaSha256 , InMemory ::plainText ($ publicKey ['key ' ])),
197215 new IssuedBy (self ::URL ),
198- // fix for #1354
199216 new LooseValidAt (SystemClock::fromSystemTimezone (), new DateInterval ('PT3S ' )),
200217 ];
201218
202- try {
203- $ this ->jwtConfig ->validator ()->assert ($ token , ...$ constraints );
204-
205- return true ;
206- } catch (RequiredConstraintsViolated $ e ) {
207- throw new InvalidStateException ($ e ->getMessage ());
208- }
219+ (new Validator ())->assert ($ token , ...$ constraints );
220+ } catch (Exception $ e ) {
221+ throw new InvalidStateException ($ e ->getMessage ());
209222 }
210-
211- throw new InvalidStateException ('Invalid JWT Signature ' );
223+ return true ;
212224 }
213225
214226 /**
0 commit comments